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