001 /* 002 * LAPIS lightweight structured text processing system 003 * 004 * Copyright (C) 1998-2002 Carnegie Mellon University, 005 * Copyright (C) 2003 Massachusetts Institute of Technology. 006 * All rights reserved. 007 * 008 * This library is free software; you can redistribute it 009 * and/or modify it under the terms of the GNU General 010 * Public License as published by the Free Software 011 * Foundation, version 2. 012 * 013 * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/ 014 */ 015 016 package lapis; 017 018 import java.util.*; 019 import lapisx.util.*; 020 import lapisx.enum.*; 021 import lapisx.iterator.*; 022 023 /** 024 * A PatternLibrary represents a collection of {@link Parser Parsers} and named {@link Pattern Patterns}. 025 * <p> 026 * A PatternLibrary may have a parent library, from which it inherits parsers and patterns. The collection represented by 027 * a particular library is the union of the parsers and patterns in all of its ancestors, up to the root (global) pattern library. 028 * <p> 029 * At present, LAPIS uses only two levels in this hierarchy. At the root is the global pattern library, a singleton, which 030 * can be obtained with {@link #getGlobalLibrary() getGlobalLibrary}. Its children are the document-specific pattern 031 * libraries, one per Document. Each document-specific library has the global pattern library as its parent. 032 * <p> 033 * The primary purpose of the PatternLibrary is to map between pattern names and Pattern objects. 034 * A client that wants to look up a named pattern (such as Word) normally follows the following 035 * procedure: 036 * <ul> 037 * <li> Fetch the document-specific library using {@link #getDocumentLibrary(Document) getDocumentLibrary}. 038 * <li> Use {@link #lookup(String) lookup} to translate the pattern name (e.g., "Word") into its fully-qualified canonical name (".English.Word") 039 * <li> Use {@link #get(String) get} to map the canonical name into a Pattern object 040 * </ul> 041 * Patterns can be stored in the pattern library with {@link #put(String,Pattern) put()}. Patterns that could apply to any document 042 * should be stored in the global pattern library. Patterns that are meaningful only for one document should be put in the 043 * document-specific library. 044 * <p> 045 * The other responsibility of the PatternLibrary is to manage a set of Parsers. Whenever a parser is added 046 * with {@link #addParser(Parser) addParser()}, the pattern library calls Parser.bind(PatternLibrary) to direct the parser to add its named Patterns to this 047 * library. Whenever a document-specific library is fetched, Parser.bind(PatternLibrary, Document) will be 048 * called on any parsers that haven't seen the document yet, so that parsers with document-specific patterns can add 049 * them to the document-specific library. 050 */ 051 public class PatternLibrary implements Cloneable { 052 053 public static Debug debug = Debug.QUIET; 054 055 /** 056 * Global pattern library. 057 */ 058 private static PatternLibrary rootLibrary = null; 059 060 /** 061 * String used as key in properties of Document; value is the 062 * doc-specific PatternLibrary 063 */ 064 private static String PatternLibraryProperty = "lapis.PatternLibrary"; 065 066 067 /** 068 * Dummy pattern returned by get() for an unbound name. 069 */ 070 private static final Pattern EMPTY_PATTERN = new Pattern() { 071 public RegionSet match(Document doc) { 072 return RegionSet.EMPTY; 073 } 074 }; 075 076 /** 077 * This library's parent, or null if this is the global library. 078 */ 079 private PatternLibrary parentLibrary = null; 080 081 /** 082 * Maps final name component (CaselessString) -> canonical name (String) or Vector of canonical names. 083 * Hidden identifiers are not included in this mapping. 084 */ 085 private Hashtable names = new Hashtable(); 086 087 /** 088 * Maps canonical name (CaselessString starting with ".") -> Pattern 089 */ 090 private Hashtable patterns = new Hashtable(); 091 092 /** 093 * Collection of parsers. 094 */ 095 private Set parsers = new HashSet (); 096 097 /** 098 * Version number. Incremented whenever a new parser is added, 099 * so that getDocumentLibrary() can tell that it needs to ask the parsers 100 * to rebind document-specific patterns. 101 */ 102 private long version = 0L; 103 104 /** 105 * Collection of LibraryListeners. 106 */ 107 private Set listeners = new SafeIteratorSet (); 108 109 110 /** 111 * Suppress default no-arg constructor. PatternLibrary instances 112 * can only be obtained by getGlobalLibrary() and getDocumentLibrary(). 113 */ 114 private PatternLibrary() { 115 } 116 117 /** 118 * Get the global pattern library, which is a singleton shared by all documents. 119 * Patterns that should be available to all documents should be stored in this 120 * library. Document-specific patterns should be stored in a document's library 121 * (see getDocumentLibrary). For looking up patterns, it's best 122 * to use getDocumentLibrary(), since a document library automatically falls back 123 * to the global library if a pattern isn't found. 124 */ 125 public static PatternLibrary getGlobalLibrary() { 126 if (rootLibrary == null) 127 rootLibrary = new PatternLibrary(); 128 return rootLibrary; 129 } 130 131 /** 132 * Get a document's pattern library. 133 * @param doc Document to be searched 134 * @return document-specific pattern library, which includes not only 135 * document-specific patterns but also patterns from the global library. 136 */ 137 public static PatternLibrary getDocumentLibrary(Document doc) { 138 PatternLibrary globalLib = getGlobalLibrary(); 139 140 if (doc == null) 141 return globalLib; 142 143 PatternLibrary docLib = 144 (PatternLibrary) doc.getProperty(PatternLibraryProperty); 145 146 if (docLib == null) { 147 docLib = new PatternLibrary(); 148 docLib.parentLibrary = getGlobalLibrary(); 149 doc.putProperty(PatternLibraryProperty, docLib); 150 } 151 152 if (docLib.version != globalLib.version) { 153 // tell all parsers in the global library to bind themselves into the new library 154 docLib.version = globalLib.version; 155 for (Iterator g = globalLib.parsers (); g.hasNext (); ) { 156 Parser parser = (Parser) g.next (); 157 parser.bind (docLib, doc); 158 } 159 } 160 161 return docLib; 162 } 163 164 /** 165 * Get this library's parent library. 166 * @return this library's parent, or null if this is the global library. 167 */ 168 public PatternLibrary getParent () { 169 return parentLibrary; 170 } 171 172 /** 173 * Add a parser to this library. 174 * @param parser Parser to add 175 * @effects Adds parser to this library's parser collection, and calls the parser's {@link Parser#bind(PatternLibrary) bind} method. 176 */ 177 public synchronized void addParser (Parser parser) { 178 if (parsers.add (parser)) { 179 ++version; 180 parser.bind (this); 181 fireAddedParserEvent (parser); 182 } 183 } 184 185 /** 186 * Remove a parser from this library. 187 * @param parser Parser to remove 188 * @effects Removes parser from this library's parser collection. 189 */ 190 public synchronized void removeParser (Parser parser) { 191 parsers.remove (parser); 192 // TODO: tell parser to unbind its patterns 193 } 194 195 /** 196 * Get the parsers in this library and all of its ancestors. 197 * @return iterator that yields the parsers in this library and all of its ancestors. 198 */ 199 public Iterator parsers () { 200 Iterator g = parsers.iterator (); 201 if (parentLibrary != null) 202 g = new ConcatIterator (g, parentLibrary.parsers ()); 203 return g; 204 } 205 206 /** 207 * Clears all patterns and parsers from this pattern library. Leaves parent 208 * unchanged. 209 */ 210 public synchronized void clear() { 211 fireClearEvent(); 212 names.clear(); 213 patterns.clear(); 214 parsers.clear (); 215 } 216 217 218 /** 219 * Get the pattern corresponding to a name. 220 * @param name Canonical name of a pattern, e.g. ".English.Word" 221 * @return Pattern bound to the name in this library. If the name is not bound in this library, recursively 222 * tries to get the named pattern from this library's parent library. If the name is not bound in any of this library's 223 * ancestors, then returns a dummy Pattern object that matches the empty region set on every document. 224 */ 225 public synchronized Pattern get(String name) { 226 Object docObj = patterns.get(makePatternsKey(name)); 227 if (docObj instanceof Pattern) 228 return (Pattern) docObj; 229 else if (parentLibrary != null) 230 return parentLibrary.get(name); 231 else { 232 if (parentLibrary == null) { 233 return EMPTY_PATTERN; 234 } else { 235 return parentLibrary.get(name); 236 } 237 } 238 } 239 240 /** 241 * Assign a pattern to a name in this library. 242 * @param name Canonical name for pattern, e.g. ".English.Word" 243 * @param pattern Pattern to bind to name 244 * @effects Assigns name to pattern in this library. Doesn't affect this library's parent library. 245 */ 246 public synchronized void put(String name, Pattern pattern) { 247 debug.println ("binding " + name + " to " + pattern.getClass ()); 248 CaselessString rskey = makePatternsKey(name); 249 250 if (patterns.put(rskey, pattern) != null) 251 return; // name already existed 252 253 // Add base name -> canonical name mapping to names hash 254 if (!isHidden(name)) { 255 CaselessString nkey = makeNamesKey(name); 256 if (name.startsWith(".")) 257 name = name.substring(1); 258 Object obj = names.get(nkey); 259 if (obj == null) 260 names.put(nkey, name); 261 else { 262 Vector v; 263 if (obj instanceof String) { 264 v = new Vector(); 265 v.addElement(obj); 266 names.put(nkey, v); 267 } else 268 v = (Vector) obj; 269 270 v.addElement(name); 271 } 272 } 273 274 //this preserves invariant of NO duplicates 275 // if (parentLibrary != null) 276 // this.parentLibrary.recursiveRemove(name); 277 278 fireAddedEvent(name); 279 } 280 281 /** 282 * Rename a pattern. 283 * @param oldName old canonical name for pattern 284 * @param newName new canonical name 285 * @effects Locates pattern named oldName by searching for it, first in this library, and failing that, in this library's 286 * ancestors. In <b>every</b> library where oldName is found, the assignment to oldName is removed, and the pattern is 287 * rebound to newName instead. 288 * @return true if oldName was found in this library or one of its ancestors; false if not. 289 */ 290 public synchronized boolean rename(String oldName, String newName) { 291 boolean found = patterns.containsKey(makePatternsKey(oldName)); 292 if (found) { 293 Pattern pat = get(oldName); 294 remove(oldName); 295 put(newName, pat); 296 } 297 298 if (parentLibrary != null) 299 found |= parentLibrary.rename(oldName, newName); 300 301 return found; 302 } 303 304 /** 305 * Remove named pattern from this library and all of its ancestors. 306 * @param name Canonical name of pattern to remove 307 * @effects Removes pattern from this library and all its ancestors, if found. 308 */ 309 public synchronized void remove (String name) { 310 // First delete name -> pattern mapping 311 if (patterns.remove (makePatternsKey(name)) != null) { 312 313 // Then delete basename -> name mapping from names hash 314 if (!isHidden(name)) { 315 CaselessString key = makeNamesKey(name); 316 Object obj = names.get(key); 317 if (obj == null); // name name not found 318 else { 319 if (obj instanceof String) 320 names.remove(key); 321 else { 322 Vector v = (Vector) obj; 323 v.removeElement(name); 324 if (v.isEmpty()) 325 names.remove(key); 326 } 327 } 328 } 329 330 fireRemovedEvent(name); 331 } 332 333 // finally, delete from ancestors 334 if (parentLibrary != null) 335 parentLibrary.remove (name); 336 } 337 338 /** 339 * Test whether this library or one of its ancestors contains a named pattern. 340 * @param name Canonical name of pattern to test for 341 * @return true if name is found in this library or one of its ancestors; false if not. 342 */ 343 public synchronized boolean contains(String name) { 344 return patterns.containsKey(makePatternsKey(name)) 345 || (parentLibrary != null && parentLibrary.contains(name)); 346 //getDefaultLibrary().patterns.containsKey (makePatternsKey (name)); 347 } 348 349 /** 350 * Convert a pattern name into a canonical name, relative to the top-level namespace. See {@link #lookup(String,String) lookup(String,String)} for 351 * more details. 352 * @param name Relative or canonical name for pattern, e.g. "Word". 353 * @return canonical name for the pattern (e.g. ".English.Word"), assuming only one named pattern matches in this 354 * library or any of its ancestors. 355 * @throws LabelNotFoundException if no names in the pattern library or its ancestors match the short pattern name. 356 * @throws LabelAmbiguousException if more than one name in the pattern library or its ancestors matches the short name. 357 */ 358 public synchronized String lookup(String name) 359 throws LabelNotFoundException, LabelAmbiguousException { 360 return lookup(name, ""); 361 } 362 363 /** 364 * Convert a short pattern name into a canonical name, given a preferred namespace. 365 * <p> 366 * A canonical pattern name begins with a period (e.g. ".English.Word.AllCapsWord"). 367 * A relative pattern name is any proper suffix (on period boundaries) of a canonical name, e.g. "AllCapsWord", 368 * "Word.AllCapsWord", and "English.Word.AllCapsWord". 369 * <p> 370 * This method resolves a name relative to the preferred namespace, using the following rules: 371 * <ol> 372 * <li>If the name is already canonical, and it appears in this library or its ancestors, it is returned unchanged. 373 * <li>If namespace+"."+name is found in this library or its ancestors, then namespace+"."+name is returned. 374 * <li>Otherwise, all canonical names in this library or its ancestors that have name as a proper suffix are returned. 375 * </ol> 376 * If exactly one canonical name matches step #3, then it is returned. If fewer than one or more than one canonical names 377 * match, then an exception is thrown. 378 * @param name Relative or canonical name for pattern, e.g. "Word". 379 * @param namespace Preferred namespace for relative name lookups. Must be canonical, i.e. starting with ".". 380 * May be null if no namespace should be preferred (in which case step #2 is skipped above). 381 * @return canonical name for the pattern (e.g. ".English.Word"), assuming only one named pattern matches in this 382 * library or any of its ancestors. 383 * @throws LabelNotFoundException if no names in the pattern library or its ancestors match the short pattern name. 384 * @throws LabelAmbiguousException if more than one name in the pattern library or its ancestors matches the short name. 385 */ 386 public synchronized String lookup(String name, String namespace) 387 throws LabelNotFoundException, LabelAmbiguousException { 388 389 // Names starting with . are already canonical -- strip the period 390 // and look it up in the patterns hash 391 if (name.startsWith(".")) { 392 String canonical = name.substring(1); 393 // if (patterns.containsKey (makePatternsKey (canonical))) 394 if (this.contains(canonical)) 395 return canonical; 396 else { 397 // throw new LabelNotFoundException (name); 398 399 if (parentLibrary == null) 400 throw new LabelNotFoundException(name); 401 else 402 return parentLibrary.lookup(name, namespace); 403 } 404 405 } 406 407 // First look for name in specified namespace. 408 String specific = 409 (namespace != null && namespace.length() > 0) 410 ? namespace + "." + name 411 : name; 412 // if (patterns.containsKey (makePatternsKey (specific))) 413 if (this.contains(specific)) 414 return specific; 415 416 // Then search for the name in all namespaces. 417 // If it appears exactly once, return that one; otherwise 418 // throw LabelAmbiguous or LabelNotFound. 419 420 // Look it up in names mapping to get 0 or more canonical names 421 // ending with that base 422 423 Object obj = names.get(makeNamesKey(name)); 424 425 /** 426 427 TOTAL HACK! 428 429 Object docSpecObj = names.get (makeNamesKey (name)); 430 Object genObj = getDefaultLibrary().names.get(makeNamesKey (name)); 431 if (this == getDefaultLibrary()) genObj = null; 432 433 Object obj = null; 434 if (docSpecObj instanceof String && genObj == null) { 435 obj = docSpecObj; 436 } 437 if (genObj instanceof String && docSpecObj == null) { 438 obj = genObj; 439 } 440 if (docSpecObj instanceof Vector && 441 genObj instanceof Vector) { 442 obj = new Vector((Vector)docSpecObj); 443 ((Vector)obj).addAll((Vector)genObj); 444 } 445 if (docSpecObj instanceof Vector && !(genObj instanceof Vector)) { 446 obj = new Vector((Vector)docSpecObj); 447 if (genObj instanceof String) { 448 ((Vector)obj).add(genObj); 449 } 450 } 451 if (genObj instanceof Vector && !(docSpecObj instanceof Vector)) { 452 obj = new Vector((Vector)genObj); 453 if (docSpecObj instanceof String) { 454 ((Vector)obj).add(docSpecObj); 455 } 456 } 457 */ 458 459 if (obj instanceof String) { 460 String canonical = (String) obj; 461 462 if (keyEndsWith(canonical, name)) 463 return canonical; 464 else { 465 //throw new LabelNotFoundException (name); 466 if (parentLibrary == null) 467 throw new LabelNotFoundException(name); 468 else 469 return parentLibrary.lookup(name, namespace); 470 } 471 } else if (obj instanceof Vector) { 472 Vector v = (Vector) obj; 473 String match = null; 474 Vector multipleMatches = null; 475 476 for (Enumeration e = v.elements(); e.hasMoreElements();) { 477 String canonical = (String) e.nextElement(); 478 if (keyEndsWith(canonical, name)) { 479 if (multipleMatches != null || match != null) { 480 if (multipleMatches == null) { 481 multipleMatches = new Vector(); 482 multipleMatches.addElement(match); 483 } 484 multipleMatches.addElement(canonical); 485 } else if (match == null) 486 match = canonical; 487 } 488 } 489 490 if (match == null) { 491 // throw new LabelNotFoundException (name); 492 if (parentLibrary == null) 493 throw new LabelNotFoundException(name); 494 else 495 return parentLibrary.lookup(name, namespace); 496 } else if (multipleMatches != null) 497 throw new LabelAmbiguousException(name, multipleMatches); 498 else 499 return match; 500 } else { 501 //throw new LabelNotFoundException (name); 502 if (parentLibrary == null) 503 throw new LabelNotFoundException(name); 504 else 505 return parentLibrary.lookup(name, namespace); 506 } 507 } 508 509 /** 510 * Get the visible names in the library (i.e., names that don't 511 * begin with an underscore). 512 * @return Enumeration of Strings, the canonical names of the 513 * visible identifiers in this library and its ancestors 514 */ 515 public Enumeration visibleNames() { 516 Enumeration e = null; 517 if (parentLibrary == null) 518 e = patterns.keys(); 519 else 520 e = 521 new ConcatEnumeration( 522 patterns.keys(), 523 parentLibrary.visibleNames()); 524 525 return new FilteredEnumeration(e) { 526 public void transform(Object o) { 527 String name = o.toString(); 528 if (!isHidden(name)) 529 yield(name); 530 } 531 }; 532 } 533 534 /** 535 * Get all names in this library and its ancestors (both visible and hidden). 536 * @return Enumeration of Strings, the canonical names of the 537 * identifiers in this library and its ancestors 538 */ 539 public Enumeration allNames() { 540 Enumeration e = null; 541 if (parentLibrary == null) 542 e = patterns.keys(); 543 else 544 e = 545 new ConcatEnumeration( 546 patterns.keys(), 547 parentLibrary.visibleNames()); 548 549 return new FilteredEnumeration(e) { 550 public void transform(Object o) { 551 yield(o.toString()); 552 } 553 }; 554 } 555 556 /** 557 * Get number of named patterns in this library and its ancestors. 558 * @return number of names that would be enumerated by {@link #allNames}. 559 */ 560 public int getSize() { 561 if (parentLibrary == null) 562 return patterns.size(); 563 else 564 return patterns.size() + this.parentLibrary.getSize(); 565 } 566 567 /** 568 * Get shortest unambiguous name for a canonical name. 569 * @param canonical canonical pattern name, e.g. ".English.Word.AllCapsWord" 570 * @return shortest unambiguous relative name for this pattern, e.g. "AllCapsWord" 571 */ 572 public synchronized String getShortestName(String canonical) { 573 if (canonical.startsWith(".")) 574 canonical = canonical.substring(1); 575 576 for (int i = canonical.lastIndexOf('.'); 577 i != -1; 578 i = canonical.lastIndexOf('.', i - 1)) { 579 try { 580 String abbr = canonical.substring(i + 1); 581 if (lookup(abbr).equalsIgnoreCase(canonical)) 582 return abbr; 583 } catch (LookupException e) { 584 //added - MN 585 if (parentLibrary != null) 586 parentLibrary.getShortestName(canonical); 587 } 588 } 589 590 return canonical; 591 } 592 593 /** 594 * Extract the last component of a name, i.e., 595 * the string following the last period. 596 * @param name Pattern name (either canonical or relative), e.g. "English.Word" 597 * @return component of name following last period, e.g. "Word" 598 */ 599 public static String getBasename(String name) { 600 if (name.startsWith(".")) 601 name = name.substring(1); 602 603 int lastPeriod = name.lastIndexOf('.'); 604 return (lastPeriod == -1) ? name : name.substring(lastPeriod + 1); 605 } 606 607 /** 608 * Test whether name is a hidden identifier. 609 * @param name Identifier to test 610 * @return true iff name's last component (its base name) 611 * starts with an underscore 612 */ 613 public static boolean isHidden(String name) { 614 return getBasename(name).startsWith("_"); 615 } 616 617 static boolean keyEndsWith(String key, String suffix) { 618 int keyLen = key.length(); 619 int suffixLen = suffix.length(); 620 int pos = keyLen - suffixLen; 621 return pos >= 0 622 && key.substring(pos).equalsIgnoreCase(suffix) 623 && (pos == 0 || key.charAt(pos - 1) == '.'); 624 } 625 626 static CaselessString makePatternsKey(String name) { 627 if (name.startsWith(".")) 628 name = name.substring(1); 629 return new CaselessString(name); 630 } 631 632 static CaselessString makeNamesKey(String name) { 633 return new CaselessString(getBasename(name)); 634 } 635 636 /** 637 * Base class for exceptions thrown by lookup(). 638 */ 639 public static class LookupException extends Exception { 640 String name; 641 public LookupException(String name, String message) { 642 super(message); 643 this.name = name; 644 } 645 public String getName() { 646 return name; 647 } 648 } 649 650 /** 651 * Exception thrown when lookup() finds no matching pattern names. 652 */ 653 public static class LabelNotFoundException extends LookupException { 654 public LabelNotFoundException(String name) { 655 super(name, "there is no pattern named " + name.toUpperCase()); 656 } 657 } 658 659 /** 660 * Exception thrown when lookup() finds multiple matching pattern names. 661 */ 662 public static class LabelAmbiguousException extends LookupException { 663 Vector choices; 664 public LabelAmbiguousException(String name, Vector choices) { 665 super(name, "pattern name " + name.toUpperCase() + " is ambiguous"); 666 this.choices = choices; 667 } 668 public String[] getChoices() { 669 String[] result = new String[choices.size()]; 670 choices.copyInto(result); 671 return result; 672 } 673 } 674 675 // 676 // Event listeners 677 // 678 679 /** 680 * Listener notified when patterns are added and removed from library. 681 */ 682 public static interface LibraryListener { 683 /** 684 * Called when a pattern is bound into the pattern library using put(). 685 * @param lib PatternLibrary that changed 686 * @param name name that was bound 687 */ 688 public void patternAdded(PatternLibrary lib, String name); 689 /** 690 * Called when a pattern name is removed from the pattern library by remove() or clear(). 691 * @param lib PatternLibrary that changed 692 * @param name name that was removed 693 */ 694 public void patternRemoved(PatternLibrary lib, String name); 695 /** 696 * Called when a parser is added to the pattern library using addParser(). 697 * @param lib PatternLibrary that changed 698 * @param parser Parser that was added 699 */ 700 public void parserAdded (PatternLibrary lib, Parser parser); 701 } 702 703 /** 704 * Add a listener to this library. The listener receives events that affect this library and all its ancestors. 705 * @param listener Listener to receive library events 706 * @effects Registers listener for future events from this library and its ancestors. 707 */ 708 public synchronized void addLibraryListener(LibraryListener listener) { 709 debug.println("adding library listener " + listener.getClass()); 710 listeners.add (listener); 711 if (parentLibrary != null) 712 parentLibrary.addLibraryListener (listener); 713 } 714 715 /** 716 * Removes a listener from this library and all its ancestors. 717 * @param listener Listener to stop receiving library events 718 * @effects Listener no longer receives events from this library or any of its ancestors. 719 */ 720 public synchronized void removeLibraryListener(LibraryListener listener) { 721 listeners.remove (listener); 722 if (parentLibrary != null) 723 parentLibrary.removeLibraryListener (listener); 724 } 725 726 private synchronized void fireAddedEvent(String name) { 727 debug.println("**** firing added event: " + name); 728 if (name.startsWith(".")) 729 name = name.substring(1); 730 731 for (Iterator g = listeners.iterator (); g.hasNext (); ) 732 ((LibraryListener) g.next ()).patternAdded (this, name); 733 } 734 735 private synchronized void fireRemovedEvent(String name) { 736 debug.println("PATTERN REMOVE:" + name); 737 if (name.startsWith(".")) 738 name = name.substring(1); 739 740 for (Iterator g = listeners.iterator (); g.hasNext (); ) 741 ((LibraryListener) g.next ()).patternRemoved (this, name); 742 } 743 744 private synchronized void fireAddedParserEvent (Parser parser) { 745 for (Iterator g = listeners.iterator (); g.hasNext (); ) 746 ((LibraryListener) g.next ()).parserAdded (this, parser); 747 } 748 749 private synchronized void fireClearEvent() { 750 debug.println("*** in fire clear event..."); 751 Iterator names = patterns.keySet().iterator(); 752 while (names.hasNext()) { 753 CaselessString temp = (CaselessString) names.next(); 754 debug.println("*** removing: " + temp); 755 fireRemovedEvent(temp.toString()); 756 } 757 } 758 }