/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser.java.classinfo; import gw.internal.gosu.parser.GenericTypeVariable; import gw.internal.gosu.parser.TypeLord; import gw.internal.gosu.parser.java.IJavaASTNode; import gw.internal.gosu.parser.java.JavaASTConstants; import gw.internal.gosu.parser.java.JavaParser; import gw.internal.gosu.parser.java.TypeASTNode; import gw.lang.parser.TypeVarToTypeMap; import gw.lang.reflect.FunctionType; import gw.lang.reflect.IAnnotationInfo; import gw.lang.reflect.IFeatureInfo; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.IType; import gw.lang.reflect.SimpleParameterInfo; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.java.ClassInfoUtil; import gw.lang.reflect.java.IJavaClassInfo; import gw.lang.reflect.java.IJavaClassMethod; import gw.lang.reflect.java.IJavaClassType; import gw.lang.reflect.java.IJavaClassTypeVariable; import gw.lang.reflect.java.IJavaMethodInfo; import gw.lang.reflect.java.ITypeInfoResolver; import gw.lang.reflect.module.IModule; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.List; public class JavaSourceMethod implements IJavaClassMethod, ITypeInfoResolver { protected IJavaASTNode _methodNode; protected JavaSourceType _containingClass; protected JavaSourceParameter[] _parameters; protected IJavaClassType _genericReturnType; protected JavaSourceModifierList _modifierList; protected IJavaClassTypeVariable[] _typeParameters; protected IJavaClassType[] _genericParameterTypes; protected IJavaClassInfo[] _parameterTypes; protected IJavaClassInfo _returnType; public JavaSourceMethod(IJavaASTNode methodNode, JavaSourceType containingClass) { _methodNode = methodNode; _containingClass = containingClass; } @Override public IJavaClassInfo getReturnClassInfo() { if (_returnType == null) { int indexOfName = _methodNode.getChildOfTypeIndex(JavaParser.IDENTIFIER); IJavaASTNode typeNode = _methodNode.getChild(indexOfName - 1); String typeName; if (typeNode instanceof TypeASTNode) { typeName = ((TypeASTNode) typeNode).getTypeName(); } else { typeName = typeNode.getText(); } _returnType = (IJavaClassInfo) JavaSourceType.createType(this, typeName, JavaSourceType.IGNORE_NONE).getConcreteType(); if (_returnType == null) { throw new RuntimeException("Cannot compute return type."); } } return _returnType; } public IJavaClassType getGenericReturnType() { if (_genericReturnType == null) { int indexOfName = _methodNode.getChildOfTypeIndex(JavaParser.IDENTIFIER); _genericReturnType = JavaSourceType.createType(this, _methodNode.getChild(indexOfName - 1)); if (_genericReturnType == null) { throw new RuntimeException("Cannot compute return type."); } } return _genericReturnType; } @Override public String getReturnTypeName() { IJavaClassType type = getGenericReturnType(); if (type instanceof JavaArrayClassInfo ) { return "[" + ((JavaArrayClassInfo) type).getComponentType().getNameSignature(); } else { return type.getName(); } } @Override public IType getReturnType() { return getReturnClassInfo().getJavaType(); } @Override public IJavaClassType[] getGenericParameterTypes() { if (_genericParameterTypes == null) { _genericParameterTypes = initGenericParameterTypes(); } return _genericParameterTypes; } @Override public IJavaClassInfo[] getParameterTypes() { if (_parameterTypes == null) { _parameterTypes = initParameterTypes(); } return _parameterTypes; } public String getName() { return _methodNode.getChildOfType(JavaParser.IDENTIFIER).getText(); } public JavaSourceParameter[] getParameters() { if (_parameters == null) { IJavaASTNode parametersNode = _methodNode.getChildOfTypes(JavaASTConstants.formalParameters); if (parametersNode == null) { _parameters = new JavaSourceParameter[0]; } else { List<IJavaASTNode> parameterNodes = parametersNode.getChildrenOfTypes(JavaASTConstants.normalParameterDecl, JavaASTConstants.ellipsisParameterDecl); _parameters = new JavaSourceParameter[parameterNodes.size()]; for (int i = 0; i < _parameters.length; i++) { _parameters[i] = new JavaSourceParameter(this, parameterNodes.get(i)); } } } return _parameters; } public boolean isConstructor() { return false; } public JavaSourceType getEnclosingClass() { return _containingClass; } @Override public int getModifiers() { return getModifierList().getModifiers(); } public IModifierList getModifierList() { if (_modifierList == null) { _modifierList = new JavaSourceModifierList(this, _methodNode.getChildOfType(JavaASTConstants.modifiers)); } return _modifierList; } public IJavaClassInfo[] getExceptionTypes() { return new IJavaClassInfo[0]; } @Override public IJavaClassType resolveType(String relativeName, int ignoreFlags) { IJavaClassTypeVariable[] typeParameters = getTypeParameters(); for (IJavaClassTypeVariable typeParameter : typeParameters) { if (relativeName.equals(typeParameter.getName())) { return typeParameter; } } return _containingClass.resolveType(relativeName, ignoreFlags); } @Override public IJavaClassType resolveType(String relativeName, IJavaClassInfo whosAskin, int ignoreFlags) { return null; } @Override public IJavaClassType resolveImport(String relativeName) { return null; } @Override public IModule getModule() { return _containingClass.getModule(); } public String toString() { return getName() + "(...)"; } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { return getModifierList().isAnnotationPresent(annotationClass); } @Override public IAnnotationInfo getAnnotation(Class annotationClass) { return getModifierList().getAnnotation(annotationClass); } @Override public IAnnotationInfo[] getDeclaredAnnotations() { return getModifierList().getAnnotations(); } protected IJavaClassInfo[] initParameterTypes() { JavaSourceParameter[] psiParameters = getParameters(); IJavaClassInfo[] paramTypes = new IJavaClassInfo[psiParameters.length]; for (int i = 0; i < psiParameters.length; i++) { paramTypes[i] = psiParameters[i].getType(); } return paramTypes; } protected IJavaClassType[] initGenericParameterTypes() { JavaSourceParameter[] parameters = getParameters(); IJavaClassType[] types = new IJavaClassType[parameters.length]; for (int i = 0; i < parameters.length; i++) { JavaSourceParameter parameter = parameters[i]; types[i] = parameter.getGenericType(); } return types; } @Override public Object getDefaultValue() { throw new RuntimeException("Should never be called"); } @Override public Object invoke(Object ctx, Object[] args) throws InvocationTargetException, IllegalAccessException { throw new RuntimeException("Not supported"); } public IJavaClassTypeVariable[] getTypeParameters() { if (_typeParameters == null) { IJavaASTNode typeParamsNode = _methodNode.getChildOfType(JavaASTConstants.typeParameters); if (typeParamsNode != null) { List<IJavaASTNode> typeParamNodes = typeParamsNode.getChildrenOfTypes(JavaASTConstants.typeParameter); _typeParameters = new IJavaClassTypeVariable[typeParamNodes.size()]; for (int i = 0; i < _typeParameters.length; i++) { _typeParameters[i] = JavaSourceTypeVariable.create(this, typeParamNodes.get(i)); } } else { _typeParameters = JavaSourceTypeVariable.EMPTY; } } return _typeParameters; } @Override public IGenericTypeVariable[] getTypeVariables(IJavaMethodInfo javaMethodInfo) { IJavaClassTypeVariable[] rawTypeParams = getTypeParameters(); IType declClass = TypeSystem.get( getEnclosingClass() ); TypeVarToTypeMap actualParamByVarName = TypeLord.mapTypeByVarName( declClass, declClass ); FunctionType functionType = new FunctionType(javaMethodInfo, true); return GenericTypeVariable.convertTypeVars( functionType, rawTypeParams, actualParamByVarName ); } @Override public boolean isBridge() { return false; } @Override public boolean isSynthetic() { return false; } public static JavaSourceMethod create(IJavaASTNode methodNode, JavaSourceType containingClass) { IJavaASTNode nameNode = methodNode.getChildOfType(JavaParser.IDENTIFIER); if (nameNode == null) { return null; } String methodName = nameNode.getText(); if (methodName.equals(containingClass.getSimpleName())) { return new JavaSourceConstructor(methodNode, containingClass); } else if (containingClass.isAnnotation()) { return new JavaSourceAnnotationMethod(methodNode, containingClass); } else { return new JavaSourceMethod(methodNode, containingClass); } } protected IParameterInfo[] getActualParameterInfos(IFeatureInfo container, TypeVarToTypeMap actualParamByVarName, boolean bKeepTypeVars) { IType[] paramTypes = ClassInfoUtil.getActualTypes(getGenericParameterTypes(), actualParamByVarName, bKeepTypeVars); IParameterInfo[] paramInfos = new IParameterInfo[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { paramInfos[i] = new SimpleParameterInfo(container, paramTypes[i], i); } return paramInfos; } @Override public int hashCode() { int result = Arrays.hashCode( _parameterTypes ); result = 31 * result + _returnType.hashCode(); result = 31 * result + getName().hashCode(); return result; } public boolean equals( Object o ) { if( !(o instanceof IJavaClassMethod) ) { return false; } IJavaClassMethod jcm = (IJavaClassMethod)o; return getName().equals( jcm.getName() ) && getReturnType() == jcm.getReturnType() && Arrays.equals( getParameterTypes(), jcm.getParameterTypes() ); } @Override public int compareTo( IJavaClassMethod o ) { return getName().compareTo( o.getName() ); } }