| /******************************************************************************* |
| * Copyright (c) 2001, 2004 IBM Corporation 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 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Jens Lukowski/Innoopract - initial renaming/restructuring |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.sse.ui; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IDocumentPartitioner; |
| import org.eclipse.jface.text.ITextInputListener; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.ITypedRegion; |
| 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.reconciler.Reconciler; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.wst.sse.core.IModelLifecycleListener; |
| import org.eclipse.wst.sse.core.IModelManager; |
| import org.eclipse.wst.sse.core.IStructuredModel; |
| import org.eclipse.wst.sse.core.ModelLifecycleEvent; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.events.IStructuredDocumentListener; |
| import org.eclipse.wst.sse.core.events.NewDocumentEvent; |
| import org.eclipse.wst.sse.core.events.NoChangeEvent; |
| import org.eclipse.wst.sse.core.events.RegionChangedEvent; |
| import org.eclipse.wst.sse.core.events.RegionsReplacedEvent; |
| import org.eclipse.wst.sse.core.events.StructuredDocumentRegionsReplacedEvent; |
| import org.eclipse.wst.sse.core.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; |
| import org.eclipse.wst.sse.core.text.IStructuredDocumentRegionList; |
| import org.eclipse.wst.sse.ui.internal.Logger; |
| import org.eclipse.wst.sse.ui.internal.reconcile.IStructuredReconcilingStrategy; |
| import org.eclipse.wst.sse.ui.internal.reconcile.validator.ValidatorStrategy; |
| import org.eclipse.wst.sse.ui.util.Assert; |
| |
| |
| /** |
| * Reconciler that maps different partitions to different strategies. |
| * Strategies contain one or more Steps Steps contain code that validates |
| * dirty regions |
| * |
| * Is aware of StructuredDocumentEvents which determine if a reconcile should |
| * be done or not. On partition change events in the document, all strategies |
| * are called. |
| * |
| * @author pavery |
| */ |
| public class StructuredTextReconciler extends Reconciler implements IStructuredDocumentListener, IModelLifecycleListener { |
| |
| /** |
| * Reconclies the entire document when the document in the viewer is |
| * changed. This happens when the document is initially opened, as well as |
| * after a save-as. |
| * |
| * Also see processPostModelEvent(...) for similar behavior when document |
| * for the model is changed. |
| */ |
| private class SourceTextInputListener implements ITextInputListener { |
| |
| public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { |
| // do nothing |
| } |
| |
| public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { |
| |
| // don't bother if reconciler not installed |
| if (isInstalled()) { |
| setDocument(newInput); |
| setDocumentOnAllStrategies(newInput); |
| setEntireDocumentDirty(newInput); |
| } |
| } |
| } |
| |
| /** |
| * Cancels any running reconcile operations via progress monitor. Ensures |
| * that strategies are released on close of the editor. |
| */ |
| private class SourceWidgetDisposeListener implements DisposeListener { |
| |
| public void widgetDisposed(DisposeEvent e) { |
| |
| getLocalProgressMonitor().setCanceled(true); |
| |
| // release all strategies |
| if (fDefaultStrategy != null && fDefaultStrategy instanceof IReleasable) { |
| ((IReleasable) fDefaultStrategy).release(); |
| fDefaultStrategy = null; |
| } |
| if (!fStrategyTypes.isEmpty()) { |
| Iterator it = fStrategyTypes.iterator(); |
| IReconcilingStrategy strategy = null; |
| while (it.hasNext()) { |
| strategy = getReconcilingStrategy((String) it.next()); |
| if (strategy instanceof IReleasable) { |
| ((IReleasable) strategy).release(); |
| strategy = null; |
| } |
| } |
| } |
| } |
| } |
| |
| public static final String TRACE_FILTER = "reconciler"; //$NON-NLS-1$ |
| |
| /** strategy called for unmapped partitions */ |
| IReconcilingStrategy fDefaultStrategy; |
| |
| /** to cancel any long running reconciles if someone closes the editor */ |
| private SourceWidgetDisposeListener fDisposeListener = null; |
| |
| /** |
| * set true after first install to prevent duplicate work done in the |
| * install method (since install gets called multiple times) |
| */ |
| private boolean fIsInstalled = false; |
| |
| /** local queue of dirty regions (created here) to be reconciled */ |
| private List fLocalDirtyRegionQueue = null; |
| |
| /** document that this reconciler works on */ |
| private IDocument fLocalDocument = null; |
| |
| // use our own local for now until we resolve abstract calling it on every |
| // document change |
| // resulting in some of our strategies getting cut short and not |
| // adding/removing annotations correctly |
| private IProgressMonitor fLocalProgressMonitor = null; |
| |
| /** local copy of model manager */ |
| private IModelManager fModelManager = null; |
| |
| /** the list of partition types for which there are strategies */ |
| List fStrategyTypes = null; |
| |
| /** for initital reconcile when document is opened */ |
| private SourceTextInputListener fTextInputListener = null; |
| |
| /** flag set via structured document events */ |
| private boolean fValidationNeeded = false; |
| |
| /** |
| * the strategy that runs validators contributed via reconcileValidator |
| * ext point |
| */ |
| private ValidatorStrategy fValidatorStrategy; |
| |
| /** |
| * Creates a new StructuredTextReconciler |
| */ |
| public StructuredTextReconciler() { |
| super(); |
| configure(); |
| } |
| |
| /** |
| * This method reduces the dirty region queue to the least common dirty |
| * region. (the region that overlaps all dirty regions) |
| * |
| * @return a condensed DirtyRegion representing all that was in the queue |
| * at the time this was called, or <code>null</code> if the |
| * queue is empty |
| */ |
| private DirtyRegion compactDirtyRegionQueue() { |
| |
| DirtyRegion result = null; |
| StringBuffer traceInfo = new StringBuffer(); |
| if (Logger.isTracing(TRACE_FILTER)) |
| traceInfo.append("[reconciler] COMPACTING STARTING... localDirtyRegionQueue.size():" + fLocalDirtyRegionQueue.size()); //$NON-NLS-1$ |
| |
| if (fLocalDirtyRegionQueue.size() == 1) |
| return (DirtyRegion) fLocalDirtyRegionQueue.remove(0); |
| |
| if (!fLocalDirtyRegionQueue.isEmpty()) { |
| result = formNewDirtyRegion(traceInfo); |
| } |
| |
| return result; |
| } |
| |
| private void configure() { |
| fStrategyTypes = new ArrayList(); |
| |
| // we are always incremental |
| setIsIncrementalReconciler(true); |
| setDelay(500); |
| //setProgressMonitor(new NullProgressMonitor()); |
| setLocalProgressMonitor(new NullProgressMonitor()); |
| fDisposeListener = new SourceWidgetDisposeListener(); |
| fTextInputListener = new SourceTextInputListener(); |
| fLocalDirtyRegionQueue = new ArrayList(); |
| } |
| |
| private DirtyRegion createDirtyRegion(int offset, int length, String type) { |
| DirtyRegion durty = null; |
| IDocument doc = getDocument(); |
| // safety for BLE |
| int docLen = doc.getLength(); |
| if (offset + length > docLen) |
| length = docLen - offset; |
| |
| if (doc != null) { |
| try { |
| durty = new DirtyRegion(offset, length, type, doc.get(offset, length)); |
| } catch (BadLocationException e) { |
| e.printStackTrace(); |
| } |
| } |
| return durty; |
| } |
| |
| private DirtyRegion createDirtyRegion(ITypedRegion tr, String type) { |
| return createDirtyRegion(tr.getOffset(), tr.getLength(), type); |
| } |
| |
| private DirtyRegion formNewDirtyRegion(StringBuffer traceInfo) { |
| DirtyRegion result; |
| int min = -1; |
| int max = -1; |
| DirtyRegion dr = null; |
| for (int i = 0; i < fLocalDirtyRegionQueue.size(); i++) { |
| dr = (DirtyRegion) fLocalDirtyRegionQueue.get(i); |
| if (dr == null) |
| continue; |
| |
| if (Logger.isTracing(TRACE_FILTER)) |
| traceInfo.append("\r\n\r\n -> compacting dirty region (" + i + ")" + " start:" + dr.getOffset() + " length:" + dr.getLength()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| |
| //possibly expand the dirty region start |
| if (min == -1 || min > dr.getOffset()) |
| min = dr.getOffset(); |
| // possibly expand the dirty region end |
| if (max == -1 || max < dr.getOffset() + dr.getLength()) |
| max = dr.getOffset() + dr.getLength(); |
| } |
| fLocalDirtyRegionQueue.clear(); |
| result = (min != -1) ? createDirtyRegion(min, max - min, DirtyRegion.INSERT) : null; |
| |
| if (Logger.isTracing(TRACE_FILTER)) { |
| traceInfo.append("\r\n\r\nCOMPACTING DONE... dirtyRangeStart:" + min + " dirtyRangeEnd:" + max + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| Logger.trace(TRACE_FILTER, traceInfo.toString()); |
| } |
| return result; |
| } |
| |
| /** |
| * Gets a strategy that is made to handle the given dirtyRegion. |
| * |
| * @param dirtyRegion |
| * @return a strategy that is made to handle the given dirtyRegion, or the |
| * default strategy for this reconciler if there isn't one |
| */ |
| protected IReconcilingStrategy getAppropriateStrategy(DirtyRegion dirtyRegion) { |
| String[] partitions = getPartitions(dirtyRegion); |
| // for now just grab first partition type in dirty region |
| IReconcilingStrategy rs = null; |
| if (partitions.length > 0) |
| rs = getReconcilingStrategy(partitions[0]); |
| return rs != null ? rs : fDefaultStrategy; |
| } |
| |
| /** |
| * Gets the default strategy for this reconciler. |
| * |
| * @return the default strategy |
| */ |
| protected IReconcilingStrategy getDefaultStrategy() { |
| return fDefaultStrategy; |
| } |
| |
| /** |
| * We use our own local progress monitor to cancel long running |
| * strategies/steps. Currently used when widget is disposed (user is |
| * trying to close the editor), and on uninstall. |
| * |
| * @return the local progress monitor |
| */ |
| IProgressMonitor getLocalProgressMonitor() { |
| return fLocalProgressMonitor; |
| } |
| |
| /** |
| * Avoid excessive calls to Platform.getPlugin(ModelPlugin.ID) |
| * |
| * @return sse model manager |
| */ |
| protected IModelManager getModelManager() { |
| |
| if (this.fModelManager == null) |
| this.fModelManager = StructuredModelManager.getInstance().getModelManager(); |
| return this.fModelManager; |
| } |
| |
| /** |
| * assumes isInstalled() == true |
| * |
| * @return the document partitioner for the document this reconciler is |
| * working on. |
| */ |
| protected IDocumentPartitioner getPartitioner() { |
| return getDocument().getDocumentPartitioner(); |
| } |
| |
| /** |
| * Utility method to get partitions of a dirty region |
| * |
| * @param dirtyRegion |
| * @return |
| */ |
| protected String[] getPartitions(DirtyRegion dirtyRegion) { |
| ITypedRegion[] regions = getPartitioner().computePartitioning(dirtyRegion.getOffset(), dirtyRegion.getLength()); |
| String[] partitions = new String[regions.length]; |
| for (int i = 0; i < regions.length; i++) |
| partitions[i] = regions[i].getType(); |
| return partitions; |
| } |
| |
| /** |
| * Remember to release model after use!! |
| * |
| * @return |
| */ |
| public IStructuredModel getStructuredModelForRead(IDocument doc) { |
| |
| IStructuredModel sModel = null; |
| if (doc != null) |
| sModel = getModelManager().getExistingModelForRead(doc); |
| return sModel; |
| } |
| |
| /** |
| * Get the strategy that runs validators from the reconcileValidator |
| * extension point. |
| * |
| * @param the |
| * ValidatorStrategy |
| */ |
| public ValidatorStrategy getValidatorStrategy() { |
| return fValidatorStrategy; |
| } |
| |
| |
| |
| /** |
| * @param document |
| */ |
| private void hookUpModelLifecycleListener(IDocument document) { |
| IStructuredModel sModel = getStructuredModelForRead(document); |
| try { |
| if (sModel != null) { |
| sModel.addModelLifecycleListener(this); |
| } |
| } finally { |
| if (sModel != null) |
| sModel.releaseFromRead(); |
| } |
| } |
| |
| protected void initialProcess() { |
| // only happens ONCE on first dirty region in queue (not on doucment |
| // open) |
| // not useful to us at the moment |
| } |
| |
| /** |
| * @see org.eclipse.jface.text.reconciler.IReconciler#install(ITextViewer) |
| */ |
| public void install(ITextViewer textViewer) { |
| // we might be called multiple times with the same viewer, |
| // maybe after being uninstalled as well, so track separately |
| if (!isInstalled()) { |
| |
| super.install(textViewer); |
| |
| textViewer.getTextWidget().addDisposeListener(fDisposeListener); |
| textViewer.addTextInputListener(fTextInputListener); |
| |
| getLocalProgressMonitor().setCanceled(false); |
| |
| setInstalled(true); |
| setValidationNeeded(true); |
| } |
| } |
| |
| /** |
| * @param dirtyRegion |
| * @return |
| */ |
| private boolean isEntireDocumentChange(DirtyRegion dirtyRegion) { |
| return getDocument().getLength() == dirtyRegion.getLength(); |
| } |
| |
| /** |
| * The viewer has been set on this Reconciler. |
| * |
| * @return true if the viewer has been set on this Reconciler, false |
| * otherwise. |
| */ |
| public boolean isInstalled() { |
| return fIsInstalled; |
| } |
| |
| /** |
| * @return Returns the needsValidation. |
| */ |
| public boolean isValidationNeeded() { |
| return fValidationNeeded; |
| } |
| |
| public void newModel(NewDocumentEvent structuredDocumentEvent) { |
| // do nothing |
| } |
| |
| public void noChange(NoChangeEvent structuredDocumentEvent) { |
| // do nothing |
| } |
| |
| public void nodesReplaced(StructuredDocumentRegionsReplacedEvent structuredDocumentEvent) { |
| Logger.trace(StructuredTextReconciler.TRACE_FILTER, "[trace reconciler] >StructuredTextReconciler: *NODES REPLACED"); //$NON-NLS-1$ |
| |
| // if partition changed, create a full document dirty region |
| // (causes processAll) |
| DirtyRegion dr = partitionChanged(structuredDocumentEvent) ? createDirtyRegion(0, getDocument().getLength(), DirtyRegion.INSERT) : createDirtyRegion(structuredDocumentEvent.getOriginalStart(), structuredDocumentEvent.getLength(), DirtyRegion.INSERT); |
| fLocalDirtyRegionQueue.add(dr); |
| |
| setValidationNeeded(true); |
| } |
| |
| /** |
| * Checks if the StructuredDocumentEvent involved a partition change. If |
| * there's a partition change, we know we should run all strategies just |
| * to be sure we cover the new regions and remove obsolete annotations. |
| * |
| * A primitive check for now. |
| * |
| * @param structuredDocumentEvent |
| * @return |
| */ |
| private boolean partitionChanged(StructuredDocumentRegionsReplacedEvent structuredDocumentEvent) { |
| boolean changed = false; |
| |
| IDocumentPartitioner partitioner = structuredDocumentEvent.getStructuredDocument().getDocumentPartitioner(); |
| |
| IStructuredDocumentRegionList oldNodes = structuredDocumentEvent.getOldStructuredDocumentRegions(); |
| IStructuredDocumentRegionList newNodes = structuredDocumentEvent.getNewStructuredDocumentRegions(); |
| |
| IStructuredDocumentRegion oldNode = (oldNodes.getLength() > 0) ? oldNode = oldNodes.item(0) : null; |
| IStructuredDocumentRegion newNode = (newNodes.getLength() > 0) ? newNodes.item(0) : null; |
| |
| if (oldNode != null && newNode != null) |
| changed = partitioner.getContentType(oldNode.getStartOffset()).equals(partitioner.getContentType(newNode.getStartOffset())); |
| |
| return changed; |
| } |
| |
| /** |
| * We keep a local copy of the dirty region queue for compacting. |
| * |
| * @see org.eclipse.jface.text.reconciler.AbstractReconciler#process(org.eclipse.jface.text.reconciler.DirtyRegion) |
| */ |
| protected void process(DirtyRegion dirtyRegion) { |
| // this is called from the background thread in AbstractReconciler |
| // called here so that it only kick off after .5 seconds |
| // but fNeedsValidation flag is set in structuredDoucmentsEvents below |
| if (isInstalled()) { |
| runStrategies(); |
| } |
| } |
| |
| /** |
| * Process the entire StructuredDocument. Much more resource intensive |
| * than simply running a strategy on a dirty region. |
| */ |
| protected void processAll() { |
| if (!isInstalled()) |
| return; |
| Logger.trace(StructuredTextReconciler.TRACE_FILTER, "[trace reconciler] >StructuredTextReconciler: PROCESSING ALL"); //$NON-NLS-1$ |
| IDocument doc = getDocument(); |
| DirtyRegion durty = null; |
| ITypedRegion tr[] = doc.getDocumentPartitioner().computePartitioning(0, doc.getLength()); |
| IReconcilingStrategy s = null; |
| for (int i = 0; i < tr.length; i++) { |
| durty = createDirtyRegion(tr[i], DirtyRegion.INSERT); |
| s = getReconcilingStrategy(tr[i].getType()); |
| if (s != null) { |
| if (s instanceof IStructuredReconcilingStrategy) |
| ((IStructuredReconcilingStrategy) s).reconcile(durty, durty, true); |
| else |
| s.reconcile(durty, durty); |
| } |
| // run validator strategy every time, it figures out if it has a |
| // validator for this partition |
| // pass in true for "refreshAll" flag = true indicating that the |
| // entire document is being reconciled, only do it once |
| if (fValidatorStrategy != null) |
| fValidatorStrategy.reconcile(tr[i], durty, true); |
| } |
| // we ran the whole doc already now we can reset the strategies |
| resetStrategies(); |
| } |
| |
| /** |
| * Process a subsection of the document. |
| */ |
| protected void processPartial(DirtyRegion durty) { |
| if (!isInstalled()) |
| return; |
| IDocument doc = getDocument(); |
| HashSet alreadyRan = new HashSet(); |
| ITypedRegion tr[] = doc.getDocumentPartitioner().computePartitioning(durty.getOffset(), durty.getLength()); |
| IReconcilingStrategy s = null; |
| for (int i = 0; i < tr.length; i++) { |
| durty = createDirtyRegion(tr[i], DirtyRegion.INSERT); |
| // keeping track of already ran might not be the way to do it... |
| if (!alreadyRan.contains(tr[i].getType())) { |
| alreadyRan.add(tr[i].getType()); |
| s = getReconcilingStrategy(tr[i].getType()); |
| if (s != null) |
| s.reconcile(durty, durty); |
| } |
| // run validator strategy every time, it figures out if it has a |
| // validator for this parition |
| if (fValidatorStrategy != null) |
| fValidatorStrategy.reconcile(tr[i], durty, false); |
| } |
| resetStrategies(); |
| } |
| |
| /** |
| * @see org.eclipse.wst.sse.core.IModelLifecycleListener#processPostModelEvent(org.eclipse.wst.sse.core.ModelLifecycleEvent) |
| */ |
| public void processPostModelEvent(ModelLifecycleEvent event) { |
| |
| // if underlying StructuredDocument changed, need to reconnect it |
| // here... |
| // ex. file is modified outside the workbench |
| if (event.getType() == ModelLifecycleEvent.MODEL_DOCUMENT_CHANGED) { |
| |
| // check that it's this model that changed |
| IStructuredModel thisModel = getStructuredModelForRead(getDocument()); |
| try { |
| if (thisModel != null && event.getModel().equals(thisModel)) { |
| |
| IStructuredDocument sDoc = event.getModel().getStructuredDocument(); |
| |
| if (Logger.isTracing(TRACE_FILTER)) { |
| System.out.println("======================================================"); //$NON-NLS-1$ |
| System.out.println("StructuredTextReconciler: DOCUMENT MODEL CHANGED TO: "); //$NON-NLS-1$ |
| System.out.println(sDoc.get()); |
| System.out.println("======================================================"); //$NON-NLS-1$ |
| } |
| setDocument(sDoc); |
| |
| // propagate document change |
| setDocumentOnAllStrategies(sDoc); |
| |
| // ensure that the document is re-reconciled |
| setEntireDocumentDirty(sDoc); |
| } |
| } finally { |
| if (thisModel != null) |
| thisModel.releaseFromRead(); |
| } |
| } |
| } |
| |
| /** |
| * @see org.eclipse.wst.sse.core.IModelLifecycleListener#processPreModelEvent(org.eclipse.wst.sse.core.ModelLifecycleEvent) |
| */ |
| public void processPreModelEvent(ModelLifecycleEvent event) { |
| |
| if (event.getType() == ModelLifecycleEvent.MODEL_DOCUMENT_CHANGED) { |
| |
| // clear the dirty region queue |
| fLocalDirtyRegionQueue.clear(); |
| |
| // note: old annotations are removed via the strategies on |
| // AbstractStructuredTextReconcilingStrategy#setDocument(...) |
| } |
| } |
| |
| /** |
| * Reinitializes listeners and sets new document onall strategies. |
| * |
| * @see org.eclipse.jface.text.reconciler.AbstractReconciler#reconcilerDocumentChanged(IDocument) |
| */ |
| protected void reconcilerDocumentChanged(IDocument document) { |
| |
| // unhook old lifecycle listner |
| unhookModelLifecycleListener(fLocalDocument); |
| |
| // add new lifecycle listener |
| hookUpModelLifecycleListener(document); |
| |
| if (fLocalDocument != null && fLocalDocument instanceof IStructuredDocument) |
| ((IStructuredDocument) fLocalDocument).removeDocumentChangedListener(this); |
| |
| setDocument(document); |
| |
| if (document != null && document instanceof IStructuredDocument) |
| ((IStructuredDocument) fLocalDocument).addDocumentChangedListener(this); |
| |
| setDocumentOnAllStrategies(document); |
| } |
| |
| public void regionChanged(RegionChangedEvent structuredDocumentEvent) { |
| Logger.trace(StructuredTextReconciler.TRACE_FILTER, "[trace reconciler] >StructuredTextReconciler: *REGION CHANGED: \r\n\r\n created dirty region from flat model event >> :" + structuredDocumentEvent.getOriginalStart() + ":" + structuredDocumentEvent.getLength() + "\r\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| |
| String dirtyRegionType = structuredDocumentEvent.getDeletedText().equals("") ? DirtyRegion.INSERT : DirtyRegion.REMOVE; //$NON-NLS-1$ |
| DirtyRegion dr = createDirtyRegion(structuredDocumentEvent.getOriginalStart(), structuredDocumentEvent.getLength(), dirtyRegionType); |
| |
| fLocalDirtyRegionQueue.add(dr); |
| |
| setValidationNeeded(true); |
| } |
| |
| public void regionsReplaced(RegionsReplacedEvent structuredDocumentEvent) { |
| Logger.trace(StructuredTextReconciler.TRACE_FILTER, "[trace reconciler] >StructuredTextReconciler: *REGIONS REPLACED: \r\n\r\n created dirty region from flat model event >> :" + structuredDocumentEvent.getOriginalStart() + ":" + structuredDocumentEvent.getLength() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| |
| DirtyRegion dr = createDirtyRegion(structuredDocumentEvent.getOriginalStart(), structuredDocumentEvent.getLength(), DirtyRegion.INSERT); |
| fLocalDirtyRegionQueue.add(dr); |
| |
| setValidationNeeded(true); |
| } |
| |
| /** |
| * Resets any flags that were set (eg. flags set during processAll()) |
| */ |
| protected void resetStrategies() { |
| Iterator it = fStrategyTypes.iterator(); |
| String type = null; |
| while (it.hasNext()) { |
| type = (String) it.next(); |
| if (getReconcilingStrategy(type) instanceof IStructuredReconcilingStrategy) |
| ((IStructuredReconcilingStrategy) getReconcilingStrategy(type)).reset(); |
| } |
| } |
| |
| /** |
| * Runs the appropriate strategies on the dirty region queue. The |
| * reconciler currently handles these reconciling scenarios: |
| * |
| * <ul> |
| * <li>partition change</li> |
| * <li>routine text edits</li> |
| * <li>entire document change</li> |
| * <li>the default strategy</li> |
| * </ul> |
| * |
| */ |
| private void runStrategies() { |
| DirtyRegion dirtyRegion = null; |
| while (fDefaultStrategy != null && isInstalled() && isValidationNeeded()) { |
| // this flag may be set to true if more dirty regions come in |
| // while this method is running |
| setValidationNeeded(false); |
| Logger.trace(TRACE_FILTER, "start RUNNING STRATEGIES IN RECONCILER"); //$NON-NLS-1$ |
| dirtyRegion = compactDirtyRegionQueue(); |
| // will be null if there is nothing in the queue |
| if (dirtyRegion != null) { |
| Logger.trace(TRACE_FILTER, "RUNNING with dirty region:" + dirtyRegion.getOffset() + ":" + dirtyRegion.getLength()); //$NON-NLS-1$ //$NON-NLS-2$ |
| try { |
| if (isEntireDocumentChange(dirtyRegion)) |
| processAll(); |
| else |
| processPartial(dirtyRegion); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets the default reconciling strategy. |
| * |
| * @param strategy |
| */ |
| public void setDefaultStrategy(IReconcilingStrategy strategy) { |
| Assert.isNotNull(strategy, "Can't set default strategy to null"); //$NON-NLS-1$ |
| |
| fDefaultStrategy = strategy; |
| fDefaultStrategy.setDocument(getDocument()); |
| if (fDefaultStrategy instanceof IReconcilingStrategyExtension) |
| ((IReconcilingStrategyExtension) fDefaultStrategy).setProgressMonitor(getLocalProgressMonitor()); |
| } |
| |
| |
| public void setDocument(IDocument doc) { |
| // making sure local document is always up to date |
| // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3858 |
| fLocalDocument = doc; |
| setDocumentOnAllStrategies(doc); |
| } |
| |
| /** |
| * Propagates a new document to all strategies and steps. |
| * |
| * @param document |
| */ |
| protected void setDocumentOnAllStrategies(IDocument document) { |
| if (isInstalled()) { |
| // default strategies |
| if (fDefaultStrategy != null) |
| fDefaultStrategy.setDocument(document); |
| |
| // external validator strategy |
| if (fValidatorStrategy != null) |
| fValidatorStrategy.setDocument(document); |
| |
| // set document on all regular strategies |
| super.reconcilerDocumentChanged(document); |
| } |
| } |
| |
| protected void setEntireDocumentDirty(IDocument document) { |
| |
| // make the entire document dirty |
| // this also happens on a "save as" |
| if (document != null && isInstalled() && fLocalDirtyRegionQueue.size() == 0) { |
| |
| // since we're marking the entire doc dirty |
| fLocalDirtyRegionQueue.clear(); |
| |
| DirtyRegion entireDocument = createDirtyRegion(0, document.getLength(), DirtyRegion.INSERT); |
| fLocalDirtyRegionQueue.add(entireDocument); |
| |
| // set this so reconcile won't be "short circuited" |
| setValidationNeeded(true); |
| } |
| } |
| |
| /** |
| * @param isInstalled |
| * The isInstalled to set. |
| */ |
| public void setInstalled(boolean isInstalled) { |
| fIsInstalled = isInstalled; |
| } |
| |
| private void setLocalProgressMonitor(IProgressMonitor pm) { |
| fLocalProgressMonitor = pm; |
| // set on default strategy |
| if (fDefaultStrategy != null && fDefaultStrategy instanceof IReconcilingStrategyExtension) |
| ((IReconcilingStrategyExtension) fDefaultStrategy).setProgressMonitor(pm); |
| // set on all other strategies |
| if (!fStrategyTypes.isEmpty()) { |
| Iterator it = fStrategyTypes.iterator(); |
| String type = null; |
| while (it.hasNext()) { |
| type = (String) it.next(); |
| if (getReconcilingStrategy(type) instanceof IReconcilingStrategyExtension) |
| ((IReconcilingStrategyExtension) getReconcilingStrategy(type)).setProgressMonitor(pm); |
| } |
| } |
| } |
| |
| /** |
| * Sets the strategy for a given contentType (partitionType) |
| * |
| * @see org.eclipse.jface.text.reconciler.Reconciler#setReconcilingStrategy(org.eclipse.jface.text.reconciler.IReconcilingStrategy, |
| * java.lang.String) |
| */ |
| public void setReconcilingStrategy(IReconcilingStrategy strategy, String contentType) { |
| super.setReconcilingStrategy(strategy, contentType); |
| if (strategy != null) { |
| strategy.setDocument(fLocalDocument); |
| if (strategy instanceof IReconcilingStrategyExtension) { |
| ((IReconcilingStrategyExtension) strategy).setProgressMonitor(getLocalProgressMonitor()); |
| } |
| } |
| fStrategyTypes.add(contentType); |
| } |
| |
| /** |
| * @param needsValidation |
| * The needsValidation to set. |
| */ |
| public void setValidationNeeded(boolean needsValidation) { |
| fValidationNeeded = needsValidation; |
| } |
| |
| /** |
| * Set the strategy that runs validators from the reconcileValidator |
| * extension point. |
| * |
| * @param the |
| * ValidatorStrategy |
| */ |
| public void setValidatorStrategy(ValidatorStrategy strategy) { |
| fValidatorStrategy = strategy; |
| if (fValidatorStrategy != null) |
| fValidatorStrategy.setDocument(getDocument()); |
| } |
| |
| /** |
| * |
| * @param document |
| */ |
| private void unhookModelLifecycleListener(IDocument document) { |
| IStructuredModel sModel = getStructuredModelForRead(document); |
| try { |
| if (sModel != null) { |
| sModel.removeModelLifecycleListener(this); |
| } |
| } finally { |
| if (sModel != null) |
| sModel.releaseFromRead(); |
| } |
| } |
| |
| /** |
| * Cleanup listeners. |
| * |
| * @see org.eclipse.jface.text.reconciler.IReconciler#uninstall() |
| */ |
| public void uninstall() { |
| setValidationNeeded(false); |
| if (isInstalled()) { |
| setInstalled(false); |
| //getProgressMonitor().setCanceled(true); |
| getLocalProgressMonitor().setCanceled(true); |
| |
| getTextViewer().removeTextInputListener(fTextInputListener); |
| |
| super.uninstall(); |
| } |
| if (fLocalDocument != null && fLocalDocument instanceof IStructuredDocument) { |
| // remove structured document listener |
| ((IStructuredDocument) fLocalDocument).removeDocumentChangedListener(this); |
| // remove lifecycle listener on the model |
| unhookModelLifecycleListener(fLocalDocument); |
| } |
| setDocument(null); |
| } |
| } |