001/** 002 * Portions Copyright 2001-2003 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.io.PrintWriter; 014import java.util.StringTokenizer; 015 016/** 017 * Represents a node in a Relation. Items can have shared contents but 018 * each item has its own set of Daughters. The shared contents of an 019 * item (represented by ItemContents) includes the feature set for the 020 * item and the set of all relations that this item is contained in. 021 * An item can be contained in a number of relations and as daughters 022 * to other items. This class is used to keep track of all of these 023 * relationships. There may be many instances of item that reference 024 * the same shared ItemContents. 025 */ 026public class Item implements Dumpable { 027 private Relation ownerRelation; 028 private ItemContents contents; 029 private Item parent; 030 private Item daughter; 031 private Item next; 032 private Item prev; 033 034 035 /** 036 * Creates an item. The item is coupled to a particular 037 * Relation. If shared contents is null a new sharedContents is 038 * created. 039 * 040 * @param relation the relation that owns this item 041 * @param sharedContents the contents that is shared with others. 042 * If null, a new sharedContents is created. 043 */ 044 public Item(Relation relation, ItemContents sharedContents) { 045 ownerRelation = relation; 046 if (sharedContents != null) { 047 contents = sharedContents; 048 } else { 049 contents = new ItemContents(); 050 } 051 parent = null; 052 daughter = null; 053 next = null; 054 prev = null; 055 056 057 getSharedContents().addItemRelation(relation.getName(), this); 058 } 059 060 /** 061 * Finds the item in the given relation that has the same shared 062 * contents. 063 * 064 * @param relationName the relation of interest 065 * 066 * @return the item as found in the given relation or null if not 067 * found 068 */ 069 public Item getItemAs(String relationName) { 070 return getSharedContents().getItemRelation(relationName); 071 } 072 073 074 /** 075 * Retrieves the owning Relation. 076 * 077 * @return the relation that owns this item 078 */ 079 public Relation getOwnerRelation() { 080 return ownerRelation; 081 } 082 083 /** 084 * Retrieves the shared contents for this item. 085 * 086 * @return the shared item contents 087 */ 088 public ItemContents getSharedContents() { 089 return contents; 090 } 091 092 /** 093 * Determines if this item has daughters. 094 * 095 * @return true if this item has daughters 096 */ 097 public boolean hasDaughters() { 098 return daughter != null; 099 } 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}