001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.lang.reflect.Field;
017: import java.lang.reflect.Modifier;
018: import java.util.ArrayList;
019: import java.util.HashMap;
020:
021: /**
022: * Token context defines the environment in which only a limited set of tokens
023: * can be used. This set can be retrieved by calling the <tt>getTokenIDs()</tt>
024: * method. The context can contain other contexts which means that the context
025: * can possibly switch into one of its children contexts during lexing. The
026: * child context can also have other children that can work in the same way. In
027: * this way context-paths can be created. They describe the way how was the
028: * token lexically recognized. The context can be retrieved when the
029: * syntax-class is known using the <tt>get</tt> method.
030: *
031: *
032: * @author Miloslav Metelka
033: * @version 1.00
034: */
035:
036: public class TokenContext {
037:
038: private static final TokenContext[] EMPTY_CHILDREN = new TokenContext[0];
039:
040: private final String namePrefix;
041:
042: private final TokenContext[] children;
043:
044: private final HashMap pathCache = new HashMap(37);
045:
046: private final ArrayList tokenIDList = new ArrayList();
047:
048: private final ArrayList tokenCategoryList = new ArrayList();
049:
050: private TokenID[] tokenIDs;
051:
052: private TokenCategory[] tokenCategories;
053:
054: private TokenContextPath contextPath;
055:
056: private TokenContextPath[] allContextPaths;
057:
058: private TokenContextPath[] lastContextPathPair;
059:
060: public TokenContext(String namePrefix) {
061: this (namePrefix, EMPTY_CHILDREN);
062: }
063:
064: /**
065: * Construct new token-context.
066: *
067: * @param namePrefix
068: * name that will prefix all the token-ids names.
069: * @param children
070: * child token contexts.
071: */
072: public TokenContext(String namePrefix, TokenContext[] children) {
073: if (namePrefix == null) {
074: throw new IllegalArgumentException(
075: "Name prefix must be non-null.");
076: }
077:
078: this .namePrefix = namePrefix.intern();
079: this .children = (children != null) ? children : EMPTY_CHILDREN;
080:
081: contextPath = TokenContextPath.get(new TokenContext[] { this });
082: }
083:
084: /** Get the prefix that this context adds to the name of its tokens. */
085: public String getNamePrefix() {
086: return namePrefix;
087: }
088:
089: /**
090: * Get the children contexts of this context. It returns empty-array if
091: * there are no children.
092: */
093: public TokenContext[] getChildren() {
094: return children;
095: }
096:
097: /** Add token-id to the set of token-ids that belong to this context. */
098: protected void addTokenID(TokenID tokenID) {
099: synchronized (tokenIDList) {
100: tokenIDList.add(tokenID);
101: tokenIDs = null;
102:
103: // Check whether there's a valid and new category for this token-id
104: TokenCategory tcat = tokenID.getCategory();
105: if (tcat != null && tokenCategoryList.indexOf(tcat) < 0) {
106: tokenCategoryList.add(tcat);
107: tokenCategories = null;
108: }
109: }
110: }
111:
112: /**
113: * Add all static-final token-id fields declared in this token-context using
114: * <tt>Class.getDeclaredFields()</tt> call.
115: */
116: protected void addDeclaredTokenIDs() throws IllegalAccessException,
117: SecurityException {
118: Field[] fields = this .getClass().getDeclaredFields();
119: for (int i = 0; i < fields.length; i++) {
120: int flags = Modifier.STATIC | Modifier.FINAL;
121: if ((fields[i].getModifiers() & flags) == flags
122: && TokenID.class.isAssignableFrom(fields[i]
123: .getType())) {
124: addTokenID((TokenID) fields[i].get(null));
125: }
126: }
127: }
128:
129: /**
130: * Get the token-ids that belong to this token-context. It doesn't return
131: * the children's token-ids.
132: */
133: public TokenID[] getTokenIDs() {
134: if (tokenIDs == null) {
135: synchronized (tokenIDList) {
136: tokenIDs = (TokenID[]) tokenIDList
137: .toArray(new TokenID[tokenIDList.size()]);
138: }
139: }
140:
141: return tokenIDs;
142: }
143:
144: /**
145: * Get the token-categories that belong to this token-context. It doesn't
146: * return the children's token-categories.
147: */
148: public TokenCategory[] getTokenCategories() {
149: if (tokenCategories == null) {
150: synchronized (tokenCategoryList) {
151: tokenCategories = (TokenCategory[]) tokenCategoryList
152: .toArray(new TokenCategory[tokenCategoryList
153: .size()]);
154: }
155: }
156:
157: return tokenCategories;
158: }
159:
160: /** Get the context path for this token-context. */
161: public TokenContextPath getContextPath() {
162: return contextPath;
163: }
164:
165: /**
166: * Get the context path for this token-context that is derived from the path
167: * of one of the children.
168: */
169: public TokenContextPath getContextPath(TokenContextPath childPath) {
170: if (childPath == null) {
171: return contextPath;
172: }
173:
174: TokenContextPath[] lastPair = lastContextPathPair;
175: if (lastPair == null || lastPair[0] != childPath) {
176: synchronized (pathCache) {
177: lastPair = (TokenContextPath[]) pathCache
178: .get(childPath);
179: if (lastPair == null) {
180: // Build the array of contexts
181: TokenContext[] origContexts = childPath
182: .getContexts();
183: TokenContext[] contexts = new TokenContext[origContexts.length + 1];
184: System.arraycopy(origContexts, 0, contexts, 0,
185: origContexts.length);
186: contexts[origContexts.length] = this ;
187:
188: TokenContextPath path = TokenContextPath
189: .get(contexts);
190:
191: lastPair = new TokenContextPath[] { childPath, path };
192: pathCache.put(childPath, lastPair);
193: }
194: lastContextPathPair = lastPair;
195: }
196: }
197:
198: return lastPair[1];
199: }
200:
201: /**
202: * Get all the context paths for this token-context including itself as the
203: * first one and all its children.
204: */
205: public TokenContextPath[] getAllContextPaths() {
206: if (allContextPaths == null) {
207: ArrayList cpList = new ArrayList();
208: cpList.add(getContextPath());
209:
210: for (int i = 0; i < children.length; i++) {
211: TokenContextPath[] childPaths = children[i]
212: .getAllContextPaths();
213: for (int j = 0; j < childPaths.length; j++) {
214: cpList.add(getContextPath(childPaths[j]));
215: }
216: }
217:
218: allContextPaths = new TokenContextPath[cpList.size()];
219: cpList.toArray(allContextPaths);
220: }
221:
222: return allContextPaths;
223: }
224:
225: }
|