/*******************************************************************************
* Copyright (c) 2010, 2011 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.docs.intent.client.ui.editor.annotation;
import com.google.common.collect.Maps;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.image.AbstractIntentImageAnnotation;
import org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.image.IntentExternalContentReferenceImageAnnotation;
import org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.image.IntentImageAnnotation;
import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.RepositoryAdapter;
import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatus;
import org.eclipse.mylyn.docs.intent.core.document.IntentGenericElement;
import org.eclipse.mylyn.docs.intent.core.modelingunit.ExternalContentReference;
import org.eclipse.mylyn.docs.intent.markup.markup.Image;
import org.eclipse.mylyn.docs.intent.serializer.ParsedElementPosition;
/**
* Handles the management of annotation models used by an IntentDocumentProvider.
*
* @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a>
*/
public class IntentAnnotationModelManager {
/**
* The handled annotationModel.
*/
private AnnotationModel annotationModel;
/**
* The currently handled compilation status list, mapped with its corresponding annotation (use for
* updating the annotations), sorted by each status's target.
*/
private Map<String, Map<CompilationStatus, Annotation>> handledCompilationStatus;
/**
* IntentAnnotationModelManager constructor.
*/
public IntentAnnotationModelManager() {
this.annotationModel = new AnnotationModel();
this.handledCompilationStatus = new HashMap<String, Map<CompilationStatus, Annotation>>();
}
/**
* Adds the given compilation status as an Annotation in the handled annotationModel at the given
* position.
*
* @param repositoryAdapter
* the Repository Adapter to use
* @param status
* the compilation status to add
* @param position
* the position of this annotation
*/
public void addAnnotationFromStatus(RepositoryAdapter repositoryAdapter, CompilationStatus status,
Position position) {
String targetID = repositoryAdapter.getIDFromElement(status.getTarget()).toString();
if (handledCompilationStatus.get(targetID) == null) {
handledCompilationStatus.put(targetID, Maps.<CompilationStatus, Annotation> newLinkedHashMap());
}
if (!(handledCompilationStatus.get(targetID).containsKey(status))) {
// We create an annotation from the status and add it to the annotation model
Annotation annotation = IntentAnnotationFactory.createAnnotationFromCompilationStatus(status);
addAnnotation(annotation, position);
handledCompilationStatus.get(targetID).put(status, annotation);
}
}
/**
* Adds the given annotation to the handled annotationModel at the given position.
*
* @param annotation
* the annotation to add
* @param position
* the position of this annotation
*/
private synchronized void addAnnotation(Annotation annotation, Position position) {
// synchronized(annotationModel.getLockObject()) {
annotationModel.addAnnotation(annotation, position);
// }
}
/**
* Returns the handled annotationModel.
*
* @return the handled annotationModel
*/
public IAnnotationModel getAnnotationModel() {
return annotationModel;
}
/**
* Removes all the compiler annotations from the handled annotationModel.
*/
public synchronized void removeAllCompilerAnnotations() {
@SuppressWarnings("unchecked")
Iterator<Annotation> annotationIterator = annotationModel.getAnnotationIterator();
while (annotationIterator.hasNext()) {
Annotation annotation = annotationIterator.next();
if (isCompilerAnnotation(annotation.getType())) {
annotationModel.removeAnnotation(annotation);
}
}
handledCompilationStatus.clear();
}
/**
* Removes all the compiler annotations associated to the given element that have changed or have been
* deleted.
*
* @param adapter
* the RepositoryAdapter to use for determine if the stored status are still valid
* @param element
* the element to inspect
*/
public synchronized void removeInvalidCompilerAnnotations(RepositoryAdapter adapter,
IntentGenericElement element) {
// For each compilationStatus associated to the given element
String elementID = adapter.getIDFromElement(element).toString();
Map<CompilationStatus, Annotation> statusToAnnotations = handledCompilationStatus.get(elementID);
if (statusToAnnotations != null) {
Iterator<Entry<CompilationStatus, Annotation>> statusToAnnotationsIterator = statusToAnnotations
.entrySet().iterator();
while (statusToAnnotationsIterator.hasNext()) {
Entry<CompilationStatus, Annotation> statusToAnnotation = statusToAnnotationsIterator.next();
boolean removeCurrentStatus = statusToAnnotation.getKey() == null
|| statusToAnnotation.getKey().getTarget() == null;
if (!removeCurrentStatus && isCompilerAnnotation(statusToAnnotation.getValue().getType())) {
// If the currentElement doesn't contain this status any more
if (!element.getCompilationStatus().contains(statusToAnnotation.getKey())) {
removeCurrentStatus = true;
}
}
if (removeCurrentStatus) {
annotationModel.removeAnnotation(statusToAnnotation.getValue());
statusToAnnotationsIterator.remove();
}
}
}
}
/**
* Creates a syntax error annotation at the given offset, of the given length.
*
* @param message
* the message associated to this syntax error
* @param offset
* offset of the syntax error annotation
* @param length
* length of the syntax error annotation.
*/
public void createSyntaxErrorAnnotation(String message, int offset, int length) {
IntentAnnotation syntaxErrorAnnotation = IntentAnnotationFactory.createSyntaxErrorAnnotation();
syntaxErrorAnnotation.setText(message);
Position position = new Position(offset, length);
addAnnotation(syntaxErrorAnnotation, position);
}
/**
* Removes all the syntax error annotations from the manage annotation model.
*/
public synchronized void removeSyntaxErrorsAnnotations() {
@SuppressWarnings("unchecked")
Iterator<Annotation> annotationIterator = (Iterator<Annotation>)annotationModel
.getAnnotationIterator();
while (annotationIterator.hasNext()) {
Annotation next = annotationIterator.next();
if (IntentAnnotationFactory.INTENT_ANNOT_SYNTAX_ERROR.equals(next.getType())) {
annotationModel.removeAnnotation(next);
}
}
}
/**
* Return true if the given annotationType indicates a compileAnnotation, false otherwise.
*
* @param type
* the type of an annotation
* @return true if the given annotationType indicates a compileAnnotation, false otherwise
*/
private boolean isCompilerAnnotation(String type) {
return IntentAnnotationFactory.INTENT_ANNOT_COMPILER_ERROR.equals(type)
|| IntentAnnotationFactory.INTENT_ANNOT_COMPILER_WARNING.equals(type)
|| IntentAnnotationFactory.INTENT_ANNOT_GENERAL_INFO.equals(type)
|| IntentAnnotationFactory.INTENT_ANNOT_SYNC_WARNING.equals(type);
}
/**
* Updates (e.g. creates or redraw) an
* {@link org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.image.AbstractIntentImageAnnotation}
* corresponding to the given {@link ExternalContentReference} or {@link Image}.
*
* @param elementToRender
* the {@link ExternalContentReference} content to render
* @param intentPosition
* the {@link ParsedElementPosition} of this instruction
*/
public void updateAnnotationFromElementToRender(EObject elementToRender,
ParsedElementPosition intentPosition) {
if (intentPosition != null) {
// Step 1: search for an already existing annotation
boolean foundAlredyExistingAnnotation = false;
Iterator<?> annotationIterator = getAnnotationModel().getAnnotationIterator();
while (annotationIterator.hasNext() && !foundAlredyExistingAnnotation) {
Object annotation = annotationIterator.next();
if (annotation instanceof IntentExternalContentReferenceImageAnnotation
&& elementToRender instanceof ExternalContentReference) {
if (((IntentExternalContentReferenceImageAnnotation)annotation)
.getExternalContentReference().getUri()
.equals(((ExternalContentReference)elementToRender).getUri())) {
// Make sure the matching annotation will be redrawn at next paint
foundAlredyExistingAnnotation = true;
((AbstractIntentImageAnnotation)annotation).setImageShouldBeRedrawn(true);
}
} else if (annotation instanceof IntentImageAnnotation && elementToRender instanceof Image) {
// Make sure the matching annotation will be redrawn at next paint
foundAlredyExistingAnnotation = true;
((AbstractIntentImageAnnotation)annotation).setImageShouldBeRedrawn(true);
}
}
// Step 2: create a new annotation if none found
if (!foundAlredyExistingAnnotation) {
if (elementToRender instanceof ExternalContentReference) {
getAnnotationModel()
.addAnnotation(
IntentAnnotationFactory
.createImageAnnotation((ExternalContentReference)elementToRender),
new Position(intentPosition.getOffset() + intentPosition.getLength(), 0));
} else if (elementToRender instanceof Image) {
getAnnotationModel().addAnnotation(
IntentAnnotationFactory.createImageAnnotation((Image)elementToRender),
new Position(intentPosition.getOffset() + intentPosition.getLength(), 0));
}
}
}
}
}