KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > lib2 > highlighting > HighlightingManager


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.editor.lib2.highlighting;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.lang.ref.WeakReference JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.WeakHashMap JavaDoc;
32 import java.util.logging.Level JavaDoc;
33 import java.util.logging.Logger JavaDoc;
34 import java.util.regex.Pattern JavaDoc;
35 import java.util.regex.PatternSyntaxException JavaDoc;
36 import javax.swing.text.Document JavaDoc;
37 import javax.swing.text.JTextComponent JavaDoc;
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 /**
54  *
55  * @author Vita Stejskal
56  */

57 public final class HighlightingManager {
58
59     private static final Logger JavaDoc 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 JavaDoc pane, HighlightsLayerFilter filter) {
69         return getHighlighting(pane).getContainer(filter);
70     }
71     
72     // ----------------------------------------------------------------------
73
// Private implementation
74
// ----------------------------------------------------------------------
75

76     private static HighlightingManager instance;
77     
78     /* package */ final WeakHashMap JavaDoc<JTextComponent JavaDoc, WeakReference JavaDoc<Highlighting>> CACHE =
79         new WeakHashMap JavaDoc<JTextComponent JavaDoc, WeakReference JavaDoc<Highlighting>>();
80     
81     /** Creates a new instance of HighlightingManager */
82     private HighlightingManager() {
83     }
84     
85     private Highlighting getHighlighting(JTextComponent JavaDoc pane) {
86         synchronized (CACHE) {
87             WeakReference JavaDoc<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 JavaDoc<Highlighting>(h));
93             }
94             
95             return h;
96         }
97     }
98     
99     private static final class Highlighting implements PropertyChangeListener JavaDoc {
100     
101         private static final String JavaDoc PROP_MIME_TYPE = "mimeType"; //NOI18N
102
private static final String JavaDoc PROP_DOCUMENT = "document"; //NOI18N
103
private static final String JavaDoc PROP_HL_INCLUDES = "HighlightsLayerIncludes"; //NOI18N
104
private static final String JavaDoc PROP_HL_EXCLUDES = "HighlightsLayerExcludes"; //NOI18N
105

106         // The factories changes tracking
107
private Lookup.Result<HighlightsLayerFactory> factories = null;
108         private LookupListener factoriesTracker = new LookupListener() {
109             public void resultChanged(LookupEvent ev) {
110                 rebuildAllContainers();
111             }
112         };
113
114         // The FontColorSettings changes tracking
115
private Lookup.Result<FontColorSettings> settings = null;
116         private LookupListener settingsTracker = new LookupListener() {
117             public void resultChanged(LookupEvent ev) {
118 // System.out.println("Settings tracker for '" + (lastKnownMimePaths == null ? "null" : lastKnownMimePaths[0].getPath()) + "'");
119
rebuildAllContainers();
120             }
121         };
122
123         private final JTextComponent JavaDoc pane;
124         private HighlightsLayerFilter paneFilter;
125         private Document JavaDoc lastKnownDocument = null;
126         private MimePath [] lastKnownMimePaths = null;
127         
128         private final WeakHashMap JavaDoc<HighlightsLayerFilter, WeakReference JavaDoc<CompoundHighlightsContainer>> containers =
129             new WeakHashMap JavaDoc<HighlightsLayerFilter, WeakReference JavaDoc<CompoundHighlightsContainer>>();
130         
131         public Highlighting(JTextComponent JavaDoc 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 JavaDoc<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 JavaDoc<CompoundHighlightsContainer>(container));
148             }
149
150             return container;
151         }
152         
153         // ----------------------------------------------------------------------
154
// PropertyChangeListener implementation
155
// ----------------------------------------------------------------------
156

