KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > lib > HighlightingDrawLayer


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.lib;
21
22 import java.lang.ref.WeakReference JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.logging.Level JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28 import javax.swing.JEditorPane JavaDoc;
29 import javax.swing.text.AttributeSet JavaDoc;
30 import javax.swing.text.Document JavaDoc;
31 import javax.swing.text.EditorKit JavaDoc;
32 import javax.swing.text.JTextComponent JavaDoc;
33 import javax.swing.text.SimpleAttributeSet JavaDoc;
34 import org.netbeans.api.editor.mimelookup.MimeLookup;
35 import org.netbeans.api.editor.mimelookup.MimePath;
36 import org.netbeans.api.editor.settings.AttributesUtilities;
37 import org.netbeans.editor.BaseDocument;
38 import org.netbeans.editor.Coloring;
39 import org.netbeans.editor.DrawContext;
40 import org.netbeans.editor.DrawLayer;
41 import org.netbeans.editor.EditorUI;
42 import org.netbeans.editor.MarkFactory;
43 import org.netbeans.editor.ext.ExtCaret;
44 import org.netbeans.modules.editor.lib2.highlighting.CaretBasedBlockHighlighting.CaretRowHighlighting;
45 import org.netbeans.modules.editor.lib2.highlighting.HighlightingManager;
46 import org.netbeans.modules.editor.lib2.highlighting.HighlightingSpiPackageAccessor;
47 import org.netbeans.modules.editor.lib2.highlighting.HighlightsLayerAccessor;
48 import org.netbeans.modules.editor.lib2.highlighting.HighlightsLayerFilter;
49 import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent;
50 import org.netbeans.spi.editor.highlighting.HighlightsChangeListener;
51 import org.netbeans.spi.editor.highlighting.HighlightsContainer;
52 import org.netbeans.spi.editor.highlighting.HighlightsLayer;
53 import org.netbeans.spi.editor.highlighting.HighlightsSequence;
54
55 /**
56  *
57  * @author vita
58  */

59 public class HighlightingDrawLayer extends DrawLayer.AbstractLayer
60     implements HighlightsChangeListener
61 {
62     private static final Logger JavaDoc LOG = Logger.getLogger(HighlightingDrawLayer.class.getName());
63
64     private static final String JavaDoc LAYER_A_NAME = "org-netbeans-lib-editor-nview-HighlightingDrawLayer/A"; //NOI18N
65
// Using the original name for the caret row highlighting, some clients use it to remove the layer.
66
private static final String JavaDoc LAYER_B_NAME = ExtCaret.HIGHLIGHT_ROW_LAYER_NAME;
67     private static final String JavaDoc LAYER_C_NAME = "org-netbeans-lib-editor-nview-HighlightingDrawLayer/C"; //NOI18N
68

69     private static final HighlightsLayerFilter FILTER_A = new HighlightsLayerFilter() {
70         public List JavaDoc<? extends HighlightsLayer> filterLayers(List JavaDoc<? extends HighlightsLayer> layers) {
71             ArrayList JavaDoc<HighlightsLayer> filteredLayers = new ArrayList JavaDoc<HighlightsLayer>();
72             boolean add = false;
73             
74             for(HighlightsLayer layer : layers) {
75                 HighlightsLayerAccessor layerAccessor =
76                     HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer);
77                 
78                 if (CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId())) {
79                     add = true;
80                     continue;
81                 }
82                 
83                 if (add) {
84                     filteredLayers.add(layer);
85                 }
86             }
87
88             return filteredLayers;
89         }
90     }; // End of FILTER_A constant
91

92     private static final HighlightsLayerFilter FILTER_B = new HighlightsLayerFilter() {
93         public List JavaDoc<? extends HighlightsLayer> filterLayers(List JavaDoc<? extends HighlightsLayer> layers) {
94             ArrayList JavaDoc<HighlightsLayer> filteredLayers = new ArrayList JavaDoc<HighlightsLayer>();
95             
96             for(HighlightsLayer layer : layers) {
97                 HighlightsLayerAccessor layerAccessor =
98                     HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer);
99                 
100                 if (CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId())) {
101                     filteredLayers.add(layer);
102                     break;
103                 }
104             }
105
106             return filteredLayers;
107         }
108     }; // End of FILTER_B constant
109

