Package com.sun.speech.freetts.lexicon

Source Code of com.sun.speech.freetts.lexicon.LexiconImpl

/**
* Portions Copyright 2001 Sun Microsystems, Inc.
* Portions Copyright 1999-2001 Language Technologies Institute,
* Carnegie Mellon University.
* All Rights Reserved.  Use is subject to license terms.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*/
package com.sun.speech.freetts.lexicon;

import com.sun.speech.freetts.util.Utilities;
import com.sun.speech.freetts.util.BulkTimer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;

import java.net.MalformedURLException;
import java.net.URL;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

/**
* Provides an implementation of a Lexicon.
*
* <p>This implementation will either read from a straight ASCII file
* or a binary file.  When reading from an ASCII file, you can specify
* when the input line is tokenized:  load, lookup, or never.  If you
* specify 'load', the entire file will be parsed when it is loaded.
* If you specify 'lookup', the file will be loaded, but the parsing
* for each line will be delayed until it is referenced and the parsed
* form will be saved away.  If you specify 'never', the lines will
* parsed each time they are referenced.  The default is 'never'.  To
* specify the load type, set the system property as follows:
*
* <pre>
*   -Dcom.sun.speech.freetts.lexicon.LexTokenize=load
* </pre>
*
* <p>If a binary file is used, you can also specify whether the new
* IO package is used.  The new IO package is new for JDK1.4, and can
* greatly improve the speed of loading files.  To enable new IO, use
* the following system property (it is enabled by default):
*
* <pre>
*   -Dcom.sun.speech.freetts.useNewIO=true
* </pre>
*
* <p>The implementation also allows users to define their own addenda
* that will be used in addition to the system addenda.  If the user
* defines their own addenda, it values will be added to the system
* addenda, overriding any existing elements in the system addenda.
* To define a user addenda, the user needs to set the following
* property:
*
* <pre>
*   -Dcom.sun.speeech.freetts.lexicon.userAddenda=&lt;URLToUserAddenda>
* </pre>
*
* Where &lt;URLToUserAddenda> is a URL pointing to an ASCII file
* containing addenda entries.
*
* <p>[[[TODO: support multiple homographs with the same part of speech.]]]
*/
abstract public class LexiconImpl implements Lexicon {
    /**
     * If true, the phone string is replaced with the phone array in
     * the hashmap when the phone array is loaded.  The side effects
     * of this are quicker lookups, but more memory usage and a longer
     * startup time.
     */
    protected boolean tokenizeOnLoad = false;
      
    /**
     * If true, the phone string is replaced with the phone array in
     * the hashmap when the phone array is first looked up.  The side effects
     * Set by cmufilelex.tokenize=lookup.
     */
    protected boolean tokenizeOnLookup = false;
    /**
     * Magic number for binary Lexicon files.
     */
    private final static int MAGIC = 0xBABB1E;

    /**
     * Current binary file version.
     */
    private final static int VERSION = 1;

    /**
     * URL for the compiled form.
     */
    private URL compiledURL;

    /**
     * URL for the addenda.
     */
    private URL addendaURL;

    /**
     * URL for the letter to sound rules.
     */
    private URL letterToSoundURL;

    /**
     * The addenda.
     */
    private Map addenda;

    /**
     * The compiled lexicon.
     */
    private Map compiled;

    /**
     * The LetterToSound rules.
     */
    private LetterToSound letterToSound = null;

    /**
     * Parts of Speech.
     */
    private ArrayList partsOfSpeech = new ArrayList();

    /**
     * A static directory of compiledURL URL objects and associated
     * already-loaded compiled Map objects. This is used to share
     * the immutable compiled lexicons between lexicon instances.
     * As the addenda can be changed using <code>addAddendum()</code>
     * and <code>removeAddendum</code>, each lexicon instance has its
     * own addenda.
     */
    private static Map loadedCompiledLexicons;
   

   
    /**
     * Loaded State of the lexicon
     */
    private boolean loaded = false;
   
    /**
     * Type of lexicon to load
     */
    private boolean binary = false;

    /**
     * No phones for this word.
     */
    final static private String[] NO_PHONES = new String[0];

    /**
     * Temporary place holder.
     */
    private char charBuffer[] = new char[128];

