/* * Copyright 2000-2013 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 { 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 "; 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)); return new PsiWildcardType(manager, true, bound); } @NotNull public static PsiWildcardType createSuper(@NotNull PsiManager manager, @NotNull PsiType bound) { LOG.assertTrue(!(bound instanceof PsiWildcardType)); return new PsiWildcardType(manager, false, bound); } @NotNull public PsiWildcardType annotate(@NotNull PsiAnnotation[] annotations) { return annotations.length == 0 ? this : new PsiWildcardType(this, annotations); } /** * @deprecated implementation details (to remove in IDEA 13) */ @SuppressWarnings("UnusedDeclaration") public static PsiWildcardType changeBound(@NotNull PsiWildcardType type, @NotNull PsiType newBound) { LOG.assertTrue(type.getBound() != null); LOG.assertTrue(newBound.isValid()); if (type.myIsExtending) { if (newBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { return createUnbounded(type.myManager); } } return new PsiWildcardType(type.myManager, type.myIsExtending, newBound); } @Override public String getPresentableText() { return getAnnotationsTextPrefix(false, false, true) + (myBound == null ? "?" : (myIsExtending ? EXTENDS_PREFIX : SUPER_PREFIX) + myBound.getPresentableText()); } @Override public String getCanonicalText() { return (myBound == null ? "?" : (myIsExtending ? EXTENDS_PREFIX : SUPER_PREFIX) + myBound.getCanonicalText()); } @Override public String getInternalCanonicalText() { return getAnnotationsTextPrefix(true, false, true) + (myBound == null ? "?" : (myIsExtending ? EXTENDS_PREFIX : SUPER_PREFIX) + myBound.getInternalCanonicalText()); } @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(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())); } } 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>. */ 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>. */ public PsiType getSuperBound() { return myBound == null || myIsExtending ? NULL : myBound; } }