package de.ovgu.cide.language.jdt.editor.inlineprojection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationAccessExtension;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.projection.AnnotationBag;
/**
* Strategy for managing annotation summaries for collapsed ranges.
*
* @since 3.0
*/
@SuppressWarnings("unchecked")
class InlineProjectionSummary {
private class Summarizer extends Thread {
private boolean fReset = true;
/**
* Creates a new thread.
*
* @param monitor
* the progress monitor
*/
public Summarizer(IProgressMonitor monitor) {
fProgressMonitor = monitor;
setDaemon(true);
start();
}
/**
* Resets the thread.
*/
public void reset() {
fReset = true;
}
/*
* @see java.lang.Thread#run()
*/
public void run() {
while (true) {
synchronized (fLock) {
if (!fReset)
break;
fReset = false;
}
internalUpdateSummaries(fProgressMonitor);
}
synchronized (fLock) {
fSummarizer = null;
}
}
}
private InlineProjectionJavaViewer fProjectionViewer;
private IAnnotationModel fAnnotationModel;
private IAnnotationAccess fAnnotationAccess;
private List fConfiguredAnnotationTypes;
private Object fLock = new Object();
private IProgressMonitor fProgressMonitor;
private volatile Summarizer fSummarizer;
/**
* Creates a new projection summary.
*
* @param projectionViewer
* the projection viewer
* @param annotationAccess
* the annotation access
*/
public InlineProjectionSummary(InlineProjectionJavaViewer projectionViewer,
IAnnotationAccess annotationAccess) {
super();
fProjectionViewer = projectionViewer;
fAnnotationAccess = annotationAccess;
}
/**
* Adds the given annotation type. For now on, annotations of that type are
* also reflected in their enclosing collapsed regions.
*
* @param annotationType
* the annotation type to add
*/
public void addAnnotationType(String annotationType) {
synchronized (fLock) {
if (fConfiguredAnnotationTypes == null) {
fConfiguredAnnotationTypes = new ArrayList();
fConfiguredAnnotationTypes.add(annotationType);
} else if (!fConfiguredAnnotationTypes.contains(annotationType))
fConfiguredAnnotationTypes.add(annotationType);
}
}
/**
* Removes the given annotation. Annotation of that type are no longer
* reflected in their enclosing collapsed region.
*
* @param annotationType
* the annotation type to remove
*/
public void removeAnnotationType(String annotationType) {
synchronized (fLock) {
if (fConfiguredAnnotationTypes != null) {
fConfiguredAnnotationTypes.remove(annotationType);
if (fConfiguredAnnotationTypes.size() == 0)
fConfiguredAnnotationTypes = null;
}
}
}
/**
* Forces an updated of the annotation summary.
*
* @param monitor
* the progress monitor
*/
public void updateSummaries(IProgressMonitor monitor) {
synchronized (fLock) {
if (fConfiguredAnnotationTypes != null) {
if (fSummarizer == null)
fSummarizer = new Summarizer(monitor);
fSummarizer.reset();
}
}
}
private void internalUpdateSummaries(IProgressMonitor monitor) {
Object previousLockObject = null;
fAnnotationModel = fProjectionViewer.getVisualAnnotationModel();
if (fAnnotationModel == null)
return;
try {
IDocument document = fProjectionViewer.getDocument();
if (document instanceof ISynchronizable
&& fAnnotationModel instanceof ISynchronizable) {
ISynchronizable sync = (ISynchronizable) fAnnotationModel;
previousLockObject = sync.getLockObject();
sync
.setLockObject(((ISynchronizable) document)
.getLockObject());
}
removeSummaries(monitor);
createSummaries(monitor);
} finally {
if (fAnnotationModel instanceof ISynchronizable) {
ISynchronizable sync = (ISynchronizable) fAnnotationModel;
sync.setLockObject(previousLockObject);
}
fAnnotationModel = null;
}
}
private boolean isCanceled(IProgressMonitor monitor) {
return monitor != null && monitor.isCanceled();
}
private void removeSummaries(IProgressMonitor monitor) {
IAnnotationModelExtension extension = null;
List bags = null;
if (fAnnotationModel instanceof IAnnotationModelExtension) {
extension = (IAnnotationModelExtension) fAnnotationModel;
bags = new ArrayList();
}
Iterator e = fAnnotationModel.getAnnotationIterator();
while (e.hasNext()) {
Annotation annotation = (Annotation) e.next();
if (annotation instanceof AnnotationBag) {
if (bags == null)
fAnnotationModel.removeAnnotation(annotation);
else
bags.add(annotation);
}
if (isCanceled(monitor))
return;
}
if (bags != null && bags.size() > 0) {
Annotation[] deletions = new Annotation[bags.size()];
bags.toArray(deletions);
if (!isCanceled(monitor))
extension.replaceAnnotations(deletions, null);
}
}
private void createSummaries(IProgressMonitor monitor) {
InlineProjectionAnnotationModel model = fProjectionViewer
.getInlineProjectionAnnotationModel();
if (model == null)
return;
Map additions = new HashMap();
Iterator e = model.getAnnotationIterator();
while (e.hasNext()) {
InlineProjectionAnnotation projection = (InlineProjectionAnnotation) e
.next();
if (projection.isCollapsed()) {
Position position = model.getPosition(projection);
if (position != null) {
IRegion[] summaryRegions = fProjectionViewer
.computeCollapsedRegions(position);
if (summaryRegions != null) {
Position summaryAnchor = fProjectionViewer
.computeCollapsedRegionAnchor(position);
if (summaryAnchor != null)
createSummary(additions, summaryRegions,
summaryAnchor);
}
}
}
if (isCanceled(monitor))
return;
}
if (additions.size() > 0) {
if (fAnnotationModel instanceof IAnnotationModelExtension) {
IAnnotationModelExtension extension = (IAnnotationModelExtension) fAnnotationModel;
if (!isCanceled(monitor))
extension.replaceAnnotations(null, additions);
} else {
Iterator e1 = additions.keySet().iterator();
while (e1.hasNext()) {
AnnotationBag bag = (AnnotationBag) e1.next();
Position position = (Position) additions.get(bag);
if (isCanceled(monitor))
return;
fAnnotationModel.addAnnotation(bag, position);
}
}
}
}
private void createSummary(Map additions, IRegion[] summaryRegions,
Position summaryAnchor) {
int size = 0;
Map map = null;
synchronized (fLock) {
if (fConfiguredAnnotationTypes != null) {
size = fConfiguredAnnotationTypes.size();
map = new HashMap();
for (int i = 0; i < size; i++) {
String type = (String) fConfiguredAnnotationTypes.get(i);
map.put(type, new AnnotationBag(type));
}
}
}
if (map == null)
return;
IAnnotationModel model = fProjectionViewer.getAnnotationModel();
if (model == null)
return;
Iterator e = model.getAnnotationIterator();
while (e.hasNext()) {
Annotation annotation = (Annotation) e.next();
AnnotationBag bag = findBagForType(map, annotation.getType());
if (bag != null) {
Position position = model.getPosition(annotation);
if (includes(summaryRegions, position))
bag.add(annotation);
}
}
for (int i = 0; i < size; i++) {
AnnotationBag bag = (AnnotationBag) map
.get(fConfiguredAnnotationTypes.get(i));
if (!bag.isEmpty())
additions.put(bag, new Position(summaryAnchor.getOffset(),
summaryAnchor.getLength()));
}
}
private AnnotationBag findBagForType(Map bagMap, String annotationType) {
AnnotationBag bag = (AnnotationBag) bagMap.get(annotationType);
if (bag == null
&& fAnnotationAccess instanceof IAnnotationAccessExtension) {
IAnnotationAccessExtension extension = (IAnnotationAccessExtension) fAnnotationAccess;
Object[] superTypes = extension.getSupertypes(annotationType);
for (int i = 0; i < superTypes.length && bag == null; i++) {
bag = (AnnotationBag) bagMap.get(superTypes[i]);
}
}
return bag;
}
private boolean includes(IRegion[] regions, Position position) {
for (int i = 0; i < regions.length; i++) {
IRegion region = regions[i];
if (position != null
&& !position.isDeleted()
&& region.getOffset() <= position.getOffset()
&& position.getOffset() + position.getLength() <= region
.getOffset()
+ region.getLength())
return true;
}
return false;
}
}