    /**
     * Use the new IO package?
     */
    private boolean useNewIO =
  Utilities.getProperty("com.sun.speech.freetts.useNewIO",
    "true").equals("true");

    /**
     * Create a new LexiconImpl by reading from the given URLS.
     *
     * @param compiledURL a URL pointing to the compiled lexicon
     * @param addendaURL a URL pointing to lexicon addenda
     * @param letterToSoundURL a LetterToSound to use if a word cannot
     *   be found in the compiled form or the addenda
     * @param binary if <code>true</code>, the input streams are binary;
     *   otherwise, they are text.
     */
    public LexiconImpl(URL compiledURL, URL addendaURL,
                       URL letterToSoundURL,
           boolean binary) {
  this();
  setLexiconParameters(compiledURL, addendaURL, letterToSoundURL, binary);
    }

    /**
     * Class constructor for an empty Lexicon.
     */
    public LexiconImpl() {
        // Find out when to convert the phone string into an array.
        //
        String tokenize =
      Utilities.getProperty("com.sun.speech.freetts.lexicon.LexTokenize",
                               "never");
  tokenizeOnLoad = tokenize.equals("load");
  tokenizeOnLookup = tokenize.equals("lookup");
    }

    /**
     * Sets the lexicon parameters
     * @param compiledURL a URL pointing to the compiled lexicon
     * @param addendaURL a URL pointing to lexicon addenda
     * @param letterToSoundURL a URL pointing to the LetterToSound to use
     * @param binary if <code>true</code>, the input streams are binary;
     *   otherwise, they are text.
     */
    protected void setLexiconParameters(URL compiledURL,
                                        URL addendaURL,
                                        URL letterToSoundURL,
                                        boolean binary) {
  this.compiledURL = compiledURL;
  this.addendaURL = addendaURL;
        this.letterToSoundURL = letterToSoundURL;
  this.binary = binary;
    }

    /**
     * Determines if this lexicon is loaded.
     *
     * @return <code>true</code> if the lexicon is loaded
     */
    public boolean isLoaded() {
  return loaded;
    }

    /**
     * Loads the data for this lexicon.  If the
     *
     * @throws IOException if errors occur during loading
     */
    public void load() throws IOException {
  BulkTimer.LOAD.start("Lexicon");

  if (compiledURL == null) {
      throw new IOException("Can't load lexicon");
  }

  if (addendaURL == null) {
      throw new IOException("Can't load lexicon addenda " );
  }

  if (loadedCompiledLexicons == null) {
      loadedCompiledLexicons = new HashMap();
  }
  if (!loadedCompiledLexicons.containsKey(compiledURL)) {
    InputStream compiledIS = Utilities.getInputStream(compiledURL);
    if (compiledIS == null) {
        throw new IOException("Can't load lexicon from " + compiledURL);
    }
    Map newCompiled = createLexicon(compiledIS, binary, 65000);
        loadedCompiledLexicons.put(compiledURL, newCompiled);
      compiledIS.close();
  }
  compiled = Collections.unmodifiableMap((Map)loadedCompiledLexicons.get(compiledURL));

  InputStream addendaIS = Utilities.getInputStream(addendaURL);
  if (addendaIS == null) {
      throw new IOException("Can't load lexicon addenda from "
        + addendaURL);
  }

  // [[[TODO: what is the best way to derive the estimated sizes?]]]
        //
        addenda = createLexicon(addendaIS, binary, 50);
  addendaIS.close();

        /* Load the user-defined addenda and override any existing
         * entries in the system addenda.
         */
        String userAddenda = Utilities.getProperty(
            "com.sun.speech.freetts.lexicon.userAddenda", null);
        if (userAddenda != null) {
            try {
                URL userAddendaURL = new URL(userAddenda);
                InputStream userAddendaIS = Utilities.getInputStream(
                    userAddendaURL);
                if (userAddendaIS == null) {
                    throw new IOException("Can't load user addenda from "
                                          + userAddenda);
                }
                Map tmpAddenda = createLexicon(userAddendaIS, false, 50);
                userAddendaIS.close();
                for (Iterator keys = tmpAddenda.keySet().iterator();
                     keys.hasNext();) {
                    Object key = keys.next();
                    addenda.put(key, tmpAddenda.get(key));
                }
            } catch (MalformedURLException e) {
                throw new IOException("User addenda URL is malformed: " +
                                      userAddenda);
            }
        }
       
  loaded = true;
  BulkTimer.LOAD.stop("Lexicon");
  letterToSound = new LetterToSoundImpl(letterToSoundURL, binary);
    }

