001    /*
002     * Copyright 2010-2013 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.jet.lang.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.psi.util.PsiTreeUtil;
027    import com.intellij.util.ArrayFactory;
028    import com.intellij.util.ArrayUtil;
029    import com.intellij.util.ReflectionUtil;
030    import org.jetbrains.annotations.NonNls;
031    import org.jetbrains.annotations.NotNull;
032    import org.jetbrains.jet.lang.psi.*;
033    import org.jetbrains.jet.plugin.JetLanguage;
034    
035    import java.lang.reflect.Array;
036    import java.lang.reflect.Constructor;
037    
038    public abstract class JetStubElementType<StubT extends StubElement, PsiT extends JetElementImplStub<?>> extends IStubElementType<StubT, PsiT> {
039    
040        @SuppressWarnings("unchecked")
041        private static final Class<? extends PsiElement>[] ALWAYS_CREATE_STUB_FOR = new Class[] { JetClass.class, JetObjectDeclaration.class };
042    
043        @NotNull
044        private final Constructor<PsiT> byNodeConstructor;
045        @NotNull
046        private final Constructor<PsiT> byStubConstructor;
047        @NotNull
048        private final PsiT[] emptyArray;
049        @NotNull
050        private final ArrayFactory<PsiT> arrayFactory;
051    
052        public JetStubElementType(@NotNull @NonNls String debugName, @NotNull final Class<PsiT> psiClass, @NotNull Class<?> stubClass) {
053            super(debugName, JetLanguage.INSTANCE);
054            try {
055                byNodeConstructor = psiClass.getConstructor(ASTNode.class);
056                byStubConstructor = psiClass.getConstructor(stubClass);
057            }
058            catch (NoSuchMethodException e) {
059                throw new RuntimeException("Stub element type declaration for " + psiClass.getSimpleName() + " is missing required constructors",e);
060            }
061            //noinspection unchecked
062            emptyArray = (PsiT[]) Array.newInstance(psiClass, 0);
063            arrayFactory = new ArrayFactory<PsiT>() {
064                @NotNull
065                @Override
066                public PsiT[] create(int count) {
067                    if (count == 0) {
068                        return emptyArray;
069                    }
070                    //noinspection unchecked
071                    return (PsiT[]) Array.newInstance(psiClass, count);
072                }
073            };
074        }
075    
076        @NotNull
077        public PsiT createPsiFromAst(@NotNull ASTNode node) {
078            return ReflectionUtil.createInstance(byNodeConstructor, node);
079        }
080    
081        @Override
082        @NotNull
083        public PsiT createPsi(@NotNull StubT stub) {
084            return ReflectionUtil.createInstance(byStubConstructor, stub);
085        }
086    
087        @NotNull
088        @Override
089        public String getExternalId() {
090            return "jet." + toString();
091        }
092    
093        @Override
094        public boolean shouldCreateStub(ASTNode node) {
095            PsiElement psi = node.getPsi();
096            if (ArrayUtil.contains(psi.getClass(), ALWAYS_CREATE_STUB_FOR)) {
097                return true;
098            }
099            if (psi instanceof JetDeclaration) {
100                return shouldCreateStubForDeclaration((JetDeclaration) psi);
101            }
102            return createStubDependingOnParent(node);
103        }
104    
105        private static boolean createStubDependingOnParent(ASTNode node) {
106            ASTNode parent = node.getTreeParent();
107            IElementType parentType = parent.getElementType();
108            if (parentType instanceof IStubElementType) {
109                return ((IStubElementType) parentType).shouldCreateStub(parent);
110            }
111            if (parentType instanceof IStubFileElementType) {
112                return true;
113            }
114            return false;
115        }
116    
117        private static boolean shouldCreateStubForDeclaration(@NotNull JetDeclaration declaration) {
118            // Do not create stubs inside function literals
119            //noinspection unchecked
120            if (PsiTreeUtil.getParentOfType(declaration, JetFunctionLiteral.class, false, ALWAYS_CREATE_STUB_FOR) != null) {
121                return false;
122            }
123    
124            // Don't create stubs if declaration is inside function or property accessor with block
125            //noinspection unchecked
126            JetBlockExpression blockExpression =
127                    PsiTreeUtil.getParentOfType(declaration, JetBlockExpression.class, false, ALWAYS_CREATE_STUB_FOR);
128            if (blockExpression != null) {
129                return false;
130            }
131    
132            // Don't create stubs if declaration is inside other declaration with expression initializer
133    
134            Class<? extends PsiElement>[] stopAt = ArrayUtil.append(ALWAYS_CREATE_STUB_FOR, JetBlockExpression.class);
135            @SuppressWarnings("unchecked") JetWithExpressionInitializer withInitializer =
136                    PsiTreeUtil.getParentOfType(declaration, JetWithExpressionInitializer.class, true, stopAt);
137            if (withInitializer != null) {
138                JetExpression initializer = withInitializer.getInitializer();
139                if (PsiTreeUtil.isAncestor(initializer, declaration, true)) {
140                    return false;
141                }
142            }
143    
144            // Don't create stubs if declaration is inside property delegate
145            @SuppressWarnings("unchecked") JetPropertyDelegate delegate = PsiTreeUtil.getParentOfType(declaration, JetPropertyDelegate.class, true, stopAt);
146            if (delegate != null) {
147                JetExpression delegateExpression = delegate.getExpression();
148                if (PsiTreeUtil.isAncestor(delegateExpression, declaration, true)) {
149                    return false;
150                }
151            }
152    
153            return true;
154        }
155    
156        @Override
157        public void indexStub(@NotNull StubT stub, @NotNull IndexSink sink) {
158            // do not force inheritors to implement this method
159        }
160    
161        @NotNull
162        public ArrayFactory<PsiT> getArrayFactory() {
163            return arrayFactory;
164        }
165    }