/******************************************************************************* * Copyright (c) 2015, 2016 Pivotal, Inc. * 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: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.editor.support.reconcile; 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.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.text.BadLocationException; 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.Region; import org.eclipse.jface.text.reconciler.DirtyRegion; import org.eclipse.jface.text.reconciler.IReconcilingStrategy; import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelExtension; import org.eclipse.jface.text.source.ISourceViewer; /** * Adapts our {@link IReconcileEngine} interface to an Eclipse {@link ReconcileStrategy}. * * @author Kris De Volder */ public class ReconcileStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension { public static final SeverityProvider DEFAULT_SEVERITY_PROVIDER = new DefaultSeverityProvider(); private ISourceViewer fViewer; private IReconcileEngine fEngine; private IDocument fDocument; private IProgressMonitor fProgressMonitor; private IProblemCollector fProblemCollector; public ReconcileStrategy(ISourceViewer viewer, IReconcileEngine engine) { this.fViewer = viewer; this.fEngine = engine; } /* * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile() */ public void initialReconcile() { reconcile(new Region(0, fDocument.getLength())); } /* * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion,org.eclipse.jface.text.IRegion) */ public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { try { IRegion startLineInfo= fDocument.getLineInformationOfOffset(subRegion.getOffset()); IRegion endLineInfo= fDocument.getLineInformationOfOffset(subRegion.getOffset() + Math.max(0, subRegion.getLength() - 1)); if (startLineInfo.getOffset() == endLineInfo.getOffset()) subRegion= startLineInfo; else subRegion= new Region(startLineInfo.getOffset(), endLineInfo.getOffset() + Math.max(0, endLineInfo.getLength() - 1) - startLineInfo.getOffset()); } catch (BadLocationException e) { subRegion= new Region(0, fDocument.getLength()); } reconcile(subRegion); } /* * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion) */ public void reconcile(IRegion region) { if (getAnnotationModel() == null || fProblemCollector == null) return; //Note: This isn't an 'incremental' reconciler. It always checks the whole document. The dirty // region is ignored. fEngine.reconcile(fDocument, fProblemCollector, fProgressMonitor); } /** * Returns the annotation model to be used by this reconcile strategy. * * @return the annotation model of the underlying editor input or * <code>null</code> if none could be determined */ protected IAnnotationModel getAnnotationModel() { return fViewer.getAnnotationModel(); } /* * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor) */ public final void setProgressMonitor(IProgressMonitor monitor) { fProgressMonitor= monitor; } @Override public void setDocument(IDocument document) { fDocument= document; fProblemCollector= createProblemCollector(); } protected IDocument getDocument() { return fDocument; } protected SeverityAwareProblemCollector createProblemCollector() { IAnnotationModel model= getAnnotationModel(); if (model == null) return null; return new SeverityAwareProblemCollector(model); } /** * Problem collector. */ private class SeverityAwareProblemCollector implements IProblemCollector { /** Annotation model. */ private IAnnotationModel fAnnotationModel; /** Annotations to add. */ private Map<Annotation, Position> fAddAnnotations; /** Lock object for modifying the annotations. */ private Object fLockObject; /** * Initializes this collector with the given annotation model. * * @param annotationModel the annotation model */ public SeverityAwareProblemCollector(IAnnotationModel annotationModel) { Assert.isLegal(annotationModel != null); fAnnotationModel= annotationModel; if (fAnnotationModel instanceof ISynchronizable) fLockObject= ((ISynchronizable)fAnnotationModel).getLockObject(); else fLockObject= fAnnotationModel; } public void accept(ReconcileProblem problem) { ProblemSeverity severity = getSeverities().getSeverity(problem); String annotationType = ReconcileProblemAnnotation.getAnnotationType(severity); if (annotationType!=null) { fAddAnnotations.put(new ReconcileProblemAnnotation(annotationType, problem), new Position(problem.getOffset(), problem.getLength())); } } public void beginCollecting() { getSeverities().startReconciling(); fAddAnnotations= new HashMap<Annotation, Position>(); } public void endCollecting() { List<Annotation> toRemove= new ArrayList<Annotation>(); synchronized (fLockObject) { @SuppressWarnings("unchecked") Iterator<Annotation> iter= fAnnotationModel.getAnnotationIterator(); while (iter.hasNext()) { Annotation annotation= iter.next(); if (ReconcileProblemAnnotation.TYPES.contains(annotation.getType())) toRemove.add(annotation); } Annotation[] annotationsToRemove= toRemove.toArray(new Annotation[toRemove.size()]); if (fAnnotationModel instanceof IAnnotationModelExtension) ((IAnnotationModelExtension)fAnnotationModel).replaceAnnotations(annotationsToRemove, fAddAnnotations); else { for (int i= 0; i < annotationsToRemove.length; i++) fAnnotationModel.removeAnnotation(annotationsToRemove[i]); for (iter= fAddAnnotations.keySet().iterator(); iter.hasNext();) { Annotation annotation= iter.next(); fAnnotationModel.addAnnotation(annotation, fAddAnnotations.get(annotation)); } } } fAddAnnotations= null; } } protected SeverityProvider getSeverities() { return DEFAULT_SEVERITY_PROVIDER; } }