    /**
     * Reads the given input stream as lexicon data and returns the
     * results in a <code>Map</code>.
     *
     * @param is the input stream
     * @param binary if <code>true</code>, the data is binary
     * @param estimatedSize the estimated size of the lexicon
     *
     * @throws IOException if errors are encountered while reading the data
     */
    protected Map createLexicon(InputStream is,
                                boolean binary,
                                int estimatedSize)
        throws IOException {
  if (binary) {
      if (useNewIO && is instanceof FileInputStream) {
    FileInputStream fis = (FileInputStream) is;
    return loadMappedBinaryLexicon(fis, estimatedSize);
      } else {
    DataInputStream dis = new DataInputStream(
      new BufferedInputStream(is));
    return loadBinaryLexicon(dis, estimatedSize);
      }
  }  else {
      return loadTextLexicon(is, estimatedSize);
  }
    }

    /**
     * Reads the given input stream as text lexicon data and returns the
     * results in a <code>Map</code>.
     *
     * @param is the input stream
     * @param estimatedSize the estimated number of entries of the lexicon
     *
     * @throws IOException if errors are encountered while reading the data
     */
    protected Map loadTextLexicon(InputStream is, int estimatedSize)
  throws IOException {
        Map lexicon = new LinkedHashMap(estimatedSize * 4 / 3);
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        String line;
       
        line = reader.readLine();
        while (line != null) {
            if (!line.startsWith("***")) {
                parseAndAdd(lexicon, line);
            }
            line = reader.readLine();
        }
        return lexicon;
    }
   
    /**
     * Creates a word from the given input line and add it to the lexicon.
     *
     * @param lexicon the lexicon
     * @param line the input text
     */
    protected void parseAndAdd(Map lexicon, String line) {
        StringTokenizer tokenizer = new StringTokenizer(line,"\t");
        String phones = null;
       
        String wordAndPos = tokenizer.nextToken();
        String pos = wordAndPos.substring(wordAndPos.length() - 1);
        if (!partsOfSpeech.contains(pos)) {
            partsOfSpeech.add(pos);
        }
        if (tokenizer.hasMoreTokens()) {
            phones = tokenizer.nextToken();
        }
  if ((phones != null) && (tokenizeOnLoad)) {
      lexicon.put(wordAndPos, getPhones(phones));
        } else if (phones == null) {
            lexicon.put(wordAndPos, NO_PHONES);
        } else {
            lexicon.put(wordAndPos, phones);
        }
    }
   
    /**
     * Gets the phone list for a given word.  If a phone list cannot
     * be found, returns <code>null</code>.  The format is lexicon
     * dependent.  If the part of speech does not matter, pass in
     * <code>null</code>.
     *
     * @param word the word to find
     * @param partOfSpeech the part of speech
     *
     * @return the list of phones for word or <code>null</code>
     */
    public String[] getPhones(String word, String partOfSpeech) {
      return getPhones(word, partOfSpeech, true);
    }

    /**
     * Gets the phone list for a given word.  If a phone list cannot
     * be found, <code>null</code> is returned.  The
     * <code>partOfSpeech</code> is implementation dependent, but
     * <code>null</code> always matches.
     *
     * @param word the word to find
     * @param partOfSpeech the part of speech or <code>null</code>
     * @param useLTS whether to use the letter-to-sound rules when
     *        the word is not in the lexicon.
     *
     * @return the list of phones for word or null
     */   
    public String[] getPhones
      (String word, String partOfSpeech, boolean useLTS){
      String[] phones = null;
   phones = getPhones(addenda, word, partOfSpeech);
      if (phones == null) {
          phones = getPhones(compiled, word, partOfSpeech);
      }
      if(useLTS){
            if (phones == null && letterToSound != null) {
                phones = letterToSound.getPhones(word, partOfSpeech);
            }
      }
      if(phones != null){
      String[] copy = new String[phones.length];
      System.arraycopy(phones, 0, copy, 0, phones.length);
            return copy;
      }
      else return null;
     
    }
    /**
     * Gets a phone list for a word from a given lexicon.  If a phone
     * list cannot be found, returns <code>null</code>.  The format is
     * lexicon dependent.  If the part of speech does not matter, pass
     * in <code>null</code>.
     *
     * @param lexicon the lexicon
     * @param word the word to find
     * @param partOfSpeech the part of speech
     *
     * @return the list of phones for word or <code>null</code>
     */
    protected String[] getPhones(Map lexicon,
                                 String word,
                                 String partOfSpeech) {
        String[] phones;
        partOfSpeech = fixPartOfSpeech(partOfSpeech);
        phones = getPhones(lexicon, word+partOfSpeech);
        for (int i = 0;
             (i < partsOfSpeech.size()) && (phones == null);
             i++) {
            if (!partOfSpeech.equals((String) partsOfSpeech.get(i))) {
                phones = getPhones(lexicon,
                                   word + (String) partsOfSpeech.get(i));
            }
        }
        return phones;
    }

