KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > lexer > LanguageOperation


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.lib.lexer;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Set JavaDoc;
27 import org.netbeans.api.lexer.Language;
28 import org.netbeans.api.lexer.LanguagePath;
29 import org.netbeans.api.lexer.Token;
30 import org.netbeans.api.lexer.TokenId;
31 import org.netbeans.lib.editor.util.CharSequenceUtilities;
32 import org.netbeans.lib.lexer.token.DefaultToken;
33 import org.netbeans.spi.lexer.LanguageHierarchy;
34 import org.netbeans.lib.lexer.token.TextToken;
35 import org.netbeans.spi.lexer.LanguageEmbedding;
36 import org.netbeans.spi.lexer.TokenFactory;
37 import org.netbeans.spi.lexer.TokenValidator;
38 import org.openide.util.WeakListeners;
39
40 /**
41  * The operation behind the language hierarchy.
42  *
43  * @author Miloslav Metelka
44  * @version 1.00
45  */

46
47 public final class LanguageOperation<T extends TokenId> implements PropertyChangeListener JavaDoc {
48     
49     private static final int MAX_START_SKIP_LENGTH_CACHED = 10;
50     
51     private static final int MAX_END_SKIP_LENGTH_CACHED = 10;
52     
53     private static final TokenValidator<TokenId> NULL_VALIDATOR
54             = new TokenValidator<TokenId>() {
55                 public Token<TokenId> validateToken(Token<TokenId> token,
56                 TokenFactory<TokenId> factory,
57                 CharSequence JavaDoc tokenText, int modRelOffset,
58                 int removedLength, CharSequence JavaDoc removedText,
59                 int insertedLength, CharSequence JavaDoc insertedText) {
60                     return null;
61                 }
62     };
63     
64     /**
65      * Find the language paths either for this language only
66      * or from TokenHierarchyOperation when adding a new default or custom embedding
67      * to the token hierarchy.
68      * <br/>
69      * As a language may finally be embedded in itself (e.g. someone might
70      * want to syntax color java code snippet embedded in javadoc)
71      * this method must prevent creation of infinite language paths
72      * by using exploredLanguages parameter.
73      *
74      * @param existingLanguagePaths set of language paths that are already known.
75      * This set is not modified by the method.
76      * @param newLanguagePaths newly discovered language paths will be added to this set.
77      * @param exploredLanguages used for checking whether the subpaths containing
78      * this language were already discovered.
79      * @param lp language path that will be checked. It must contain the "language"
80      * parameter as the most embedded language.
81      * @param language language which token ids will be checked whether they contain
82      * default embeddings or not.
83      */

84     public static <T extends TokenId> void findLanguagePaths(
85     Set JavaDoc<LanguagePath> existingLanguagePaths, Set JavaDoc<LanguagePath> newLanguagePaths,
86     Set JavaDoc<Language<? extends TokenId>> exploredLanguages, LanguagePath lp,
87     Language<T> language) {
88         // Get the complete language path
89
if (!existingLanguagePaths.contains(lp)) {
90             newLanguagePaths.add(lp);
91         }
92         if (!exploredLanguages.contains(language)) {
93             exploredLanguages.add(language);
94             Set JavaDoc<T> ids = language.tokenIds();
95             for (T id : ids) {
96                 // Create a fake empty token
97
DefaultToken<T> emptyToken = new DefaultToken<T>(id);
98                 LanguageEmbedding<? extends TokenId> embedding
99                         = LexerUtilsConstants.findEmbedding(emptyToken, lp, null);
100                 if (embedding != null) {
101                     LanguagePath elp = LanguagePath.get(lp, embedding.language());
102                     findLanguagePaths(existingLanguagePaths, newLanguagePaths,
103                             exploredLanguages, elp, embedding.language());
104                 }
105             }
106         }
107     }
108     
109
110     private LanguageHierarchy<T> languageHierarchy;
111     
112     private Language<T> language;
113     
114     /** Embeddings caached by start skip length and end skip length. */
115     private LanguageEmbedding<T>[][] cachedEmbeddings;
116     
117     private LanguageEmbedding<T>[][] cachedJoinSectionsEmbeddings;
118     
119     private TokenValidator<T>[] tokenValidators;
120     
121     private Set JavaDoc<LanguagePath> languagePaths;
122     
123     private Set JavaDoc<Language<? extends TokenId>> exploredLanguages;
124     
125     private FlyItem<T>[] flyItems;
126     
127     public LanguageOperation(LanguageHierarchy<T> languageHierarchy) {
128         this.languageHierarchy = languageHierarchy;
129         // Listen on changes in language manager
130
LanguageManager.getInstance().addPropertyChangeListener(
131         WeakListeners.create(PropertyChangeListener JavaDoc.class, this, LanguageManager.getInstance()));
132     }
133     
134     public LanguageHierarchy<T> languageHierarchy() {
135         return languageHierarchy;
136     }
137     
138     /**
139      * Get language at this level of language hierarchy.
140      *
141      * @return non-null language.
142      */

143     public synchronized Language<T> language() {
144         if (language == null) {
145             // Cause api accessor impl to get initialized
146
try {
147                 Class.forName(Language.class.getName(), true, LanguageOperation.class.getClassLoader());
148             } catch (ClassNotFoundException JavaDoc e) {
149                 //cannot happen
150
}
151             
152             // Both tokenIds() and tokenCategories() should impose no locks
153
// so call in synchronized block
154
language = LexerApiPackageAccessor.get().createLanguage(
155                     languageHierarchy);
156         }
157         return language;
158     }
159     
160     public synchronized TokenValidator<T> tokenValidator(T id) {
161         if (tokenValidators == null) {
162             tokenValidators = allocateTokenValidatorArray(language.maxOrdinal() + 1);
163         }
164         // Not synced intentionally (no problem to create dup instances)
165
TokenValidator<T> validator = tokenValidators[id.ordinal()];
166         if (validator == null) {
167             validator = LexerSpiPackageAccessor.get().createTokenValidator(languageHierarchy(), id);
168             if (validator == null) {
169                 validator = nullValidator();
170             }
171             tokenValidators[id.ordinal()] = validator;
172         }
173         return (validator == nullValidator()) ? null : validator;
174     }
175     
176     public synchronized TextToken<T> getFlyweightToken(T id, String JavaDoc text) {
177         TextToken<T> token;
178         if (flyItems == null) {
179             // Create flyItems array
180
@SuppressWarnings JavaDoc("unchecked")
181             FlyItem<T>[] arr = (FlyItem<T>[])new FlyItem[language.maxOrdinal() + 1];
182             flyItems = arr;
183         }
184         FlyItem<T> item = flyItems[id.ordinal()];
185         if (item == null) {
186             token = new TextToken<T>(id, text); // create flyweight token
187
token.makeFlyweight();
188             flyItems[id.ordinal()] = new FlyItem<T>(token);
189         } else { // already a valid item
190
token = item.token();
191             if (token.text() != text) {
192                 token = item.token2();
193                 if (token == null || token.text() != text) {
194                     token = item.token();
195                     if (!CharSequenceUtilities.textEquals(token.text(), text)) {
196                         token = item.token2();
197                         if (token == null || !CharSequenceUtilities.textEquals(token.text(), text)) {
198                             // Create new token
199
token = new TextToken<T>(id, text);
200                             token.makeFlyweight();
201                         }
202                         item.pushToken(token);
203                     }
204                 } else { // found token2
205
item.pushToken(token);
206                 }
207             }
208         }
209         assert (token != null); // Should return non-null token
210
return token;
211     }
212     
213     /**
214      * Get cached or create a new embedding with the language of this operation
215      * and the given start and end skip lengths.
216      * @return non-null embedding.
217      */

218     public synchronized LanguageEmbedding<T> getEmbedding(
219     int startSkipLength, int endSkipLength, boolean joinSections) {
220         LanguageEmbedding<T>[][] ce = joinSections ? cachedJoinSectionsEmbeddings : cachedEmbeddings;
221         if (ce == null || startSkipLength >= ce.length) {
222             if (startSkipLength > MAX_START_SKIP_LENGTH_CACHED)
223                 return createEmbedding(startSkipLength, endSkipLength, joinSections);
224             @SuppressWarnings JavaDoc("unchecked")
225             LanguageEmbedding<T>[][] tmp = (LanguageEmbedding<T>[][])
226                     new LanguageEmbedding[startSkipLength + 1][];
227             if (ce != null)
228                 System.arraycopy(ce, 0, tmp, 0, ce.length);
229             ce = tmp;
230             if (joinSections)
231                 cachedJoinSectionsEmbeddings = ce;
232             else
233                 cachedEmbeddings = ce;
234         }
235         LanguageEmbedding<T>[] byESL = ce[startSkipLength];
236         if (byESL == null || endSkipLength >= byESL.length) { // given endSkipLength not cached
237
if (endSkipLength > MAX_END_SKIP_LENGTH_CACHED)
238                 return createEmbedding(startSkipLength, endSkipLength, joinSections);
239             @SuppressWarnings JavaDoc("unchecked")
240             LanguageEmbedding<T>[] tmp = (LanguageEmbedding<T>[])
241                     new LanguageEmbedding[endSkipLength + 1];
242             if (byESL != null)
243                 System.arraycopy(byESL, 0, tmp, 0, byESL.length);
244             byESL = tmp;
245             ce[startSkipLength] = byESL;
246         }
247         LanguageEmbedding<T> e = byESL[endSkipLength];
248         if (e == null) {
249             e = createEmbedding(startSkipLength, endSkipLength, joinSections);
250             byESL[endSkipLength] = e;
251         }
252         return e;
253     }
254     
255     private LanguageEmbedding<T> createEmbedding(int startSkipLength, int endSkipLength, boolean joinSections) {
256         return LexerSpiPackageAccessor.get().createLanguageEmbedding(
257                 language(), startSkipLength, endSkipLength, joinSections);
258     }
259     
260     /**
261      * Get static language paths for this language.
262      */

263     public Set JavaDoc<LanguagePath> languagePaths() {
264         Set JavaDoc<LanguagePath> lps;
265         synchronized (this) {
266             lps = languagePaths;
267         }
268         if (lps == null) {
269             lps = new HashSet JavaDoc<LanguagePath>();
270             Set JavaDoc<LanguagePath> existingLps = Collections.emptySet();
271             Set JavaDoc<Language<? extends TokenId>> exploredLangs = new HashSet JavaDoc<Language<? extends TokenId>>();
272             findLanguagePaths(existingLps, lps, exploredLangs, LanguagePath.get(language()), language());
273             synchronized (this) {
274                 languagePaths = lps;
275                 exploredLanguages = exploredLangs;
276             }
277         }
278         return lps;
279     }
280     
281     public Set JavaDoc<Language<? extends TokenId>> exploredLanguages() {
282         languagePaths(); // Init exploredLanguages
283
return exploredLanguages;
284     }
285
286     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
287         synchronized (this) {
288             languagePaths = null;
289             exploredLanguages = null;
290         }
291     }
292
293     @SuppressWarnings JavaDoc("unchecked")
294     private final TokenValidator<T> nullValidator() {
295         return (TokenValidator<T>)NULL_VALIDATOR;
296     }
297
298     @SuppressWarnings JavaDoc("unchecked")
299     private final TokenValidator<T>[] allocateTokenValidatorArray(int length) {
300         return (TokenValidator<T>[]) new TokenValidator[length];
301     }
302
303     private static final class FlyItem<T extends TokenId> {
304         
305         private TextToken<T> token;
306         
307         private TextToken<T> token2;
308         
309         public FlyItem(TextToken<T> token) {
310             this.token = token;
311         }
312         
313         public TextToken<T> token() {
314             return token;
315         }
316         
317         public TextToken<T> token2() {
318             return token2;
319         }
320         
321         public void pushToken(TextToken<T> token) {
322             this.token2 = this.token;
323             this.token = token;
324         }
325         
326     }
327
328 }
329
Popular Tags