upstream version 1.2.2
[debian/freetts] / com / sun / speech / freetts / PathExtractorImpl.java
1 /**
2  * Portions Copyright 2001 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.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.StringTokenizer;
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20
21 import com.sun.speech.freetts.util.Utilities;
22
23
24 /**
25  * Interface that Manages a feature or item path. Allows navigation
26  * to the corresponding feature or item.
27  * This class in controlled by the following system properties:
28  *
29  * <pre>
30  *   com.sun.speech.freetts.interpretCartPaths - default false
31  *   com.sun.speech.freetts.lazyCartCompile - default true
32  * </pre>
33  *   com.sun.speech.freetts.interpretCartPaths
34  *
35  * Instances of this class will optionally pre-compile the paths.
36  * Pre-compiling paths reduces the processing time and objects needed
37  * to extract a feature or an item based upon a path.
38  */
39 public class PathExtractorImpl implements PathExtractor {
40     /** Logger instance. */
41     private static final Logger LOGGER =
42         Logger.getLogger(PathExtractorImpl.class.getName());
43
44     /**
45       * If this system property is set to true, paths will
46       * not be compiled.
47       */
48     public final static String INTERPRET_PATHS_PROPERTY =
49         "com.sun.speech.freetts.interpretCartPaths";
50
51     /**
52      * If this system property is set to true, CART feature/item
53      * paths will only be compiled as needed.
54      */
55     public final static String LAZY_COMPILE_PROPERTY =
56         "com.sun.speech.freetts.lazyCartCompile";
57
58     private final static boolean INTERPRET_PATHS = 
59         Utilities.getProperty(INTERPRET_PATHS_PROPERTY, "false").equals("true");
60     private final static boolean LAZY_COMPILE  = 
61         Utilities.getProperty(LAZY_COMPILE_PROPERTY, "true").equals("true");
62
63     private String pathAndFeature;
64     private String path;
65     private String feature;
66     private Object[] compiledPath;
67     private boolean wantFeature = false;
68
69     /**
70      * Creates a path for the given feature.
71      */
72     public PathExtractorImpl(String pathAndFeature, boolean wantFeature) {
73         this.pathAndFeature = pathAndFeature;
74         if (INTERPRET_PATHS)  {
75             path = pathAndFeature;
76             return;
77         }
78     
79         if (wantFeature) {
80             int lastDot = pathAndFeature.lastIndexOf(".");
81             // string can be of the form "p.feature" or just "feature"
82
83             if (lastDot == -1) {
84                 feature = pathAndFeature;
85                 path = null;
86             } else {
87                 feature = pathAndFeature.substring(lastDot + 1);
88                 path = pathAndFeature.substring(0, lastDot);
89             }
90             this.wantFeature = wantFeature;
91         } else {
92             this.path = pathAndFeature;
93         }
94
95         if (!LAZY_COMPILE) {
96             compiledPath = compile(path);
97         }
98     }
99
100     /**
101      * Finds the item associated with this Path.
102      * @param item the item to start at
103      * @return the item associated with the path or null
104      */
105     public Item findItem(Item item) {
106
107         if (INTERPRET_PATHS) {
108             return item.findItem(path);
109         }
110
111         if (compiledPath == null) {
112             compiledPath = compile(path);
113         }
114
115         Item pitem = item;
116
117         for (int i = 0; pitem != null && i < compiledPath.length; ) {
118             OpEnum op = (OpEnum) compiledPath[i++];
119             if (op == OpEnum.NEXT) {
120                 pitem = pitem.getNext();
121             } else if (op == OpEnum.PREV) {
122                 pitem = pitem.getPrevious();
123             } else if (op == OpEnum.NEXT_NEXT) {
124                 pitem = pitem.getNext();
125                 if (pitem != null) {
126                     pitem = pitem.getNext();
127                 }
128             } else if (op == OpEnum.PREV_PREV) {
129                 pitem = pitem.getPrevious();
130                 if (pitem != null) {
131                     pitem = pitem.getPrevious();
132                 }
133             } else if (op == OpEnum.PARENT) {
134                 pitem = pitem.getParent();
135             } else if (op == OpEnum.DAUGHTER) {
136                 pitem = pitem.getDaughter();
137             } else if (op == OpEnum.LAST_DAUGHTER) {
138                 pitem = pitem.getLastDaughter();
139             } else if (op == OpEnum.RELATION) {
140                 String relationName = (String) compiledPath[i++];
141                 pitem = pitem.getSharedContents().getItemRelation(relationName);
142             } else {
143                 System.out.println("findItem: bad feature " + op +
144                         " in " + path);
145             }
146         }
147         return pitem;
148     }
149
150
151     /**
152      * Finds the feature associated with this Path.
153      * @param item the item to start at
154      * @return the feature associated or "0"  if the
155      * feature was not found.
156      */
157     public Object findFeature(Item item) {
158
159         if (INTERPRET_PATHS) {
160             return item.findFeature(path);
161         }
162
163         Item pitem = findItem(item);
164         Object results = null;
165         if (pitem != null) {
166                 if (LOGGER.isLoggable(Level.FINER)) {
167                     LOGGER.finer("findFeature: Item [" + pitem + "], feature '" 
168                             + feature + "'");
169                 }
170
171             FeatureProcessor fp =
172                 pitem.getOwnerRelation().getUtterance().
173                     getVoice().getFeatureProcessor(feature);
174
175             if (fp != null) {
176                 if (LOGGER.isLoggable(Level.FINER)) {
177                     LOGGER.finer(
178                             "findFeature: There is a feature processor for '" 
179                             + feature + "'");
180                 }
181                 try {
182                     results = fp.process(pitem);
183                 } catch (ProcessException pe) {
184                     LOGGER.severe("trouble while processing " + fp);
185                      throw new Error(pe);
186                 }
187             } else {
188                 results = pitem.getFeatures().getObject(feature);
189             }
190         }
191
192         results = (results == null) ? "0" : results;
193         if (LOGGER.isLoggable(Level.FINER)) {
194             LOGGER.finer("findFeature: ...results = '" + results + "'");
195         }
196         return results;
197     }
198
199
200     /**
201      * Compiles the given path into the compiled form
202      * @param path the path to compile
203      * @return the compiled form which is in the form
204      * of an array path traversal enums and associated strings
205      */
206     private Object[] compile(String path) {
207         List list = new ArrayList();
208
209         if (path == null) {
210             return list.toArray();
211         }
212
213         StringTokenizer tok = new StringTokenizer(path, ":.");
214
215         while (tok.hasMoreTokens()) {
216             String token = tok.nextToken();
217             OpEnum op = OpEnum.getInstance(token);
218             if (op == null) {
219                 throw new Error("Bad path compiled " + path);
220             } 
221
222             list.add(op);
223
224             if (op == OpEnum.RELATION) {
225                 list.add(tok.nextToken());
226             }
227         }
228         return list.toArray();
229     }
230
231     // inherited for Object
232
233     public String toString() {
234         return pathAndFeature;
235     }
236
237
238     // TODO: add these to the interface should we support binary
239     // files
240     /*
241     public void writeBinary();
242     public void readBinary();
243     */
244 }
245
246
247 /**
248  * An enumerated type associated with path operations.
249  */
250 class OpEnum {
251     static private Map map = new HashMap();
252
253     public final static OpEnum NEXT = new OpEnum("n");
254     public final static OpEnum PREV = new OpEnum("p");
255     public final static OpEnum NEXT_NEXT = new OpEnum("nn");
256     public final static OpEnum PREV_PREV = new OpEnum("pp");
257     public final static OpEnum PARENT = new OpEnum("parent");
258     public final static OpEnum DAUGHTER = new OpEnum("daughter");
259     public final static OpEnum LAST_DAUGHTER = new OpEnum("daughtern");
260     public final static OpEnum RELATION = new OpEnum("R");
261
262     private String name;
263
264     /**
265      * Creates a new OpEnum.. There is a limited
266      * set of OpEnums
267      * @param name the path name for this Enum
268      */
269     private OpEnum(String name) {
270         this.name = name;
271         map.put(name, this);
272     }
273
274     /**
275      * gets an OpEnum thats associated with
276      * the given name.
277      * @param name the name of the OpEnum of interest
278      */
279     public static OpEnum getInstance(String name) {
280         return (OpEnum) map.get(name);
281     }
282
283     // inherited from Object
284     public String toString() {
285         return name;
286     }
287 }