001/** 002 * Portions Copyright 2001 Sun Microsystems, Inc. 003 * Portions Copyright 1999-2001 Language Technologies Institute, 004 * Carnegie Mellon University. 005 * All Rights Reserved. Use is subject to license terms. 006 * 007 * See the file "license.terms" for information on usage and 008 * redistribution of this file, and for a DISCLAIMER OF ALL 009 * WARRANTIES. 010 */ 011package com.sun.speech.freetts; 012 013import java.util.ArrayList; 014import java.util.HashMap; 015import java.util.List; 016import java.util.Map; 017import java.util.StringTokenizer; 018import java.util.logging.Level; 019import java.util.logging.Logger; 020 021import com.sun.speech.freetts.util.Utilities; 022 023 024/** 025 * Interface that Manages a feature or item path. Allows navigation 026 * to the corresponding feature or item. 027 * This class in controlled by the following system properties: 028 * 029 * <pre> 030 * com.sun.speech.freetts.interpretCartPaths - default false 031 * com.sun.speech.freetts.lazyCartCompile - default true 032 * </pre> 033 * com.sun.speech.freetts.interpretCartPaths 034 * 035 * Instances of this class will optionally pre-compile the paths. 036 * Pre-compiling paths reduces the processing time and objects needed 037 * to extract a feature or an item based upon a path. 038 */ 039public class PathExtractorImpl implements PathExtractor { 040 /** Logger instance. */ 041 private static final Logger LOGGER = 042 Logger.getLogger(PathExtractorImpl.class.getName()); 043 044 /** 045 * If this system property is set to true, paths will 046 * not be compiled. 047 */ 048 public final static String INTERPRET_PATHS_PROPERTY = 049 "com.sun.speech.freetts.interpretCartPaths"; 050 051 /** 052 * If this system property is set to true, CART feature/item 053 * paths will only be compiled as needed. 054 */ 055 public final static String LAZY_COMPILE_PROPERTY = 056 "com.sun.speech.freetts.lazyCartCompile"; 057 058 private final static boolean INTERPRET_PATHS = 059 Utilities.getProperty(INTERPRET_PATHS_PROPERTY, "false").equals("true"); 060 private final static boolean LAZY_COMPILE = 061 Utilities.getProperty(LAZY_COMPILE_PROPERTY, "true").equals("true"); 062 063 private String pathAndFeature; 064 private String path; 065 private String feature; 066 private Object[] compiledPath; 067 private boolean wantFeature = false; 068 069 /** 070 * Creates a path for the given feature. 071 */ 072 public PathExtractorImpl(String pathAndFeature, boolean wantFeature) { 073 this.pathAndFeature = pathAndFeature; 074 if (INTERPRET_PATHS) { 075 path = pathAndFeature; 076 return; 077 } 078 079 if (wantFeature) { 080 int lastDot = pathAndFeature.lastIndexOf("."); 081 // string can be of the form "p.feature" or just "feature" 082 083 if (lastDot == -1) { 084 feature = pathAndFeature; 085 path = null; 086 } else { 087 feature = pathAndFeature.substring(lastDot + 1); 088 path = pathAndFeature.substring(0, lastDot); 089 } 090 this.wantFeature = wantFeature; 091 } else { 092 this.path = pathAndFeature; 093 } 094 095 if (!LAZY_COMPILE) { 096 compiledPath = compile(path); 097 } 098 } 099 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 */ 250class 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}