Frames | No Frames |
1: /* AbstractDocument.java -- 2: Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing.text; 40: 41: import java.io.PrintStream; 42: import java.io.Serializable; 43: import java.util.Dictionary; 44: import java.util.Enumeration; 45: import java.util.EventListener; 46: import java.util.Hashtable; 47: import java.util.Vector; 48: 49: import javax.swing.event.DocumentEvent; 50: import javax.swing.event.DocumentListener; 51: import javax.swing.event.EventListenerList; 52: import javax.swing.event.UndoableEditEvent; 53: import javax.swing.event.UndoableEditListener; 54: import javax.swing.tree.TreeNode; 55: import javax.swing.undo.AbstractUndoableEdit; 56: import javax.swing.undo.CompoundEdit; 57: import javax.swing.undo.UndoableEdit; 58: 59: /** 60: * An abstract base implementation for the {@link Document} interface. 61: * This class provides some common functionality for all <code>Element</code>s, 62: * most notably it implements a locking mechanism to make document modification 63: * thread-safe. 64: * 65: * @author original author unknown 66: * @author Roman Kennke (roman@kennke.org) 67: */ 68: public abstract class AbstractDocument implements Document, Serializable 69: { 70: /** The serialization UID (compatible with JDK1.5). */ 71: private static final long serialVersionUID = 6842927725919637215L; 72: 73: /** 74: * Standard error message to indicate a bad location. 75: */ 76: protected static final String BAD_LOCATION = "document location failure"; 77: 78: /** 79: * Standard name for unidirectional <code>Element</code>s. 80: */ 81: public static final String BidiElementName = "bidi level"; 82: 83: /** 84: * Standard name for content <code>Element</code>s. These are usually 85: * {@link LeafElement}s. 86: */ 87: public static final String ContentElementName = "content"; 88: 89: /** 90: * Standard name for paragraph <code>Element</code>s. These are usually 91: * {@link BranchElement}s. 92: */ 93: public static final String ParagraphElementName = "paragraph"; 94: 95: /** 96: * Standard name for section <code>Element</code>s. These are usually 97: * {@link DefaultStyledDocument.SectionElement}s. 98: */ 99: public static final String SectionElementName = "section"; 100: 101: /** 102: * Attribute key for storing the element name. 103: */ 104: public static final String ElementNameAttribute = "$ename"; 105: 106: /** 107: * The actual content model of this <code>Document</code>. 108: */ 109: Content content; 110: 111: /** 112: * The AttributeContext for this <code>Document</code>. 113: */ 114: AttributeContext context; 115: 116: /** 117: * The currently installed <code>DocumentFilter</code>. 118: */ 119: DocumentFilter documentFilter; 120: 121: /** 122: * The documents properties. 123: */ 124: Dictionary properties; 125: 126: /** 127: * Manages event listeners for this <code>Document</code>. 128: */ 129: protected EventListenerList listenerList = new EventListenerList(); 130: 131: /** 132: * Stores the current writer thread. Used for locking. 133: */ 134: private Thread currentWriter = null; 135: 136: /** 137: * The number of readers. Used for locking. 138: */ 139: private int numReaders = 0; 140: 141: /** 142: * Tells if there are one or more writers waiting. 143: */ 144: private int numWritersWaiting = 0; 145: 146: /** 147: * A condition variable that readers and writers wait on. 148: */ 149: Object documentCV = new Object(); 150: 151: 152: /** 153: * Creates a new <code>AbstractDocument</code> with the specified 154: * {@link Content} model. 155: * 156: * @param doc the <code>Content</code> model to be used in this 157: * <code>Document<code> 158: * 159: * @see GapContent 160: * @see StringContent 161: */ 162: protected AbstractDocument(Content doc) 163: { 164: this(doc, StyleContext.getDefaultStyleContext()); 165: } 166: 167: /** 168: * Creates a new <code>AbstractDocument</code> with the specified 169: * {@link Content} model and {@link AttributeContext}. 170: * 171: * @param doc the <code>Content</code> model to be used in this 172: * <code>Document<code> 173: * @param ctx the <code>AttributeContext</code> to use 174: * 175: * @see GapContent 176: * @see StringContent 177: */ 178: protected AbstractDocument(Content doc, AttributeContext ctx) 179: { 180: content = doc; 181: context = ctx; 182: } 183: 184: /** 185: * Returns the paragraph {@link Element} that holds the specified position. 186: * 187: * @param pos the position for which to get the paragraph element 188: * 189: * @return the paragraph {@link Element} that holds the specified position 190: */ 191: public abstract Element getParagraphElement(int pos); 192: 193: /** 194: * Returns the default root {@link Element} of this <code>Document</code>. 195: * Usual <code>Document</code>s only have one root element and return this. 196: * However, there may be <code>Document</code> implementations that 197: * support multiple root elements, they have to return a default root element 198: * here. 199: * 200: * @return the default root {@link Element} of this <code>Document</code> 201: */ 202: public abstract Element getDefaultRootElement(); 203: 204: /** 205: * Creates and returns a branch element with the specified 206: * <code>parent</code> and <code>attributes</code>. Note that the new 207: * <code>Element</code> is linked to the parent <code>Element</code> 208: * through {@link Element#getParentElement}, but it is not yet added 209: * to the parent <code>Element</code> as child. 210: * 211: * @param parent the parent <code>Element</code> for the new branch element 212: * @param attributes the text attributes to be installed in the new element 213: * 214: * @return the new branch <code>Element</code> 215: * 216: * @see BranchElement 217: */ 218: protected Element createBranchElement(Element parent, 219: AttributeSet attributes) 220: { 221: return new BranchElement(parent, attributes); 222: } 223: 224: /** 225: * Creates and returns a leaf element with the specified 226: * <code>parent</code> and <code>attributes</code>. Note that the new 227: * <code>Element</code> is linked to the parent <code>Element</code> 228: * through {@link Element#getParentElement}, but it is not yet added 229: * to the parent <code>Element</code> as child. 230: * 231: * @param parent the parent <code>Element</code> for the new branch element 232: * @param attributes the text attributes to be installed in the new element 233: * 234: * @return the new branch <code>Element</code> 235: * 236: * @see LeafElement 237: */ 238: protected Element createLeafElement(Element parent, AttributeSet attributes, 239: int start, int end) 240: { 241: return new LeafElement(parent, attributes, start, end); 242: } 243: 244: /** 245: * Creates a {@link Position} that keeps track of the location at the 246: * specified <code>offset</code>. 247: * 248: * @param offset the location in the document to keep track by the new 249: * <code>Position</code> 250: * 251: * @return the newly created <code>Position</code> 252: * 253: * @throws BadLocationException if <code>offset</code> is not a valid 254: * location in the documents content model 255: */ 256: public Position createPosition(final int offset) throws BadLocationException 257: { 258: return content.createPosition(offset); 259: } 260: 261: /** 262: * Notifies all registered listeners when the document model changes. 263: * 264: * @param event the <code>DocumentEvent</code> to be fired 265: */ 266: protected void fireChangedUpdate(DocumentEvent event) 267: { 268: DocumentListener[] listeners = getDocumentListeners(); 269: 270: for (int index = 0; index < listeners.length; ++index) 271: listeners[index].changedUpdate(event); 272: } 273: 274: /** 275: * Notifies all registered listeners when content is inserted in the document 276: * model. 277: * 278: * @param event the <code>DocumentEvent</code> to be fired 279: */ 280: protected void fireInsertUpdate(DocumentEvent event) 281: { 282: DocumentListener[] listeners = getDocumentListeners(); 283: 284: for (int index = 0; index < listeners.length; ++index) 285: listeners[index].insertUpdate(event); 286: } 287: 288: /** 289: * Notifies all registered listeners when content is removed from the 290: * document model. 291: * 292: * @param event the <code>DocumentEvent</code> to be fired 293: */ 294: protected void fireRemoveUpdate(DocumentEvent event) 295: { 296: DocumentListener[] listeners = getDocumentListeners(); 297: 298: for (int index = 0; index < listeners.length; ++index) 299: listeners[index].removeUpdate(event); 300: } 301: 302: /** 303: * Notifies all registered listeners when an <code>UndoableEdit</code> has 304: * been performed on this <code>Document</code>. 305: * 306: * @param event the <code>UndoableEditEvent</code> to be fired 307: */ 308: protected void fireUndoableEditUpdate(UndoableEditEvent event) 309: { 310: UndoableEditListener[] listeners = getUndoableEditListeners(); 311: 312: for (int index = 0; index < listeners.length; ++index) 313: listeners[index].undoableEditHappened(event); 314: } 315: 316: /** 317: * Returns the asynchronous loading priority. Returns <code>-1</code> if this 318: * document should not be loaded asynchronously. 319: * 320: * @return the asynchronous loading priority 321: */ 322: public int getAsynchronousLoadPriority() 323: { 324: return 0; 325: } 326: 327: /** 328: * Returns the {@link AttributeContext} used in this <code>Document</code>. 329: * 330: * @return the {@link AttributeContext} used in this <code>Document</code> 331: */ 332: protected AttributeContext getAttributeContext() 333: { 334: return context; 335: } 336: 337: /** 338: * Returns the root element for bidirectional content. 339: * 340: * @return the root element for bidirectional content 341: */ 342: public Element getBidiRootElement() 343: { 344: return null; 345: } 346: 347: /** 348: * Returns the {@link Content} model for this <code>Document</code> 349: * 350: * @return the {@link Content} model for this <code>Document</code> 351: * 352: * @see GapContent 353: * @see StringContent 354: */ 355: protected final Content getContent() 356: { 357: return content; 358: } 359: 360: /** 361: * Returns the thread that currently modifies this <code>Document</code> 362: * if there is one, otherwise <code>null</code>. This can be used to 363: * distinguish between a method call that is part of an ongoing modification 364: * or if it is a separate modification for which a new lock must be aquired. 365: * 366: * @return the thread that currently modifies this <code>Document</code> 367: * if there is one, otherwise <code>null</code> 368: */ 369: protected Thread getCurrentWriter() 370: { 371: return currentWriter; 372: } 373: 374: /** 375: * Returns the properties of this <code>Document</code>. 376: * 377: * @return the properties of this <code>Document</code> 378: */ 379: public Dictionary getDocumentProperties() 380: { 381: // FIXME: make me thread-safe 382: if (properties == null) 383: properties = new Hashtable(); 384: 385: return properties; 386: } 387: 388: /** 389: * Returns a {@link Position} which will always mark the end of the 390: * <code>Document</code>. 391: * 392: * @return a {@link Position} which will always mark the end of the 393: * <code>Document</code> 394: */ 395: public Position getEndPosition() 396: { 397: // FIXME: Properly implement this by calling Content.createPosition(). 398: return new Position() 399: { 400: public int getOffset() 401: { 402: return getLength(); 403: } 404: }; 405: } 406: 407: /** 408: * Returns the length of this <code>Document</code>'s content. 409: * 410: * @return the length of this <code>Document</code>'s content 411: */ 412: public int getLength() 413: { 414: // We return Content.getLength() -1 here because there is always an 415: // implicit \n at the end of the Content which does count in Content 416: // but not in Document. 417: return content.length() - 1; 418: } 419: 420: /** 421: * Returns all registered listeners of a given listener type. 422: * 423: * @param listenerType the type of the listeners to be queried 424: * 425: * @return all registered listeners of the specified type 426: */ 427: public EventListener[] getListeners(Class listenerType) 428: { 429: return listenerList.getListeners(listenerType); 430: } 431: 432: /** 433: * Returns a property from this <code>Document</code>'s property list. 434: * 435: * @param key the key of the property to be fetched 436: * 437: * @return the property for <code>key</code> or <code>null</code> if there 438: * is no such property stored 439: */ 440: public Object getProperty(Object key) 441: { 442: // FIXME: make me thread-safe 443: Object value = null; 444: if (properties != null) 445: value = properties.get(key); 446: 447: return value; 448: } 449: 450: /** 451: * Returns all root elements of this <code>Document</code>. By default 452: * this just returns the single root element returned by 453: * {@link #getDefaultRootElement()}. <code>Document</code> implementations 454: * that support multiple roots must override this method and return all roots 455: * here. 456: * 457: * @return all root elements of this <code>Document</code> 458: */ 459: public Element[] getRootElements() 460: { 461: Element[] elements = new Element[1]; 462: elements[0] = getDefaultRootElement(); 463: return elements; 464: } 465: 466: /** 467: * Returns a {@link Position} which will always mark the beginning of the 468: * <code>Document</code>. 469: * 470: * @return a {@link Position} which will always mark the beginning of the 471: * <code>Document</code> 472: */ 473: public Position getStartPosition() 474: { 475: // FIXME: Properly implement this using Content.createPosition(). 476: return new Position() 477: { 478: public int getOffset() 479: { 480: return 0; 481: } 482: }; 483: } 484: 485: /** 486: * Returns a piece of this <code>Document</code>'s content. 487: * 488: * @param offset the start offset of the content 489: * @param length the length of the content 490: * 491: * @return the piece of content specified by <code>offset</code> and 492: * <code>length</code> 493: * 494: * @throws BadLocationException if <code>offset</code> or <code>offset + 495: * length</code> are invalid locations with this 496: * <code>Document</code> 497: */ 498: public String getText(int offset, int length) throws BadLocationException 499: { 500: return content.getString(offset, length); 501: } 502: 503: /** 504: * Fetches a piece of this <code>Document</code>'s content and stores 505: * it in the given {@link Segment}. 506: * 507: * @param offset the start offset of the content 508: * @param length the length of the content 509: * @param segment the <code>Segment</code> to store the content in 510: * 511: * @throws BadLocationException if <code>offset</code> or <code>offset + 512: * length</code> are invalid locations with this 513: * <code>Document</code> 514: */ 515: public void getText(int offset, int length, Segment segment) 516: throws BadLocationException 517: { 518: content.getChars(offset, length, segment); 519: } 520: 521: /** 522: * Inserts a String into this <code>Document</code> at the specified 523: * position and assigning the specified attributes to it. 524: * 525: * @param offset the location at which the string should be inserted 526: * @param text the content to be inserted 527: * @param attributes the text attributes to be assigned to that string 528: * 529: * @throws BadLocationException if <code>offset</code> is not a valid 530: * location in this <code>Document</code> 531: */ 532: public void insertString(int offset, String text, AttributeSet attributes) 533: throws BadLocationException 534: { 535: // Just return when no text to insert was given. 536: if (text == null || text.length() == 0) 537: return; 538: DefaultDocumentEvent event = 539: new DefaultDocumentEvent(offset, text.length(), 540: DocumentEvent.EventType.INSERT); 541: 542: writeLock(); 543: UndoableEdit undo = content.insertString(offset, text); 544: insertUpdate(event, attributes); 545: writeUnlock(); 546: 547: fireInsertUpdate(event); 548: if (undo != null) 549: fireUndoableEditUpdate(new UndoableEditEvent(this, undo)); 550: } 551: 552: /** 553: * Called to indicate that text has been inserted into this 554: * <code>Document</code>. The default implementation does nothing. 555: * This method is executed within a write lock. 556: * 557: * @param chng the <code>DefaultDocumentEvent</code> describing the change 558: * @param attr the attributes of the changed content 559: */ 560: protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) 561: { 562: // Do nothing here. Subclasses may want to override this. 563: } 564: 565: /** 566: * Called after some content has been removed from this 567: * <code>Document</code>. The default implementation does nothing. 568: * This method is executed within a write lock. 569: * 570: * @param chng the <code>DefaultDocumentEvent</code> describing the change 571: */ 572: protected void postRemoveUpdate(DefaultDocumentEvent chng) 573: { 574: // Do nothing here. Subclasses may want to override this. 575: } 576: 577: /** 578: * Stores a property in this <code>Document</code>'s property list. 579: * 580: * @param key the key of the property to be stored 581: * @param value the value of the property to be stored 582: */ 583: public void putProperty(Object key, Object value) 584: { 585: // FIXME: make me thread-safe 586: if (properties == null) 587: properties = new Hashtable(); 588: 589: properties.put(key, value); 590: } 591: 592: /** 593: * Blocks until a read lock can be obtained. Must block if there is 594: * currently a writer modifying the <code>Document</code>. 595: */ 596: public void readLock() 597: { 598: if (currentWriter != null && currentWriter.equals(Thread.currentThread())) 599: return; 600: synchronized (documentCV) 601: { 602: while (currentWriter != null || numWritersWaiting > 0) 603: { 604: try 605: { 606: documentCV.wait(); 607: } 608: catch (InterruptedException ie) 609: { 610: throw new Error("interrupted trying to get a readLock"); 611: } 612: } 613: numReaders++; 614: } 615: } 616: 617: /** 618: * Releases the read lock. If this was the only reader on this 619: * <code>Document</code>, writing may begin now. 620: */ 621: public void readUnlock() 622: { 623: // Note we could have a problem here if readUnlock was called without a 624: // prior call to readLock but the specs simply warn users to ensure that 625: // balance by using a finally block: 626: // readLock() 627: // try 628: // { 629: // doSomethingHere 630: // } 631: // finally 632: // { 633: // readUnlock(); 634: // } 635: 636: // All that the JDK seems to check for is that you don't call unlock 637: // more times than you've previously called lock, but it doesn't make 638: // sure that the threads calling unlock were the same ones that called lock 639: 640: // FIXME: the reference implementation throws a 641: // javax.swing.text.StateInvariantError here 642: if (numReaders == 0) 643: throw new IllegalStateException("document lock failure"); 644: 645: synchronized (documentCV) 646: { 647: // If currentWriter is not null, the application code probably had a 648: // writeLock and then tried to obtain a readLock, in which case 649: // numReaders wasn't incremented 650: if (currentWriter == null) 651: { 652: numReaders --; 653: if (numReaders == 0 && numWritersWaiting != 0) 654: documentCV.notify(); 655: } 656: } 657: } 658: 659: /** 660: * Removes a piece of content from this <code>Document</code>. 661: * 662: * @param offset the start offset of the fragment to be removed 663: * @param length the length of the fragment to be removed 664: * 665: * @throws BadLocationException if <code>offset</code> or 666: * <code>offset + length</code> or invalid locations within this 667: * document 668: */ 669: public void remove(int offset, int length) throws BadLocationException 670: { 671: DefaultDocumentEvent event = 672: new DefaultDocumentEvent(offset, length, 673: DocumentEvent.EventType.REMOVE); 674: 675: removeUpdate(event); 676: 677: boolean shouldFire = content.getString(offset, length).length() != 0; 678: 679: writeLock(); 680: UndoableEdit temp = content.remove(offset, length); 681: writeUnlock(); 682: 683: postRemoveUpdate(event); 684: 685: if (shouldFire) 686: fireRemoveUpdate(event); 687: } 688: 689: /** 690: * Replaces a piece of content in this <code>Document</code> with 691: * another piece of content. 692: * 693: * @param offset the start offset of the fragment to be removed 694: * @param length the length of the fragment to be removed 695: * @param text the text to replace the content with 696: * @param attributes the text attributes to assign to the new content 697: * 698: * @throws BadLocationException if <code>offset</code> or 699: * <code>offset + length</code> or invalid locations within this 700: * document 701: * 702: * @since 1.4 703: */ 704: public void replace(int offset, int length, String text, 705: AttributeSet attributes) 706: throws BadLocationException 707: { 708: remove(offset, length); 709: insertString(offset, text, attributes); 710: } 711: 712: /** 713: * Adds a <code>DocumentListener</code> object to this document. 714: * 715: * @param listener the listener to add 716: */ 717: public void addDocumentListener(DocumentListener listener) 718: { 719: listenerList.add(DocumentListener.class, listener); 720: } 721: 722: /** 723: * Removes a <code>DocumentListener</code> object from this document. 724: * 725: * @param listener the listener to remove 726: */ 727: public void removeDocumentListener(DocumentListener listener) 728: { 729: listenerList.remove(DocumentListener.class, listener); 730: } 731: 732: /** 733: * Returns all registered <code>DocumentListener</code>s. 734: * 735: * @return all registered <code>DocumentListener</code>s 736: */ 737: public DocumentListener[] getDocumentListeners() 738: { 739: return (DocumentListener[]) getListeners(DocumentListener.class); 740: } 741: 742: /** 743: * Adds an {@link UndoableEditListener} to this <code>Document</code>. 744: * 745: * @param listener the listener to add 746: */ 747: public void addUndoableEditListener(UndoableEditListener listener) 748: { 749: listenerList.add(UndoableEditListener.class, listener); 750: } 751: 752: /** 753: * Removes an {@link UndoableEditListener} from this <code>Document</code>. 754: * 755: * @param listener the listener to remove 756: */ 757: public void removeUndoableEditListener(UndoableEditListener listener) 758: { 759: listenerList.remove(UndoableEditListener.class, listener); 760: } 761: 762: /** 763: * Returns all registered {@link UndoableEditListener}s. 764: * 765: * @return all registered {@link UndoableEditListener}s 766: */ 767: public UndoableEditListener[] getUndoableEditListeners() 768: { 769: return (UndoableEditListener[]) getListeners(UndoableEditListener.class); 770: } 771: 772: /** 773: * Called before some content gets removed from this <code>Document</code>. 774: * The default implementation does nothing but may be overridden by 775: * subclasses to modify the <code>Document</code> structure in response 776: * to a remove request. The method is executed within a write lock. 777: * 778: * @param chng the <code>DefaultDocumentEvent</code> describing the change 779: */ 780: protected void removeUpdate(DefaultDocumentEvent chng) 781: { 782: // Do nothing here. Subclasses may wish to override this. 783: } 784: 785: /** 786: * Called to render this <code>Document</code> visually. It obtains a read 787: * lock, ensuring that no changes will be made to the <code>document</code> 788: * during the rendering process. It then calls the {@link Runnable#run()} 789: * method on <code>runnable</code>. This method <em>must not</em> attempt 790: * to modifiy the <code>Document</code>, since a deadlock will occur if it 791: * tries to obtain a write lock. When the {@link Runnable#run()} method 792: * completes (either naturally or by throwing an exception), the read lock 793: * is released. Note that there is nothing in this method related to 794: * the actual rendering. It could be used to execute arbitrary code within 795: * a read lock. 796: * 797: * @param runnable the {@link Runnable} to execute 798: */ 799: public void render(Runnable runnable) 800: { 801: readLock(); 802: try 803: { 804: runnable.run(); 805: } 806: finally 807: { 808: readUnlock(); 809: } 810: } 811: 812: /** 813: * Sets the asynchronous loading priority for this <code>Document</code>. 814: * A value of <code>-1</code> indicates that this <code>Document</code> 815: * should be loaded synchronously. 816: * 817: * @param p the asynchronous loading priority to set 818: */ 819: public void setAsynchronousLoadPriority(int p) 820: { 821: // TODO: Implement this properly. 822: } 823: 824: /** 825: * Sets the properties of this <code>Document</code>. 826: * 827: * @param p the document properties to set 828: */ 829: public void setDocumentProperties(Dictionary p) 830: { 831: // FIXME: make me thread-safe 832: properties = p; 833: } 834: 835: /** 836: * Blocks until a write lock can be obtained. Must wait if there are 837: * readers currently reading or another thread is currently writing. 838: */ 839: protected void writeLock() 840: { 841: if (currentWriter!= null && currentWriter.equals(Thread.currentThread())) 842: return; 843: synchronized (documentCV) 844: { 845: numWritersWaiting++; 846: while (numReaders > 0) 847: { 848: try 849: { 850: documentCV.wait(); 851: } 852: catch (InterruptedException ie) 853: { 854: throw new Error("interruped while trying to obtain write lock"); 855: } 856: } 857: numWritersWaiting --; 858: currentWriter = Thread.currentThread(); 859: } 860: } 861: 862: /** 863: * Releases the write lock. This allows waiting readers or writers to 864: * obtain the lock. 865: */ 866: protected void writeUnlock() 867: { 868: synchronized (documentCV) 869: { 870: if (Thread.currentThread().equals(currentWriter)) 871: { 872: currentWriter = null; 873: documentCV.notifyAll(); 874: } 875: } 876: } 877: 878: /** 879: * Returns the currently installed {@link DocumentFilter} for this 880: * <code>Document</code>. 881: * 882: * @return the currently installed {@link DocumentFilter} for this 883: * <code>Document</code> 884: * 885: * @since 1.4 886: */ 887: public DocumentFilter getDocumentFilter() 888: { 889: return documentFilter; 890: } 891: 892: /** 893: * Sets the {@link DocumentFilter} for this <code>Document</code>. 894: * 895: * @param filter the <code>DocumentFilter</code> to set 896: * 897: * @since 1.4 898: */ 899: public void setDocumentFilter(DocumentFilter filter) 900: { 901: this.documentFilter = filter; 902: } 903: 904: /** 905: * Dumps diagnostic information to the specified <code>PrintStream</code>. 906: * 907: * @param out the stream to write the diagnostic information to 908: */ 909: public void dump(PrintStream out) 910: { 911: ((AbstractElement) getDefaultRootElement()).dump(out, 0); 912: } 913: 914: /** 915: * Defines a set of methods for managing text attributes for one or more 916: * <code>Document</code>s. 917: * 918: * Replicating {@link AttributeSet}s throughout a <code>Document</code> can 919: * be very expensive. Implementations of this interface are intended to 920: * provide intelligent management of <code>AttributeSet</code>s, eliminating 921: * costly duplication. 922: * 923: * @see StyleContext 924: */ 925: public interface AttributeContext 926: { 927: /** 928: * Returns an {@link AttributeSet} that contains the attributes 929: * of <code>old</code> plus the new attribute specified by 930: * <code>name</code> and <code>value</code>. 931: * 932: * @param old the attribute set to be merged with the new attribute 933: * @param name the name of the attribute to be added 934: * @param value the value of the attribute to be added 935: * 936: * @return the old attributes plus the new attribute 937: */ 938: AttributeSet addAttribute(AttributeSet old, Object name, Object value); 939: 940: /** 941: * Returns an {@link AttributeSet} that contains the attributes 942: * of <code>old</code> plus the new attributes in <code>attributes</code>. 943: * 944: * @param old the set of attributes where to add the new attributes 945: * @param attributes the attributes to be added 946: * 947: * @return an {@link AttributeSet} that contains the attributes 948: * of <code>old</code> plus the new attributes in 949: * <code>attributes</code> 950: */ 951: AttributeSet addAttributes(AttributeSet old, AttributeSet attributes); 952: 953: /** 954: * Returns an empty {@link AttributeSet}. 955: * 956: * @return an empty {@link AttributeSet} 957: */ 958: AttributeSet getEmptySet(); 959: 960: /** 961: * Called to indicate that the attributes in <code>attributes</code> are 962: * no longer used. 963: * 964: * @param attributes the attributes are no longer used 965: */ 966: void reclaim(AttributeSet attributes); 967: 968: /** 969: * Returns a {@link AttributeSet} that has the attribute with the specified 970: * <code>name</code> removed from <code>old</code>. 971: * 972: * @param old the attribute set from which an attribute is removed 973: * @param name the name of the attribute to be removed 974: * 975: * @return the attributes of <code>old</code> minus the attribute 976: * specified by <code>name</code> 977: */ 978: AttributeSet removeAttribute(AttributeSet old, Object name); 979: 980: /** 981: * Removes all attributes in <code>attributes</code> from <code>old</code> 982: * and returns the resulting <code>AttributeSet</code>. 983: * 984: * @param old the set of attributes from which to remove attributes 985: * @param attributes the attributes to be removed from <code>old</code> 986: * 987: * @return the attributes of <code>old</code> minus the attributes in 988: * <code>attributes</code> 989: */ 990: AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes); 991: 992: /** 993: * Removes all attributes specified by <code>names</code> from 994: * <code>old</code> and returns the resulting <code>AttributeSet</code>. 995: * 996: * @param old the set of attributes from which to remove attributes 997: * @param names the names of the attributes to be removed from 998: * <code>old</code> 999: * 1000: * @return the attributes of <code>old</code> minus the attributes in 1001: * <code>attributes</code> 1002: */ 1003: AttributeSet removeAttributes(AttributeSet old, Enumeration names); 1004: } 1005: 1006: /** 1007: * A sequence of data that can be edited. This is were the actual content 1008: * in <code>AbstractDocument</code>'s is stored. 1009: */ 1010: public interface Content 1011: { 1012: /** 1013: * Creates a {@link Position} that keeps track of the location at 1014: * <code>offset</code>. 1015: * 1016: * @return a {@link Position} that keeps track of the location at 1017: * <code>offset</code>. 1018: * 1019: * @throw BadLocationException if <code>offset</code> is not a valid 1020: * location in this <code>Content</code> model 1021: */ 1022: Position createPosition(int offset) throws BadLocationException; 1023: 1024: /** 1025: * Returns the length of the content. 1026: * 1027: * @return the length of the content 1028: */ 1029: int length(); 1030: 1031: /** 1032: * Inserts a string into the content model. 1033: * 1034: * @param where the offset at which to insert the string 1035: * @param str the string to be inserted 1036: * 1037: * @return an <code>UndoableEdit</code> or <code>null</code> if undo is 1038: * not supported by this <code>Content</code> model 1039: * 1040: * @throws BadLocationException if <code>where</code> is not a valid 1041: * location in this <code>Content</code> model 1042: */ 1043: UndoableEdit insertString(int where, String str) 1044: throws BadLocationException; 1045: 1046: /** 1047: * Removes a piece of content from the content model. 1048: * 1049: * @param where the offset at which to remove content 1050: * @param nitems the number of characters to be removed 1051: * 1052: * @return an <code>UndoableEdit</code> or <code>null</code> if undo is 1053: * not supported by this <code>Content</code> model 1054: * 1055: * @throws BadLocationException if <code>where</code> is not a valid 1056: * location in this <code>Content</code> model 1057: */ 1058: UndoableEdit remove(int where, int nitems) throws BadLocationException; 1059: 1060: /** 1061: * Returns a piece of content. 1062: * 1063: * @param where the start offset of the requested fragment 1064: * @param len the length of the requested fragment 1065: * 1066: * @return the requested fragment 1067: * @throws BadLocationException if <code>offset</code> or 1068: * <code>offset + len</code>is not a valid 1069: * location in this <code>Content</code> model 1070: */ 1071: String getString(int where, int len) throws BadLocationException; 1072: 1073: /** 1074: * Fetches a piece of content and stores it in <code>txt</code>. 1075: * 1076: * @param where the start offset of the requested fragment 1077: * @param len the length of the requested fragment 1078: * @param txt the <code>Segment</code> where to fragment is stored into 1079: * 1080: * @throws BadLocationException if <code>offset</code> or 1081: * <code>offset + len</code>is not a valid 1082: * location in this <code>Content</code> model 1083: */ 1084: void getChars(int where, int len, Segment txt) throws BadLocationException; 1085: } 1086: 1087: /** 1088: * An abstract base implementation of the {@link Element} interface. 1089: */ 1090: public abstract class AbstractElement 1091: implements Element, MutableAttributeSet, TreeNode, Serializable 1092: { 1093: /** The serialization UID (compatible with JDK1.5). */ 1094: private static final long serialVersionUID = 1712240033321461704L; 1095: 1096: /** The number of characters that this Element spans. */ 1097: int count; 1098: 1099: /** The starting offset of this Element. */ 1100: int offset; 1101: 1102: /** The attributes of this Element. */ 1103: AttributeSet attributes; 1104: 1105: /** The parent element. */ 1106: Element element_parent; 1107: 1108: /** The parent in the TreeNode interface. */ 1109: TreeNode tree_parent; 1110: 1111: /** The children of this element. */ 1112: Vector tree_children; 1113: 1114: /** 1115: * Creates a new instance of <code>AbstractElement</code> with a 1116: * specified parent <code>Element</code> and <code>AttributeSet</code>. 1117: * 1118: * @param p the parent of this <code>AbstractElement</code> 1119: * @param s the attributes to be assigned to this 1120: * <code>AbstractElement</code> 1121: */ 1122: public AbstractElement(Element p, AttributeSet s) 1123: { 1124: element_parent = p; 1125: AttributeContext ctx = getAttributeContext(); 1126: attributes = ctx.getEmptySet(); 1127: if (s != null) 1128: attributes = ctx.addAttributes(attributes, s); 1129: } 1130: 1131: /** 1132: * Returns the child nodes of this <code>Element</code> as an 1133: * <code>Enumeration</code> of {@link TreeNode}s. 1134: * 1135: * @return the child nodes of this <code>Element</code> as an 1136: * <code>Enumeration</code> of {@link TreeNode}s 1137: */ 1138: public abstract Enumeration children(); 1139: 1140: /** 1141: * Returns <code>true</code> if this <code>AbstractElement</code> 1142: * allows children. 1143: * 1144: * @return <code>true</code> if this <code>AbstractElement</code> 1145: * allows children 1146: */ 1147: public abstract boolean getAllowsChildren(); 1148: 1149: /** 1150: * Returns the child of this <code>AbstractElement</code> at 1151: * <code>index</code>. 1152: * 1153: * @param index the position in the child list of the child element to 1154: * be returned 1155: * 1156: * @return the child of this <code>AbstractElement</code> at 1157: * <code>index</code> 1158: */ 1159: public TreeNode getChildAt(int index) 1160: { 1161: return (TreeNode) tree_children.get(index); 1162: } 1163: 1164: /** 1165: * Returns the number of children of this <code>AbstractElement</code>. 1166: * 1167: * @return the number of children of this <code>AbstractElement</code> 1168: */ 1169: public int getChildCount() 1170: { 1171: return tree_children.size(); 1172: } 1173: 1174: /** 1175: * Returns the index of a given child <code>TreeNode</code> or 1176: * <code>-1</code> if <code>node</code> is not a child of this 1177: * <code>AbstractElement</code>. 1178: * 1179: * @param node the node for which the index is requested 1180: * 1181: * @return the index of a given child <code>TreeNode</code> or 1182: * <code>-1</code> if <code>node</code> is not a child of this 1183: * <code>AbstractElement</code> 1184: */ 1185: public int getIndex(TreeNode node) 1186: { 1187: return tree_children.indexOf(node); 1188: } 1189: 1190: /** 1191: * Returns the parent <code>TreeNode</code> of this 1192: * <code>AbstractElement</code> or <code>null</code> if this element 1193: * has no parent. 1194: * 1195: * @return the parent <code>TreeNode</code> of this 1196: * <code>AbstractElement</code> or <code>null</code> if this 1197: * element has no parent 1198: */ 1199: public TreeNode getParent() 1200: { 1201: return tree_parent; 1202: } 1203: 1204: /** 1205: * Returns <code>true</code> if this <code>AbstractElement</code> is a 1206: * leaf element, <code>false</code> otherwise. 1207: * 1208: * @return <code>true</code> if this <code>AbstractElement</code> is a 1209: * leaf element, <code>false</code> otherwise 1210: */ 1211: public abstract boolean isLeaf(); 1212: 1213: /** 1214: * Adds an attribute to this element. 1215: * 1216: * @param name the name of the attribute to be added 1217: * @param value the value of the attribute to be added 1218: */ 1219: public void addAttribute(Object name, Object value) 1220: { 1221: attributes = getAttributeContext().addAttribute(attributes, name, value); 1222: } 1223: 1224: /** 1225: * Adds a set of attributes to this element. 1226: * 1227: * @param attrs the attributes to be added to this element 1228: */ 1229: public void addAttributes(AttributeSet attrs) 1230: { 1231: attributes = getAttributeContext().addAttributes(attributes, attrs); 1232: } 1233: 1234: /** 1235: * Removes an attribute from this element. 1236: * 1237: * @param name the name of the attribute to be removed 1238: */ 1239: public void removeAttribute(Object name) 1240: { 1241: attributes = getAttributeContext().removeAttribute(attributes, name); 1242: } 1243: 1244: /** 1245: * Removes a set of attributes from this element. 1246: * 1247: * @param attrs the attributes to be removed 1248: */ 1249: public void removeAttributes(AttributeSet attrs) 1250: { 1251: attributes = getAttributeContext().removeAttributes(attributes, attrs); 1252: } 1253: 1254: /** 1255: * Removes a set of attribute from this element. 1256: * 1257: * @param names the names of the attributes to be removed 1258: */ 1259: public void removeAttributes(Enumeration names) 1260: { 1261: attributes = getAttributeContext().removeAttributes(attributes, names); 1262: } 1263: 1264: /** 1265: * Sets the parent attribute set against which the element can resolve 1266: * attributes that are not defined in itself. 1267: * 1268: * @param parent the resolve parent to set 1269: */ 1270: public void setResolveParent(AttributeSet parent) 1271: { 1272: attributes = getAttributeContext().addAttribute(attributes, 1273: ResolveAttribute, 1274: parent); 1275: } 1276: 1277: /** 1278: * Returns <code>true</code> if this element contains the specified 1279: * attribute. 1280: * 1281: * @param name the name of the attribute to check 1282: * @param value the value of the attribute to check 1283: * 1284: * @return <code>true</code> if this element contains the specified 1285: * attribute 1286: */ 1287: public boolean containsAttribute(Object name, Object value) 1288: { 1289: return attributes.containsAttribute(name, value); 1290: } 1291: 1292: /** 1293: * Returns <code>true</code> if this element contains all of the 1294: * specified attributes. 1295: * 1296: * @param attrs the attributes to check 1297: * 1298: * @return <code>true</code> if this element contains all of the 1299: * specified attributes 1300: */ 1301: public boolean containsAttributes(AttributeSet attrs) 1302: { 1303: return attributes.containsAttributes(attrs); 1304: } 1305: 1306: /** 1307: * Returns a copy of the attributes of this element. 1308: * 1309: * @return a copy of the attributes of this element 1310: */ 1311: public AttributeSet copyAttributes() 1312: { 1313: return attributes.copyAttributes(); 1314: } 1315: 1316: /** 1317: * Returns the attribute value with the specified key. If this attribute 1318: * is not defined in this element and this element has a resolving 1319: * parent, the search goes upward to the resolve parent chain. 1320: * 1321: * @param key the key of the requested attribute 1322: * 1323: * @return the attribute value for <code>key</code> of <code>null</code> 1324: * if <code>key</code> is not found locally and cannot be resolved 1325: * in this element's resolve parents 1326: */ 1327: public Object getAttribute(Object key) 1328: { 1329: return attributes.getAttribute(key); 1330: } 1331: 1332: /** 1333: * Returns the number of defined attributes in this element. 1334: * 1335: * @return the number of defined attributes in this element 1336: */ 1337: public int getAttributeCount() 1338: { 1339: return attributes.getAttributeCount(); 1340: } 1341: 1342: /** 1343: * Returns the names of the attributes of this element. 1344: * 1345: * @return the names of the attributes of this element 1346: */ 1347: public Enumeration getAttributeNames() 1348: { 1349: return attributes.getAttributeNames(); 1350: } 1351: 1352: /** 1353: * Returns the resolve parent of this element. 1354: * This is taken from the AttributeSet, but if this is null, 1355: * this method instead returns the Element's parent's 1356: * AttributeSet 1357: * 1358: * @return the resolve parent of this element 1359: * 1360: * @see #setResolveParent(AttributeSet) 1361: */ 1362: public AttributeSet getResolveParent() 1363: { 1364: if (attributes.getResolveParent() != null) 1365: return attributes.getResolveParent(); 1366: return element_parent.getAttributes(); 1367: } 1368: 1369: /** 1370: * Returns <code>true</code> if an attribute with the specified name 1371: * is defined in this element, <code>false</code> otherwise. 1372: * 1373: * @param attrName the name of the requested attributes 1374: * 1375: * @return <code>true</code> if an attribute with the specified name 1376: * is defined in this element, <code>false</code> otherwise 1377: */ 1378: public boolean isDefined(Object attrName) 1379: { 1380: return attributes.isDefined(attrName); 1381: } 1382: 1383: /** 1384: * Returns <code>true</code> if the specified <code>AttributeSet</code> 1385: * is equal to this element's <code>AttributeSet</code>, <code>false</code> 1386: * otherwise. 1387: * 1388: * @param attrs the attributes to compare this element to 1389: * 1390: * @return <code>true</code> if the specified <code>AttributeSet</code> 1391: * is equal to this element's <code>AttributeSet</code>, 1392: * <code>false</code> otherwise 1393: */ 1394: public boolean isEqual(AttributeSet attrs) 1395: { 1396: return attributes.isEqual(attrs); 1397: } 1398: 1399: /** 1400: * Returns the attributes of this element. 1401: * 1402: * @return the attributes of this element 1403: */ 1404: public AttributeSet getAttributes() 1405: { 1406: return this; 1407: } 1408: 1409: /** 1410: * Returns the {@link Document} to which this element belongs. 1411: * 1412: * @return the {@link Document} to which this element belongs 1413: */ 1414: public Document getDocument() 1415: { 1416: return AbstractDocument.this; 1417: } 1418: 1419: /** 1420: * Returns the child element at the specified <code>index</code>. 1421: * 1422: * @param index the index of the requested child element 1423: * 1424: * @return the requested element 1425: */ 1426: public abstract Element getElement(int index); 1427: 1428: /** 1429: * Returns the name of this element. 1430: * 1431: * @return the name of this element 1432: */ 1433: public String getName() 1434: { 1435: return (String) getAttribute(NameAttribute); 1436: } 1437: 1438: /** 1439: * Returns the parent element of this element. 1440: * 1441: * @return the parent element of this element 1442: */ 1443: public Element getParentElement() 1444: { 1445: return element_parent; 1446: } 1447: 1448: /** 1449: * Returns the offset inside the document model that is after the last 1450: * character of this element. 1451: * 1452: * @return the offset inside the document model that is after the last 1453: * character of this element 1454: */ 1455: public abstract int getEndOffset(); 1456: 1457: /** 1458: * Returns the number of child elements of this element. 1459: * 1460: * @return the number of child elements of this element 1461: */ 1462: public abstract int getElementCount(); 1463: 1464: /** 1465: * Returns the index of the child element that spans the specified 1466: * offset in the document model. 1467: * 1468: * @param offset the offset for which the responsible element is searched 1469: * 1470: * @return the index of the child element that spans the specified 1471: * offset in the document model 1472: */ 1473: public abstract int getElementIndex(int offset); 1474: 1475: /** 1476: * Returns the start offset if this element inside the document model. 1477: * 1478: * @return the start offset if this element inside the document model 1479: */ 1480: public abstract int getStartOffset(); 1481: 1482: /** 1483: * Prints diagnostic output to the specified stream. 1484: * 1485: * @param stream the stream to write to 1486: * @param indent the indentation level 1487: */ 1488: public void dump(PrintStream stream, int indent) 1489: { 1490: StringBuffer b = new StringBuffer(); 1491: for (int i = 0; i < indent; ++i) 1492: b.append(' '); 1493: b.append('<'); 1494: b.append(getName()); 1495: // Dump attributes if there are any. 1496: if (getAttributeCount() > 0) 1497: { 1498: b.append('\n'); 1499: Enumeration attNames = getAttributeNames(); 1500: while (attNames.hasMoreElements()) 1501: { 1502: for (int i = 0; i < indent + 2; ++i) 1503: b.append(' '); 1504: Object attName = attNames.nextElement(); 1505: b.append(attName); 1506: b.append('='); 1507: Object attribute = getAttribute(attName); 1508: b.append(attribute); 1509: b.append('\n'); 1510: } 1511: } 1512: b.append(">\n"); 1513: 1514: // Dump element content for leaf elements. 1515: if (isLeaf()) 1516: { 1517: for (int i = 0; i < indent + 2; ++i) 1518: b.append(' '); 1519: int start = getStartOffset(); 1520: int end = getEndOffset(); 1521: b.append('['); 1522: b.append(start); 1523: b.append(','); 1524: b.append(end); 1525: b.append("]["); 1526: try 1527: { 1528: b.append(getDocument().getText(start, end - start)); 1529: } 1530: catch (BadLocationException ex) 1531: { 1532: AssertionError err = new AssertionError("BadLocationException " 1533: + "must not be thrown " 1534: + "here."); 1535: err.initCause(ex); 1536: throw err; 1537: } 1538: b.append("]\n"); 1539: } 1540: stream.print(b.toString()); 1541: 1542: // Dump child elements if any. 1543: int count = getElementCount(); 1544: for (int i = 0; i < count; ++i) 1545: { 1546: Element el = getElement(i); 1547: if (el instanceof AbstractElement) 1548: ((AbstractElement) el).dump(stream, indent + 2); 1549: } 1550: } 1551: } 1552: 1553: /** 1554: * An implementation of {@link Element} to represent composite 1555: * <code>Element</code>s that contain other <code>Element</code>s. 1556: */ 1557: public class BranchElement extends AbstractElement 1558: { 1559: /** The serialization UID (compatible with JDK1.5). */ 1560: private static final long serialVersionUID = -6037216547466333183L; 1561: 1562: /** The child elements of this BranchElement. */ 1563: private Element[] children = new Element[0]; 1564: 1565: /** 1566: * Creates a new <code>BranchElement</code> with the specified 1567: * parent and attributes. 1568: * 1569: * @param parent the parent element of this <code>BranchElement</code> 1570: * @param attributes the attributes to set on this 1571: * <code>BranchElement</code> 1572: */ 1573: public BranchElement(Element parent, AttributeSet attributes) 1574: { 1575: super(parent, attributes); 1576: } 1577: 1578: /** 1579: * Returns the children of this <code>BranchElement</code>. 1580: * 1581: * @return the children of this <code>BranchElement</code> 1582: */ 1583: public Enumeration children() 1584: { 1585: if (children.length == 0) 1586: return null; 1587: 1588: Vector tmp = new Vector(); 1589: 1590: for (int index = 0; index < children.length; ++index) 1591: tmp.add(children[index]); 1592: 1593: return tmp.elements(); 1594: } 1595: 1596: /** 1597: * Returns <code>true</code> since <code>BranchElements</code> allow 1598: * child elements. 1599: * 1600: * @return <code>true</code> since <code>BranchElements</code> allow 1601: * child elements 1602: */ 1603: public boolean getAllowsChildren() 1604: { 1605: return true; 1606: } 1607: 1608: /** 1609: * Returns the child element at the specified <code>index</code>. 1610: * 1611: * @param index the index of the requested child element 1612: * 1613: * @return the requested element 1614: */ 1615: public Element getElement(int index) 1616: { 1617: if (index < 0 || index >= children.length) 1618: return null; 1619: 1620: return children[index]; 1621: } 1622: 1623: /** 1624: * Returns the number of child elements of this element. 1625: * 1626: * @return the number of child elements of this element 1627: */ 1628: public int getElementCount() 1629: { 1630: return children.length; 1631: } 1632: 1633: /** 1634: * Returns the index of the child element that spans the specified 1635: * offset in the document model. 1636: * 1637: * @param offset the offset for which the responsible element is searched 1638: * 1639: * @return the index of the child element that spans the specified 1640: * offset in the document model 1641: */ 1642: public int getElementIndex(int offset) 1643: { 1644: // If offset is less than the start offset of our first child, 1645: // return 0 1646: if (offset < getStartOffset()) 1647: return 0; 1648: 1649: // XXX: There is surely a better algorithm 1650: // as beginning from first element each time. 1651: for (int index = 0; index < children.length - 1; ++index) 1652: { 1653: Element elem = children[index]; 1654: 1655: if ((elem.getStartOffset() <= offset) 1656: && (offset < elem.getEndOffset())) 1657: return index; 1658: // If the next element's start offset is greater than offset 1659: // then we have to return the closest Element, since no Elements 1660: // will contain the offset 1661: if (children[index + 1].getStartOffset() > offset) 1662: { 1663: if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset)) 1664: return index + 1; 1665: else 1666: return index; 1667: } 1668: } 1669: 1670: // If offset is greater than the index of the last element, return 1671: // the index of the last element. 1672: return getElementCount() - 1; 1673: } 1674: 1675: /** 1676: * Returns the offset inside the document model that is after the last 1677: * character of this element. 1678: * This is the end offset of the last child element. If this element 1679: * has no children, this method throws a <code>NullPointerException</code>. 1680: * 1681: * @return the offset inside the document model that is after the last 1682: * character of this element 1683: * 1684: * @throws NullPointerException if this branch element has no children 1685: */ 1686: public int getEndOffset() 1687: { 1688: if (getElementCount() == 0) 1689: throw new NullPointerException("This BranchElement has no children."); 1690: return children[children.length - 1].getEndOffset(); 1691: } 1692: 1693: /** 1694: * Returns the name of this element. This is {@link #ParagraphElementName} 1695: * in this case. 1696: * 1697: * @return the name of this element 1698: */ 1699: public String getName() 1700: { 1701: return ParagraphElementName; 1702: } 1703: 1704: /** 1705: * Returns the start offset of this element inside the document model. 1706: * This is the start offset of the first child element. If this element 1707: * has no children, this method throws a <code>NullPointerException</code>. 1708: * 1709: * @return the start offset of this element inside the document model 1710: * 1711: * @throws NullPointerException if this branch element has no children 1712: */ 1713: public int getStartOffset() 1714: { 1715: if (getElementCount() == 0) 1716: throw new NullPointerException("This BranchElement has no children."); 1717: return children[0].getStartOffset(); 1718: } 1719: 1720: /** 1721: * Returns <code>false</code> since <code>BranchElement</code> are no 1722: * leafes. 1723: * 1724: * @return <code>false</code> since <code>BranchElement</code> are no 1725: * leafes 1726: */ 1727: public boolean isLeaf() 1728: { 1729: return false; 1730: } 1731: 1732: /** 1733: * Returns the <code>Element</code> at the specified <code>Document</code> 1734: * offset. 1735: * 1736: * @return the <code>Element</code> at the specified <code>Document</code> 1737: * offset 1738: * 1739: * @see #getElementIndex(int) 1740: */ 1741: public Element positionToElement(int position) 1742: { 1743: // XXX: There is surely a better algorithm 1744: // as beginning from first element each time. 1745: for (int index = 0; index < children.length; ++index) 1746: { 1747: Element elem = children[index]; 1748: 1749: if ((elem.getStartOffset() <= position) 1750: && (position < elem.getEndOffset())) 1751: return elem; 1752: } 1753: 1754: return null; 1755: } 1756: 1757: /** 1758: * Replaces a set of child elements with a new set of child elemens. 1759: * 1760: * @param offset the start index of the elements to be removed 1761: * @param length the number of elements to be removed 1762: * @param elements the new elements to be inserted 1763: */ 1764: public void replace(int offset, int length, Element[] elements) 1765: { 1766: Element[] target = new Element[children.length - length 1767: + elements.length]; 1768: System.arraycopy(children, 0, target, 0, offset); 1769: System.arraycopy(elements, 0, target, offset, elements.length); 1770: System.arraycopy(children, offset + length, target, 1771: offset + elements.length, 1772: children.length - offset - length); 1773: children = target; 1774: } 1775: 1776: /** 1777: * Returns a string representation of this element. 1778: * 1779: * @return a string representation of this element 1780: */ 1781: public String toString() 1782: { 1783: return ("BranchElement(" + getName() + ") " 1784: + getStartOffset() + "," + getEndOffset() + "\n"); 1785: } 1786: } 1787: 1788: /** 1789: * Stores the changes when a <code>Document</code> is beeing modified. 1790: */ 1791: public class DefaultDocumentEvent extends CompoundEdit 1792: implements DocumentEvent 1793: { 1794: /** The serialization UID (compatible with JDK1.5). */ 1795: private static final long serialVersionUID = 5230037221564563284L; 1796: 1797: /** The starting offset of the change. */ 1798: private int offset; 1799: 1800: /** The length of the change. */ 1801: private int length; 1802: 1803: /** The type of change. */ 1804: private DocumentEvent.EventType type; 1805: 1806: /** 1807: * Maps <code>Element</code> to their change records. 1808: */ 1809: Hashtable changes; 1810: 1811: /** 1812: * Creates a new <code>DefaultDocumentEvent</code>. 1813: * 1814: * @param offset the starting offset of the change 1815: * @param length the length of the change 1816: * @param type the type of change 1817: */ 1818: public DefaultDocumentEvent(int offset, int length, 1819: DocumentEvent.EventType type) 1820: { 1821: this.offset = offset; 1822: this.length = length; 1823: this.type = type; 1824: changes = new Hashtable(); 1825: } 1826: 1827: /** 1828: * Adds an UndoableEdit to this <code>DocumentEvent</code>. If this 1829: * edit is an instance of {@link ElementEdit}, then this record can 1830: * later be fetched by calling {@link #getChange}. 1831: * 1832: * @param edit the undoable edit to add 1833: */ 1834: public boolean addEdit(UndoableEdit edit) 1835: { 1836: // XXX - Fully qualify ElementChange to work around gcj bug #2499. 1837: if (edit instanceof DocumentEvent.ElementChange) 1838: { 1839: DocumentEvent.ElementChange elEdit = 1840: (DocumentEvent.ElementChange) edit; 1841: changes.put(elEdit.getElement(), elEdit); 1842: } 1843: return super.addEdit(edit); 1844: } 1845: 1846: /** 1847: * Returns the document that has been modified. 1848: * 1849: * @return the document that has been modified 1850: */ 1851: public Document getDocument() 1852: { 1853: return AbstractDocument.this; 1854: } 1855: 1856: /** 1857: * Returns the length of the modification. 1858: * 1859: * @return the length of the modification 1860: */ 1861: public int getLength() 1862: { 1863: return length; 1864: } 1865: 1866: /** 1867: * Returns the start offset of the modification. 1868: * 1869: * @return the start offset of the modification 1870: */ 1871: public int getOffset() 1872: { 1873: return offset; 1874: } 1875: 1876: /** 1877: * Returns the type of the modification. 1878: * 1879: * @return the type of the modification 1880: */ 1881: public DocumentEvent.EventType getType() 1882: { 1883: return type; 1884: } 1885: 1886: /** 1887: * Returns the changes for an element. 1888: * 1889: * @param elem the element for which the changes are requested 1890: * 1891: * @return the changes for <code>elem</code> or <code>null</code> if 1892: * <code>elem</code> has not been changed 1893: */ 1894: public DocumentEvent.ElementChange getChange(Element elem) 1895: { 1896: // XXX - Fully qualify ElementChange to work around gcj bug #2499. 1897: return (DocumentEvent.ElementChange) changes.get(elem); 1898: } 1899: } 1900: 1901: /** 1902: * An implementation of {@link DocumentEvent.ElementChange} to be added 1903: * to {@link DefaultDocumentEvent}s. 1904: */ 1905: public static class ElementEdit extends AbstractUndoableEdit 1906: implements DocumentEvent.ElementChange 1907: { 1908: /** The serial version UID of ElementEdit. */ 1909: private static final long serialVersionUID = -1216620962142928304L; 1910: 1911: /** 1912: * The changed element. 1913: */ 1914: private Element elem; 1915: 1916: /** 1917: * The index of the change. 1918: */ 1919: private int index; 1920: 1921: /** 1922: * The removed elements. 1923: */ 1924: private Element[] removed; 1925: 1926: /** 1927: * The added elements. 1928: */ 1929: private Element[] added; 1930: 1931: /** 1932: * Creates a new <code>ElementEdit</code>. 1933: * 1934: * @param elem the changed element 1935: * @param index the index of the change 1936: * @param removed the removed elements 1937: * @param added the added elements 1938: */ 1939: public ElementEdit(Element elem, int index, 1940: Element[] removed, Element[] added) 1941: { 1942: this.elem = elem; 1943: this.index = index; 1944: this.removed = removed; 1945: this.added = added; 1946: } 1947: 1948: /** 1949: * Returns the added elements. 1950: * 1951: * @return the added elements 1952: */ 1953: public Element[] getChildrenAdded() 1954: { 1955: return added; 1956: } 1957: 1958: /** 1959: * Returns the removed elements. 1960: * 1961: * @return the removed elements 1962: */ 1963: public Element[] getChildrenRemoved() 1964: { 1965: return removed; 1966: } 1967: 1968: /** 1969: * Returns the changed element. 1970: * 1971: * @return the changed element 1972: */ 1973: public Element getElement() 1974: { 1975: return elem; 1976: } 1977: 1978: /** 1979: * Returns the index of the change. 1980: * 1981: * @return the index of the change 1982: */ 1983: public int getIndex() 1984: { 1985: return index; 1986: } 1987: } 1988: 1989: /** 1990: * An implementation of {@link Element} that represents a leaf in the 1991: * document structure. This is used to actually store content. 1992: */ 1993: public class LeafElement extends AbstractElement 1994: { 1995: /** The serialization UID (compatible with JDK1.5). */ 1996: private static final long serialVersionUID = -8906306331347768017L; 1997: 1998: /** Manages the start offset of this element. */ 1999: Position startPos; 2000: 2001: /** Manages the end offset of this element. */ 2002: Position endPos; 2003: 2004: /** 2005: * Creates a new <code>LeafElement</code>. 2006: * 2007: * @param parent the parent of this <code>LeafElement</code> 2008: * @param attributes the attributes to be set 2009: * @param start the start index of this element inside the document model 2010: * @param end the end index of this element inside the document model 2011: */ 2012: public LeafElement(Element parent, AttributeSet attributes, int start, 2013: int end) 2014: { 2015: super(parent, attributes); 2016: { 2017: try 2018: { 2019: if (parent != null) 2020: { 2021: startPos = parent.getDocument().createPosition(start); 2022: endPos = parent.getDocument().createPosition(end); 2023: } 2024: else 2025: { 2026: startPos = createPosition(start); 2027: endPos = createPosition(end); 2028: } 2029: } 2030: catch (BadLocationException ex) 2031: { 2032: AssertionError as; 2033: as = new AssertionError("BadLocationException thrown " 2034: + "here. start=" + start 2035: + ", end=" + end 2036: + ", length=" + getLength()); 2037: as.initCause(ex); 2038: throw as; 2039: } 2040: } 2041: } 2042: 2043: /** 2044: * Returns <code>null</code> since <code>LeafElement</code>s cannot have 2045: * children. 2046: * 2047: * @return <code>null</code> since <code>LeafElement</code>s cannot have 2048: * children 2049: */ 2050: public Enumeration children() 2051: { 2052: return null; 2053: } 2054: 2055: /** 2056: * Returns <code>false</code> since <code>LeafElement</code>s cannot have 2057: * children. 2058: * 2059: * @return <code>false</code> since <code>LeafElement</code>s cannot have 2060: * children 2061: */ 2062: public boolean getAllowsChildren() 2063: { 2064: return false; 2065: } 2066: 2067: /** 2068: * Returns <code>null</code> since <code>LeafElement</code>s cannot have 2069: * children. 2070: * 2071: * @return <code>null</code> since <code>LeafElement</code>s cannot have 2072: * children 2073: */ 2074: public Element getElement(int index) 2075: { 2076: return null; 2077: } 2078: 2079: /** 2080: * Returns <code>0</code> since <code>LeafElement</code>s cannot have 2081: * children. 2082: * 2083: * @return <code>0</code> since <code>LeafElement</code>s cannot have 2084: * children 2085: */ 2086: public int getElementCount() 2087: { 2088: return 0; 2089: } 2090: 2091: /** 2092: * Returns <code>-1</code> since <code>LeafElement</code>s cannot have 2093: * children. 2094: * 2095: * @return <code>-1</code> since <code>LeafElement</code>s cannot have 2096: * children 2097: */ 2098: public int getElementIndex(int offset) 2099: { 2100: return -1; 2101: } 2102: 2103: /** 2104: * Returns the end offset of this <code>Element</code> inside the 2105: * document. 2106: * 2107: * @return the end offset of this <code>Element</code> inside the 2108: * document 2109: */ 2110: public int getEndOffset() 2111: { 2112: return endPos.getOffset(); 2113: } 2114: 2115: /** 2116: * Returns the name of this <code>Element</code>. This is 2117: * {@link #ContentElementName} in this case. 2118: * 2119: * @return the name of this <code>Element</code> 2120: */ 2121: public String getName() 2122: { 2123: String name = super.getName(); 2124: if (name == null) 2125: name = ContentElementName; 2126: return name; 2127: } 2128: 2129: /** 2130: * Returns the start offset of this <code>Element</code> inside the 2131: * document. 2132: * 2133: * @return the start offset of this <code>Element</code> inside the 2134: * document 2135: */ 2136: public int getStartOffset() 2137: { 2138: return startPos.getOffset(); 2139: } 2140: 2141: /** 2142: * Returns <code>true</code>. 2143: * 2144: * @return <code>true</code> 2145: */ 2146: public boolean isLeaf() 2147: { 2148: return true; 2149: } 2150: 2151: /** 2152: * Returns a string representation of this <code>Element</code>. 2153: * 2154: * @return a string representation of this <code>Element</code> 2155: */ 2156: public String toString() 2157: { 2158: return ("LeafElement(" + getName() + ") " 2159: + getStartOffset() + "," + getEndOffset() + "\n"); 2160: } 2161: } 2162: }