blob: 2162eca43fce0497d0ae82d8546a3c76b94e2922 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 Ericsson AB 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
*
* Description:
*
* This class implements the annotation model for the R4E annotations.
*
* Contributors:
* Sebastien Dubois - Created for Mylyn Review R4E project
*
******************************************************************************/
package org.eclipse.mylyn.reviews.r4e.ui.internal.annotation.content;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModelEvent;
import org.eclipse.jface.text.source.IAnnotationModelListener;
import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
import org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotation;
import org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel;
import org.eclipse.mylyn.reviews.r4e.core.model.R4EID;
import org.eclipse.mylyn.reviews.r4e.ui.R4EUIPlugin;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.IR4EUIModelElement;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUIAnomalyBasic;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUIAnomalyContainer;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUIContent;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUIContentsContainer;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUIDelta;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUIFileContext;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUIPostponedFile;
import org.eclipse.mylyn.reviews.r4e.ui.internal.model.R4EUISelection;
/**
* @author Sebastien Dubois
* @version $Revision: 1.0 $
*/
public class R4EAnnotationModel implements IReviewAnnotationModel {
// ------------------------------------------------------------------------
// Members
// ------------------------------------------------------------------------
/**
* Field fAnnotationModelListeners.
*/
private final Set<IAnnotationModelListener> fAnnotationModelListeners = new HashSet<IAnnotationModelListener>(2);
/**
* Field fAnnotationsMap.
*/
private final Map<R4EID, IReviewAnnotation> fAnnotationsMap = new HashMap<R4EID, IReviewAnnotation>();
/**
* Field fSortedAnnotationsList.
*/
private final Map<String, List<IReviewAnnotation>> fSortedAnnotationsListsMap = new HashMap<String, List<IReviewAnnotation>>();
/**
* Field fSortedAnnotationsIndexMap.
*/
private final Map<String, Integer> fSortedAnnotationsIndexMap = new HashMap<String, Integer>();
/**
* Field fFileContext.
*/
private R4EUIFileContext fFileContext = null;
/**
* Field fDocument.
*/
private IDocument fDocument;
/**
* Field fDocumentListener.
*/
private final IDocumentListener fDocumentListener = new IDocumentListener() {
public void documentAboutToBeChanged(DocumentEvent aEvent) {
//not used for now
}
public void documentChanged(DocumentEvent aEvent) {
//not used for now
}
};
/**
* Field ANNOTATION_COMPARATOR.
*/
private static final Comparator<IReviewAnnotation> ANNOTATION_COMPARATOR = new Comparator<IReviewAnnotation>() {
// This is where the sorting happens.
public int compare(IReviewAnnotation aObject1, IReviewAnnotation aObject2) {
final int sortOrder = aObject1.getPosition().getOffset() - aObject2.getPosition().getOffset();
if (sortOrder == 0) {
return aObject1.getPosition().getLength() - aObject2.getPosition().getLength();
}
return sortOrder;
}
};
// ------------------------------------------------------------------------
// Methods
// ------------------------------------------------------------------------
/**
* Method getAnnotationIterator.
*
* @return Iterator<IReviewAnnotation>
* @see org.eclipse.jface.text.source.IAnnotationModel#getAnnotationIterator()
*/
public Iterator<IReviewAnnotation> getAnnotationIterator() {
return fAnnotationsMap.values().iterator();
}
/**
* Method setFile.
*
* @param aFileContext
* Object
*/
public void setFile(Object aFileContext) {
fFileContext = (R4EUIFileContext) aFileContext;
fFileContext.registerAnnotationModel(this);
}
/**
* Method getPosition.
*
* @param aAnnotation
* Annotation
* @return Position
* @see org.eclipse.jface.text.source.IAnnotationModel#getPosition(Annotation)
*/
public Position getPosition(Annotation aAnnotation) {
if (aAnnotation instanceof IReviewAnnotation) {
return ((IReviewAnnotation) aAnnotation).getPosition();
} else {
// we dont understand any other annotations
return null;
}
}
/**
* Method addAnnotationModelListener.
*
* @param aListener
* IAnnotationModelListener
* @see org.eclipse.jface.text.source.IAnnotationModel#addAnnotationModelListener(IAnnotationModelListener)
*/
public void addAnnotationModelListener(IAnnotationModelListener aListener) {
fAnnotationModelListeners.add(aListener);
}
/**
* Method removeAnnotationModelListener.
*
* @param aListener
* IAnnotationModelListener
* @see org.eclipse.jface.text.source.IAnnotationModel#removeAnnotationModelListener(IAnnotationModelListener)
*/
public void removeAnnotationModelListener(IAnnotationModelListener aListener) {
fAnnotationModelListeners.remove(aListener);
}
/**
* Method connect.
*
* @param aDocument
* IDocument
* @see org.eclipse.jface.text.source.IAnnotationModel#connect(IDocument)
*/
public void connect(IDocument aDocument) {
fDocument = aDocument;
if (fDocument.getLength() > 0) {
for (IReviewAnnotation annotation : fAnnotationsMap.values()) {
try {
fDocument.addPosition(annotation.getPosition());
} catch (BadLocationException e) {
R4EUIPlugin.Ftracer.traceError("Exception: " + e.toString() + " (" + e.getMessage() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
R4EUIPlugin.getDefault().logError("Exception: " + e.toString(), e); //$NON-NLS-1$
}
}
fDocument.addDocumentListener(fDocumentListener);
refreshAnnotations();
}
}
/**
* Method disconnect.
*
* @param aDocument
* IDocument
* @see org.eclipse.jface.text.source.IAnnotationModel#disconnect(IDocument)
*/
public void disconnect(IDocument aDocument) {
for (IReviewAnnotation annotation : fAnnotationsMap.values()) {
aDocument.removePosition(annotation.getPosition());
}
aDocument.removeDocumentListener(fDocumentListener);
fDocument = null;
}
/**
* Method clearAnnotations.
*/
public void clearAnnotations() {
final AnnotationModelEvent event = new AnnotationModelEvent(this);
clear(event);
fireModelChanged(event);
}
/**
* Method clear.
*
* @param aEvent
* AnnotationModelEvent
*/
private void clear(AnnotationModelEvent aEvent) {
for (IReviewAnnotation annotation : fAnnotationsMap.values()) {
aEvent.annotationRemoved((R4EAnnotation) annotation, annotation.getPosition());
}
fAnnotationsMap.clear();
for (List<IReviewAnnotation> annotationList : fSortedAnnotationsListsMap.values()) {
annotationList.clear();
}
fSortedAnnotationsIndexMap.clear();
}
/**
* Method refreshAnnotations.
*/
public void refreshAnnotations() {
final AnnotationModelEvent event = new AnnotationModelEvent(this);
clear(event);
if ((fDocument != null) && (fFileContext != null)) {
R4EUIAnomalyContainer anomalies = fFileContext.getAnomalyContainerElement();
if (null != anomalies) {
for (IR4EUIModelElement anomaly : anomalies.getChildren()) {
addAnnotation(fDocument, event, anomaly);
}
}
R4EUIContentsContainer contents = fFileContext.getContentsContainerElement();
if (null != contents) {
for (IR4EUIModelElement content : fFileContext.getContentsContainerElement().getChildren()) {
addAnnotation(fDocument, event, content);
}
}
if (fFileContext instanceof R4EUIPostponedFile) {
IR4EUIModelElement[] postponedAnomalies = ((R4EUIPostponedFile) fFileContext).getChildren();
for (IR4EUIModelElement anomaly : postponedAnomalies) {
addAnnotation(fDocument, event, anomaly);
}
}
}
fireModelChanged(event);
}
/**
* Method updateAnnotation.
*
* @param aAnnotationContent
* Object
* @see org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel#updateAnnotation(Object)
*/
public void updateAnnotation(Object aAnnotationContent) {
removeAnnotation(aAnnotationContent);
addAnnotation(aAnnotationContent);
}
/**
* Method addAnnotation.
*
* @param aAnnotationContent
* Object
* @see org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel#addAnnotation(Object)
*/
public void addAnnotation(Object aAnnotationContent) {
final AnnotationModelEvent event = new AnnotationModelEvent(this);
if ((fDocument != null) && (fFileContext != null)) {
addAnnotation(fDocument, event, aAnnotationContent);
}
fireModelChanged(event);
}
/**
* Method addAnnotation.
*
* @param aDocument
* IDocument
* @param aEvent
* AnnotationModelEvent
* @param aAnnotationContent
* Object
*/
private void addAnnotation(IDocument aDocument, AnnotationModelEvent aEvent, Object aAnnotationContent) {
R4EAnnotation newAnnotation = null;
if (aAnnotationContent instanceof R4EUIAnomalyBasic) {
newAnnotation = new R4EAnomalyAnnotation((R4EUIAnomalyBasic) aAnnotationContent);
} else if (aAnnotationContent instanceof R4EUIDelta) {
newAnnotation = new R4EDeltaAnnotation((R4EUIDelta) aAnnotationContent);
} else if (aAnnotationContent instanceof R4EUISelection) {
newAnnotation = new R4ESelectionAnnotation((R4EUISelection) aAnnotationContent);
}
if (null != newAnnotation) {
addAnnotation(newAnnotation, null);
aEvent.annotationAdded(newAnnotation);
}
}
/**
* Method addAnnotation.
*
* @param aAnnotation
* Annotation
* @param aPosition
* Position
* @see org.eclipse.jface.text.source.IAnnotationModel#addAnnotation(Annotation, Position)
*/
public void addAnnotation(Annotation aAnnotation, Position aPosition) {
if (aAnnotation instanceof R4EAnnotation) {
final R4EAnnotation newAnnotation = (R4EAnnotation) aAnnotation;
fAnnotationsMap.put((R4EID) newAnnotation.getId(), newAnnotation);
List<IReviewAnnotation> annotationList = fSortedAnnotationsListsMap.get(newAnnotation.getType());
if (null == annotationList) {
annotationList = new ArrayList<IReviewAnnotation>();
fSortedAnnotationsListsMap.put(newAnnotation.getType(), annotationList);
}
annotationList.add(newAnnotation);
Collections.sort(annotationList, ANNOTATION_COMPARATOR);
fSortedAnnotationsIndexMap.put(newAnnotation.getType(), annotationList.indexOf(newAnnotation));
}
//Ignore any other Annotation type
}
/**
* Method removeAnnotation.
*
* @param aAnnotationContent
* Object
* @see org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel#removeAnnotation(Object)
*/
public void removeAnnotation(Object aAnnotationContent) {
R4EID id = null;
if (aAnnotationContent instanceof R4EUIAnomalyBasic) {
id = ((R4EUIAnomalyBasic) aAnnotationContent).getAnomaly().getR4eId();
} else if (aAnnotationContent instanceof R4EUIContent) {
id = ((R4EUIContent) aAnnotationContent).getContent().getR4eId();
}
if (null != id) {
final IReviewAnnotation removedAnnotation = fAnnotationsMap.remove(id);
removeAnnotation((R4EAnnotation) removedAnnotation);
final AnnotationModelEvent event = new AnnotationModelEvent(this);
event.annotationRemoved((R4EAnnotation) removedAnnotation);
fireModelChanged(event);
}
}
/**
* Method removeAnnotation.
*
* @param aAnnotation
* Annotation
* @see org.eclipse.jface.text.source.IAnnotationModel#removeAnnotation(Annotation)
*/
public void removeAnnotation(Annotation aAnnotation) {
if (aAnnotation instanceof R4EAnnotation) {
final R4EAnnotation remAnnotation = (R4EAnnotation) aAnnotation;
fAnnotationsMap.remove(remAnnotation.getId());
final List<IReviewAnnotation> annotationList = fSortedAnnotationsListsMap.get(remAnnotation.getType());
int remAnnotationIndex = annotationList.indexOf(remAnnotation);
annotationList.remove(remAnnotation);
if (remAnnotationIndex < 0) {
fSortedAnnotationsIndexMap.remove(remAnnotation.getType()); //type list is now empty
} else {
fSortedAnnotationsIndexMap.put(remAnnotation.getType(), --remAnnotationIndex);
}
}
//Ignore any other Annotation type
}
/**
* Method fireModelChanged.
*
* @param aEvent
* AnnotationModelEvent
* @see org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel#fireModelChanged(AnnotationModelEvent)
*/
public void fireModelChanged(AnnotationModelEvent aEvent) {
aEvent.markSealed();
if (!aEvent.isEmpty()) {
for (IAnnotationModelListener listener : fAnnotationModelListeners) {
if (listener instanceof IAnnotationModelListenerExtension) {
((IAnnotationModelListenerExtension) listener).modelChanged(aEvent);
} else {
listener.modelChanged(this);
}
}
}
}
/**
* Method isAnnotationsAvailable.
*
* @param aType
* String
* @return boolean
*/
public boolean isAnnotationsAvailable(String aType) {
return true; //Always return available even if there are no annotations
}
/**
* Method getNextAnnotation.
*
* @param aType
* String
* @return IReviewAnnotation
* @see org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel#getNextAnnotation(String)
*/
public IReviewAnnotation getNextAnnotation(String aType) {
final List<IReviewAnnotation> annotationList = fSortedAnnotationsListsMap.get(aType);
if (null == annotationList || annotationList.size() == 0) {
return null; //empty list
} else {
int annotationIndex = fSortedAnnotationsIndexMap.get(aType);
++annotationIndex;
if (annotationIndex == annotationList.size()) {
annotationIndex = 0; //wrap around
}
fSortedAnnotationsIndexMap.put(aType, annotationIndex);
return annotationList.get(annotationIndex);
}
}
/**
* Method getPreviousAnnotation.
*
* @param aType
* String
* @return IReviewAnnotation
* @see org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel#getPreviousAnnotation(String)
*/
public IReviewAnnotation getPreviousAnnotation(String aType) {
final List<IReviewAnnotation> annotationList = fSortedAnnotationsListsMap.get(aType);
if (null == annotationList || annotationList.size() == 0) {
return null; //empty list
} else {
int annotationIndex = fSortedAnnotationsIndexMap.get(aType);
--annotationIndex;
if (annotationIndex < 0) {
annotationIndex = annotationList.size() - 1; //wrap around
}
fSortedAnnotationsIndexMap.put(aType, annotationIndex);
return annotationList.get(annotationIndex);
}
}
//Test Methods
/**
* Method findAnnotation.
*
* @param aType
* String
* @param aSourceElement
* Object
* @return IReviewAnnotation
* @see org.eclipse.mylyn.reviews.frame.ui.annotation.IReviewAnnotationModel#findAnnotation(String, Object)
*/
public IReviewAnnotation findAnnotation(String aType, Object aSourceElement) {
final List<IReviewAnnotation> annotationList = fSortedAnnotationsListsMap.get(aType);
for (IReviewAnnotation annotation : annotationList) {
if (((R4EAnnotation) annotation).getSourceElement().equals(aSourceElement)) {
return annotation;
}
}
return null;
}
}