diff org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children 5feec68b4556
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.text/src/org/eclipse/jface/text/source/AnnotationModel.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,1064 @@
+/*******************************************************************************
+ * 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 org.eclipse.jface.text.source.AnnotationModel;
+
+import org.eclipse.jface.text.source.ISharedTextColors; // packageimport
+import org.eclipse.jface.text.source.ILineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport
+import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.TextInvocationContext; // packageimport
+import org.eclipse.jface.text.source.LineChangeHover; // packageimport
+import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport
+import org.eclipse.jface.text.source.LineNumberRulerColumn; // packageimport
+import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport
+import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport
+import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport
+import org.eclipse.jface.text.source.ISourceViewer; // packageimport
+import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport
+import org.eclipse.jface.text.source.IVerticalRuler; // packageimport
+import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport
+import org.eclipse.jface.text.source.SourceViewer; // packageimport
+import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport
+import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport
+import org.eclipse.jface.text.source.CompositeRuler; // packageimport
+import org.eclipse.jface.text.source.ImageUtilities; // packageimport
+import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModel; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport
+import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport
+import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport
+import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ILineDiffer; // packageimport
+import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport
+import org.eclipse.jface.text.source.AnnotationColumn; // packageimport
+import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport
+import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport
+import org.eclipse.jface.text.source.AnnotationMap; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport
+import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport
+import org.eclipse.jface.text.source.LineRange; // packageimport
+import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport
+import org.eclipse.jface.text.source.VerticalRuler; // packageimport
+import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport
+import org.eclipse.jface.text.source.IOverviewRuler; // packageimport
+import org.eclipse.jface.text.source.Annotation; // packageimport
+import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport
+import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport
+import org.eclipse.jface.text.source.AnnotationPainter; // packageimport
+import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport
+import org.eclipse.jface.text.source.OverviewRuler; // packageimport
+import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport
+
+import java.lang.all;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import tango.core.Exception;
+import java.lang.JThread;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.AbstractDocument;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.ISynchronizable;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Standard implementation of {@link IAnnotationModel} and its extension
+ * interfaces. This class can directly be used by clients. Subclasses may adapt
+ * this annotation model to other existing annotation mechanisms. This class
+ * also implements {@link org.eclipse.jface.text.ISynchronizable}. All
+ * modifications of the model's internal annotation map are synchronized using
+ * the model's lock object.
+ */
+public class AnnotationModel : IAnnotationModel, IAnnotationModelExtension, IAnnotationModelExtension2, ISynchronizable {
+
+
+    /**
+     * Iterator that returns the annotations for a given region.
+     *
+     * @since 3.4
+     * @see AnnotationModel.RegionIterator#RegionIterator(Iterator, IAnnotationModel, int, int, bool, bool)
+     */
+    private static final class RegionIterator : Iterator {
+
+        private const Iterator fParentIterator;
+        private const bool fCanEndAfter;
+        private const bool fCanStartBefore;
+        private const IAnnotationModel fModel;
+        private Object fNext;
+        private Position fRegion;
+
+        /**
+         * Iterator that returns all annotations from the parent iterator which
+         * have a position in the given model inside the given region.
+         * <p>
+         * See {@link IAnnotationModelExtension2} for a definition of inside.
+         * </p>
+         *
+         * @param parentIterator iterator containing all annotations
+         * @param model the model to use to retrieve positions from for each
+         *            annotation
+         * @param offset start position of the region
+         * @param length length of the region
+         * @param canStartBefore include annotations starting before region
+         * @param canEndAfter include annotations ending after region
+         * @see IAnnotationModelExtension2
+         */
+        public this(Iterator parentIterator, IAnnotationModel model, int offset, int length, bool canStartBefore, bool canEndAfter) {
+            fParentIterator= parentIterator;
+            fModel= model;
+            fRegion= new Position(offset, length);
+            fCanEndAfter= canEndAfter;
+            fCanStartBefore= canStartBefore;
+            fNext= findNext();
+        }
+
+        /*
+         * @see java.util.Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fNext !is null;
+        }
+
+        /*
+         * @see java.util.Iterator#next()
+         */
+        public Object next() {
+            if (!hasNext())
+                throw new NoSuchElementException(null);
+
+            Object result= fNext;
+            fNext= findNext();
+            return result;
+        }
+
+        /*
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Object findNext() {
+            while (fParentIterator.hasNext()) {
+                Annotation next= cast(Annotation) fParentIterator.next();
+                Position position= fModel.getPosition(next);
+                if (position !is null) {
+                    int offset= position.getOffset();
+                    if (isWithinRegion(offset, position.getLength()))
+                        return next;
+                }
+            }
+            return null;
+        }
+
+        private bool isWithinRegion(int start, int length) {
+            if (fCanStartBefore && fCanEndAfter)
+                return fRegion.overlapsWith(start, length);
+            else if (fCanStartBefore)
+                return fRegion.includes(start + length - 1);
+            else if (fCanEndAfter)
+                return fRegion.includes(start);
+            else
+                return fRegion.includes(start) && fRegion.includes(start + length - 1);
+        }
+    }
+
+    /**
+     * An iterator iteration over a Positions and mapping positions to
+     * annotations using a provided map if the provided map contains the element.
+     *
+     * @since 3.4
+     */
+    private static final class AnnotationsInterator : Iterator {
+
+        private Object fNext;
+        private const Position[] fPositions;
+        private int fIndex;
+        private const Map fMap;
+
+        /**
+         * @param positions positions to iterate over
+         * @param map a map to map positions to annotations
+         */
+        public this(Position[] positions, Map map) {
+            fPositions= positions;
+            fIndex= 0;
+            fMap= map;
+            fNext= findNext();
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.Iterator#hasNext()
+         */
+        public bool hasNext() {
+            return fNext !is null;
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.Iterator#next()
+         */
+        public Object next() {
+            Object result= fNext;
+            fNext= findNext();
+            return result;
+        }
+
+        /* (non-Javadoc)
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Object findNext() {
+            while (fIndex < fPositions.length) {
+                Position position= fPositions[fIndex];
+                fIndex++;
+                if (fMap.containsKey(position))
+                    return fMap.get(position);
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * A single iterator builds its behavior based on a sequence of iterators.
+     *
+     * @since 3.1
+     */
+    private static class MetaIterator : Iterator {
+
+        /** The iterator over a list of iterators. */
+        private Iterator fSuperIterator;
+        /** The current iterator. */
+        private Iterator fCurrent;
+        /** The current element. */
+        private Object fCurrentElement;
+
+
+        public this(Iterator iterator) {
+            fSuperIterator= iterator;
+            fCurrent= cast(Iterator) fSuperIterator.next(); // there is at least one.
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        public bool hasNext() {
+            if (fCurrentElement !is null)
+                return true;
+
+            if (fCurrent.hasNext()) {
+                fCurrentElement= fCurrent.next();
+                return true;
+            } else if (fSuperIterator.hasNext()) {
+                fCurrent= cast(Iterator) fSuperIterator.next();
+                return hasNext();
+            } else
+                return false;
+        }
+
+        public Object next() {
+            if (!hasNext())
+                throw new NoSuchElementException(null);
+
+            Object element= fCurrentElement;
+            fCurrentElement= null;
+            return element;
+        }
+    }
+
+    /**
+     * Internal annotation model listener for forwarding annotation model changes from the attached models to the
+     * registered listeners of the outer most annotation model.
+     *
+     * @since 3.0
+     */
+    private class InternalModelListener : IAnnotationModelListener, IAnnotationModelListenerExtension {
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
+         */
+        public void modelChanged(IAnnotationModel model) {
+            this.outer.fireModelChanged(new AnnotationModelEvent(model, true));
+        }
+
+        /*
+         * @see org.eclipse.jface.text.source.IAnnotationModelListenerExtension#modelChanged(org.eclipse.jface.text.source.AnnotationModelEvent)
+         */
+        public void modelChanged(AnnotationModelEvent event) {
+            this.outer.fireModelChanged(event);
+        }
+    }
+
+    /**
+     * The list of managed annotations
+     * @deprecated since 3.0 use <code>getAnnotationMap</code> instead
+     */
+    protected Map fAnnotations;
+    /**
+     * The map which maps {@link Position} to {@link Annotation}.
+     * @since 3.4
+     **/
+    private IdentityHashMap fPositions;
+    /** The list of annotation model listeners */
+    protected ArrayList fAnnotationModelListeners;
+    /** The document connected with this model */
+    protected IDocument fDocument;
+    /** The number of open connections to the same document */
+    private int fOpenConnections= 0;
+    /** The document listener for tracking whether document positions might have been changed. */
+    private IDocumentListener fDocumentListener;
+    /** The flag indicating whether the document positions might have been changed. */
+    private bool fDocumentChanged= true;
+    /**
+     * The model's attachment.
+     * @since 3.0
+     */
+    private Map fAttachments;
+    /**
+     * The annotation model listener on attached sub-models.
+     * @since 3.0
+     */
+    private IAnnotationModelListener fModelListener;
+    /**
+     * The current annotation model event.
+     * @since 3.0
+     */
+    private AnnotationModelEvent fModelEvent;
+    /**
+     * The modification stamp.
+     * @since 3.0
+     */
+    private Object fModificationStamp;
+    /**
+     * Creates a new annotation model. The annotation is empty, i.e. does not
+     * manage any annotations and is not connected to any document.
+     */
+    public this() {
+        fAttachments= new HashMap();
+        fModelListener= new InternalModelListener();
+        fModificationStamp= new Object();
+        fAnnotations= new AnnotationMap(10);
+        fPositions= new IdentityHashMap(10);
+        fAnnotationModelListeners= new ArrayList(2);
+
+        fDocumentListener= new class()  IDocumentListener {
+
+            public void documentAboutToBeChanged(DocumentEvent event) {
+            }
+
+            public void documentChanged(DocumentEvent event) {
+                fDocumentChanged= true;
+            }
+        };
+    }
+
+    /**
+     * Returns the annotation map internally used by this annotation model.
+     *
+     * @return the annotation map internally used by this annotation model
+     * @since 3.0
+     */
+    protected IAnnotationMap getAnnotationMap() {
+        return cast(IAnnotationMap) fAnnotations;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISynchronizable#getLockObject()
+     * @since 3.0
+     */
+    public Object getLockObject() {
+        return getAnnotationMap().getLockObject();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.ISynchronizable#setLockObject(java.lang.Object)
+     * @since 3.0
+     */
+    public void setLockObject(Object lockObject) {
+        getAnnotationMap().setLockObject(lockObject);
+    }
+
+    /**
+     * Returns the current annotation model event. This is the event that will be sent out
+     * when calling <code>fireModelChanged</code>.
+     *
+     * @return the current annotation model event
+     * @since 3.0
+     */
+    protected final AnnotationModelEvent getAnnotationModelEvent() {
+        synchronized (getLockObject()) {
+            if (fModelEvent is null) {
+                fModelEvent= createAnnotationModelEvent();
+                fModelEvent.markWorldChange(false);
+                fModificationStamp= fModelEvent;
+            }
+            return fModelEvent;
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#addAnnotation(org.eclipse.jface.text.source.Annotation, org.eclipse.jface.text.Position)
+     */
+    public void addAnnotation(Annotation annotation, Position position) {
+        try {
+            addAnnotation(annotation, position, true);
+        } catch (BadLocationException e) {
+            // ignore invalid position
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#replaceAnnotations(org.eclipse.jface.text.source.Annotation[], java.util.Map)
+     * @since 3.0
+     */
+    public void replaceAnnotations(Annotation[] annotationsToRemove, Map annotationsToAdd) {
+        try {
+            replaceAnnotations(annotationsToRemove, annotationsToAdd, true);
+        } catch (BadLocationException x) {
+        }
+    }
+
+    /**
+     * Replaces the given annotations in this model and if advised fires a
+     * model change event.
+     *
+     * @param annotationsToRemove the annotations to be removed
+     * @param annotationsToAdd the annotations to be added
+     * @param fireModelChanged <code>true</code> if a model change event
+     *            should be fired, <code>false</code> otherwise
+     * @throws BadLocationException in case an annotation should be added at an
+     *             invalid position
+     * @since 3.0
+     */
+    protected void replaceAnnotations(Annotation[] annotationsToRemove, Map annotationsToAdd, bool fireModelChanged_)  {
+
+        if (annotationsToRemove !is null) {
+            for (int i= 0, length= annotationsToRemove.length; i < length; i++)
+                removeAnnotation(annotationsToRemove[i], false);
+        }
+
+        if (annotationsToAdd !is null) {
+            Iterator iter= annotationsToAdd.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry mapEntry= cast(Map.Entry) iter.next();
+                Annotation annotation= cast(Annotation) mapEntry.getKey();
+                Position position= cast(Position) mapEntry.getValue();
+                addAnnotation(annotation, position, false);
+            }
+        }
+
+        if (fireModelChanged_)
+            fireModelChanged();
+    }
+
+    /**
+     * Adds the given annotation to this model. Associates the
+     * annotation with the given position. If requested, all annotation
+     * model listeners are informed about this model change. If the annotation
+     * is already managed by this model nothing happens.
+     *
+     * @param annotation the annotation to add
+     * @param position the associate position
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @throws BadLocationException if the position is not a valid document position
+     */
+    protected void addAnnotation(Annotation annotation, Position position, bool fireModelChanged_)  {
+        if (!fAnnotations.containsKey(annotation)) {
+
+            addPosition(fDocument, position);
+            fAnnotations.put(annotation, position);
+            fPositions.put(position, annotation);
+            synchronized (getLockObject()) {
+                getAnnotationModelEvent().annotationAdded(annotation);
+            }
+
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#addAnnotationModelListener(org.eclipse.jface.text.source.IAnnotationModelListener)
+     */
+    public void addAnnotationModelListener(IAnnotationModelListener listener) {
+        if (!fAnnotationModelListeners.contains(cast(Object)listener)) {
+            fAnnotationModelListeners.add(cast(Object)listener);
+            if ( cast(IAnnotationModelListenerExtension)listener ) {
+                IAnnotationModelListenerExtension extension= cast(IAnnotationModelListenerExtension) listener;
+                AnnotationModelEvent event= createAnnotationModelEvent();
+                event.markSealed();
+                extension.modelChanged(event);
+            } else
+                listener.modelChanged(this);
+        }
+    }
+
+    /**
+     * Adds the given position to the default position category of the
+     * given document.
+     *
+     * @param document the document to which to add the position
+     * @param position the position to add
+     * @throws BadLocationException if the position is not a valid document position
+     */
+    protected void addPosition(IDocument document, Position position)  {
+        if (document !is null)
+            document.addPosition(position);
+    }
+
+    /**
+     * Removes the given position from the default position category of the
+     * given document.
+     *
+     * @param document the document to which to add the position
+     * @param position the position to add
+     *
+     * @since 3.0
+     */
+    protected void removePosition(IDocument document, Position position) {
+        if (document !is null)
+            document.removePosition(position);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#connect(org.eclipse.jface.text.IDocument)
+     */
+    public void connect(IDocument document) {
+        Assert.isTrue(fDocument is null || fDocument is document);
+
+        if (fDocument is null) {
+            fDocument= document;
+            Iterator e= getAnnotationMap().valuesIterator();
+            while (e.hasNext())
+                try {
+                    addPosition(fDocument, cast(Position) e.next());
+                } catch (BadLocationException x) {
+                    // ignore invalid position
+                }
+        }
+
+        ++ fOpenConnections;
+        if (fOpenConnections is 1) {
+            fDocument.addDocumentListener(fDocumentListener);
+            connected();
+        }
+
+        for (Iterator it= fAttachments.keySet().iterator(); it.hasNext();) {
+            IAnnotationModel model= cast(IAnnotationModel) fAttachments.get(it.next());
+            model.connect(document);
+        }
+    }
+
+    /**
+     * Hook method. Is called as soon as this model becomes connected to a document.
+     * Subclasses may re-implement.
+     */
+    protected void connected() {
+    }
+
+    /**
+     * Hook method. Is called as soon as this model becomes disconnected from its document.
+     * Subclasses may re-implement.
+     */
+    protected void disconnected() {
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#disconnect(org.eclipse.jface.text.IDocument)
+     */
+    public void disconnect(IDocument document) {
+
+        Assert.isTrue(fDocument is document);
+
+        for (Iterator it= fAttachments.keySet().iterator(); it.hasNext();) {
+            IAnnotationModel model= cast(IAnnotationModel) fAttachments.get(it.next());
+            model.disconnect(document);
+        }
+
+        -- fOpenConnections;
+        if (fOpenConnections is 0) {
+
+            disconnected();
+            fDocument.removeDocumentListener(fDocumentListener);
+
+            if (fDocument !is null) {
+                Iterator e= getAnnotationMap().valuesIterator();
+                while (e.hasNext()) {
+                    Position p= cast(Position) e.next();
+                    removePosition(fDocument, p);
+                }
+                fDocument= null;
+            }
+        }
+    }
+
+    /**
+     * Informs all annotation model listeners that this model has been changed.
+     */
+    protected void fireModelChanged() {
+        AnnotationModelEvent modelEvent= null;
+
+        synchronized(getLockObject()) {
+            if (fModelEvent !is null) {
+                modelEvent= fModelEvent;
+                fModelEvent= null;
+            }
+        }
+
+        if (modelEvent !is null)
+            fireModelChanged(modelEvent);
+    }
+
+    /**
+     * Creates and returns a new annotation model event. Subclasses may override.
+     *
+     * @return a new and empty annotation model event
+     * @since 3.0
+     */
+    protected AnnotationModelEvent createAnnotationModelEvent() {
+        return new AnnotationModelEvent(this);
+    }
+
+    /**
+     * Informs all annotation model listeners that this model has been changed
+     * as described in the annotation model event. The event is sent out
+     * to all listeners implementing <code>IAnnotationModelListenerExtension</code>.
+     * All other listeners are notified by just calling <code>modelChanged(IAnnotationModel)</code>.
+     *
+     * @param event the event to be sent out to the listeners
+     * @since 2.0
+     */
+    protected void fireModelChanged(AnnotationModelEvent event) {
+
+        event.markSealed();
+
+        if (event.isEmpty())
+            return;
+
+        ArrayList v= new ArrayList(fAnnotationModelListeners);
+        Iterator e= v.iterator();
+        while (e.hasNext()) {
+            IAnnotationModelListener l= cast(IAnnotationModelListener) e.next();
+            if ( cast(IAnnotationModelListenerExtension)l )
+                (cast(IAnnotationModelListenerExtension) l).modelChanged(event);
+            else if (l !is null)
+                l.modelChanged(this);
+        }
+    }
+
+    /**
+     * Removes the given annotations from this model. If requested all
+     * annotation model listeners will be informed about this change.
+     * <code>modelInitiated</code> indicates whether the deletion has
+     * been initiated by this model or by one of its clients.
+     *
+     * @param annotations the annotations to be removed
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @param modelInitiated indicates whether this changes has been initiated by this model
+     */
+    protected void removeAnnotations(List annotations, bool fireModelChanged_, bool modelInitiated) {
+        if (annotations.size() > 0) {
+            Iterator e= annotations.iterator();
+            while (e.hasNext())
+                removeAnnotation(cast(Annotation) e.next(), false);
+
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /**
+     * Removes all annotations from the model whose associated positions have been
+     * deleted. If requested inform all model listeners about the change.
+     *
+     * @param fireModelChanged indicates whether to notify all model listeners
+     */
+    protected void cleanup(bool fireModelChanged_) {
+        cleanup(fireModelChanged_, true);
+    }
+
+    /**
+     * Removes all annotations from the model whose associated positions have been
+     * deleted. If requested inform all model listeners about the change. If requested
+     * a new thread is created for the notification of the model listeners.
+     *
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @param forkNotification <code>true</code> iff notification should be done in a new thread
+     * @since 3.0
+     */
+    private void cleanup(bool fireModelChanged_, bool forkNotification) {
+        if (fDocumentChanged) {
+            fDocumentChanged= false;
+
+            ArrayList deleted= new ArrayList();
+            Iterator e= getAnnotationMap().keySetIterator();
+            while (e.hasNext()) {
+                Annotation a= cast(Annotation) e.next();
+                Position p= cast(Position) fAnnotations.get(a);
+                if (p is null || p.isDeleted())
+                    deleted.add(a);
+            }
+
+            if (fireModelChanged_ && forkNotification) {
+                removeAnnotations(deleted, false, false);
+                synchronized (getLockObject()) {
+                    if (fModelEvent !is null)
+                        (new JThread ( &fireModelChanged )).start();
+                }
+            } else
+                removeAnnotations(deleted, fireModelChanged_, false);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#getAnnotationIterator()
+     */
+    public Iterator getAnnotationIterator() {
+        return getAnnotationIterator(true, true);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 3.4
+     */
+    public Iterator getAnnotationIterator(int offset, int length, bool canStartBefore, bool canEndAfter) {
+        Iterator regionIterator= getRegionAnnotationIterator(offset, length, canStartBefore, canEndAfter);
+
+        if (fAttachments.isEmpty())
+            return regionIterator;
+
+        List iterators= new ArrayList(fAttachments.size() + 1);
+        iterators.add(cast(Object)regionIterator);
+        Iterator it= fAttachments.keySet().iterator();
+        while (it.hasNext()) {
+            IAnnotationModel attachment= cast(IAnnotationModel) fAttachments.get(it.next());
+            if ( cast(IAnnotationModelExtension2)attachment )
+                iterators.add(cast(Object)(cast(IAnnotationModelExtension2) attachment).getAnnotationIterator(offset, length, canStartBefore, canEndAfter));
+            else
+                iterators.add(new RegionIterator(attachment.getAnnotationIterator(), attachment, offset, length, canStartBefore, canEndAfter));
+        }
+
+        return new MetaIterator(iterators.iterator());
+    }
+
+    /**
+     * Returns an iterator as specified in {@link IAnnotationModelExtension2#getAnnotationIterator(int, int, bool, bool)}
+     *
+     * @param offset region start
+     * @param length region length
+     * @param canStartBefore position can start before region
+     * @param canEndAfter position can end after region
+     * @return an iterator to iterate over annotations in region
+     * @see IAnnotationModelExtension2#getAnnotationIterator(int, int, bool, bool)
+     * @since 3.4
+     */
+    private Iterator getRegionAnnotationIterator(int offset, int length, bool canStartBefore, bool canEndAfter) {
+        if (!( cast(AbstractDocument)fDocument ))
+            return new RegionIterator(getAnnotationIterator(true), this, offset, length, canStartBefore, canEndAfter);
+
+        AbstractDocument document= cast(AbstractDocument) fDocument;
+        cleanup(true);
+
+        try {
+            Position[] positions= document.getPositions(IDocument.DEFAULT_CATEGORY, offset, length, canStartBefore, canEndAfter);
+            return new AnnotationsInterator(positions, fPositions);
+        } catch (BadPositionCategoryException e) {
+            //can not happen
+            Assert.isTrue(false);
+            return null;
+        }
+    }
+
+    /**
+     * Returns all annotations managed by this model. <code>cleanup</code>
+     * indicates whether all annotations whose associated positions are
+     * deleted should previously be removed from the model. <code>recurse</code> indicates
+     * whether annotations of attached sub-models should also be returned.
+     *
+     * @param cleanup indicates whether annotations with deleted associated positions are removed
+     * @param recurse whether to return annotations managed by sub-models.
+     * @return all annotations managed by this model
+     * @since 3.0
+     */
+    private Iterator getAnnotationIterator(bool cleanup, bool recurse) {
+        Iterator iter= getAnnotationIterator(cleanup);
+        if (!recurse || fAttachments.isEmpty())
+            return iter;
+
+        List iterators= new ArrayList(fAttachments.size() + 1);
+        iterators.add(cast(Object)iter);
+        Iterator it= fAttachments.keySet().iterator();
+        while (it.hasNext())
+            iterators.add(cast(Object)(cast(IAnnotationModel) fAttachments.get(it.next())).getAnnotationIterator());
+
+        return new MetaIterator(iterators.iterator());
+    }
+
+    /**
+     * Returns all annotations managed by this model. <code>cleanup</code>
+     * indicates whether all annotations whose associated positions are
+     * deleted should previously be removed from the model.
+     *
+     * @param cleanup indicates whether annotations with deleted associated positions are removed
+     * @return all annotations managed by this model
+     */
+    protected Iterator getAnnotationIterator(bool cleanup_) {
+        if (cleanup_)
+            cleanup(true);
+
+        return getAnnotationMap().keySetIterator();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#getPosition(org.eclipse.jface.text.source.Annotation)
+     */
+    public Position getPosition(Annotation annotation) {
+        Position position= cast(Position) fAnnotations.get(annotation);
+        if (position !is null)
+            return position;
+
+        Iterator it= fAttachments.values().iterator();
+        while (position is null && it.hasNext())
+            position= (cast(IAnnotationModel) it.next()).getPosition(annotation);
+        return position;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#removeAllAnnotations()
+     * @since 3.0
+     */
+    public void removeAllAnnotations() {
+        removeAllAnnotations(true);
+    }
+
+    /**
+     * Removes all annotations from the annotation model. If requested
+     * inform all model change listeners about this change.
+     *
+     * @param fireModelChanged indicates whether to notify all model listeners
+     */
+    protected void removeAllAnnotations(bool fireModelChanged_) {
+
+        if (fDocument !is null) {
+            Iterator e= getAnnotationMap().keySetIterator();
+            while (e.hasNext()) {
+                Annotation a= cast(Annotation) e.next();
+                Position p= cast(Position) fAnnotations.get(a);
+                removePosition(fDocument, p);
+//              p.delete_();
+                synchronized (getLockObject()) {
+                    getAnnotationModelEvent().annotationRemoved(a, p);
+                }
+            }
+        }
+
+        fAnnotations.clear();
+        fPositions.clear();
+
+        if (fireModelChanged_)
+            fireModelChanged();
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModel#removeAnnotation(org.eclipse.jface.text.source.Annotation)
+     */
+    public void removeAnnotation(Annotation annotation) {
+        removeAnnotation(annotation, true);
+    }
+
+    /**
+     * Removes the given annotation from the annotation model.
+     * If requested inform all model change listeners about this change.
+     *
+     * @param annotation the annotation to be removed
+     * @param fireModelChanged indicates whether to notify all model listeners
+     */
+    protected void removeAnnotation(Annotation annotation, bool fireModelChanged_) {
+        if (fAnnotations.containsKey(annotation)) {
+
+            Position p= null;
+            p= cast(Position) fAnnotations.get(annotation);
+            if (fDocument !is null) {
+                removePosition(fDocument, p);
+//              p.delete_();
+            }
+
+            fAnnotations.remove(annotation);
+            fPositions.remove(p);
+            synchronized (getLockObject()) {
+                getAnnotationModelEvent().annotationRemoved(annotation, p);
+            }
+
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#modifyAnnotationPosition(org.eclipse.jface.text.source.Annotation, org.eclipse.jface.text.Position)
+     * @since 3.0
+     */
+    public void modifyAnnotationPosition(Annotation annotation, Position position) {
+        modifyAnnotationPosition(annotation, position, true);
+    }
+
+    /**
+     * Modifies the associated position of the given annotation to the given
+     * position. If the annotation is not yet managed by this annotation model,
+     * the annotation is added. When the position is <code>null</code>, the
+     * annotation is removed from the model.
+     * <p>
+     * If requested, all annotation model change listeners will be informed
+     * about the change.
+     *
+     * @param annotation the annotation whose associated position should be
+     *            modified
+     * @param position the position to whose values the associated position
+     *            should be changed
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @since 3.0
+     */
+    protected void modifyAnnotationPosition(Annotation annotation, Position position, bool fireModelChanged_) {
+        if (position is null) {
+            removeAnnotation(annotation, fireModelChanged_);
+        } else {
+            Position p= cast(Position) fAnnotations.get(annotation);
+            if (p !is null) {
+
+                if (position.getOffset() !is p.getOffset() || position.getLength() !is p.getLength()) {
+                    fDocument.removePosition(p);
+                    p.setOffset(position.getOffset());
+                    p.setLength(position.getLength());
+                    try {
+                        fDocument.addPosition(p);
+                    } catch (BadLocationException e) {
+                        // ignore invalid position
+                    }
+                }
+                synchronized (getLockObject()) {
+                    getAnnotationModelEvent().annotationChanged(annotation);
+                }
+                if (fireModelChanged_)
+                    fireModelChanged();
+
+            } else {
+                try {
+                    addAnnotation(annotation, position, fireModelChanged_);
+                } catch (BadLocationException x) {
+                    // ignore invalid position
+                }
+            }
+        }
+    }
+
+    /**
+     * Modifies the given annotation if the annotation is managed by this
+     * annotation model.
+     * <p>
+     * If requested, all annotation model change listeners will be informed
+     * about the change.
+     *
+     * @param annotation the annotation to be modified
+     * @param fireModelChanged indicates whether to notify all model listeners
+     * @since 3.0
+     */
+    protected void modifyAnnotation(Annotation annotation, bool fireModelChanged_) {
+        if (fAnnotations.containsKey(annotation)) {
+            synchronized (getLockObject()) {
+                getAnnotationModelEvent().annotationChanged(annotation);
+            }
+            if (fireModelChanged_)
+                fireModelChanged();
+        }
+    }
+
+    /*
+     * @see IAnnotationModel#removeAnnotationModelListener(IAnnotationModelListener)
+     */
+    public void removeAnnotationModelListener(IAnnotationModelListener listener) {
+        fAnnotationModelListeners.remove(cast(Object)listener);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#attach(java.lang.Object, java.lang.Object)
+     * @since 3.0
+     */
+    public void addAnnotationModel(Object key, IAnnotationModel attachment) {
+        Assert.isNotNull(cast(Object)attachment);
+        if (!fAttachments.containsValue(cast(Object)attachment)) {
+            fAttachments.put(key, cast(Object)attachment);
+            for (int i= 0; i < fOpenConnections; i++)
+                attachment.connect(fDocument);
+            attachment.addAnnotationModelListener(fModelListener);
+        }
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#get(java.lang.Object)
+     * @since 3.0
+     */
+    public IAnnotationModel getAnnotationModel(Object key) {
+        return cast(IAnnotationModel) fAttachments.get(key);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#detach(java.lang.Object)
+     * @since 3.0
+     */
+    public IAnnotationModel removeAnnotationModel(Object key) {
+        IAnnotationModel ret= cast(IAnnotationModel) fAttachments.remove(key);
+        if (ret !is null) {
+            for (int i= 0; i < fOpenConnections; i++)
+                ret.disconnect(fDocument);
+            ret.removeAnnotationModelListener(fModelListener);
+        }
+        return ret;
+    }
+
+    /*
+     * @see org.eclipse.jface.text.source.IAnnotationModelExtension#getModificationStamp()
+     * @since 3.0
+     */
+    public Object getModificationStamp() {
+        return fModificationStamp;
+    }
+}