110     private static final HighlightsLayerFilter FILTER_C = new HighlightsLayerFilter() {
111         public List JavaDoc<? extends HighlightsLayer> filterLayers(List JavaDoc<? extends HighlightsLayer> layers) {
112             ArrayList JavaDoc<HighlightsLayer> filteredLayers = new ArrayList JavaDoc<HighlightsLayer>();
113             
114             for(HighlightsLayer layer : layers) {
115                 HighlightsLayerAccessor layerAccessor =
116                     HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer);
117                 
118                 if (CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId())) {
119                     break;
120                 }
121                 
122                 filteredLayers.add(layer);
123             }
124
125             return filteredLayers;
126         }
127     }; // End of FILTER_B constant
128

129     public static void hookUp(EditorUI eui) {
130         DrawLayer layerA = eui.findLayer(LAYER_A_NAME);
131         if (layerA == null) {
132             layerA = new HighlightingDrawLayer(LAYER_A_NAME, FILTER_A);
133             eui.addLayer(layerA, 10000); // the old caret row draw layer's z-order
134

135             if (LOG.isLoggable(Level.FINE)) {
136                 LOG.fine("Successfully registered layerA in " + simpleToString(eui)); //NOI18N
137
}
138         } else {
139             if (LOG.isLoggable(Level.FINE)) {
140                 LOG.fine("LayerA is already registered in " + simpleToString(eui)); //NOI18N
141
}
142         }
143
144         DrawLayer layerB = eui.findLayer(LAYER_B_NAME);
145         if (layerB == null) {
146             layerB = new HighlightingDrawLayer(LAYER_B_NAME, FILTER_B);
147             eui.addLayer(layerB, 2050); // the old caret row draw layer's z-order
148

149             if (LOG.isLoggable(Level.FINE)) {
150                 LOG.fine("Successfully registered layerB in " + simpleToString(eui)); //NOI18N
151
}
152         } else {
153             if (LOG.isLoggable(Level.FINE)) {
154                 LOG.fine("LayerB is already registered in " + simpleToString(eui)); //NOI18N
155
}
156         }
157
158         DrawLayer layerC = eui.findLayer(LAYER_C_NAME);
159         if (layerC == null) {
160             layerC = new HighlightingDrawLayer(LAYER_C_NAME, FILTER_C);
161             eui.addLayer(layerC, 1000); // the old syntax draw layer's z-order
162

163             if (LOG.isLoggable(Level.FINE)) {
164                 LOG.fine("Successfully registered layerC in " + simpleToString(eui)); //NOI18N
165
}
166         } else {
167             if (LOG.isLoggable(Level.FINE)) {
168                 LOG.fine("LayerC is already registered in " + simpleToString(eui)); //NOI18N
169
}
170         }
171     }
172     
173     private final HighlightsLayerFilter filter;
174
175     private WeakReference JavaDoc<JTextComponent JavaDoc> paneRef = null;
176     private HighlightsContainer highlights = null;
177     
178     private AttributeSet JavaDoc lastAttributeSet = null;
179     
180     // The end-of-line attributes cache
181
private AttributeSet JavaDoc lastEOLAttribs = null;
182     private AttributeSet JavaDoc lastELAttribs = null;
183     private boolean theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = false;
184     
185     private HighlightingDrawLayer(String JavaDoc name, HighlightsLayerFilter filter) {
186         super(name);
187         this.filter = filter;
188     }
189
190     public void init(DrawContext ctx) {
191         super.init(ctx);
192         
193         if (highlights == null) {
194             // Initialize
195
JTextComponent JavaDoc pane = ctx.getEditorUI().getComponent();
196             
197             // HACK: the component can be null when printing, so we will just
198
// create a fake JEditorPane
199
if (pane == null) {
200                 Document JavaDoc doc = ctx.getEditorUI().getDocument();
201                 
202                 // Get the document's mime type
203
String JavaDoc mimeType = (String JavaDoc) doc.getProperty("mimeType"); //NOI18N
204
assert mimeType != null : "Document's mime type can't be null: " + doc; //NOI18N
205

206 // HACK: can't set the kit on fakePane, because it needs to run in AWT, which
207
// the print actions generally don't. So, the fakePane has EditorKit with
208
// the wrong mime type (most likely text/plain). It does not matter much, because
209
// the SyntaxHighlighting and NonLexerSyntaxHighlighting layers are registred for
210
// all mime types and we do not care about other layers.
211
//
212
// // Find the appropriate editor kit
213
// EditorKit kit = MimeLookup.getLookup(MimePath.parse(mimeType)).lookup(EditorKit.class);
214
// assert kit != null : "Can't finde EditorKit for mime type '" + mimeType + "'";
215

216                 // Create a fake pane
217
JEditorPane JavaDoc fakePane = new JEditorPane JavaDoc();
218 // fakePane.setEditorKit(kit);
219
fakePane.setDocument(doc);
220                 
221                 // Filter out all highlights layers, but syntax highlighting
222
fakePane.putClientProperty("HighlightsLayerIncludes", new String JavaDoc [] { "^.*NonLexerSyntaxHighlighting$", "^.*SyntaxHighlighting$"}); //NOI18N
223

224                 pane = fakePane;
225             }
226             this.paneRef = new WeakReference JavaDoc<JTextComponent JavaDoc>(pane);
227
228             HighlightingManager hm = HighlightingManager.getInstance();
229             this.highlights = hm.getHighlights(pane, filter);
230             this.highlights.addHighlightsChangeListener(this);
231         }
232     
233         lastAttributeSet = null;
234         
235         // Reset the end-of-line attributes cache
236
lastEOLAttribs = null;
237         lastELAttribs = null;
238         theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = false;
239     }
240     
241     public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) {
242         if (highlights != null) {
243             return processOffset(ctx, false);
244         } else {
245             return false;
246         }
247     }
248
249     public void updateContext(DrawContext ctx) {
250         if (highlights != null) {
251             if (ctx.isEOL() && ctx.isBOL()) {
252                 if (extendsEmptyLine() && !theLittleSpitAtTheBeginningOfAnEmptyLineDrawn) {
253                     theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = true;
254                     Coloring coloring = Coloring.fromAttributeSet(lastELAttribs);
255                     coloring.apply(ctx);
256                 } else {
257                     if (extendsEOL()) {
258                         Coloring coloring = Coloring.fromAttributeSet(lastEOLAttribs);
259                         coloring.apply(ctx);
260                     }
261                 }
262             } else if (ctx.isEOL()) {
263                 if (extendsEOL()) {
264                     Coloring coloring = Coloring.fromAttributeSet(lastEOLAttribs);
265                     coloring.apply(ctx);
266                 }
267             } else {
268                 processOffset(ctx, true);
269             }
270         }
271     }
272
273     public boolean extendsEOL() {
274         if (lastEOLAttribs == null && lastAttributeSet != null) {
275             @SuppressWarnings JavaDoc("unchecked")
276             List JavaDoc<AttributeSet JavaDoc> allSets = (List JavaDoc<AttributeSet JavaDoc>) lastAttributeSet.getAttribute("dismantled-structure"); //NOI18N
277
AttributeSet JavaDoc [] arr = filter(allSets != null ? allSets : Collections.singletonList(lastAttributeSet));
278             lastEOLAttribs = arr[0];
279             lastELAttribs = arr[1];
280         }
281         
282         return lastEOLAttribs != null && lastEOLAttribs != SimpleAttributeSet.EMPTY;
283     }
284     
285     public boolean extendsEmptyLine() {
286         if (lastELAttribs == null && lastAttributeSet != null) {
287             @SuppressWarnings JavaDoc("unchecked")
288             List JavaDoc<AttributeSet JavaDoc> allSets = (List JavaDoc<AttributeSet JavaDoc>) lastAttributeSet.getAttribute("dismantled-structure"); //NOI18N
289
AttributeSet JavaDoc [] arr = filter(allSets != null ? allSets : Collections.singletonList(lastAttributeSet));
290             lastEOLAttribs = arr[0];
291             lastELAttribs = arr[1];
292         }
293
294         return lastELAttribs != null && lastELAttribs != SimpleAttributeSet.EMPTY;
295     }
296     
297     // ----------------------------------------------------------------------
298
// HighlightsChangeListener implementation
299
// ----------------------------------------------------------------------
300

