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; } }