1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.jface.text.reconciler;
15 
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 
22 import org.eclipse.core.runtime.Assert;
23 import org.eclipse.core.runtime.IProgressMonitor;
24 
25 import org.eclipse.jface.text.BadLocationException;
26 import org.eclipse.jface.text.IDocument;
27 import org.eclipse.jface.text.IDocumentExtension3;
28 import org.eclipse.jface.text.IRegion;
29 import org.eclipse.jface.text.ITypedRegion;
30 import org.eclipse.jface.text.Region;
31 import org.eclipse.jface.text.TextUtilities;
32 import org.eclipse.jface.text.TypedRegion;
33 
34 
35 /**
36  * Standard implementation of {@link org.eclipse.jface.text.reconciler.IReconciler}.
37  * The reconciler is configured with a set of {@linkplain org.eclipse.jface.text.reconciler.IReconcilingStrategy reconciling strategies}
38  * each of which is responsible for a particular content type.
39  * <p>
40  * Usually, clients instantiate this class and configure it before using it.
41  * </p>
42  *
43  * @see org.eclipse.jface.text.IDocumentListener
44  * @see org.eclipse.jface.text.ITextInputListener
45  * @see org.eclipse.jface.text.reconciler.DirtyRegion
46  */
47 public class Reconciler extends AbstractReconciler implements IReconcilerExtension {
48 
49 	/** The map of reconciling strategies. */
50 	private Map<String, IReconcilingStrategy> fStrategies;
51 
52 	/**
53 	 * The partitioning this reconciler uses.
54 	 *@since 3.0
55 	 */
56 	private String fPartitioning;
57 
58 	/**
59 	 * Creates a new reconciler with the following configuration: it is
60 	 * an incremental reconciler with a standard delay of 500 milliseconds. There
61 	 * are no predefined reconciling strategies. The partitioning it uses
62 	 * is the default partitioning {@link IDocumentExtension3#DEFAULT_PARTITIONING}.
63 	 */
Reconciler()64 	public Reconciler() {
65 		super();
66 		fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
67 	}
68 
69 	/**
70 	 * Sets the document partitioning for this reconciler.
71 	 *
72 	 * @param partitioning the document partitioning for this reconciler
73 	 * @since 3.0
74 	 */
setDocumentPartitioning(String partitioning)75 	public void setDocumentPartitioning(String partitioning) {
76 		Assert.isNotNull(partitioning);
77 		fPartitioning= partitioning;
78 	}
79 
80 	@Override
getDocumentPartitioning()81 	public String getDocumentPartitioning() {
82 		return fPartitioning;
83 	}
84 
85 	/**
86 	 * Registers a given reconciling strategy for a particular content type.
87 	 * If there is already a strategy registered for this type, the new strategy
88 	 * is registered instead of the old one.
89 	 *
90 	 * @param strategy the reconciling strategy to register, or <code>null</code> to remove an existing one
91 	 * @param contentType the content type under which to register
92 	 */
setReconcilingStrategy(IReconcilingStrategy strategy, String contentType)93 	public void setReconcilingStrategy(IReconcilingStrategy strategy, String contentType) {
94 
95 		Assert.isNotNull(contentType);
96 
97 		if (fStrategies == null)
98 			fStrategies= new HashMap<>();
99 
100 		if (strategy == null)
101 			fStrategies.remove(contentType);
102 		else {
103 			fStrategies.put(contentType, strategy);
104 			if (strategy instanceof IReconcilingStrategyExtension && getProgressMonitor() != null) {
105 				IReconcilingStrategyExtension extension= (IReconcilingStrategyExtension) strategy;
106 				extension.setProgressMonitor(getProgressMonitor());
107 			}
108 		}
109 	}
110 
111 	@Override
getReconcilingStrategy(String contentType)112 	public IReconcilingStrategy getReconcilingStrategy(String contentType) {
113 
114 		Assert.isNotNull(contentType);
115 
116 		if (fStrategies == null)
117 			return null;
118 
119 		return fStrategies.get(contentType);
120 	}
121 
122 	/**
123 	 * Processes a dirty region. If the dirty region is <code>null</code> the whole
124 	 * document is consider being dirty. The dirty region is partitioned by the
125 	 * document and each partition is handed over to a reconciling strategy registered
126 	 * for the partition's content type.
127 	 *
128 	 * @param dirtyRegion the dirty region to be processed
129 	 * @see AbstractReconciler#process(DirtyRegion)
130 	 */
131 	@Override
process(DirtyRegion dirtyRegion)132 	protected void process(DirtyRegion dirtyRegion) {
133 
134 		IRegion region= dirtyRegion;
135 
136 		if (region == null)
137 			region= new Region(0, getDocument().getLength());
138 
139 		ITypedRegion[] regions= computePartitioning(region.getOffset(), region.getLength());
140 
141 		for (ITypedRegion r : regions) {
142 			IReconcilingStrategy s= getReconcilingStrategy(r.getType());
143 			if (s == null)
144 				continue;
145 
146 			if(dirtyRegion != null)
147 				s.reconcile(dirtyRegion, r);
148 			else
149 				s.reconcile(r);
150 		}
151 	}
152 
153 	@Override
reconcilerDocumentChanged(IDocument document)154 	protected void reconcilerDocumentChanged(IDocument document) {
155 		if (fStrategies != null) {
156 			Iterator<IReconcilingStrategy> e= fStrategies.values().iterator();
157 			while (e.hasNext()) {
158 				IReconcilingStrategy strategy= e.next();
159 				strategy.setDocument(document);
160 			}
161 		}
162 	}
163 
164 	@Override
setProgressMonitor(IProgressMonitor monitor)165 	public void setProgressMonitor(IProgressMonitor monitor) {
166 		super.setProgressMonitor(monitor);
167 
168 		if (fStrategies != null) {
169 			Iterator<IReconcilingStrategy> e= fStrategies.values().iterator();
170 			while (e.hasNext()) {
171 				IReconcilingStrategy strategy= e.next();
172 				if (strategy instanceof IReconcilingStrategyExtension) {
173 					IReconcilingStrategyExtension extension= (IReconcilingStrategyExtension) strategy;
174 					extension.setProgressMonitor(monitor);
175 				}
176 			}
177 		}
178 	}
179 
180 	@Override
initialProcess()181 	protected void initialProcess() {
182 		ITypedRegion[] regions= computePartitioning(0, getDocument().getLength());
183 		List<String> contentTypes= new ArrayList<>(regions.length);
184 		for (ITypedRegion region : regions) {
185 			String contentType= region.getType();
186 			if( contentTypes.contains(contentType))
187 				continue;
188 			contentTypes.add(contentType);
189 			IReconcilingStrategy s= getReconcilingStrategy(contentType);
190 			if (s instanceof IReconcilingStrategyExtension) {
191 				IReconcilingStrategyExtension e= (IReconcilingStrategyExtension) s;
192 				e.initialReconcile();
193 			}
194 		}
195 	}
196 
197 	/**
198 	 * Computes and returns the partitioning for the given region of the input document
199 	 * of the reconciler's connected text viewer.
200 	 *
201 	 * @param offset the region offset
202 	 * @param length the region length
203 	 * @return the computed partitioning
204 	 * @since 3.0
205 	 */
computePartitioning(int offset, int length)206 	private ITypedRegion[] computePartitioning(int offset, int length) {
207 		ITypedRegion[] regions= null;
208 		try {
209 			regions= TextUtilities.computePartitioning(getDocument(), getDocumentPartitioning(), offset, length, false);
210 		} catch (BadLocationException x) {
211 			regions= new TypedRegion[0];
212 		}
213 		return regions;
214 	}
215 }
216