301     public void highlightChanged(HighlightsChangeEvent event) {
302         if (LOG.isLoggable(Level.FINEST)) {
303             LOG.finest("BRIDGE-LAYER: changed area [" + event.getStartOffset() + ", " + event.getEndOffset() + "]"); //NOI18N
304

305 // LOG.log(Level.FINE, "Dumping highlights: {");
306
// HighlightsSequence seq = highlights.getHighlights(0, Integer.MAX_VALUE);
307
// while(seq.moveNext()) {
308
// StringBuilder sb = new StringBuilder();
309
// sb.append(" <");
310
// sb.append(seq.getStartOffset());
311
// sb.append(", ");
312
// sb.append(seq.getEndOffset());
313
// sb.append(", {");
314
//
315
// Enumeration<?> attrNames = seq.getAttributes().getAttributeNames();
316
// while(attrNames.hasMoreElements()) {
317
// Object attrName = attrNames.nextElement();
318
// Object attrValue = seq.getAttributes().getAttribute(attrName);
319
//
320
// sb.append(attrName == null ? "null" : attrName.toString());
321
// sb.append(" = ");
322
// sb.append(attrValue == null ? "null" : attrValue.toString());
323
//
324
// if (attrNames.hasMoreElements()) {
325
// sb.append(", ");
326
// }
327
// }
328
//
329
// sb.append("}>");
330
// LOG.log(Level.FINE, sb.toString());
331
// }
332
// LOG.log(Level.FINE, "--- End of Dumping highlights");
333
}
334         setNextActivityChangeOffset(0);
335         
336         JTextComponent JavaDoc pane = paneRef.get();
337         if (pane != null) {
338             int rangeEnd = Math.min(event.getEndOffset(), pane.getDocument().getLength());
339             int rangeStart = event.getStartOffset() >= rangeEnd ? 0 : event.getStartOffset();
340             
341             if (rangeStart < rangeEnd) {
342                 try {
343                     pane.getUI().damageRange(pane, rangeStart, rangeEnd);
344                 } catch (Exception JavaDoc e) {
345                     LOG.log(Level.INFO, "Can't update view: range = [" + rangeStart + ", " + rangeEnd + "]", e); //NOI18N
346
}
347             }
348         }
349     }
350
351     // ----------------------------------------------------------------------
352
// Private implementation
353
// ----------------------------------------------------------------------
354

