KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > api > lexer > LanguagePath


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.api.lexer;
21
22 import java.lang.ref.Reference JavaDoc;
23 import java.lang.ref.SoftReference JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.WeakHashMap JavaDoc;
26
27 /**
28  * Language path describes a complete embedding
29  * of the languages starting from the root (top-level) language
30  * till the most embedded language.
31  * <br/>
32  * Language path consists of one root language
33  * and zero or more embedded languages.
34  * <br/>
35  * E.g. for javadoc embedded in java that is embedded in jsp
36  * then the language path <code>lp</code> would return the following:<pre>
37  * lp.size() == 3
38  * lp.language(0) == JspTokenId.language()
39  * lp.language(1) == JavaTokenId.language()
40  * lp.language(2) == JavadocTokenId.language()
41  * </pre>
42  *
43  * <p>
44  * The two language paths for the same languages in the same order
45  * represent a single object. Therefore language paths can be compared
46  * by using == operator.
47  * </p>
48  *
49  * <p>
50  * <b>Lifetime:</b>
51  * Once a particular language path is created
52  * it is held by a soft reference from its "parent" language path.
53  * </p>
54  *
55  * <p>
56  * This class may safely be used by multiple threads.
57  * </p>
58  *
59  * @author Miloslav Metelka
60  * @version 1.0
61  */

