TTSBuilder.java :  » XML » alttext » nl_dutchear_speechgen » tts » Java Open Source

Java Open Source » XML » alttext 
alttext » nl_dutchear_speechgen » tts » TTSBuilder.java
/*
 * Daisy Pipeline (C) 2005-2008 Daisy Consortium
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package nl_dutchear_speechgen.tts;

import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.daisy.pipeline.core.transformer.TransformerDelegateListener;
import org.daisy.util.i18n.LocaleUtils;
import org.daisy.util.xml.XPathUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import nl_dutchear_speechgen.tts.concurrent.TTSAdapter;
import nl_dutchear_speechgen.tts.concurrent.TTSGroup;
import nl_dutchear_speechgen.tts.util.TTSUtils;


/**
 * Constructs tts instances given a certain configuration and environment.
 * 
 * @author Martin Blomberg
 *
 */
public class TTSBuilder implements TTSConstants {

  private File configFile;
  private Document configuration;

  /**
   * Constructor pointing out the TTSBuilder configuration file.
   * @param configFile the configuration file.
   * @throws TTSBuilderException 
   */
  public TTSBuilder(File configFile) throws TTSBuilderException {
    this.configFile = configFile;
    this.configuration = readXML(configFile);
    keepFirstMatchingOS(configuration);
  }

  public TTSBuilder(File configFile, Map<String,String> fileSubstitutions) throws TTSBuilderException {
    //System.setProperty("org.daisy.debug", "foo");
    this.configFile = configFile;
    this.configuration = readXML(configFile);
    XMLParameter xmlp = new XMLParameter(fileSubstitutions);
    xmlp.eval(configuration);
    keepFirstMatchingOS(configuration);
  }

  /**
   * Keeps the first os configuration from <code>config</code> that matches the current environment.
   * @param config the configuration document 
   */
  private void keepFirstMatchingOS(Document config) {
    Properties systemProps = System.getProperties();
    Element docElem = config.getDocumentElement();

    NodeList osList = XPathUtils.selectNodes(docElem, "/ttsbuilder/os");

    int numRemovedSystems = 0;
    // for each operating system currentOS
    for (int i = 0; i < osList.getLength(); i++) {
      Node currentOS = osList.item(i);
      NodeList propList = XPathUtils.selectNodes(currentOS, "property");

      // for each property required for currentOS
      boolean matching = true;
      for (int j = 0; j < propList.getLength(); j++) {
        Element prop = (Element) propList.item(j);

        String propertyName = prop.getAttribute("name");
        String propertyMatch = prop.getAttribute("match");
        String systemPropValue = systemProps.getProperty(propertyName);

        if (!systemPropValue.matches(propertyMatch)) {
          currentOS.getParentNode().removeChild(currentOS);
          numRemovedSystems++;
          matching = false;
          break;
        }
      }

      // is currentOS the one we should keep?
      if (matching) {
        i++;
        for ( ; i < osList.getLength(); i++) {
          // tmp is either redundant or incorrect
          Node tmp = osList.item(i);
          tmp.getParentNode().removeChild(tmp);
          numRemovedSystems++;
        }
      }
    }

    // what? no matching operating systems left in the configuration file?
    if (osList.getLength() == numRemovedSystems) {
      String osName = System.getProperty("os.name");
      String message = "TTSBuilder configuration file does not " +
      "contain any operating system entry matching the current environment: " + 
      osName + "\n" + "Add a proper " + osName + " matching section to " + 
      configFile + " to fix this problem.";
      throw new IllegalArgumentException(message);
    }

    DEBUG(config);
  }