    /**
     * Gets a phone list for a word from a given lexicon.  If a phone
     * list cannot be found, returns <code>null</code>.
     *
     * @param lexicon the lexicon
     * @param wordAndPartOfSpeech word and part of speech concatenated
     *   together
     *
     * @return the list of phones for word or <code>null</code>
     */
    protected String[] getPhones(Map lexicon,
                                 String wordAndPartOfSpeech) {
        Object value = lexicon.get(wordAndPartOfSpeech);
        if (value instanceof String[]) {
            return (String[]) value;
        } else if (value instanceof String) {
            String[] phoneArray;
            phoneArray = getPhones((String) value);
            if (tokenizeOnLookup) {
                lexicon.put(wordAndPartOfSpeech, phoneArray);
            }
            return phoneArray;
        } else {
            return null;
        }
    }
   
    /**
     * Turns the phone <code>String</code> into a <code>String[]</code>,
     * using " " as the delimiter.
     *
     * @param phones the phones
     *
     * @return the phones split into an array
     */
    protected String[] getPhones(String phones) {
        ArrayList phoneList = new ArrayList();
        StringTokenizer tokenizer = new StringTokenizer(phones, " ");
        while (tokenizer.hasMoreTokens()) {
            phoneList.add(tokenizer.nextToken());
        }
        return (String[]) phoneList.toArray(new String[0]);
    }
   
    /**
     * Adds a word to the addenda.
     *
     * @param word the word to find
     * @param partOfSpeech the part of speech
     * @param phones the phones for the word
     *
     */
    public void addAddendum(String word,
                            String partOfSpeech,
                            String[] phones) {
        String pos = fixPartOfSpeech(partOfSpeech);
        if (!partsOfSpeech.contains(pos)) {
            partsOfSpeech.add(pos);
        }
        addenda.put(word + pos, phones);
    }  

    /**
     * Removes a word from the addenda.
     *
     * @param word the word to remove
     * @param partOfSpeech the part of speech
     */
    public void removeAddendum(String word, String partOfSpeech) {
        addenda.remove(word + fixPartOfSpeech(partOfSpeech));       
    }

    /**
     * Outputs a string to a data output stream.
     *
     * @param dos the data output stream
     * @param s the string to output
     *
     * @throws IOException if errors occur during writing
     */
    private void outString(DataOutputStream dos, String s)
      throws IOException {
  dos.writeByte((byte) s.length());
  for (int i = 0; i < s.length(); i++) {
      dos.writeChar(s.charAt(i));
  }
    }

    /**
     * Inputs a string from a DataInputStream.  This method is not re-entrant.
     *
     * @param dis the data input stream
     *
     * @return the string
     *
     * @throws IOException if errors occur during reading
     */
    private String getString(DataInputStream dis) throws IOException {
  int size = dis.readByte();
  for (int i = 0; i < size; i++) {
      charBuffer[i] = dis.readChar();
  }
  return new String(charBuffer, 0, size);
    }

    /**
     * Inputs a string from a DataInputStream.  This method is not re-entrant.
     *
     * @param bb the input byte buffer
     *
     * @return the string
     *
     * @throws IOException if errors occur during reading
     */
    private String getString(ByteBuffer bb) throws IOException {
  int size = bb.get();
  for (int i = 0; i < size; i++) {
      charBuffer[i] = bb.getChar();
  }
  return new String(charBuffer, 0, size);
    }


