/* * Copyright 2010-2015 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 org.jetbrains.kotlin.psi.stubs.elements; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.stubs.IndexSink; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IStubFileElementType; import com.intellij.util.ArrayFactory; import com.intellij.util.ReflectionUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.KotlinLanguage; import org.jetbrains.kotlin.psi.KtClassOrObject; import org.jetbrains.kotlin.psi.KtElementImplStub; import org.jetbrains.kotlin.psi.KtFunction; import org.jetbrains.kotlin.psi.KtProperty; import java.lang.reflect.Array; import java.lang.reflect.Constructor; public abstract class KtStubElementType<StubT extends StubElement, PsiT extends KtElementImplStub<?>> extends IStubElementType<StubT, PsiT> { @NotNull private final Constructor<PsiT> byNodeConstructor; @NotNull private final Constructor<PsiT> byStubConstructor; @NotNull private final PsiT[] emptyArray; @NotNull private final ArrayFactory<PsiT> arrayFactory; public KtStubElementType(@NotNull @NonNls String debugName, @NotNull Class<PsiT> psiClass, @NotNull Class<?> stubClass) { super(debugName, KotlinLanguage.INSTANCE); try { byNodeConstructor = psiClass.getConstructor(ASTNode.class); byStubConstructor = psiClass.getConstructor(stubClass); } catch (NoSuchMethodException e) { throw new RuntimeException("Stub element type declaration for " + psiClass.getSimpleName() + " is missing required constructors",e); } //noinspection unchecked emptyArray = (PsiT[]) Array.newInstance(psiClass, 0); arrayFactory = count -> { if (count == 0) { return emptyArray; } //noinspection unchecked return (PsiT[]) Array.newInstance(psiClass, count); }; } @NotNull public PsiT createPsiFromAst(@NotNull ASTNode node) { return ReflectionUtil.createInstance(byNodeConstructor, node); } @Override @NotNull public PsiT createPsi(@NotNull StubT stub) { return ReflectionUtil.createInstance(byStubConstructor, stub); } @NotNull @Override public String getExternalId() { return "kotlin." + toString(); } @Override public boolean shouldCreateStub(ASTNode node) { PsiElement psi = node.getPsi(); if (psi instanceof KtClassOrObject) { return true; } if (psi instanceof KtFunction) { return !((KtFunction) psi).isLocal(); } if (psi instanceof KtProperty) { return !((KtProperty) psi).isLocal(); } return createStubDependingOnParent(node); } private static boolean createStubDependingOnParent(ASTNode node) { ASTNode parent = node.getTreeParent(); IElementType parentType = parent.getElementType(); if (parentType instanceof IStubElementType) { return ((IStubElementType) parentType).shouldCreateStub(parent); } if (parentType instanceof IStubFileElementType) { return true; } return false; } @Override public void indexStub(@NotNull StubT stub, @NotNull IndexSink sink) { // do not force inheritors to implement this method } @NotNull public ArrayFactory<PsiT> getArrayFactory() { return arrayFactory; } }