001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.editor;
043:
044: import java.lang.reflect.Field;
045: import java.lang.reflect.Modifier;
046: import java.util.HashMap;
047: import java.util.Arrays;
048: import java.util.ArrayList;
049:
050: /**
051: * Token context defines the environment in which only a limited set
052: * of tokens can be used. This set can be retrieved by calling
053: * the <tt>getTokenIDs()</tt> method. The context can contain other
054: * contexts which means that the context can possibly switch
055: * into one of its children contexts during lexing.
056: * The child context can also have other children that can
057: * work in the same way.
058: * In this way context-paths can be created. They describe
059: * the way how was the token lexically recognized.
060: * The context can be retrieved when the syntax-class is known
061: * using the <tt>get</tt> method.
062: *
063: *
064: * @author Miloslav Metelka
065: * @version 1.00
066: */
067:
068: public class TokenContext {
069:
070: private static final TokenContext[] EMPTY_CHILDREN = new TokenContext[0];
071:
072: private final String namePrefix;
073:
074: private final TokenContext[] children;
075:
076: private final HashMap pathCache = new HashMap(37);
077:
078: private final ArrayList tokenIDList = new ArrayList();
079:
080: private final ArrayList tokenCategoryList = new ArrayList();
081:
082: private TokenID[] tokenIDs;
083:
084: private TokenCategory[] tokenCategories;
085:
086: private TokenContextPath contextPath;
087:
088: private TokenContextPath[] allContextPaths;
089:
090: private TokenContextPath[] lastContextPathPair;
091:
092: public TokenContext(String namePrefix) {
093: this (namePrefix, EMPTY_CHILDREN);
094: }
095:
096: /** Construct new token-context.
097: * @param namePrefix name that will prefix all the token-ids names.
098: * @param children child token contexts.
099: */
100: public TokenContext(String namePrefix, TokenContext[] children) {
101: if (namePrefix == null) {
102: throw new IllegalArgumentException(
103: "Name prefix must be non-null."); // NOI18N
104: }
105:
106: this .namePrefix = namePrefix.intern();
107: this .children = (children != null) ? children : EMPTY_CHILDREN;
108:
109: contextPath = TokenContextPath.get(new TokenContext[] { this });
110: }
111:
112: /** Get the prefix that this context adds to the name of its tokens. */
113: public String getNamePrefix() {
114: return namePrefix;
115: }
116:
117: /** Get the children contexts of this context. It returns empty-array
118: * if there are no children.
119: */
120: public TokenContext[] getChildren() {
121: return children;
122: }
123:
124: /** Add token-id to the set of token-ids that belong to this context. */
125: protected void addTokenID(TokenID tokenID) {
126: synchronized (tokenIDList) {
127: tokenIDList.add(tokenID);
128: tokenIDs = null;
129:
130: // Check whether there's a valid and new category for this token-id
131: TokenCategory tcat = tokenID.getCategory();
132: if (tcat != null && tokenCategoryList.indexOf(tcat) < 0) {
133: tokenCategoryList.add(tcat);
134: tokenCategories = null;
135: }
136: }
137: }
138:
139: /** Add all static-final token-id fields declared
140: * in this token-context using <tt>Class.getDeclaredFields()</tt> call.
141: */
142: protected void addDeclaredTokenIDs() throws IllegalAccessException,
143: SecurityException {
144: Field[] fields = this .getClass().getDeclaredFields();
145: for (int i = 0; i < fields.length; i++) {
146: int flags = Modifier.STATIC | Modifier.FINAL;
147: if ((fields[i].getModifiers() & flags) == flags
148: && TokenID.class.isAssignableFrom(fields[i]
149: .getType())) {
150: addTokenID((TokenID) fields[i].get(null));
151: }
152: }
153: }
154:
155: /** Get the token-ids that belong to this token-context. It doesn't
156: * return the children's token-ids.
157: */
158: public TokenID[] getTokenIDs() {
159: if (tokenIDs == null) {
160: synchronized (tokenIDList) {
161: tokenIDs = (TokenID[]) tokenIDList
162: .toArray(new TokenID[tokenIDList.size()]);
163: }
164: }
165:
166: return tokenIDs;
167: }
168:
169: /** Get the token-categories that belong to this token-context. It doesn't
170: * return the children's token-categories.
171: */
172: public TokenCategory[] getTokenCategories() {
173: if (tokenCategories == null) {
174: synchronized (tokenCategoryList) {
175: tokenCategories = (TokenCategory[]) tokenCategoryList
176: .toArray(new TokenCategory[tokenCategoryList
177: .size()]);
178: }
179: }
180:
181: return tokenCategories;
182: }
183:
184: /** Get the context path for this token-context. */
185: public TokenContextPath getContextPath() {
186: return contextPath;
187: }
188:
189: /** Get the context path for this token-context that is derived
190: * from the path of one of the children.
191: */
192: public TokenContextPath getContextPath(TokenContextPath childPath) {
193: if (childPath == null) {
194: return contextPath;
195: }
196:
197: TokenContextPath[] lastPair = lastContextPathPair;
198: if (lastPair == null || lastPair[0] != childPath) {
199: synchronized (pathCache) {
200: lastPair = (TokenContextPath[]) pathCache
201: .get(childPath);
202: if (lastPair == null) {
203: // Build the array of contexts
204: TokenContext[] origContexts = childPath
205: .getContexts();
206: TokenContext[] contexts = new TokenContext[origContexts.length + 1];
207: System.arraycopy(origContexts, 0, contexts, 0,
208: origContexts.length);
209: contexts[origContexts.length] = this ;
210:
211: TokenContextPath path = TokenContextPath
212: .get(contexts);
213:
214: lastPair = new TokenContextPath[] { childPath, path };
215: pathCache.put(childPath, lastPair);
216: }
217: lastContextPathPair = lastPair;
218: }
219: }
220:
221: return lastPair[1];
222: }
223:
224: /** Get all the context paths for this token-context including
225: * itself as the first one and all its children.
226: */
227: public TokenContextPath[] getAllContextPaths() {
228: if (allContextPaths == null) {
229: ArrayList cpList = new ArrayList();
230: cpList.add(getContextPath());
231:
232: for (int i = 0; i < children.length; i++) {
233: TokenContextPath[] childPaths = children[i]
234: .getAllContextPaths();
235: for (int j = 0; j < childPaths.length; j++) {
236: cpList.add(getContextPath(childPaths[j]));
237: }
238: }
239:
240: allContextPaths = new TokenContextPath[cpList.size()];
241: cpList.toArray(allContextPaths);
242: }
243:
244: return allContextPaths;
245: }
246:
247: }
|