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.JetLanguage;
031    import org.jetbrains.kotlin.psi.JetClassOrObject;
032    import org.jetbrains.kotlin.psi.JetElementImplStub;
033    import org.jetbrains.kotlin.psi.JetFunction;
034    import org.jetbrains.kotlin.psi.JetProperty;
035    
036    import java.lang.reflect.Array;
037    import java.lang.reflect.Constructor;
038    
039    public abstract class JetStubElementType<StubT extends StubElement, PsiT extends JetElementImplStub<?>> 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 JetStubElementType(@NotNull @NonNls String debugName, @NotNull final Class<PsiT> psiClass, @NotNull Class<?> stubClass) {
051            super(debugName, JetLanguage.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 JetClassOrObject) {
095                return true;
096            }
097            if (psi instanceof JetFunction) {
098                return !((JetFunction) psi).isLocal();
099            }
100            if (psi instanceof JetProperty) {
101                return !((JetProperty) 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    }