| /* |
| * 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.impl.compiled; |
| |
| import com.intellij.openapi.util.AtomicNotNullLazyValue; |
| import com.intellij.openapi.util.AtomicNullableLazyValue; |
| import com.intellij.openapi.util.NotNullLazyValue; |
| import com.intellij.openapi.util.NullableLazyValue; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.PsiImplUtil; |
| import com.intellij.psi.impl.PsiJavaParserFacadeImpl; |
| import com.intellij.psi.impl.cache.TypeInfo; |
| import com.intellij.psi.impl.source.PsiClassReferenceType; |
| import com.intellij.psi.impl.source.tree.JavaElementType; |
| import com.intellij.psi.impl.source.tree.TreeElement; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| public class ClsTypeElementImpl extends ClsElementImpl implements PsiTypeElement { |
| @NonNls static final char VARIANCE_NONE = '\0'; |
| @NonNls static final char VARIANCE_EXTENDS = '+'; |
| @NonNls static final char VARIANCE_SUPER = '-'; |
| @NonNls static final char VARIANCE_INVARIANT = '*'; |
| @NonNls static final String VARIANCE_EXTENDS_PREFIX = "? extends "; |
| @NonNls static final String VARIANCE_SUPER_PREFIX = "? super "; |
| |
| private final PsiElement myParent; |
| private final String myTypeText; |
| private final char myVariance; |
| private final NullableLazyValue<ClsElementImpl> myChild; |
| private final NotNullLazyValue<PsiType> myCachedType; |
| |
| public ClsTypeElementImpl(@NotNull PsiElement parent, @NotNull String typeText, char variance) { |
| myParent = parent; |
| myTypeText = TypeInfo.internFrequentType(typeText); |
| myVariance = variance; |
| myChild = new AtomicNullableLazyValue<ClsElementImpl>() { |
| @Override |
| protected ClsElementImpl compute() { |
| return calculateChild(); |
| } |
| }; |
| myCachedType = new AtomicNotNullLazyValue<PsiType>() { |
| @NotNull |
| @Override |
| protected PsiType compute() { |
| return calculateType(); |
| } |
| }; |
| } |
| |
| @Override |
| @NotNull |
| public PsiElement[] getChildren() { |
| ClsElementImpl child = myChild.getValue(); |
| return child != null ? new PsiElement[]{child} : PsiElement.EMPTY_ARRAY; |
| } |
| |
| @Override |
| public PsiElement getParent() { |
| return myParent; |
| } |
| |
| @Override |
| public String getText() { |
| final String shortClassName = PsiNameHelper.getShortClassName(myTypeText); |
| return decorateTypeText(shortClassName); |
| } |
| |
| private String decorateTypeText(final String shortClassName) { |
| switch (myVariance) { |
| case VARIANCE_NONE: |
| return shortClassName; |
| case VARIANCE_EXTENDS: |
| return VARIANCE_EXTENDS_PREFIX + shortClassName; |
| case VARIANCE_SUPER: |
| return VARIANCE_SUPER_PREFIX + shortClassName; |
| case VARIANCE_INVARIANT: |
| return "?"; |
| default: |
| assert false : myVariance; |
| return null; |
| } |
| } |
| |
| public String getCanonicalText() { |
| return decorateTypeText(myTypeText); |
| } |
| |
| @Override |
| public void appendMirrorText(int indentLevel, @NotNull StringBuilder buffer) { |
| buffer.append(decorateTypeText(myTypeText)); |
| } |
| |
| @Override |
| public void setMirror(@NotNull TreeElement element) throws InvalidMirrorException { |
| setMirrorCheckingType(element, JavaElementType.TYPE); |
| |
| ClsElementImpl child = myChild.getValue(); |
| if (child != null) { |
| child.setMirror(element.getFirstChildNode()); |
| } |
| } |
| |
| private boolean isArray() { |
| return myTypeText.endsWith("[]"); |
| } |
| |
| private boolean isVarArgs() { |
| return myTypeText.endsWith("..."); |
| } |
| |
| @Override |
| @NotNull |
| public PsiType getType() { |
| return myCachedType.getValue(); |
| } |
| |
| @Override |
| public PsiJavaCodeReferenceElement getInnermostComponentReferenceElement() { |
| return null; |
| } |
| |
| private ClsElementImpl calculateChild() { |
| if (PsiJavaParserFacadeImpl.getPrimitiveType(myTypeText) != null) { |
| return null; |
| } |
| if (isArray()) { |
| return myVariance == VARIANCE_NONE |
| ? new ClsTypeElementImpl(this, myTypeText.substring(0, myTypeText.length() - 2), myVariance) |
| : new ClsTypeElementImpl(this, myTypeText, VARIANCE_NONE); |
| } |
| if (isVarArgs()) { |
| return new ClsTypeElementImpl(this, myTypeText.substring(0, myTypeText.length() - 3), myVariance); |
| } |
| return myVariance == VARIANCE_INVARIANT ? null : new ClsJavaCodeReferenceElementImpl(this, myTypeText); |
| } |
| |
| @NotNull |
| private PsiType calculateType() { |
| PsiType result = PsiJavaParserFacadeImpl.getPrimitiveType(myTypeText); |
| if (result != null) return result; |
| |
| ClsElementImpl childElement = myChild.getValue(); |
| if (childElement instanceof ClsTypeElementImpl) { |
| if (isArray()) { |
| switch (myVariance) { |
| case VARIANCE_NONE: |
| return ((PsiTypeElement)childElement).getType().createArrayType(); |
| case VARIANCE_EXTENDS: |
| return PsiWildcardType.createExtends(getManager(), ((PsiTypeElement)childElement).getType()); |
| case VARIANCE_SUPER: |
| return PsiWildcardType.createSuper(getManager(), ((PsiTypeElement)childElement).getType()); |
| default: |
| assert false : myVariance; |
| return null; |
| } |
| } |
| else { |
| assert isVarArgs() : this; |
| return new PsiEllipsisType(((PsiTypeElement)childElement).getType()); |
| } |
| } |
| if (childElement instanceof ClsJavaCodeReferenceElementImpl) { |
| PsiClassReferenceType psiClassReferenceType = new PsiClassReferenceType((PsiJavaCodeReferenceElement)childElement, null); |
| switch (myVariance) { |
| case VARIANCE_NONE: |
| return psiClassReferenceType; |
| case VARIANCE_EXTENDS: |
| return PsiWildcardType.createExtends(getManager(), psiClassReferenceType); |
| case VARIANCE_SUPER: |
| return PsiWildcardType.createSuper(getManager(), psiClassReferenceType); |
| case VARIANCE_INVARIANT: |
| return PsiWildcardType.createUnbounded(getManager()); |
| default: |
| assert false : myVariance; |
| return null; |
| } |
| } |
| assert childElement == null : this; |
| return PsiWildcardType.createUnbounded(getManager()); |
| } |
| |
| @Override |
| public void accept(@NotNull PsiElementVisitor visitor) { |
| if (visitor instanceof JavaElementVisitor) { |
| ((JavaElementVisitor)visitor).visitTypeElement(this); |
| } |
| else { |
| visitor.visitElement(this); |
| } |
| } |
| |
| @Override |
| @NotNull |
| public PsiAnnotation[] getAnnotations() { |
| throw new UnsupportedOperationException();//todo |
| } |
| |
| @Override |
| public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { |
| return PsiImplUtil.findAnnotation(this, qualifiedName); |
| } |
| |
| @Override |
| @NotNull |
| public PsiAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { |
| throw new UnsupportedOperationException();//todo |
| } |
| |
| @Override |
| @NotNull |
| public PsiAnnotation[] getApplicableAnnotations() { |
| return getAnnotations(); |
| } |
| |
| @Override |
| public String toString() { |
| return "PsiTypeElement:" + getText(); |
| } |
| } |