< prev index next >

src/java.desktop/share/classes/javax/swing/text/DefaultStyledDocument.java

Print this page




  45 import javax.swing.undo.CannotUndoException;
  46 import javax.swing.undo.UndoableEdit;
  47 import javax.swing.SwingUtilities;
  48 import static sun.swing.SwingUtilities2.IMPLIED_CR;
  49 
  50 /**
  51  * A document that can be marked up with character and paragraph
  52  * styles in a manner similar to the Rich Text Format.  The element
  53  * structure for this document represents style crossings for
  54  * style runs.  These style runs are mapped into a paragraph element
  55  * structure (which may reside in some other structure).  The
  56  * style runs break at paragraph boundaries since logical styles are
  57  * assigned to paragraph boundaries.
  58  * <p>
  59  * <strong>Warning:</strong>
  60  * Serialized objects of this class will not be compatible with
  61  * future Swing releases. The current serialization support is
  62  * appropriate for short term storage or RMI between applications running
  63  * the same version of Swing.  As of 1.4, support for long term storage
  64  * of all JavaBeans&trade;
  65  * has been added to the <code>java.beans</code> package.
  66  * Please see {@link java.beans.XMLEncoder}.
  67  *
  68  * @author  Timothy Prinzing
  69  * @see     Document
  70  * @see     AbstractDocument
  71  */
  72 @SuppressWarnings("serial") // Same-version serialization only
  73 public class DefaultStyledDocument extends AbstractDocument implements StyledDocument {
  74 
  75     /**
  76      * Constructs a styled document.
  77      *
  78      * @param c  the container for the content
  79      * @param styles resources and style definitions which may
  80      *  be shared across documents
  81      */
  82     public DefaultStyledDocument(Content c, StyleContext styles) {
  83         super(c, styles);
  84         listeningStyles = new Vector<Style>();
  85         buffer = new ElementBuffer(createDefaultRoot());


 103      * and has a style context that is scoped by the lifetime
 104      * of the document and is not shared with other documents.
 105      */
 106     public DefaultStyledDocument() {
 107         this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
 108     }
 109 
 110     /**
 111      * Gets the default root element.
 112      *
 113      * @return the root
 114      * @see Document#getDefaultRootElement
 115      */
 116     public Element getDefaultRootElement() {
 117         return buffer.getRootElement();
 118     }
 119 
 120     /**
 121      * Initialize the document to reflect the given element
 122      * structure (i.e. the structure reported by the
 123      * <code>getDefaultRootElement</code> method.  If the
 124      * document contained any data it will first be removed.
 125      * @param data the element data
 126      */
 127     protected void create(ElementSpec[] data) {
 128         try {
 129             if (getLength() != 0) {
 130                 remove(0, getLength());
 131             }
 132             writeLock();
 133 
 134             // install the content
 135             Content c = getContent();
 136             int n = data.length;
 137             StringBuilder sb = new StringBuilder();
 138             for (int i = 0; i < n; i++) {
 139                 ElementSpec es = data[i];
 140                 if (es.getLength() > 0) {
 141                     sb.append(es.getArray(), es.getOffset(),  es.getLength());
 142                 }
 143             }


 232      * the text in the range identified by the element.  If the
 233      * element isn't associated with the document, {@code
 234      * IllegalArgumentException} is thrown.</p>
 235      *
 236      * <p>As empty branch elements are not allowed in the document, if the
 237      * element is the sole child, its parent element is removed as well,
 238      * recursively.  This means that when replacing all the children of a
 239      * particular element, new children should be added <em>before</em>
 240      * removing old children.
 241      *
 242      * <p>Element removal results in two events being fired, the
 243      * {@code DocumentEvent} for changes in element structure and {@code
 244      * UndoableEditEvent} for changes in document content.</p>
 245      *
 246      * <p>If the element contains end-of-content mark (the last {@code
 247      * "\n"} character in document), this character is not removed;
 248      * instead, preceding leaf element is extended to cover the
 249      * character.  If the last leaf already ends with {@code "\n",} it is
 250      * included in content removal.</p>
 251      *
 252      * <p>If the element is {@code null,} {@code NullPointerException} is
 253      * thrown.  If the element structure would become invalid after the removal,
 254      * for example if the element is the document root element, {@code
 255      * IllegalArgumentException} is thrown.  If the current element structure is
 256      * invalid, {@code IllegalStateException} is thrown.</p>
 257      *
 258      * @param  elem                      the element to remove
 259      * @throws NullPointerException      if the element is {@code null}
 260      * @throws IllegalArgumentException  if the element could not be removed
 261      * @throws IllegalStateException     if the element structure is invalid
 262      *
 263      * @since  1.7
 264      */
 265     public void removeElement(Element elem) {
 266         try {
 267             writeLock();
 268             removeElementImpl(elem);
 269         } finally {
 270             writeUnlock();
 271         }
 272     }


 570                 }
 571                 attr.addAttributes(s);
 572                 if (isI18N && !hasRuns) {
 573                     hasRuns = (attr.getAttribute(TextAttribute.RUN_DIRECTION) != null);
 574                 }
 575             }
 576 
 577             if (hasRuns) {
 578                 updateBidi( changes );
 579             }
 580 
 581             changes.end();
 582             fireChangedUpdate(changes);
 583             fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
 584         } finally {
 585             writeUnlock();
 586         }
 587     }
 588 
 589     /**
 590      * Gets the paragraph element at the offset <code>pos</code>.
 591      * A paragraph consists of at least one child Element, which is usually
 592      * a leaf.
 593      *
 594      * @param pos the starting offset &gt;= 0
 595      * @return the element
 596      */
 597     public Element getParagraphElement(int pos) {
 598         Element e;
 599         for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
 600             int index = e.getElementIndex(pos);
 601             e = e.getElement(index);
 602         }
 603         if(e != null)
 604             return e.getParentElement();
 605         return e;
 606     }
 607 
 608     /**
 609      * Gets a character element based on a position.
 610      *


 788                 // Assure that the composed text element is named properly
 789                 // and doesn't have the CR attribute defined.
 790                 mattr.addAttribute(StyleConstants.NameAttribute,
 791                         AbstractDocument.ContentElementName);
 792                 if (mattr.isDefined(IMPLIED_CR)) {
 793                     mattr.removeAttribute(IMPLIED_CR);
 794                 }
 795             }
 796 
 797             ElementSpec[] spec = new ElementSpec[parseBuffer.size()];
 798             parseBuffer.copyInto(spec);
 799             buffer.insert(offset, length, spec, chng);
 800         } catch (BadLocationException bl) {
 801         }
 802 
 803         super.insertUpdate( chng, attr );
 804     }
 805 
 806     /**
 807      * This is called by insertUpdate when inserting after a new line.
 808      * It generates, in <code>parseBuffer</code>, ElementSpecs that will
 809      * position the stack in <code>paragraph</code>.<p>
 810      * It returns the direction the last StartSpec should have (this don't
 811      * necessarily create the last start spec).
 812      */
 813     short createSpecsForInsertAfterNewline(Element paragraph,
 814             Element pParagraph, AttributeSet pattr, Vector<ElementSpec> parseBuffer,
 815                                                  int offset, int endOffset) {
 816         // Need to find the common parent of pParagraph and paragraph.
 817         if(paragraph.getParentElement() == pParagraph.getParentElement()) {
 818             // The simple (and common) case that pParagraph and
 819             // paragraph have the same parent.
 820             ElementSpec spec = new ElementSpec(pattr, ElementSpec.EndTagType);
 821             parseBuffer.addElement(spec);
 822             spec = new ElementSpec(pattr, ElementSpec.StartTagType);
 823             parseBuffer.addElement(spec);
 824             if(pParagraph.getEndOffset() != endOffset)
 825                 return ElementSpec.JoinFractureDirection;
 826 
 827             Element parent = pParagraph.getParentElement();
 828             if((parent.getElementIndex(offset) + 1) < parent.getElementCount())
 829                 return ElementSpec.JoinNextDirection;


1115 
1116     /** Listens to Styles. */
1117     private transient ChangeListener styleChangeListener;
1118 
1119     /** Listens to Styles. */
1120     private transient ChangeListener styleContextChangeListener;
1121 
1122     /** Run to create a change event for the document */
1123     private transient ChangeUpdateRunnable updateRunnable;
1124 
1125     /**
1126      * Default root element for a document... maps out the
1127      * paragraphs/lines contained.
1128      * <p>
1129      * <strong>Warning:</strong>
1130      * Serialized objects of this class will not be compatible with
1131      * future Swing releases. The current serialization support is
1132      * appropriate for short term storage or RMI between applications running
1133      * the same version of Swing.  As of 1.4, support for long term storage
1134      * of all JavaBeans&trade;
1135      * has been added to the <code>java.beans</code> package.
1136      * Please see {@link java.beans.XMLEncoder}.
1137      */
1138     @SuppressWarnings("serial") // Same-version serialization only
1139     protected class SectionElement extends BranchElement {
1140 
1141         /**
1142          * Creates a new SectionElement.
1143          */
1144         public SectionElement() {
1145             super(null, null);
1146         }
1147 
1148         /**
1149          * Gets the name of the element.
1150          *
1151          * @return the name
1152          */
1153         public String getName() {
1154             return SectionElementName;
1155         }
1156     }
1157 
1158     /**
1159      * Specification for building elements.
1160      * <p>
1161      * <strong>Warning:</strong>
1162      * Serialized objects of this class will not be compatible with
1163      * future Swing releases. The current serialization support is
1164      * appropriate for short term storage or RMI between applications running
1165      * the same version of Swing.  As of 1.4, support for long term storage
1166      * of all JavaBeans&trade;
1167      * has been added to the <code>java.beans</code> package.
1168      * Please see {@link java.beans.XMLEncoder}.
1169      */
1170     @SuppressWarnings("serial") // Same-version serialization only
1171     public static class ElementSpec {
1172 
1173         /**
1174          * A possible value for getType.  This specifies
1175          * that this record type is a start tag and
1176          * represents markup that specifies the start
1177          * of an element.
1178          */
1179         public static final short StartTagType = 1;
1180 
1181         /**
1182          * A possible value for getType.  This specifies
1183          * that this record type is a end tag and
1184          * represents markup that specifies the end
1185          * of an element.
1186          */
1187         public static final short EndTagType = 2;


1383 
1384         private AttributeSet attr;
1385         private int len;
1386         private short type;
1387         private short direction;
1388 
1389         private int offs;
1390         private char[] data;
1391     }
1392 
1393     /**
1394      * Class to manage changes to the element
1395      * hierarchy.
1396      * <p>
1397      * <strong>Warning:</strong>
1398      * Serialized objects of this class will not be compatible with
1399      * future Swing releases. The current serialization support is
1400      * appropriate for short term storage or RMI between applications running
1401      * the same version of Swing.  As of 1.4, support for long term storage
1402      * of all JavaBeans&trade;
1403      * has been added to the <code>java.beans</code> package.
1404      * Please see {@link java.beans.XMLEncoder}.
1405      */
1406     @SuppressWarnings("serial") // Same-version serialization only
1407     public class ElementBuffer implements Serializable {
1408 
1409         /**
1410          * Creates a new ElementBuffer.
1411          *
1412          * @param root the root element
1413          * @since 1.4
1414          */
1415         public ElementBuffer(Element root) {
1416             this.root = root;
1417             changes = new Vector<ElemChanges>();
1418             path = new Stack<ElemChanges>();
1419         }
1420 
1421         /**
1422          * Gets the root element.
1423          *


1912                         Element leaf = createLeafElement(ec.parent, first.
1913                                  getAttributes(), pos, first.getEndOffset());
1914                         ec.added.addElement(leaf);
1915                         ec.removed.addElement(first);
1916                     }
1917                     else {
1918                         // Parent was fractured element.
1919                         Element first = ec.parent.getElement(0);
1920                         Element leaf = createLeafElement(ec.parent, first.
1921                                  getAttributes(), pos, first.getEndOffset());
1922                         ec.added.addElement(leaf);
1923                         ec.removed.addElement(first);
1924                     }
1925                 }
1926                 pos += len;
1927                 break;
1928             }
1929         }
1930 
1931         /**
1932          * Remove the elements from <code>elem</code> in range
1933          * <code>rmOffs0</code>, <code>rmOffs1</code>. This uses
1934          * <code>canJoin</code> and <code>join</code> to handle joining
1935          * the endpoints of the insertion.
1936          *
1937          * @return true if elem will no longer have any elements.
1938          */
1939         boolean removeElements(Element elem, int rmOffs0, int rmOffs1) {
1940             if (! elem.isLeaf()) {
1941                 // update path for changes
1942                 int index0 = elem.getElementIndex(rmOffs0);
1943                 int index1 = elem.getElementIndex(rmOffs1);
1944                 push(elem, index0);
1945                 ElemChanges ec = path.peek();
1946 
1947                 // if the range is contained by one element,
1948                 // we just forward the request
1949                 if (index0 == index1) {
1950                     Element child0 = elem.getElement(index0);
1951                     if(rmOffs0 <= child0.getStartOffset() &&
1952                        rmOffs1 >= child0.getEndOffset()) {
1953                         // Element totally removed.
1954                         ec.removed.addElement(child0);


2154             }
2155             Element e = createBranchElement(parent, clonee.getAttributes());
2156             int n = clonee.getElementCount();
2157             ArrayList<Element> childrenList = new ArrayList<Element>(n);
2158             for (int i = 0; i < n; i++) {
2159                 Element elem = clonee.getElement(i);
2160                 if (elem.getStartOffset() < rmOffs0 || elem.getEndOffset() > rmOffs1) {
2161                     childrenList.add(cloneAsNecessary(e, elem, rmOffs0, rmOffs1));
2162                 }
2163             }
2164             Element[] children = new Element[childrenList.size()];
2165             children = childrenList.toArray(children);
2166             ((BranchElement)e).replace(0, 0, children);
2167             return e;
2168         }
2169 
2170         /**
2171          * Determines if a fracture needs to be performed. A fracture
2172          * can be thought of as moving the right part of a tree to a
2173          * new location, where the right part is determined by what has
2174          * been inserted. <code>depth</code> is used to indicate a
2175          * JoinToFracture is needed to an element at a depth
2176          * of <code>depth</code>. Where the root is 0, 1 is the children
2177          * of the root...
2178          * <p>This will invoke <code>fractureFrom</code> if it is determined
2179          * a fracture needs to happen.
2180          */
2181         void fracture(int depth) {
2182             int cLength = insertPath.length;
2183             int lastIndex = -1;
2184             boolean needRecreate = recreateLeafs;
2185             ElemChanges lastChange = insertPath[cLength - 1];
2186             // Use childAltered to determine when a child has been altered,
2187             // that is the point of insertion is less than the element count.
2188             boolean childAltered = ((lastChange.index + 1) <
2189                                     lastChange.parent.getElementCount());
2190             int deepestAlteredIndex = (needRecreate) ? cLength : -1;
2191             int lastAlteredIndex = cLength - 1;
2192 
2193             createdFracture = true;
2194             // Determine where to start recreating from.
2195             // Start at - 2, as first one is indicated by recreateLeafs and
2196             // childAltered.
2197             for(int counter = cLength - 2; counter >= 0; counter--) {
2198                 ElemChanges change = insertPath[counter];


2204                             deepestAlteredIndex = lastAlteredIndex + 1;
2205                     }
2206                 }
2207                 if(!childAltered && change.index <
2208                    change.parent.getElementCount()) {
2209                     childAltered = true;
2210                     lastAlteredIndex = counter;
2211                 }
2212             }
2213             if(needRecreate) {
2214                 // Recreate all children to right of parent starting
2215                 // at lastIndex.
2216                 if(lastIndex == -1)
2217                     lastIndex = cLength - 1;
2218                 fractureFrom(insertPath, lastIndex, deepestAlteredIndex);
2219             }
2220         }
2221 
2222         /**
2223          * Recreates the elements to the right of the insertion point.
2224          * This starts at <code>startIndex</code> in <code>changed</code>,
2225          * and calls duplicate to duplicate existing elements.
2226          * This will also duplicate the elements along the insertion
2227          * point, until a depth of <code>endFractureIndex</code> is
2228          * reached, at which point only the elements to the right of
2229          * the insertion point are duplicated.
2230          */
2231         void fractureFrom(ElemChanges[] changed, int startIndex,
2232                           int endFractureIndex) {
2233             // Recreate the element representing the inserted index.
2234             ElemChanges change = changed[startIndex];
2235             Element child;
2236             Element newChild;
2237             int changeLength = changed.length;
2238 
2239             if((startIndex + 1) == changeLength)
2240                 child = change.parent.getElement(change.index);
2241             else
2242                 child = change.parent.getElement(change.index - 1);
2243             if(child.isLeaf()) {
2244                 newChild = createLeafElement(change.parent,
2245                                child.getAttributes(), Math.max(endOffset,
2246                                child.getStartOffset()), child.getEndOffset());
2247             }


2319                     else {
2320                         // Last leaf, need to recreate part of it.
2321                         moveStartIndex = change.index + 1;
2322                     }
2323                     kids = new Element[kidsToMove];
2324                     kids[0] = newChild;
2325                 }
2326 
2327                 for(int counter = kidStartIndex; counter < kidsToMove;
2328                     counter++) {
2329                     Element toMove =change.parent.getElement(moveStartIndex++);
2330                     kids[counter] = recreateFracturedElement(parent, toMove);
2331                     change.removed.addElement(toMove);
2332                 }
2333                 ((BranchElement)parent).replace(0, 0, kids);
2334                 parent = newChild;
2335             }
2336         }
2337 
2338         /**
2339          * Recreates <code>toDuplicate</code>. This is called when an
2340          * element needs to be created as the result of an insertion. This
2341          * will recurse and create all the children. This is similar to
2342          * <code>clone</code>, but deteremines the offsets differently.
2343          */
2344         Element recreateFracturedElement(Element parent, Element toDuplicate) {
2345             if(toDuplicate.isLeaf()) {
2346                 return createLeafElement(parent, toDuplicate.getAttributes(),
2347                                          Math.max(toDuplicate.getStartOffset(),
2348                                                   endOffset),
2349                                          toDuplicate.getEndOffset());
2350             }
2351             // Not a leaf
2352             Element newParent = createBranchElement(parent, toDuplicate.
2353                                                     getAttributes());
2354             int childCount = toDuplicate.getElementCount();
2355             Element[] newKids = new Element[childCount];
2356             for(int counter = 0; counter < childCount; counter++) {
2357                 newKids[counter] = recreateFracturedElement(newParent,
2358                                              toDuplicate.getElement(counter));
2359             }
2360             ((BranchElement)newParent).replace(0, 0, newKids);
2361             return newParent;
2362         }
2363 
2364         /**
2365          * Splits the bottommost leaf in <code>path</code>.
2366          * This is called from insert when the first element is NOT content.
2367          */
2368         void fractureDeepestLeaf(ElementSpec[] specs) {
2369             // Split the bottommost leaf. It will be recreated elsewhere.
2370             ElemChanges ec = path.peek();
2371             Element child = ec.parent.getElement(ec.index);
2372             // Inserts at offset 0 do not need to recreate child (it would
2373             // have a length of 0!).
2374             if (offset != 0) {
2375                 Element newChild = createLeafElement(ec.parent,
2376                                                  child.getAttributes(),
2377                                                  child.getStartOffset(),
2378                                                  offset);
2379 
2380                 ec.added.addElement(newChild);
2381             }
2382             ec.removed.addElement(child);
2383             if(child.getEndOffset() != endOffset)
2384                 recreateLeafs = true;
2385             else


2710      */
2711     static class StyleChangeHandler extends AbstractChangeHandler {
2712 
2713         StyleChangeHandler(DefaultStyledDocument d) {
2714             super(d);
2715         }
2716 
2717         void fireStateChanged(DefaultStyledDocument d, ChangeEvent e) {
2718             Object source = e.getSource();
2719             if (source instanceof Style) {
2720                 d.styleChanged((Style) source);
2721             } else {
2722                 d.styleChanged(null);
2723             }
2724         }
2725     }
2726 
2727 
2728     /**
2729      * Added to the StyleContext. When the StyleContext changes, this invokes
2730      * <code>updateStylesListeningTo</code>.
2731      */
2732     static class StyleContextChangeHandler extends AbstractChangeHandler {
2733 
2734         StyleContextChangeHandler(DefaultStyledDocument d) {
2735             super(d);
2736         }
2737 
2738         void fireStateChanged(DefaultStyledDocument d, ChangeEvent e) {
2739             d.updateStylesListeningTo();
2740         }
2741     }
2742 
2743 
2744     /**
2745      * When run this creates a change event for the complete document
2746      * and fires it.
2747      */
2748     class ChangeUpdateRunnable implements Runnable {
2749         boolean isPending = false;
2750 


  45 import javax.swing.undo.CannotUndoException;
  46 import javax.swing.undo.UndoableEdit;
  47 import javax.swing.SwingUtilities;
  48 import static sun.swing.SwingUtilities2.IMPLIED_CR;
  49 
  50 /**
  51  * A document that can be marked up with character and paragraph
  52  * styles in a manner similar to the Rich Text Format.  The element
  53  * structure for this document represents style crossings for
  54  * style runs.  These style runs are mapped into a paragraph element
  55  * structure (which may reside in some other structure).  The
  56  * style runs break at paragraph boundaries since logical styles are
  57  * assigned to paragraph boundaries.
  58  * <p>
  59  * <strong>Warning:</strong>
  60  * Serialized objects of this class will not be compatible with
  61  * future Swing releases. The current serialization support is
  62  * appropriate for short term storage or RMI between applications running
  63  * the same version of Swing.  As of 1.4, support for long term storage
  64  * of all JavaBeans&trade;
  65  * has been added to the {@code java.beans} package.
  66  * Please see {@link java.beans.XMLEncoder}.
  67  *
  68  * @author  Timothy Prinzing
  69  * @see     Document
  70  * @see     AbstractDocument
  71  */
  72 @SuppressWarnings("serial") // Same-version serialization only
  73 public class DefaultStyledDocument extends AbstractDocument implements StyledDocument {
  74 
  75     /**
  76      * Constructs a styled document.
  77      *
  78      * @param c  the container for the content
  79      * @param styles resources and style definitions which may
  80      *  be shared across documents
  81      */
  82     public DefaultStyledDocument(Content c, StyleContext styles) {
  83         super(c, styles);
  84         listeningStyles = new Vector<Style>();
  85         buffer = new ElementBuffer(createDefaultRoot());


 103      * and has a style context that is scoped by the lifetime
 104      * of the document and is not shared with other documents.
 105      */
 106     public DefaultStyledDocument() {
 107         this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
 108     }
 109 
 110     /**
 111      * Gets the default root element.
 112      *
 113      * @return the root
 114      * @see Document#getDefaultRootElement
 115      */
 116     public Element getDefaultRootElement() {
 117         return buffer.getRootElement();
 118     }
 119 
 120     /**
 121      * Initialize the document to reflect the given element
 122      * structure (i.e. the structure reported by the
 123      * {@code getDefaultRootElement} method.  If the
 124      * document contained any data it will first be removed.
 125      * @param data the element data
 126      */
 127     protected void create(ElementSpec[] data) {
 128         try {
 129             if (getLength() != 0) {
 130                 remove(0, getLength());
 131             }
 132             writeLock();
 133 
 134             // install the content
 135             Content c = getContent();
 136             int n = data.length;
 137             StringBuilder sb = new StringBuilder();
 138             for (int i = 0; i < n; i++) {
 139                 ElementSpec es = data[i];
 140                 if (es.getLength() > 0) {
 141                     sb.append(es.getArray(), es.getOffset(),  es.getLength());
 142                 }
 143             }


 232      * the text in the range identified by the element.  If the
 233      * element isn't associated with the document, {@code
 234      * IllegalArgumentException} is thrown.</p>
 235      *
 236      * <p>As empty branch elements are not allowed in the document, if the
 237      * element is the sole child, its parent element is removed as well,
 238      * recursively.  This means that when replacing all the children of a
 239      * particular element, new children should be added <em>before</em>
 240      * removing old children.
 241      *
 242      * <p>Element removal results in two events being fired, the
 243      * {@code DocumentEvent} for changes in element structure and {@code
 244      * UndoableEditEvent} for changes in document content.</p>
 245      *
 246      * <p>If the element contains end-of-content mark (the last {@code
 247      * "\n"} character in document), this character is not removed;
 248      * instead, preceding leaf element is extended to cover the
 249      * character.  If the last leaf already ends with {@code "\n",} it is
 250      * included in content removal.</p>
 251      *
 252      * <p>If the element is {@code null, NullPointerException} is
 253      * thrown.  If the element structure would become invalid after the removal,
 254      * for example if the element is the document root element, {@code
 255      * IllegalArgumentException} is thrown.  If the current element structure is
 256      * invalid, {@code IllegalStateException} is thrown.</p>
 257      *
 258      * @param  elem                      the element to remove
 259      * @throws NullPointerException      if the element is {@code null}
 260      * @throws IllegalArgumentException  if the element could not be removed
 261      * @throws IllegalStateException     if the element structure is invalid
 262      *
 263      * @since  1.7
 264      */
 265     public void removeElement(Element elem) {
 266         try {
 267             writeLock();
 268             removeElementImpl(elem);
 269         } finally {
 270             writeUnlock();
 271         }
 272     }


 570                 }
 571                 attr.addAttributes(s);
 572                 if (isI18N && !hasRuns) {
 573                     hasRuns = (attr.getAttribute(TextAttribute.RUN_DIRECTION) != null);
 574                 }
 575             }
 576 
 577             if (hasRuns) {
 578                 updateBidi( changes );
 579             }
 580 
 581             changes.end();
 582             fireChangedUpdate(changes);
 583             fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
 584         } finally {
 585             writeUnlock();
 586         }
 587     }
 588 
 589     /**
 590      * Gets the paragraph element at the offset {@code pos}.
 591      * A paragraph consists of at least one child Element, which is usually
 592      * a leaf.
 593      *
 594      * @param pos the starting offset &gt;= 0
 595      * @return the element
 596      */
 597     public Element getParagraphElement(int pos) {
 598         Element e;
 599         for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
 600             int index = e.getElementIndex(pos);
 601             e = e.getElement(index);
 602         }
 603         if(e != null)
 604             return e.getParentElement();
 605         return e;
 606     }
 607 
 608     /**
 609      * Gets a character element based on a position.
 610      *


 788                 // Assure that the composed text element is named properly
 789                 // and doesn't have the CR attribute defined.
 790                 mattr.addAttribute(StyleConstants.NameAttribute,
 791                         AbstractDocument.ContentElementName);
 792                 if (mattr.isDefined(IMPLIED_CR)) {
 793                     mattr.removeAttribute(IMPLIED_CR);
 794                 }
 795             }
 796 
 797             ElementSpec[] spec = new ElementSpec[parseBuffer.size()];
 798             parseBuffer.copyInto(spec);
 799             buffer.insert(offset, length, spec, chng);
 800         } catch (BadLocationException bl) {
 801         }
 802 
 803         super.insertUpdate( chng, attr );
 804     }
 805 
 806     /**
 807      * This is called by insertUpdate when inserting after a new line.
 808      * It generates, in {@code parseBuffer}, ElementSpecs that will
 809      * position the stack in {@code paragraph}.<p>
 810      * It returns the direction the last StartSpec should have (this don't
 811      * necessarily create the last start spec).
 812      */
 813     short createSpecsForInsertAfterNewline(Element paragraph,
 814             Element pParagraph, AttributeSet pattr, Vector<ElementSpec> parseBuffer,
 815                                                  int offset, int endOffset) {
 816         // Need to find the common parent of pParagraph and paragraph.
 817         if(paragraph.getParentElement() == pParagraph.getParentElement()) {
 818             // The simple (and common) case that pParagraph and
 819             // paragraph have the same parent.
 820             ElementSpec spec = new ElementSpec(pattr, ElementSpec.EndTagType);
 821             parseBuffer.addElement(spec);
 822             spec = new ElementSpec(pattr, ElementSpec.StartTagType);
 823             parseBuffer.addElement(spec);
 824             if(pParagraph.getEndOffset() != endOffset)
 825                 return ElementSpec.JoinFractureDirection;
 826 
 827             Element parent = pParagraph.getParentElement();
 828             if((parent.getElementIndex(offset) + 1) < parent.getElementCount())
 829                 return ElementSpec.JoinNextDirection;


1115 
1116     /** Listens to Styles. */
1117     private transient ChangeListener styleChangeListener;
1118 
1119     /** Listens to Styles. */
1120     private transient ChangeListener styleContextChangeListener;
1121 
1122     /** Run to create a change event for the document */
1123     private transient ChangeUpdateRunnable updateRunnable;
1124 
1125     /**
1126      * Default root element for a document... maps out the
1127      * paragraphs/lines contained.
1128      * <p>
1129      * <strong>Warning:</strong>
1130      * Serialized objects of this class will not be compatible with
1131      * future Swing releases. The current serialization support is
1132      * appropriate for short term storage or RMI between applications running
1133      * the same version of Swing.  As of 1.4, support for long term storage
1134      * of all JavaBeans&trade;
1135      * has been added to the {@code java.beans} package.
1136      * Please see {@link java.beans.XMLEncoder}.
1137      */
1138     @SuppressWarnings("serial") // Same-version serialization only
1139     protected class SectionElement extends BranchElement {
1140 
1141         /**
1142          * Creates a new SectionElement.
1143          */
1144         public SectionElement() {
1145             super(null, null);
1146         }
1147 
1148         /**
1149          * Gets the name of the element.
1150          *
1151          * @return the name
1152          */
1153         public String getName() {
1154             return SectionElementName;
1155         }
1156     }
1157 
1158     /**
1159      * Specification for building elements.
1160      * <p>
1161      * <strong>Warning:</strong>
1162      * Serialized objects of this class will not be compatible with
1163      * future Swing releases. The current serialization support is
1164      * appropriate for short term storage or RMI between applications running
1165      * the same version of Swing.  As of 1.4, support for long term storage
1166      * of all JavaBeans&trade;
1167      * has been added to the {@code java.beans} package.
1168      * Please see {@link java.beans.XMLEncoder}.
1169      */
1170     @SuppressWarnings("serial") // Same-version serialization only
1171     public static class ElementSpec {
1172 
1173         /**
1174          * A possible value for getType.  This specifies
1175          * that this record type is a start tag and
1176          * represents markup that specifies the start
1177          * of an element.
1178          */
1179         public static final short StartTagType = 1;
1180 
1181         /**
1182          * A possible value for getType.  This specifies
1183          * that this record type is a end tag and
1184          * represents markup that specifies the end
1185          * of an element.
1186          */
1187         public static final short EndTagType = 2;


1383 
1384         private AttributeSet attr;
1385         private int len;
1386         private short type;
1387         private short direction;
1388 
1389         private int offs;
1390         private char[] data;
1391     }
1392 
1393     /**
1394      * Class to manage changes to the element
1395      * hierarchy.
1396      * <p>
1397      * <strong>Warning:</strong>
1398      * Serialized objects of this class will not be compatible with
1399      * future Swing releases. The current serialization support is
1400      * appropriate for short term storage or RMI between applications running
1401      * the same version of Swing.  As of 1.4, support for long term storage
1402      * of all JavaBeans&trade;
1403      * has been added to the {@code java.beans} package.
1404      * Please see {@link java.beans.XMLEncoder}.
1405      */
1406     @SuppressWarnings("serial") // Same-version serialization only
1407     public class ElementBuffer implements Serializable {
1408 
1409         /**
1410          * Creates a new ElementBuffer.
1411          *
1412          * @param root the root element
1413          * @since 1.4
1414          */
1415         public ElementBuffer(Element root) {
1416             this.root = root;
1417             changes = new Vector<ElemChanges>();
1418             path = new Stack<ElemChanges>();
1419         }
1420 
1421         /**
1422          * Gets the root element.
1423          *


1912                         Element leaf = createLeafElement(ec.parent, first.
1913                                  getAttributes(), pos, first.getEndOffset());
1914                         ec.added.addElement(leaf);
1915                         ec.removed.addElement(first);
1916                     }
1917                     else {
1918                         // Parent was fractured element.
1919                         Element first = ec.parent.getElement(0);
1920                         Element leaf = createLeafElement(ec.parent, first.
1921                                  getAttributes(), pos, first.getEndOffset());
1922                         ec.added.addElement(leaf);
1923                         ec.removed.addElement(first);
1924                     }
1925                 }
1926                 pos += len;
1927                 break;
1928             }
1929         }
1930 
1931         /**
1932          * Remove the elements from {@code elem} in range
1933          * {@code rmOffs0}, {@code rmOffs1}. This uses
1934          * {@code canJoin} and {@code join} to handle joining
1935          * the endpoints of the insertion.
1936          *
1937          * @return true if elem will no longer have any elements.
1938          */
1939         boolean removeElements(Element elem, int rmOffs0, int rmOffs1) {
1940             if (! elem.isLeaf()) {
1941                 // update path for changes
1942                 int index0 = elem.getElementIndex(rmOffs0);
1943                 int index1 = elem.getElementIndex(rmOffs1);
1944                 push(elem, index0);
1945                 ElemChanges ec = path.peek();
1946 
1947                 // if the range is contained by one element,
1948                 // we just forward the request
1949                 if (index0 == index1) {
1950                     Element child0 = elem.getElement(index0);
1951                     if(rmOffs0 <= child0.getStartOffset() &&
1952                        rmOffs1 >= child0.getEndOffset()) {
1953                         // Element totally removed.
1954                         ec.removed.addElement(child0);


2154             }
2155             Element e = createBranchElement(parent, clonee.getAttributes());
2156             int n = clonee.getElementCount();
2157             ArrayList<Element> childrenList = new ArrayList<Element>(n);
2158             for (int i = 0; i < n; i++) {
2159                 Element elem = clonee.getElement(i);
2160                 if (elem.getStartOffset() < rmOffs0 || elem.getEndOffset() > rmOffs1) {
2161                     childrenList.add(cloneAsNecessary(e, elem, rmOffs0, rmOffs1));
2162                 }
2163             }
2164             Element[] children = new Element[childrenList.size()];
2165             children = childrenList.toArray(children);
2166             ((BranchElement)e).replace(0, 0, children);
2167             return e;
2168         }
2169 
2170         /**
2171          * Determines if a fracture needs to be performed. A fracture
2172          * can be thought of as moving the right part of a tree to a
2173          * new location, where the right part is determined by what has
2174          * been inserted. {@code depth} is used to indicate a
2175          * JoinToFracture is needed to an element at a depth
2176          * of {@code depth}. Where the root is 0, 1 is the children
2177          * of the root...
2178          * <p>This will invoke {@code fractureFrom} if it is determined
2179          * a fracture needs to happen.
2180          */
2181         void fracture(int depth) {
2182             int cLength = insertPath.length;
2183             int lastIndex = -1;
2184             boolean needRecreate = recreateLeafs;
2185             ElemChanges lastChange = insertPath[cLength - 1];
2186             // Use childAltered to determine when a child has been altered,
2187             // that is the point of insertion is less than the element count.
2188             boolean childAltered = ((lastChange.index + 1) <
2189                                     lastChange.parent.getElementCount());
2190             int deepestAlteredIndex = (needRecreate) ? cLength : -1;
2191             int lastAlteredIndex = cLength - 1;
2192 
2193             createdFracture = true;
2194             // Determine where to start recreating from.
2195             // Start at - 2, as first one is indicated by recreateLeafs and
2196             // childAltered.
2197             for(int counter = cLength - 2; counter >= 0; counter--) {
2198                 ElemChanges change = insertPath[counter];


2204                             deepestAlteredIndex = lastAlteredIndex + 1;
2205                     }
2206                 }
2207                 if(!childAltered && change.index <
2208                    change.parent.getElementCount()) {
2209                     childAltered = true;
2210                     lastAlteredIndex = counter;
2211                 }
2212             }
2213             if(needRecreate) {
2214                 // Recreate all children to right of parent starting
2215                 // at lastIndex.
2216                 if(lastIndex == -1)
2217                     lastIndex = cLength - 1;
2218                 fractureFrom(insertPath, lastIndex, deepestAlteredIndex);
2219             }
2220         }
2221 
2222         /**
2223          * Recreates the elements to the right of the insertion point.
2224          * This starts at {@code startIndex} in {@code changed},
2225          * and calls duplicate to duplicate existing elements.
2226          * This will also duplicate the elements along the insertion
2227          * point, until a depth of {@code endFractureIndex} is
2228          * reached, at which point only the elements to the right of
2229          * the insertion point are duplicated.
2230          */
2231         void fractureFrom(ElemChanges[] changed, int startIndex,
2232                           int endFractureIndex) {
2233             // Recreate the element representing the inserted index.
2234             ElemChanges change = changed[startIndex];
2235             Element child;
2236             Element newChild;
2237             int changeLength = changed.length;
2238 
2239             if((startIndex + 1) == changeLength)
2240                 child = change.parent.getElement(change.index);
2241             else
2242                 child = change.parent.getElement(change.index - 1);
2243             if(child.isLeaf()) {
2244                 newChild = createLeafElement(change.parent,
2245                                child.getAttributes(), Math.max(endOffset,
2246                                child.getStartOffset()), child.getEndOffset());
2247             }


2319                     else {
2320                         // Last leaf, need to recreate part of it.
2321                         moveStartIndex = change.index + 1;
2322                     }
2323                     kids = new Element[kidsToMove];
2324                     kids[0] = newChild;
2325                 }
2326 
2327                 for(int counter = kidStartIndex; counter < kidsToMove;
2328                     counter++) {
2329                     Element toMove =change.parent.getElement(moveStartIndex++);
2330                     kids[counter] = recreateFracturedElement(parent, toMove);
2331                     change.removed.addElement(toMove);
2332                 }
2333                 ((BranchElement)parent).replace(0, 0, kids);
2334                 parent = newChild;
2335             }
2336         }
2337 
2338         /**
2339          * Recreates {@code toDuplicate}. This is called when an
2340          * element needs to be created as the result of an insertion. This
2341          * will recurse and create all the children. This is similar to
2342          * {@code clone}, but deteremines the offsets differently.
2343          */
2344         Element recreateFracturedElement(Element parent, Element toDuplicate) {
2345             if(toDuplicate.isLeaf()) {
2346                 return createLeafElement(parent, toDuplicate.getAttributes(),
2347                                          Math.max(toDuplicate.getStartOffset(),
2348                                                   endOffset),
2349                                          toDuplicate.getEndOffset());
2350             }
2351             // Not a leaf
2352             Element newParent = createBranchElement(parent, toDuplicate.
2353                                                     getAttributes());
2354             int childCount = toDuplicate.getElementCount();
2355             Element[] newKids = new Element[childCount];
2356             for(int counter = 0; counter < childCount; counter++) {
2357                 newKids[counter] = recreateFracturedElement(newParent,
2358                                              toDuplicate.getElement(counter));
2359             }
2360             ((BranchElement)newParent).replace(0, 0, newKids);
2361             return newParent;
2362         }
2363 
2364         /**
2365          * Splits the bottommost leaf in {@code path}.
2366          * This is called from insert when the first element is NOT content.
2367          */
2368         void fractureDeepestLeaf(ElementSpec[] specs) {
2369             // Split the bottommost leaf. It will be recreated elsewhere.
2370             ElemChanges ec = path.peek();
2371             Element child = ec.parent.getElement(ec.index);
2372             // Inserts at offset 0 do not need to recreate child (it would
2373             // have a length of 0!).
2374             if (offset != 0) {
2375                 Element newChild = createLeafElement(ec.parent,
2376                                                  child.getAttributes(),
2377                                                  child.getStartOffset(),
2378                                                  offset);
2379 
2380                 ec.added.addElement(newChild);
2381             }
2382             ec.removed.addElement(child);
2383             if(child.getEndOffset() != endOffset)
2384                 recreateLeafs = true;
2385             else


2710      */
2711     static class StyleChangeHandler extends AbstractChangeHandler {
2712 
2713         StyleChangeHandler(DefaultStyledDocument d) {
2714             super(d);
2715         }
2716 
2717         void fireStateChanged(DefaultStyledDocument d, ChangeEvent e) {
2718             Object source = e.getSource();
2719             if (source instanceof Style) {
2720                 d.styleChanged((Style) source);
2721             } else {
2722                 d.styleChanged(null);
2723             }
2724         }
2725     }
2726 
2727 
2728     /**
2729      * Added to the StyleContext. When the StyleContext changes, this invokes
2730      * {@code updateStylesListeningTo}.
2731      */
2732     static class StyleContextChangeHandler extends AbstractChangeHandler {
2733 
2734         StyleContextChangeHandler(DefaultStyledDocument d) {
2735             super(d);
2736         }
2737 
2738         void fireStateChanged(DefaultStyledDocument d, ChangeEvent e) {
2739             d.updateStylesListeningTo();
2740         }
2741     }
2742 
2743 
2744     /**
2745      * When run this creates a change event for the complete document
2746      * and fires it.
2747      */
2748     class ChangeUpdateRunnable implements Runnable {
2749         boolean isPending = false;
2750 
< prev index next >