157         public void propertyChange(PropertyChangeEvent JavaDoc 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         // ----------------------------------------------------------------------
171
// Private implementation
172
// ----------------------------------------------------------------------
173

174         private MimePath [] getAllDocumentMimePath() {
175             Document JavaDoc doc = pane.getDocument();
176             String JavaDoc mainMimeType;
177
178             Object JavaDoc 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             // Get the new set of mime path
190
MimePath [] mimePaths = getAllDocumentMimePath();
191
192             // Recalculate factories and all containers if needed
193
if (!Utilities.compareObjects(lastKnownDocument, pane.getDocument()) ||
194                 !Arrays.equals(lastKnownMimePaths, mimePaths)
195             ) {
196                 // Unregister listeners
197
if (factories != null) {
198                     factories.removeLookupListener(factoriesTracker);
199                 }
200                 if (settings != null) {
201                     settings.removeLookupListener(settingsTracker);
202                 }
203
204                 if (mimePaths != null) {
205                     ArrayList JavaDoc<Lookup> lookups = new ArrayList JavaDoc<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                 // Start listening again
219
if (factories != null) {
220                     factories.addLookupListener(factoriesTracker);
221                     factories.allItems(); // otherwise we won't get any events at all
222
}
223                 if (settings != null) {
224                     settings.addLookupListener(settingsTracker);
225                     settings.allItems(); // otherwise we won't get any events at all
226
}
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 JavaDoc<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 JavaDoc<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 JavaDoc doc = pane.getDocument();
260                 Collection JavaDoc<? extends HighlightsLayerFactory> all = factories.allInstances();
261                 HashMap JavaDoc<String JavaDoc, HighlightsLayer> layers = new HashMap JavaDoc<String JavaDoc, 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 JavaDoc layerTypeId = layerAccessor.getLayerTypeId();
276                         if (!layers.containsKey(layerTypeId)) {
277                             layers.put(layerTypeId, layer);
278                         }
279                     }
280                 }
281
282                 // Sort the layers by their z-order
283
List JavaDoc<? extends HighlightsLayer> sortedLayers;
284                 try {
285                     sortedLayers = HighlightingSpiPackageAccessor.get().sort(layers.values());
286                 } catch (TopologicalSortException tse) {
287                     ErrorManager.getDefault().notify(tse);
288                     @SuppressWarnings JavaDoc("unchecked") //NOI18N
289
List JavaDoc<? extends HighlightsLayer> sl
290                             = (List JavaDoc<? extends HighlightsLayer>)tse.partialSort();
291                     sortedLayers = sl;
292                 }
293                 
294                 // filter the layers
295
sortedLayers = paneFilter.filterLayers(Collections.unmodifiableList(sortedLayers));
296                 sortedLayers = filter.filterLayers(Collections.unmodifiableList(sortedLayers));
297
298                 // Get the containers
299
ArrayList JavaDoc<HighlightsContainer> hcs = new ArrayList JavaDoc<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 JavaDoc doc, MimePath [] mimePaths, List JavaDoc<? extends HighlightsLayer> layers) {
318             StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
319             
320             sb.append("HighlighsLayers {\n"); //NOI18N
321
sb.append(" * document : "); //NOI18N
322
sb.append(doc.toString());
323             sb.append("\n"); //NOI18N
324

325             sb.append(" * mime paths : \n"); //NOI18N
326
for(MimePath mimePath : mimePaths) {
327                 sb.append(" "); //NOI18N
328
sb.append(mimePath.getPath());
329                 sb.append("\n"); //NOI18N
330
}
331             
332             sb.append(" * layers : \n"); //NOI18N
333
for(HighlightsLayer layer : layers) {
334                 HighlightsLayerAccessor layerAccessor =
335                     HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer);
336
337                 sb.append(" "); //NOI18N
338
sb.append(layerAccessor.getLayerTypeId());
339                 sb.append("\n"); //NOI18N
340
}
341             
342             sb.append("}\n"); //NOI18N
343

344             LOG.fine(sb.toString());
345         }
346         
347     } // End of Highlighting class
348

349     private static final class RegExpFilter implements HighlightsLayerFilter {
350         
351         private final List JavaDoc<Pattern JavaDoc> includes;
352         private final List JavaDoc<Pattern JavaDoc> excludes;
353         
354         public RegExpFilter(Object JavaDoc includes, Object JavaDoc excludes) {
355             this.includes = buildPatterns(includes);
356             this.excludes = buildPatterns(excludes);
357         }
358
359         public List JavaDoc<? extends HighlightsLayer> filterLayers(List JavaDoc<? extends HighlightsLayer> layers) {
360             List JavaDoc<? extends HighlightsLayer> includedLayers;
361             
362             if (includes.isEmpty()) {
363                 includedLayers = layers;
364             } else {
365                 includedLayers = filter(layers, includes, true);
366             }
367             
368             List JavaDoc<? 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 JavaDoc<? extends HighlightsLayer> filter(
379             List JavaDoc<? extends HighlightsLayer> layers,
380             List JavaDoc<Pattern JavaDoc> patterns,
381             boolean includeMatches // true means include matching layers, false means include non-matching layers
382
) {
383             List JavaDoc<HighlightsLayer> filtered = new ArrayList JavaDoc<HighlightsLayer>();
384             
385             for(HighlightsLayer layer : layers) {
386                 HighlightsLayerAccessor layerAccessor =
387                     HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer);
388                 
389                 for(Pattern JavaDoc 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 JavaDoc<Pattern JavaDoc> buildPatterns(Object JavaDoc expressions) {
406             List JavaDoc<Pattern JavaDoc> patterns = new ArrayList JavaDoc<Pattern JavaDoc>();
407             
408             if (expressions instanceof String JavaDoc) {
409                 try {
410                     patterns.add(Pattern.compile((String JavaDoc) expressions));
411                 } catch (PatternSyntaxException JavaDoc e) {
412                     LOG.log(Level.WARNING, "Ignoring invalid regexp for the HighlightsLayer filtering.", e); //NOI18N
413
}
414             } else if (expressions instanceof String JavaDoc[]) {
415                 for(String JavaDoc expression : (String JavaDoc []) expressions) {
416                     try {
417                         patterns.add(Pattern.compile(expression));
418                     } catch (PatternSyntaxException JavaDoc e) {
419                         LOG.log(Level.WARNING, "Ignoring invalid regexp for the HighlightsLayer filtering.", e); //NOI18N
420
}
421                 }
422             }
423             
424             return patterns;
425         }
426     } // End of RegExpFilter class
427
}
428
Popular Tags