/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; /** * Token context defines the environment in which only a limited set * of tokens can be used. This set can be retrieved by calling * the <tt>getTokenIDs()</tt> method. The context can contain other * contexts which means that the context can possibly switch * into one of its children contexts during lexing. * The child context can also have other children that can * work in the same way. * In this way context-paths can be created. They describe * the way how was the token lexically recognized. * The context can be retrieved when the syntax-class is known * using the <tt>get</tt> method. * * * @author Miloslav Metelka * @version 1.00 */ public class TokenContext { private static final TokenContext[] EMPTY_CHILDREN = new TokenContext[0]; private final String namePrefix; private final TokenContext[] children; private final HashMap pathCache = new HashMap(37); private final ArrayList tokenIDList = new ArrayList(); private final ArrayList tokenCategoryList = new ArrayList(); private TokenID[] tokenIDs; private TokenCategory[] tokenCategories; private TokenContextPath contextPath; private TokenContextPath[] allContextPaths; private TokenContextPath[] lastContextPathPair; public TokenContext(String namePrefix) { this(namePrefix, EMPTY_CHILDREN); } /** Construct new token-context. * @param namePrefix name that will prefix all the token-ids names. * @param children child token contexts. */ public TokenContext(String namePrefix, TokenContext[] children) { if (namePrefix == null) { throw new IllegalArgumentException("Name prefix must be non-null."); } this.namePrefix = namePrefix.intern(); this.children = (children != null) ? children : EMPTY_CHILDREN; contextPath = TokenContextPath.get(new TokenContext[] { this }); } /** Get the prefix that this context adds to the name of its tokens. */ public String getNamePrefix() { return namePrefix; } /** Get the children contexts of this context. It returns empty-array * if there are no children. */ public TokenContext[] getChildren() { return children; } /** Add token-id to the set of token-ids that belong to this context. */ protected void addTokenID(TokenID tokenID) { synchronized (tokenIDList) { tokenIDList.add(tokenID); tokenIDs = null; // Check whether there's a valid and new category for this token-id TokenCategory tcat = tokenID.getCategory(); if (tcat != null && tokenCategoryList.indexOf(tcat) < 0) { tokenCategoryList.add(tcat); tokenCategories = null; } } } /** Add all static-final token-id fields declared * in this token-context using <tt>Class.getDeclaredFields()</tt> call. */ protected void addDeclaredTokenIDs() throws IllegalAccessException, SecurityException { Field[] fields = this.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { int flags = Modifier.STATIC | Modifier.FINAL; if ((fields[i].getModifiers() & flags) == flags && TokenID.class.isAssignableFrom(fields[i].getType()) ) { addTokenID((TokenID)fields[i].get(null)); } } } /** Get the token-ids that belong to this token-context. It doesn't * return the children's token-ids. */ public TokenID[] getTokenIDs() { if (tokenIDs == null) { synchronized (tokenIDList) { tokenIDs = (TokenID[])tokenIDList.toArray(new TokenID[tokenIDList.size()]); } } return tokenIDs; } /** Get the token-categories that belong to this token-context. It doesn't * return the children's token-categories. */ public TokenCategory[] getTokenCategories() { if (tokenCategories == null) { synchronized (tokenCategoryList) { tokenCategories = (TokenCategory[])tokenCategoryList.toArray( new TokenCategory[tokenCategoryList.size()]); } } return tokenCategories; } /** Get the context path for this token-context. */ public TokenContextPath getContextPath() { return contextPath; } /** Get the context path for this token-context that is derived * from the path of one of the children. */ public TokenContextPath getContextPath(TokenContextPath childPath) { if (childPath == null) { return contextPath; } TokenContextPath[] lastPair = lastContextPathPair; if (lastPair == null || lastPair[0] != childPath) { synchronized (pathCache) { lastPair = (TokenContextPath[])pathCache.get(childPath); if (lastPair == null) { // Build the array of contexts TokenContext[] origContexts = childPath.getContexts(); TokenContext[] contexts = new TokenContext[origContexts.length + 1]; System.arraycopy(origContexts, 0, contexts, 0, origContexts.length); contexts[origContexts.length] = this; TokenContextPath path = TokenContextPath.get(contexts); lastPair = new TokenContextPath[] { childPath, path }; pathCache.put(childPath, lastPair); } lastContextPathPair = lastPair; } } return lastPair[1]; } /** Get all the context paths for this token-context including * itself as the first one and all its children. */ public TokenContextPath[] getAllContextPaths() { if (allContextPaths == null) { ArrayList cpList = new ArrayList(); cpList.add(getContextPath()); for (int i = 0; i < children.length; i++) { TokenContextPath[] childPaths = children[i].getAllContextPaths(); for (int j = 0; j < childPaths.length; j++) { cpList.add(getContextPath(childPaths[j])); } } allContextPaths = new TokenContextPath[cpList.size()]; cpList.toArray(allContextPaths); } return allContextPaths; } }