upstream version 1.2.2
[debian/freetts] / com / sun / speech / engine / synthesis / text / TextSynthesizerQueueItem.java
1 /**
2  * Copyright 2001 Sun Microsystems, Inc.
3  * 
4  * See the file "license.terms" for information on usage and
5  * redistribution of this file, and for a DISCLAIMER OF ALL 
6  * WARRANTIES.
7  */
8 package com.sun.speech.engine.synthesis.text;
9
10 import javax.speech.Engine;
11 import javax.speech.synthesis.Speakable;
12 import javax.speech.synthesis.SpeakableEvent;
13
14 import com.sun.speech.engine.synthesis.BaseSynthesizer;
15 import com.sun.speech.engine.synthesis.BaseSynthesizerQueueItem;
16
17 import java.net.URL;
18 import java.io.IOException;
19
20 import org.w3c.dom.Document;
21 import org.w3c.dom.Node;
22 import org.w3c.dom.Element;
23 import org.w3c.dom.Text;
24
25 /**
26  * Represents an object on the speech output queue of a
27  * <code>TextSynthesizer</code>.
28  */
29 public class TextSynthesizerQueueItem extends BaseSynthesizerQueueItem {
30     static public final String JSML = "jsml";
31     static public final String[] JSML_ATTRIBUTES = {
32         "lang", "mark"
33     };
34
35     static public final String DIV = "div";
36     static public final String[] DIV_ATTRIBUTES = {
37         "type", "mark"
38     };
39     
40     static public final String VOICE = "voice";
41     static public final String[] VOICE_ATTRIBUTES = {
42         "voice", "gender", "age", "variant", "name", "mark"
43     };
44     
45     static public final String SAYAS = "sayas";
46     static public final String[] SAYAS_ATTRIBUTES = {
47         "class", "mark"
48     };
49
50     static public final String PHONEME = "phoneme";
51     static public final String[] PHONEME_ATTRIBUTES = {
52         "original", "mark"
53     };
54
55     static public final String EMPHASIS = "emphasis";
56     static public final String[] EMPHASIS_ATTRIBUTES = {
57         "level", "mark"
58     };
59
60     static public final String BREAK = "break";
61     static public final String[] BREAK_ATTRIBUTES = {
62         "size", "time", "mark"
63     };
64
65     static public final String PROSODY = "prosody";
66     static public final String[] PROSODY_ATTRIBUTES = {
67         "rate", "volume", "pitch", "range", "mark"
68     };
69
70     static public final String MARKER = "marker";
71     static public final String[] MARKER_ATTRIBUTES = {
72         "mark"
73     };
74     
75     static public final String ENGINE = "engine";
76     static public final String[] ENGINE_ATTRIBUTES = {
77         "name", "data", "mark"
78     };
79
80     static public final String[] ELEMENTS = {
81         JSML,
82         DIV,
83         VOICE,
84         SAYAS,
85         PHONEME,
86         EMPHASIS,
87         BREAK,
88         PROSODY,
89         MARKER,
90         ENGINE
91     };
92
93     static public final String[][] ELEMENT_ATTRIBUTES = {
94         JSML_ATTRIBUTES,
95         DIV_ATTRIBUTES,
96         VOICE_ATTRIBUTES,
97         SAYAS_ATTRIBUTES,
98         PHONEME_ATTRIBUTES,
99         EMPHASIS_ATTRIBUTES,
100         BREAK_ATTRIBUTES,
101         PROSODY_ATTRIBUTES,
102         MARKER_ATTRIBUTES,
103         ENGINE_ATTRIBUTES
104     };
105
106     
107     /*
108      * Commands to be encoded in the text.
109      */
110     static public final String COMMAND_PREFIX = "/";
111     static public final String COMMAND_SUFFIX = "/";
112     static public final String DATA_PREFIX = "[";
113     static public final String DATA_SUFFIX = "]";
114     static public final String ELEMENT_START = "start";
115     static public final String ELEMENT_END = "end";
116     
117     /**
118      * Class constructor.
119      */
120     public TextSynthesizerQueueItem() {
121         super();
122     }
123
124     /**
125      * Gets the type of this queue item.
126      *
127      * @return a <code>String</code> for debug purposes
128      */
129     public String getTypeString() {
130         if (isPlainText()) {
131             return "Plain-text String";
132         } else if (getSource() instanceof String) {
133             return "JSML from String";
134         } else if (getSource() instanceof Speakable) {
135             return "JSML from Speakable";
136         } else if (getSource() instanceof URL) {
137             return "JSML from URL";
138         } else {
139             return "Unknown Output";
140         }
141     }
142
143     /**
144      * Appends the text for this node to the given StringBuffer.
145      *
146      * @param n the node to traverse in depth-first order
147      * @param buf the buffer to append text to
148      */
149     protected void linearize(Node n, StringBuffer buf) {
150         StringBuffer endText = processNode(n, buf);
151         for (Node child = n.getFirstChild();
152              child != null;
153              child = child.getNextSibling()) {
154             linearize(child, buf);
155         }
156
157         if (endText != null) {
158             buf.append(endText);
159         }
160     }
161
162     /**
163      * Adds text for just this node, and returns any text that might
164      * be needed to undo the effects of this node after it is
165      * processed.
166      *
167      * @param n the node to traverse in depth-first order
168      * @param buf the buffer to append text to
169      *
170      * @return a <code>String</code> containing text to undo the
171      *   effects of the node
172      */
173     protected StringBuffer processNode(Node n, StringBuffer buf) {
174         StringBuffer endText = null;
175         
176         int type = n.getNodeType();
177         switch (type) {
178             case Node.ATTRIBUTE_NODE:
179                  break;
180                  
181             case Node.DOCUMENT_NODE:
182                 break;
183                 
184             case Node.ELEMENT_NODE:
185                 endText = processElement((Element) n, buf);
186                 break;
187                 
188             case Node.TEXT_NODE:
189                 buf.append(((Text) n).getData());
190                 break;
191
192             // Pass processing instructions (e.g., <?blah?>
193             // right on to the synthesizer.  These types of things
194             // probably should not be used.  Instead the 'engine'
195             // element is probably the best thing to do.
196             //
197             case Node.PROCESSING_INSTRUCTION_NODE:
198                 break;
199                 
200             // The document type had better be JSML.
201             //
202             case Node.DOCUMENT_TYPE_NODE:
203                 break;
204
205             // I think NOTATION nodes are only DTD's.
206             //
207             case Node.NOTATION_NODE:
208                 break;
209
210             // Should not get COMMENTS because the JSMLParser
211             // ignores them.
212             //
213             case Node.COMMENT_NODE:
214                 break;
215
216             // Should not get CDATA because the JSMLParser is
217             // coalescing.
218             //    
219             case Node.CDATA_SECTION_NODE:
220                 break;
221
222             // Should not get ENTITY related notes because
223             // entities are expanded by the JSMLParser
224             //
225             case Node.ENTITY_NODE:
226             case Node.ENTITY_REFERENCE_NODE:
227                 break;
228
229             // Should not get DOCUMENT_FRAGMENT nodes because I
230             // [[[WDW]]] think they are only created via the API's
231             // and cannot be defined via content.
232             //
233             case Node.DOCUMENT_FRAGMENT_NODE:
234                 break;
235
236             default:
237                 break;
238         }
239         
240         return endText;
241     }
242
243     /**
244      * Adds any commands for this element and returns any text that might
245      * be needed to undo the effects of this element after it is processed.
246      *
247      * @param element the element to traverse in depth-first order
248      * @param buf the buffer to append text to
249      *
250      * @return a <code>String</code> containing text to undo the
251      *   effects of the element
252      */
253     protected StringBuffer processElement(Element element, StringBuffer buf) {
254         StringBuffer endText;
255         StringBuffer attributeText = null;
256         
257         String elementName = element.getTagName();
258         for (int i = 0; i < ELEMENTS.length; i++) {
259             if (ELEMENTS[i].equals(elementName)) {
260                 attributeText = processAttributes(
261                     element, ELEMENT_ATTRIBUTES[i]);
262                 break;
263             }
264         }
265
266         buf.append(COMMAND_PREFIX + elementName + " " + ELEMENT_START);
267         if (attributeText != null) {
268             buf.append(attributeText);
269         }
270         buf.append(COMMAND_SUFFIX);
271
272         endText = new StringBuffer(
273             COMMAND_PREFIX + elementName + " " + ELEMENT_END);
274         if (attributeText != null) {
275             endText.append(attributeText);
276         }
277         endText.append(COMMAND_SUFFIX);
278
279         return endText;
280     }
281
282     /**
283      * Gets the list of attributes of the element and returns them in
284      * a <code>StringBuffer</code>.
285      *
286      * @param element the element containing attributes (if any)
287      * @param attributes the allowed attributes for
288      *   <code>element</code>
289      *
290      * @return a buffer containing the attributes in text form
291      */
292     protected StringBuffer processAttributes(Element element,
293                                              String[] attributes) {
294         StringBuffer attributeText = new StringBuffer();
295         for (int i = 0; i < attributes.length; i++) {
296             if (element.hasAttribute(attributes[i])) {
297                 String data = element.getAttribute(attributes[i]);
298                 attributeText.append(
299                     DATA_PREFIX + attributes[i] + "=" + data + DATA_SUFFIX);
300             }
301         }
302         return attributeText;
303     }
304        
305     /**
306      * Gets the text form of this queue item.
307      *
308      * @return the text form of this queue item.
309      */
310     public String getEngineText() {
311         if (isPlainText()) {
312             return text;
313         } else {
314             StringBuffer textBuffer = new StringBuffer();
315             Document document = getDocument();
316             linearize(document, textBuffer);
317             return(textBuffer.toString());
318         }
319     }
320 }