0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jface.text;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Arrays;
0014: import java.util.HashMap;
0015: import java.util.Iterator;
0016: import java.util.List;
0017: import java.util.Map;
0018: import java.util.regex.PatternSyntaxException;
0019:
0020: import org.eclipse.core.runtime.Assert;
0021: import org.eclipse.core.runtime.ListenerList;
0022:
0023: /**
0024: * Abstract default implementation of <code>IDocument</code> and its extension
0025: * interfaces {@link org.eclipse.jface.text.IDocumentExtension},
0026: * {@link org.eclipse.jface.text.IDocumentExtension2},
0027: * {@link org.eclipse.jface.text.IDocumentExtension3},
0028: * {@link org.eclipse.jface.text.IDocumentExtension4}, as well as
0029: * {@link org.eclipse.jface.text.IRepairableDocument}.
0030: * <p>
0031: *
0032: * An <code>AbstractDocument</code> supports the following implementation
0033: * plug-ins:
0034: * <ul>
0035: * <li>a text store implementing {@link org.eclipse.jface.text.ITextStore} for
0036: * storing and managing the document's content,</li>
0037: * <li>a line tracker implementing {@link org.eclipse.jface.text.ILineTracker}
0038: * to map character positions to line numbers and vice versa</li>
0039: * </ul>
0040: * The document can dynamically change the text store when switching between
0041: * sequential rewrite mode and normal mode.
0042: * <p>
0043: *
0044: * This class must be subclassed. Subclasses must configure which implementation
0045: * plug-ins the document instance should use. Subclasses are not intended to
0046: * overwrite existing methods.
0047: *
0048: * @see org.eclipse.jface.text.ITextStore
0049: * @see org.eclipse.jface.text.ILineTracker
0050: */
0051: public abstract class AbstractDocument implements IDocument,
0052: IDocumentExtension, IDocumentExtension2, IDocumentExtension3,
0053: IDocumentExtension4, IRepairableDocument {
0054:
0055: /**
0056: * Tells whether this class is in debug mode.
0057: * @since 3.1
0058: */
0059: private static final boolean DEBUG = false;
0060:
0061: /**
0062: * Inner class to bundle a registered post notification replace operation together with its
0063: * owner.
0064: *
0065: * @since 2.0
0066: */
0067: static private class RegisteredReplace {
0068: /** The owner of this replace operation. */
0069: IDocumentListener fOwner;
0070: /** The replace operation */
0071: IDocumentExtension.IReplace fReplace;
0072:
0073: /**
0074: * Creates a new bundle object.
0075: * @param owner the document listener owning the replace operation
0076: * @param replace the replace operation
0077: */
0078: RegisteredReplace(IDocumentListener owner,
0079: IDocumentExtension.IReplace replace) {
0080: fOwner = owner;
0081: fReplace = replace;
0082: }
0083: }
0084:
0085: /** The document's text store */
0086: private ITextStore fStore;
0087: /** The document's line tracker */
0088: private ILineTracker fTracker;
0089: /** The registered document listeners */
0090: private ListenerList fDocumentListeners;
0091: /** The registered pre-notified document listeners */
0092: private ListenerList fPrenotifiedDocumentListeners;
0093: /** The registered document partitioning listeners */
0094: private ListenerList fDocumentPartitioningListeners;
0095: /** All positions managed by the document */
0096: private Map fPositions;
0097: /** All registered document position updaters */
0098: private List fPositionUpdaters;
0099: /**
0100: * The list of post notification changes
0101: * @since 2.0
0102: */
0103: private List fPostNotificationChanges;
0104: /**
0105: * The reentrance count for post notification changes.
0106: * @since 2.0
0107: */
0108: private int fReentranceCount = 0;
0109: /**
0110: * Indicates whether post notification change processing has been stopped.
0111: * @since 2.0
0112: */
0113: private int fStoppedCount = 0;
0114: /**
0115: * Indicates whether the registration of post notification changes should be ignored.
0116: * @since 2.1
0117: */
0118: private boolean fAcceptPostNotificationReplaces = true;
0119: /**
0120: * Indicates whether the notification of listeners has been stopped.
0121: * @since 2.1
0122: */
0123: private int fStoppedListenerNotification = 0;
0124: /**
0125: * The document event to be sent after listener notification has been resumed.
0126: * @since 2.1
0127: */
0128: private DocumentEvent fDeferredDocumentEvent;
0129: /**
0130: * The registered document partitioners.
0131: * @since 3.0
0132: */
0133: private Map fDocumentPartitioners;
0134: /**
0135: * The partitioning changed event.
0136: * @since 3.0
0137: */
0138: private DocumentPartitioningChangedEvent fDocumentPartitioningChangedEvent;
0139: /**
0140: * The find/replace document adapter.
0141: * @since 3.0
0142: */
0143: private FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
0144: /**
0145: * The active document rewrite session.
0146: * @since 3.1
0147: */
0148: private DocumentRewriteSession fDocumentRewriteSession;
0149: /**
0150: * The registered document rewrite session listeners.
0151: * @since 3.1
0152: */
0153: private List fDocumentRewriteSessionListeners;
0154: /**
0155: * The current modification stamp.
0156: * @since 3.1
0157: */
0158: private long fModificationStamp = IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
0159: /**
0160: * Keeps track of next modification stamp.
0161: * @since 3.1.1
0162: */
0163: private long fNextModificationStamp = IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
0164: /**
0165: * This document's default line delimiter.
0166: * @since 3.1
0167: */
0168: private String fInitialLineDelimiter;
0169:
0170: /**
0171: * The default constructor does not perform any configuration
0172: * but leaves it to the clients who must first initialize the
0173: * implementation plug-ins and then call <code>completeInitialization</code>.
0174: * Results in the construction of an empty document.
0175: */
0176: protected AbstractDocument() {
0177: fModificationStamp = getNextModificationStamp();
0178: }
0179:
0180: /**
0181: * Returns the document's text store. Assumes that the
0182: * document has been initialized with a text store.
0183: *
0184: * @return the document's text store
0185: */
0186: protected ITextStore getStore() {
0187: Assert.isNotNull(fStore);
0188: return fStore;
0189: }
0190:
0191: /**
0192: * Returns the document's line tracker. Assumes that the
0193: * document has been initialized with a line tracker.
0194: *
0195: * @return the document's line tracker
0196: */
0197: protected ILineTracker getTracker() {
0198: Assert.isNotNull(fTracker);
0199: return fTracker;
0200: }
0201:
0202: /**
0203: * Returns the document's document listeners.
0204: *
0205: * @return the document's document listeners
0206: */
0207: protected List getDocumentListeners() {
0208: return Arrays.asList(fDocumentListeners.getListeners());
0209: }
0210:
0211: /**
0212: * Returns the document's partitioning listeners.
0213: *
0214: * @return the document's partitioning listeners
0215: */
0216: protected List getDocumentPartitioningListeners() {
0217: return Arrays.asList(fDocumentPartitioningListeners
0218: .getListeners());
0219: }
0220:
0221: /**
0222: * Returns all positions managed by the document grouped by category.
0223: *
0224: * @return the document's positions
0225: */
0226: protected Map getDocumentManagedPositions() {
0227: return fPositions;
0228: }
0229:
0230: /*
0231: * @see org.eclipse.jface.text.IDocument#getDocumentPartitioner()
0232: */
0233: public IDocumentPartitioner getDocumentPartitioner() {
0234: return getDocumentPartitioner(DEFAULT_PARTITIONING);
0235: }
0236:
0237: //--- implementation configuration interface ------------
0238:
0239: /**
0240: * Sets the document's text store.
0241: * Must be called at the beginning of the constructor.
0242: *
0243: * @param store the document's text store
0244: */
0245: protected void setTextStore(ITextStore store) {
0246: fStore = store;
0247: }
0248:
0249: /**
0250: * Sets the document's line tracker.
0251: * Must be called at the beginning of the constructor.
0252: *
0253: * @param tracker the document's line tracker
0254: */
0255: protected void setLineTracker(ILineTracker tracker) {
0256: fTracker = tracker;
0257: }
0258:
0259: /*
0260: * @see org.eclipse.jface.text.IDocument#setDocumentPartitioner(org.eclipse.jface.text.IDocumentPartitioner)
0261: */
0262: public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
0263: setDocumentPartitioner(DEFAULT_PARTITIONING, partitioner);
0264: }
0265:
0266: /**
0267: * Initializes document listeners, positions, and position updaters.
0268: * Must be called inside the constructor after the implementation plug-ins
0269: * have been set.
0270: */
0271: protected void completeInitialization() {
0272:
0273: fPositions = new HashMap();
0274: fPositionUpdaters = new ArrayList();
0275: fDocumentListeners = new ListenerList();
0276: fPrenotifiedDocumentListeners = new ListenerList();
0277: fDocumentPartitioningListeners = new ListenerList();
0278: fDocumentRewriteSessionListeners = new ArrayList();
0279:
0280: addPositionCategory(DEFAULT_CATEGORY);
0281: addPositionUpdater(new DefaultPositionUpdater(DEFAULT_CATEGORY));
0282: }
0283:
0284: //-------------------------------------------------------
0285:
0286: /*
0287: * @see org.eclipse.jface.text.IDocument#addDocumentListener(org.eclipse.jface.text.IDocumentListener)
0288: */
0289: public void addDocumentListener(IDocumentListener listener) {
0290: Assert.isNotNull(listener);
0291: fDocumentListeners.add(listener);
0292: }
0293:
0294: /*
0295: * @see org.eclipse.jface.text.IDocument#removeDocumentListener(org.eclipse.jface.text.IDocumentListener)
0296: */
0297: public void removeDocumentListener(IDocumentListener listener) {
0298: Assert.isNotNull(listener);
0299: fDocumentListeners.remove(listener);
0300: }
0301:
0302: /*
0303: * @see org.eclipse.jface.text.IDocument#addPrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
0304: */
0305: public void addPrenotifiedDocumentListener(
0306: IDocumentListener listener) {
0307: Assert.isNotNull(listener);
0308: fPrenotifiedDocumentListeners.add(listener);
0309: }
0310:
0311: /*
0312: * @see org.eclipse.jface.text.IDocument#removePrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
0313: */
0314: public void removePrenotifiedDocumentListener(
0315: IDocumentListener listener) {
0316: Assert.isNotNull(listener);
0317: fPrenotifiedDocumentListeners.remove(listener);
0318: }
0319:
0320: /*
0321: * @see org.eclipse.jface.text.IDocument#addDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
0322: */
0323: public void addDocumentPartitioningListener(
0324: IDocumentPartitioningListener listener) {
0325: Assert.isNotNull(listener);
0326: fDocumentPartitioningListeners.add(listener);
0327: }
0328:
0329: /*
0330: * @see org.eclipse.jface.text.IDocument#removeDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
0331: */
0332: public void removeDocumentPartitioningListener(
0333: IDocumentPartitioningListener listener) {
0334: Assert.isNotNull(listener);
0335: fDocumentPartitioningListeners.remove(listener);
0336: }
0337:
0338: /*
0339: * @see org.eclipse.jface.text.IDocument#addPosition(java.lang.String, org.eclipse.jface.text.Position)
0340: */
0341: public void addPosition(String category, Position position)
0342: throws BadLocationException, BadPositionCategoryException {
0343:
0344: if ((0 > position.offset) || (0 > position.length)
0345: || (position.offset + position.length > getLength()))
0346: throw new BadLocationException();
0347:
0348: if (category == null)
0349: throw new BadPositionCategoryException();
0350:
0351: List list = (List) fPositions.get(category);
0352: if (list == null)
0353: throw new BadPositionCategoryException();
0354:
0355: list.add(computeIndexInPositionList(list, position.offset),
0356: position);
0357: }
0358:
0359: /*
0360: * @see org.eclipse.jface.text.IDocument#addPosition(org.eclipse.jface.text.Position)
0361: */
0362: public void addPosition(Position position)
0363: throws BadLocationException {
0364: try {
0365: addPosition(DEFAULT_CATEGORY, position);
0366: } catch (BadPositionCategoryException e) {
0367: }
0368: }
0369:
0370: /*
0371: * @see org.eclipse.jface.text.IDocument#addPositionCategory(java.lang.String)
0372: */
0373: public void addPositionCategory(String category) {
0374:
0375: if (category == null)
0376: return;
0377:
0378: if (!containsPositionCategory(category))
0379: fPositions.put(category, new ArrayList());
0380: }
0381:
0382: /*
0383: * @see org.eclipse.jface.text.IDocument#addPositionUpdater(org.eclipse.jface.text.IPositionUpdater)
0384: */
0385: public void addPositionUpdater(IPositionUpdater updater) {
0386: insertPositionUpdater(updater, fPositionUpdaters.size());
0387: }
0388:
0389: /*
0390: * @see org.eclipse.jface.text.IDocument#containsPosition(java.lang.String, int, int)
0391: */
0392: public boolean containsPosition(String category, int offset,
0393: int length) {
0394:
0395: if (category == null)
0396: return false;
0397:
0398: List list = (List) fPositions.get(category);
0399: if (list == null)
0400: return false;
0401:
0402: int size = list.size();
0403: if (size == 0)
0404: return false;
0405:
0406: int index = computeIndexInPositionList(list, offset);
0407: if (index < size) {
0408: Position p = (Position) list.get(index);
0409: while (p != null && p.offset == offset) {
0410: if (p.length == length)
0411: return true;
0412: ++index;
0413: p = (index < size) ? (Position) list.get(index) : null;
0414: }
0415: }
0416:
0417: return false;
0418: }
0419:
0420: /*
0421: * @see org.eclipse.jface.text.IDocument#containsPositionCategory(java.lang.String)
0422: */
0423: public boolean containsPositionCategory(String category) {
0424: if (category != null)
0425: return fPositions.containsKey(category);
0426: return false;
0427: }
0428:
0429: /**
0430: * Computes the index in the list of positions at which a position with the given
0431: * offset would be inserted. The position is supposed to become the first in this list
0432: * of all positions with the same offset.
0433: *
0434: * @param positions the list in which the index is computed
0435: * @param offset the offset for which the index is computed
0436: * @return the computed index
0437: *
0438: * @see IDocument#computeIndexInCategory(String, int)
0439: */
0440: protected int computeIndexInPositionList(List positions, int offset) {
0441:
0442: if (positions.size() == 0)
0443: return 0;
0444:
0445: int left = 0;
0446: int right = positions.size() - 1;
0447: int mid = 0;
0448: Position p = null;
0449:
0450: while (left < right) {
0451:
0452: mid = (left + right) / 2;
0453:
0454: p = (Position) positions.get(mid);
0455: if (offset < p.getOffset()) {
0456: if (left == mid)
0457: right = left;
0458: else
0459: right = mid - 1;
0460: } else if (offset > p.getOffset()) {
0461: if (right == mid)
0462: left = right;
0463: else
0464: left = mid + 1;
0465: } else if (offset == p.getOffset()) {
0466: left = right = mid;
0467: }
0468:
0469: }
0470:
0471: int pos = left;
0472: p = (Position) positions.get(pos);
0473: if (offset > p.getOffset()) {
0474: // append to the end
0475: pos++;
0476: } else {
0477: // entry will became the first of all entries with the same offset
0478: do {
0479: --pos;
0480: if (pos < 0)
0481: break;
0482: p = (Position) positions.get(pos);
0483: } while (offset == p.getOffset());
0484: ++pos;
0485: }
0486:
0487: Assert.isTrue(0 <= pos && pos <= positions.size());
0488:
0489: return pos;
0490: }
0491:
0492: /*
0493: * @see org.eclipse.jface.text.IDocument#computeIndexInCategory(java.lang.String, int)
0494: */
0495: public int computeIndexInCategory(String category, int offset)
0496: throws BadLocationException, BadPositionCategoryException {
0497:
0498: if (0 > offset || offset > getLength())
0499: throw new BadLocationException();
0500:
0501: List c = (List) fPositions.get(category);
0502: if (c == null)
0503: throw new BadPositionCategoryException();
0504:
0505: return computeIndexInPositionList(c, offset);
0506: }
0507:
0508: /**
0509: * Fires the document partitioning changed notification to all registered
0510: * document partitioning listeners. Uses a robust iterator.
0511: *
0512: * @deprecated as of 2.0. Use <code>fireDocumentPartitioningChanged(IRegion)</code> instead.
0513: */
0514: protected void fireDocumentPartitioningChanged() {
0515: if (fDocumentPartitioningListeners == null)
0516: return;
0517:
0518: Object[] listeners = fDocumentPartitioningListeners
0519: .getListeners();
0520: for (int i = 0; i < listeners.length; i++)
0521: ((IDocumentPartitioningListener) listeners[i])
0522: .documentPartitioningChanged(this );
0523: }
0524:
0525: /**
0526: * Fires the document partitioning changed notification to all registered
0527: * document partitioning listeners. Uses a robust iterator.
0528: *
0529: * @param region the region in which partitioning has changed
0530: *
0531: * @see IDocumentPartitioningListenerExtension
0532: * @since 2.0
0533: * @deprecated as of 3.0. Use
0534: * <code>fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent)</code>
0535: * instead.
0536: */
0537: protected void fireDocumentPartitioningChanged(IRegion region) {
0538: if (fDocumentPartitioningListeners == null)
0539: return;
0540:
0541: Object[] listeners = fDocumentPartitioningListeners
0542: .getListeners();
0543: for (int i = 0; i < listeners.length; i++) {
0544: IDocumentPartitioningListener l = (IDocumentPartitioningListener) listeners[i];
0545: if (l instanceof IDocumentPartitioningListenerExtension)
0546: ((IDocumentPartitioningListenerExtension) l)
0547: .documentPartitioningChanged(this , region);
0548: else
0549: l.documentPartitioningChanged(this );
0550: }
0551: }
0552:
0553: /**
0554: * Fires the document partitioning changed notification to all registered
0555: * document partitioning listeners. Uses a robust iterator.
0556: *
0557: * @param event the document partitioning changed event
0558: *
0559: * @see IDocumentPartitioningListenerExtension2
0560: * @since 3.0
0561: */
0562: protected void fireDocumentPartitioningChanged(
0563: DocumentPartitioningChangedEvent event) {
0564: if (fDocumentPartitioningListeners == null)
0565: return;
0566:
0567: Object[] listeners = fDocumentPartitioningListeners
0568: .getListeners();
0569: for (int i = 0; i < listeners.length; i++) {
0570: IDocumentPartitioningListener l = (IDocumentPartitioningListener) listeners[i];
0571: if (l instanceof IDocumentPartitioningListenerExtension2) {
0572: IDocumentPartitioningListenerExtension2 extension2 = (IDocumentPartitioningListenerExtension2) l;
0573: extension2.documentPartitioningChanged(event);
0574: } else if (l instanceof IDocumentPartitioningListenerExtension) {
0575: IDocumentPartitioningListenerExtension extension = (IDocumentPartitioningListenerExtension) l;
0576: extension.documentPartitioningChanged(this , event
0577: .getCoverage());
0578: } else {
0579: l.documentPartitioningChanged(this );
0580: }
0581: }
0582: }
0583:
0584: /**
0585: * Fires the given document event to all registers document listeners informing them
0586: * about the forthcoming document manipulation. Uses a robust iterator.
0587: *
0588: * @param event the event to be sent out
0589: */
0590: protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
0591:
0592: // IDocumentExtension
0593: if (fReentranceCount == 0)
0594: flushPostNotificationChanges();
0595:
0596: if (fDocumentPartitioners != null) {
0597: Iterator e = fDocumentPartitioners.values().iterator();
0598: while (e.hasNext()) {
0599: IDocumentPartitioner p = (IDocumentPartitioner) e
0600: .next();
0601: if (p instanceof IDocumentPartitionerExtension3) {
0602: IDocumentPartitionerExtension3 extension = (IDocumentPartitionerExtension3) p;
0603: if (extension.getActiveRewriteSession() != null)
0604: continue;
0605: }
0606: p.documentAboutToBeChanged(event);
0607: }
0608: }
0609:
0610: Object[] listeners = fPrenotifiedDocumentListeners
0611: .getListeners();
0612: for (int i = 0; i < listeners.length; i++)
0613: ((IDocumentListener) listeners[i])
0614: .documentAboutToBeChanged(event);
0615:
0616: listeners = fDocumentListeners.getListeners();
0617: for (int i = 0; i < listeners.length; i++)
0618: ((IDocumentListener) listeners[i])
0619: .documentAboutToBeChanged(event);
0620: }
0621:
0622: /**
0623: * Updates document partitioning and document positions according to the
0624: * specification given by the document event.
0625: *
0626: * @param event the document event describing the change to which structures must be adapted
0627: */
0628: protected void updateDocumentStructures(DocumentEvent event) {
0629:
0630: if (fDocumentPartitioners != null) {
0631: fDocumentPartitioningChangedEvent = new DocumentPartitioningChangedEvent(
0632: this );
0633: Iterator e = fDocumentPartitioners.keySet().iterator();
0634: while (e.hasNext()) {
0635: String partitioning = (String) e.next();
0636: IDocumentPartitioner partitioner = (IDocumentPartitioner) fDocumentPartitioners
0637: .get(partitioning);
0638:
0639: if (partitioner instanceof IDocumentPartitionerExtension3) {
0640: IDocumentPartitionerExtension3 extension = (IDocumentPartitionerExtension3) partitioner;
0641: if (extension.getActiveRewriteSession() != null)
0642: continue;
0643: }
0644:
0645: if (partitioner instanceof IDocumentPartitionerExtension) {
0646: IDocumentPartitionerExtension extension = (IDocumentPartitionerExtension) partitioner;
0647: IRegion r = extension.documentChanged2(event);
0648: if (r != null)
0649: fDocumentPartitioningChangedEvent
0650: .setPartitionChange(partitioning, r
0651: .getOffset(), r.getLength());
0652: } else {
0653: if (partitioner.documentChanged(event))
0654: fDocumentPartitioningChangedEvent
0655: .setPartitionChange(partitioning, 0,
0656: event.getDocument().getLength());
0657: }
0658: }
0659: }
0660:
0661: if (fPositions.size() > 0)
0662: updatePositions(event);
0663: }
0664:
0665: /**
0666: * Notifies all listeners about the given document change. Uses a robust
0667: * iterator.
0668: * <p>
0669: * Executes all registered post notification replace operation.
0670: *
0671: * @param event the event to be sent out.
0672: */
0673: protected void doFireDocumentChanged(DocumentEvent event) {
0674: boolean changed = fDocumentPartitioningChangedEvent != null
0675: && !fDocumentPartitioningChangedEvent.isEmpty();
0676: IRegion change = changed ? fDocumentPartitioningChangedEvent
0677: .getCoverage() : null;
0678: doFireDocumentChanged(event, changed, change);
0679: }
0680:
0681: /**
0682: * Notifies all listeners about the given document change.
0683: * Uses a robust iterator. <p>
0684: * Executes all registered post notification replace operation.
0685: *
0686: * @param event the event to be sent out
0687: * @param firePartitionChange <code>true</code> if a partition change notification should be sent
0688: * @param partitionChange the region whose partitioning changed
0689: * @since 2.0
0690: * @deprecated as of 3.0. Use <code>doFireDocumentChanged2(DocumentEvent)</code> instead; this method will be removed.
0691: */
0692: protected void doFireDocumentChanged(DocumentEvent event,
0693: boolean firePartitionChange, IRegion partitionChange) {
0694: doFireDocumentChanged2(event);
0695: }
0696:
0697: /**
0698: * Notifies all listeners about the given document change. Uses a robust
0699: * iterator.
0700: * <p>
0701: * Executes all registered post notification replace operation.
0702: * <p>
0703: * This method will be renamed to <code>doFireDocumentChanged</code>.
0704: *
0705: * @param event the event to be sent out
0706: * @since 3.0
0707: */
0708: protected void doFireDocumentChanged2(DocumentEvent event) {
0709:
0710: DocumentPartitioningChangedEvent p = fDocumentPartitioningChangedEvent;
0711: fDocumentPartitioningChangedEvent = null;
0712: if (p != null && !p.isEmpty())
0713: fireDocumentPartitioningChanged(p);
0714:
0715: Object[] listeners = fPrenotifiedDocumentListeners
0716: .getListeners();
0717: for (int i = 0; i < listeners.length; i++)
0718: ((IDocumentListener) listeners[i]).documentChanged(event);
0719:
0720: listeners = fDocumentListeners.getListeners();
0721: for (int i = 0; i < listeners.length; i++)
0722: ((IDocumentListener) listeners[i]).documentChanged(event);
0723:
0724: // IDocumentExtension
0725: ++fReentranceCount;
0726: try {
0727: if (fReentranceCount == 1)
0728: executePostNotificationChanges();
0729: } finally {
0730: --fReentranceCount;
0731: }
0732: }
0733:
0734: /**
0735: * Updates the internal document structures and informs all document listeners
0736: * if listener notification has been enabled. Otherwise it remembers the event
0737: * to be sent to the listeners on resume.
0738: *
0739: * @param event the document event to be sent out
0740: */
0741: protected void fireDocumentChanged(DocumentEvent event) {
0742: updateDocumentStructures(event);
0743:
0744: if (fStoppedListenerNotification == 0)
0745: doFireDocumentChanged(event);
0746: else
0747: fDeferredDocumentEvent = event;
0748: }
0749:
0750: /*
0751: * @see org.eclipse.jface.text.IDocument#getChar(int)
0752: */
0753: public char getChar(int pos) throws BadLocationException {
0754: if ((0 > pos) || (pos >= getLength()))
0755: throw new BadLocationException();
0756: return getStore().get(pos);
0757: }
0758:
0759: /*
0760: * @see org.eclipse.jface.text.IDocument#getContentType(int)
0761: */
0762: public String getContentType(int offset)
0763: throws BadLocationException {
0764: String contentType = null;
0765: try {
0766: contentType = getContentType(DEFAULT_PARTITIONING, offset,
0767: false);
0768: Assert.isNotNull(contentType);
0769: } catch (BadPartitioningException e) {
0770: Assert.isTrue(false);
0771: }
0772: return contentType;
0773: }
0774:
0775: /*
0776: * @see org.eclipse.jface.text.IDocument#getLegalContentTypes()
0777: */
0778: public String[] getLegalContentTypes() {
0779: String[] contentTypes = null;
0780: try {
0781: contentTypes = getLegalContentTypes(DEFAULT_PARTITIONING);
0782: Assert.isNotNull(contentTypes);
0783: } catch (BadPartitioningException e) {
0784: Assert.isTrue(false);
0785: }
0786: return contentTypes;
0787: }
0788:
0789: /*
0790: * @see org.eclipse.jface.text.IDocument#getLength()
0791: */
0792: public int getLength() {
0793: return getStore().getLength();
0794: }
0795:
0796: /*
0797: * @see org.eclipse.jface.text.IDocument#getLineDelimiter(int)
0798: */
0799: public String getLineDelimiter(int line)
0800: throws BadLocationException {
0801: return getTracker().getLineDelimiter(line);
0802: }
0803:
0804: /*
0805: * @see org.eclipse.jface.text.IDocument#getLegalLineDelimiters()
0806: */
0807: public String[] getLegalLineDelimiters() {
0808: return getTracker().getLegalLineDelimiters();
0809: }
0810:
0811: /*
0812: * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
0813: * @since 3.1
0814: */
0815: public String getDefaultLineDelimiter() {
0816:
0817: String lineDelimiter = null;
0818:
0819: try {
0820: lineDelimiter = getLineDelimiter(0);
0821: } catch (BadLocationException x) {
0822: }
0823:
0824: if (lineDelimiter != null)
0825: return lineDelimiter;
0826:
0827: if (fInitialLineDelimiter != null)
0828: return fInitialLineDelimiter;
0829:
0830: String sysLineDelimiter = System.getProperty("line.separator"); //$NON-NLS-1$
0831: String[] delimiters = getLegalLineDelimiters();
0832: Assert.isTrue(delimiters.length > 0);
0833: for (int i = 0; i < delimiters.length; i++) {
0834: if (delimiters[i].equals(sysLineDelimiter)) {
0835: lineDelimiter = sysLineDelimiter;
0836: break;
0837: }
0838: }
0839:
0840: if (lineDelimiter == null)
0841: lineDelimiter = delimiters[0];
0842:
0843: return lineDelimiter;
0844:
0845: }
0846:
0847: /*
0848: * @see org.eclipse.jface.text.IDocumentExtension4#setInitialLineDelimiter(java.lang.String)
0849: * @since 3.1
0850: */
0851: public void setInitialLineDelimiter(String lineDelimiter) {
0852: Assert.isNotNull(lineDelimiter);
0853: fInitialLineDelimiter = lineDelimiter;
0854: }
0855:
0856: /*
0857: * @see org.eclipse.jface.text.IDocument#getLineLength(int)
0858: */
0859: public int getLineLength(int line) throws BadLocationException {
0860: return getTracker().getLineLength(line);
0861: }
0862:
0863: /*
0864: * @see org.eclipse.jface.text.IDocument#getLineOfOffset(int)
0865: */
0866: public int getLineOfOffset(int pos) throws BadLocationException {
0867: return getTracker().getLineNumberOfOffset(pos);
0868: }
0869:
0870: /*
0871: * @see org.eclipse.jface.text.IDocument#getLineOffset(int)
0872: */
0873: public int getLineOffset(int line) throws BadLocationException {
0874: return getTracker().getLineOffset(line);
0875: }
0876:
0877: /*
0878: * @see org.eclipse.jface.text.IDocument#getLineInformation(int)
0879: */
0880: public IRegion getLineInformation(int line)
0881: throws BadLocationException {
0882: return getTracker().getLineInformation(line);
0883: }
0884:
0885: /*
0886: * @see org.eclipse.jface.text.IDocument#getLineInformationOfOffset(int)
0887: */
0888: public IRegion getLineInformationOfOffset(int offset)
0889: throws BadLocationException {
0890: return getTracker().getLineInformationOfOffset(offset);
0891: }
0892:
0893: /*
0894: * @see org.eclipse.jface.text.IDocument#getNumberOfLines()
0895: */
0896: public int getNumberOfLines() {
0897: return getTracker().getNumberOfLines();
0898: }
0899:
0900: /*
0901: * @see org.eclipse.jface.text.IDocument#getNumberOfLines(int, int)
0902: */
0903: public int getNumberOfLines(int offset, int length)
0904: throws BadLocationException {
0905: return getTracker().getNumberOfLines(offset, length);
0906: }
0907:
0908: /*
0909: * @see org.eclipse.jface.text.IDocument#computeNumberOfLines(java.lang.String)
0910: */
0911: public int computeNumberOfLines(String text) {
0912: return getTracker().computeNumberOfLines(text);
0913: }
0914:
0915: /*
0916: * @see org.eclipse.jface.text.IDocument#getPartition(int)
0917: */
0918: public ITypedRegion getPartition(int offset)
0919: throws BadLocationException {
0920: ITypedRegion partition = null;
0921: try {
0922: partition = getPartition(DEFAULT_PARTITIONING, offset,
0923: false);
0924: Assert.isNotNull(partition);
0925: } catch (BadPartitioningException e) {
0926: Assert.isTrue(false);
0927: }
0928: return partition;
0929: }
0930:
0931: /*
0932: * @see org.eclipse.jface.text.IDocument#computePartitioning(int, int)
0933: */
0934: public ITypedRegion[] computePartitioning(int offset, int length)
0935: throws BadLocationException {
0936: ITypedRegion[] partitioning = null;
0937: try {
0938: partitioning = computePartitioning(DEFAULT_PARTITIONING,
0939: offset, length, false);
0940: Assert.isNotNull(partitioning);
0941: } catch (BadPartitioningException e) {
0942: Assert.isTrue(false);
0943: }
0944: return partitioning;
0945: }
0946:
0947: /*
0948: * @see org.eclipse.jface.text.IDocument#getPositions(java.lang.String)
0949: */
0950: public Position[] getPositions(String category)
0951: throws BadPositionCategoryException {
0952:
0953: if (category == null)
0954: throw new BadPositionCategoryException();
0955:
0956: List c = (List) fPositions.get(category);
0957: if (c == null)
0958: throw new BadPositionCategoryException();
0959:
0960: Position[] positions = new Position[c.size()];
0961: c.toArray(positions);
0962: return positions;
0963: }
0964:
0965: /*
0966: * @see org.eclipse.jface.text.IDocument#getPositionCategories()
0967: */
0968: public String[] getPositionCategories() {
0969: String[] categories = new String[fPositions.size()];
0970: Iterator keys = fPositions.keySet().iterator();
0971: for (int i = 0; i < categories.length; i++)
0972: categories[i] = (String) keys.next();
0973: return categories;
0974: }
0975:
0976: /*
0977: * @see org.eclipse.jface.text.IDocument#getPositionUpdaters()
0978: */
0979: public IPositionUpdater[] getPositionUpdaters() {
0980: IPositionUpdater[] updaters = new IPositionUpdater[fPositionUpdaters
0981: .size()];
0982: fPositionUpdaters.toArray(updaters);
0983: return updaters;
0984: }
0985:
0986: /*
0987: * @see org.eclipse.jface.text.IDocument#get()
0988: */
0989: public String get() {
0990: return getStore().get(0, getLength());
0991: }
0992:
0993: /*
0994: * @see org.eclipse.jface.text.IDocument#get(int, int)
0995: */
0996: public String get(int pos, int length) throws BadLocationException {
0997: int myLength = getLength();
0998: if ((0 > pos) || (0 > length) || (pos + length > myLength))
0999: throw new BadLocationException();
1000: return getStore().get(pos, length);
1001: }
1002:
1003: /*
1004: * @see org.eclipse.jface.text.IDocument#insertPositionUpdater(org.eclipse.jface.text.IPositionUpdater, int)
1005: */
1006: public void insertPositionUpdater(IPositionUpdater updater,
1007: int index) {
1008:
1009: for (int i = fPositionUpdaters.size() - 1; i >= 0; i--) {
1010: if (fPositionUpdaters.get(i) == updater)
1011: return;
1012: }
1013:
1014: if (index == fPositionUpdaters.size())
1015: fPositionUpdaters.add(updater);
1016: else
1017: fPositionUpdaters.add(index, updater);
1018: }
1019:
1020: /*
1021: * @see org.eclipse.jface.text.IDocument#removePosition(java.lang.String, org.eclipse.jface.text.Position)
1022: */
1023: public void removePosition(String category, Position position)
1024: throws BadPositionCategoryException {
1025:
1026: if (position == null)
1027: return;
1028:
1029: if (category == null)
1030: throw new BadPositionCategoryException();
1031:
1032: List c = (List) fPositions.get(category);
1033: if (c == null)
1034: throw new BadPositionCategoryException();
1035:
1036: // remove based on identity not equality
1037: int size = c.size();
1038: for (int i = 0; i < size; i++) {
1039: if (position == c.get(i)) {
1040: c.remove(i);
1041: return;
1042: }
1043: }
1044: }
1045:
1046: /*
1047: * @see org.eclipse.jface.text.IDocument#removePosition(org.eclipse.jface.text.Position)
1048: */
1049: public void removePosition(Position position) {
1050: try {
1051: removePosition(DEFAULT_CATEGORY, position);
1052: } catch (BadPositionCategoryException e) {
1053: }
1054: }
1055:
1056: /*
1057: * @see org.eclipse.jface.text.IDocument#removePositionCategory(java.lang.String)
1058: */
1059: public void removePositionCategory(String category)
1060: throws BadPositionCategoryException {
1061:
1062: if (category == null)
1063: return;
1064:
1065: if (!containsPositionCategory(category))
1066: throw new BadPositionCategoryException();
1067:
1068: fPositions.remove(category);
1069: }
1070:
1071: /*
1072: * @see org.eclipse.jface.text.IDocument#removePositionUpdater(org.eclipse.jface.text.IPositionUpdater)
1073: */
1074: public void removePositionUpdater(IPositionUpdater updater) {
1075: for (int i = fPositionUpdaters.size() - 1; i >= 0; i--) {
1076: if (fPositionUpdaters.get(i) == updater) {
1077: fPositionUpdaters.remove(i);
1078: return;
1079: }
1080: }
1081: }
1082:
1083: private long getNextModificationStamp() {
1084: if (fNextModificationStamp == Long.MAX_VALUE
1085: || fNextModificationStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
1086: fNextModificationStamp = 0;
1087: else
1088: fNextModificationStamp = fNextModificationStamp + 1;
1089:
1090: return fNextModificationStamp;
1091: }
1092:
1093: /*
1094: * @see org.eclipse.jface.text.IDocumentExtension4#getModificationStamp()
1095: * @since 3.1
1096: */
1097: public long getModificationStamp() {
1098: return fModificationStamp;
1099: }
1100:
1101: /*
1102: * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
1103: * @since 3.1
1104: */
1105: public void replace(int pos, int length, String text,
1106: long modificationStamp) throws BadLocationException {
1107: if ((0 > pos) || (0 > length) || (pos + length > getLength()))
1108: throw new BadLocationException();
1109:
1110: DocumentEvent e = new DocumentEvent(this , pos, length, text);
1111: fireDocumentAboutToBeChanged(e);
1112:
1113: boolean mustRepairLineInformation = mustRepairLineInformation(
1114: pos, length, text);
1115:
1116: getStore().replace(pos, length, text);
1117:
1118: if (mustRepairLineInformation)
1119: repairLineInformation();
1120: else
1121: getTracker().replace(pos, length, text);
1122:
1123: fModificationStamp = modificationStamp;
1124: fNextModificationStamp = Math.max(fModificationStamp,
1125: fNextModificationStamp);
1126: e.fModificationStamp = fModificationStamp;
1127:
1128: fireDocumentChanged(e);
1129: }
1130:
1131: /**
1132: * Checks whether the line information needs to be repaired.
1133: *
1134: * @param pos the start position to check
1135: * @param length the length position to check
1136: * @param text the substitution text to check
1137: * @return <code>true</code> if the line information must be repaired
1138: * @since 3.4
1139: */
1140: protected boolean mustRepairLineInformation(int pos, int length,
1141: String text) {
1142: return false;
1143: }
1144:
1145: /*
1146: * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
1147: */
1148: public void replace(int pos, int length, String text)
1149: throws BadLocationException {
1150: if (length == 0 && (text == null || text.length() == 0))
1151: replace(pos, length, text, getModificationStamp());
1152: else
1153: replace(pos, length, text, getNextModificationStamp());
1154: }
1155:
1156: /*
1157: * @see org.eclipse.jface.text.IDocument#set(java.lang.String)
1158: */
1159: public void set(String text) {
1160: set(text, getNextModificationStamp());
1161: }
1162:
1163: /*
1164: * @see org.eclipse.jface.text.IDocument#set(java.lang.String, long)
1165: */
1166: public void set(String text, long modificationStamp) {
1167: int length = getStore().getLength();
1168:
1169: DocumentEvent e = new DocumentEvent(this , 0, length, text);
1170: fireDocumentAboutToBeChanged(e);
1171:
1172: getStore().set(text);
1173: getTracker().set(text);
1174:
1175: fModificationStamp = modificationStamp;
1176: fNextModificationStamp = Math.max(fModificationStamp,
1177: fNextModificationStamp);
1178: e.fModificationStamp = fModificationStamp;
1179:
1180: fireDocumentChanged(e);
1181: }
1182:
1183: /**
1184: * Updates all positions of all categories to the change described by the
1185: * document event. All registered document updaters are called in the
1186: * sequence they have been arranged. Uses a robust iterator.
1187: *
1188: * @param event the document event describing the change to which to adapt
1189: * the positions
1190: */
1191: protected void updatePositions(DocumentEvent event) {
1192: List list = new ArrayList(fPositionUpdaters);
1193: Iterator e = list.iterator();
1194: while (e.hasNext()) {
1195: IPositionUpdater u = (IPositionUpdater) e.next();
1196: u.update(event);
1197: }
1198: }
1199:
1200: /*
1201: * @see org.eclipse.jface.text.IDocument#search(int, java.lang.String, boolean, boolean, boolean)
1202: */
1203: public int search(int startPosition, String findString,
1204: boolean forwardSearch, boolean caseSensitive,
1205: boolean wholeWord) throws BadLocationException {
1206: try {
1207: IRegion region = getFindReplaceDocumentAdapter().find(
1208: startPosition, findString, forwardSearch,
1209: caseSensitive, wholeWord, false);
1210: return region == null ? -1 : region.getOffset();
1211: } catch (IllegalStateException ex) {
1212: return -1;
1213: } catch (PatternSyntaxException ex) {
1214: return -1;
1215: }
1216: }
1217:
1218: /**
1219: * Returns the find/replace adapter for this document.
1220: *
1221: * @return this document's find/replace document adapter
1222: * @since 3.0
1223: */
1224: private FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
1225: if (fFindReplaceDocumentAdapter == null)
1226: fFindReplaceDocumentAdapter = new FindReplaceDocumentAdapter(
1227: this );
1228:
1229: return fFindReplaceDocumentAdapter;
1230: }
1231:
1232: /**
1233: * Flushes all registered post notification changes.
1234: *
1235: * @since 2.0
1236: */
1237: private void flushPostNotificationChanges() {
1238: if (fPostNotificationChanges != null)
1239: fPostNotificationChanges.clear();
1240: }
1241:
1242: /**
1243: * Executes all registered post notification changes. The process is
1244: * repeated until no new post notification changes are added.
1245: *
1246: * @since 2.0
1247: */
1248: private void executePostNotificationChanges() {
1249:
1250: if (fStoppedCount > 0)
1251: return;
1252:
1253: while (fPostNotificationChanges != null) {
1254: List changes = fPostNotificationChanges;
1255: fPostNotificationChanges = null;
1256:
1257: Iterator e = changes.iterator();
1258: while (e.hasNext()) {
1259: RegisteredReplace replace = (RegisteredReplace) e
1260: .next();
1261: replace.fReplace.perform(this , replace.fOwner);
1262: }
1263: }
1264: }
1265:
1266: /*
1267: * @see org.eclipse.jface.text.IDocumentExtension2#acceptPostNotificationReplaces()
1268: * @since 2.1
1269: */
1270: public void acceptPostNotificationReplaces() {
1271: fAcceptPostNotificationReplaces = true;
1272: }
1273:
1274: /*
1275: * @see org.eclipse.jface.text.IDocumentExtension2#ignorePostNotificationReplaces()
1276: * @since 2.1
1277: */
1278: public void ignorePostNotificationReplaces() {
1279: fAcceptPostNotificationReplaces = false;
1280: }
1281:
1282: /*
1283: * @see org.eclipse.jface.text.IDocumentExtension#registerPostNotificationReplace(org.eclipse.jface.text.IDocumentListener, org.eclipse.jface.text.IDocumentExtension.IReplace)
1284: * @since 2.0
1285: */
1286: public void registerPostNotificationReplace(
1287: IDocumentListener owner, IDocumentExtension.IReplace replace) {
1288: if (fAcceptPostNotificationReplaces) {
1289: if (fPostNotificationChanges == null)
1290: fPostNotificationChanges = new ArrayList(1);
1291: fPostNotificationChanges.add(new RegisteredReplace(owner,
1292: replace));
1293: }
1294: }
1295:
1296: /*
1297: * @see org.eclipse.jface.text.IDocumentExtension#stopPostNotificationProcessing()
1298: * @since 2.0
1299: */
1300: public void stopPostNotificationProcessing() {
1301: ++fStoppedCount;
1302: }
1303:
1304: /*
1305: * @see org.eclipse.jface.text.IDocumentExtension#resumePostNotificationProcessing()
1306: * @since 2.0
1307: */
1308: public void resumePostNotificationProcessing() {
1309: --fStoppedCount;
1310: if (fStoppedCount == 0 && fReentranceCount == 0)
1311: executePostNotificationChanges();
1312: }
1313:
1314: /*
1315: * @see org.eclipse.jface.text.IDocumentExtension#startSequentialRewrite(boolean)
1316: * @since 2.0
1317: */
1318: public void startSequentialRewrite(boolean normalized) {
1319: }
1320:
1321: /*
1322: * @see org.eclipse.jface.text.IDocumentExtension#stopSequentialRewrite()
1323: * @since 2.0
1324: */
1325: public void stopSequentialRewrite() {
1326: }
1327:
1328: /*
1329: * @see org.eclipse.jface.text.IDocumentExtension2#resumeListenerNotification()
1330: * @since 2.1
1331: */
1332: public void resumeListenerNotification() {
1333: --fStoppedListenerNotification;
1334: if (fStoppedListenerNotification == 0) {
1335: resumeDocumentListenerNotification();
1336: }
1337: }
1338:
1339: /*
1340: * @see org.eclipse.jface.text.IDocumentExtension2#stopListenerNotification()
1341: * @since 2.1
1342: */
1343: public void stopListenerNotification() {
1344: ++fStoppedListenerNotification;
1345: }
1346:
1347: /**
1348: * Resumes the document listener notification by sending out the remembered
1349: * partition changed and document event.
1350: *
1351: * @since 2.1
1352: */
1353: private void resumeDocumentListenerNotification() {
1354: if (fDeferredDocumentEvent != null) {
1355: DocumentEvent event = fDeferredDocumentEvent;
1356: fDeferredDocumentEvent = null;
1357: doFireDocumentChanged(event);
1358: }
1359: }
1360:
1361: /*
1362: * @see org.eclipse.jface.text.IDocumentExtension3#computeZeroLengthPartitioning(java.lang.String, int, int)
1363: * @since 3.0
1364: */
1365: public ITypedRegion[] computePartitioning(String partitioning,
1366: int offset, int length, boolean includeZeroLengthPartitions)
1367: throws BadLocationException, BadPartitioningException {
1368: if ((0 > offset) || (0 > length)
1369: || (offset + length > getLength()))
1370: throw new BadLocationException();
1371:
1372: IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
1373:
1374: if (partitioner instanceof IDocumentPartitionerExtension2) {
1375: checkStateOfPartitioner(partitioner, partitioning);
1376: return ((IDocumentPartitionerExtension2) partitioner)
1377: .computePartitioning(offset, length,
1378: includeZeroLengthPartitions);
1379: } else if (partitioner != null) {
1380: checkStateOfPartitioner(partitioner, partitioning);
1381: return partitioner.computePartitioning(offset, length);
1382: } else if (DEFAULT_PARTITIONING.equals(partitioning))
1383: return new TypedRegion[] { new TypedRegion(offset, length,
1384: DEFAULT_CONTENT_TYPE) };
1385: else
1386: throw new BadPartitioningException();
1387: }
1388:
1389: /*
1390: * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthContentType(java.lang.String, int)
1391: * @since 3.0
1392: */
1393: public String getContentType(String partitioning, int offset,
1394: boolean preferOpenPartitions) throws BadLocationException,
1395: BadPartitioningException {
1396: if ((0 > offset) || (offset > getLength()))
1397: throw new BadLocationException();
1398:
1399: IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
1400:
1401: if (partitioner instanceof IDocumentPartitionerExtension2) {
1402: checkStateOfPartitioner(partitioner, partitioning);
1403: return ((IDocumentPartitionerExtension2) partitioner)
1404: .getContentType(offset, preferOpenPartitions);
1405: } else if (partitioner != null) {
1406: checkStateOfPartitioner(partitioner, partitioning);
1407: return partitioner.getContentType(offset);
1408: } else if (DEFAULT_PARTITIONING.equals(partitioning))
1409: return DEFAULT_CONTENT_TYPE;
1410: else
1411: throw new BadPartitioningException();
1412: }
1413:
1414: /*
1415: * @see org.eclipse.jface.text.IDocumentExtension3#getDocumentPartitioner(java.lang.String)
1416: * @since 3.0
1417: */
1418: public IDocumentPartitioner getDocumentPartitioner(
1419: String partitioning) {
1420: return fDocumentPartitioners != null ? (IDocumentPartitioner) fDocumentPartitioners
1421: .get(partitioning)
1422: : null;
1423: }
1424:
1425: /*
1426: * @see org.eclipse.jface.text.IDocumentExtension3#getLegalContentTypes(java.lang.String)
1427: * @since 3.0
1428: */
1429: public String[] getLegalContentTypes(String partitioning)
1430: throws BadPartitioningException {
1431: IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
1432: if (partitioner != null)
1433: return partitioner.getLegalContentTypes();
1434: if (DEFAULT_PARTITIONING.equals(partitioning))
1435: return new String[] { DEFAULT_CONTENT_TYPE };
1436: throw new BadPartitioningException();
1437: }
1438:
1439: /*
1440: * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthPartition(java.lang.String, int)
1441: * @since 3.0
1442: */
1443: public ITypedRegion getPartition(String partitioning, int offset,
1444: boolean preferOpenPartitions) throws BadLocationException,
1445: BadPartitioningException {
1446: if ((0 > offset) || (offset > getLength()))
1447: throw new BadLocationException();
1448:
1449: IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning);
1450:
1451: if (partitioner instanceof IDocumentPartitionerExtension2) {
1452: checkStateOfPartitioner(partitioner, partitioning);
1453: return ((IDocumentPartitionerExtension2) partitioner)
1454: .getPartition(offset, preferOpenPartitions);
1455: } else if (partitioner != null) {
1456: checkStateOfPartitioner(partitioner, partitioning);
1457: return partitioner.getPartition(offset);
1458: } else if (DEFAULT_PARTITIONING.equals(partitioning))
1459: return new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE);
1460: else
1461: throw new BadPartitioningException();
1462: }
1463:
1464: /*
1465: * @see org.eclipse.jface.text.IDocumentExtension3#getPartitionings()
1466: * @since 3.0
1467: */
1468: public String[] getPartitionings() {
1469: if (fDocumentPartitioners == null)
1470: return new String[0];
1471: String[] partitionings = new String[fDocumentPartitioners
1472: .size()];
1473: fDocumentPartitioners.keySet().toArray(partitionings);
1474: return partitionings;
1475: }
1476:
1477: /*
1478: * @see org.eclipse.jface.text.IDocumentExtension3#setDocumentPartitioner(java.lang.String, org.eclipse.jface.text.IDocumentPartitioner)
1479: * @since 3.0
1480: */
1481: public void setDocumentPartitioner(String partitioning,
1482: IDocumentPartitioner partitioner) {
1483: if (partitioner == null) {
1484: if (fDocumentPartitioners != null) {
1485: fDocumentPartitioners.remove(partitioning);
1486: if (fDocumentPartitioners.size() == 0)
1487: fDocumentPartitioners = null;
1488: }
1489: } else {
1490: if (fDocumentPartitioners == null)
1491: fDocumentPartitioners = new HashMap();
1492: fDocumentPartitioners.put(partitioning, partitioner);
1493: }
1494: DocumentPartitioningChangedEvent event = new DocumentPartitioningChangedEvent(
1495: this );
1496: event.setPartitionChange(partitioning, 0, getLength());
1497: fireDocumentPartitioningChanged(event);
1498: }
1499:
1500: /*
1501: * @see org.eclipse.jface.text.IRepairableDocument#repairLineInformation()
1502: * @since 3.0
1503: */
1504: public void repairLineInformation() {
1505: getTracker().set(get());
1506: }
1507:
1508: /**
1509: * Fires the given event to all registered rewrite session listeners. Uses robust iterators.
1510: *
1511: * @param event the event to be fired
1512: * @since 3.1
1513: */
1514: protected void fireRewriteSessionChanged(
1515: DocumentRewriteSessionEvent event) {
1516: if (fDocumentRewriteSessionListeners.size() > 0) {
1517: List list = new ArrayList(fDocumentRewriteSessionListeners);
1518: Iterator e = list.iterator();
1519: while (e.hasNext()) {
1520: IDocumentRewriteSessionListener l = (IDocumentRewriteSessionListener) e
1521: .next();
1522: l.documentRewriteSessionChanged(event);
1523: }
1524: }
1525: }
1526:
1527: /*
1528: * @see org.eclipse.jface.text.IDocumentExtension4#getActiveRewriteSession()
1529: */
1530: public final DocumentRewriteSession getActiveRewriteSession() {
1531: return fDocumentRewriteSession;
1532: }
1533:
1534: /*
1535: * @see org.eclipse.jface.text.IDocumentExtension4#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSessionType)
1536: * @since 3.1
1537: */
1538: public DocumentRewriteSession startRewriteSession(
1539: DocumentRewriteSessionType sessionType) {
1540:
1541: if (getActiveRewriteSession() != null)
1542: throw new IllegalStateException();
1543:
1544: fDocumentRewriteSession = new DocumentRewriteSession(
1545: sessionType);
1546: if (DEBUG)
1547: System.out
1548: .println("AbstractDocument: Starting rewrite session: " + fDocumentRewriteSession); //$NON-NLS-1$
1549:
1550: fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this ,
1551: fDocumentRewriteSession,
1552: DocumentRewriteSessionEvent.SESSION_START));
1553:
1554: startRewriteSessionOnPartitioners(fDocumentRewriteSession);
1555:
1556: ILineTracker tracker = getTracker();
1557: if (tracker instanceof ILineTrackerExtension) {
1558: ILineTrackerExtension extension = (ILineTrackerExtension) tracker;
1559: extension.startRewriteSession(fDocumentRewriteSession);
1560: }
1561:
1562: if (DocumentRewriteSessionType.SEQUENTIAL == sessionType)
1563: startSequentialRewrite(false);
1564: else if (DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType)
1565: startSequentialRewrite(true);
1566:
1567: return fDocumentRewriteSession;
1568: }
1569:
1570: /**
1571: * Starts the given rewrite session.
1572: *
1573: * @param session the rewrite session
1574: * @since 3.1
1575: */
1576: protected final void startRewriteSessionOnPartitioners(
1577: DocumentRewriteSession session) {
1578: if (fDocumentPartitioners != null) {
1579: Iterator e = fDocumentPartitioners.values().iterator();
1580: while (e.hasNext()) {
1581: Object partitioner = e.next();
1582: if (partitioner instanceof IDocumentPartitionerExtension3) {
1583: IDocumentPartitionerExtension3 extension = (IDocumentPartitionerExtension3) partitioner;
1584: extension.startRewriteSession(session);
1585: }
1586: }
1587: }
1588: }
1589:
1590: /*
1591: * @see org.eclipse.jface.text.IDocumentExtension4#stopRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
1592: * @since 3.1
1593: */
1594: public void stopRewriteSession(DocumentRewriteSession session) {
1595: if (fDocumentRewriteSession == session) {
1596:
1597: if (DEBUG)
1598: System.out
1599: .println("AbstractDocument: Stopping rewrite session: " + session); //$NON-NLS-1$
1600:
1601: DocumentRewriteSessionType sessionType = session
1602: .getSessionType();
1603: if (DocumentRewriteSessionType.SEQUENTIAL == sessionType
1604: || DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType)
1605: stopSequentialRewrite();
1606:
1607: ILineTracker tracker = getTracker();
1608: if (tracker instanceof ILineTrackerExtension) {
1609: ILineTrackerExtension extension = (ILineTrackerExtension) tracker;
1610: extension.stopRewriteSession(session, get());
1611: }
1612:
1613: stopRewriteSessionOnPartitioners(fDocumentRewriteSession);
1614:
1615: fDocumentRewriteSession = null;
1616: fireRewriteSessionChanged(new DocumentRewriteSessionEvent(
1617: this , session,
1618: DocumentRewriteSessionEvent.SESSION_STOP));
1619: }
1620: }
1621:
1622: /**
1623: * Stops the given rewrite session.
1624: *
1625: * @param session the rewrite session
1626: * @since 3.1
1627: */
1628: protected final void stopRewriteSessionOnPartitioners(
1629: DocumentRewriteSession session) {
1630: if (fDocumentPartitioners != null) {
1631: DocumentPartitioningChangedEvent event = new DocumentPartitioningChangedEvent(
1632: this );
1633: Iterator e = fDocumentPartitioners.keySet().iterator();
1634: while (e.hasNext()) {
1635: String partitioning = (String) e.next();
1636: IDocumentPartitioner partitioner = (IDocumentPartitioner) fDocumentPartitioners
1637: .get(partitioning);
1638: if (partitioner instanceof IDocumentPartitionerExtension3) {
1639: IDocumentPartitionerExtension3 extension = (IDocumentPartitionerExtension3) partitioner;
1640: extension.stopRewriteSession(session);
1641: event.setPartitionChange(partitioning, 0,
1642: getLength());
1643: }
1644: }
1645: if (!event.isEmpty())
1646: fireDocumentPartitioningChanged(event);
1647: }
1648: }
1649:
1650: /*
1651: * @see org.eclipse.jface.text.IDocumentExtension4#addDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
1652: * @since 3.1
1653: */
1654: public void addDocumentRewriteSessionListener(
1655: IDocumentRewriteSessionListener listener) {
1656: Assert.isNotNull(listener);
1657: if (!fDocumentRewriteSessionListeners.contains(listener))
1658: fDocumentRewriteSessionListeners.add(listener);
1659: }
1660:
1661: /*
1662: * @see org.eclipse.jface.text.IDocumentExtension4#removeDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
1663: * @since 3.1
1664: */
1665: public void removeDocumentRewriteSessionListener(
1666: IDocumentRewriteSessionListener listener) {
1667: Assert.isNotNull(listener);
1668: fDocumentRewriteSessionListeners.remove(listener);
1669: }
1670:
1671: /**
1672: * Checks the state for the given partitioner and stops the
1673: * active rewrite session.
1674: *
1675: * @param partitioner the document partitioner to be checked
1676: * @param partitioning the document partitioning the partitioner is registered for
1677: * @since 3.1
1678: */
1679: protected final void checkStateOfPartitioner(
1680: IDocumentPartitioner partitioner, String partitioning) {
1681: if (!(partitioner instanceof IDocumentPartitionerExtension3))
1682: return;
1683:
1684: IDocumentPartitionerExtension3 extension = (IDocumentPartitionerExtension3) partitioner;
1685: DocumentRewriteSession session = extension
1686: .getActiveRewriteSession();
1687: if (session != null) {
1688: extension.stopRewriteSession(session);
1689:
1690: if (DEBUG)
1691: System.out
1692: .println("AbstractDocument: Flushing rewrite session for partition type: " + partitioning); //$NON-NLS-1$
1693:
1694: DocumentPartitioningChangedEvent event = new DocumentPartitioningChangedEvent(
1695: this );
1696: event.setPartitionChange(partitioning, 0, getLength());
1697: fireDocumentPartitioningChanged(event);
1698: }
1699: }
1700: }
|