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