Copyright (c) 2000, 2015 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
IBM Corporation - initial API and implementation
Anton Leherbauer - [implementation] AnnotationModel.fModificationStamp leaks annotations - http://bugs.eclipse.org/345715
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Anton Leherbauer <anton.leherbauer@windriver.com> - [implementation] AnnotationModel.fModificationStamp leaks annotations - http://bugs.eclipse.org/345715
*******************************************************************************/
package org.eclipse.jface.text.source;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
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 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 ISynchronizable
. All modifications of the model's internal annotation map are synchronized using the model's lock object. /**
* 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 implements IAnnotationModel, IAnnotationModelExtension, IAnnotationModelExtension2, ISynchronizable {
Iterator that returns the annotations for a given region.
See Also: Since: 3.4
/**
* Iterator that returns the annotations for a given region.
*
* @since 3.4
* @see AnnotationModel.RegionIterator#RegionIterator(Iterator, IAnnotationModel, int, int, boolean, boolean)
*/
private static final class RegionIterator implements Iterator<Annotation> {
private final Iterator<Annotation> fParentIterator;
private final boolean fCanEndAfter;
private final boolean fCanStartBefore;
private final IAnnotationModel fModel;
private Annotation 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.
See IAnnotationModelExtension2
for a definition of inside.
Params: - parentIterator – iterator containing all annotations
- model – the model to use to retrieve positions from for each
annotation
- offset – start position of the region
- length – length of the region
- canStartBefore – include annotations starting before region
- canEndAfter – include annotations ending after region
See Also:
/**
* 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 RegionIterator(Iterator<Annotation> parentIterator, IAnnotationModel model, int offset, int length, boolean canStartBefore, boolean canEndAfter) {
fParentIterator= parentIterator;
fModel= model;
fRegion= new Position(offset, length);
fCanEndAfter= canEndAfter;
fCanStartBefore= canStartBefore;
fNext= findNext();
}
@Override
public boolean hasNext() {
return fNext != null;
}
@Override
public Annotation next() {
if (!hasNext())
throw new NoSuchElementException();
Annotation result= fNext;
fNext= findNext();
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private Annotation findNext() {
while (fParentIterator.hasNext()) {
Annotation next= fParentIterator.next();
Position position= fModel.getPosition(next);
if (position != null) {
int offset= position.getOffset();
if (isWithinRegion(offset, position.getLength()))
return next;
}
}
return null;
}
private boolean isWithinRegion(int start, int length) {
if (fCanStartBefore && fCanEndAfter)
return fRegion.overlapsWith(start, length);
else if (fCanStartBefore)
return fRegion.includes(start + length - (length > 0 ? 1 : 0));
else if (fCanEndAfter)
return fRegion.includes(start);
else
return fRegion.includes(start) && fRegion.includes(start + length - (length > 0 ? 1 : 0));
}
}
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
/**
* 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 implements Iterator<Annotation> {
private Annotation fNext;
private final Position[] fPositions;
private int fIndex;
private final Map<Position, Annotation> fMap;
Params: - positions – positions to iterate over
- map – a map to map positions to annotations
/**
* @param positions positions to iterate over
* @param map a map to map positions to annotations
*/
public AnnotationsInterator(Position[] positions, Map<Position, Annotation> map) {
fPositions= positions;
fIndex= 0;
fMap= map;
fNext= findNext();
}
@Override
public boolean hasNext() {
return fNext != null;
}
@Override
public Annotation next() {
Annotation result= fNext;
fNext= findNext();
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private Annotation 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.
Type parameters: - <E> – the type of elements returned by this iterator
Since: 3.1
/**
* A single iterator builds its behavior based on a sequence of iterators.
*
* @param <E> the type of elements returned by this iterator
* @since 3.1
*/
private static class MetaIterator<E> implements Iterator<E> {
The iterator over a list of iterators. /** The iterator over a list of iterators. */
private Iterator<? extends Iterator<? extends E>> fSuperIterator;
The current iterator. /** The current iterator. */
private Iterator<? extends E> fCurrent;
The current element. /** The current element. */
private E fCurrentElement;
public MetaIterator(Iterator<? extends Iterator<? extends E>> iterator) {
fSuperIterator= iterator;
fCurrent= fSuperIterator.next(); // there is at least one.
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() {
if (fCurrentElement != null)
return true;
if (fCurrent.hasNext()) {
fCurrentElement= fCurrent.next();
return true;
} else if (fSuperIterator.hasNext()) {
fCurrent= fSuperIterator.next();
return hasNext();
} else
return false;
}
@Override
public E next() {
if (!hasNext())
throw new NoSuchElementException();
E 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
/**
* 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 implements IAnnotationModelListener, IAnnotationModelListenerExtension {
@Override
public void modelChanged(IAnnotationModel model) {
AnnotationModel.this.fireModelChanged(new AnnotationModelEvent(model, true));
}
@Override
public void modelChanged(AnnotationModelEvent event) {
AnnotationModel.this.fireModelChanged(event);
}
}
The list of managed annotations
Deprecated: since 3.0 use getAnnotationMap
instead
/**
* The list of managed annotations
* @deprecated since 3.0 use <code>getAnnotationMap</code> instead
*/
@Deprecated
protected Map<Annotation, Position> fAnnotations;
The map which maps Position
to Annotation
. Since: 3.4
/**
* The map which maps {@link Position} to {@link Annotation}.
* @since 3.4
**/
private IdentityHashMap<Position, Annotation> fPositions;
The list of annotation model listeners /** The list of annotation model listeners */
protected ArrayList<IAnnotationModelListener> fAnnotationModelListeners;
The document connected with this model /** The document connected with this model */
protected IDocument fDocument;
The number of open connections to the same document /** 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. /** 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. /** The flag indicating whether the document positions might have been changed. */
private boolean fDocumentChanged= true;
The model's attachment.
Since: 3.0
/**
* The model's attachment.
* @since 3.0
*/
private Map<Object, IAnnotationModel> fAttachments= new HashMap<>();
The annotation model listener on attached sub-models.
Since: 3.0
/**
* The annotation model listener on attached sub-models.
* @since 3.0
*/
private IAnnotationModelListener fModelListener= new InternalModelListener();
The current annotation model event.
Since: 3.0
/**
* The current annotation model event.
* @since 3.0
*/
private AnnotationModelEvent fModelEvent;
The modification stamp.
Since: 3.0
/**
* The modification stamp.
* @since 3.0
*/
private Object fModificationStamp= new Object();
Creates a new annotation model. The annotation is empty, i.e. does not
manage any annotations and is not connected to any document.
/**
* Creates a new annotation model. The annotation is empty, i.e. does not
* manage any annotations and is not connected to any document.
*/
public AnnotationModel() {
fAnnotations= new AnnotationMap(10);
fPositions= new IdentityHashMap<>(10);
fAnnotationModelListeners= new ArrayList<>(2);
fDocumentListener= new IDocumentListener() {
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
}
@Override
public void documentChanged(DocumentEvent event) {
fDocumentChanged= true;
}
};
}
Returns the annotation map internally used by this annotation model.
Returns: the annotation map internally used by this annotation model Since: 3.0
/**
* 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 (IAnnotationMap) fAnnotations;
}
@Override
public Object getLockObject() {
return getAnnotationMap().getLockObject();
}
@Override
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 fireModelChanged
.
Returns: the current annotation model event Since: 3.0
/**
* 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 == null) {
fModelEvent= createAnnotationModelEvent();
fModelEvent.markWorldChange(false);
fModificationStamp= new Object();
}
return fModelEvent;
}
}
@Override
public void addAnnotation(Annotation annotation, Position position) {
try {
addAnnotation(annotation, position, true);
} catch (BadLocationException e) {
// ignore invalid position
}
}
@Override
public void replaceAnnotations(Annotation[] annotationsToRemove, Map<? extends Annotation, ? extends Position> annotationsToAdd) {
try {
replaceAnnotations(annotationsToRemove, annotationsToAdd, true);
} catch (BadLocationException x) {
}
}
Replaces the given annotations in this model and if advised fires a
model change event.
Params: - annotationsToRemove – the annotations to be removed
- annotationsToAdd – the annotations to be added
- fireModelChanged –
true
if a model change event
should be fired, false
otherwise
Throws: - BadLocationException – in case an annotation should be added at an
invalid position
Since: 3.0
/**
* 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<? extends Annotation, ? extends Position> annotationsToAdd, boolean fireModelChanged) throws BadLocationException {
if (annotationsToRemove != null) {
for (Annotation element : annotationsToRemove)
removeAnnotation(element, false);
}
if (annotationsToAdd != null) {
Iterator<? extends Entry<? extends Annotation, ? extends Position>> iter= annotationsToAdd.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<? extends Annotation, ? extends Position> mapEntry= iter.next();
Annotation annotation= mapEntry.getKey();
Position 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.
Params: - annotation – the annotation to add
- position – the associate position
- fireModelChanged – indicates whether to notify all model listeners
Throws: - BadLocationException – if the position is not a valid document position
/**
* 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, boolean fireModelChanged) throws BadLocationException {
if (!fAnnotations.containsKey(annotation)) {
addPosition(fDocument, position);
fAnnotations.put(annotation, position);
fPositions.put(position, annotation);
synchronized (getLockObject()) {
getAnnotationModelEvent().annotationAdded(annotation);
}
if (fireModelChanged)
fireModelChanged();
}
}
@Override
public void addAnnotationModelListener(IAnnotationModelListener listener) {
if (!fAnnotationModelListeners.contains(listener)) {
fAnnotationModelListeners.add(listener);
if (listener instanceof IAnnotationModelListenerExtension) {
IAnnotationModelListenerExtension extension= (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.
Params: - document – the document to which to add the position
- position – the position to add
Throws: - BadLocationException – if the position is not a valid document position
/**
* 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) throws BadLocationException {
if (document != null)
document.addPosition(position);
}
Removes the given position from the default position category of the
given document.
Params: - document – the document to which to add the position
- position – the position to add
Since: 3.0
/**
* 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 != null)
document.removePosition(position);
}
@Override
public void connect(IDocument document) {
Assert.isTrue(fDocument == null || fDocument == document);
if (fDocument == null) {
fDocument= document;
Iterator<Position> e= getAnnotationMap().valuesIterator();
while (e.hasNext())
try {
addPosition(document, e.next());
} catch (BadLocationException x) {
// ignore invalid position
}
}
++ fOpenConnections;
if (fOpenConnections == 1) {
document.addDocumentListener(fDocumentListener);
connected();
}
for (IAnnotationModel model : fAttachments.values()) {
model.connect(document);
}
}
Hook method. Is called as soon as this model becomes connected to a document.
Subclasses may re-implement.
/**
* 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.
/**
* Hook method. Is called as soon as this model becomes disconnected from its document.
* Subclasses may re-implement.
*/
protected void disconnected() {
}
@Override
public void disconnect(IDocument document) {
Assert.isTrue(fDocument == document);
for (IAnnotationModel model : fAttachments.values()) {
model.disconnect(document);
}
-- fOpenConnections;
if (fOpenConnections == 0) {
disconnected();
document.removeDocumentListener(fDocumentListener);
Iterator<Position> e= getAnnotationMap().valuesIterator();
while (e.hasNext()) {
Position p= e.next();
removePosition(document, p);
}
fDocument= null;
}
}
Informs all annotation model listeners that this model has been changed.
/**
* Informs all annotation model listeners that this model has been changed.
*/
protected void fireModelChanged() {
AnnotationModelEvent modelEvent= null;
synchronized(getLockObject()) {
if (fModelEvent != null) {
modelEvent= fModelEvent;
fModelEvent= null;
}
}
if (modelEvent != null)
fireModelChanged(modelEvent);
}
Creates and returns a new annotation model event. Subclasses may override.
Returns: a new and empty annotation model event Since: 3.0
/**
* 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 IAnnotationModelListenerExtension
.
All other listeners are notified by just calling modelChanged(IAnnotationModel)
.
Params: - event – the event to be sent out to the listeners
Since: 2.0
/**
* 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<IAnnotationModelListener> v= new ArrayList<>(fAnnotationModelListeners);
Iterator<IAnnotationModelListener> e= v.iterator();
while (e.hasNext()) {
IAnnotationModelListener l= e.next();
if (l instanceof IAnnotationModelListenerExtension)
((IAnnotationModelListenerExtension) l).modelChanged(event);
else if (l != null)
l.modelChanged(this);
}
}
Removes the given annotations from this model. If requested all
annotation model listeners will be informed about this change.
modelInitiated
indicates whether the deletion has
been initiated by this model or by one of its clients.
Params: - annotations – the annotations to be removed
- fireModelChanged – indicates whether to notify all model listeners
- modelInitiated – indicates whether this changes has been initiated by this model
/**
* 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<? extends Annotation> annotations, boolean fireModelChanged, boolean modelInitiated) {
if (!annotations.isEmpty()) {
Iterator<? extends Annotation> e= annotations.iterator();
while (e.hasNext())
removeAnnotation(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.
Params: - fireModelChanged – indicates whether to notify all model listeners
/**
* 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(boolean 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.
Params: - fireModelChanged – indicates whether to notify all model listeners
- forkNotification –
true
iff notification should be done in a new thread
Since: 3.0
/**
* 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(boolean fireModelChanged, boolean forkNotification) {
if (fDocumentChanged) {
fDocumentChanged= false;
ArrayList<Annotation> deleted= new ArrayList<>();
Iterator<Annotation> e= getAnnotationMap().keySetIterator();
while (e.hasNext()) {
Annotation a= e.next();
Position p= fAnnotations.get(a);
if (p == null || p.isDeleted())
deleted.add(a);
}
if (fireModelChanged && forkNotification) {
removeAnnotations(deleted, false, false);
synchronized (getLockObject()) {
if (fModelEvent != null)
new Thread() {
@Override
public void run() {
fireModelChanged();
}
}.start();
}
} else
removeAnnotations(deleted, fireModelChanged, false);
}
}
@Override
public Iterator<Annotation> getAnnotationIterator() {
return getAnnotationIterator(true, true);
}
{@inheritDoc}
Since: 3.4
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public Iterator<Annotation> getAnnotationIterator(int offset, int length, boolean canStartBefore, boolean canEndAfter) {
Iterator<Annotation> regionIterator= getRegionAnnotationIterator(offset, length, canStartBefore, canEndAfter);
if (fAttachments.isEmpty())
return regionIterator;
List<Iterator<Annotation>> iterators= new ArrayList<>(fAttachments.size() + 1);
iterators.add(regionIterator);
Iterator<Object> it= fAttachments.keySet().iterator();
while (it.hasNext()) {
IAnnotationModel attachment= fAttachments.get(it.next());
if (attachment instanceof IAnnotationModelExtension2)
iterators.add(((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 IAnnotationModelExtension2.getAnnotationIterator(int, int, boolean, boolean)
Params: - offset – region start
- length – region length
- canStartBefore – position can start before region
- canEndAfter – position can end after region
See Also: Returns: an iterator to iterate over annotations in region Since: 3.4
/**
* Returns an iterator as specified in {@link IAnnotationModelExtension2#getAnnotationIterator(int, int, boolean, boolean)}
*
* @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, boolean, boolean)
* @since 3.4
*/
private Iterator<Annotation> getRegionAnnotationIterator(int offset, int length, boolean canStartBefore, boolean canEndAfter) {
if (!(fDocument instanceof AbstractDocument))
return new RegionIterator(getAnnotationIterator(true), this, offset, length, canStartBefore, canEndAfter);
AbstractDocument document= (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 happen if e.g. the document doesn't contain such a category, or when removed in a different thread
return Collections.<Annotation>emptyList().iterator();
}
}
Returns all annotations managed by this model. cleanup
indicates whether all annotations whose associated positions are
deleted should previously be removed from the model. recurse
indicates
whether annotations of attached sub-models should also be returned.
Params: - cleanup – indicates whether annotations with deleted associated positions are removed
- recurse – whether to return annotations managed by sub-models.
Returns: all annotations managed by this model Since: 3.0
/**
* 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<Annotation> getAnnotationIterator(boolean cleanup, boolean recurse) {
Iterator<Annotation> iter= getAnnotationIterator(cleanup);
if (!recurse || fAttachments.isEmpty())
return iter;
List<Iterator<Annotation>> iterators= new ArrayList<>(fAttachments.size() + 1);
iterators.add(iter);
Iterator<Object> it= fAttachments.keySet().iterator();
while (it.hasNext())
iterators.add(fAttachments.get(it.next()).getAnnotationIterator());
return new MetaIterator<>(iterators.iterator());
}
Returns all annotations managed by this model. cleanup
indicates whether all annotations whose associated positions are
deleted should previously be removed from the model.
Params: - cleanup – indicates whether annotations with deleted associated positions are removed
Returns: all annotations managed by this model
/**
* 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<Annotation> getAnnotationIterator(boolean cleanup) {
if (cleanup)
cleanup(true);
return getAnnotationMap().keySetIterator();
}
@Override
public Position getPosition(Annotation annotation) {
Position position= fAnnotations.get(annotation);
if (position != null)
return position;
Iterator<IAnnotationModel> it= fAttachments.values().iterator();
while (position == null && it.hasNext())
position= it.next().getPosition(annotation);
return position;
}
@Override
public void removeAllAnnotations() {
removeAllAnnotations(true);
}
Removes all annotations from the annotation model. If requested
inform all model change listeners about this change.
Params: - fireModelChanged – indicates whether to notify all model listeners
/**
* 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(boolean fireModelChanged) {
if (fDocument != null) {
Iterator<Annotation> e= getAnnotationMap().keySetIterator();
while (e.hasNext()) {
Annotation a= e.next();
Position p= fAnnotations.get(a);
removePosition(fDocument, p);
// p.delete();
synchronized (getLockObject()) {
getAnnotationModelEvent().annotationRemoved(a, p);
}
}
}
fAnnotations.clear();
fPositions.clear();
if (fireModelChanged)
fireModelChanged();
}
@Override
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.
Params: - annotation – the annotation to be removed
- fireModelChanged – indicates whether to notify all model listeners
/**
* 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, boolean fireModelChanged) {
if (fAnnotations.containsKey(annotation)) {
Position p= null;
p= fAnnotations.get(annotation);
if (fDocument != null) {
removePosition(fDocument, p);
// p.delete();
}
fAnnotations.remove(annotation);
fPositions.remove(p);
synchronized (getLockObject()) {
getAnnotationModelEvent().annotationRemoved(annotation, p);
}
if (fireModelChanged)
fireModelChanged();
}
}
@Override
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 null
, the
annotation is removed from the model.
If requested, all annotation model change listeners will be informed
about the change.
Params: - annotation – the annotation whose associated position should be
modified
- position – the position to whose values the associated position
should be changed
- fireModelChanged – indicates whether to notify all model listeners
Since: 3.0
/**
* 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, boolean fireModelChanged) {
if (position == null) {
removeAnnotation(annotation, fireModelChanged);
} else {
Position p= fAnnotations.get(annotation);
if (p != null) {
if (position.getOffset() != p.getOffset() || position.getLength() != 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.
If requested, all annotation model change listeners will be informed
about the change.
Params: - annotation – the annotation to be modified
- fireModelChanged – indicates whether to notify all model listeners
Since: 3.0
/**
* 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, boolean fireModelChanged) {
if (fAnnotations.containsKey(annotation)) {
synchronized (getLockObject()) {
getAnnotationModelEvent().annotationChanged(annotation);
}
if (fireModelChanged)
fireModelChanged();
}
}
@Override
public void removeAnnotationModelListener(IAnnotationModelListener listener) {
fAnnotationModelListeners.remove(listener);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationModelExtension#attach(java.lang.Object, java.lang.Object)
* @since 3.0
*/
@Override
public void addAnnotationModel(Object key, IAnnotationModel attachment) {
Assert.isNotNull(attachment);
if (!fAttachments.containsValue(attachment)) {
fAttachments.put(key, 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
*/
@Override
public IAnnotationModel getAnnotationModel(Object key) {
return fAttachments.get(key);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationModelExtension#detach(java.lang.Object)
* @since 3.0
*/
@Override
public IAnnotationModel removeAnnotationModel(Object key) {
IAnnotationModel ret= fAttachments.remove(key);
if (ret != null) {
for (int i= 0; i < fOpenConnections; i++)
ret.disconnect(fDocument);
ret.removeAnnotationModelListener(fModelListener);
}
return ret;
}
@Override
public Object getModificationStamp() {
return fModificationStamp;
}
}