/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.lang.psi.impl.statements.typedef.members; import com.intellij.lang.ASTNode; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.util.Key; import com.intellij.psi.HierarchicalMethodSignature; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiCodeBlock; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiMethodReceiver; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiModifierList; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.PsiReferenceList; import com.intellij.psi.PsiSubstitutor; import com.intellij.psi.PsiType; import com.intellij.psi.PsiTypeElement; import com.intellij.psi.ResolveState; import com.intellij.psi.impl.ElementPresentationUtil; import com.intellij.psi.impl.PsiClassImplUtil; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.PsiSuperMethodImplUtil; import com.intellij.psi.impl.source.HierarchicalMethodSignatureImpl; import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.presentation.java.JavaPresentationUtil; import com.intellij.psi.scope.NameHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.SearchScope; import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.stubs.NamedStub; import com.intellij.psi.util.MethodSignature; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; import com.intellij.ui.RowIcon; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import gw.internal.gosu.parser.DynamicFunctionSymbol; import gw.internal.gosu.parser.statements.FunctionStatement; import gw.lang.parser.IDynamicFunctionSymbol; import gw.lang.parser.IParsedElement; import gw.lang.parser.Keyword; import gw.lang.parser.statements.IFunctionStatement; import gw.lang.parser.statements.IPropertyStatement; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IType; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.java.IJavaType; import gw.plugin.ij.icons.GosuIcons; import gw.plugin.ij.lang.parser.GosuCompositeElement; import gw.plugin.ij.lang.parser.GosuElementTypes; import gw.plugin.ij.lang.psi.IGosuFileBase; import gw.plugin.ij.lang.psi.api.auxilary.IGosuModifierList; import gw.plugin.ij.lang.psi.api.statements.params.IGosuParameter; import gw.plugin.ij.lang.psi.api.statements.params.IGosuParameterList; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuMember; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuMethod; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuTypeDefinition; import gw.plugin.ij.lang.psi.api.types.IGosuTypeElement; import gw.plugin.ij.lang.psi.api.types.IGosuTypeVariable; import gw.plugin.ij.lang.psi.api.types.IGosuTypeVariableList; import gw.plugin.ij.lang.psi.impl.AbstractGosuClassFileImpl; import gw.plugin.ij.lang.psi.impl.GosuDeclaredElementImpl; import gw.plugin.ij.lang.psi.impl.GosuElementVisitor; import gw.plugin.ij.lang.psi.impl.GosuPsiImplUtil; import gw.plugin.ij.lang.psi.impl.expressions.GosuIdentifierImpl; import gw.plugin.ij.lang.psi.impl.resolvers.PsiFeatureResolver; import gw.plugin.ij.lang.psi.impl.statements.params.GosuParameterListImpl; import gw.plugin.ij.lang.psi.util.GosuDocUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.List; public abstract class GosuMethodBaseImpl<T extends NamedStub> extends GosuDeclaredElementImpl<IFunctionStatement, T> implements IGosuMethod { public static final Key<GosuMethodImpl> ORIGINAL_CONSTRUCTOR = new Key<>("OriginalConstructor"); protected GosuMethodBaseImpl(@NotNull final T stub, @NotNull IStubElementType nodeType) { super(stub, nodeType); } public GosuMethodBaseImpl(@NotNull GosuCompositeElement node) { super(node); } public void accept(@NotNull GosuElementVisitor visitor) { visitor.visitMethod(this); } public int getTextOffset() { if (isConstructor()) { return getNode().getStartOffset(); } else { return getNameIdentifier().getTextRange().getStartOffset(); } } @NotNull public IGosuParameter[] getParameters() { final GosuParameterListImpl parameters = findChildByClass(GosuParameterListImpl.class); return parameters != null ? parameters.getParameters() : IGosuParameter.EMPTY_ARRAY; } @Nullable public IGosuTypeElement getReturnTypeElementGosu() { return findChildByClass(IGosuTypeElement.class); } public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) { for (final IGosuTypeVariable typeParameter : getTypeParameters()) { if (!processElement(processor, typeParameter, state)) { return false; } } for (final IGosuParameter parameter : getParameters()) { if (!processElement(processor, parameter, state)) { return false; } } processor.handleEvent(DECLARATION_SCOPE_PASSED, this); return true; } public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() { }; boolean processElement(@NotNull PsiScopeProcessor processor, @NotNull PsiNamedElement namedElement, ResolveState state) { NameHint nameHint = processor.getHint(NameHint.KEY); String name = nameHint == null ? null : nameHint.getName(state); if (name == null || name.equals(namedElement.getName())) { return processor.execute(namedElement, state); } return true; } @NotNull public IGosuMember[] getMembers() { return new IGosuMember[]{this}; } // private static final Function<IGosuMethod, PsiType> ourTypesCalculator = new NullableFunction<IGosuMethod, PsiType>() // { // public PsiType fun( IGosuMethod method ) // { // PsiType nominal = method.getReturnType(); // if( nominal != null && nominal.equals( PsiType.VOID ) ) // { // return nominal; // } // // if( GppTypeConverter.hasTypedContext( method ) ) // { // if( nominal != null ) // { // return nominal; // } // // return PsiType.getJavaLangObject( method.getManager(), method.getResolveScope() ); // } // // PsiType inferred = getInferredType( method ); // if( nominal == null ) // { // if( inferred == null ) // { // return PsiType.getJavaLangObject( method.getManager(), method.getResolveScope() ); // } // return inferred; // } // if( inferred != null && inferred != PsiType.NULL ) // { // if( inferred instanceof PsiClassType && nominal instanceof PsiClassType ) // { // final PsiClassType.ClassResolveResult declaredResult = ((PsiClassType)nominal).resolveGenerics(); // final PsiClass declaredClass = declaredResult.getElement(); // if( declaredClass != null ) // { // final PsiClassType.ClassResolveResult initializerResult = ((PsiClassType)inferred).resolveGenerics(); // final PsiClass initializerClass = initializerResult.getElement(); // if( initializerClass != null && // com.intellij.psi.util.PsiUtil.isRawSubstitutor( initializerClass, initializerResult.getSubstitutor() ) ) // { // if( declaredClass == initializerClass ) // { // return nominal; // } // final PsiSubstitutor declaredResultSubstitutor = declaredResult.getSubstitutor(); // final PsiSubstitutor superSubstitutor = // TypeConversionUtil.getClassSubstitutor( declaredClass, initializerClass, declaredResultSubstitutor ); // // if( superSubstitutor != null ) // { // return JavaPsiFacade.getInstance( method.getProject() ).getElementFactory() // .createType( declaredClass, TypesUtil.composeSubstitutors( declaredResultSubstitutor, superSubstitutor ) ); // } // } // } // } // if( nominal.isAssignableFrom( inferred ) ) // { // return inferred; // } // } // return nominal; // } // // @Nullable // private PsiType getInferredType( IGosuMethod method ) // { // final GosuOpenBlock block = method.getBlock(); // if( block == null ) // { // return null; // } // // if( GosuPsiManager.isTypeBeingInferred( method ) ) // { // return null; // } // // return GosuPsiManager.inferType( method, new MethodTypeInferencer( block ) ); // } // }; @Nullable public PsiType getReturnType() { if (isConstructor()) { return null; } else { final IGosuTypeElement element = getReturnTypeElementGosu(); return element != null ? element.getType() : PsiType.VOID; } } @Nullable public IGosuTypeElement setReturnType(@Nullable PsiType newReturnType) { //## todo: return null; // IGosuTypeElement typeElement = getReturnTypeElementGosu(); // if( newReturnType == null ) // { // if( typeElement != null ) // { // typeElement.delete(); // } // return null; // } // IGosuTypeElement newTypeElement = GosuPsiElementFactory.getInstance( getProject() ).createTypeElement( newReturnType ); // if( typeElement == null ) // { // IGosuModifierList list = getModifierList(); // newTypeElement = (IGosuTypeElement)addAfter( newTypeElement, list ); // } // else // { // newTypeElement = (IGosuTypeElement)typeElement.replace( newTypeElement ); // } // // newTypeElement.accept( new GosuRecursiveElementVisitor() // { // @Override // public void visitCodeReferenceElement( IGosuCodeReferenceElement refElement ) // { // super.visitCodeReferenceElement( refElement ); // PsiUtil.shortenReference( refElement ); // } // } ); // return newTypeElement; } @Override public ItemPresentation getPresentation() { return JavaPresentationUtil.getMethodPresentation(this); } @Nullable public PsiTypeElement getReturnTypeElement() { return null; } @NotNull public IGosuParameterList getParameterList() { return (IGosuParameterList) findElement(this, GosuElementTypes.ELEM_TYPE_ParameterListClause); } @NotNull public PsiReferenceList getThrowsList() { return new ThrowsReferenceList(getManager(), getTextOffset()); } @Nullable public PsiCodeBlock getBody() { return findChildByClass(PsiCodeBlock.class); } public boolean isConstructor() { PsiIdentifier nameIdentifier = getNameIdentifier(); if (nameIdentifier == null || nameIdentifier.getText() == null) { return true; } else { return getContainingClass() != null && nameIdentifier.getText().equals(getContainingClass().getName()); } } public boolean isVarArgs() { final IGosuParameter[] parameters = getParameters(); return parameters.length > 0 && parameters[parameters.length - 1].isVarArgs(); } @NotNull public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) { return MethodSignatureBackedByPsiMethod.create(this, substitutor); } @Nullable public PsiIdentifier getNameIdentifier() { PsiIdentifier id = null; ASTNode[] children = this.getNode().getChildren(null); for (ASTNode child : children) { if (child instanceof LeafPsiElement && child.getText().equals("(")) { break; } if( child instanceof GosuCompositeElement && child.getFirstChildNode() instanceof GosuIdentifierImpl && child.getLastChildNode() == child.getFirstChildNode() ) { // This can happen when the child is a keyword like "get" where the GosuIdentifierImpl is wrapped in a GosuCompositeElement child = child.getFirstChildNode(); } if (child instanceof GosuIdentifierImpl) { id = (PsiIdentifier) child; // Avoid keeping an id for "get" "set" property qualifiers if (!id.getText().equals(Keyword.KW_get.getName()) && !id.getText().equals(Keyword.KW_set.getName())) { break; } } } if (id == null) { return null; } PsiElement firstChild = id.getFirstChild(); if (firstChild != null && firstChild instanceof PsiIdentifier) { // Always return the leaf token node; we always want to patch in just the name and not mess with upper-level tree nodes id = (PsiIdentifier) firstChild; } return id; } @NotNull public PsiMethod[] findDeepestSuperMethods() { return PsiSuperMethodImplUtil.findDeepestSuperMethods(this); } @NotNull public PsiMethod[] findSuperMethods(boolean checkAccess) { PsiMethod[] superMethods = PsiSuperMethodImplUtil.findSuperMethods( this, checkAccess ); if( superMethods.length > 0 ) { return superMethods; } if( !canHaveSuperMethod( checkAccess ) ) { return PsiMethod.EMPTY_ARRAY; } return resolveMethodFromSuperJavaClass(); } // Handle odd cases where param types may not be directly equal in IJ's eyes // e.g., Gosu: Collection<Object> Java: Collection<? extends Object> private PsiMethod[] resolveMethodFromSuperJavaClass() { PsiMethod[] superMethods = PsiMethod.EMPTY_ARRAY; IParsedElement pe = getParsedElement(); if( pe instanceof FunctionStatement ) { FunctionStatement funcStmt = (FunctionStatement)pe; DynamicFunctionSymbol dfs = funcStmt.getDynamicFunctionSymbol(); DynamicFunctionSymbol superDfs = dfs.getSuperDfs(); if( superDfs != null ) { IType declaringType = superDfs.getDeclaringTypeInfo().getOwnersType(); if( IGosuClass.ProxyUtil.isProxy( declaringType ) ) { IJavaType javaType = ((IGosuClass)declaringType).getJavaType(); TypeSystem.pushModule( javaType.getTypeLoader().getModule() ); try { IMethodInfo mi = ((IRelativeTypeInfo)javaType.getTypeInfo()).getMethod( javaType, superDfs.getDisplayName(), superDfs.getArgTypes() ); superMethods = new PsiMethod[] {(PsiMethod)PsiFeatureResolver.resolveMethodOrConstructor( mi, this )}; } finally { TypeSystem.popModule( javaType.getTypeLoader().getModule() ); } } } } return superMethods; } private boolean canHaveSuperMethod( boolean checkAccess ) { if( isConstructor() ) { return false; } if( hasModifierProperty( PsiModifier.STATIC ) ) { return false; } if( checkAccess && hasModifierProperty( PsiModifier.PRIVATE ) ) { return false; } PsiClass parentClass = getContainingClass(); return parentClass != null && !"java.lang.Object".equals( parentClass.getQualifiedName() ); } @NotNull public PsiMethod[] findSuperMethods(PsiClass parentClass) { return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass); } @NotNull public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) { return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess); } @NotNull public PsiMethod[] findSuperMethods() { return PsiSuperMethodImplUtil.findSuperMethods(this); } @Nullable public PsiMethod findDeepestSuperMethod() { final PsiMethod[] methods = findDeepestSuperMethods(); return methods.length > 0 ? methods[0] : null; } @NotNull public IGosuModifierList getModifierList() { return (IGosuModifierList)findChildByClass( PsiModifierList.class ); } public boolean hasModifierProperty(@NonNls @NotNull String name) { if (name.equals(PsiModifier.ABSTRACT)) { final PsiClass klass = getContainingClass(); if (klass != null && klass.isInterface()) { return true; } } return getModifierList().hasModifierProperty( name ); } @NotNull public String getName() { // for constructors this method should always return the enclosing type's name (not 'construct') // otherwise a class rename will take forever and run out of memory if (isConstructor()) { PsiClass containingClass = getContainingClass(); if (containingClass == null) { GosuMethodImpl originalConstructor = getUserData(ORIGINAL_CONSTRUCTOR); return originalConstructor.getName(); } return containingClass.getName(); } else { return GosuPsiImplUtil.getName(this); } } @NotNull public HierarchicalMethodSignature getHierarchicalMethodSignature() { try { return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this); } catch (IllegalArgumentException e) { //ignore it as it will always throw for methods with generics (Equal objects must have equal hashcodes.) return new HierarchicalMethodSignatureImpl((MethodSignatureBackedByPsiMethod) getSignature(PsiSubstitutor.EMPTY)); } } @NotNull public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { PsiIdentifier psiName = getNameIdentifier(); if (psiName != null) { GosuPsiImplUtil.setName(psiName, name); } return this; } public boolean hasTypeParameters() { return getTypeParameters().length > 0; } @Nullable public IGosuTypeVariableList getTypeParameterList() { return findChildByClass(IGosuTypeVariableList.class); } @NotNull public IGosuTypeVariable[] getTypeParameters() { final IGosuTypeVariableList list = getTypeParameterList(); if (list != null) { return (IGosuTypeVariable[]) list.getTypeParameters(); } return IGosuTypeVariable.EMPTY_ARRAY; } public PsiClass getContainingClass() { PsiElement parent = getParent(); if (parent instanceof PsiClass) { return (PsiClass) parent; } final PsiFile file = getContainingFile(); if (file instanceof IGosuFileBase) { return ((IGosuFileBase) file).getPsiClass(); } return null; } @Nullable public PsiDocComment getDocComment() { return GosuDocUtil.findDocCommnentNode(getNode()); } public boolean isDeprecated() { return GosuPsiImplUtil.isDeprecatedByAnnotation(this) || PsiImplUtil.isDeprecatedByDocTag(this); } @NotNull public SearchScope getUseScope() { return com.intellij.psi.impl.PsiImplUtil.getMemberUseScope(this); } @NotNull public PsiElement getOriginalElement() { final PsiClass containingClass = getContainingClass(); if (containingClass == null) { return this; } PsiClass originalClass = (PsiClass) containingClass.getOriginalElement(); final PsiMethod originalMethod = originalClass.findMethodBySignature(this, false); return originalMethod != null ? originalMethod : this; } public void delete() throws IncorrectOperationException { PsiElement parent = getParent(); if (parent instanceof AbstractGosuClassFileImpl || parent instanceof IGosuTypeDefinition) { super.delete(); return; } throw new IncorrectOperationException("Invalid enclosing type definition"); } public String[] getNamedParametersArray() { return ArrayUtil.EMPTY_STRING_ARRAY; // GosuOpenBlock body = getBlock(); // if( body == null ) // { // return ArrayUtil.EMPTY_STRING_ARRAY; // } // // IGosuParameter[] parameters = getParameters(); // if( parameters.length == 0 ) // { // return ArrayUtil.EMPTY_STRING_ARRAY; // } // IGosuParameter firstParameter = parameters[0]; // // PsiType type = firstParameter.getTypeGosu(); // IGosuTypeElement typeElement = firstParameter.getTypeElementGosu(); // //equalsToText can't be called here because of stub creating // // if( type != null && typeElement != null && type.getPresentableText() != null && !type.getPresentableText().endsWith( "Map" ) ) // { // return ArrayUtil.EMPTY_STRING_ARRAY; // } // // GosuNamedArgumentSearchVisitor visitor = new GosuNamedArgumentSearchVisitor( firstParameter.getNameIdentifierGosu().getText() ); // // body.accept( visitor ); // return visitor.getResult(); } @Override public boolean isForProperty() { IFunctionStatement parsedElement = getParsedElement(); if(parsedElement == null) { return false; } IDynamicFunctionSymbol dynamicFunctionSymbol = parsedElement.getDynamicFunctionSymbol(); return dynamicFunctionSymbol != null && dynamicFunctionSymbol.getName().startsWith( "@" ); } @Override public boolean isForPropertySetter() { IFunctionStatement parsedElement = getParsedElement(); if (parsedElement != null) { IParsedElement parent = parsedElement.getParent(); if (parent instanceof IPropertyStatement) { return ((IPropertyStatement) parent).getDps().getSetterDfs() == parsedElement.getDynamicFunctionSymbol(); } } return false; } @Override public boolean isForPropertyGetter() { IFunctionStatement parsedElement = getParsedElement(); if (parsedElement != null) { IParsedElement parent = parsedElement.getParent(); if (parent instanceof IPropertyStatement) { return ((IPropertyStatement) parent).getDps().getGetterDfs() == parsedElement.getDynamicFunctionSymbol(); } } return false; } public PsiMethodReceiver getMethodReceiver() { return null; } public PsiType getReturnTypeNoResolve() { throw new UnsupportedOperationException(); } @Override public boolean isEquivalentTo(PsiElement another) { return PsiClassImplUtil.isMethodEquivalentTo(this, another); } @Override protected Icon getElementIcon(@IconFlags int flags) { Icon methodIcon = hasModifierProperty(PsiModifier.ABSTRACT) ? GosuIcons.ABSTRACT_METHOD : GosuIcons.METHOD; RowIcon baseIcon = ElementPresentationUtil.createLayeredIcon(methodIcon, this, false); return ElementPresentationUtil.addVisibilityIcon(this, flags, baseIcon); } @Override public String toString() { return getName(); } }