001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.psi.stubs.elements;
018
019 import com.intellij.lang.ASTNode;
020 import com.intellij.psi.PsiElement;
021 import com.intellij.psi.stubs.IStubElementType;
022 import com.intellij.psi.stubs.IndexSink;
023 import com.intellij.psi.stubs.StubElement;
024 import com.intellij.psi.tree.IElementType;
025 import com.intellij.psi.tree.IStubFileElementType;
026 import com.intellij.util.ArrayFactory;
027 import com.intellij.util.ReflectionUtil;
028 import org.jetbrains.annotations.NonNls;
029 import org.jetbrains.annotations.NotNull;
030 import org.jetbrains.kotlin.idea.KotlinLanguage;
031 import org.jetbrains.kotlin.psi.KtClassOrObject;
032 import org.jetbrains.kotlin.psi.KtElementImplStub;
033 import org.jetbrains.kotlin.psi.KtFunction;
034 import org.jetbrains.kotlin.psi.KtProperty;
035
036 import java.lang.reflect.Array;
037 import java.lang.reflect.Constructor;
038
039 public abstract class KtStubElementType<StubT extends StubElement, PsiT extends KtElementImplStub<?>> extends IStubElementType<StubT, PsiT> {
040
041 @NotNull
042 private final Constructor<PsiT> byNodeConstructor;
043 @NotNull
044 private final Constructor<PsiT> byStubConstructor;
045 @NotNull
046 private final PsiT[] emptyArray;
047 @NotNull
048 private final ArrayFactory<PsiT> arrayFactory;
049
050 public KtStubElementType(@NotNull @NonNls String debugName, @NotNull final Class<PsiT> psiClass, @NotNull Class<?> stubClass) {
051 super(debugName, KotlinLanguage.INSTANCE);
052 try {
053 byNodeConstructor = psiClass.getConstructor(ASTNode.class);
054 byStubConstructor = psiClass.getConstructor(stubClass);
055 }
056 catch (NoSuchMethodException e) {
057 throw new RuntimeException("Stub element type declaration for " + psiClass.getSimpleName() + " is missing required constructors",e);
058 }
059 //noinspection unchecked
060 emptyArray = (PsiT[]) Array.newInstance(psiClass, 0);
061 arrayFactory = new ArrayFactory<PsiT>() {
062 @NotNull
063 @Override
064 public PsiT[] create(int count) {
065 if (count == 0) {
066 return emptyArray;
067 }
068 //noinspection unchecked
069 return (PsiT[]) Array.newInstance(psiClass, count);
070 }
071 };
072 }
073
074 @NotNull
075 public PsiT createPsiFromAst(@NotNull ASTNode node) {
076 return ReflectionUtil.createInstance(byNodeConstructor, node);
077 }
078
079 @Override
080 @NotNull
081 public PsiT createPsi(@NotNull StubT stub) {
082 return ReflectionUtil.createInstance(byStubConstructor, stub);
083 }
084
085 @NotNull
086 @Override
087 public String getExternalId() {
088 return "kotlin." + toString();
089 }
090
091 @Override
092 public boolean shouldCreateStub(ASTNode node) {
093 PsiElement psi = node.getPsi();
094 if (psi instanceof KtClassOrObject) {
095 return true;
096 }
097 if (psi instanceof KtFunction) {
098 return !((KtFunction) psi).isLocal();
099 }
100 if (psi instanceof KtProperty) {
101 return !((KtProperty) psi).isLocal();
102 }
103 return createStubDependingOnParent(node);
104 }
105
106 private static boolean createStubDependingOnParent(ASTNode node) {
107 ASTNode parent = node.getTreeParent();
108 IElementType parentType = parent.getElementType();
109 if (parentType instanceof IStubElementType) {
110 return ((IStubElementType) parentType).shouldCreateStub(parent);
111 }
112 if (parentType instanceof IStubFileElementType) {
113 return true;
114 }
115 return false;
116 }
117
118 @Override
119 public void indexStub(@NotNull StubT stub, @NotNull IndexSink sink) {
120 // do not force inheritors to implement this method
121 }
122
123 @NotNull
124 public ArrayFactory<PsiT> getArrayFactory() {
125 return arrayFactory;
126 }
127 }