    /**
     * Dumps a binary form of the database.  This method is not thread-safe.
     *
     * <p>Binary format is:
     * <pre>
     * MAGIC
     * VERSION
     * (int) numPhonemes
     * (String) phoneme0
     * (String) phoneme1
     * (String) phonemeN
     * (int) numEntries
     * (String) nameWithPOS
     * (byte) numPhonemes
     * phoneme index 1
     * phoneme index 2
     * phoneme index n
     * </pre>
     *
     * <p>Strings are formatted as: <code>(byte) len char0 char1 charN</code>
     *
     * <p>Limits: Strings: 128 chars
     * <p>Limits: Strings: 128 phonemes per word
     *
     * @param lexicon the lexicon to dump
     * @param path the path to dump the file to
     */
    private void dumpBinaryLexicon(Map lexicon, String path) {
  try {
      FileOutputStream fos = new FileOutputStream(path);
      DataOutputStream dos = new DataOutputStream(new
        BufferedOutputStream(fos));
      List phonemeList = findPhonemes(lexicon);

      dos.writeInt(MAGIC);
      dos.writeInt(VERSION);
      dos.writeInt(phonemeList.size());

      for (int i = 0; i < phonemeList.size(); i++) {
    outString(dos, (String) phonemeList.get(i));
      }

      dos.writeInt(lexicon.keySet().size());
      for (Iterator i = lexicon.keySet().iterator(); i.hasNext(); ) {
    String key = (String) i.next();
    outString(dos, key);
    String[] phonemes = getPhones(lexicon, key);
    dos.writeByte((byte) phonemes.length);
    for (int index = 0; index < phonemes.length; index++) {
        int phonemeIndex = phonemeList.indexOf(phonemes[index]);
        if (phonemeIndex == -1) {
      throw new Error("Can't find phoneme index");
        }
        dos.writeByte((byte) phonemeIndex);
    }
      }
      dos.close();
  } catch (FileNotFoundException fe) {
      throw new Error("Can't dump binary database " +
        fe.getMessage());
  } catch (IOException ioe) {
      throw new Error("Can't write binary database " +
        ioe.getMessage());
  }
    }

    /**
     * Loads the binary lexicon from the given InputStream.
     * This method is not thread safe.
     *
     * @param is the InputStream to load the database from
     * @param estimatedSize estimate of how large the database is
     *
     * @return a <code>Map</code> containing the lexicon
     *
     * @throws IOException if an IO error occurs
     */
    private Map loadMappedBinaryLexicon(FileInputStream is, int estimatedSize)
  throws IOException {
  FileChannel fc = is.getChannel();

  MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY,
    0, (int) fc.size());
  bb.load();
  int size = 0;
  int numEntries = 0;
  List phonemeList = new ArrayList();

  // we get better performance for some reason if we
  // just ignore estimated size
        //
  // Map lexicon = new HashMap();
        Map lexicon = new LinkedHashMap(estimatedSize * 4 / 3);

  if (bb.getInt() != MAGIC) {
      throw new Error("bad magic number in lexicon");
  }

  if (bb.getInt() != VERSION) {
      throw new Error("bad version number in lexicon");
  }

  size = bb.getInt();
  for (int i = 0; i < size; i++) {
      String phoneme = getString(bb);
      phonemeList.add(phoneme);
  }
  numEntries = bb.getInt();