62 public final class LanguagePath {
63     
64     /**
65      * Empty language path for internal use and referencing the top-level language paths.
66      */

67     private static final LanguagePath EMPTY = new LanguagePath();
68
69     /**
70      * Get language path that contains a single language.
71      *
72      * @param language non-null language.
73      * @return non-null language path.
74      */

75     public static LanguagePath get(Language<? extends TokenId> language) {
76         return get(null, language);
77     }
78     
79     /**
80      * Get language path corresponding to the language embedded in the given context
81      * language path.
82      * <br/>
83      * For example for java scriplet embedded in jsp the prefix would
84      * be a language-path for jsp language and language would be java language.
85      * <br/>
86      * By using this method language paths with arbitrary depth can be created.
87      *
88      * @param prefix prefix language path determining the context in which
89      * the language is embedded or null if there is no prefix.
90      * @param language non-null language.
91      * @return non-null language path.
92      */

93     public static LanguagePath get(LanguagePath prefix, Language<? extends TokenId> language) {
94         if (prefix == null)
95             prefix = EMPTY;
96         return prefix.createEmbedded(language);
97     }
98     
99     /**
100      * Array of component language paths for this language path.
101      * <br>
102      * The last member of the array is <code>this</code>.
103      */

104     private final Language<? extends TokenId>[] languages;
105     
106     /**
107      * Mapping of embedded language to a weak reference to LanguagePath.
108      */

109     private Map JavaDoc<Language<? extends TokenId>, Reference JavaDoc<LanguagePath>> language2path;
110     
111     /**
112      * Cached and interned mime-path string.
113      */

114     private String JavaDoc mimePath;
115     
116     
117     private LanguagePath(LanguagePath prefix, Language<? extends TokenId> language) {
118         int prefixSize = prefix.size();
119         this.languages = allocateLanguageArray(prefixSize + 1);
120         System.arraycopy(prefix.languages, 0, this.languages, 0, prefixSize);
121         this.languages[prefixSize] = language;
122     }
123     
124     /** Build EMPTY LanguagePath */
125     private LanguagePath() {
126         this.languages = allocateLanguageArray(0);
127     }
128     
129     /**
130      * Get total number of languages in this language path.
131      *
132      * @return >=1 number of languages contained in this language path.
133      */

134     public int size() {
135         return languages.length;
136     }
137     
138     /**
139      * Get language of this language path at the given index.
140      * <br>
141      * Index zero corresponds to the root language.
142      *
143      * @param index >=0 && < {@link #size()}.
144      * @return non-null language at the given index.
145      * @throws IndexOutOfBoundsException in case the index is not within
146      * required bounds.
147      */

148     public Language<? extends TokenId> language(int index) {
149         return languages[index];
150     }
151
152     /**
153      * Check whether the language of this language path
154      * at the given index is the given language.
155      * <br>
156      * Index zero corresponds to the root language.
157      *
158      * @param index >=0 && < {@link #size()}.
159      * @return non-null language at the given index.
160      * @throws IndexOutOfBoundsException in case the index is not within
161      * required bounds.
162      */

163     public boolean isLanguage(int index, Language<? extends TokenId> language) {
164         return (language(index) == language);
165     }
166
167     /**
168      * Return the top-level language of this language path.
169      * <br/>
170      * It's equivalent to <code>language(0)</code>.
171      *
172      * @see #language(int)
173      */

174     public Language<? extends TokenId> topLanguage() {
175         return language(0);
176     }
177     
178     /**
179      * Check whether the top-level language of this language path
180      * is the given language.
181      *
182      * @see #isLanguage(int, Language)
183      */

184     public boolean isTopLanguage(Language<? extends TokenId> language) {
185         return (topLanguage() == language);
186     }
187     
188     /**
189      * Return the most inner language of this path.
190      * <br/>
191      * It's equivalent to <code>language(size() - 1)</code>.
192      *
193      * @see #language(int)
194      */

195     public Language<? extends TokenId> innerLanguage() {
196         return language(size() - 1);
197     }
198     
199     /**
200      * Check whether the most inner language of this language path
201      * is the given language.
202      *
203      * @see #isLanguage(int, Language)
204      */

205     public boolean isInnerLanguage(Language<? extends TokenId> language) {
206         return (innerLanguage() == language);
207     }
208     
209     /**
210      * Check whether this language path ends with the given language path.
211      * <br/>
212      * This may be useful for checking whether a given input contains certain language
213      * (or language path) that may possibly be embedded somewhere in the input.
214      *
215      * @param languagePath non-null language path to be checked.
216      * @return true if this language path contains the given language path
217      * at its end (applies for <code>this</code> as well).
218      */

219     public boolean endsWith(LanguagePath languagePath) {
220         if (languagePath == this || languagePath == EMPTY)
221             return true;
222         int lpSize = languagePath.size();
223         if (lpSize <= size()) {
224             for (int i = 1; i <= lpSize; i++) {
225                 if (language(size() - i) != languagePath.language(lpSize - i))
226                     return false;
227             }
228             return true;
229         }
230         return false;
231     }
232     
233     /**
234      * Gets the path starting at the given index and ending after
235      * the last language contained in this path.
236      *
237      * @see #subPath(int, int)
238      */

239     public LanguagePath subPath(int startIndex) {
240         return subPath(startIndex, size());
241     }
242
243     /**
244      * Gets the path starting at the given index and ending after
245      * the last language contained in this path.
246      *
247      * @param startIndex >=0 starting index of the requested path in this path.
248      * @param endIndex >startIndex index after the last item
249      * of the requested path.
250      * @return non-null language path containing items between startIndex and endIndex.
251      */

252     public LanguagePath subPath(int startIndex, int endIndex) {
253         if (startIndex < 0) {
254         throw new IndexOutOfBoundsException JavaDoc("startIndex=" + startIndex + " < 0"); // NOI18N
255
}
256     if (endIndex > size()) {
257         throw new IndexOutOfBoundsException JavaDoc("endIndex=" + endIndex + " > size()=" + size());
258     }
259     if (startIndex >= endIndex) {
260         throw new IndexOutOfBoundsException JavaDoc("startIndex=" + startIndex + " >= endIndex=" + endIndex);
261     }
262     if (startIndex == 0 && endIndex == size()) {
263             return this;
264         }
265         LanguagePath lp = LanguagePath.get(language(startIndex++));
266         while (startIndex < endIndex) {
267             lp = LanguagePath.get(lp, language(startIndex++));
268         }
269         return lp;
270     }
271
272     /**
273      * Gets the mime path equivalent of this language path. The mime path is
274      * a concatenation of mime types of all the languages in this language path.
275      * The mime types are separated by the '/' character.
276      *
277      * <p>
278      * For example the language path of the java language embedded in the
279      * JSP language will return 'text/x-jsp/text/x-java' when this method is called.
280      * </p>
281      *
282      * <p>
283      * The returned string path can be used in MimeLookup's operation
284      * to obtain a corresponding MimePath object by using
285      * <code>MimePath.parse(returned-mime-path-string)</code>.
286      * </p>
287      *
288      * @return The mime path string.
289      * @see org.netbeans.spi.lexer.LanguageHierarchy#mimeType()
290      */

291     public String JavaDoc mimePath() {
292         synchronized (languages) {
293             if (mimePath == null) {
294                 StringBuilder JavaDoc sb = new StringBuilder JavaDoc(15 * languages.length);
295                 for (Language<? extends TokenId> language : languages) {
296                     if (sb.length() > 0) {
297                         sb.append('/');
298                     }
299                     sb.append(language.mimeType());
300                 }
301                 // Intern the mimePath for faster operation of MimePath.parse()
302
mimePath = sb.toString().intern();
303             }
304             return mimePath;
305         }
306     }
307     
308     private LanguagePath createEmbedded(Language<? extends TokenId> language) {
309         if (language == null) {
310             throw new IllegalArgumentException JavaDoc("language cannot be null");
311         }
312         // Attempt to retrieve from the cache first
313
synchronized (languages) {
314             if (language2path == null) {
315                 language2path = new WeakHashMap JavaDoc<Language<? extends TokenId>,Reference JavaDoc<LanguagePath>>();
316             }
317             Reference JavaDoc<LanguagePath> lpRef = language2path.get(language);
318             LanguagePath lp;
319             if (lpRef == null || (lp = (LanguagePath)lpRef.get()) == null) {
320                 // Construct the LanguagePath
321
lp = new LanguagePath(this, language);
322                 language2path.put(language, new SoftReference JavaDoc<LanguagePath>(lp));
323             }
324         
325             return lp;
326         }
327     }
328     
329     private Language<? extends TokenId>[] allocateLanguageArray(int length) {
330         return (Language<? extends TokenId>[])(new Language[length]);
331     }
332     
333     public String JavaDoc toString() {
334         StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
335         sb.append("LanguagePath: size=");
336         sb.append(size());
337         sb.append('\n');
338         for (int i = 0; i < size(); i++) {
339             sb.append('[').append(i).append("]: "); // NOI18N
340
sb.append(language(i)).append('\n');
341         }
342         return sb.toString();
343     }
344     
345 }
Popular Tags