KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > lexer > editorbridge > LexerLayer


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.lexer.editorbridge;
21
22 import java.util.List JavaDoc;
23 import java.util.Stack JavaDoc;
24 import javax.swing.text.AttributeSet JavaDoc;
25 import javax.swing.text.Document JavaDoc;
26 import org.netbeans.api.lexer.TokenHierarchyEvent;
27 import org.netbeans.api.lexer.TokenHierarchyListener;
28 import org.netbeans.editor.DrawLayer;
29 import org.netbeans.editor.DrawContext;
30 import org.netbeans.editor.MarkFactory;
31 import org.netbeans.editor.Coloring;
32 import org.netbeans.api.lexer.TokenId;
33 import org.netbeans.api.lexer.Token;
34 import javax.swing.text.JTextComponent JavaDoc;
35 import java.beans.PropertyChangeListener JavaDoc;
36 import java.beans.PropertyChangeEvent JavaDoc;
37 import java.util.Enumeration JavaDoc;
38 import javax.swing.text.EditorKit JavaDoc;
39 import org.netbeans.api.editor.mimelookup.MimeLookup;
40 import org.netbeans.api.editor.mimelookup.MimePath;
41 import org.netbeans.api.editor.settings.FontColorSettings;
42 import org.netbeans.api.lexer.Language;
43 import org.netbeans.api.lexer.LanguagePath;
44 import org.netbeans.api.lexer.TokenHierarchy;
45 import org.netbeans.api.lexer.TokenSequence;
46 import org.openide.util.Lookup;
47
48 final class LexerLayer extends DrawLayer.AbstractLayer {
49
50     public static final String JavaDoc NAME = "lexer-layer"; //NOI18N
51

52     public static final int VISIBILITY = 1050;
53
54     private static final Coloring NULL_COLORING = new Coloring();
55     
56     private final JTextComponent JavaDoc component;
57
58     private Listener listener;
59
60     private TokenHierarchy listenerHierarchy;
61
62     private Stack JavaDoc<TokenSequence> pastSequences;
63     private TokenSequence tokenSequence;
64     private boolean moveNext = true;
65     private boolean goToEmbed = true;
66
67     private int tokenEndOffset;
68     
69     private Coloring coloring;
70     
71     private boolean active;
72     
73     
74     public LexerLayer(JTextComponent JavaDoc component) {
75         super(NAME);
76
77         assert (component != null);
78         this.component = component;
79
80         Document JavaDoc doc = component.getDocument();
81         component.addPropertyChangeListener(
82             new PropertyChangeListener JavaDoc() {
83                 public void propertyChange(PropertyChangeEvent JavaDoc evt) {
84                     if ("document".equals(evt.getPropertyName())) { //NOI18N
85
// Remove old listening
86
if (listenerHierarchy != null) {
87                             listenerHierarchy.removeTokenHierarchyListener(listener);
88                             listenerHierarchy = null;
89                         }
90                     }
91                 }
92             }
93         );
94     }
95     
96     public boolean extendsEOL() {
97         return true;
98     }
99     
100     public void init(final DrawContext ctx) {
101         coloring = null;
102         tokenEndOffset = 0;
103         int startOffset = ctx.getStartOffset();
104
105         TokenHierarchy hi = tokenHierarchy();
106         active = (hi != null);
107
108         if (active) {
109             pastSequences = new Stack JavaDoc<TokenSequence>();
110             tokenSequence = hi.tokenSequence();
111             tokenSequence.move(startOffset);
112             if (tokenSequence.moveNext()) {
113                 updateTokenEndOffsetAndColoring(startOffset);
114             } else { // no tokens
115
active = false;
116             }
117         }
118     }
119     
120     public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) {
121         return active;
122     }
123     
124     public void updateContext(DrawContext ctx) {
125         if (coloring != null) {
126             coloring.apply(ctx);
127         }
128
129         int fragEndOffset = ctx.getFragmentOffset() + ctx.getFragmentLength();
130         while (active && fragEndOffset >= tokenEndOffset) {
131             if (!moveNext || tokenSequence.moveNext()) {
132                 updateTokenEndOffsetAndColoring(-1);
133             } else {
134                 if (pastSequences.isEmpty()) {
135                     active = false;
136                 } else {
137                     tokenSequence = pastSequences.pop();
138                     if ((tokenSequence.offset() + tokenSequence.token().length()) > tokenEndOffset) {
139                         //highlight the rest of the popped token:
140
goToEmbed = false;
141                         moveNext = false;
142                     }
143                 }
144             }
145         }
146     }
147
148     private TokenHierarchy tokenHierarchy() {
149         TokenHierarchy hi = TokenHierarchy.get(component.getDocument());
150         // Possibly start listening on the token changes in the hierarchy
151
if (hi != null && listenerHierarchy == null) {
152             if (listener == null) {
153                 listener = new Listener();
154             }
155             listenerHierarchy = hi;
156             listenerHierarchy.addTokenHierarchyListener(listener);
157         }
158         return hi;
159     }
160     
161     private void updateTokenEndOffsetAndColoring(int offset) {
162         int origOffset = tokenSequence.offset();
163         boolean isInside = tokenSequence.offset() < offset;
164         Token origToken = tokenSequence.token();
165         LanguagePath origPath = tokenSequence.languagePath();
166         boolean wasEmbedd = false;
167         
168         while (origOffset == tokenSequence.offset() && goToEmbed) {
169             TokenSequence embed = tokenSequence.embedded();
170             
171             if (embed != null) {
172                 wasEmbedd = true;
173                 if (offset == (-1)) {
174                     if (embed.moveNext()) {
175                         pastSequences.push(tokenSequence);
176                         tokenSequence = embed;
177                     } else {
178                         break;
179                     }
180                 } else {
181                     embed.move(offset);
182                     if (embed.moveNext()) {
183                         pastSequences.push(tokenSequence);
184                         tokenSequence = embed;
185                     } else {
186                         break;
187                     }
188                 }
189             } else {
190                 break;
191             }
192         }
193         
194         goToEmbed = true;
195         
196         Token token;
197         LanguagePath path;
198         
199         if (origOffset == tokenSequence.offset() || isInside) {
200             token = tokenSequence.token();
201             tokenEndOffset = tokenSequence.offset() + token.length();
202             path = tokenSequence.languagePath();
203             moveNext = true;
204         } else {
205             token = origToken;
206             tokenEndOffset = tokenSequence.offset();
207             path = origPath;
208             moveNext = !wasEmbedd;
209         }
210         
211         setNextActivityChangeOffset(tokenEndOffset);
212         coloring = findColoring(token.id(), path);
213     }
214
215     private Coloring findColoring(TokenId tokenId, LanguagePath languagePath) {
216         MimePath mimePath = languagePathToMimePathHack(languagePath);
217         Lookup lookup = MimeLookup.getLookup(mimePath);
218         FontColorSettings fcs = lookup.lookup(FontColorSettings.class);
219         AttributeSet JavaDoc attribs = findFontAndColors(fcs, tokenId, languagePath.innerLanguage());
220      
221 // dumpAttribs(attribs, tokenId.name(), languagePath.mimePath());
222

223         return attribs == null ? NULL_COLORING : Coloring.fromAttributeSet(attribs);
224     }
225
226     private void dumpAttribs(AttributeSet JavaDoc attribs, String JavaDoc token, String JavaDoc lang) {
227 // if (!lang.contains("xml")) {
228
// return;
229
// }
230

231         StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
232         sb.append("Attribs for token '"); //NOI18N
233
sb.append(token);
234         sb.append("', language '"); //NOI18N
235
sb.append(lang);
236         sb.append("' = {"); //NOI18N
237

238         if (attribs != null) {
239             Enumeration JavaDoc<?> keys = attribs.getAttributeNames();
240             while (keys.hasMoreElements()) {
241                 Object JavaDoc key = keys.nextElement();
242                 Object JavaDoc value = attribs.getAttribute(key);
243
244                 sb.append("'" + key + "' = '" + value + "'"); //NOI18N
245
if (keys.hasMoreElements()) {
246                     sb.append(", "); //NOI18N
247
}
248             }
249         }
250         
251         sb.append("} LexerLayer.this = "); //NOI18N
252
sb.append(this.toString());
253         
254         System.out.println(sb.toString());
255     }
256     
257     // XXX: This hack is here to make sure that preview panels in Tools-Options
258
// work. Currently there is no way how to force a particular JTextComponent
259
// to use a particular MimeLookup. They all use MimeLookup common for all components
260
// and for the mime path of things displayed in that component. The preview panels
261
// however need special MimeLookup that loads colorings from a special profile
262
// (i.e. not the currently active coloring profile, which is used normally by
263
// all the other components).
264
//
265
// The hack is that Tools-Options modifies mime type of the document loaded
266
// in the preview panel and prepend 'textXXXX_' at the beginning. The normal
267
// MimeLookup for this mime type and any mime path derived from this mime type
268
// is empty. The editor/settings/storage however provides a special handling
269
// for these 'test' mime paths and bridge them to the MimeLookup that you would
270
// normally get for the mime path without the 'testXXXX_' at the beginning, plus
271
// they supply special colorings from the profile called 'testXXXX'. This way
272
// the preview panels can have different colorings from the rest of the IDE.
273
//
274
// This is obviously very fragile and not fully transparent for clients as
275
// you can see here. We need a better solution for that. Generally it should
276
// be posible to ask somewhere for a component-specific MimeLookup. This would
277
// normally be a standard MimeLookup as you know it, but in special cases it
278
// could be modified by the client code that created the component - e.g. Tools-Options
279
// panel.
280
private MimePath languagePathToMimePathHack(LanguagePath languagePath) {
281         String JavaDoc mimeType = getMimeType(component);
282         if (languagePath.size() == 1) {
283             return MimePath.parse(mimeType);
284         } else if (languagePath.size() > 1) {
285             return MimePath.parse(mimeType + "/" + languagePath.subPath(1).mimePath()); //NOI18N
286
} else {
287             throw new IllegalStateException JavaDoc("LanguagePath should not be empty."); //NOI18N
288
}
289     }
290
291     private String JavaDoc getMimeType(JTextComponent JavaDoc c) {
292         Object JavaDoc mimeTypeProp = c.getDocument().getProperty("mimeType"); //NOI18N
293
if (mimeTypeProp instanceof String JavaDoc) {
294             return (String JavaDoc) mimeTypeProp;
295         } else {
296             return c.getUI().getEditorKit(c).getContentType();
297         }
298     }
299     
300     private String JavaDoc updateColoringName(String JavaDoc coloringName) {
301         EditorKit JavaDoc kit = component.getUI().getEditorKit(component);
302         if (kit instanceof LexerEditorKit) {
303             String JavaDoc updatedName = ((LexerEditorKit)kit).updateColoringName(coloringName);
304             if (updatedName != null) {
305                 coloringName = updatedName;
306             }
307         }
308         return coloringName;
309     }
310     
311     private AttributeSet JavaDoc findFontAndColors(FontColorSettings fcs, TokenId tokenId, Language lang) {
312         // First try the token's name
313
String JavaDoc name = tokenId.name();
314         AttributeSet JavaDoc attribs = fcs.getTokenFontColors(updateColoringName(name));
315
316         // Then try the primary category
317
if (attribs == null) {
318             String JavaDoc primary = tokenId.primaryCategory();
319             if (primary != null) {
320                 attribs = fcs.getTokenFontColors(updateColoringName(primary));
321             }
322         }
323
324         // Then try all the other categories
325
if (attribs == null) {
326             @SuppressWarnings JavaDoc("unchecked") //NOI18N
327
List JavaDoc<String JavaDoc> categories = ((Language<TokenId>)lang).nonPrimaryTokenCategories(tokenId);
328             for(String JavaDoc c : categories) {
329                 attribs = fcs.getTokenFontColors(updateColoringName(c));
330                 if (attribs != null) {
331                     break;
332                 }
333             }
334         }
335
336         return attribs;
337     }
338     
339     private final class Listener implements TokenHierarchyListener {
340
341         public void tokenHierarchyChanged(TokenHierarchyEvent evt) {
342             javax.swing.plaf.TextUI JavaDoc ui = (javax.swing.plaf.TextUI JavaDoc)component.getUI();
343             int startRepaintOffset = evt.affectedStartOffset();
344             int endRepaintOffset = Math.max(evt.affectedEndOffset(), startRepaintOffset + 1);
345             ui.damageRange(component, startRepaintOffset, endRepaintOffset);
346         }
347
348     } // End of Listener class
349

350 }
351
352
Popular Tags