1 19 20 package org.netbeans.modules.editor.lib2.highlighting; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.lang.ref.WeakReference ; 25 import java.util.ArrayList ; 26 import java.util.Arrays ; 27 import java.util.Collection ; 28 import java.util.Collections ; 29 import java.util.HashMap ; 30 import java.util.List ; 31 import java.util.WeakHashMap ; 32 import java.util.logging.Level ; 33 import java.util.logging.Logger ; 34 import java.util.regex.Pattern ; 35 import java.util.regex.PatternSyntaxException ; 36 import javax.swing.text.Document ; 37 import javax.swing.text.JTextComponent ; 38 import org.netbeans.api.editor.mimelookup.MimeLookup; 39 import org.netbeans.api.editor.mimelookup.MimePath; 40 import org.netbeans.api.editor.settings.FontColorSettings; 41 import org.netbeans.spi.editor.highlighting.HighlightsContainer; 42 import org.netbeans.spi.editor.highlighting.HighlightsLayer; 43 import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; 44 import org.openide.ErrorManager; 45 import org.openide.util.Lookup; 46 import org.openide.util.LookupEvent; 47 import org.openide.util.LookupListener; 48 import org.openide.util.TopologicalSortException; 49 import org.openide.util.Utilities; 50 import org.openide.util.WeakListeners; 51 import org.openide.util.lookup.ProxyLookup; 52 53 57 public final class HighlightingManager { 58 59 private static final Logger LOG = Logger.getLogger(HighlightingManager.class.getName()); 60 61 public static synchronized HighlightingManager getInstance() { 62 if (instance == null) { 63 instance = new HighlightingManager(); 64 } 65 return instance; 66 } 67 68 public HighlightsContainer getHighlights(JTextComponent pane, HighlightsLayerFilter filter) { 69 return getHighlighting(pane).getContainer(filter); 70 } 71 72 76 private static HighlightingManager instance; 77 78 final WeakHashMap <JTextComponent , WeakReference <Highlighting>> CACHE = 79 new WeakHashMap <JTextComponent , WeakReference <Highlighting>>(); 80 81 82 private HighlightingManager() { 83 } 84 85 private Highlighting getHighlighting(JTextComponent pane) { 86 synchronized (CACHE) { 87 WeakReference <Highlighting> ref = CACHE.get(pane); 88 Highlighting h = ref == null ? null : ref.get(); 89 90 if (h == null) { 91 h = new Highlighting(pane); 92 CACHE.put(pane, new WeakReference <Highlighting>(h)); 93 } 94 95 return h; 96 } 97 } 98 99 private static final class Highlighting implements PropertyChangeListener { 100 101 private static final String PROP_MIME_TYPE = "mimeType"; private static final String PROP_DOCUMENT = "document"; private static final String PROP_HL_INCLUDES = "HighlightsLayerIncludes"; private static final String PROP_HL_EXCLUDES = "HighlightsLayerExcludes"; 106 private Lookup.Result<HighlightsLayerFactory> factories = null; 108 private LookupListener factoriesTracker = new LookupListener() { 109 public void resultChanged(LookupEvent ev) { 110 rebuildAllContainers(); 111 } 112 }; 113 114 private Lookup.Result<FontColorSettings> settings = null; 116 private LookupListener settingsTracker = new LookupListener() { 117 public void resultChanged(LookupEvent ev) { 118 rebuildAllContainers(); 120 } 121 }; 122 123 private final JTextComponent pane; 124 private HighlightsLayerFilter paneFilter; 125 private Document lastKnownDocument = null; 126 private MimePath [] lastKnownMimePaths = null; 127 128 private final WeakHashMap <HighlightsLayerFilter, WeakReference <CompoundHighlightsContainer>> containers = 129 new WeakHashMap <HighlightsLayerFilter, WeakReference <CompoundHighlightsContainer>>(); 130 131 public Highlighting(JTextComponent pane) { 132 this.pane = pane; 133 this.paneFilter = new RegExpFilter(pane.getClientProperty(PROP_HL_INCLUDES), pane.getClientProperty(PROP_HL_EXCLUDES)); 134 this.pane.addPropertyChangeListener(WeakListeners.propertyChange(this, pane)); 135 136 rebuildAll(); 137 } 138 139 public synchronized HighlightsContainer getContainer(HighlightsLayerFilter filter) { 140 WeakReference <CompoundHighlightsContainer> ref = containers.get(filter); 141 CompoundHighlightsContainer container = ref == null ? null : ref.get(); 142 143 if (container == null) { 144 container = new CompoundHighlightsContainer(); 145 rebuildContainer(filter, container); 146 147 containers.put(filter, new WeakReference <CompoundHighlightsContainer>(container)); 148 } 149 150 return container; 151 } 152 153 157 public void propertyChange(PropertyChangeEvent evt) { 158 if (evt.getPropertyName() == null || PROP_DOCUMENT.equals(evt.getPropertyName())) { 159 rebuildAll(); 160 } 161 162 if (PROP_HL_INCLUDES.equals(evt.getPropertyName()) || PROP_HL_EXCLUDES.equals(evt.getPropertyName())) { 163 synchronized (this) { 164 paneFilter = new RegExpFilter(pane.getClientProperty(PROP_HL_INCLUDES), pane.getClientProperty(PROP_HL_EXCLUDES)); 165 rebuildAllContainers(); 166 } 167 } 168 } 169 170 174 private MimePath [] getAllDocumentMimePath() { 175 Document doc = pane.getDocument(); 176 String mainMimeType; 177 178 Object propMimeType = doc.getProperty(PROP_MIME_TYPE); 179 if (propMimeType != null) { 180 mainMimeType = propMimeType.toString(); 181 } else { 182 mainMimeType = pane.getUI().getEditorKit(pane).getContentType(); 183 } 184 185 return new MimePath [] { MimePath.parse(mainMimeType) }; 186 } 187 188 private synchronized void rebuildAll() { 189 MimePath [] mimePaths = getAllDocumentMimePath(); 191 192 if (!Utilities.compareObjects(lastKnownDocument, pane.getDocument()) || 194 !Arrays.equals(lastKnownMimePaths, mimePaths) 195 ) { 196 if (factories != null) { 198 factories.removeLookupListener(factoriesTracker); 199 } 200 if (settings != null) { 201 settings.removeLookupListener(settingsTracker); 202 } 203 204 if (mimePaths != null) { 205 ArrayList <Lookup> lookups = new ArrayList <Lookup>(); 206 for(MimePath mimePath : mimePaths) { 207 lookups.add(MimeLookup.getLookup(mimePath)); 208 } 209 210 ProxyLookup lookup = new ProxyLookup(lookups.toArray(new Lookup[lookups.size()])); 211 factories = lookup.lookup(new Lookup.Template<HighlightsLayerFactory>(HighlightsLayerFactory.class)); 212 settings = lookup.lookup(new Lookup.Template<FontColorSettings>(FontColorSettings.class)); 213 } else { 214 factories = null; 215 settings = null; 216 } 217 218 if (factories != null) { 220 factories.addLookupListener(factoriesTracker); 221 factories.allItems(); } 223 if (settings != null) { 224 settings.addLookupListener(settingsTracker); 225 settings.allItems(); } 227 228 lastKnownDocument = pane.getDocument(); 229 lastKnownMimePaths = mimePaths; 230 231 rebuildAllContainers(); 232 } 233 } 234 235 private synchronized void resetAllContainers() { 236 for(HighlightsLayerFilter filter : containers.keySet()) { 237 WeakReference <CompoundHighlightsContainer> ref = containers.get(filter); 238 CompoundHighlightsContainer container = ref == null ? null : ref.get(); 239 240 if (container != null) { 241 container.resetCache(); 242 } 243 } 244 } 245 246 private synchronized void rebuildAllContainers() { 247 for(HighlightsLayerFilter filter : containers.keySet()) { 248 WeakReference <CompoundHighlightsContainer> ref = containers.get(filter); 249 CompoundHighlightsContainer container = ref == null ? null : ref.get(); 250 251 if (container != null) { 252 rebuildContainer(filter, container); 253 } 254 } 255 } 256 257 private synchronized void rebuildContainer(HighlightsLayerFilter filter, CompoundHighlightsContainer container) { 258 if (factories != null) { 259 Document doc = pane.getDocument(); 260 Collection <? extends HighlightsLayerFactory> all = factories.allInstances(); 261 HashMap <String , HighlightsLayer> layers = new HashMap <String , HighlightsLayer>(); 262 263 HighlightsLayerFactory.Context context = HighlightingSpiPackageAccessor.get().createFactoryContext(doc, pane); 264 265 for(HighlightsLayerFactory factory : all) { 266 HighlightsLayer [] factoryLayers = factory.createLayers(context); 267 if (factoryLayers == null) { 268 continue; 269 } 270 271 for(HighlightsLayer layer : factoryLayers) { 272 HighlightsLayerAccessor layerAccessor = 273 HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); 274 275 String layerTypeId = layerAccessor.getLayerTypeId(); 276 if (!layers.containsKey(layerTypeId)) { 277 layers.put(layerTypeId, layer); 278 } 279 } 280 } 281 282 List <? extends HighlightsLayer> sortedLayers; 284 try { 285 sortedLayers = HighlightingSpiPackageAccessor.get().sort(layers.values()); 286 } catch (TopologicalSortException tse) { 287 ErrorManager.getDefault().notify(tse); 288 @SuppressWarnings ("unchecked") List <? extends HighlightsLayer> sl 290 = (List <? extends HighlightsLayer>)tse.partialSort(); 291 sortedLayers = sl; 292 } 293 294 sortedLayers = paneFilter.filterLayers(Collections.unmodifiableList(sortedLayers)); 296 sortedLayers = filter.filterLayers(Collections.unmodifiableList(sortedLayers)); 297 298 ArrayList <HighlightsContainer> hcs = new ArrayList <HighlightsContainer>(); 300 for(HighlightsLayer layer : sortedLayers) { 301 HighlightsLayerAccessor layerAccessor = 302 HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); 303 304 hcs.add(layerAccessor.getContainer()); 305 } 306 307 if (LOG.isLoggable(Level.FINE)) { 308 logLayers(doc, lastKnownMimePaths, sortedLayers); 309 } 310 311 container.setLayers(doc, hcs.toArray(new HighlightsContainer[hcs.size()])); 312 } else { 313 container.setLayers(null, null); 314 } 315 } 316 317 private static void logLayers(Document doc, MimePath [] mimePaths, List <? extends HighlightsLayer> layers) { 318 StringBuilder sb = new StringBuilder (); 319 320 sb.append("HighlighsLayers {\n"); sb.append(" * document : "); sb.append(doc.toString()); 323 sb.append("\n"); 325 sb.append(" * mime paths : \n"); for(MimePath mimePath : mimePaths) { 327 sb.append(" "); sb.append(mimePath.getPath()); 329 sb.append("\n"); } 331 332 sb.append(" * layers : \n"); for(HighlightsLayer layer : layers) { 334 HighlightsLayerAccessor layerAccessor = 335 HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); 336 337 sb.append(" "); sb.append(layerAccessor.getLayerTypeId()); 339 sb.append("\n"); } 341 342 sb.append("}\n"); 344 LOG.fine(sb.toString()); 345 } 346 347 } 349 private static final class RegExpFilter implements HighlightsLayerFilter { 350 351 private final List <Pattern > includes; 352 private final List <Pattern > excludes; 353 354 public RegExpFilter(Object includes, Object excludes) { 355 this.includes = buildPatterns(includes); 356 this.excludes = buildPatterns(excludes); 357 } 358 359 public List <? extends HighlightsLayer> filterLayers(List <? extends HighlightsLayer> layers) { 360 List <? extends HighlightsLayer> includedLayers; 361 362 if (includes.isEmpty()) { 363 includedLayers = layers; 364 } else { 365 includedLayers = filter(layers, includes, true); 366 } 367 368 List <? extends HighlightsLayer> filteredLayers; 369 if (excludes.isEmpty()) { 370 filteredLayers = includedLayers; 371 } else { 372 filteredLayers = filter(includedLayers, excludes, false); 373 } 374 375 return filteredLayers; 376 } 377 378 private static List <? extends HighlightsLayer> filter( 379 List <? extends HighlightsLayer> layers, 380 List <Pattern > patterns, 381 boolean includeMatches ) { 383 List <HighlightsLayer> filtered = new ArrayList <HighlightsLayer>(); 384 385 for(HighlightsLayer layer : layers) { 386 HighlightsLayerAccessor layerAccessor = 387 HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); 388 389 for(Pattern pattern : patterns) { 390 boolean matches = pattern.matcher(layerAccessor.getLayerTypeId()).matches(); 391 392 if (matches && includeMatches) { 393 filtered.add(layer); 394 } 395 396 if (!matches && !includeMatches) { 397 filtered.add(layer); 398 } 399 } 400 } 401 402 return filtered; 403 } 404 405 private static List <Pattern > buildPatterns(Object expressions) { 406 List <Pattern > patterns = new ArrayList <Pattern >(); 407 408 if (expressions instanceof String ) { 409 try { 410 patterns.add(Pattern.compile((String ) expressions)); 411 } catch (PatternSyntaxException e) { 412 LOG.log(Level.WARNING, "Ignoring invalid regexp for the HighlightsLayer filtering.", e); } 414 } else if (expressions instanceof String []) { 415 for(String expression : (String []) expressions) { 416 try { 417 patterns.add(Pattern.compile(expression)); 418 } catch (PatternSyntaxException e) { 419 LOG.log(Level.WARNING, "Ignoring invalid regexp for the HighlightsLayer filtering.", e); } 421 } 422 } 423 424 return patterns; 425 } 426 } } 428 | Popular Tags |