0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: /**
0018: * @author Alexey A. Ivanov
0019: * @version $Revision$
0020: */package javax.swing.text;
0021:
0022: import java.awt.Color;
0023: import java.awt.Font;
0024: import java.io.IOException;
0025: import java.io.ObjectInputStream;
0026: import java.io.ObjectOutputStream;
0027: import java.io.Serializable;
0028: import java.util.ArrayList;
0029: import java.util.Enumeration;
0030: import java.util.LinkedList;
0031: import java.util.List;
0032: import java.util.Stack;
0033:
0034: import javax.swing.event.ChangeEvent;
0035: import javax.swing.event.ChangeListener;
0036: import javax.swing.event.DocumentListener;
0037: import javax.swing.event.UndoableEditEvent;
0038: import javax.swing.event.DocumentEvent.EventType;
0039: import javax.swing.undo.AbstractUndoableEdit;
0040: import javax.swing.undo.UndoableEdit;
0041:
0042: import org.apache.harmony.x.swing.internal.nls.Messages;
0043:
0044: public class DefaultStyledDocument extends AbstractDocument implements
0045: StyledDocument {
0046:
0047: public static class AttributeUndoableEdit extends
0048: AbstractUndoableEdit {
0049: protected AttributeSet copy;
0050: protected Element element;
0051: protected boolean isReplacing;
0052: protected AttributeSet newAttributes;
0053:
0054: public AttributeUndoableEdit(final Element element,
0055: final AttributeSet newAttributes,
0056: final boolean isReplacing) {
0057: this .element = element;
0058: this .newAttributes = newAttributes;
0059: this .isReplacing = isReplacing;
0060: this .copy = element.getAttributes().copyAttributes();
0061: }
0062:
0063: public void redo() {
0064: final AbstractElement elem = (AbstractElement) element;
0065: if (isReplacing) {
0066: elem.removeAttributes(elem);
0067: }
0068: elem.addAttributes(newAttributes);
0069: }
0070:
0071: public void undo() {
0072: final AbstractElement elem = (AbstractElement) element;
0073: elem.removeAttributes(newAttributes);
0074: elem.addAttributes(copy);
0075: }
0076: }
0077:
0078: public class ElementBuffer implements Serializable {
0079: private final Element root;
0080: private transient DefaultDocumentEvent event;
0081: private transient int offset;
0082: private transient int length;
0083: private transient ChangeDesc current;
0084:
0085: private transient Stack changeStack;
0086: private transient List changes;
0087: private transient boolean create;
0088: private transient Element tail;
0089:
0090: public ElementBuffer(final Element root) {
0091: this .root = root;
0092: initChangeLists();
0093: }
0094:
0095: public Element getRootElement() {
0096: return root;
0097: }
0098:
0099: public void insert(final int offset, final int length,
0100: final ElementSpec[] spec,
0101: final DefaultDocumentEvent event) {
0102: prepare(offset, length, event);
0103:
0104: insertUpdate(spec);
0105:
0106: collectEdits();
0107: }
0108:
0109: public void remove(final int offset, final int length,
0110: final DefaultDocumentEvent event) {
0111: prepare(offset, length, event);
0112:
0113: removeUpdate();
0114:
0115: applyEdits();
0116: collectEdits();
0117: }
0118:
0119: public void change(final int offset, final int length,
0120: final DefaultDocumentEvent event) {
0121: prepare(offset, length, event);
0122:
0123: changeUpdate();
0124:
0125: applyEdits();
0126: collectEdits();
0127: }
0128:
0129: public Element clone(final Element parent, final Element clonee) {
0130: if (clonee.isLeaf()) {
0131: return createLeafElement(parent,
0132: clonee.getAttributes(),
0133: clonee.getStartOffset(), clonee.getEndOffset());
0134: }
0135: BranchElement result = (BranchElement) createBranchElement(
0136: parent, clonee.getAttributes());
0137: final int count = clonee.getElementCount();
0138: if (count > 0) {
0139: Element[] children = new Element[count];
0140: for (int i = 0; i < count; i++) {
0141: children[i] = clone(result, clonee.getElement(i));
0142: }
0143: result.replace(0, 0, children);
0144: }
0145: return result;
0146: }
0147:
0148: protected void insertUpdate(final ElementSpec[] spec) {
0149: // Find the deepest branch
0150: Element branch = root;
0151: do {
0152: changeStack.push(new ChangeDesc(branch));
0153: branch = branch.getElement(branch
0154: .getElementIndex(offset));
0155: } while (!branch.isLeaf());
0156:
0157: current = (ChangeDesc) changeStack.peek();
0158:
0159: performSpecs(spec);
0160: leaveParagraph();
0161: }
0162:
0163: protected void removeUpdate() {
0164: final int endOffset = offset + length;
0165: final Element startLeaf = getCharacterElement(offset);
0166: final Element startBranch = startLeaf.getParentElement();
0167:
0168: final Element endLeaf = endOffset == startLeaf
0169: .getEndOffset()
0170: && endOffset < startBranch.getEndOffset() ? startLeaf
0171: : getCharacterElement(endOffset);
0172: final Element endBranch = endLeaf.getParentElement();
0173:
0174: if (startLeaf == endLeaf) {
0175: if (startLeaf.getStartOffset() == offset
0176: && endOffset == startLeaf.getEndOffset()) {
0177:
0178: current = new ChangeDesc(startBranch, offset);
0179: current.removed.add(startLeaf);
0180: changes.add(current);
0181: }
0182: } else if (startBranch == endBranch) {
0183: final int index = startBranch.getElementIndex(offset);
0184: current = new ChangeDesc(startBranch);
0185: for (int i = index; i < startBranch.getElementCount(); i++) {
0186: final Element child = startBranch.getElement(i);
0187: if (offset <= child.getStartOffset()
0188: && child.getEndOffset() <= endOffset) {
0189:
0190: current.setChildIndex(i);
0191: current.removed.add(child);
0192: }
0193: if (endOffset < child.getEndOffset()) {
0194: break;
0195: }
0196: }
0197:
0198: changes.add(current);
0199: } else {
0200: final BranchElement parent = (BranchElement) startBranch
0201: .getParentElement();
0202: if (parent != null) {
0203: current = new ChangeDesc(parent, offset);
0204:
0205: BranchElement branch = (BranchElement) createBranchElement(
0206: parent, startBranch.getAttributes());
0207: List children = new LinkedList();
0208:
0209: // Copy elements from startBranch
0210: int index = startBranch.getElementIndex(offset);
0211: if (startBranch.getElement(index).getStartOffset() < offset) {
0212: ++index;
0213: }
0214: for (int i = 0; i < index; i++) {
0215: children.add(clone(branch, startBranch
0216: .getElement(i)));
0217: }
0218:
0219: // Copy elements from endBranch
0220: index = endBranch.getElementIndex(endOffset);
0221: for (int i = index; i < endBranch.getElementCount(); i++) {
0222: children.add(clone(branch, endBranch
0223: .getElement(i)));
0224: }
0225:
0226: index = parent.getElementIndex(endOffset);
0227: for (int i = current.getChildIndex(); i <= index; i++) {
0228: current.removeChildElement(i);
0229: }
0230: current.added.add(branch);
0231:
0232: branch.replace(0, 0, listToElementArray(children));
0233: } else {
0234: current = new ChangeDesc(startBranch, offset);
0235:
0236: // Copy elements from endBranch
0237: int index = endBranch.getElementIndex(endOffset);
0238: for (int i = index; i < endBranch.getElementCount(); i++) {
0239: current.added.add(clone(startBranch, endBranch
0240: .getElement(i)));
0241: }
0242:
0243: // Copy elements from startBranch
0244: int startIndex = startBranch
0245: .getElementIndex(offset);
0246: int endIndex = startBranch
0247: .getElementIndex(endOffset);
0248: for (int i = startIndex; i <= endIndex; i++) {
0249: current.removeChildElement(i);
0250: }
0251: current.setChildIndex(startIndex);
0252:
0253: current.apply();
0254: }
0255:
0256: changes.add(current);
0257: }
0258: }
0259:
0260: protected void changeUpdate() {
0261: final int endOffset = offset + length;
0262: final Element startLeaf = getCharacterElement(offset);
0263: final Element endLeaf = getCharacterElement(endOffset);
0264:
0265: if (startLeaf.getStartOffset() == offset
0266: && endOffset == startLeaf.getEndOffset()) {
0267: return;
0268: }
0269:
0270: if (startLeaf == endLeaf) {
0271: current = new ChangeDesc(startLeaf.getParentElement(),
0272: offset);
0273: current.splitLeafElement(startLeaf, offset, endOffset,
0274: true, startLeaf.getAttributes());
0275:
0276: changes.add(current);
0277: } else {
0278: // Break the startLeaf
0279: int start = startLeaf.getStartOffset();
0280: int end = startLeaf.getEndOffset();
0281:
0282: if (start < offset) {
0283: current = new ChangeDesc(startLeaf
0284: .getParentElement(), offset);
0285: current.splitLeafElement(startLeaf, offset);
0286: changes.add(current);
0287: }
0288:
0289: // Break the endLeaf
0290: start = endLeaf.getStartOffset();
0291: end = endLeaf.getEndOffset();
0292:
0293: if (start < endOffset && endOffset < end) {
0294: final boolean sameParents = current != null
0295: && current.element == endLeaf
0296: .getParentElement();
0297: if (!sameParents) {
0298: current = new ChangeDesc(endLeaf
0299: .getParentElement(), endOffset);
0300: } else {
0301: final int endIndex = current
0302: .getChildIndexAtOffset(endOffset);
0303: for (int i = current.getChildIndex() + 1; i < endIndex; i++) {
0304:
0305: final Element child = current
0306: .getChildElement(i);
0307: current.removed.add(child);
0308: current.added.add(child);
0309: }
0310: }
0311:
0312: current.splitLeafElement(endLeaf, endOffset);
0313:
0314: if (!sameParents) {
0315: changes.add(current);
0316: }
0317: }
0318: }
0319: }
0320:
0321: final void create(final ElementSpec[] specs,
0322: final DefaultDocumentEvent event) {
0323: prepare(event.getOffset(), event.getLength(), event);
0324: create = true;
0325:
0326: // Remove all elements from the only paragraph
0327: current = new ChangeDesc(getParagraphElement(0));
0328: current.setChildIndex(0);
0329: current.createLeafElement(current.getChildElement(0)
0330: .getAttributes(), length, length + 1);
0331: for (int i = 0; i < current.element.getElementCount(); i++) {
0332: current.removeChildElement(i);
0333: }
0334: current.apply();
0335: changes.add(current);
0336: current = null;
0337:
0338: performSpecs(specs);
0339: leaveParagraph();
0340:
0341: collectEdits();
0342: }
0343:
0344: private void performSpecs(final ElementSpec[] spec)
0345: throws Error {
0346: for (int i = 0; i < spec.length; i++) {
0347: switch (spec[i].getType()) {
0348: case ElementSpec.ContentType:
0349: insertContent(spec[i]);
0350: break;
0351:
0352: case ElementSpec.EndTagType:
0353: insertEndTag();
0354: break;
0355:
0356: case ElementSpec.StartTagType:
0357: insertStartTag(spec[i]);
0358: break;
0359:
0360: default:
0361: throw new Error(Messages.getString("swing.err.12")); //$NON-NLS-1$
0362: }
0363: }
0364: }
0365:
0366: private void applyEdits() {
0367: for (int i = 0; i < changes.size(); i++) {
0368: final ChangeDesc desc = (ChangeDesc) changes.get(i);
0369: desc.apply();
0370: }
0371: }
0372:
0373: private void collectEdits() {
0374: while (!changeStack.empty()) {
0375: ChangeDesc desc = (ChangeDesc) changeStack.pop();
0376: if (!desc.isEmpty()) {
0377: changes.add(desc);
0378: }
0379: }
0380:
0381: for (int i = 0; i < changes.size(); i++) {
0382: final ChangeDesc desc = (ChangeDesc) changes.get(i);
0383: if (!desc.isEmpty()) {
0384: event.addEdit(desc.toElementEdit());
0385: }
0386: }
0387: changes.clear();
0388:
0389: clear();
0390: }
0391:
0392: private void clear() {
0393: event = null;
0394: current = null;
0395: }
0396:
0397: private void insertContent(final ElementSpec spec) {
0398: switch (spec.getDirection()) {
0399: case ElementSpec.OriginateDirection:
0400: insertContentOriginate(spec);
0401: break;
0402:
0403: case ElementSpec.JoinNextDirection:
0404: insertContentJoinNext(spec);
0405: break;
0406:
0407: case ElementSpec.JoinPreviousDirection:
0408: break;
0409:
0410: case ElementSpec.JoinFractureDirection:
0411: insertContentOriginate(spec);
0412: break;
0413: }
0414: offset += spec.getLength();
0415: length -= spec.getLength();
0416: }
0417:
0418: private void insertContentOriginate(final ElementSpec spec) {
0419: final AttributeSet specAttr = spec.getAttributes();
0420: if (current.element.getElementCount() == 0) {
0421: current.setChildIndex(0);
0422: current.createLeafElement(specAttr, offset, offset
0423: + spec.length);
0424: } else {
0425: current.setChildIndexByOffset(offset);
0426: final Element leafToRemove = current.getCurrentChild();
0427: if (offset == 0 && leafToRemove.isLeaf()) {
0428: current.removed.add(leafToRemove);
0429: current.createLeafElement(specAttr, offset, offset
0430: + spec.length);
0431: current.createLeafElement(leafToRemove
0432: .getAttributes(), offset + length,
0433: leafToRemove.getEndOffset());
0434: tail = current.getLastAddedElement();
0435: current.added.remove(tail);
0436: } else if (offset == event.getOffset()
0437: && leafToRemove.getStartOffset() < offset
0438: && offset < leafToRemove.getEndOffset()) {
0439: if (leafToRemove.isLeaf()) {
0440: current.splitLeafElement(leafToRemove, offset,
0441: offset + spec.length, offset + length,
0442: true, specAttr);
0443: tail = current.getLastAddedElement();
0444: current.added.remove(tail);
0445: } else {
0446: tail = splitBranch(leafToRemove);
0447: current.createLeafElement(specAttr, offset,
0448: offset + spec.length);
0449: current.childIndex = current.getChildIndex() + 1;
0450: }
0451: } else {
0452: current.createLeafElement(specAttr, offset, offset
0453: + spec.length);
0454: if (offset >= current.element.getEndOffset()
0455: && current.getChildIndex() < current.element
0456: .getElementCount()) {
0457: current.childIndex = current.getChildIndex() + 1;
0458: }
0459: }
0460: }
0461: }
0462:
0463: private void insertContentJoinNext(final ElementSpec spec) {
0464: current.setChildIndexByOffset(offset);
0465: final Element leaf = current.getCurrentChild();
0466: if (leaf.getStartOffset() >= offset) {
0467: current.removed.add(leaf);
0468: current.createLeafElement(leaf.getAttributes(), offset,
0469: leaf.getEndOffset());
0470: } else {
0471: final Element next = current.getChildElement(current
0472: .getChildIndex() + 1);
0473: current.removed.add(leaf);
0474: current.removed.add(next);
0475: current.createLeafElement(leaf.getAttributes(), leaf
0476: .getStartOffset(), offset);
0477: current.createLeafElement(next.getAttributes(), offset,
0478: next.getEndOffset());
0479: }
0480: }
0481:
0482: private void insertStartTag(final ElementSpec spec) {
0483: switch (spec.getDirection()) {
0484: case ElementSpec.OriginateDirection:
0485: insertStartOriginate(spec);
0486: break;
0487:
0488: case ElementSpec.JoinNextDirection:
0489: insertStartJoinNext(spec);
0490: break;
0491:
0492: case ElementSpec.JoinPreviousDirection:
0493: insertStartJoinPrevious(spec);
0494: break;
0495:
0496: case ElementSpec.JoinFractureDirection:
0497: insertStartFracture(spec);
0498: break;
0499:
0500: default:
0501: throw new Error(Messages.getString(
0502: "swing.err.13", "ElementSpec")); //$NON-NLS-1$ //$NON-NLS-2$
0503: }
0504: }
0505:
0506: private void insertStartFracture(final ElementSpec spec) {
0507: final AttributeSet attrs = spec.getDirection() == ElementSpec.OriginateDirection ? spec
0508: .getAttributes()
0509: : findLastExistedBranch().getAttributes();
0510: final BranchElement newBranch = (BranchElement) createBranchElement(
0511: current.element, attrs);
0512:
0513: final ChangeDesc lastChange = (ChangeDesc) changes
0514: .get(changes.size() - 1);
0515: int startIndex = lastChange.getChildIndexAtOffset(offset);
0516: if (lastChange.getChildElement(startIndex).getEndOffset() <= offset) {
0517: ++startIndex;
0518: }
0519: moveChildren(newBranch, lastChange, startIndex);
0520:
0521: current.added.add(newBranch);
0522: if (current.getChildIndex() == -1) {
0523: int newIndex = current.getChildIndexAtOffset(offset);
0524: if (newBranch.getElementCount() > 0
0525: && newBranch.getEndOffset() > current
0526: .getChildElement(newIndex)
0527: .getStartOffset()) {
0528: ++newIndex;
0529: }
0530: current.setChildIndex(newIndex);
0531: }
0532: if (current.isApplied()) {
0533: int replaceIndex = current
0534: .getChildIndexAtOffset(offset);
0535: if (newBranch.getElementCount() > 0
0536: && newBranch.getEndOffset() > current
0537: .getChildElement(replaceIndex)
0538: .getStartOffset()) {
0539: ++replaceIndex;
0540: }
0541: current.element.replace(replaceIndex, 0,
0542: new Element[] { newBranch });
0543: } else {
0544: current.apply();
0545: }
0546:
0547: current = new ChangeDesc(newBranch, true);
0548: changeStack.push(current);
0549: }
0550:
0551: private void moveChildren(final BranchElement newParent,
0552: final ChangeDesc sourceDesc, final int startIndex) {
0553: // copy all elements from lastBranch to the new one
0554: final int count = sourceDesc.element.getElementCount();
0555: final Element[] children = new Element[count - startIndex];
0556: for (int i = startIndex; i < count; i++) {
0557: children[i - startIndex] = clone(newParent, sourceDesc
0558: .getChildElement(i));
0559: }
0560: // Now we need to remove all previously added elements which were
0561: // copied from added list
0562: final int i = startIndex - sourceDesc.getChildIndex();
0563: for (int j = startIndex; j < count; j++) {
0564: final Object addedElement = sourceDesc
0565: .getAddedElement(i);
0566: final Object existingElement = sourceDesc
0567: .getChildElement(j);
0568: if (addedElement == existingElement) {
0569: sourceDesc.added.remove(addedElement);
0570: } else if (!sourceDesc.justCreated) {
0571: sourceDesc.removed.add(existingElement);
0572: }
0573: }
0574: // Complete the removal of elements from source
0575: if (count - startIndex > 0) {
0576: sourceDesc.element.replace(startIndex, count
0577: - startIndex, new Element[0]);
0578: }
0579:
0580: // Place copied children into the new parent
0581: newParent.replace(0, 0, children);
0582: }
0583:
0584: private void insertStartOriginate(final ElementSpec spec) {
0585: if (current == null) {
0586: insertStartJoinPrevious(spec);
0587: } else if (!create && !changes.isEmpty()) {
0588: insertStartFracture(spec);
0589: } else {
0590: Element branch = createBranchElement(current.element,
0591: spec.getAttributes());
0592: current.setChildIndexByOffset(offset);
0593: current.added.add(branch);
0594: current = new ChangeDesc(branch, true);
0595: changeStack.push(current);
0596: }
0597: }
0598:
0599: private void insertStartJoinNext(final ElementSpec spec) {
0600: current = new ChangeDesc(current.getChildAtOffset(offset));
0601: changeStack.push(current);
0602: }
0603:
0604: private void insertStartJoinPrevious(final ElementSpec spec) {
0605: if (current == null) {
0606: current = new ChangeDesc(getRootElement());
0607: // TODO are old attributes to be removed?
0608: final AttributeSet specAttr = spec.getAttributes();
0609: if (specAttr != null) {
0610: ((AbstractElement) getRootElement())
0611: .addAttributes(specAttr);
0612: }
0613: changeStack.push(current);
0614: } else {
0615: current = new ChangeDesc(current
0616: .getChildAtOffset(offset));
0617: changeStack.push(current);
0618: }
0619: }
0620:
0621: private void insertEndTag() {
0622: if (current.isEmpty()) {
0623: current.setChildIndexByOffset(offset);
0624: Element leaf = current.getCurrentChild();
0625: final int start = leaf.getStartOffset();
0626: final int end = leaf.getEndOffset();
0627: if (start < offset && offset < end
0628: || start < offset + length
0629: && offset + length < end) {
0630:
0631: if (leaf.isLeaf()) {
0632: current.splitLeafElement(leaf, offset, offset
0633: + length, false, null);
0634: } else if (length != 0) {
0635: BranchElement rightBranch = splitBranch(leaf);
0636: current.added.add(rightBranch);
0637: int newIndex = current
0638: .getChildIndexAtOffset(offset + length);
0639: if (rightBranch.getElementCount() > 0
0640: && rightBranch.getEndOffset() > current
0641: .getChildElement(newIndex)
0642: .getStartOffset()) {
0643: ++newIndex;
0644: }
0645: current.childIndex = newIndex;
0646: }
0647: }
0648: }
0649: leaveParagraph();
0650: changes.add(current);
0651: changeStack.pop();
0652: current = changeStack.empty() ? null
0653: : (ChangeDesc) changeStack.peek();
0654: }
0655:
0656: private BranchElement splitBranch(final Element branch) {
0657: BranchElement result = current.createBranchElement(branch
0658: .getAttributes());
0659: final ChangeDesc lastChange = (ChangeDesc) changes
0660: .get(changes.size() - 1);
0661: int startIndex = lastChange.getChildIndexAtOffset(offset
0662: + length);
0663: moveChildren(result, lastChange, startIndex);
0664: return result;
0665: }
0666:
0667: private BranchElement findLastExistedBranch() {
0668: int i = changes.size() - 1;
0669: ChangeDesc desc = null;
0670: while (i >= 0
0671: && (desc = (ChangeDesc) changes.get(i)).justCreated) {
0672: i--;
0673: }
0674: return i >= 0 ? desc.element : null;
0675: }
0676:
0677: private void leaveParagraph() {
0678: if (current == null || current.isEmpty()) {
0679: return;
0680: }
0681:
0682: if (tail != null) {
0683: current.added.add(tail);
0684: }
0685: tail = null;
0686: current.apply();
0687: }
0688:
0689: private Element[] listToElementArray(final List list) {
0690: return (Element[]) list.toArray(new Element[list.size()]);
0691: }
0692:
0693: private void initChangeLists() {
0694: changeStack = new Stack();
0695: changes = new ArrayList();
0696: }
0697:
0698: private void prepare(final int offset, final int length,
0699: final DefaultDocumentEvent event) {
0700: this .offset = offset;
0701: this .length = length;
0702: this .event = event;
0703:
0704: this .changes.clear();
0705: this .changeStack.clear();
0706: this .current = null;
0707:
0708: this .create = false;
0709: this .tail = null;
0710: }
0711:
0712: private void readObject(final ObjectInputStream ois)
0713: throws IOException, ClassNotFoundException {
0714:
0715: ois.defaultReadObject();
0716: initChangeLists();
0717: }
0718:
0719: private void writeObject(final ObjectOutputStream oos)
0720: throws IOException {
0721:
0722: oos.defaultWriteObject();
0723: }
0724: }
0725:
0726: public static class ElementSpec {
0727: public static final short ContentType = 3;
0728: public static final short EndTagType = 2;
0729: public static final short StartTagType = 1;
0730:
0731: public static final short JoinFractureDirection = 7;
0732: public static final short JoinNextDirection = 5;
0733: public static final short JoinPreviousDirection = 4;
0734: public static final short OriginateDirection = 6;
0735:
0736: private AttributeSet attrs;
0737: private short type;
0738: private char[] text;
0739: private int offset;
0740: private int length;
0741: private short direction;
0742:
0743: public ElementSpec(final AttributeSet attrs, final short type) {
0744: this (attrs, type, null, 0, 0);
0745: }
0746:
0747: public ElementSpec(final AttributeSet attrs, final short type,
0748: final char[] text, final int offset, final int length) {
0749: this .attrs = attrs;
0750: this .type = type;
0751: this .text = text;
0752: this .offset = offset;
0753: this .length = length;
0754:
0755: this .direction = OriginateDirection;
0756: }
0757:
0758: public ElementSpec(final AttributeSet attrs, final short type,
0759: final int length) {
0760: this (attrs, type, null, 0, length);
0761: }
0762:
0763: public char[] getArray() {
0764: return text;
0765: }
0766:
0767: public AttributeSet getAttributes() {
0768: return attrs;
0769: }
0770:
0771: public short getDirection() {
0772: return direction;
0773: }
0774:
0775: public int getLength() {
0776: return length;
0777: }
0778:
0779: public int getOffset() {
0780: return offset;
0781: }
0782:
0783: public short getType() {
0784: return type;
0785: }
0786:
0787: public void setDirection(final short direction) {
0788: this .direction = direction;
0789: }
0790:
0791: public void setType(final short type) {
0792: this .type = type;
0793: }
0794:
0795: /*
0796: * The format of the string is based on 1.5 release behavior
0797: * which can be revealed using the following code:
0798: *
0799: * Object obj = new DefaultStyledDocument.ElementSpec(null,
0800: * DefaultStyledDocument.ElementSpec.ContentType);
0801: * System.out.println(obj.toString());
0802: */
0803: public String toString() {
0804: String result;
0805: switch (type) {
0806: case StartTagType:
0807: result = "StartTag:";
0808: break;
0809: case ContentType:
0810: result = "Content:";
0811: break;
0812: case EndTagType:
0813: result = "EndTag:";
0814: break;
0815: default:
0816: result = "??:";
0817: }
0818:
0819: switch (direction) {
0820: case OriginateDirection:
0821: result += "Originate:";
0822: break;
0823: case JoinFractureDirection:
0824: result += "Fracture:";
0825: break;
0826: case JoinNextDirection:
0827: result += "JoinNext:";
0828: break;
0829: case JoinPreviousDirection:
0830: result += "JoinPrevious:";
0831: break;
0832: default:
0833: result += "??:";
0834: }
0835:
0836: result += length;
0837:
0838: return result;
0839: }
0840:
0841: }
0842:
0843: protected class SectionElement extends BranchElement {
0844: public SectionElement() {
0845: super (null, null);
0846: }
0847:
0848: public String getName() {
0849: return AbstractDocument.SectionElementName;
0850: }
0851: }
0852:
0853: private final class ChangeDesc {
0854: public final BranchElement element;
0855: private int childIndex = -1;
0856: public final List added = new ArrayList();
0857: public final List removed = new ArrayList();
0858: public final boolean justCreated;
0859: private boolean applied;
0860:
0861: public ChangeDesc(final Element element) {
0862: this (element, false);
0863: }
0864:
0865: public ChangeDesc(final Element element,
0866: final boolean justCreated) {
0867: this .element = (BranchElement) element;
0868: this .justCreated = justCreated;
0869: }
0870:
0871: public ChangeDesc(final Element element, final int offset) {
0872: this (element, false);
0873: setChildIndexByOffset(offset);
0874: }
0875:
0876: public void setChildIndex(final int index) {
0877: if (this .childIndex == -1) {
0878: this .childIndex = index;
0879: }
0880: }
0881:
0882: public int getChildIndex() {
0883: return childIndex;
0884: }
0885:
0886: public Element[] getChildrenAdded() {
0887: return (Element[]) added.toArray(new Element[added.size()]);
0888: }
0889:
0890: public Element[] getChildrenRemoved() {
0891: return (Element[]) removed.toArray(new Element[removed
0892: .size()]);
0893: }
0894:
0895: public ElementEdit toElementEdit() {
0896: return new ElementEdit(element, childIndex,
0897: getChildrenRemoved(), getChildrenAdded());
0898: }
0899:
0900: public void apply() {
0901: if (applied || isEmpty()) {
0902: return;
0903: }
0904: if (childIndex == -1) {
0905: childIndex = 0;
0906: }
0907:
0908: applied = true;
0909: element.replace(childIndex, removed.size(),
0910: getChildrenAdded());
0911: }
0912:
0913: public boolean isEmpty() {
0914: return removed.size() == 0 && added.size() == 0;
0915: }
0916:
0917: public boolean isApplied() {
0918: return applied;
0919: }
0920:
0921: public void createLeafElement(final AttributeSet attr,
0922: final int start, final int end) {
0923: added.add(DefaultStyledDocument.this .createLeafElement(
0924: element, attr, start, end));
0925: }
0926:
0927: public BranchElement createBranchElement(final AttributeSet attr) {
0928: return (BranchElement) DefaultStyledDocument.this
0929: .createBranchElement(element, attr);
0930: }
0931:
0932: public void splitLeafElement(final Element leaf,
0933: final int splitOffset) {
0934: final AttributeSet attrs = leaf.getAttributes();
0935: createLeafElement(attrs, leaf.getStartOffset(), splitOffset);
0936: createLeafElement(attrs, splitOffset, leaf.getEndOffset());
0937: removed.add(leaf);
0938: }
0939:
0940: public void splitLeafElement(final Element child,
0941: final int splitOffset1, final int splitOffset2,
0942: final boolean createMiddle,
0943: final AttributeSet middleAttr) {
0944: splitLeafElement(child, splitOffset1, splitOffset2,
0945: splitOffset2, createMiddle, middleAttr);
0946: }
0947:
0948: public void splitLeafElement(final Element child,
0949: final int splitOffset1, final int splitOffset2,
0950: final int splitOffset3, final boolean createMiddle,
0951: final AttributeSet middleAttr) {
0952: final AttributeSet attrs = child.getAttributes();
0953: if (child.getStartOffset() < splitOffset1) {
0954: createLeafElement(attrs, child.getStartOffset(),
0955: splitOffset1);
0956: }
0957: if (createMiddle) {
0958: createLeafElement(middleAttr, splitOffset1,
0959: splitOffset2);
0960: }
0961: if (splitOffset3 < child.getEndOffset()) {
0962: createLeafElement(attrs, splitOffset3, child
0963: .getEndOffset());
0964: }
0965: removed.add(child);
0966: }
0967:
0968: public void setChildIndexByOffset(final int offset) {
0969: setChildIndex(element.getElementIndex(offset));
0970: }
0971:
0972: public Element getChildAtOffset(final int offset) {
0973: return element.getElement(element.getElementIndex(offset));
0974: }
0975:
0976: public int getChildIndexAtOffset(final int offset) {
0977: return element.getElementIndex(offset);
0978: }
0979:
0980: public Element getCurrentChild() {
0981: return element.getElement(childIndex);
0982: }
0983:
0984: public Element getChildElement(final int index) {
0985: return element.getElement(index);
0986: }
0987:
0988: public void removeChildElement(final int index) {
0989: removed.add(element.getElement(index));
0990: }
0991:
0992: public Element getAddedElement(final int i) {
0993: return (i > 0 && i < added.size()) ? (Element) added.get(i)
0994: : null;
0995: }
0996:
0997: public Element getLastAddedElement() {
0998: return (Element) added.get(added.size() - 1);
0999: }
1000:
1001: public Element getLastRemovedElement() {
1002: return (Element) removed.get(removed.size() - 1);
1003: }
1004: }
1005:
1006: public static final int BUFFER_SIZE_DEFAULT = 4096;
1007: private transient AttributeSet defaultLogicalStyle;
1008:
1009: protected ElementBuffer buffer;
1010:
1011: private ChangeListener styleContextChangeListener;
1012: private ChangeListener styleChangeListener;
1013:
1014: public DefaultStyledDocument() {
1015: this (new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
1016: }
1017:
1018: public DefaultStyledDocument(final Content content,
1019: final StyleContext styles) {
1020: super (content, styles);
1021: createDefaultLogicalStyle();
1022: buffer = new ElementBuffer(createDefaultRoot());
1023: }
1024:
1025: public DefaultStyledDocument(final StyleContext styles) {
1026: this (new GapContent(BUFFER_SIZE_DEFAULT), styles);
1027: }
1028:
1029: public Style addStyle(final String name, final Style parent) {
1030: return getStyleContext().addStyle(name, parent);
1031: }
1032:
1033: public void removeStyle(final String name) {
1034: getStyleContext().removeStyle(name);
1035: }
1036:
1037: public Style getStyle(final String name) {
1038: return getStyleContext().getStyle(name);
1039: }
1040:
1041: public Enumeration<?> getStyleNames() {
1042: return getStyleContext().getStyleNames();
1043: }
1044:
1045: public Color getForeground(final AttributeSet attrs) {
1046: return getStyleContext().getForeground(attrs);
1047: }
1048:
1049: public Color getBackground(final AttributeSet attrs) {
1050: return getStyleContext().getBackground(attrs);
1051: }
1052:
1053: public Font getFont(final AttributeSet attrs) {
1054: return getStyleContext().getFont(attrs);
1055: }
1056:
1057: public Element getDefaultRootElement() {
1058: return buffer.getRootElement();
1059: }
1060:
1061: public Element getCharacterElement(final int offset) {
1062: final Element paragraph = getParagraphElement(offset);
1063: return paragraph.getElement(paragraph.getElementIndex(offset));
1064: }
1065:
1066: public Element getParagraphElement(final int offset) {
1067: Element branch;
1068: Element child = getDefaultRootElement();
1069: do {
1070: branch = child;
1071: child = branch.getElement(branch.getElementIndex(offset));
1072: } while (!child.isLeaf());
1073: return branch;
1074: }
1075:
1076: public void setCharacterAttributes(final int offset,
1077: final int length, final AttributeSet attrs,
1078: final boolean replace) {
1079: if (checkInvalid(offset, length)) {
1080: return;
1081: }
1082:
1083: writeLock();
1084: try {
1085: final DefaultDocumentEvent event = new DefaultDocumentEvent(
1086: offset, length, EventType.CHANGE);
1087:
1088: buffer.change(offset, length, event);
1089:
1090: AbstractElement element;
1091: int currentOffset = offset;
1092: final int limit = offset + length;
1093: while (currentOffset < limit) {
1094: element = (AbstractElement) getCharacterElement(currentOffset);
1095: event.addEdit(new AttributeUndoableEdit(element, attrs,
1096: replace));
1097: if (replace) {
1098: element.removeAttributes(element
1099: .getAttributeNames());
1100: }
1101: element.addAttributes(attrs);
1102: currentOffset = element.getEndOffset();
1103: }
1104:
1105: event.end();
1106: fireChangedUpdate(event);
1107: fireUndoableEditUpdate(new UndoableEditEvent(this , event));
1108: } finally {
1109: writeUnlock();
1110: }
1111: }
1112:
1113: public void setParagraphAttributes(final int offset,
1114: final int length, final AttributeSet attrs,
1115: final boolean replace) {
1116: if (checkInvalid(offset, length)) {
1117: return;
1118: }
1119:
1120: writeLock();
1121: try {
1122: final DefaultDocumentEvent event = new DefaultDocumentEvent(
1123: offset, length, EventType.CHANGE);
1124:
1125: AbstractElement element;
1126: int currentOffset = offset;
1127: final int limit = offset + length;
1128: while (currentOffset < limit) {
1129: element = (AbstractElement) getParagraphElement(currentOffset);
1130: event.addEdit(new AttributeUndoableEdit(element, attrs,
1131: replace));
1132: if (replace) {
1133: element.removeAttributes(element
1134: .getAttributeNames());
1135: }
1136: element.addAttributes(attrs);
1137: currentOffset = element.getEndOffset();
1138: }
1139:
1140: event.end();
1141: fireChangedUpdate(event);
1142: fireUndoableEditUpdate(new UndoableEditEvent(this , event));
1143: } finally {
1144: writeUnlock();
1145: }
1146: }
1147:
1148: public void setLogicalStyle(final int offset, final Style style) {
1149: final AbstractElement branch = (AbstractElement) getParagraphElement(offset);
1150: writeLock();
1151: try {
1152: branch.setResolveParent(style);
1153: } finally {
1154: writeUnlock();
1155: }
1156: }
1157:
1158: public Style getLogicalStyle(final int offset) {
1159: final Element element = getParagraphElement(offset);
1160: Object resolver = element.getAttributes().getResolveParent();
1161: return resolver instanceof Style ? (Style) resolver : null;
1162: }
1163:
1164: public void addDocumentListener(final DocumentListener listener) {
1165: super .addDocumentListener(listener);
1166: getStyleContext().addChangeListener(getStyleContextListener());
1167: addListenerToStyles();
1168: }
1169:
1170: public void removeDocumentListener(final DocumentListener listener) {
1171: super .removeDocumentListener(listener);
1172: if (getDocumentListeners().length == 0) {
1173: getStyleContext().removeChangeListener(
1174: getStyleContextListener());
1175: removeListenerFromStyles();
1176: }
1177: }
1178:
1179: protected AbstractElement createDefaultRoot() {
1180: final BranchElement result = new SectionElement();
1181: writeLock();
1182: try {
1183: final BranchElement paragraph = (BranchElement) createBranchElement(
1184: result, null);
1185: paragraph
1186: .setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
1187: final Element content = createLeafElement(paragraph, null,
1188: getStartPosition().getOffset(), getEndPosition()
1189: .getOffset());
1190: paragraph.replace(0, 0, new Element[] { content });
1191: result.replace(0, 0, new Element[] { paragraph });
1192: } finally {
1193: writeUnlock();
1194: }
1195: return result;
1196: }
1197:
1198: protected void create(final ElementSpec[] specs) {
1199: final StringBuffer text = appendSpecsText(specs);
1200:
1201: writeLock();
1202: try {
1203: if (getLength() > 0) {
1204: try {
1205: remove(0, getLength());
1206: } catch (BadLocationException e) {
1207: e.printStackTrace();
1208: }
1209: }
1210:
1211: final int offset = 0;
1212: UndoableEdit contentInsert = null;
1213: try {
1214: contentInsert = getContent().insertString(offset,
1215: text.toString());
1216: } catch (BadLocationException e) {
1217: e.printStackTrace();
1218: }
1219:
1220: DefaultDocumentEvent event = new DefaultDocumentEvent(
1221: offset, text.length(), EventType.INSERT);
1222: if (contentInsert != null) {
1223: event.addEdit(contentInsert);
1224: }
1225: event.addEdit(new AttributeUndoableEdit(buffer
1226: .getRootElement(), getStyleContext().getEmptySet(),
1227: true));
1228: ((AbstractElement) buffer.getRootElement())
1229: .removeAttributes(buffer.getRootElement()
1230: .getAttributes());
1231:
1232: buffer.create(specs, event);
1233:
1234: event.end();
1235: fireInsertUpdate(event);
1236: if (contentInsert != null) {
1237: fireUndoableEditUpdate(new UndoableEditEvent(this ,
1238: event));
1239: }
1240: } finally {
1241: writeUnlock();
1242: }
1243: }
1244:
1245: protected void insert(final int offset, final ElementSpec[] specs)
1246: throws BadLocationException {
1247:
1248: final StringBuffer text = appendSpecsText(specs);
1249: writeLock();
1250: try {
1251: UndoableEdit contentInsert = getContent().insertString(
1252: offset, text.toString());
1253:
1254: DefaultDocumentEvent event = new DefaultDocumentEvent(
1255: offset, text.length(), EventType.INSERT);
1256: if (contentInsert != null) {
1257: event.addEdit(contentInsert);
1258: }
1259:
1260: buffer.insert(offset, text.length(), specs, event);
1261:
1262: event.end();
1263: fireInsertUpdate(event);
1264: if (contentInsert != null) {
1265: fireUndoableEditUpdate(new UndoableEditEvent(this ,
1266: event));
1267: }
1268: } finally {
1269: writeUnlock();
1270: }
1271: }
1272:
1273: protected void insertUpdate(final DefaultDocumentEvent event,
1274: final AttributeSet attrs) {
1275: final AttributeSet attributes = attrs == null ? getStyleContext()
1276: .getEmptySet()
1277: : attrs;
1278:
1279: final List specs = new LinkedList();
1280:
1281: String text = null;
1282: final int offset = event.getOffset();
1283: final int length = event.getLength();
1284:
1285: try {
1286: text = getText(offset, length);
1287: } catch (final BadLocationException e) {
1288: }
1289:
1290: boolean splitPrevParagraph = false;
1291: try {
1292: splitPrevParagraph = offset > 0
1293: && getText(offset - 1, 1).charAt(0) == '\n';
1294: } catch (final BadLocationException e) {
1295: }
1296:
1297: final int firstBreak = text.indexOf('\n');
1298: final int lastBreak = text.lastIndexOf('\n');
1299: final boolean hasLineBreak = firstBreak != -1;
1300:
1301: Element charElem = getCharacterElement(offset);
1302: ElementSpec spec = null;
1303: if (!hasLineBreak) {
1304: if (splitPrevParagraph) {
1305: splitBranch(specs, offset, length, charElem,
1306: ElementSpec.JoinNextDirection);
1307: // The direction of the next Content element must be chosen
1308: // based on attributes of the first Content element
1309: // in the next paragraph
1310: charElem = getCharacterElement(offset + length);
1311: }
1312: spec = new ElementSpec(attributes, ElementSpec.ContentType,
1313: length);
1314: if (charElem.getAttributes().isEqual(attributes)) {
1315: spec
1316: .setDirection(splitPrevParagraph ? ElementSpec.JoinNextDirection
1317: : ElementSpec.JoinPreviousDirection);
1318: }
1319: specs.add(spec);
1320: } else {
1321: int currentOffset = offset;
1322: int currentIndex = firstBreak;
1323: int processedLength = 0;
1324:
1325: if (splitPrevParagraph) {
1326: splitBranch(specs, offset, length, charElem,
1327: ElementSpec.OriginateDirection);
1328: }
1329:
1330: while (currentOffset < offset + length) {
1331: if (!(currentIndex < 0)) {
1332: spec = new ElementSpec(attributes,
1333: ElementSpec.ContentType, currentIndex + 1
1334: - processedLength);
1335: currentOffset += spec.getLength();
1336: processedLength += spec.getLength();
1337: if (specs.size() == 0
1338: && charElem.getAttributes().isEqual(
1339: attributes)) {
1340:
1341: spec
1342: .setDirection(ElementSpec.JoinPreviousDirection);
1343: }
1344: specs.add(spec);
1345:
1346: specs.add(new ElementSpec(null,
1347: ElementSpec.EndTagType));
1348:
1349: spec = new ElementSpec(defaultLogicalStyle,
1350: ElementSpec.StartTagType);
1351: if (currentIndex == lastBreak) {
1352: spec
1353: .setDirection(splitPrevParagraph ? ElementSpec.JoinNextDirection
1354: : ElementSpec.JoinFractureDirection);
1355: }
1356: specs.add(spec);
1357:
1358: currentIndex = text.indexOf('\n', currentIndex + 1);
1359: } else {
1360: spec = new ElementSpec(attributes,
1361: ElementSpec.ContentType, length
1362: - processedLength);
1363: currentOffset += spec.getLength();
1364: processedLength += spec.getLength();
1365: if (getCharacterElement(currentOffset)
1366: .getAttributes().isEqual(attributes)) {
1367:
1368: spec
1369: .setDirection(ElementSpec.JoinNextDirection);
1370: }
1371: specs.add(spec);
1372: }
1373: }
1374:
1375: }
1376:
1377: final Object[] specArray = specs.toArray(new ElementSpec[specs
1378: .size()]);
1379: buffer.insert(offset, length, (ElementSpec[]) specArray, event);
1380:
1381: super .insertUpdate(event, attrs);
1382: }
1383:
1384: private void splitBranch(final List specs, final int offset,
1385: final int length, final Element leaf,
1386: final short lastSpecDirection) {
1387: ElementSpec spec = null;
1388: Element branch = leaf.getParentElement();
1389: final int endOffset = offset + length;
1390: while (branch != null && branch.getEndOffset() == endOffset) {
1391: specs.add(new ElementSpec(null, ElementSpec.EndTagType));
1392: branch = branch.getParentElement();
1393: }
1394:
1395: branch = branch.getElement(branch.getElementIndex(offset) + 1);
1396: while (branch != null && !branch.isLeaf()
1397: && branch.getStartOffset() == endOffset) {
1398: spec = new ElementSpec(branch.getAttributes(),
1399: ElementSpec.StartTagType);
1400: spec.setDirection(ElementSpec.JoinNextDirection);
1401: specs.add(spec);
1402: branch = branch.getElement(0);
1403: }
1404: spec.setDirection(lastSpecDirection);
1405: }
1406:
1407: protected void removeUpdate(final DefaultDocumentEvent event) {
1408: buffer.remove(event.getOffset(), event.getLength(), event);
1409: }
1410:
1411: protected void styleChanged(final Style style) {
1412: }
1413:
1414: private StringBuffer appendSpecsText(final ElementSpec[] specs) {
1415: final StringBuffer result = new StringBuffer();
1416: for (int i = 0; i < specs.length; i++) {
1417: if (specs[i].getLength() > 0) {
1418: result.append(specs[i].getArray(),
1419: specs[i].getOffset(), specs[i].getLength());
1420: }
1421: }
1422: return result;
1423: }
1424:
1425: private void addListenerToStyles() {
1426: final Enumeration names = getStyleNames();
1427: while (names.hasMoreElements()) {
1428: String name = (String) names.nextElement();
1429: getStyle(name).addChangeListener(getStyleChangeListener());
1430: }
1431: }
1432:
1433: private void removeListenerFromStyles() {
1434: final Enumeration names = getStyleNames();
1435: while (names.hasMoreElements()) {
1436: String name = (String) names.nextElement();
1437: getStyle(name).removeChangeListener(
1438: getStyleChangeListener());
1439: }
1440: }
1441:
1442: private boolean checkInvalid(final int offset, final int length) {
1443: return offset < 0 || length <= 0
1444: || offset + length > getLength() + 1;
1445: }
1446:
1447: private void createDefaultLogicalStyle() {
1448: final StyleContext styles = getStyleContext();
1449: defaultLogicalStyle = styles.addAttribute(styles.getEmptySet(),
1450: AttributeSet.ResolveAttribute, styles
1451: .getStyle(StyleContext.DEFAULT_STYLE));
1452: }
1453:
1454: private ChangeListener getStyleChangeListener() {
1455: if (styleChangeListener == null) {
1456: styleChangeListener = new ChangeListener() {
1457: public void stateChanged(final ChangeEvent e) {
1458: styleChanged((Style) e.getSource());
1459: }
1460: };
1461: }
1462: return styleChangeListener;
1463: }
1464:
1465: private ChangeListener getStyleContextListener() {
1466: if (styleContextChangeListener == null) {
1467: styleContextChangeListener = new ChangeListener() {
1468: public void stateChanged(final ChangeEvent e) {
1469: removeListenerFromStyles();
1470: addListenerToStyles();
1471: }
1472: };
1473: }
1474: return styleContextChangeListener;
1475: }
1476:
1477: private StyleContext getStyleContext() {
1478: return (StyleContext) getAttributeContext();
1479: }
1480:
1481: private void readObject(final ObjectInputStream ois)
1482: throws IOException, ClassNotFoundException {
1483:
1484: ois.defaultReadObject();
1485: createDefaultLogicalStyle();
1486: }
1487:
1488: private void writeObject(final ObjectOutputStream oos)
1489: throws IOException {
1490:
1491: oos.defaultWriteObject();
1492: }
1493: }
|