/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.tree;
import com.intellij.lang.Language;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.ArrayFactory;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Interface for token types returned from lexical analysis and for types
* of nodes in the AST tree. All used element types are added to a registry which
* can be enumerated or accessed by index.
*
* @see com.intellij.lexer.Lexer#getTokenType()
* @see com.intellij.lang.ASTNode#getElementType()
*/
public class IElementType {
public static final IElementType[] EMPTY_ARRAY = new IElementType[0];
public static ArrayFactory<IElementType> ARRAY_FACTORY = new ArrayFactory<IElementType>() {
@NotNull
@Override
public IElementType[] create(int count) {
return count == 0 ? EMPTY_ARRAY : new IElementType[count];
}
};
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.tree.IElementType");
public static final short FIRST_TOKEN_INDEX = 1;
public static final short MAX_INDEXED_TYPES = 15000;
private static short ourCounter = FIRST_TOKEN_INDEX;
private static final List<IElementType> ourRegistry = new ArrayList<IElementType>(700);
private final short myIndex;
@NotNull
private final String myDebugName;
@NotNull
private final Language myLanguage;
/**
* Creates and registers a new element type for the specified language.
*
* @param debugName the name of the element type, used for debugging purposes.
* @param language the language with which the element type is associated.
*/
public IElementType(@NotNull @NonNls String debugName, @Nullable Language language) {
this(debugName, language, true);
}
protected IElementType(@NotNull @NonNls String debugName,
@Nullable Language language,
boolean register) {
myDebugName = debugName;
myLanguage = language == null ? Language.ANY : language;
if (register) {
//noinspection AssignmentToStaticFieldFromInstanceMethod
myIndex = ourCounter++;
LOG.assertTrue(ourCounter < MAX_INDEXED_TYPES, "Too many element types registered. Out of (short) range.");
synchronized (ourRegistry) {
ourRegistry.add(this);
}
}
else {
myIndex = -1;
}
}
/**
* Returns the language associated with the element type.
*
* @return the associated language.
*/
@NotNull
public Language getLanguage() {
return myLanguage;
}
/**
* Returns the index of the element type in the table of all registered element
* types.
*
* @return the element type index.
*/
public final short getIndex() {
return myIndex;
}
public String toString() {
return myDebugName;
}
/**
* Controls whitespace balancing behavior of PsiBuilder.
* <p>By default, empty composite elements (containing no children) are bounded to the right (previous) neighbour, forming following tree:
* <pre>
* [previous_element]
* [whitespace]
* [empty_element]
* <empty>
* [next_element]
* </pre>
* <p>Left-bound elements are bounded to the left (next) neighbour instead:
* <pre>
* [previous_element]
* [empty_element]
* <empty>
* [whitespace]
* [next_element]
* </pre>
* <p>See com.intellij.lang.impl.PsiBuilderImpl.prepareLightTree() for details.
*
* @return true if empty elements of this type should be bound to the left.
*/
public boolean isLeftBound() {
return false;
}
/**
* Returns the element type registered at the specified index.
*
* @param idx the index for which the element type should be returned.
* @return the element type at the specified index.
* @throws IndexOutOfBoundsException if the index is out of registered elements' range.
*/
public static IElementType find(short idx) {
synchronized (ourRegistry) {
if (idx == 0) {
return ourRegistry.get(0); // We've changed FIRST_TOKEN_INDEX from 0 to 1. This is just for old plugins to avoid crashes.
}
if (idx >= ourRegistry.size() + FIRST_TOKEN_INDEX) return null;
return ourRegistry.get(idx - FIRST_TOKEN_INDEX);
}
}
static short getAllocatedTypesCount() {
return ourCounter;
}
/**
* Enumerates all registered token types which match the specified predicate.
*
* @param p the predicate which should be matched by the element types.
* @return the array of matching element types.
*/
@NotNull
public static IElementType[] enumerate(@NotNull Processor<IElementType> p) {
IElementType[] copy;
synchronized (ourRegistry) {
copy = ourRegistry.toArray(new IElementType[ourRegistry.size()]);
}
List<IElementType> matches = new ArrayList<IElementType>();
for (IElementType value : copy) {
if (p.process(value)) {
matches.add(value);
}
}
return matches.toArray(new IElementType[matches.size()]);
}
}