| /* |
| * 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.impl.source; |
| |
| import com.intellij.lang.ASTNode; |
| import com.intellij.navigation.ItemPresentation; |
| import com.intellij.navigation.ItemPresentationProviders; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.CheckUtil; |
| import com.intellij.psi.impl.ElementPresentationUtil; |
| import com.intellij.psi.impl.PsiImplUtil; |
| import com.intellij.psi.impl.cache.TypeInfo; |
| import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; |
| import com.intellij.psi.impl.java.stubs.PsiParameterStub; |
| import com.intellij.psi.impl.source.tree.CompositeElement; |
| import com.intellij.psi.impl.source.tree.JavaSharedImplUtil; |
| import com.intellij.psi.search.LocalSearchScope; |
| import com.intellij.psi.search.SearchScope; |
| import com.intellij.psi.stubs.IStubElementType; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.reference.SoftReference; |
| import com.intellij.ui.RowIcon; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.PlatformIcons; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.lang.ref.Reference; |
| import java.util.Arrays; |
| |
| public class PsiParameterImpl extends JavaStubPsiElement<PsiParameterStub> implements PsiParameter { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiParameterImpl"); |
| |
| private volatile Reference<PsiType> myCachedType = null; |
| |
| public PsiParameterImpl(@NotNull PsiParameterStub stub) { |
| this(stub, JavaStubElementTypes.PARAMETER); |
| } |
| |
| protected PsiParameterImpl(@NotNull PsiParameterStub stub, @NotNull IStubElementType type) { |
| super(stub, type); |
| } |
| |
| public PsiParameterImpl(@NotNull ASTNode node) { |
| super(node); |
| } |
| |
| public static PsiType getLambdaParameterType(PsiParameter param) { |
| final PsiElement paramParent = param.getParent(); |
| if (paramParent instanceof PsiParameterList) { |
| final int parameterIndex = ((PsiParameterList)paramParent).getParameterIndex(param); |
| if (parameterIndex > -1) { |
| final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(param, PsiLambdaExpression.class); |
| if (lambdaExpression != null) { |
| |
| PsiType type = LambdaUtil.getFunctionalInterfaceType(lambdaExpression, true); |
| if (type == null) { |
| type = LambdaUtil.getFunctionalInterfaceType(lambdaExpression, false); |
| } |
| if (type instanceof PsiIntersectionType) { |
| final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts(); |
| for (PsiType conjunct : conjuncts) { |
| final PsiType lambdaParameterFromType = getLambdaParameterFromType(parameterIndex, lambdaExpression, conjunct); |
| if (lambdaParameterFromType != null) return lambdaParameterFromType; |
| } |
| } else { |
| final PsiType lambdaParameterFromType = getLambdaParameterFromType(parameterIndex, lambdaExpression, type); |
| if (lambdaParameterFromType != null) { |
| return lambdaParameterFromType; |
| } |
| } |
| } |
| } |
| } |
| return new PsiLambdaParameterType(param); |
| } |
| |
| private static PsiType getLambdaParameterFromType(int parameterIndex, PsiLambdaExpression lambdaExpression, PsiType conjunct) { |
| final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(conjunct); |
| if (resolveResult != null) { |
| final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(conjunct); |
| if (method != null) { |
| final PsiParameter[] parameters = method.getParameterList().getParameters(); |
| if (parameterIndex < parameters.length) { |
| final PsiType psiType = LambdaUtil.getSubstitutor(method, resolveResult).substitute(parameters[parameterIndex].getType()); |
| if (!LambdaUtil.dependsOnTypeParams(psiType, conjunct, lambdaExpression)) { |
| return GenericsUtil.eliminateWildcards(psiType, false); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public void subtreeChanged() { |
| super.subtreeChanged(); |
| myCachedType = null; |
| } |
| |
| @Override |
| protected Object clone() { |
| PsiParameterImpl clone = (PsiParameterImpl)super.clone(); |
| clone.myCachedType = null; |
| |
| return clone; |
| } |
| |
| @Override |
| @NotNull |
| public final String getName() { |
| PsiParameterStub stub = getStub(); |
| if (stub != null) { |
| return stub.getName(); |
| } |
| |
| return getParameterIdentifier().getText(); |
| } |
| |
| @Override |
| public final PsiElement setName(@NotNull String name) throws IncorrectOperationException { |
| if (this instanceof PsiReceiverParameter) { |
| throw new IncorrectOperationException("Cannot rename receiver parameter"); |
| } |
| |
| PsiImplUtil.setName(getParameterIdentifier(), name); |
| return this; |
| } |
| |
| @Override |
| public final PsiIdentifier getNameIdentifier() { |
| return PsiTreeUtil.getChildOfType(this, PsiIdentifier.class); |
| } |
| |
| @NotNull |
| private PsiElement getParameterIdentifier() { |
| PsiJavaToken identifier = PsiTreeUtil.getChildOfAnyType(this, PsiIdentifier.class, PsiKeyword.class); |
| assert identifier != null : getClass() + ":" + getText(); |
| return identifier; |
| } |
| |
| @Override |
| @NotNull |
| public CompositeElement getNode() { |
| return (CompositeElement)super.getNode(); |
| } |
| |
| @Override |
| @NotNull |
| public PsiType getType() { |
| PsiParameterStub stub = getStub(); |
| if (stub != null) { |
| PsiType type = SoftReference.dereference(myCachedType); |
| if (type != null) return type; |
| |
| String typeText = TypeInfo.createTypeText(stub.getType(true)); |
| assert typeText != null : stub; |
| try { |
| type = JavaPsiFacade.getInstance(getProject()).getParserFacade().createTypeFromText(typeText, this); |
| myCachedType = new SoftReference<PsiType>(type); |
| return type; |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| return null; |
| } |
| } |
| |
| myCachedType = null; |
| |
| PsiTypeElement typeElement = getTypeElement(); |
| if (typeElement == null) { |
| assert isLambdaParameter() : this; |
| return getLambdaParameterType(this); |
| } |
| else { |
| return JavaSharedImplUtil.getType(typeElement, getParameterIdentifier()); |
| } |
| } |
| |
| private boolean isLambdaParameter() { |
| final PsiElement parent = getParent(); |
| return parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression; |
| } |
| |
| @Override |
| public PsiTypeElement getTypeElement() { |
| for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) { |
| if (child instanceof PsiTypeElement) { |
| //noinspection unchecked |
| return (PsiTypeElement)child; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| @NotNull |
| public PsiModifierList getModifierList() { |
| PsiModifierList modifierList = getStubOrPsiChild(JavaStubElementTypes.MODIFIER_LIST); |
| assert modifierList != null : this; |
| return modifierList; |
| } |
| |
| @Override |
| public boolean hasModifierProperty(@NotNull String name) { |
| return getModifierList().hasModifierProperty(name); |
| } |
| |
| @Override |
| public PsiExpression getInitializer() { |
| return null; |
| } |
| |
| @Override |
| public boolean hasInitializer() { |
| return false; |
| } |
| |
| @Override |
| public Object computeConstantValue() { |
| return null; |
| } |
| |
| @Override |
| public void normalizeDeclaration() throws IncorrectOperationException { |
| CheckUtil.checkWritable(this); |
| JavaSharedImplUtil.normalizeBrackets(this); |
| } |
| |
| @Override |
| public void accept(@NotNull PsiElementVisitor visitor) { |
| if (visitor instanceof JavaElementVisitor) { |
| ((JavaElementVisitor)visitor).visitParameter(this); |
| } |
| else { |
| visitor.visitElement(this); |
| } |
| } |
| |
| public String toString() { |
| return "PsiParameter:" + getName(); |
| } |
| |
| @Override |
| @NotNull |
| public PsiElement getDeclarationScope() { |
| final PsiElement parent = getParent(); |
| if (parent == null) return this; |
| |
| if (parent instanceof PsiParameterList) { |
| return parent.getParent(); |
| } |
| if (parent instanceof PsiForeachStatement) { |
| return parent; |
| } |
| if (parent instanceof PsiCatchSection) { |
| return parent; |
| } |
| |
| PsiElement[] children = parent.getChildren(); |
| //noinspection ConstantConditions |
| if (children != null) { |
| ext: |
| for (int i = 0; i < children.length; i++) { |
| if (children[i].equals(this)) { |
| for (int j = i + 1; j < children.length; j++) { |
| if (children[j] instanceof PsiCodeBlock) return children[j]; |
| } |
| break ext; |
| } |
| } |
| } |
| |
| LOG.error("Code block not found among parameter' (" + this + ") parent' (" + parent + ") children: " + Arrays.asList(children)); |
| return null; |
| } |
| |
| @Override |
| public boolean isVarArgs() { |
| final PsiParameterStub stub = getStub(); |
| if (stub != null) { |
| return stub.isParameterTypeEllipsis(); |
| } |
| |
| myCachedType = null; |
| final PsiTypeElement typeElement = getTypeElement(); |
| return typeElement != null && SourceTreeToPsiMap.psiToTreeNotNull(typeElement).findChildByType(JavaTokenType.ELLIPSIS) != null; |
| } |
| |
| @Override |
| public ItemPresentation getPresentation() { |
| return ItemPresentationProviders.getItemPresentation(this); |
| } |
| |
| @Override |
| public Icon getElementIcon(final int flags) { |
| final RowIcon baseIcon = createLayeredIcon(this, PlatformIcons.PARAMETER_ICON, 0); |
| return ElementPresentationUtil.addVisibilityIcon(this, flags, baseIcon); |
| } |
| @Override |
| protected boolean isVisibilitySupported() { |
| return true; |
| } |
| |
| @Override |
| @NotNull |
| public SearchScope getUseScope() { |
| final PsiElement declarationScope = getDeclarationScope(); |
| return new LocalSearchScope(declarationScope); |
| } |
| |
| @Override |
| public PsiElement getOriginalElement() { |
| PsiElement parent = getParent(); |
| if (parent instanceof PsiParameterList) { |
| PsiElement gParent = parent.getParent(); |
| if (gParent instanceof PsiMethod) { |
| PsiElement originalMethod = gParent.getOriginalElement(); |
| if (originalMethod instanceof PsiMethod) { |
| int index = ((PsiParameterList)parent).getParameterIndex(this); |
| PsiParameter[] originalParameters = ((PsiMethod)originalMethod).getParameterList().getParameters(); |
| if (index < originalParameters.length) { |
| return originalParameters[index]; |
| } |
| } |
| } |
| } |
| return this; |
| } |
| |
| } |