KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > TokenContextPath


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.editor;
21
22 import java.util.HashMap JavaDoc;
23 import java.util.ArrayList JavaDoc;
24
25 /**
26 * Immutable and 'interned' wrapper holding
27 * an array of the contexts starting
28 * with the original context in which the token is defined
29 * and ending with the target context from which the token
30 * is being returned.
31 * It is final and has no public constructor.
32 * The only entrypoint is through the <tt>get()</tt> method.
33 * It's guaranteed that the two context-paths containing
34 * the same contexts in the same order are the same objects
35 * and the equal-operator can be used instead of calling <tt>equals()</tt>.
36 *
37 * @author Miloslav Metelka
38 * @version 1.00
39 */

40
41 public final class TokenContextPath {
42
43     /** Cache containing [ArrayMatcher, TokenContextPath] pairs. */
44     private static final HashMap JavaDoc registry = new HashMap JavaDoc(199);
45
46     /** Contexts contained in this context-path. */
47     private TokenContext[] contexts;
48
49     /** Path for context-array without the last member. */
50     private TokenContextPath parent;
51
52     /** Path corresponding to the first context in the context-array. */
53     private TokenContextPath base;
54
55     /** Full name-prefix consisting of the prefixes
56     * of all the contexts in the path.
57     */

58     private String JavaDoc namePrefix;
59
60     /** Map holding [token-name, prefixed-token-name] pairs. */
61     private final HashMap JavaDoc tokenNameCache = new HashMap JavaDoc();
62
63     /** Map holding [start-path-replacement, replaced-path] */
64     private final HashMap JavaDoc replaceStartCache = new HashMap JavaDoc();
65
66     /** Get the context-path for non-empty array of the contexts. */
67     static synchronized TokenContextPath get(TokenContext[] contexts) {
68         if (contexts == null || contexts.length == 0) {
69             throw new IllegalArgumentException JavaDoc("Contexts must be valid and non-empty."); // NOI18N
70
}
71
72         ArrayMatcher am = new ArrayMatcher(contexts);
73         TokenContextPath path = (TokenContextPath)registry.get(am);
74         if (path == null) {
75             path = new TokenContextPath(contexts);
76             registry.put(am, path);
77         }
78
79         return path;
80     }
81
82     /** Construction from outside prohibited. */
83     private TokenContextPath(TokenContext[] contexts) {
84         this.contexts = contexts;
85
86         if (contexts.length == 1) {
87             base = this; // it's base for itself
88
}
89     }
90
91     /** Retrieve the contexts of this context-path. The contexts
92     * of the context-array must NOT be modified in any way.
93     */

94     public TokenContext[] getContexts() {
95         return contexts;
96     }
97
98     /** Get the length of the path returning the length of the contexts array. */
99     public int length() {
100         return contexts.length;
101     }
102
103     /** Get parent context-path that consists of all the contexts
104     * of this path except the last one.
105     */

106     public TokenContextPath getParent() {
107         if (parent == null && contexts.length > 1) {
108             TokenContext[] parentContexts = new TokenContext[contexts.length - 1];
109             System.arraycopy(contexts, 0, parentContexts, 0, contexts.length - 1);
110
111             parent = get(parentContexts);
112         }
113
114         return parent;
115     }
116
117     /** Get the base path which corresponds to only the first context
118     * in the context-array. The base path can be used for fast checking
119     * of the origin path of the token.
120     */

121     public TokenContextPath getBase() {
122         if (base == null) {
123             base = getParent().getBase();
124         }
125
126         return base;
127     }
128
129     /** Does this path contain the given path. It corresponds
130     * to the situation when the contexts of the given path
131     * are at the begining of this path.
132     */

133     public boolean contains(TokenContextPath tcp) {
134         if (tcp == this) {
135             return true;
136
137         } else if (contexts.length > 1) {
138             return getParent().contains(tcp);
139
140         } else {
141             return false;
142         }
143     }
144
145     /** Get the path which has the initial part of the path
146      * (usually only the base path) replaced by the given path. The length
147      * of the replaced part of the path is the same as the length
148      * of the path that will replace it.
149      * For better performance the method caches the [byPath, result-path]
150      * in hashmap.
151      * @param byPath path that will replace the initial portion
152      * of this path. The length of the portion is the same as the length
153      * of this parameter.
154      * @return the path with the initial part being replaced.
155      */

156     public TokenContextPath replaceStart(TokenContextPath byPath) {
157         // Check whether byPath isn't longer than this path
158
if (contexts.length < byPath.contexts.length) {
159             throw new IllegalArgumentException JavaDoc("byPath=" + byPath + " is too long."); // NOI18N
160
}
161
162         synchronized (replaceStartCache) {
163             TokenContextPath ret = (TokenContextPath)replaceStartCache.get(byPath);
164             if (ret == null) {
165                 TokenContext[] targetContexts = (TokenContext[])contexts.clone();
166                 for (int i = byPath.contexts.length - 1; i >= 0; i--) {
167                     targetContexts[i] = byPath.contexts[i];
168                 }
169                 ret = get(targetContexts);
170                 replaceStartCache.put(byPath, ret);
171             }
172
173             return ret;
174         }
175     }
176
177     /** Get the prefix that this context adds to the name of its tokens. */
178     public String JavaDoc getNamePrefix() {
179         if (namePrefix == null) {
180             if (contexts.length == 1) {
181                 namePrefix = contexts[0].getNamePrefix();
182
183             } else { // path has more contexts
184
namePrefix = (contexts[contexts.length - 1].getNamePrefix()
185                               + getParent().getNamePrefix()).intern();
186             }
187         }
188
189         return namePrefix;
190     }
191
192     /** Get the token-name with the name-prefix of this context-path.
193     * It merges the token-name with the name-prefix of this context-path
194     * but it does it without creating a new object.
195     */

196     public String JavaDoc getFullTokenName(TokenCategory tokenIDOrCategory) {
197         String JavaDoc tokenName = tokenIDOrCategory.getName();
198         String JavaDoc fullName;
199         synchronized (tokenNameCache) {
200             fullName = (String JavaDoc)tokenNameCache.get(tokenName);
201             if (fullName == null) {
202                 fullName = (getNamePrefix() + tokenName).intern();
203                 tokenNameCache.put(tokenName, fullName);
204             }
205         }
206
207         return fullName;
208     }
209
210     public String JavaDoc toString() {
211         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("|"); // NOI18N
212
for (int i = 0; i < contexts.length; i++) {
213             String JavaDoc shortName = contexts[i].getClass().getName();
214             shortName = shortName.substring(shortName.lastIndexOf('.') + 1);
215
216             sb.append('<');
217             sb.append(shortName);
218             sb.append('>');
219         }
220         sb.append('|');
221
222         return sb.toString();
223     }
224
225     private static final class ArrayMatcher {
226
227         /** Cached hash-code */
228         private int hash;
229
230         private TokenContext[] contexts;
231
232         ArrayMatcher(TokenContext[] contexts) {
233             this.contexts = contexts;
234         }
235
236         public int hashCode() {
237             int h = hash;
238             if (h == 0) {
239                 for (int i = contexts.length - 1; i >= 0; i--) {
240                     h = h * 31 + contexts[i].hashCode(); // !!!
241
}
242                 hash = h;
243             }
244
245             return h;
246         }
247
248         public boolean equals(Object JavaDoc o) {
249             if (this == o) {
250                 return true;
251             }
252
253             if (o instanceof ArrayMatcher) {
254                 ArrayMatcher am = (ArrayMatcher)o;
255                 if (contexts.length == am.contexts.length) {
256                     for (int i = contexts.length - 1; i >= 0; i--) {
257                         if (!contexts[i].equals(am.contexts[i])) {
258                             return false;
259                         }
260                     }
261                     return true;
262                 }
263             }
264
265             return false;
266         }
267
268     }
269
270 }
271
Popular Tags