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 }