diff dwtx/jface/text/AbstractDocument.d @ 129:eb30df5ca28b

Added JFace Text sources
author Frank Benoit <benoit@tionex.de>
date Sat, 23 Aug 2008 19:10:48 +0200
parents
children c4fb132a086c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/text/AbstractDocument.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,1796 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.text.AbstractDocument;
+
+import dwt.dwthelper.utils;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.PatternSyntaxException;
+
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+
+
+/**
+ * Abstract default implementation of <code>IDocument</code> and its extension
+ * interfaces {@link dwtx.jface.text.IDocumentExtension},
+ * {@link dwtx.jface.text.IDocumentExtension2},
+ * {@link dwtx.jface.text.IDocumentExtension3},
+ * {@link dwtx.jface.text.IDocumentExtension4}, as well as
+ * {@link dwtx.jface.text.IRepairableDocument}.
+ * <p>
+ *
+ * An <code>AbstractDocument</code> supports the following implementation
+ * plug-ins:
+ * <ul>
+ * <li>a text store implementing {@link dwtx.jface.text.ITextStore} for
+ *     storing and managing the document's content,</li>
+ * <li>a line tracker implementing {@link dwtx.jface.text.ILineTracker}
+ *     to map character positions to line numbers and vice versa</li>
+ * </ul>
+ * The document can dynamically change the text store when switching between
+ * sequential rewrite mode and normal mode.
+ * <p>
+ *
+ * This class must be subclassed. Subclasses must configure which implementation
+ * plug-ins the document instance should use. Subclasses are not intended to
+ * overwrite existing methods.
+ *
+ * @see dwtx.jface.text.ITextStore
+ * @see dwtx.jface.text.ILineTracker
+ */
+public abstract class AbstractDocument : IDocument, IDocumentExtension, IDocumentExtension2, IDocumentExtension3, IDocumentExtension4, IRepairableDocument, IRepairableDocumentExtension {
+
+    /**
+     * Tells whether this class is in debug mode.
+     * @since 3.1
+     */
+    private static final bool DEBUG= false;
+
+
+    /**
+     * Inner class to bundle a registered post notification replace operation together with its
+     * owner.
+     *
+     * @since 2.0
+     */
+    static private class RegisteredReplace {
+        /** The owner of this replace operation. */
+        IDocumentListener fOwner;
+        /** The replace operation */
+        IDocumentExtension.IReplace fReplace;
+
+        /**
+         * Creates a new bundle object.
+         * @param owner the document listener owning the replace operation
+         * @param replace the replace operation
+         */
+        RegisteredReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+            fOwner= owner;
+            fReplace= replace;
+        }
+    }
+
+
+    /** The document's text store */
+    private ITextStore   fStore;
+    /** The document's line tracker */
+    private ILineTracker fTracker;
+    /** The registered document listeners */
+    private ListenerList fDocumentListeners;
+    /** The registered pre-notified document listeners */
+    private ListenerList fPrenotifiedDocumentListeners;
+    /** The registered document partitioning listeners */
+    private ListenerList fDocumentPartitioningListeners;
+    /** All positions managed by the document ordered by their start positions. */
+    private Map fPositions;
+    /**
+     * All positions managed by the document ordered by there end positions.
+     * @since 3.4
+     */
+    private Map fEndPositions;
+    /** All registered document position updaters */
+    private List fPositionUpdaters;
+    /**
+     * The list of post notification changes
+     * @since 2.0
+     */
+    private List fPostNotificationChanges;
+    /**
+     * The reentrance count for post notification changes.
+     * @since 2.0
+     */
+    private int fReentranceCount= 0;
+    /**
+     * Indicates whether post notification change processing has been stopped.
+     * @since 2.0
+     */
+    private int fStoppedCount= 0;
+    /**
+     * Indicates whether the registration of post notification changes should be ignored.
+     * @since 2.1
+     */
+    private bool fAcceptPostNotificationReplaces= true;
+    /**
+     * Indicates whether the notification of listeners has been stopped.
+     * @since 2.1
+     */
+    private int fStoppedListenerNotification= 0;
+    /**
+     * The document event to be sent after listener notification has been resumed.
+     * @since 2.1
+     */
+    private DocumentEvent fDeferredDocumentEvent;
+    /**
+     * The registered document partitioners.
+     * @since 3.0
+     */
+    private Map fDocumentPartitioners;
+    /**
+     * The partitioning changed event.
+     * @since 3.0
+     */
+    private DocumentPartitioningChangedEvent fDocumentPartitioningChangedEvent;
+    /**
+     * The find/replace document adapter.
+     * @since 3.0
+     */
+    private FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
+    /**
+     * The active document rewrite session.
+     * @since 3.1
+     */
+    private DocumentRewriteSession fDocumentRewriteSession;
+    /**
+     * The registered document rewrite session listeners.
+     * @since 3.1
+     */
+    private List fDocumentRewriteSessionListeners;
+    /**
+     * The current modification stamp.
+     * @since 3.1
+     */
+    private long fModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+    /**
+     * Keeps track of next modification stamp.
+     * @since 3.1.1
+     */
+    private long fNextModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+    /**
+     * This document's default line delimiter.
+     * @since 3.1
+     */
+    private String fInitialLineDelimiter;
+
+
+    /**
+     * The default constructor does not perform any configuration
+     * but leaves it to the clients who must first initialize the
+     * implementation plug-ins and then call <code>completeInitialization</code>.
+     * Results in the construction of an empty document.
+     */
+    protected AbstractDocument() {
+        fModificationStamp= getNextModificationStamp();
+    }
+
+
+    /**
+     * Returns the document's text store. Assumes that the
+     * document has been initialized with a text store.
+     *
+     * @return the document's text store
+     */
+    protected ITextStore getStore() {
+        Assert.isNotNull(fStore);
+        return fStore;
+    }
+
+    /**
+     * Returns the document's line tracker. Assumes that the
+     * document has been initialized with a line tracker.
+     *
+     * @return the document's line tracker
+     */
+    protected ILineTracker getTracker() {
+        Assert.isNotNull(fTracker);
+        return fTracker;
+    }
+
+    /**
+     * Returns the document's document listeners.
+     *
+     * @return the document's document listeners
+     */
+    protected List getDocumentListeners() {
+        return Arrays.asList(fDocumentListeners.getListeners());
+    }
+
+    /**
+     * Returns the document's partitioning listeners.
+     *
+     * @return the document's partitioning listeners
+     */
+    protected List getDocumentPartitioningListeners() {
+        return Arrays.asList(fDocumentPartitioningListeners.getListeners());
+    }
+
+    /**
+     * Returns all positions managed by the document grouped by category.
+     *
+     * @return the document's positions
+     */
+    protected Map getDocumentManagedPositions() {
+        return fPositions;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getDocumentPartitioner()
+     */
+    public IDocumentPartitioner getDocumentPartitioner() {
+        return getDocumentPartitioner(DEFAULT_PARTITIONING);
+    }
+
+
+
+    //--- implementation configuration interface ------------
+
+    /**
+     * Sets the document's text store.
+     * Must be called at the beginning of the constructor.
+     *
+     * @param store the document's text store
+     */
+    protected void setTextStore(ITextStore store) {
+        fStore= store;
+    }
+
+    /**
+     * Sets the document's line tracker.
+     * Must be called at the beginning of the constructor.
+     *
+     * @param tracker the document's line tracker
+     */
+    protected void setLineTracker(ILineTracker tracker) {
+        fTracker= tracker;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#setDocumentPartitioner(dwtx.jface.text.IDocumentPartitioner)
+     */
+    public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
+        setDocumentPartitioner(DEFAULT_PARTITIONING, partitioner);
+    }
+
+    /**
+     * Initializes document listeners, positions, and position updaters.
+     * Must be called inside the constructor after the implementation plug-ins
+     * have been set.
+     */
+    protected void completeInitialization() {
+
+        fPositions= new HashMap();
+        fEndPositions= new HashMap();
+        fPositionUpdaters= new ArrayList();
+        fDocumentListeners= new ListenerList(ListenerList.IDENTITY);
+        fPrenotifiedDocumentListeners= new ListenerList(ListenerList.IDENTITY);
+        fDocumentPartitioningListeners= new ListenerList(ListenerList.IDENTITY);
+        fDocumentRewriteSessionListeners= new ArrayList();
+
+        addPositionCategory(DEFAULT_CATEGORY);
+        addPositionUpdater(new DefaultPositionUpdater(DEFAULT_CATEGORY));
+    }
+
+
+    //-------------------------------------------------------
+
+    /*
+     * @see dwtx.jface.text.IDocument#addDocumentListener(dwtx.jface.text.IDocumentListener)
+     */
+    public void addDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(listener);
+        fDocumentListeners.add(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#removeDocumentListener(dwtx.jface.text.IDocumentListener)
+     */
+    public void removeDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(listener);
+        fDocumentListeners.remove(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#addPrenotifiedDocumentListener(dwtx.jface.text.IDocumentListener)
+     */
+    public void addPrenotifiedDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(listener);
+        fPrenotifiedDocumentListeners.add(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#removePrenotifiedDocumentListener(dwtx.jface.text.IDocumentListener)
+     */
+    public void removePrenotifiedDocumentListener(IDocumentListener listener) {
+        Assert.isNotNull(listener);
+        fPrenotifiedDocumentListeners.remove(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#addDocumentPartitioningListener(dwtx.jface.text.IDocumentPartitioningListener)
+     */
+    public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+        Assert.isNotNull(listener);
+        fDocumentPartitioningListeners.add(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#removeDocumentPartitioningListener(dwtx.jface.text.IDocumentPartitioningListener)
+     */
+    public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+        Assert.isNotNull(listener);
+        fDocumentPartitioningListeners.remove(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#addPosition(java.lang.String, dwtx.jface.text.Position)
+     */
+    public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException  {
+
+        if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getLength()))
+            throw new BadLocationException();
+
+        if (category is null)
+            throw new BadPositionCategoryException();
+
+        List list= (List) fPositions.get(category);
+        if (list is null)
+            throw new BadPositionCategoryException();
+        list.add(computeIndexInPositionList(list, position.offset), position);
+        
+        List endPositions= (List) fEndPositions.get(category);
+        if (endPositions is null)
+            throw new BadPositionCategoryException();
+        endPositions.add(computeIndexInPositionList(endPositions, position.offset + position.length - 1, false), position);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#addPosition(dwtx.jface.text.Position)
+     */
+    public void addPosition(Position position) throws BadLocationException {
+        try {
+            addPosition(DEFAULT_CATEGORY, position);
+        } catch (BadPositionCategoryException e) {
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#addPositionCategory(java.lang.String)
+     */
+    public void addPositionCategory(String category) {
+
+        if (category is null)
+            return;
+
+        if (!containsPositionCategory(category)) {
+            fPositions.put(category, new ArrayList());
+            fEndPositions.put(category, new ArrayList());
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#addPositionUpdater(dwtx.jface.text.IPositionUpdater)
+     */
+    public void addPositionUpdater(IPositionUpdater updater) {
+        insertPositionUpdater(updater, fPositionUpdaters.size());
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#containsPosition(java.lang.String, int, int)
+     */
+    public bool containsPosition(String category, int offset, int length) {
+
+        if (category is null)
+            return false;
+
+        List list= (List) fPositions.get(category);
+        if (list is null)
+            return false;
+
+        int size= list.size();
+        if (size is 0)
+            return false;
+
+        int index= computeIndexInPositionList(list, offset);
+        if (index < size) {
+            Position p= (Position) list.get(index);
+            while (p !is null && p.offset is offset) {
+                if (p.length is length)
+                    return true;
+                ++ index;
+                p= (index < size) ? (Position) list.get(index) : null;
+            }
+        }
+
+        return false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#containsPositionCategory(java.lang.String)
+     */
+    public bool containsPositionCategory(String category) {
+        if (category !is null)
+            return fPositions.containsKey(category);
+        return false;
+    }
+
+
+    /**
+     * Computes the index in the list of positions at which a position with the given
+     * offset would be inserted. The position is supposed to become the first in this list
+     * of all positions with the same offset.
+     *
+     * @param positions the list in which the index is computed
+     * @param offset the offset for which the index is computed
+     * @return the computed index
+     *
+     * @see IDocument#computeIndexInCategory(String, int)
+     * @deprecated As of 3.4, replaced by {@link #computeIndexInPositionList(List, int, bool)}
+     */
+    protected int computeIndexInPositionList(List positions, int offset) {
+        return computeIndexInPositionList(positions, offset, true);
+    }
+    
+    /**
+     * Computes the index in the list of positions at which a position with the given
+     * position would be inserted. The position to insert is supposed to become the first
+     * in this list of all positions with the same position.
+     *
+     * @param positions the list in which the index is computed
+     * @param offset the offset for which the index is computed
+     * @param orderedByOffset <code>true</code> if ordered by offset, false if ordered by end position
+     * @return the computed index
+     * @since 3.4
+     */
+    protected int computeIndexInPositionList(List positions, int offset, bool orderedByOffset) {
+        if (positions.size() is 0)
+            return 0;
+
+        int left= 0;
+        int right= positions.size() -1;
+        int mid= 0;
+        Position p= null;
+
+        while (left < right) {
+
+            mid= (left + right) / 2;
+
+            p= (Position) positions.get(mid);
+            int pOffset= getOffset(orderedByOffset, p);
+            if (offset < pOffset) {
+                if (left is mid)
+                    right= left;
+                else
+                    right= mid -1;
+            } else if (offset > pOffset) {
+                if (right is mid)
+                    left= right;
+                else
+                    left= mid  +1;
+            } else if (offset is pOffset) {
+                left= right= mid;
+            }
+
+        }
+
+        int pos= left;
+        p= (Position) positions.get(pos);
+        int pPosition= getOffset(orderedByOffset, p);
+        if (offset > pPosition) {
+            // append to the end
+            pos++;
+        } else {
+            // entry will become the first of all entries with the same offset
+            do {
+                --pos;
+                if (pos < 0)
+                    break;
+                p= (Position) positions.get(pos);
+                pPosition= getOffset(orderedByOffset, p);
+            } while (offset is pPosition);
+            ++pos;
+        }
+
+        Assert.isTrue(0 <= pos && pos <= positions.size());
+
+        return pos;
+    }
+
+    /*
+     * @since 3.4
+     */
+    private int getOffset(bool orderedByOffset, Position position) {
+        if (orderedByOffset || position.getLength() is 0)
+            return position.getOffset();
+        return position.getOffset() + position.getLength() - 1;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#computeIndexInCategory(java.lang.String, int)
+     */
+    public int computeIndexInCategory(String category, int offset) throws BadLocationException, BadPositionCategoryException {
+
+        if (0 > offset || offset > getLength())
+            throw new BadLocationException();
+
+        List c= (List) fPositions.get(category);
+        if (c is null)
+            throw new BadPositionCategoryException();
+
+        return computeIndexInPositionList(c, offset);
+    }
+
+    /**
+     * Fires the document partitioning changed notification to all registered
+     * document partitioning listeners. Uses a robust iterator.
+     *
+     * @deprecated as of 2.0. Use <code>fireDocumentPartitioningChanged(IRegion)</code> instead.
+     */
+    protected void fireDocumentPartitioningChanged() {
+        if (fDocumentPartitioningListeners is null)
+            return;
+        
+        Object[] listeners= fDocumentPartitioningListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            ((IDocumentPartitioningListener)listeners[i]).documentPartitioningChanged(this);
+    }
+
+    /**
+     * Fires the document partitioning changed notification to all registered
+     * document partitioning listeners. Uses a robust iterator.
+     *
+     * @param region the region in which partitioning has changed
+     *
+     * @see IDocumentPartitioningListenerExtension
+     * @since 2.0
+     * @deprecated as of 3.0. Use
+     *             <code>fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent)</code>
+     *             instead.
+     */
+    protected void fireDocumentPartitioningChanged(IRegion region) {
+        if (fDocumentPartitioningListeners is null)
+            return;
+        
+        Object[] listeners= fDocumentPartitioningListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            IDocumentPartitioningListener l= (IDocumentPartitioningListener)listeners[i];
+            if (l instanceof IDocumentPartitioningListenerExtension)
+                ((IDocumentPartitioningListenerExtension) l).documentPartitioningChanged(this, region);
+            else
+                l.documentPartitioningChanged(this);
+        }
+    }
+
+    /**
+     * Fires the document partitioning changed notification to all registered
+     * document partitioning listeners. Uses a robust iterator.
+     *
+     * @param event the document partitioning changed event
+     *
+     * @see IDocumentPartitioningListenerExtension2
+     * @since 3.0
+     */
+    protected void fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent event) {
+        if (fDocumentPartitioningListeners is null)
+            return;
+
+        Object[] listeners= fDocumentPartitioningListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            IDocumentPartitioningListener l= (IDocumentPartitioningListener)listeners[i];
+            if (l instanceof IDocumentPartitioningListenerExtension2) {
+                IDocumentPartitioningListenerExtension2 extension2= (IDocumentPartitioningListenerExtension2) l;
+                extension2.documentPartitioningChanged(event);
+            } else if (l instanceof IDocumentPartitioningListenerExtension) {
+                IDocumentPartitioningListenerExtension extension= (IDocumentPartitioningListenerExtension) l;
+                extension.documentPartitioningChanged(this, event.getCoverage());
+            } else {
+                l.documentPartitioningChanged(this);
+            }
+        }
+    }
+
+    /**
+     * Fires the given document event to all registers document listeners informing them
+     * about the forthcoming document manipulation. Uses a robust iterator.
+     *
+     * @param event the event to be sent out
+     */
+    protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
+
+        // IDocumentExtension
+        if (fReentranceCount is 0)
+            flushPostNotificationChanges();
+
+        if (fDocumentPartitioners !is null) {
+            Iterator e= fDocumentPartitioners.values().iterator();
+            while (e.hasNext()) {
+                IDocumentPartitioner p= (IDocumentPartitioner) e.next();
+                if (p instanceof IDocumentPartitionerExtension3) {
+                    IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) p;
+                    if (extension.getActiveRewriteSession() !is null)
+                        continue;
+                }
+                p.documentAboutToBeChanged(event);
+            }
+        }
+
+        Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            ((IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
+        
+        listeners= fDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            ((IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
+    }
+
+
+    /**
+     * Updates document partitioning and document positions according to the
+     * specification given by the document event.
+     *
+     * @param event the document event describing the change to which structures must be adapted
+     */
+    protected void updateDocumentStructures(DocumentEvent event) {
+
+        if (fDocumentPartitioners !is null) {
+            fDocumentPartitioningChangedEvent= new DocumentPartitioningChangedEvent(this);
+            Iterator e= fDocumentPartitioners.keySet().iterator();
+            while (e.hasNext()) {
+                String partitioning= (String) e.next();
+                IDocumentPartitioner partitioner= (IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
+
+                if (partitioner instanceof IDocumentPartitionerExtension3) {
+                    IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
+                    if (extension.getActiveRewriteSession() !is null)
+                        continue;
+                }
+
+                if (partitioner instanceof IDocumentPartitionerExtension) {
+                    IDocumentPartitionerExtension extension= (IDocumentPartitionerExtension) partitioner;
+                    IRegion r= extension.documentChanged2(event);
+                    if (r !is null)
+                        fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, r.getOffset(), r.getLength());
+                } else {
+                    if (partitioner.documentChanged(event))
+                        fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, 0, event.getDocument().getLength());
+                }
+            }
+        }
+
+        if (fPositions.size() > 0)
+            updatePositions(event);
+    }
+
+    /**
+     * Notifies all listeners about the given document change. Uses a robust
+     * iterator.
+     * <p>
+     * Executes all registered post notification replace operation.
+     *
+     * @param event the event to be sent out.
+     */
+    protected void doFireDocumentChanged(DocumentEvent event) {
+        bool changed= fDocumentPartitioningChangedEvent !is null && !fDocumentPartitioningChangedEvent.isEmpty();
+        IRegion change= changed ? fDocumentPartitioningChangedEvent.getCoverage() : null;
+        doFireDocumentChanged(event, changed, change);
+    }
+
+    /**
+     * Notifies all listeners about the given document change.
+     * Uses a robust iterator. <p>
+     * Executes all registered post notification replace operation.
+     *
+     * @param event the event to be sent out
+     * @param firePartitionChange <code>true</code> if a partition change notification should be sent
+     * @param partitionChange the region whose partitioning changed
+     * @since 2.0
+     * @deprecated as of 3.0. Use <code>doFireDocumentChanged2(DocumentEvent)</code> instead; this method will be removed.
+     */
+    protected void doFireDocumentChanged(DocumentEvent event, bool firePartitionChange, IRegion partitionChange) {
+        doFireDocumentChanged2(event);
+    }
+
+    /**
+     * Notifies all listeners about the given document change. Uses a robust
+     * iterator.
+     * <p>
+     * Executes all registered post notification replace operation.
+     * <p>
+     * This method will be renamed to <code>doFireDocumentChanged</code>.
+     *
+     * @param event the event to be sent out
+     * @since 3.0
+     */
+    protected void doFireDocumentChanged2(DocumentEvent event) {
+
+        DocumentPartitioningChangedEvent p= fDocumentPartitioningChangedEvent;
+        fDocumentPartitioningChangedEvent= null;
+        if (p !is null && !p.isEmpty())
+            fireDocumentPartitioningChanged(p);
+
+        Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            ((IDocumentListener)listeners[i]).documentChanged(event);
+
+        listeners= fDocumentListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++)
+            ((IDocumentListener)listeners[i]).documentChanged(event);
+
+        // IDocumentExtension
+        ++ fReentranceCount;
+        try {
+            if (fReentranceCount is 1)
+                executePostNotificationChanges();
+        } finally {
+            -- fReentranceCount;
+        }
+    }
+
+    /**
+     * Updates the internal document structures and informs all document listeners
+     * if listener notification has been enabled. Otherwise it remembers the event
+     * to be sent to the listeners on resume.
+     *
+     * @param event the document event to be sent out
+     */
+    protected void fireDocumentChanged(DocumentEvent event) {
+        updateDocumentStructures(event);
+
+        if (fStoppedListenerNotification is 0)
+            doFireDocumentChanged(event);
+        else
+            fDeferredDocumentEvent= event;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getChar(int)
+     */
+    public char getChar(int pos) throws BadLocationException {
+        if ((0 > pos) || (pos >= getLength()))
+            throw new BadLocationException();
+        return getStore().get(pos);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getContentType(int)
+     */
+    public String getContentType(int offset) throws BadLocationException {
+        String contentType= null;
+        try {
+            contentType= getContentType(DEFAULT_PARTITIONING, offset, false);
+            Assert.isNotNull(contentType);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return contentType;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLegalContentTypes()
+     */
+    public String[] getLegalContentTypes() {
+        String[] contentTypes= null;
+        try {
+            contentTypes= getLegalContentTypes(DEFAULT_PARTITIONING);
+            Assert.isNotNull(contentTypes);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return contentTypes;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLength()
+     */
+    public int getLength() {
+        return getStore().getLength();
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLineDelimiter(int)
+     */
+    public String getLineDelimiter(int line) throws BadLocationException {
+        return getTracker().getLineDelimiter(line);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLegalLineDelimiters()
+     */
+    public String[] getLegalLineDelimiters() {
+        return getTracker().getLegalLineDelimiters();
+    }
+    
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
+     * @since 3.1
+     */
+    public String getDefaultLineDelimiter() {
+        
+        String lineDelimiter= null;
+        
+        try {
+            lineDelimiter= getLineDelimiter(0);
+        } catch (BadLocationException x) {
+        }
+    
+        if (lineDelimiter !is null)
+            return lineDelimiter;
+
+        if (fInitialLineDelimiter !is null)
+            return fInitialLineDelimiter;
+
+        String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
+        String[] delimiters= getLegalLineDelimiters();
+        Assert.isTrue(delimiters.length > 0);
+        for (int i= 0; i < delimiters.length; i++) {
+            if (delimiters[i].equals(sysLineDelimiter)) {
+                lineDelimiter= sysLineDelimiter;
+                break;
+            }
+        }
+        
+        if (lineDelimiter is null)
+            lineDelimiter= delimiters[0];
+    
+        return lineDelimiter;
+        
+    }
+    
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#setInitialLineDelimiter(java.lang.String)
+     * @since 3.1
+     */
+    public void setInitialLineDelimiter(String lineDelimiter) {
+        Assert.isNotNull(lineDelimiter);
+        fInitialLineDelimiter= lineDelimiter;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLineLength(int)
+     */
+    public int getLineLength(int line) throws BadLocationException {
+        return getTracker().getLineLength(line);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLineOfOffset(int)
+     */
+    public int getLineOfOffset(int pos) throws BadLocationException {
+        return getTracker().getLineNumberOfOffset(pos);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLineOffset(int)
+     */
+    public int getLineOffset(int line) throws BadLocationException {
+        return getTracker().getLineOffset(line);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLineInformation(int)
+     */
+    public IRegion getLineInformation(int line) throws BadLocationException {
+        return getTracker().getLineInformation(line);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getLineInformationOfOffset(int)
+     */
+    public IRegion getLineInformationOfOffset(int offset) throws BadLocationException {
+        return getTracker().getLineInformationOfOffset(offset);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getNumberOfLines()
+     */
+    public int getNumberOfLines() {
+        return getTracker().getNumberOfLines();
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getNumberOfLines(int, int)
+     */
+    public int getNumberOfLines(int offset, int length) throws BadLocationException {
+        return getTracker().getNumberOfLines(offset, length);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#computeNumberOfLines(java.lang.String)
+     */
+    public int computeNumberOfLines(String text) {
+        return getTracker().computeNumberOfLines(text);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getPartition(int)
+     */
+    public ITypedRegion getPartition(int offset) throws BadLocationException {
+        ITypedRegion partition= null;
+        try {
+            partition= getPartition(DEFAULT_PARTITIONING, offset, false);
+            Assert.isNotNull(partition);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return  partition;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#computePartitioning(int, int)
+     */
+    public ITypedRegion[] computePartitioning(int offset, int length) throws BadLocationException {
+        ITypedRegion[] partitioning= null;
+        try {
+            partitioning= computePartitioning(DEFAULT_PARTITIONING, offset, length, false);
+            Assert.isNotNull(partitioning);
+        } catch (BadPartitioningException e) {
+            Assert.isTrue(false);
+        }
+        return partitioning;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getPositions(java.lang.String)
+     */
+    public Position[] getPositions(String category) throws BadPositionCategoryException {
+
+        if (category is null)
+            throw new BadPositionCategoryException();
+
+        List c= (List) fPositions.get(category);
+        if (c is null)
+            throw new BadPositionCategoryException();
+
+        Position[] positions= new Position[c.size()];
+        c.toArray(positions);
+        return positions;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getPositionCategories()
+     */
+    public String[] getPositionCategories() {
+        String[] categories= new String[fPositions.size()];
+        Iterator keys= fPositions.keySet().iterator();
+        for (int i= 0; i < categories.length; i++)
+            categories[i]= (String) keys.next();
+        return categories;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#getPositionUpdaters()
+     */
+    public IPositionUpdater[] getPositionUpdaters() {
+        IPositionUpdater[] updaters= new IPositionUpdater[fPositionUpdaters.size()];
+        fPositionUpdaters.toArray(updaters);
+        return updaters;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#get()
+     */
+    public String get() {
+        return getStore().get(0, getLength());
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#get(int, int)
+     */
+    public String get(int pos, int length) throws BadLocationException {
+        int myLength= getLength();
+        if ((0 > pos) || (0 > length) || (pos + length > myLength))
+            throw new BadLocationException();
+        return getStore().get(pos, length);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#insertPositionUpdater(dwtx.jface.text.IPositionUpdater, int)
+     */
+    public void insertPositionUpdater(IPositionUpdater updater, int index) {
+
+        for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
+            if (fPositionUpdaters.get(i) is updater)
+                return;
+        }
+
+        if (index is fPositionUpdaters.size())
+            fPositionUpdaters.add(updater);
+        else
+            fPositionUpdaters.add(index, updater);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#removePosition(java.lang.String, dwtx.jface.text.Position)
+     */
+    public void removePosition(String category, Position position) throws BadPositionCategoryException {
+
+        if (position is null)
+            return;
+
+        if (category is null)
+            throw new BadPositionCategoryException();
+
+        List c= (List) fPositions.get(category);
+        if (c is null)
+            throw new BadPositionCategoryException();
+        removeFromPositionsList(c, position, true);
+        
+        List endPositions= (List) fEndPositions.get(category);
+        if (endPositions is null)
+            throw new BadPositionCategoryException();
+        removeFromPositionsList(endPositions, position, false);
+    }
+
+    /**
+     * Remove the given position form the given list of positions based on identity not equality.
+     *
+     * @param positions a list of positions
+     * @param position the position to remove
+     * @param orderedByOffset true if <code>positions</code> is ordered by offset, false if ordered by end position
+     * @since 3.4
+     */
+    private void removeFromPositionsList(List positions, Position position, bool orderedByOffset) {
+        int size= positions.size();
+        
+        //Assume position is somewhere near it was before
+        int index= computeIndexInPositionList(positions, orderedByOffset ? position.offset : position.offset + position.length - 1, orderedByOffset);
+        if (index < size && positions.get(index) is position) {
+            positions.remove(index);
+            return;
+        }
+        
+        int back= index - 1;
+        int forth= index + 1;
+        while (back >= 0 || forth < size) {
+            if (back >= 0) {
+                if (position is positions.get(back)) {
+                    positions.remove(back);
+                    return;
+                }
+                back--;
+            }
+            
+            if (forth < size) {
+                if (position is positions.get(forth)) {
+                    positions.remove(forth);
+                    return;
+                }
+                forth++;
+            }
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#removePosition(dwtx.jface.text.Position)
+     */
+    public void removePosition(Position position) {
+        try {
+            removePosition(DEFAULT_CATEGORY, position);
+        } catch (BadPositionCategoryException e) {
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#removePositionCategory(java.lang.String)
+     */
+    public void removePositionCategory(String category) throws BadPositionCategoryException {
+
+        if (category is null)
+            return;
+
+        if ( !containsPositionCategory(category))
+            throw new BadPositionCategoryException();
+
+        fPositions.remove(category);
+        fEndPositions.remove(category);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#removePositionUpdater(dwtx.jface.text.IPositionUpdater)
+     */
+    public void removePositionUpdater(IPositionUpdater updater) {
+        for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
+            if (fPositionUpdaters.get(i) is updater) {
+                fPositionUpdaters.remove(i);
+                return;
+            }
+        }
+    }
+
+    private long getNextModificationStamp() {
+        if (fNextModificationStamp is Long.MAX_VALUE || fNextModificationStamp is IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
+            fNextModificationStamp= 0;
+        else
+            fNextModificationStamp= fNextModificationStamp + 1;
+
+        return fNextModificationStamp;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#getModificationStamp()
+     * @since 3.1
+     */
+    public long getModificationStamp() {
+        return fModificationStamp;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#replace(int, int, java.lang.String)
+     * @since 3.1
+     */
+    public void replace(int pos, int length, String text, long modificationStamp) throws BadLocationException {
+        if ((0 > pos) || (0 > length) || (pos + length > getLength()))
+            throw new BadLocationException();
+
+        DocumentEvent e= new DocumentEvent(this, pos, length, text);
+        fireDocumentAboutToBeChanged(e);
+
+        getStore().replace(pos, length, text);
+        getTracker().replace(pos, length, text);
+
+        fModificationStamp= modificationStamp;
+        fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
+        e.fModificationStamp= fModificationStamp;
+
+        fireDocumentChanged(e);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @since 3.4
+     */
+    public bool isLineInformationRepairNeeded(int offset, int length, String text) throws BadLocationException {
+        return false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#replace(int, int, java.lang.String)
+     */
+    public void replace(int pos, int length, String text) throws BadLocationException {
+        if (length is 0 && (text is null || text.length() is 0))
+            replace(pos, length, text, getModificationStamp());
+        else
+            replace(pos, length, text, getNextModificationStamp());
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#set(java.lang.String)
+     */
+    public void set(String text) {
+        set(text, getNextModificationStamp());
+    }
+    
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#set(java.lang.String, long)
+     * @since 3.1
+     */
+    public void set(String text, long modificationStamp) {
+        int length= getStore().getLength();
+
+        DocumentEvent e= new DocumentEvent(this, 0, length, text);
+        fireDocumentAboutToBeChanged(e);
+
+        getStore().set(text);
+        getTracker().set(text);
+
+        fModificationStamp= modificationStamp;
+        fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
+        e.fModificationStamp= fModificationStamp;
+
+        fireDocumentChanged(e);
+    }
+
+    /**
+     * Updates all positions of all categories to the change described by the
+     * document event. All registered document updaters are called in the
+     * sequence they have been arranged. Uses a robust iterator.
+     *
+     * @param event the document event describing the change to which to adapt
+     *            the positions
+     */
+    protected void updatePositions(DocumentEvent event) {
+        List list= new ArrayList(fPositionUpdaters);
+        Iterator e= list.iterator();
+        while (e.hasNext()) {
+            IPositionUpdater u= (IPositionUpdater) e.next();
+            u.update(event);
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocument#search(int, java.lang.String, bool, bool, bool)
+     */
+    public int search(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord) throws BadLocationException {
+        try {
+            IRegion region= getFindReplaceDocumentAdapter().find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
+            return region is null ?  -1 : region.getOffset();
+        } catch (IllegalStateException ex) {
+            return -1;
+        } catch (PatternSyntaxException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the find/replace adapter for this document.
+     *
+     * @return this document's find/replace document adapter
+     * @since 3.0
+     */
+    private FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
+        if (fFindReplaceDocumentAdapter is null)
+            fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(this);
+
+        return fFindReplaceDocumentAdapter;
+    }
+
+    /**
+     * Flushes all registered post notification changes.
+     *
+     * @since 2.0
+     */
+    private void flushPostNotificationChanges() {
+        if (fPostNotificationChanges !is null)
+            fPostNotificationChanges.clear();
+    }
+
+    /**
+     * Executes all registered post notification changes. The process is
+     * repeated until no new post notification changes are added.
+     *
+     * @since 2.0
+     */
+    private void executePostNotificationChanges() {
+
+        if (fStoppedCount > 0)
+            return;
+
+        while (fPostNotificationChanges !is null) {
+            List changes= fPostNotificationChanges;
+            fPostNotificationChanges= null;
+
+            Iterator e= changes.iterator();
+            while (e.hasNext()) {
+                RegisteredReplace replace= (RegisteredReplace) e.next();
+                replace.fReplace.perform(this, replace.fOwner);
+            }
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension2#acceptPostNotificationReplaces()
+     * @since 2.1
+     */
+    public void acceptPostNotificationReplaces() {
+        fAcceptPostNotificationReplaces= true;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension2#ignorePostNotificationReplaces()
+     * @since 2.1
+     */
+    public void ignorePostNotificationReplaces() {
+        fAcceptPostNotificationReplaces= false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension#registerPostNotificationReplace(dwtx.jface.text.IDocumentListener, dwtx.jface.text.IDocumentExtension.IReplace)
+     * @since 2.0
+     */
+    public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+        if (fAcceptPostNotificationReplaces) {
+            if (fPostNotificationChanges is null)
+                fPostNotificationChanges= new ArrayList(1);
+            fPostNotificationChanges.add(new RegisteredReplace(owner, replace));
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension#stopPostNotificationProcessing()
+     * @since 2.0
+     */
+    public void stopPostNotificationProcessing() {
+        ++ fStoppedCount;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension#resumePostNotificationProcessing()
+     * @since 2.0
+     */
+    public void resumePostNotificationProcessing() {
+        -- fStoppedCount;
+        if (fStoppedCount is 0 && fReentranceCount is 0)
+            executePostNotificationChanges();
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension#startSequentialRewrite(bool)
+     * @since 2.0
+     */
+    public void startSequentialRewrite(bool normalized) {
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension#stopSequentialRewrite()
+     * @since 2.0
+     */
+    public void stopSequentialRewrite() {
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension2#resumeListenerNotification()
+     * @since 2.1
+     */
+    public void resumeListenerNotification() {
+        -- fStoppedListenerNotification;
+        if (fStoppedListenerNotification is 0) {
+            resumeDocumentListenerNotification();
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension2#stopListenerNotification()
+     * @since 2.1
+     */
+    public void stopListenerNotification() {
+        ++ fStoppedListenerNotification;
+    }
+
+    /**
+     * Resumes the document listener notification by sending out the remembered
+     * partition changed and document event.
+     *
+     * @since 2.1
+     */
+    private void resumeDocumentListenerNotification() {
+        if (fDeferredDocumentEvent !is null) {
+            DocumentEvent event= fDeferredDocumentEvent;
+            fDeferredDocumentEvent= null;
+            doFireDocumentChanged(event);
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension3#computeZeroLengthPartitioning(java.lang.String, int, int)
+     * @since 3.0
+     */
+    public ITypedRegion[] computePartitioning(String partitioning, int offset, int length, bool includeZeroLengthPartitions) throws BadLocationException, BadPartitioningException {
+        if ((0 > offset) || (0 > length) || (offset + length > getLength()))
+            throw new BadLocationException();
+
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+
+        if (partitioner instanceof IDocumentPartitionerExtension2) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return ((IDocumentPartitionerExtension2) partitioner).computePartitioning(offset, length, includeZeroLengthPartitions);
+        } else if (partitioner !is null) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return partitioner.computePartitioning(offset, length);
+        } else if (DEFAULT_PARTITIONING.equals(partitioning))
+            return new TypedRegion[] { new TypedRegion(offset, length, DEFAULT_CONTENT_TYPE) };
+        else
+            throw new BadPartitioningException();
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension3#getZeroLengthContentType(java.lang.String, int)
+     * @since 3.0
+     */
+    public String getContentType(String partitioning, int offset, bool preferOpenPartitions) throws BadLocationException, BadPartitioningException {
+        if ((0 > offset) || (offset > getLength()))
+            throw new BadLocationException();
+
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+
+        if (partitioner instanceof IDocumentPartitionerExtension2) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return ((IDocumentPartitionerExtension2) partitioner).getContentType(offset, preferOpenPartitions);
+        } else if (partitioner !is null) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return partitioner.getContentType(offset);
+        } else if (DEFAULT_PARTITIONING.equals(partitioning))
+            return DEFAULT_CONTENT_TYPE;
+        else
+            throw new BadPartitioningException();
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension3#getDocumentPartitioner(java.lang.String)
+     * @since 3.0
+     */
+    public IDocumentPartitioner getDocumentPartitioner(String partitioning)  {
+        return fDocumentPartitioners !is null ? (IDocumentPartitioner) fDocumentPartitioners.get(partitioning) : null;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension3#getLegalContentTypes(java.lang.String)
+     * @since 3.0
+     */
+    public String[] getLegalContentTypes(String partitioning) throws BadPartitioningException {
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+        if (partitioner !is null)
+            return partitioner.getLegalContentTypes();
+        if (DEFAULT_PARTITIONING.equals(partitioning))
+            return new String[] { DEFAULT_CONTENT_TYPE };
+        throw new BadPartitioningException();
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension3#getZeroLengthPartition(java.lang.String, int)
+     * @since 3.0
+     */
+    public ITypedRegion getPartition(String partitioning, int offset, bool preferOpenPartitions) throws BadLocationException, BadPartitioningException {
+        if ((0 > offset) || (offset > getLength()))
+            throw new BadLocationException();
+
+        IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
+
+        if (partitioner instanceof IDocumentPartitionerExtension2) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return ((IDocumentPartitionerExtension2) partitioner).getPartition(offset, preferOpenPartitions);
+        } else if (partitioner !is null) {
+            checkStateOfPartitioner(partitioner, partitioning);
+            return partitioner.getPartition(offset);
+        } else if (DEFAULT_PARTITIONING.equals(partitioning))
+            return new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE);
+        else
+            throw new BadPartitioningException();
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension3#getPartitionings()
+     * @since 3.0
+     */
+    public String[] getPartitionings() {
+        if (fDocumentPartitioners is null)
+            return new String[0];
+        String[] partitionings= new String[fDocumentPartitioners.size()];
+        fDocumentPartitioners.keySet().toArray(partitionings);
+        return partitionings;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension3#setDocumentPartitioner(java.lang.String, dwtx.jface.text.IDocumentPartitioner)
+     * @since 3.0
+     */
+    public void setDocumentPartitioner(String partitioning, IDocumentPartitioner partitioner) {
+        if (partitioner is null) {
+            if (fDocumentPartitioners !is null) {
+                fDocumentPartitioners.remove(partitioning);
+                if (fDocumentPartitioners.size() is 0)
+                    fDocumentPartitioners= null;
+            }
+        } else {
+            if (fDocumentPartitioners is null)
+                fDocumentPartitioners= new HashMap();
+            fDocumentPartitioners.put(partitioning, partitioner);
+        }
+        DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
+        event.setPartitionChange(partitioning, 0, getLength());
+        fireDocumentPartitioningChanged(event);
+    }
+
+    /*
+     * @see dwtx.jface.text.IRepairableDocument#repairLineInformation()
+     * @since 3.0
+     */
+    public void repairLineInformation() {
+        getTracker().set(get());
+    }
+
+    /**
+     * Fires the given event to all registered rewrite session listeners. Uses robust iterators.
+     *
+     * @param event the event to be fired
+     * @since 3.1
+     */
+    protected void fireRewriteSessionChanged(DocumentRewriteSessionEvent event) {
+        if (fDocumentRewriteSessionListeners.size() > 0) {
+            List list= new ArrayList(fDocumentRewriteSessionListeners);
+            Iterator e= list.iterator();
+            while (e.hasNext()) {
+                IDocumentRewriteSessionListener l= (IDocumentRewriteSessionListener) e.next();
+                l.documentRewriteSessionChanged(event);
+            }
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#getActiveRewriteSession()
+     */
+    public final DocumentRewriteSession getActiveRewriteSession() {
+        return fDocumentRewriteSession;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#startRewriteSession(dwtx.jface.text.DocumentRewriteSessionType)
+     * @since 3.1
+     */
+    public DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) {
+
+        if (getActiveRewriteSession() !is null)
+            throw new IllegalStateException();
+
+
+        fDocumentRewriteSession= new DocumentRewriteSession(sessionType);
+        if (DEBUG)
+            System.out.println("AbstractDocument: Starting rewrite session: " + fDocumentRewriteSession); //$NON-NLS-1$
+
+        fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, fDocumentRewriteSession, DocumentRewriteSessionEvent.SESSION_START));
+
+        startRewriteSessionOnPartitioners(fDocumentRewriteSession);
+
+        ILineTracker tracker= getTracker();
+        if (tracker instanceof ILineTrackerExtension) {
+            ILineTrackerExtension extension= (ILineTrackerExtension) tracker;
+            extension.startRewriteSession(fDocumentRewriteSession);
+        }
+
+        if (DocumentRewriteSessionType.SEQUENTIAL is sessionType)
+            startSequentialRewrite(false);
+        else if (DocumentRewriteSessionType.STRICTLY_SEQUENTIAL is sessionType)
+            startSequentialRewrite(true);
+
+        return fDocumentRewriteSession;
+    }
+
+    /**
+     * Starts the given rewrite session.
+     *
+     * @param session the rewrite session
+     * @since 3.1
+     */
+    protected final void startRewriteSessionOnPartitioners(DocumentRewriteSession session) {
+        if (fDocumentPartitioners !is null) {
+            Iterator e= fDocumentPartitioners.values().iterator();
+            while (e.hasNext()) {
+                Object partitioner= e.next();
+                if (partitioner instanceof IDocumentPartitionerExtension3) {
+                    IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
+                    extension.startRewriteSession(session);
+                }
+            }
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#stopRewriteSession(dwtx.jface.text.DocumentRewriteSession)
+     * @since 3.1
+     */
+    public void stopRewriteSession(DocumentRewriteSession session) {
+        if (fDocumentRewriteSession is session) {
+
+            if (DEBUG)
+                System.out.println("AbstractDocument: Stopping rewrite session: " + session); //$NON-NLS-1$
+
+            DocumentRewriteSessionType sessionType= session.getSessionType();
+            if (DocumentRewriteSessionType.SEQUENTIAL is sessionType || DocumentRewriteSessionType.STRICTLY_SEQUENTIAL is sessionType)
+                stopSequentialRewrite();
+
+            ILineTracker tracker= getTracker();
+            if (tracker instanceof ILineTrackerExtension) {
+                ILineTrackerExtension extension= (ILineTrackerExtension) tracker;
+                extension.stopRewriteSession(session, get());
+            }
+
+            stopRewriteSessionOnPartitioners(fDocumentRewriteSession);
+
+            fDocumentRewriteSession= null;
+            fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_STOP));
+        }
+    }
+
+    /**
+     * Stops the given rewrite session.
+     *
+     * @param session the rewrite session
+     * @since 3.1
+     */
+    protected final void stopRewriteSessionOnPartitioners(DocumentRewriteSession session) {
+        if (fDocumentPartitioners !is null) {
+            DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
+            Iterator e= fDocumentPartitioners.keySet().iterator();
+            while (e.hasNext()) {
+                String partitioning= (String) e.next();
+                IDocumentPartitioner partitioner= (IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
+                if (partitioner instanceof IDocumentPartitionerExtension3) {
+                    IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
+                    extension.stopRewriteSession(session);
+                    event.setPartitionChange(partitioning, 0, getLength());
+                }
+            }
+            if (!event.isEmpty())
+                fireDocumentPartitioningChanged(event);
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#addDocumentRewriteSessionListener(dwtx.jface.text.IDocumentRewriteSessionListener)
+     * @since 3.1
+     */
+    public void addDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
+        Assert.isNotNull(listener);
+        if (! fDocumentRewriteSessionListeners.contains(listener))
+            fDocumentRewriteSessionListeners.add(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentExtension4#removeDocumentRewriteSessionListener(dwtx.jface.text.IDocumentRewriteSessionListener)
+     * @since 3.1
+     */
+    public void removeDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
+        Assert.isNotNull(listener);
+        fDocumentRewriteSessionListeners.remove(listener);
+    }
+
+    /**
+     * Checks the state for the given partitioner and stops the
+     * active rewrite session.
+     *
+     * @param partitioner the document partitioner to be checked
+     * @param partitioning the document partitioning the partitioner is registered for
+     * @since 3.1
+     */
+    protected final void checkStateOfPartitioner(IDocumentPartitioner partitioner, String partitioning) {
+        if (!(partitioner instanceof IDocumentPartitionerExtension3))
+            return;
+
+        IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
+        DocumentRewriteSession session= extension.getActiveRewriteSession();
+        if (session !is null) {
+            extension.stopRewriteSession(session);
+
+            if (DEBUG)
+                System.out.println("AbstractDocument: Flushing rewrite session for partition type: " + partitioning); //$NON-NLS-1$
+
+            DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
+            event.setPartitionChange(partitioning, 0, getLength());
+            fireDocumentPartitioningChanged(event);
+        }
+    }
+    
+    /**
+     * Returns all positions of the given category that are inside the given region.
+     * 
+     * @param category the position category
+     * @param offset the start position of the region, must be >= 0
+     * @param length the length of the region, must be >= 0
+     * @param canStartBefore if <code>true</code> then positions are included
+     *            which start before the region if they end at or after the regions start
+     * @param canEndAfter if <code>true</code> then positions are included
+     *            which end after the region if they start at or before the regions end
+     * @return all positions inside the region of the given category
+     * @throws BadPositionCategoryException if category is undefined in this document
+     * @since 3.4
+     */
+    public Position[] getPositions(String category, int offset, int length, bool canStartBefore, bool canEndAfter) throws BadPositionCategoryException {
+        if (canStartBefore && canEndAfter || (!canStartBefore && !canEndAfter)) {
+            List documentPositions;
+            if (canStartBefore && canEndAfter) {
+                if (offset < getLength() / 2) {
+                    documentPositions= getStartingPositions(category, 0, offset + length);
+                } else {
+                    documentPositions= getEndingPositions(category, offset, getLength() - offset + 1);
+                }
+            } else {
+                documentPositions= getStartingPositions(category, offset, length);
+            }
+            
+            ArrayList list= new ArrayList(documentPositions.size());
+            
+            Position region= new Position(offset, length);
+            
+            for (Iterator iterator= documentPositions.iterator(); iterator.hasNext();) {
+                Position position= (Position) iterator.next();
+                if (isWithinRegion(region, position, canStartBefore, canEndAfter)) {
+                    list.add(position);
+                }
+            }
+            
+            Position[] positions= new Position[list.size()];
+            list.toArray(positions);
+            return positions;
+        } else if (canStartBefore) {
+            List list= getEndingPositions(category, offset, length);
+            Position[] positions= new Position[list.size()];
+            list.toArray(positions);
+            return positions;
+        } else {
+            Assert.isLegal(canEndAfter && !canStartBefore);
+            
+            List list= getStartingPositions(category, offset, length);
+            Position[] positions= new Position[list.size()];
+            list.toArray(positions);
+            return positions;
+        }
+    }
+    
+    /*
+     * @since 3.4
+     */
+    private bool isWithinRegion(Position region, Position position, bool canStartBefore, bool canEndAfter) {
+        if (canStartBefore && canEndAfter) {
+            return region.overlapsWith(position.getOffset(), position.getLength());
+        } else if (canStartBefore) {
+            return region.includes(position.getOffset() + position.getLength() - 1);
+        } else if (canEndAfter) {
+            return region.includes(position.getOffset());
+        } else {
+            int start= position.getOffset();
+            return region.includes(start) && region.includes(start + position.getLength() - 1);
+        }
+    }
+    
+    /**
+     * A list of positions in the given category with an offset inside the given
+     * region. The order of the positions is arbitrary.
+     * 
+     * @param category the position category
+     * @param offset the offset of the region
+     * @param length the length of the region
+     * @return a list of the positions in the region
+     * @throws BadPositionCategoryException if category is undefined in this document
+     * @since 3.4
+     */
+    private List getStartingPositions(String category, int offset, int length) throws BadPositionCategoryException {
+        List positions= (List) fPositions.get(category);
+        if (positions is null)
+            throw new BadPositionCategoryException();
+        
+        int indexStart= computeIndexInPositionList(positions, offset, true);
+        int indexEnd= computeIndexInPositionList(positions, offset + length, true);
+        
+        return positions.subList(indexStart, indexEnd);
+    }
+
+    /**
+     * A list of positions in the given category with an end position inside
+     * the given region. The order of the positions is arbitrary.
+     * 
+     * @param category the position category
+     * @param offset the offset of the region
+     * @param length the length of the region
+     * @return a list of the positions in the region
+     * @throws BadPositionCategoryException if category is undefined in this document
+     * @since 3.4
+     */
+    private List getEndingPositions(String category, int offset, int length) throws BadPositionCategoryException {
+        List positions= (List) fEndPositions.get(category);
+        if (positions is null)
+            throw new BadPositionCategoryException();
+        
+        int indexStart= computeIndexInPositionList(positions, offset, false);
+        int indexEnd= computeIndexInPositionList(positions, offset + length, false);
+        
+        return positions.subList(indexStart, indexEnd);
+    }
+
+
+}