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™
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 >= 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™
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™
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™
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™
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 >= 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™
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™
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™
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
|