  for (int i = 0; i < numEntries; i++) {
      String wordAndPos = getString(bb);
      String pos = Character.toString(
        wordAndPos.charAt(wordAndPos.length() - 1));
      if (!partsOfSpeech.contains(pos)) {
    partsOfSpeech.add(pos);
      }

      int numPhonemes = bb.get();
      String[] phonemes = new String[numPhonemes];

      for (int j = 0; j < numPhonemes; j++) {
    phonemes[j] = (String) phonemeList.get(bb.get());
      }
      lexicon.put(wordAndPos, phonemes);
  }
  fc.close();
  return lexicon;
    }

    /**
     * Loads the binary lexicon from the given InputStream.
     * This method is not thread safe.
     *
     * @param is the InputStream to load the database from
     * @param estimatedSize estimate of how large the database is
     *
     * @return a <code>Map</code> containing the lexicon
     *
     * @throws IOException if an IO error occurs
     */
    private Map loadBinaryLexicon(InputStream is, int estimatedSize)
  throws IOException {
  DataInputStream dis = new DataInputStream(new
    BufferedInputStream(is));
  int size = 0;
  int numEntries = 0;
  List phonemeList = new ArrayList();

  // we get better performance for some reason if we
  // just ignore estimated size
        //
        Map lexicon = new LinkedHashMap();

  if (dis.readInt() != MAGIC) {
      throw new Error("bad magic number in lexicon");
  }

  if (dis.readInt() != VERSION) {
      throw new Error("bad version number in lexicon");
  }

  size = dis.readInt();
  for (int i = 0; i < size; i++) {
      String phoneme = getString(dis);
      phonemeList.add(phoneme);
  }
  numEntries = dis.readInt();

  for (int i = 0; i < numEntries; i++) {
      String wordAndPos = getString(dis);
      String pos = Character.toString(
        wordAndPos.charAt(wordAndPos.length() - 1));
      if (!partsOfSpeech.contains(pos)) {
    partsOfSpeech.add(pos);
      }

      int numPhonemes = dis.readByte();
      String[] phonemes = new String[numPhonemes];

      for (int j = 0; j < numPhonemes; j++) {
    phonemes[j] = (String) phonemeList.get(dis.readByte());
      }
      lexicon.put(wordAndPos, phonemes);
  }
  dis.close();
  return lexicon;
    }

    /**
     * Dumps this lexicon (just the compiled form). Lexicon will be
     * dumped to two binary files PATH_compiled.bin and
     * PATH_addenda.bin
     *
     * @param path the root path to dump it to
     */
    public void dumpBinary(String path) {
        String compiledPath = path + "_compiled.bin";
        String addendaPath = path + "_addenda.bin";
       
        dumpBinaryLexicon(compiled, compiledPath);
        dumpBinaryLexicon(addenda, addendaPath);
    }

    /**
     * Returns a list of the unique phonemes in the lexicon.
     *
     * @param lexicon the lexicon of interest
     *
     * @return list the unique set of phonemes
     */
    private List findPhonemes(Map lexicon) {
  List phonemeList = new ArrayList();
  for (Iterator i = lexicon.keySet().iterator(); i.hasNext(); ) {
      String key = (String) i.next();
      String[] phonemes = getPhones(lexicon, key);
      for (int index = 0; index < phonemes.length; index++) {
    if (!phonemeList.contains(phonemes[index])) {
        phonemeList.add(phonemes[index]);
    }
      }
  }
  return phonemeList;
    }


    /**
     * Tests to see if this lexicon is identical to the other for
     * debugging purposes.
     *
     * @param other the other lexicon to compare to
     *
     * @return true if lexicons are identical
     */
    public boolean compare(LexiconImpl other) {
  return compare(addenda, other.addenda) &&
        compare(compiled, other.compiled);
    }

    /**
     * Determines if the two lexicons are identical for debugging purposes.
     *
     * @param lex this lex
     * @param other the other lexicon to chd
     *
     * @return true if they are identical
     */
    private boolean compare(Map lex, Map other) {
  for (Iterator i = lex.keySet().iterator(); i.hasNext(); ) {
      String key = (String) i.next();
      String[] thisPhonemes = getPhones(lex, key);
      String[] otherPhonemes = getPhones(other, key);
      if (thisPhonemes == null) {
    System.out.println(key + " not found in this.");
    return false;
      } else if (otherPhonemes == null) {
    System.out.println(key + " not found in other.");
    return false;
      } else if (thisPhonemes.length == otherPhonemes.length) {
    for (int j = 0; j < thisPhonemes.length; j++) {
        if (!thisPhonemes[j].equals(otherPhonemes[j])) {
      return false;
        }
    }
      } else {
    return false;
      }
  }
  return true;
    }
    /**
     * Fixes the part of speech if it is <code>null</code>.  The
     * default representation of a <code>null</code> part of speech
     * is the number "0".
     */
    static protected String fixPartOfSpeech(String partOfSpeech) {
        return (partOfSpeech == null) ? "0" : partOfSpeech;
    }
}
TOP

Related Classes of com.sun.speech.freetts.lexicon.LexiconImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.