  public TTS newTTS(String lang, TransformerDelegateListener tdl) throws TTSBuilderException {

    String xpath = null;
    Element docElement = null;
    Element ttsElement = null;

    // if lang != null, someone wants a TTS for a specific xml:lang
    if (lang != null) {
      // is lang a valid locale?
      if (null == LocaleUtils.string2locale(lang)) {
        // what? throw InvalidLangException??
        throw new IllegalArgumentException(lang + " is not a valid lang parameter.");
      }

      xpath = "/ttsbuilder/os/lang[@lang='" + lang + "']/tts";
      docElement = configuration.getDocumentElement();
      ttsElement = (Element) XPathUtils.selectSingleNode(docElement, xpath);

      if (null == ttsElement) {
        String msg = "No TTS found for language: " + lang;
        throw new TTSBuilderException(msg);
        
      }
    } else {
      // lang is null, a default voice is desired
      xpath = "/ttsbuilder/os/lang/tts[@default='true']";
      docElement = configuration.getDocumentElement();
      ttsElement = (Element) XPathUtils.selectSingleNode(docElement, xpath);;

      if (null == ttsElement) {
        String msg = "No default TTS found!";
        throw new TTSBuilderException(msg);
      }
    }


    // are we supposed to start several instances?
    int numTTSInstances = 1;
    String instancesValue = ttsElement.getAttribute("instances");
    if (instancesValue != null && instancesValue.trim().length() > 0) {
      int temp;
      try {
        temp = Integer.parseInt(instancesValue);
      } catch (NumberFormatException e) {
        temp = -1;
      }
      numTTSInstances = Math.max(numTTSInstances, temp);
    }

    // get the commands used to start (possibly) several instances of this tts.
    // if no commands are present, create a single empty map just to get by.
    Map<String, String>[] params = null;
    NodeList commands = XPathUtils.selectNodes(ttsElement, "param[@name='" + COMMAND + "']");
    if (commands.getLength() > 0) {
      params = new Map[commands.getLength()];
      for (int i = 0; i < params.length; i++) {
        params[i] = new HashMap<String, String>();
        Element elem = (Element) commands.item(i);
        params[i].put(COMMAND, elem.getAttribute("value"));
      }
    } else {
      params = new Map[]{new HashMap<String, String>()};
    }
    
    //System.err.println("#instances of " + lang + ": " + (numTTSInstances * params.length));

    // ttsElement represents a tts matching user requirements,
    // read the tts specific parameters from the configfile
    Map<String, String> parameters = new HashMap<String, String>();
    NodeList paramElems = XPathUtils.selectNodes(ttsElement, "param[@name!='" + COMMAND + "']");
    DEBUG("paramElems.getLength() = " + paramElems.getLength());
    for (int i = 0; i < paramElems.getLength(); i++) {
      Element elem = (Element) paramElems.item(i);
      parameters.put(elem.getAttribute("name"), elem.getAttribute("value"));
    }

    String fullClassName = parameters.get(CLASS);
    if (null == fullClassName) {
      throw new IllegalArgumentException("The full class name must be provided for every TTS Java wrapper implementation, edit " + 
          configFile.getAbsolutePath() + 
          " in order to fix this problem. Tried to create tts for xml:lang= " + lang);
    }

    Class<?> ttsClass = null; 
    Class<?> parameterList[] = null;
    Object constrParam[] = null;
    Constructor<?> constructor = null;
    TTSAdapter tts = null;

    try {
      DEBUG("fullClassName: " + fullClassName);
      ttsClass = Class.forName(fullClassName);
    } catch (ClassNotFoundException e) {
      throw new TTSBuilderException(e.getMessage(), e);
    }


    try {
      parameterList = new Class[]{
          Class.forName("nl_dutchear_speechgen.tts.util.TTSUtils"), 
          Class.forName("java.util.Map")
        };
      
      constrParam = new Object[2];
      constructor = ttsClass.getConstructor(parameterList);
    } catch (Exception e) {
      //throw new TTSInstantiationException(e.getMessage(), e);
      // Ok, we didn't get the constructor we wanted. We continue
      // anyway, further down there is a possibility to try a
      // default no-parameters constructor.
    }

    
    List<TTSAdapter> ttsInstances = new ArrayList<TTSAdapter>();
    
    // for each command
    for (int i = 0; i < params.length; i++) {
      params[i].putAll(parameters);
      
      // for each "instance"
      for (int j = 0; j < numTTSInstances; j++) {
              
        // did we get hold of the desired constructor?
        if (constructor != null && constrParam != null) {

          // if so, create wished number of instances, if there are several
          // commands, one instance will be created for each one of them.
          try {
            Map<String, String> m = new HashMap<String, String>();
            m.putAll(params[i]);
            TTSUtils tu = new TTSUtils(m);
            
            constrParam[0] = tu;
            constrParam[1] = params[i];
            
            tts = (TTSAdapter) constructor.newInstance(constrParam);
          } catch (Exception e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
            throw new TTSBuilderException(e.getMessage(), e);
          }
        } 
        // so there is no constructor accepting parametes. (as far as we know)
        // let's try the default approach.
        else {
          try {
            tts = (TTSAdapter) ttsClass.newInstance();
          } catch (InstantiationException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
            throw new TTSBuilderException(e.getMessage(), e);
          } catch (IllegalAccessException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
            throw new TTSBuilderException(e.getMessage(), e);
          }
        }
        // register the transformer delegate listener
        tts.setTransformerDelegateListener(tdl);
        // initialize the adapter
        tts.init();
        // add the tts to the group
        ttsInstances.add(tts);
      }
    }
    
    // create a TTSGroup with all the new Instances.
    TTSGroup group = new TTSGroup(ttsInstances,tdl);
    return group;
  }

  /**
   * Reads the configuration file into a DOM object.
   * @param file the configuration file
   * @return a DOM representation of the XML in the configuration file
   * @throws TTSBuilderException
   */
  private Document readXML(File file) throws TTSBuilderException {
    Document doc = null;
    try {
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      doc = db.parse(file);
    } catch (Exception e) {
      System.err.println(e.getMessage());
      e.printStackTrace();
      throw new TTSBuilderException(e.getMessage(), e);
    }
    return doc;
  }

  /**
   * Optional debug messages.
   * @param d the xml document to display.
   */
  private void DEBUG(Document d) {
    if (System.getProperty("org.daisy.debug") != null) {
      DEBUG("DEBUG(Document):");
      try {
        TransformerFactory xformFactory = TransformerFactory.newInstance();  
        javax.xml.transform.Transformer idTransform = xformFactory.newTransformer();
        idTransform.setOutputProperty(OutputKeys.INDENT, "yes");
        Source input = new DOMSource(d);
        Result output = new StreamResult(System.out);
        idTransform.transform(input, output);
      } catch (TransformerConfigurationException e) {
        e.printStackTrace();
      } catch (TransformerException e) {
        e.printStackTrace();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * Optional debug messages.
   * @param msg the message to display.
   */
  private void DEBUG(String msg) {
    if (System.getProperty("org.daisy.debug") != null) {
      System.out.println("DEBUG: TTSBuilder#" + msg);
    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.