355     private boolean processOffset(DrawContext ctx, boolean applyAttributes) {
356         BaseDocument doc = ctx.getEditorUI().getDocument();
357         int currentOffset = ctx.getFragmentOffset();
358         int endOffset = doc.getParagraphElement(currentOffset).getEndOffset();
359         
360         if (endOffset >= doc.getLength()) {
361             endOffset = Integer.MAX_VALUE;
362         }
363         
364         HighlightsSequence hs = highlights.getHighlights(currentOffset, endOffset);
365         boolean hasHighlight = hs.moveNext();
366
367         if (hasHighlight) {
368             if (hs.getStartOffset() <= currentOffset) {
369                 if (applyAttributes) {
370                     Coloring coloring = Coloring.fromAttributeSet(hs.getAttributes());
371                     coloring.apply(ctx);
372                 }
373                 
374                 lastAttributeSet = hs.getAttributes();
375                 setNextActivityChangeOffset(hs.getEndOffset());
376             } else {
377                 setNextActivityChangeOffset(hs.getStartOffset());
378             }
379
380             return true;
381         } else {
382             return false;
383         }
384     }
385
386     private AttributeSet JavaDoc [] filter(List JavaDoc<AttributeSet JavaDoc> sets) {
387         ArrayList JavaDoc<AttributeSet JavaDoc> eolSets = new ArrayList JavaDoc<AttributeSet JavaDoc>();
388         ArrayList JavaDoc<AttributeSet JavaDoc> elSets = new ArrayList JavaDoc<AttributeSet JavaDoc>();
389         
390         for(AttributeSet JavaDoc set : sets) {
391             Object JavaDoc value = set.getAttribute(HighlightsContainer.ATTR_EXTENDS_EOL);
392             
393             if ((value instanceof Boolean JavaDoc) && ((Boolean JavaDoc) value).booleanValue()) {
394                 eolSets.add(set);
395             }
396             
397             value = set.getAttribute(HighlightsContainer.ATTR_EXTENDS_EMPTY_LINE);
398             if ((value instanceof Boolean JavaDoc) && ((Boolean JavaDoc) value).booleanValue()) {
399                 elSets.add(set);
400             }
401         }
402
403         AttributeSet JavaDoc eolAttribs;
404         if (eolSets.size() > 1) {
405             eolAttribs = AttributesUtilities.createComposite(eolSets.toArray(new AttributeSet JavaDoc[eolSets.size()]));
406         } else if (eolSets.size() == 1) {
407             eolAttribs = eolSets.get(0);
408         } else {
409             eolAttribs = SimpleAttributeSet.EMPTY;
410         }
411
412         AttributeSet JavaDoc elAttribs;
413         if (elSets.size() > 1) {
414             elAttribs = AttributesUtilities.createComposite(elSets.toArray(new AttributeSet JavaDoc[elSets.size()]));
415         } else if (elSets.size() == 1) {
416             elAttribs = elSets.get(0);
417         } else {
418             elAttribs = SimpleAttributeSet.EMPTY;
419         }
420
421         return new AttributeSet JavaDoc [] { eolAttribs, elAttribs };
422     }
423     
424     private static String JavaDoc simpleToString(Object JavaDoc o) {
425         return o == null ? "null" : o.getClass() + "@" + Integer.toHexString(System.identityHashCode(o)); //NOI18N
426
}
427 }
428
Popular Tags