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: package javax.swing.text;
0018:
0019: import java.awt.font.TextAttribute;
0020: import java.io.IOException;
0021: import java.io.ObjectInputStream;
0022: import java.io.ObjectOutputStream;
0023: import java.io.PrintStream;
0024: import java.io.Serializable;
0025: import java.text.Bidi;
0026: import java.util.ArrayList;
0027: import java.util.Arrays;
0028: import java.util.Comparator;
0029: import java.util.Dictionary;
0030: import java.util.Enumeration;
0031: import java.util.EventListener;
0032: import java.util.HashMap;
0033: import java.util.Hashtable;
0034: import java.util.List;
0035:
0036: import javax.swing.UIDefaults;
0037: import javax.swing.UIManager;
0038: import javax.swing.event.DocumentEvent;
0039: import javax.swing.event.DocumentListener;
0040: import javax.swing.event.EventListenerList;
0041: import javax.swing.event.UndoableEditEvent;
0042: import javax.swing.event.UndoableEditListener;
0043: import javax.swing.event.DocumentEvent.ElementChange;
0044: import javax.swing.event.DocumentEvent.EventType;
0045: import javax.swing.tree.TreeNode;
0046: import javax.swing.undo.AbstractUndoableEdit;
0047: import javax.swing.undo.CompoundEdit;
0048: import javax.swing.undo.UndoableEdit;
0049:
0050: import org.apache.harmony.awt.text.PropertyNames;
0051: import org.apache.harmony.awt.text.TextUtils;
0052: import org.apache.harmony.x.swing.StringConstants;
0053:
0054: import org.apache.harmony.x.swing.internal.nls.Messages;
0055:
0056: public abstract class AbstractDocument implements Document,
0057: Serializable {
0058:
0059: public abstract class AbstractElement implements Element,
0060: MutableAttributeSet, Serializable, TreeNode {
0061:
0062: private static final String INDENT_LEVEL = " ";
0063: private static final int MAX_LEAF_CONTENT_LENGTH = 40;
0064:
0065: private transient AttributeSet attrs;
0066:
0067: private final Element parent;
0068:
0069: public AbstractElement(final Element parent,
0070: final AttributeSet attributes) {
0071: this .parent = parent;
0072:
0073: this .attrs = context.getEmptySet();
0074:
0075: if (attributes != null) {
0076: addAttributes(attributes);
0077: }
0078: }
0079:
0080: public abstract boolean getAllowsChildren();
0081:
0082: public abstract boolean isLeaf();
0083:
0084: public abstract Enumeration children();
0085:
0086: public abstract Element getElement(final int index);
0087:
0088: public abstract int getElementCount();
0089:
0090: public abstract int getElementIndex(final int offset);
0091:
0092: public abstract int getStartOffset();
0093:
0094: public abstract int getEndOffset();
0095:
0096: public void addAttribute(final Object key, final Object value) {
0097: checkWriteLock();
0098:
0099: attrs = context.addAttribute(attrs, key, value);
0100: }
0101:
0102: public void addAttributes(final AttributeSet attrSet) {
0103: checkWriteLock();
0104:
0105: attrs = context.addAttributes(attrs, attrSet);
0106: }
0107:
0108: public boolean containsAttribute(final Object key,
0109: final Object value) {
0110: return attrs.containsAttribute(key, value);
0111: }
0112:
0113: public boolean containsAttributes(final AttributeSet attrSet) {
0114: return attrs.containsAttributes(attrSet);
0115: }
0116:
0117: public AttributeSet copyAttributes() {
0118: return attrs.copyAttributes();
0119: }
0120:
0121: /**
0122: * Prints the element name and lists all attributes.
0123: */
0124: public void dump(final PrintStream ps, final int indent) {
0125: final String name = getName();
0126:
0127: String indentation = "";
0128: for (int i = indent; i > 0; i--) {
0129: indentation += INDENT_LEVEL;
0130: }
0131:
0132: if (getAttributeCount() == 0) {
0133: ps.println(indentation + "<" + name + ">");
0134: indentation += INDENT_LEVEL;
0135: } else {
0136: ps.println(indentation + "<" + name);
0137: final String closing = indentation + ">";
0138:
0139: indentation += INDENT_LEVEL;
0140:
0141: Enumeration keys = getAttributeNames();
0142: while (keys.hasMoreElements()) {
0143: Object key = keys.nextElement();
0144: Object value = getAttribute(key);
0145: ps.println(indentation + key + "=" + value);
0146: }
0147: ps.println(closing);
0148: }
0149:
0150: if (isLeaf()) {
0151: try {
0152: String text = content.getString(getStartOffset(),
0153: getEndOffset() - getStartOffset());
0154: if (text.length() > MAX_LEAF_CONTENT_LENGTH) {
0155: text = text.substring(0,
0156: MAX_LEAF_CONTENT_LENGTH);
0157: text += "...";
0158: }
0159: ps.println(indentation + "[" + getStartOffset()
0160: + "," + getEndOffset() + "][" + text + "]");
0161: } catch (final NullPointerException e) {
0162: } catch (final BadLocationException e) {
0163: }
0164: } else {
0165: Enumeration leaves = children();
0166: if (leaves == null) {
0167: return;
0168: }
0169:
0170: while (leaves.hasMoreElements()) {
0171: Object child = leaves.nextElement();
0172: if (child instanceof AbstractElement) {
0173: ((AbstractElement) child).dump(ps, indent + 1);
0174: }
0175: }
0176: }
0177: }
0178:
0179: public Object getAttribute(final Object key) {
0180: Object value = attrs.getAttribute(key);
0181: AttributeSet resolver = getResolveParent();
0182: if (value == null && resolver != null) {
0183: value = resolver.getAttribute(key);
0184: }
0185: return value;
0186: }
0187:
0188: public int getAttributeCount() {
0189: return attrs.getAttributeCount();
0190: }
0191:
0192: public Enumeration<?> getAttributeNames() {
0193: return attrs.getAttributeNames();
0194: }
0195:
0196: public AttributeSet getAttributes() {
0197: return this ;
0198: }
0199:
0200: public TreeNode getChildAt(final int index) {
0201: return (TreeNode) getElement(index);
0202: }
0203:
0204: public int getChildCount() {
0205: return getElementCount();
0206: }
0207:
0208: public Document getDocument() {
0209: return AbstractDocument.this ;
0210: }
0211:
0212: public int getIndex(final TreeNode node) {
0213: if (getAllowsChildren()) {
0214: final int count = getChildCount();
0215: for (int i = 0; i < count; i++) {
0216: if (getChildAt(i) == node) {
0217: return i;
0218: }
0219: }
0220: }
0221:
0222: return -1;
0223: }
0224:
0225: public String getName() {
0226: return isDefined(ElementNameAttribute) ? (String) getAttribute(ElementNameAttribute)
0227: : null;
0228: }
0229:
0230: public TreeNode getParent() {
0231: final Element parentElement = getParentElement();
0232: return parentElement instanceof TreeNode ? (TreeNode) parentElement
0233: : null;
0234: }
0235:
0236: public Element getParentElement() {
0237: return parent;
0238: }
0239:
0240: public AttributeSet getResolveParent() {
0241: AttributeSet resolver = attrs.getResolveParent();
0242: if (resolver == null && parent != null) {
0243: resolver = parent.getAttributes();
0244: }
0245: return resolver;
0246: }
0247:
0248: public boolean isDefined(final Object key) {
0249: return attrs.isDefined(key);
0250: }
0251:
0252: public boolean isEqual(final AttributeSet attrSet) {
0253: return attrs.isEqual(attrSet);
0254: }
0255:
0256: public void removeAttribute(final Object key) {
0257: checkWriteLock();
0258:
0259: attrs = context.removeAttribute(attrs, key);
0260: }
0261:
0262: public void removeAttributes(final AttributeSet attrSet) {
0263: checkWriteLock();
0264:
0265: attrs = context.removeAttributes(attrs, attrSet);
0266: }
0267:
0268: public void removeAttributes(final Enumeration<?> attrNames) {
0269: checkWriteLock();
0270:
0271: attrs = context.removeAttributes(attrs, attrNames);
0272: }
0273:
0274: public void setResolveParent(final AttributeSet resolveParent) {
0275: checkWriteLock();
0276:
0277: attrs = context.addAttribute(attrs,
0278: AttributeSet.ResolveAttribute, resolveParent);
0279: }
0280:
0281: final void checkWriteLock() {
0282: if (AbstractDocument.this .getCurrentWriter() != Thread
0283: .currentThread()) {
0284:
0285: throw new Error(Messages.getString("swing.err.0F")); //$NON-NLS-1$
0286: }
0287: }
0288:
0289: private void readObject(final ObjectInputStream ois)
0290: throws IOException, ClassNotFoundException {
0291:
0292: ois.defaultReadObject();
0293:
0294: MutableAttributeSet mas = new SimpleAttributeSet();
0295: StyleContext.readAttributeSet(ois, mas);
0296:
0297: attrs = context.getEmptySet();
0298: if (mas.getAttributeCount() > 0) {
0299: writeLock();
0300: addAttributes(mas);
0301: writeUnlock();
0302: }
0303: }
0304:
0305: private void writeObject(final ObjectOutputStream oos)
0306: throws IOException {
0307:
0308: oos.defaultWriteObject();
0309: StyleContext.writeAttributeSet(oos, attrs);
0310: }
0311:
0312: }
0313:
0314: public static interface AttributeContext {
0315:
0316: AttributeSet addAttribute(AttributeSet old, Object key,
0317: Object value);
0318:
0319: AttributeSet addAttributes(AttributeSet old, AttributeSet toAdd);
0320:
0321: AttributeSet getEmptySet();
0322:
0323: void reclaim(AttributeSet attrs);
0324:
0325: AttributeSet removeAttribute(AttributeSet old, Object key);
0326:
0327: AttributeSet removeAttributes(AttributeSet old,
0328: AttributeSet toDelete);
0329:
0330: AttributeSet removeAttributes(AttributeSet old,
0331: Enumeration<?> names);
0332:
0333: }
0334:
0335: @SuppressWarnings("serial")
0336: public class BranchElement extends AbstractElement {
0337: Element[] elements = new Element[0];
0338:
0339: private final SearchElement searchElem = new SearchElement();
0340: private final Comparator<Object> comparator = new ElementComparator();
0341:
0342: public BranchElement(final Element parent,
0343: final AttributeSet attributes) {
0344: super (parent, attributes);
0345: }
0346:
0347: @Override
0348: public Enumeration children() {
0349: if (elements == null || elements.length == 0) {
0350: return null;
0351: } else {
0352: return new Enumeration() {
0353: private int index = 0;
0354:
0355: public boolean hasMoreElements() {
0356: return index < elements.length;
0357: }
0358:
0359: public Object nextElement() {
0360: return elements[index++];
0361: }
0362: };
0363: }
0364: }
0365:
0366: @Override
0367: public boolean getAllowsChildren() {
0368: return true;
0369: }
0370:
0371: @Override
0372: public Element getElement(final int index) {
0373: if (0 <= index && index < elements.length) {
0374: return elements[index];
0375: } else {
0376: return null;
0377: }
0378: }
0379:
0380: @Override
0381: public int getElementCount() {
0382: return elements.length;
0383: }
0384:
0385: @Override
0386: public int getElementIndex(final int offset) {
0387: if (elements.length <= 1) {
0388: return 0;
0389: }
0390:
0391: searchElem.offset = offset;
0392: int result = Arrays.binarySearch(elements, searchElem,
0393: comparator);
0394: if (result < 0) {
0395: result = -result - 1;
0396: }
0397: if (result >= elements.length) {
0398: result = elements.length - 1;
0399: }
0400:
0401: return result;
0402: }
0403:
0404: @Override
0405: public int getEndOffset() {
0406: if (elements.length == 0) {
0407: throw new NullPointerException();
0408: }
0409: return elements[elements.length - 1].getEndOffset();
0410: }
0411:
0412: @Override
0413: public String getName() {
0414: final String inherited = super .getName();
0415: return inherited != null ? inherited : ParagraphElementName;
0416: }
0417:
0418: @Override
0419: public int getStartOffset() {
0420: if (elements.length == 0) {
0421: throw new NullPointerException();
0422: }
0423: return elements[0].getStartOffset();
0424: }
0425:
0426: @Override
0427: public boolean isLeaf() {
0428: return false;
0429: }
0430:
0431: public Element positionToElement(final int offset) {
0432: if (offset < getStartOffset()
0433: || offset > getEndOffset() - 1) {
0434: return null;
0435: }
0436: return elements[getElementIndex(offset)];
0437: }
0438:
0439: public void replace(final int index, final int length,
0440: final Element[] newElements) {
0441: Element[] newArray;
0442:
0443: if (index < 0 || length < 0
0444: || elements.length < index + length) {
0445: throw new ArrayIndexOutOfBoundsException();
0446: }
0447:
0448: newArray = new Element[elements.length - length
0449: + newElements.length];
0450:
0451: System.arraycopy(elements, 0, newArray, 0, index);
0452: System.arraycopy(newElements, 0, newArray, index,
0453: newElements.length);
0454: System.arraycopy(elements, index + length, newArray, index
0455: + newElements.length, elements.length
0456: - (index + length));
0457:
0458: elements = newArray;
0459: }
0460:
0461: @Override
0462: public String toString() {
0463: return "BranchElement(" + getName() + ") "
0464: + getStartOffset() + "," + getEndOffset() + "\n";
0465: }
0466:
0467: }
0468:
0469: public static interface Content {
0470:
0471: Position createPosition(int offset) throws BadLocationException;
0472:
0473: void getChars(int offset, int length, Segment text)
0474: throws BadLocationException;
0475:
0476: String getString(int offset, int length)
0477: throws BadLocationException;
0478:
0479: UndoableEdit insertString(int offset, String str)
0480: throws BadLocationException;
0481:
0482: int length();
0483:
0484: UndoableEdit remove(int offset, int length)
0485: throws BadLocationException;
0486:
0487: }
0488:
0489: @SuppressWarnings("serial")
0490: public class DefaultDocumentEvent extends CompoundEdit implements
0491: DocumentEvent {
0492:
0493: private static final int THRESHOLD = 10;
0494:
0495: private HashMap<Element, ElementChange> changes;
0496: private int length;
0497: private int offset;
0498: private EventType type;
0499:
0500: public DefaultDocumentEvent(final int offset, final int length,
0501: final DocumentEvent.EventType type) {
0502: this .offset = offset;
0503: this .length = length;
0504: this .type = type;
0505: }
0506:
0507: @Override
0508: public boolean addEdit(final UndoableEdit anEdit) {
0509: boolean result = super .addEdit(anEdit);
0510:
0511: if (result && edits.size() > THRESHOLD) {
0512: if (changes == null) {
0513: changes = new HashMap<Element, ElementChange>();
0514:
0515: for (UndoableEdit edit : edits) {
0516: if (edit instanceof ElementChange) {
0517: ElementChange change = (ElementChange) edit;
0518: changes.put(change.getElement(), change);
0519: }
0520: }
0521: } else {
0522: if (anEdit instanceof ElementChange) {
0523: ElementChange change = (ElementChange) anEdit;
0524: changes.put(change.getElement(), change);
0525: }
0526: }
0527: }
0528:
0529: return result;
0530: }
0531:
0532: public ElementChange getChange(final Element element) {
0533: if (changes != null) {
0534: return changes.get(element);
0535: }
0536:
0537: for (UndoableEdit edit : edits) {
0538: if (edit instanceof ElementChange) {
0539: ElementChange change = (ElementChange) edit;
0540: if (change.getElement() == element) {
0541: return change;
0542: }
0543: }
0544: }
0545: return null;
0546: }
0547:
0548: public Document getDocument() {
0549: return AbstractDocument.this ;
0550: }
0551:
0552: public int getLength() {
0553: return length;
0554: }
0555:
0556: public int getOffset() {
0557: return offset;
0558: }
0559:
0560: @Override
0561: public String getPresentationName() {
0562: if (type == EventType.INSERT) {
0563: return getLocalizedString("AbstractDocument.additionText");
0564: } else if (type == EventType.REMOVE) {
0565: return getLocalizedString("AbstractDocument.deletionText");
0566: } else if (type == EventType.CHANGE) {
0567: return getLocalizedString("AbstractDocument.styleChangeText");
0568: }
0569:
0570: assert false : "Valid values are DocumentEvent.EventType.INSERT, "
0571: + "REMOVE, CHANGE";
0572: return null;
0573: }
0574:
0575: @Override
0576: public String getRedoPresentationName() {
0577: return getLocalizedRedoName() + " " + getPresentationName();
0578: }
0579:
0580: public EventType getType() {
0581: return type;
0582: }
0583:
0584: @Override
0585: public String getUndoPresentationName() {
0586: return getLocalizedUndoName() + " " + getPresentationName();
0587: }
0588:
0589: @Override
0590: public boolean isSignificant() {
0591: return true;
0592: }
0593:
0594: @Override
0595: public void redo() {
0596: writeLock();
0597: try {
0598: super .redo();
0599:
0600: fireEvent(false);
0601: } finally {
0602: writeUnlock();
0603: }
0604: }
0605:
0606: @Override
0607: public String toString() {
0608: StringBuffer result = new StringBuffer();
0609: for (UndoableEdit edit : edits) {
0610: result.append(", ").append(edit.toString());
0611: }
0612: result.replace(0, 2, "[").append("]");
0613: return result.toString();
0614: }
0615:
0616: @Override
0617: public void undo() {
0618: writeLock();
0619: try {
0620: super .undo();
0621:
0622: fireEvent(true);
0623: } finally {
0624: writeUnlock();
0625: }
0626: }
0627:
0628: private void fireEvent(final boolean undone) {
0629: final EventType eventType = type;
0630: if (type == EventType.CHANGE) {
0631: fireChangedUpdate(this );
0632: } else {
0633: if (type == EventType.INSERT && undone) {
0634: type = EventType.REMOVE;
0635: fireRemoveUpdate(this );
0636: } else {
0637: type = EventType.INSERT;
0638: fireInsertUpdate(this );
0639: }
0640: type = eventType;
0641: }
0642: }
0643:
0644: private String getLocalizedUndoName() {
0645: return UIManager.getString("AbstractDocument.undoText");
0646: }
0647:
0648: private String getLocalizedRedoName() {
0649: return UIManager.getString("AbstractDocument.redoText");
0650: }
0651: }
0652:
0653: @SuppressWarnings("serial")
0654: public static class ElementEdit extends AbstractUndoableEdit
0655: implements ElementChange {
0656:
0657: private Element[] added;
0658: private Element element;
0659: private int index;
0660: private Element[] removed;
0661:
0662: public ElementEdit(final Element element, final int index,
0663: final Element[] removed, final Element[] added) {
0664: this .element = element;
0665: this .index = index;
0666: this .removed = removed;
0667: this .added = added;
0668: }
0669:
0670: public Element[] getChildrenAdded() {
0671: return added;
0672: }
0673:
0674: public Element[] getChildrenRemoved() {
0675: return removed;
0676: }
0677:
0678: public Element getElement() {
0679: return element;
0680: }
0681:
0682: public int getIndex() {
0683: return index;
0684: }
0685:
0686: @Override
0687: public void redo() {
0688: super .redo();
0689: Element[] temp = added;
0690: added = removed;
0691: removed = temp;
0692: ((BranchElement) element).replace(index, removed.length,
0693: added);
0694: }
0695:
0696: @Override
0697: public void undo() {
0698: super .undo();
0699: ((BranchElement) element).replace(index, added.length,
0700: removed);
0701: Element[] temp = added;
0702: added = removed;
0703: removed = temp;
0704: }
0705:
0706: }
0707:
0708: @SuppressWarnings("serial")
0709: public class LeafElement extends AbstractElement {
0710: private transient Position end;
0711: private transient Position start;
0712:
0713: public LeafElement(final Element parent,
0714: final AttributeSet attributes, final int startOffset,
0715: final int endOffset) {
0716: super (parent, attributes);
0717:
0718: int adjustedStartOffset = startOffset;
0719: int adjustedEndOffset = endOffset;
0720:
0721: if (adjustedStartOffset < 0) {
0722: adjustedStartOffset = 0;
0723: }
0724:
0725: if (adjustedEndOffset < adjustedStartOffset) {
0726: adjustedEndOffset = adjustedStartOffset;
0727: }
0728:
0729: initPositions(adjustedStartOffset, adjustedEndOffset);
0730: }
0731:
0732: @Override
0733: public Enumeration children() {
0734: return null;
0735: }
0736:
0737: @Override
0738: public boolean getAllowsChildren() {
0739: return false;
0740: }
0741:
0742: @Override
0743: public Element getElement(final int offset) {
0744: return null;
0745: }
0746:
0747: @Override
0748: public int getElementCount() {
0749: return 0;
0750: }
0751:
0752: @Override
0753: public int getElementIndex(final int offset) {
0754: return -1;
0755: }
0756:
0757: @Override
0758: public int getEndOffset() {
0759: return end.getOffset();
0760: }
0761:
0762: @Override
0763: public String getName() {
0764: final String inherited = super .getName();
0765: return inherited != null ? inherited : ContentElementName;
0766: }
0767:
0768: @Override
0769: public int getStartOffset() {
0770: return start.getOffset();
0771: }
0772:
0773: @Override
0774: public boolean isLeaf() {
0775: return true;
0776: }
0777:
0778: @Override
0779: public String toString() {
0780: return "LeafElement(" + getName() + ") " + getStartOffset()
0781: + "," + getEndOffset() + "\n";
0782: }
0783:
0784: private void initPositions(final int startOffset,
0785: final int endOffset) {
0786: try {
0787: start = createPosition(startOffset);
0788: end = createPosition(endOffset);
0789: } catch (final BadLocationException e) {
0790: }
0791: }
0792:
0793: private void readObject(final ObjectInputStream ois)
0794: throws IOException, ClassNotFoundException {
0795:
0796: ois.defaultReadObject();
0797:
0798: final int startOffset = ois.readInt();
0799: final int endOffset = ois.readInt();
0800: initPositions(startOffset, endOffset);
0801: }
0802:
0803: private void writeObject(final ObjectOutputStream oos)
0804: throws IOException {
0805:
0806: oos.defaultWriteObject();
0807:
0808: oos.writeInt(start.getOffset());
0809: oos.writeInt(end.getOffset());
0810: }
0811:
0812: }
0813:
0814: @SuppressWarnings("serial")
0815: private class BidiElement extends LeafElement {
0816: public BidiElement(final AttributeSet attributes,
0817: final int startOffset, final int endOffset) {
0818: super (getBidiRootElement(), attributes, startOffset,
0819: endOffset);
0820: }
0821:
0822: @Override
0823: public String getName() {
0824: return BidiElementName;
0825: }
0826: }
0827:
0828: @SuppressWarnings("serial")
0829: private class BidiRoot extends BranchElement {
0830: public BidiRoot() {
0831: super (null, null);
0832: }
0833:
0834: @Override
0835: public String getName() {
0836: return "bidi root";
0837: }
0838: }
0839:
0840: private static class ReadWriteLock {
0841: private boolean callingListeners;
0842: private int writeAcquestCounter;
0843: private int waitingWritersCounter;
0844: private Thread activeWriter;
0845: private final List<Thread> activeReaders = new ArrayList<Thread>();
0846: private final Object lock = new Object();
0847:
0848: public final Thread getCurrentWriter() {
0849: return activeWriter;
0850: }
0851:
0852: public final void readLock() {
0853: final Thread thread = Thread.currentThread();
0854:
0855: if (thread == activeWriter) {
0856: return;
0857: }
0858:
0859: synchronized (lock) {
0860: while ((activeWriter != null)
0861: || (waitingWritersCounter > 0)) {
0862: try {
0863: lock.wait();
0864: } catch (final InterruptedException e) {
0865: return;
0866: }
0867: }
0868:
0869: activeReaders.add(thread);
0870: }
0871: }
0872:
0873: public final void readUnlock() {
0874: final Thread thread = Thread.currentThread();
0875:
0876: if (thread == activeWriter) {
0877: return;
0878: }
0879:
0880: synchronized (lock) {
0881: if (!activeReaders.remove(thread)) {
0882: throw new Error(Messages.getString("swing.err.10")); //$NON-NLS-1$
0883: }
0884:
0885: lock.notifyAll();
0886: }
0887: }
0888:
0889: public final void setCallingListeners(final boolean flag) {
0890: callingListeners = flag;
0891: }
0892:
0893: public final void writeLock() {
0894: if (callingListeners) {
0895: throw new IllegalStateException(Messages
0896: .getString("swing.7E")); //$NON-NLS-1$
0897: }
0898:
0899: final Thread thread = Thread.currentThread();
0900:
0901: if (thread == activeWriter) {
0902: writeAcquestCounter++;
0903: return;
0904: }
0905:
0906: synchronized (lock) {
0907: if ((activeReaders.size() > 0)
0908: || (activeWriter != null)) {
0909:
0910: waitingWritersCounter++;
0911:
0912: while ((activeReaders.size() > 0)
0913: || (activeWriter != null)) {
0914: try {
0915: lock.wait();
0916: } catch (final InterruptedException e) {
0917: waitingWritersCounter--;
0918: return;
0919: }
0920: }
0921:
0922: waitingWritersCounter--;
0923: }
0924:
0925: writeAcquestCounter++;
0926: activeWriter = thread;
0927: }
0928: }
0929:
0930: public final void writeUnlock() {
0931: if (activeWriter != Thread.currentThread()) {
0932: throw new Error(Messages.getString("swing.err.11")); //$NON-NLS-1$ }
0933: }
0934:
0935: if (--writeAcquestCounter == 0) {
0936: synchronized (lock) {
0937: activeWriter = null;
0938: lock.notifyAll();
0939: }
0940: }
0941: }
0942: }
0943:
0944: /**
0945: * Object of this class serves as key when searching for element index.
0946: * @see BranchElement#getElementIndex(int)
0947: */
0948: @SuppressWarnings("serial")
0949: private static final class SearchElement implements Serializable {
0950: /**
0951: * Indicates the offset of interest.
0952: */
0953: public transient int offset;
0954:
0955: @Override
0956: public String toString() {
0957: return "SearchElement(none) " + offset + ", " + offset
0958: + "\n";
0959: }
0960: }
0961:
0962: /**
0963: * The comparator to compare <code>Element</code>s when searching for
0964: * element index.
0965: * <p><em><strong>Note:</strong> This comparator imposes ordering that
0966: * is inconsistent with equals.</em> Most of <code>Element</code>s
0967: * does not override <code>equals</code>.
0968: * @see BranchElement#getElementIndex(int)
0969: */
0970: @SuppressWarnings("serial")
0971: private static final class ElementComparator implements
0972: Comparator<Object>, Serializable {
0973:
0974: /**
0975: * It is expected that the first argument is the <code>Element</code>
0976: * to compare with and the second argument is the key containing the
0977: * offset of interest.
0978: *
0979: * @param item element to compare with.
0980: * Must be of type <code>Element</code>.
0981: * @param key the search key. Object of type <code>SearchElement</code>
0982: * which contains the desired offset.
0983: * @return 0, -1 or +1
0984: */
0985: public int compare(final Object item, final Object key) {
0986: Element e = (Element) item;
0987: int offset = ((SearchElement) key).offset;
0988: if (e.getStartOffset() <= offset
0989: && offset < e.getEndOffset()) {
0990:
0991: return 0;
0992: } else if (e.getEndOffset() <= offset) {
0993: return -1;
0994: } else {
0995: return +1;
0996: }
0997: }
0998: }
0999:
1000: public static final String BidiElementName = "bidi level";
1001: public static final String ContentElementName = "content";
1002: public static final String ElementNameAttribute = "$ename";
1003: public static final String ParagraphElementName = "paragraph";
1004: public static final String SectionElementName = "section";
1005:
1006: protected static final String BAD_LOCATION = "document location failure";
1007:
1008: protected transient EventListenerList listenerList;
1009:
1010: private transient BranchElement bidiRoot;
1011: private transient Content content;
1012: private transient AttributeContext context;
1013: private transient Position docEnd;
1014: private DocumentFilter docFilter;
1015: private Dictionary<Object, Object> docProperties = new Hashtable<Object, Object>();
1016: private transient Position docStart;
1017: private transient DocumentFilter.FilterBypass filterBypasser;
1018:
1019: private UIDefaults uiDefaults = null;
1020:
1021: private transient ReadWriteLock lock;
1022:
1023: protected AbstractDocument(final Content content) {
1024: this (content, StyleContext.getDefaultStyleContext());
1025: }
1026:
1027: protected AbstractDocument(final Content content,
1028: final AttributeContext context) {
1029:
1030: this .content = content;
1031: this .context = context;
1032:
1033: putProperty(StringConstants.BIDI_PROPERTY, Boolean.FALSE);
1034:
1035: initTransientFields();
1036:
1037: writeLock();
1038: bidiRoot = new BidiRoot();
1039: bidiRoot.replace(0, 0, new Element[] { new BidiElement(context
1040: .addAttribute(context.getEmptySet(),
1041: StyleConstants.BidiLevel, new Integer(0)),
1042: docStart.getOffset(), docEnd.getOffset()) });
1043: writeUnlock();
1044: }
1045:
1046: public abstract Element getDefaultRootElement();
1047:
1048: public abstract Element getParagraphElement(final int offset);
1049:
1050: public void addDocumentListener(final DocumentListener listener) {
1051: listenerList.add(DocumentListener.class, listener);
1052: }
1053:
1054: public void addUndoableEditListener(
1055: final UndoableEditListener listerner) {
1056: listenerList.add(UndoableEditListener.class, listerner);
1057: }
1058:
1059: public synchronized Position createPosition(final int offset)
1060: throws BadLocationException {
1061:
1062: return content.createPosition(offset);
1063: }
1064:
1065: /**
1066: * Dumps the array returned by getRootElements.
1067: */
1068: public void dump(final PrintStream ps) {
1069: Element[] roots = getRootElements();
1070:
1071: for (Element root : roots) {
1072: if (root instanceof AbstractElement) {
1073: ((AbstractElement) root).dump(ps, 0);
1074: }
1075: }
1076: }
1077:
1078: public int getAsynchronousLoadPriority() {
1079: Object value = getProperty(PropertyNames.LOAD_PRIORITY);
1080: return value instanceof Integer ? ((Integer) value).intValue()
1081: : -1;
1082: }
1083:
1084: public Element getBidiRootElement() {
1085: return bidiRoot;
1086: }
1087:
1088: public DocumentFilter getDocumentFilter() {
1089: return docFilter;
1090: }
1091:
1092: public DocumentListener[] getDocumentListeners() {
1093: return listenerList.getListeners(DocumentListener.class);
1094: }
1095:
1096: public Dictionary<Object, Object> getDocumentProperties() {
1097: return docProperties;
1098: }
1099:
1100: public final Position getEndPosition() {
1101: return docEnd;
1102: }
1103:
1104: public int getLength() {
1105: return content.length() - 1;
1106: }
1107:
1108: public <T extends EventListener> T[] getListeners(
1109: final Class<T> listenerType) {
1110: return listenerList.getListeners(listenerType);
1111: }
1112:
1113: public final Object getProperty(final Object key) {
1114: return getDocumentProperties().get(key);
1115: }
1116:
1117: public Element[] getRootElements() {
1118: return new Element[] { getDefaultRootElement(),
1119: getBidiRootElement() };
1120: }
1121:
1122: public final Position getStartPosition() {
1123: return docStart;
1124: }
1125:
1126: public String getText(final int offset, final int length)
1127: throws BadLocationException {
1128:
1129: return content.getString(offset, length);
1130: }
1131:
1132: public void getText(final int offset, final int length,
1133: final Segment text) throws BadLocationException {
1134:
1135: content.getChars(offset, length, text);
1136: }
1137:
1138: public UndoableEditListener[] getUndoableEditListeners() {
1139: return listenerList.getListeners(UndoableEditListener.class);
1140: }
1141:
1142: public void insertString(final int offset, final String text,
1143: final AttributeSet attrs) throws BadLocationException {
1144:
1145: if (org.apache.harmony.x.swing.Utilities.isEmptyString(text)) {
1146: return;
1147: }
1148:
1149: writeLock();
1150: try {
1151: if (docFilter != null) {
1152: docFilter.insertString(filterBypasser, offset, text,
1153: attrs);
1154: } else {
1155: doInsert(offset, text, attrs);
1156: }
1157: } finally {
1158: writeUnlock();
1159: }
1160: }
1161:
1162: public final void putProperty(final Object key, final Object value) {
1163: if (value == null) {
1164: getDocumentProperties().remove(key);
1165: } else {
1166: getDocumentProperties().put(key, value);
1167: }
1168: }
1169:
1170: public final void readLock() {
1171: lock.readLock();
1172: }
1173:
1174: public final void readUnlock() {
1175: lock.readUnlock();
1176: }
1177:
1178: public void remove(final int offset, final int length)
1179: throws BadLocationException {
1180:
1181: writeLock();
1182: try {
1183: if (docFilter != null) {
1184: docFilter.remove(filterBypasser, offset, length);
1185: } else {
1186: doRemove(offset, length);
1187: }
1188: } finally {
1189: writeUnlock();
1190: }
1191: }
1192:
1193: public void removeDocumentListener(final DocumentListener listener) {
1194: listenerList.remove(DocumentListener.class, listener);
1195: }
1196:
1197: public void removeUndoableEditListener(
1198: final UndoableEditListener listener) {
1199: listenerList.remove(UndoableEditListener.class, listener);
1200: }
1201:
1202: public void render(final Runnable r) {
1203: readLock();
1204: try {
1205: r.run();
1206: } finally {
1207: readUnlock();
1208: }
1209: }
1210:
1211: public void replace(final int offset, final int length,
1212: final String newText, final AttributeSet attrs)
1213: throws BadLocationException {
1214:
1215: writeLock();
1216:
1217: try {
1218: if (docFilter != null) {
1219: docFilter.replace(filterBypasser, offset, length,
1220: newText, attrs);
1221: } else {
1222: doReplace(offset, length, newText, attrs);
1223: }
1224: } finally {
1225: writeUnlock();
1226: }
1227: }
1228:
1229: public void setAsynchronousLoadPriority(final int priority) {
1230: putProperty(PropertyNames.LOAD_PRIORITY, new Integer(priority));
1231: }
1232:
1233: public void setDocumentFilter(final DocumentFilter filter) {
1234: this .docFilter = filter;
1235: }
1236:
1237: public void setDocumentProperties(
1238: final Dictionary<Object, Object> properties) {
1239: this .docProperties = properties;
1240: }
1241:
1242: protected Element createBranchElement(final Element parent,
1243: final AttributeSet as) {
1244: return new BranchElement(parent, as);
1245: }
1246:
1247: protected Element createLeafElement(final Element parent,
1248: final AttributeSet as, final int start, final int end) {
1249: return new LeafElement(parent, as, start, end);
1250: }
1251:
1252: protected void fireChangedUpdate(final DocumentEvent event) {
1253: lock.setCallingListeners(true);
1254:
1255: try {
1256: DocumentListener[] listeners = getDocumentListeners();
1257: for (DocumentListener listener : listeners) {
1258: listener.changedUpdate(event);
1259: }
1260: } finally {
1261: lock.setCallingListeners(false);
1262: }
1263: }
1264:
1265: protected void fireInsertUpdate(final DocumentEvent event) {
1266: lock.setCallingListeners(true);
1267:
1268: try {
1269: DocumentListener[] listeners = getDocumentListeners();
1270: for (DocumentListener listener : listeners) {
1271: listener.insertUpdate(event);
1272: }
1273: } finally {
1274: lock.setCallingListeners(false);
1275: }
1276: }
1277:
1278: protected void fireRemoveUpdate(final DocumentEvent event) {
1279: lock.setCallingListeners(true);
1280:
1281: try {
1282: DocumentListener[] listeners = getDocumentListeners();
1283: for (DocumentListener listener : listeners) {
1284: listener.removeUpdate(event);
1285: }
1286: } finally {
1287: lock.setCallingListeners(false);
1288: }
1289: }
1290:
1291: protected void fireUndoableEditUpdate(final UndoableEditEvent event) {
1292: lock.setCallingListeners(true);
1293:
1294: try {
1295: UndoableEditListener[] listeners = getUndoableEditListeners();
1296: for (UndoableEditListener listener : listeners) {
1297: listener.undoableEditHappened(event);
1298: }
1299: } finally {
1300: lock.setCallingListeners(false);
1301: }
1302: }
1303:
1304: protected final AttributeContext getAttributeContext() {
1305: return context;
1306: }
1307:
1308: protected final Content getContent() {
1309: return content;
1310: }
1311:
1312: protected final Thread getCurrentWriter() {
1313: return lock.getCurrentWriter();
1314: }
1315:
1316: protected void insertUpdate(final DefaultDocumentEvent event,
1317: final AttributeSet attrs) {
1318: final int offset = event.getOffset();
1319: final int length = event.getLength();
1320:
1321: final Segment text = new Segment();
1322:
1323: try {
1324: content.getChars(offset, length, text);
1325: } catch (final BadLocationException e) {
1326: }
1327:
1328: if (!bidiUpdateProperty(offset, text)) {
1329: return;
1330: }
1331:
1332: final List<Element> added = new ArrayList<Element>();
1333: Element par;
1334: int nextOffset = offset;
1335: do {
1336: par = getParagraphElement(nextOffset);
1337: bidiParseParagraph(added, par, text);
1338:
1339: nextOffset = par.getEndOffset();
1340: } while (par.getEndOffset() < offset + length);
1341:
1342: bidiUpdateStructure(event, added);
1343: }
1344:
1345: protected void postRemoveUpdate(final DefaultDocumentEvent event) {
1346: if (!hasBidiInfo()) {
1347: return;
1348: }
1349:
1350: final List<Element> added = new ArrayList<Element>();
1351:
1352: bidiParseParagraph(added,
1353: getParagraphElement(event.getOffset()), new Segment());
1354:
1355: bidiUpdateStructure(event, added);
1356: }
1357:
1358: protected void removeUpdate(final DefaultDocumentEvent event) {
1359: }
1360:
1361: protected final void writeLock() {
1362: lock.writeLock();
1363: }
1364:
1365: protected final void writeUnlock() {
1366: lock.writeUnlock();
1367: }
1368:
1369: void doInsert(final int offset, final String text,
1370: final AttributeSet attrs) throws BadLocationException {
1371:
1372: final DefaultDocumentEvent event = new DefaultDocumentEvent(
1373: offset, text.length(), EventType.INSERT);
1374:
1375: final UndoableEdit contentEdit = content.insertString(offset,
1376: text);
1377: if (contentEdit != null) {
1378: event.addEdit(contentEdit);
1379: }
1380:
1381: insertUpdate(event, attrs);
1382:
1383: event.end();
1384:
1385: fireInsertUpdate(event);
1386: if (contentEdit != null) {
1387: fireUndoableEditUpdate(new UndoableEditEvent(this , event));
1388: }
1389: }
1390:
1391: final void doRemove(final int offset, final int length)
1392: throws BadLocationException {
1393:
1394: if (length == 0) {
1395: return;
1396: }
1397:
1398: if (offset < 0 || offset > getLength()) {
1399: throw new BadLocationException(Messages
1400: .getString("swing.7F"), offset); //$NON-NLS-1$
1401: }
1402:
1403: if (offset + length > getLength()) {
1404: throw new BadLocationException(Messages
1405: .getString("swing.80"), //$NON-NLS-1$
1406: offset + length);
1407: }
1408:
1409: final DefaultDocumentEvent event = new DefaultDocumentEvent(
1410: offset, length, EventType.REMOVE);
1411:
1412: removeUpdate(event);
1413:
1414: final UndoableEdit contentEdit = content.remove(offset, length);
1415: if (contentEdit != null) {
1416: event.addEdit(contentEdit);
1417: }
1418:
1419: postRemoveUpdate(event);
1420:
1421: event.end();
1422:
1423: fireRemoveUpdate(event);
1424: if (contentEdit != null) {
1425: fireUndoableEditUpdate(new UndoableEditEvent(this , event));
1426: }
1427: }
1428:
1429: final void doReplace(final int offset, final int length,
1430: final String newText, final AttributeSet attrs)
1431: throws BadLocationException {
1432:
1433: if (length > 0) {
1434: doRemove(offset, length);
1435: }
1436:
1437: if (!org.apache.harmony.x.swing.Utilities
1438: .isEmptyString(newText)) {
1439: doInsert(offset, newText, attrs);
1440: }
1441: }
1442:
1443: final boolean isLeftToRight(final int offset) {
1444: final Element e = bidiRoot.getElement(bidiRoot
1445: .getElementIndex(offset));
1446: return TextUtils.isLTR(getBidiLevel(e));
1447: }
1448:
1449: private int bidiAdjustFirstElement(final List<Element> added,
1450: final Element par) {
1451: int prevParIndex = bidiRoot.getElementIndex(par
1452: .getStartOffset() - 1);
1453: final Element prevParBidi = bidiRoot.getElement(prevParIndex);
1454: final Element firstAdded = added.get(0);
1455:
1456: if (getBidiLevel(prevParBidi) == getBidiLevel(firstAdded)) {
1457: // Combine these two
1458: added.remove(0);
1459: added.add(0, new BidiElement(firstAdded.getAttributes(),
1460: prevParBidi.getStartOffset(), firstAdded
1461: .getEndOffset()));
1462: } else if (prevParBidi.getStartOffset() < firstAdded
1463: .getStartOffset()
1464: && prevParBidi.getEndOffset() > firstAdded
1465: .getStartOffset()) {
1466: // Divide prevParBidi
1467: added.add(0, new BidiElement(prevParBidi.getAttributes(),
1468: prevParBidi.getStartOffset(), firstAdded
1469: .getStartOffset()));
1470: } else if (prevParBidi.getStartOffset() != firstAdded
1471: .getStartOffset()) {
1472: // prevParBidi don't need to be deleted
1473: prevParIndex++;
1474: }
1475:
1476: return prevParIndex;
1477: }
1478:
1479: private int bidiAdjustLastElement(final List<Element> added) {
1480: final Element lastAdded = added.get(added.size() - 1);
1481: int nextParIndex = bidiRoot.getElementIndex(lastAdded
1482: .getEndOffset() + 1);
1483: final Element nextParBidi = bidiRoot.getElement(nextParIndex);
1484: if (nextParBidi == null) {
1485: return nextParIndex;
1486: }
1487:
1488: if (getBidiLevel(nextParBidi) == getBidiLevel(lastAdded)) {
1489: // Combine these two
1490: added.remove(added.size() - 1);
1491: added.add(new BidiElement(lastAdded.getAttributes(),
1492: lastAdded.getStartOffset(), nextParBidi
1493: .getEndOffset()));
1494: } else if (lastAdded.getEndOffset() > nextParBidi
1495: .getStartOffset()
1496: && lastAdded.getEndOffset() < nextParBidi
1497: .getEndOffset()) {
1498: // Divide nextParBidi
1499: added.add(new BidiElement(nextParBidi.getAttributes(),
1500: lastAdded.getEndOffset(), nextParBidi
1501: .getEndOffset()));
1502: } else if (nextParBidi.getEndOffset() != lastAdded
1503: .getEndOffset()) {
1504: // nextParBidi is left unchanged
1505: nextParIndex--;
1506: }
1507:
1508: return nextParIndex;
1509: }
1510:
1511: private void bidiParseParagraph(final List<Element> added,
1512: final Element par, final Segment text) {
1513: final int parStart = par.getStartOffset();
1514: final int parEnd = par.getEndOffset();
1515: final int parLen = parEnd - parStart;
1516:
1517: try {
1518: content.getChars(parStart, parLen, text);
1519: } catch (final BadLocationException e) {
1520: }
1521:
1522: Bidi bidi = new Bidi(text.array, text.offset, null, 0,
1523: text.count, getDefaultDirection(par));
1524:
1525: final int runCount = bidi.getRunCount();
1526: for (int i = 0; i < runCount; i++) {
1527: int level = bidi.getRunLevel(i);
1528:
1529: if (i == 0 && added.size() > 0) {
1530: Element prevBidi = added.get(added.size() - 1);
1531: if (getBidiLevel(prevBidi) == level) {
1532: added.remove(added.size() - 1);
1533: added.add(new BidiElement(prevBidi.getAttributes(),
1534: prevBidi.getStartOffset(), parStart
1535: + bidi.getRunLimit(i)));
1536: continue;
1537: }
1538: }
1539:
1540: added.add(new BidiElement(context.addAttribute(context
1541: .getEmptySet(), StyleConstants.BidiLevel,
1542: new Integer(level)),
1543: parStart + bidi.getRunStart(i), parStart
1544: + bidi.getRunLimit(i)));
1545: }
1546:
1547: }
1548:
1549: private boolean bidiUpdateProperty(final int offset,
1550: final Segment text) {
1551: final boolean hasBidiInfo = hasBidiInfo();
1552:
1553: if (!hasBidiInfo
1554: && TextUtils.isLTR(getDefaultDirection(offset))
1555: && !Bidi.requiresBidi(text.array, text.offset,
1556: text.offset + text.count)) {
1557:
1558: return false;
1559: }
1560:
1561: final Bidi bidi = new Bidi(text.array, text.offset, null, 0,
1562: text.count, getDefaultDirection(offset));
1563:
1564: if (hasBidiInfo && !bidi.isMixed()
1565: && isLeftToRight(offset) == bidi.isLeftToRight()) {
1566:
1567: return false;
1568: }
1569:
1570: if (!hasBidiInfo) {
1571: putProperty(StringConstants.BIDI_PROPERTY, Boolean.TRUE);
1572: }
1573: return true;
1574: }
1575:
1576: private void bidiUpdateStructure(final DefaultDocumentEvent event,
1577: final List<Element> added) {
1578: Element par = getParagraphElement(event.getOffset());
1579:
1580: int prevParIndex = bidiAdjustFirstElement(added, par);
1581:
1582: int nextParIndex = bidiAdjustLastElement(added);
1583: if (nextParIndex == -1) {
1584: nextParIndex = bidiRoot.getElementCount() - 1;
1585: }
1586:
1587: int removedLength = nextParIndex - prevParIndex + 1;
1588: Element[] removed = new Element[removedLength];
1589: System.arraycopy(bidiRoot.elements, prevParIndex, removed, 0,
1590: removedLength);
1591:
1592: Element[] addedElements = added.toArray(new Element[added
1593: .size()]);
1594: bidiRoot.replace(prevParIndex, removedLength, addedElements);
1595:
1596: ElementEdit edit = new ElementEdit(bidiRoot, prevParIndex,
1597: removed, addedElements);
1598: event.addEdit(edit);
1599: }
1600:
1601: private int getDefaultDirection(final Element par) {
1602: Object runDirection = null;
1603:
1604: if (par != null) {
1605: runDirection = par.getAttributes().getAttribute(
1606: TextAttribute.RUN_DIRECTION);
1607:
1608: if (runDirection != null) {
1609: return getDefaultDirection(runDirection);
1610: }
1611: }
1612:
1613: runDirection = getProperty(TextAttribute.RUN_DIRECTION);
1614: if (runDirection != null) {
1615: return getDefaultDirection(runDirection);
1616: } else {
1617: return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
1618: }
1619: }
1620:
1621: private int getDefaultDirection(final int offset) {
1622: return getDefaultDirection(getParagraphElement(offset));
1623: }
1624:
1625: private boolean hasBidiInfo() {
1626: final Object docProperty = getProperty(StringConstants.BIDI_PROPERTY);
1627: return Boolean.TRUE.equals(docProperty);
1628: }
1629:
1630: private UIDefaults getUIDefaults() {
1631: if (uiDefaults == null) {
1632: uiDefaults = new UIDefaults();
1633: }
1634:
1635: return uiDefaults;
1636: }
1637:
1638: private String getLocalizedString(final String key) {
1639: Boolean isAWTDoc = (Boolean) getProperty(PropertyNames.AWT_DOCUMENT);
1640: if (isAWTDoc != null && isAWTDoc.booleanValue()) {
1641: return getUIDefaults().getString(key);
1642: } else {
1643: return UIManager.getString(key);
1644: }
1645: }
1646:
1647: private void initTransientFields() {
1648: try {
1649: docStart = content.createPosition(0);
1650: docEnd = content.createPosition(content.length());
1651: } catch (final BadLocationException e) {
1652: }
1653:
1654: lock = new ReadWriteLock();
1655:
1656: filterBypasser = new DocumentFilter.FilterBypass() {
1657:
1658: @Override
1659: public Document getDocument() {
1660: return AbstractDocument.this ;
1661: }
1662:
1663: @Override
1664: public void insertString(final int offset,
1665: final String text, final AttributeSet attrs)
1666: throws BadLocationException {
1667:
1668: doInsert(offset, text, attrs);
1669: }
1670:
1671: @Override
1672: public void remove(final int offset, final int length)
1673: throws BadLocationException {
1674:
1675: doRemove(offset, length);
1676: }
1677:
1678: @Override
1679: public void replace(final int offset, final int length,
1680: final String newText, final AttributeSet attrs)
1681: throws BadLocationException {
1682:
1683: doReplace(offset, length, newText, attrs);
1684: }
1685:
1686: };
1687:
1688: listenerList = new EventListenerList();
1689: }
1690:
1691: private void readObject(final ObjectInputStream ois)
1692: throws IOException, ClassNotFoundException {
1693:
1694: ois.defaultReadObject();
1695:
1696: context = (AttributeContext) ois.readObject();
1697: content = (Content) ois.readObject();
1698:
1699: initTransientFields();
1700:
1701: bidiRoot = (BranchElement) ois.readObject();
1702: }
1703:
1704: private void writeObject(final ObjectOutputStream oos)
1705: throws IOException {
1706: oos.defaultWriteObject();
1707:
1708: oos.writeObject(context);
1709: oos.writeObject(content);
1710: oos.writeObject(bidiRoot);
1711: }
1712:
1713: private static int getBidiLevel(final Element e) {
1714: return StyleConstants.getBidiLevel(e.getAttributes());
1715: }
1716:
1717: private static int getDefaultDirection(final Object runDirection) {
1718: return TextAttribute.RUN_DIRECTION_LTR.equals(runDirection) ? Bidi.DIRECTION_LEFT_TO_RIGHT
1719: : Bidi.DIRECTION_RIGHT_TO_LEFT;
1720: }
1721:
1722: }
|