diff --git a/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java b/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java index 47bd7be64c65..71292309cfc4 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiArrayType.java @@ -39,6 +39,10 @@ public class PsiArrayType extends PsiType.Stub { super(annotations); myComponentType = componentType; } + public PsiArrayType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider annotations) { + super(annotations); + myComponentType = componentType; + } @NotNull @Override @@ -114,7 +118,9 @@ public class PsiArrayType extends PsiType.Stub { } public boolean equals(Object obj) { - return obj != null && getClass().equals(obj.getClass()) && myComponentType.equals(((PsiArrayType)obj).getComponentType()); + return obj instanceof PsiArrayType && + (this instanceof PsiEllipsisType == obj instanceof PsiEllipsisType) && + myComponentType.equals(((PsiArrayType)obj).getComponentType()); } public int hashCode() { diff --git a/java/java-psi-api/src/com/intellij/psi/PsiClassType.java b/java/java-psi-api/src/com/intellij/psi/PsiClassType.java index 23f13a418693..8a322bca993b 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiClassType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiClassType.java @@ -53,6 +53,11 @@ public abstract class PsiClassType extends PsiType { myLanguageLevel = languageLevel; } + public PsiClassType(LanguageLevel languageLevel, @NotNull TypeAnnotationProvider annotations) { + super(annotations); + myLanguageLevel = languageLevel; + } + /** * Resolves the class reference and returns the resulting class. * @@ -295,6 +300,10 @@ public abstract class PsiClassType extends PsiType { super(languageLevel, annotations); } + public Stub(LanguageLevel languageLevel, @NotNull TypeAnnotationProvider annotations) { + super(languageLevel, annotations); + } + @NotNull @Override public final String getCanonicalText() { diff --git a/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java b/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java index 085854f51a65..b00ece6a7798 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiEllipsisType.java @@ -35,6 +35,9 @@ public class PsiEllipsisType extends PsiArrayType { public PsiEllipsisType(@NotNull PsiType componentType, @NotNull PsiAnnotation[] annotations) { super(componentType, annotations); } + public PsiEllipsisType(@NotNull PsiType componentType, @NotNull TypeAnnotationProvider annotations) { + super(componentType, annotations); + } @NotNull public static PsiType createEllipsis(@NotNull PsiType componentType, @NotNull PsiAnnotation[] annotations) { @@ -71,7 +74,7 @@ public class PsiEllipsisType extends PsiArrayType { * @return the array type instance. */ public PsiType toArrayType() { - return getComponentType().createArrayType(getAnnotations()); + return new PsiArrayType(getComponentType(), getAnnotationProvider()); } @Override @@ -79,10 +82,6 @@ public class PsiEllipsisType extends PsiArrayType { return visitor.visitEllipsisType(this); } - public boolean equals(Object obj) { - return obj instanceof PsiEllipsisType && super.equals(obj); - } - public int hashCode() { return super.hashCode() * 5; } diff --git a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java index 6a3d5c98ab40..82854ddffb85 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiPrimitiveType.java @@ -48,6 +48,10 @@ public class PsiPrimitiveType extends PsiType.Stub { super(annotations); myName = name; } + public PsiPrimitiveType(@NonNls @NotNull String name, @NotNull TypeAnnotationProvider annotations) { + super(annotations); + myName = name; + } @NotNull @Override diff --git a/java/java-psi-api/src/com/intellij/psi/PsiType.java b/java/java-psi-api/src/com/intellij/psi/PsiType.java index 2eff5ce3388c..1a68df9c6bf0 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiType.java @@ -51,10 +51,26 @@ public abstract class PsiType implements PsiAnnotationOwner { return ARRAY_FACTORY.create(count); } - private final PsiAnnotation[] myAnnotations; + private final TypeAnnotationProvider myAnnotationProvider; - protected PsiType(@NotNull PsiAnnotation[] annotations) { - myAnnotations = annotations; + /** + * Constructs a PsiType with given annotations + */ + protected PsiType(@NotNull final PsiAnnotation[] annotations) { + this(annotations.length == 0 ? TypeAnnotationProvider.EMPTY : new TypeAnnotationProvider() { + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + return annotations; + } + }); + } + + /** + * Constructs a PsiType that will take its annotations from the given annotation provider. + */ + protected PsiType(@NotNull TypeAnnotationProvider annotations) { + myAnnotationProvider = annotations; } /** @@ -273,15 +289,27 @@ public abstract class PsiType implements PsiAnnotationOwner { @NotNull public abstract PsiType[] getSuperTypes(); + /** + * @return provider for this type's annotations. Can be used to construct other PsiType instances + * without actually evaluating the annotation array, which can be computationally expensive sometimes. + */ + @NotNull + public final TypeAnnotationProvider getAnnotationProvider() { + return myAnnotationProvider; + } + + /** + * @return annotations for this type. Uses {@link #getAnnotationProvider()} to retrieve the annotations. + */ @Override @NotNull public PsiAnnotation[] getAnnotations() { - return myAnnotations; + return myAnnotationProvider.getAnnotations(); } @Override public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { - for (PsiAnnotation annotation : myAnnotations) { + for (PsiAnnotation annotation : getAnnotations()) { if (qualifiedName.equals(annotation.getQualifiedName())) { return annotation; } @@ -315,6 +343,10 @@ public abstract class PsiType implements PsiAnnotationOwner { super(annotations); } + public Stub(@NotNull TypeAnnotationProvider annotations) { + super(annotations); + } + @NotNull @Override public final String getCanonicalText() { diff --git a/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java b/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java index e73706736cb9..f5c199de6642 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiWildcardType.java @@ -45,7 +45,7 @@ public class PsiWildcardType extends PsiType.Stub { myBound = bound; } - private PsiWildcardType(@NotNull PsiWildcardType type, @NotNull PsiAnnotation[] annotations) { + private PsiWildcardType(@NotNull PsiWildcardType type, @NotNull TypeAnnotationProvider annotations) { super(annotations); myManager = type.myManager; myIsExtending = type.myIsExtending; @@ -76,8 +76,19 @@ public class PsiWildcardType extends PsiType.Stub { } @NotNull - public PsiWildcardType annotate(@NotNull PsiAnnotation[] annotations) { - return annotations.length == 0 ? this : new PsiWildcardType(this, annotations); + public PsiWildcardType annotate(@NotNull final PsiAnnotation[] annotations) { + return annotations.length == 0 ? this : new PsiWildcardType(this, new TypeAnnotationProvider() { + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + return annotations; + } + }); + } + + @NotNull + public PsiWildcardType annotate(@NotNull final TypeAnnotationProvider annotations) { + return new PsiWildcardType(this, annotations); } @NotNull diff --git a/java/java-psi-api/src/com/intellij/psi/TypeAnnotationProvider.java b/java/java-psi-api/src/com/intellij/psi/TypeAnnotationProvider.java new file mode 100644 index 000000000000..8184778ee46c --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/TypeAnnotationProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-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 com.intellij.psi; + +import org.jetbrains.annotations.NotNull; + +/** + * An object that returns annotations for {@link PsiType}. Since computing type annotations might be computationally expensive sometimes, + * this object is used to delay the calculation until annotations are really needed, + * and to pass the annotations without calculating them when creating new types based on existing types. + * + * @see PsiType#getAnnotationProvider() + * @see PsiType#PsiType(TypeAnnotationProvider) + */ +public interface TypeAnnotationProvider { + TypeAnnotationProvider EMPTY = new TypeAnnotationProvider() { + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + return PsiAnnotation.EMPTY_ARRAY; + } + }; + + @NotNull + PsiAnnotation[] getAnnotations(); +} diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 54cade32e37a..f6b43e8c5ec4 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -1195,14 +1195,6 @@ public final class PsiUtil extends PsiUtilCore { } throw new AssertionError("Invalid type: " + type + " of class " + type.getClass() + " " + customMessage); } - for (PsiAnnotation annotation : type.getAnnotations()) { - try { - PsiUtilCore.ensureValid(annotation); - } - catch (PsiInvalidElementAccessException e) { - throw customMessage == null? e : new RuntimeException(customMessage, e); - } - } } @Nullable diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java b/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java index 8b6ef64d2b60..124c8b4b511b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiTypeMapper.java @@ -42,7 +42,7 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx { PsiType mappedComponent = mapType(componentType); if (mappedComponent == null) return null; if (mappedComponent == componentType) return type; - return new PsiArrayType(mappedComponent, type.getAnnotations()); + return new PsiArrayType(mappedComponent, type.getAnnotationProvider()); } @Override @@ -51,7 +51,7 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx { PsiType mappedComponent = mapType(componentType); if (mappedComponent == null) return null; if (mappedComponent == componentType) return type; - return new PsiEllipsisType(mappedComponent, type.getAnnotations()); + return new PsiEllipsisType(mappedComponent, type.getAnnotationProvider()); } @Override diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java b/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java index a56b50c725fa..ede55b6f2660 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/TypeCorrector.java @@ -167,7 +167,7 @@ class TypeCorrector extends PsiTypeMapper { public PsiCorrectedClassType(LanguageLevel languageLevel, PsiClassType delegate, CorrectedResolveResult resolveResult) { - super(languageLevel, delegate.getAnnotations()); + super(languageLevel, delegate.getAnnotationProvider()); myDelegate = delegate; myResolveResult = resolveResult; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java index f7f7045af78c..c4cf9db93b6c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java @@ -42,6 +42,11 @@ public class PsiClassReferenceType extends PsiClassType.Stub { myReference = reference; } + public PsiClassReferenceType(@NotNull PsiJavaCodeReferenceElement reference, LanguageLevel level, @NotNull TypeAnnotationProvider annotations) { + super(level, annotations); + myReference = reference; + } + private static PsiAnnotation[] collectAnnotations(PsiJavaCodeReferenceElement reference) { List result = null; for (PsiElement child = reference.getFirstChild(); child != null; child = child.getNextSibling()) { @@ -80,7 +85,7 @@ public class PsiClassReferenceType extends PsiClassType.Stub { @Override public PsiClassType setLanguageLevel(@NotNull final LanguageLevel languageLevel) { if (languageLevel.equals(myLanguageLevel)) return this; - return new PsiClassReferenceType(myReference, languageLevel, getAnnotations()); + return new PsiClassReferenceType(myReference, languageLevel, getAnnotationProvider()); } @Override @@ -151,13 +156,13 @@ public class PsiClassReferenceType extends PsiClassType.Stub { PsiManager manager = myReference.getManager(); final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); final PsiSubstitutor rawSubstitutor = factory.createRawSubstitutor(aClass); - return factory.createType(aClass, rawSubstitutor, getLanguageLevel(), getAnnotations()); + return new PsiImmediateClassType(aClass, rawSubstitutor, getLanguageLevel(), getAnnotationProvider()); } String qualifiedName = myReference.getQualifiedName(); String name = myReference.getReferenceName(); if (name == null) name = ""; LightClassReference reference = new LightClassReference(myReference.getManager(), name, qualifiedName, myReference.getResolveScope()); - return new PsiClassReferenceType(reference, null, getAnnotations()); + return new PsiClassReferenceType(reference, null, getAnnotationProvider()); } @Override diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java index 46a4ea6b859c..4c2957e7b6c1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiImmediateClassType.java @@ -96,6 +96,16 @@ public class PsiImmediateClassType extends PsiClassType.Stub { mySubstitutor = substitutor; assert substitutor.isValid(); } + public PsiImmediateClassType(@NotNull PsiClass aClass, + @NotNull PsiSubstitutor substitutor, + @Nullable LanguageLevel level, + @NotNull TypeAnnotationProvider annotations) { + super(level, annotations); + myClass = aClass; + myManager = aClass.getManager(); + mySubstitutor = substitutor; + assert substitutor.isValid(); + } @Override public PsiClass resolve() { @@ -298,6 +308,6 @@ public class PsiImmediateClassType extends PsiClassType.Stub { @NotNull @Override public PsiClassType setLanguageLevel(@NotNull LanguageLevel level) { - return level.equals(myLanguageLevel) ? this : new PsiImmediateClassType(myClass, mySubstitutor, level, getAnnotations()); + return level.equals(myLanguageLevel) ? this : new PsiImmediateClassType(myClass, mySubstitutor, level, getAnnotationProvider()); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java index 8df615aa7c91..9d7e19ba5a98 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiTypeElementImpl.java @@ -16,9 +16,11 @@ package com.intellij.psi.impl.source; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.augment.PsiAugmentProvider; import com.intellij.psi.impl.PsiImplUtil; +import com.intellij.psi.impl.PsiJavaParserFacadeImpl; import com.intellij.psi.impl.source.tree.CompositePsiElement; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; @@ -34,6 +36,8 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeElement { @@ -77,7 +81,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl } PsiType type = null; - SmartList annotations = new SmartList(); + final SmartList annotations = new SmartList(); for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) { if (child instanceof PsiComment || child instanceof PsiWhiteSpace) continue; @@ -97,25 +101,23 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl } else if (PsiUtil.isJavaToken(child, ElementType.PRIMITIVE_TYPE_BIT_SET)) { assert type == null : this; - addTypeUseAnnotations(annotations); - PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); - type = JavaPsiFacade.getInstance(getProject()).getElementFactory().createPrimitiveType(child.getText(), array); + addTypeUseCandidateAnnotations(annotations); + String text = child.getText(); + type = annotations.isEmpty() ? PsiJavaParserFacadeImpl.getPrimitiveType(text) + : new PsiPrimitiveType(text, filterTypeUse(annotations)); } else if (child instanceof PsiJavaCodeReferenceElement) { assert type == null : this; - addTypeUseAnnotations(annotations); - PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); - type = new PsiClassReferenceType((PsiJavaCodeReferenceElement)child, null, array); + addTypeUseCandidateAnnotations(annotations); + type = new PsiClassReferenceType((PsiJavaCodeReferenceElement)child, null, filterTypeUse(annotations)); } else if (PsiUtil.isJavaToken(child, JavaTokenType.LBRACKET)) { assert type != null : this; - PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); - type = type.createArrayType(array); + type = new PsiArrayType(type, filterTypeUse(annotations)); } else if (PsiUtil.isJavaToken(child, JavaTokenType.ELLIPSIS)) { assert type != null : this; - PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); - type = PsiEllipsisType.createEllipsis(type, array); + type = new PsiEllipsisType(type, filterTypeUse(annotations)); } if (PsiUtil.isJavaToken(child, JavaTokenType.QUEST) || @@ -132,8 +134,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl else { type = PsiWildcardType.createUnbounded(getManager()); } - PsiAnnotation[] array = ContainerUtil.copyAndClear(annotations, PsiAnnotation.ARRAY_FACTORY, true); - type = ((PsiWildcardType)type).annotate(array); + type = ((PsiWildcardType)type).annotate(filterTypeUse(annotations)); break; } @@ -155,13 +156,39 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl return type == null ? PsiType.NULL : type; } - private void addTypeUseAnnotations(List annotations) { + @NotNull + private static TypeAnnotationProvider filterTypeUse(List annotations) { + if (annotations.isEmpty()) return TypeAnnotationProvider.EMPTY; + + final ArrayList copy = ContainerUtil.newArrayList(annotations); + annotations.clear(); + return new TypeAnnotationProvider() { + private volatile PsiAnnotation[] myCached; + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + PsiAnnotation[] result = myCached; + if (result == null) { + List filtered = ContainerUtil.filter(copy, new Condition() { + @Override + public boolean value(PsiAnnotation annotation) { + return PsiImplUtil.isTypeAnnotation(annotation); + } + }); + myCached = result = filtered.isEmpty() ? PsiAnnotation.EMPTY_ARRAY : filtered.toArray(new PsiAnnotation[filtered.size()]); + } + return result; + } + }; + } + + private void addTypeUseCandidateAnnotations(List annotations) { PsiElement parent = this; while (parent instanceof PsiTypeElement) { PsiElement left = PsiTreeUtil.skipSiblingsBackward(parent, INTERMEDIATES); if (left instanceof PsiModifierList) { - PsiImplUtil.collectTypeUseAnnotations((PsiModifierList)left, annotations); + Collections.addAll(annotations, ((PsiModifierList)left).getAnnotations()); break; } @@ -220,7 +247,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl @NotNull public PsiAnnotation[] getApplicableAnnotations() { List annotations = PsiTreeUtil.getChildrenOfTypeAsList(this, PsiAnnotation.class); - addTypeUseAnnotations(annotations); + addTypeUseCandidateAnnotations(annotations); return annotations.toArray(PsiAnnotation.ARRAY_FACTORY.create(annotations.size())); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java index 51adfb34bf76..cb07c0ab3ff6 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java @@ -98,7 +98,7 @@ public class JavaResolveCache { type = psiClass == null ? type // for type with unresolved reference, leave it in the cache // for clients still might be able to retrieve its getCanonicalText() from the reference text - : new PsiImmediateClassType(psiClass, result.getSubstitutor(), ((PsiClassReferenceType)type).getLanguageLevel(), type.getAnnotations()); + : new PsiImmediateClassType(psiClass, result.getSubstitutor(), ((PsiClassReferenceType)type).getLanguageLevel(), type.getAnnotationProvider()); } }