| /* |
| * Copyright 2000-2014 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 com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * Represents a wildcard type, with bounds. |
| * |
| * @author dsl |
| */ |
| public class PsiWildcardType extends PsiType.Stub { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.PsiWildcardType"); |
| |
| private static final Key<PsiWildcardType> UNBOUNDED_WILDCARD = new Key<PsiWildcardType>("UNBOUNDED_WILDCARD"); |
| @NonNls private static final String EXTENDS_PREFIX = "? extends "; |
| @NonNls private static final String SUPER_PREFIX = "? super "; |
| |
| @NotNull |
| private final PsiManager myManager; |
| private final boolean myIsExtending; |
| private final PsiType myBound; |
| |
| private PsiWildcardType(@NotNull PsiManager manager, boolean isExtending, @Nullable PsiType bound) { |
| super(PsiAnnotation.EMPTY_ARRAY); |
| myManager = manager; |
| myIsExtending = isExtending; |
| myBound = bound; |
| } |
| |
| private PsiWildcardType(@NotNull PsiWildcardType type, @NotNull PsiAnnotation[] annotations) { |
| super(annotations); |
| myManager = type.myManager; |
| myIsExtending = type.myIsExtending; |
| myBound = type.myBound; |
| } |
| |
| @NotNull |
| public static PsiWildcardType createUnbounded(@NotNull PsiManager manager) { |
| PsiWildcardType unboundedWildcard = manager.getUserData(UNBOUNDED_WILDCARD); |
| if (unboundedWildcard == null) { |
| unboundedWildcard = manager.putUserDataIfAbsent(UNBOUNDED_WILDCARD, new PsiWildcardType(manager, false, null)); |
| } |
| return unboundedWildcard; |
| } |
| |
| @NotNull |
| public static PsiWildcardType createExtends(@NotNull PsiManager manager, @NotNull PsiType bound) { |
| LOG.assertTrue(!(bound instanceof PsiWildcardType)); |
| LOG.assertTrue(bound != PsiType.NULL); |
| return new PsiWildcardType(manager, true, bound); |
| } |
| |
| @NotNull |
| public static PsiWildcardType createSuper(@NotNull PsiManager manager, @NotNull PsiType bound) { |
| LOG.assertTrue(!(bound instanceof PsiWildcardType)); |
| LOG.assertTrue(bound != PsiType.NULL); |
| return new PsiWildcardType(manager, false, bound); |
| } |
| |
| @NotNull |
| public PsiWildcardType annotate(@NotNull PsiAnnotation[] annotations) { |
| return annotations.length == 0 ? this : new PsiWildcardType(this, annotations); |
| } |
| |
| @NotNull |
| @Override |
| public String getPresentableText() { |
| return getText(false, true, myBound == null ? null : myBound.getPresentableText()); |
| } |
| |
| @Override |
| @NotNull |
| public String getCanonicalText(boolean annotated) { |
| return getText(true, annotated, myBound == null ? null : myBound.getCanonicalText(annotated)); |
| } |
| |
| @NotNull |
| @Override |
| public String getInternalCanonicalText() { |
| return getText(true, true, myBound == null ? null : myBound.getInternalCanonicalText()); |
| } |
| |
| private String getText(boolean qualified, boolean annotated, @Nullable String suffix) { |
| PsiAnnotation[] annotations = getAnnotations(); |
| if ((!annotated || annotations.length == 0) && suffix == null) return "?"; |
| |
| StringBuilder sb = new StringBuilder(); |
| if (annotated) { |
| PsiNameHelper.appendAnnotations(sb, annotations, qualified); |
| } |
| if (suffix == null) { |
| sb.append('?'); |
| } |
| else { |
| sb.append(myIsExtending ? EXTENDS_PREFIX : SUPER_PREFIX); |
| sb.append(suffix); |
| } |
| return sb.toString(); |
| } |
| |
| @Override |
| @NotNull |
| public GlobalSearchScope getResolveScope() { |
| if (myBound != null) { |
| GlobalSearchScope scope = myBound.getResolveScope(); |
| if (scope != null) { |
| return scope; |
| } |
| } |
| return GlobalSearchScope.allScope(myManager.getProject()); |
| } |
| |
| @Override |
| @NotNull |
| public PsiType[] getSuperTypes() { |
| return new PsiType[]{getExtendsBound()}; |
| } |
| |
| @Override |
| public boolean equalsToText(@NotNull String text) { |
| if (myBound == null) return "?".equals(text); |
| if (myIsExtending) { |
| return text.startsWith(EXTENDS_PREFIX) && myBound.equalsToText(text.substring(EXTENDS_PREFIX.length())); |
| } |
| else { |
| return text.startsWith(SUPER_PREFIX) && myBound.equalsToText(text.substring(SUPER_PREFIX.length())); |
| } |
| } |
| @NotNull |
| public PsiManager getManager() { |
| return myManager; |
| } |
| |
| public boolean equals(Object o) { |
| if (!(o instanceof PsiWildcardType)) return false; |
| |
| PsiWildcardType that = (PsiWildcardType)o; |
| if (myBound == null && that.myBound != null) { |
| return that.isExtends() && that.myBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT); |
| } |
| else if (myBound != null && that.myBound == null) { |
| return isExtends() && myBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT); |
| } |
| return myIsExtending == that.myIsExtending && Comparing.equal(myBound, that.myBound); |
| } |
| |
| public int hashCode() { |
| return (myIsExtending ? 1 : 0) + (myBound != null ? myBound.hashCode() : 0); |
| } |
| |
| /** |
| * Use this method to obtain a bound of wildcard type. |
| * |
| * @return <code>null</code> if unbounded, a bound otherwise. |
| */ |
| @Nullable |
| public PsiType getBound() { |
| return myBound; |
| } |
| |
| @Override |
| public <A> A accept(@NotNull PsiTypeVisitor<A> visitor) { |
| return visitor.visitWildcardType(this); |
| } |
| |
| @Override |
| public boolean isValid() { |
| return myBound == null || myBound.isValid(); |
| } |
| |
| /** |
| * Returns whether this is a lower bound (<code>? extends XXX</code>). |
| * |
| * @return <code>true</code> for <code>extends</code> wildcards, <code>false</code> for <code>super</code> |
| * and unbounded wildcards. |
| */ |
| public boolean isExtends() { |
| return myBound != null && myIsExtending; |
| } |
| |
| /** |
| * Returns whether this is an upper bound (<code>? super XXX</code>). |
| * |
| * @return <code>true</code> for <code>super</code> wildcards, <code>false</code> for <code>extends</code> |
| * and unbounded wildcards. |
| */ |
| public boolean isSuper() { |
| return myBound != null && !myIsExtending; |
| } |
| |
| /** |
| * @return false for unbounded wildcards, true otherwise |
| */ |
| public boolean isBounded() { |
| return myBound != null; |
| } |
| |
| /** |
| * A lower bound that this wildcard imposes on type parameter value.<br> |
| * That is:<br> |
| * <ul> |
| * <li> for <code>? extends XXX</code>: <code>XXX</code> |
| * <li> for <code>? super XXX</code>: <code>java.lang.Object</code> |
| * <li> for <code>?</code>: <code>java.lang.Object</code> |
| * </ul> |
| * |
| * @return <code>PsiType</code> representing a lower bound. Never returns <code>null</code>. |
| */ |
| @NotNull |
| public PsiType getExtendsBound() { |
| if (myBound == null || !myIsExtending) { |
| return getJavaLangObject(myManager, getResolveScope()); |
| } |
| return myBound; |
| } |
| |
| /** |
| * An upper bound that this wildcard imposes on type parameter value.<br> |
| * That is:<br> |
| * <ul> |
| * <li> for <code>? extends XXX</code>: null type |
| * <li> for <code>? super XXX</code>: <code>XXX</code> |
| * <li> for <code>?</code>: null type |
| * </ul> |
| * |
| * @return <code>PsiType</code> representing an upper bound. Never returns <code>null</code>. |
| */ |
| @NotNull |
| public PsiType getSuperBound() { |
| return myBound == null || myIsExtending ? NULL : myBound; |
| } |
| } |