upstream version 1.2.2
[debian/freetts] / com / sun / speech / freetts / Item.java
1 /**
2  * Portions Copyright 2001-2003 Sun Microsystems, Inc.
3  * Portions Copyright 1999-2001 Language Technologies Institute, 
4  * Carnegie Mellon University.
5  * All Rights Reserved.  Use is subject to license terms.
6  * 
7  * See the file "license.terms" for information on usage and
8  * redistribution of this file, and for a DISCLAIMER OF ALL 
9  * WARRANTIES.
10  */
11 package com.sun.speech.freetts;
12
13 import java.io.PrintWriter;
14 import java.util.StringTokenizer;
15
16 /**
17  * Represents a node in a Relation. Items can have shared contents but 
18  * each item has its own set of Daughters. The shared contents of an
19  * item (represented by ItemContents) includes the feature set for the
20  * item and the set of all relations that this item is contained in.
21  * An item can be contained in a number of relations and as daughters
22  * to other items. This class is used to keep track of all of these
23  * relationships. There may be many instances of item that reference
24  * the same shared ItemContents. 
25  */
26 public class Item implements Dumpable {
27     private Relation ownerRelation;
28     private ItemContents contents;
29     private Item parent;
30     private Item daughter;
31     private Item next;
32     private Item prev;
33
34
35     /**
36      * Creates an item. The item is coupled to a particular
37      * Relation. If shared contents is null a new sharedContents is
38      * created.
39      *
40      * @param relation the relation that owns this item
41      * @param sharedContents the contents that is shared with others.
42      *   If null, a new sharedContents is created.
43      */
44     public Item(Relation relation, ItemContents sharedContents) {
45         ownerRelation = relation;
46         if (sharedContents != null) {
47             contents = sharedContents;
48         } else {
49             contents = new ItemContents();
50         }
51         parent = null;
52         daughter = null;
53         next = null;
54         prev = null;
55
56
57         getSharedContents().addItemRelation(relation.getName(), this);
58     }
59
60     /**
61      * Finds the item in the given relation that has the same shared
62      * contents.
63      *
64      * @param relationName the relation of interest
65      *
66      * @return the item as found in the given relation or null if not
67      *   found
68      */
69     public Item getItemAs(String relationName) {
70         return getSharedContents().getItemRelation(relationName);
71     }
72
73
74     /**
75      * Retrieves the owning Relation.
76      *
77      * @return the relation that owns this item
78      */
79     public Relation getOwnerRelation() {
80         return ownerRelation;
81     }
82
83     /**
84      * Retrieves the shared contents for this item.
85      *
86      * @return the shared item contents
87      */
88     public ItemContents getSharedContents() {
89         return contents;
90     }
91
92     /**
93      * Determines if this item has daughters.
94      *
95      * @return true if this item has daughters
96      */
97     public boolean hasDaughters() {
98         return daughter != null;
99     }
100
101     /**
102      * Retrieves the first daughter of this item.
103      *
104      * @return the first daughter or null if none
105      */
106     public Item getDaughter() {
107         return daughter;
108     }
109     
110     /**
111      * Retrieves the Nth daughter of this item.
112      *
113      * @param which the index of the daughter to return
114      *
115      * @return the Nth daughter or null if none at the given index
116      */
117     public Item getNthDaughter(int which) {
118         Item d = daughter;
119         int count = 0;
120         while (count++ != which && d != null) {
121             d = d.next;
122         }
123         return d;
124     }
125
126     /**
127      * Retrieves the last daughter of this item.
128      *
129      * @return the last daughter or null if none at the given index
130      */
131     public Item getLastDaughter() {
132         Item d = daughter;
133         if (d == null) {
134             return null;
135         }
136         while (d.next != null) {
137             d = d.next;
138         }
139         return d;
140     }
141
142     /**
143      * Adds the given item as a daughter to this item.
144      *
145      * @param item the new daughter
146      */
147     public Item addDaughter(Item item) {
148         Item newItem;
149         ItemContents  contents;
150
151         Item p = getLastDaughter();
152
153         if (p != null) {
154             newItem = p.appendItem(item);
155         } else {
156             if (item == null) {
157                 contents = new ItemContents();
158             } else {
159                 contents = item.getSharedContents();
160             }
161             newItem = new Item(getOwnerRelation(), contents);
162             newItem.parent = this;
163             daughter = newItem;
164         }
165         return newItem;
166     }
167
168     /**
169      * Creates a new Item, adds it as a daughter to this item
170      * and returns the new item.
171      *
172      * @return the newly created item that was added as a daughter
173      */
174     public Item createDaughter() {
175         return addDaughter(null);
176     }
177
178
179     /**
180      * Returns the parent of this item.
181      *
182      * @return the parent of this item
183      */
184     public Item getParent() {
185         Item n;
186         for (n = this; n.prev != null; n = n.prev) {
187             if (n == null) {
188                 return null;
189             }
190         }
191         return n.parent;
192     }
193
194
195     /**
196      * Sets the parent of this item.
197      *
198      * @param parent the parent of this item
199      */
200     /*
201     private void setParent(Item parent) {
202         this.parent = parent;
203     }
204     */
205
206     /**
207      * Returns the utterance associated with this item.
208      *
209      * @return the utterance that contains this item
210      */
211     public Utterance getUtterance() {
212         return getOwnerRelation().getUtterance();
213     }
214
215     /**
216      * Returns the feature set of this item.
217      *
218      * @return the feature set of this item
219      */
220     public FeatureSet getFeatures() {
221         return getSharedContents().getFeatures();
222     }
223
224     /**
225      * Dumps out this item to the given output stream.
226      *
227      * @param out where to send the output
228      * @param pad the leading whitspace
229      */
230     public void dump(PrintWriter out, int pad, String title) {
231         String itemName = title + ":" + toString();
232         getFeatures().dump(out, pad, itemName);
233         if (hasDaughters()) {
234             Item daughter = getDaughter();
235             while (daughter != null) {
236                 daughter.dump(out, pad + 8, "d");
237                 daughter = daughter.next;
238             }
239         }
240     }
241
242     /**
243      * Finds the feature by following the given path.
244      * Path is a string of
245      *   ":" or "." separated strings with the following interpretations:
246      * <ul>
247      * <li> n - next item
248      * <li> p - previous item
249      * <li> parent - the parent
250      * <li> daughter - the daughter
251      * <li> daughter1 - same as daughter
252      * <li> daughtern - the last daughter
253      * <li> R:relname - the item as found in the given relation 'relname'
254      * </ul>
255      * The last element of the path will be interpreted as a
256      * voice/language specific feature function (if present) or an
257      * item feature name. If the feature function exists it will be
258      * called with the item specified by the path, otherwise, a
259      * feature will be retrieved with the given name. If neither exist
260      * than a String "0" is returned.
261      *
262      * @param pathAndFeature the path to follow
263      */
264     public Object findFeature(String pathAndFeature) {
265         int lastDot;
266         String feature;
267         String path;
268         Item item;
269         FeatureProcessor fp;
270
271         Voice voice = getOwnerRelation().getUtterance().getVoice();
272         Object results = null;
273         
274
275         lastDot = pathAndFeature.lastIndexOf(".");
276         // string can be of the form "p.feature" or just "feature"
277
278         if (lastDot == -1) {
279             feature = pathAndFeature;
280             path = null;
281         } else {
282             feature = pathAndFeature.substring(lastDot + 1);
283             path = pathAndFeature.substring(0, lastDot);
284         }
285
286
287         item = findItem(path);
288         if (item != null) {
289             fp = voice.getFeatureProcessor(feature);
290
291             if (fp != null) {
292                 try {
293                     results = fp.process(item);
294                 } catch (ProcessException pe) {
295                     System.err.println("Trouble while processing " +
296                                        fp.toString());
297                 }
298             } else {
299                 results = item.getFeatures().getObject(feature);
300             }
301         }
302         results =  (results == null)
303                 ? "0" 
304                 : results;
305
306     // System.out.println("FI " + pathAndFeature + " are " + results);
307
308         return results;
309     }
310
311     /** 
312      * Finds the item specified by the given path.
313      
314      * Path is a string of ":" or
315      * "." separated strings with the following interpretations:
316      * <ul>
317      * <li> n - next item
318      * <li> p - previous item
319      * <li> parent - the parent
320      * <li> daughter - the daughter
321      * <li> daughter1 - same as daughter
322      * <li> daughtern - the last daughter
323      * <li> R:relname - the item as found in the given relation 'relname'
324      * </ul>
325      * If the given path takes us outside of the bounds of the item
326      * graph, then list access exceptions will be thrown.
327      *
328      * @param path the path to follow
329      *
330      * @return the item at the given path
331      */
332     public Item findItem(String path) {
333         Item pitem = this;
334         StringTokenizer tok;
335         
336         if (path == null) {
337             return this;
338         }
339
340         tok = new StringTokenizer(path, ":.");
341
342         while (pitem != null && tok.hasMoreTokens()) {
343             String token = tok.nextToken();
344             if (token.equals("n")) {
345                 pitem = pitem.getNext();
346             } else if (token.equals("p")) {
347                 pitem = pitem.getPrevious();
348             } else if (token.equals("nn")) {
349                 pitem = pitem.getNext();
350                 if (pitem != null) {
351                     pitem = pitem.getNext();
352                 }
353             } else if (token.equals("pp")) {
354                 pitem = pitem.getPrevious();
355                 if (pitem != null) {
356                     pitem = pitem.getPrevious();
357                 }
358             } else if (token.equals("parent")) {
359                 pitem = pitem.getParent();
360             } else if (token.equals("daughter") || token.equals("daughter1")) {
361                 pitem = pitem.getDaughter();
362             } else if (token.equals("daughtern")) {
363                 pitem = pitem.getLastDaughter();
364             } else if (token.equals("R")) {
365                 String relationName = tok.nextToken();
366                 pitem = pitem.getSharedContents().getItemRelation(relationName);
367             } else {
368                 System.out.println("findItem: bad feature " + token +
369                         " in " + path);
370             }
371         }
372         return pitem;
373     }
374
375
376     /**
377      * Gets the next item in this list.
378      *
379      * @return the next item or null
380      */
381     public Item getNext() {
382         return next;
383     }
384
385
386     /**
387      * Gets the previous item in this list.
388      *
389      * @return the previous item or null
390      */
391     public Item getPrevious() {
392         return prev;
393     }
394
395
396     /**
397      * Appends an item in this list after this item.
398      *
399      * @param originalItem new item has shared contents with this 
400      *   item (or * null)
401      *
402      * @return the newly appended item
403      */
404     public Item appendItem(Item originalItem) {
405         ItemContents contents;
406         Item newItem;
407
408         if (originalItem == null) {
409             contents = null;
410         } else {
411             contents = originalItem.getSharedContents();
412         }
413
414         newItem = new Item(getOwnerRelation(), contents);
415         newItem.next = this.next;
416         if (this.next != null) {
417             this.next.prev = newItem;
418         }
419
420         attach(newItem);
421
422         if (this.ownerRelation.getTail() == this) {
423                 this.ownerRelation.setTail(newItem);
424         }
425         return newItem;
426     }
427
428     /**
429      * Attaches/appends an item to this one.
430      *
431      * @param item the item to append
432      */
433     void attach(Item item) {
434         this.next = item;
435         item.prev = this;
436     }
437
438     /**
439      * Prepends an item in this list before this item.
440      *
441      * @param originalItem new item has shared contents with this 
442      *   item (or * null)
443      *
444      * @return the newly appended item
445      */
446     public Item prependItem(Item originalItem) {
447         ItemContents contents;
448         Item newItem;
449
450         if (originalItem == null) {
451             contents = null;
452         } else {
453             contents = originalItem.getSharedContents();
454         }
455
456         newItem = new Item(getOwnerRelation(), contents);
457         newItem.prev = this.prev;
458         if (this.prev != null) {
459             this.prev.next = newItem;
460         }
461         newItem.next = this;
462         this.prev = newItem;
463         if (this.parent != null) {
464             this.parent.daughter = newItem;
465             newItem.parent = this.parent;
466             this.parent = null;
467         }
468         if (this.ownerRelation.getHead() == this) {
469             this.ownerRelation.setHead(newItem);
470         }
471         return newItem;
472     }
473
474
475
476     // Inherited from object
477     public String toString() {
478         // if we have a feature called 'name' use that
479         // otherwise fall back on the default.
480         String name = getFeatures().getString("name");
481         if (name == null) {
482             name = "";
483         }
484         return name;
485     }
486
487     /**
488      * Determines if the shared contents of the two items are the same.
489      *
490      * @param otherItem the item to compare
491      *
492      * @return true if the shared contents are the same
493      */
494     public boolean equalsShared(Item otherItem) {
495         if (otherItem == null) {
496             return false;
497         } else {
498             return getSharedContents().equals(otherItem.getSharedContents());
499         }
500     }
501 }