MethodResolutionLogic.java revision f821be8dfe72fcd3f4e14f75420617b87ddb8689
1d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassettipackage me.tomassetti.symbolsolver.model; 2d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti 37b6c27e95fef9e725fbf9e15afb852bb952dfa34Federico Tomassettiimport me.tomassetti.symbolsolver.model.declarations.MethodAmbiguityException; 4630eb94d43670f2080c7273584d49c0b0d4162f1Federico Tomassettiimport me.tomassetti.symbolsolver.model.declarations.MethodDeclaration; 5f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassettiimport me.tomassetti.symbolsolver.model.reflection.ReflectionClassDeclaration; 674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassettiimport me.tomassetti.symbolsolver.model.usages.MethodUsage; 7630eb94d43670f2080c7273584d49c0b0d4162f1Federico Tomassettiimport me.tomassetti.symbolsolver.model.usages.TypeUsage; 8f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassettiimport me.tomassetti.symbolsolver.model.usages.TypeUsageOfTypeDeclaration; 9630eb94d43670f2080c7273584d49c0b0d4162f1Federico Tomassetti 10d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassettiimport java.util.List; 1174026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassettiimport java.util.Optional; 12d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassettiimport java.util.stream.Collectors; 13d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti 14d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti/** 15d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti * Created by federico on 02/08/15. 16d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti */ 17d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassettipublic class MethodResolutionLogic { 18d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti 1980f951258992f75441239c79e0f31eda7d47e107Federico Tomassetti public static boolean isApplicable(MethodDeclaration method, String name, List<TypeUsage> paramTypes, TypeSolver typeSolver) { 20d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti if (!method.getName().equals(name)) { 21d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti return false; 22d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 23d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti // TODO Consider varargs 24d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti if (method.getNoParams() != paramTypes.size()) { 25d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti return false; 26d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 27d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti for (int i=0; i<method.getNoParams(); i++) { 28f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti TypeUsage expectedType = method.getParam(i).getType(typeSolver); 29f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti for (TypeParameter tp : method.getTypeParameters()) { 30f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti expectedType = replaceTypeParam(expectedType, tp); 31f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti } 32f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti 33f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti if (name.equals("cloneNodes")) { 34f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti System.out.println("foo"); 35f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti } 36f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti 37f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti if (!expectedType.isAssignableBy(paramTypes.get(i), typeSolver)){ 38d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti return false; 39d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 40d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 41d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti return true; 42d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 43d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti 44f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti private static TypeUsage replaceTypeParam(TypeUsage typeUsage, TypeParameter tp){ 45f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti if (typeUsage.isTypeVariable()) { 46f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti if (typeUsage.getTypeName().equals(tp.getName())) { 47f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti // TODO use bounds 48f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti return new TypeUsageOfTypeDeclaration(new ReflectionClassDeclaration(Object.class)); 49f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti } 50f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti } 51f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti // TODO consider annidated types 52f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti return typeUsage; 53f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti } 54f821be8dfe72fcd3f4e14f75420617b87ddb8689Federico Tomassetti 5574026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti public static boolean isApplicable(MethodUsage method, String name, List<TypeUsage> paramTypes, TypeSolver typeSolver) { 5674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (!method.getName().equals(name)) { 5774026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return false; 5874026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 5974026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti // TODO Consider varargs 6074026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (method.getNoParams() != paramTypes.size()) { 6174026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return false; 6274026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 6374026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti for (int i=0; i<method.getNoParams(); i++) { 6474026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (!method.getParamType(i, typeSolver).isAssignableBy(paramTypes.get(i), typeSolver)){ 6574026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return false; 6674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 6774026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 6874026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return true; 6974026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 7074026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti 71a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti /** 72a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti * 73a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti * @param methods we expect the methods to be ordered such that inherited methods are later in the list 74a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti * @param name 75a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti * @param paramTypes 76a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti * @param typeSolver 77a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti * @return 78a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti */ 7980f951258992f75441239c79e0f31eda7d47e107Federico Tomassetti public static SymbolReference<MethodDeclaration> findMostApplicable(List<MethodDeclaration> methods, String name, List<TypeUsage> paramTypes, TypeSolver typeSolver){ 8080f951258992f75441239c79e0f31eda7d47e107Federico Tomassetti List<MethodDeclaration> applicableMethods = methods.stream().filter((m) -> isApplicable(m, name, paramTypes, typeSolver)).collect(Collectors.toList()); 81d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti if (applicableMethods.isEmpty()) { 82d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti return SymbolReference.unsolved(MethodDeclaration.class); 83d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 84d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti if (applicableMethods.size() == 1) { 85d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti return SymbolReference.solved(applicableMethods.get(0)); 86d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } else { 8727bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti MethodDeclaration winningCandidate = applicableMethods.get(0); 8827bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti for (int i=1; i<applicableMethods.size(); i++) { 8927bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti MethodDeclaration other = applicableMethods.get(i); 9027bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti if (isMoreSpecific(winningCandidate, other, typeSolver)) { 9127bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti // nothing to do 9227bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } else if (isMoreSpecific(other, winningCandidate, typeSolver)) { 9327bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti winningCandidate = other; 9427bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } else { 95a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti if (winningCandidate.declaringType().getQualifiedName().equals(other.declaringType().getQualifiedName())) { 967b6c27e95fef9e725fbf9e15afb852bb952dfa34Federico Tomassetti throw new MethodAmbiguityException("Ambiguous method call: cannot find a most applicable method: "+winningCandidate+", "+other); 97a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti } else { 98a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti // we expect the methods to be ordered such that inherited methods are later in the list 99a7fa0c3db19bd74be9affdd624447251963b68afFederico Tomassetti } 10027bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } 10127bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } 10227bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti return SymbolReference.solved(winningCandidate); 10327bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } 10427bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } 10527bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti 10627bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti private static boolean isMoreSpecific(MethodDeclaration methodA, MethodDeclaration methodB, TypeSolver typeSolver) { 10727bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti boolean oneMoreSpecificFound = false; 10827bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti for (int i=0; i < methodA.getNoParams(); i++){ 10909ceccd596e6aeebb5eca309093ef32216291f1eFederico Tomassetti TypeUsage tdA = methodA.getParam(i).getType(typeSolver); 11009ceccd596e6aeebb5eca309093ef32216291f1eFederico Tomassetti TypeUsage tdB = methodB.getParam(i).getType(typeSolver); 11127bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti // B is more specific 112f3b8a0115850b9157d99a134879e37a29b7235d5Federico Tomassetti if (tdB.isAssignableBy(tdA, typeSolver) && !tdA.isAssignableBy(tdB, typeSolver)) { 113590b94f8538d2c2fc3080c9b606bfaaacebcbde7Federico Tomassetti oneMoreSpecificFound = true; 11427bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } 11527bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti // A is more specific 116f3b8a0115850b9157d99a134879e37a29b7235d5Federico Tomassetti if (tdA.isAssignableBy(tdB, typeSolver) && !tdB.isAssignableBy(tdA, typeSolver)) { 117590b94f8538d2c2fc3080c9b606bfaaacebcbde7Federico Tomassetti return false; 11827bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti } 119d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 12027bee3fd5da7ae397014a33e4365608f77d04485Federico Tomassetti return oneMoreSpecificFound; 121d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti } 122d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti 12374026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti private static boolean isMoreSpecific(MethodUsage methodA, MethodUsage methodB, TypeSolver typeSolver) { 12474026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti boolean oneMoreSpecificFound = false; 12574026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti for (int i=0; i < methodA.getNoParams(); i++){ 12674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti TypeUsage tdA = methodA.getParamType(i, typeSolver); 12774026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti TypeUsage tdB = methodB.getParamType(i, typeSolver); 12874026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti // B is more specific 12974026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (tdB.isAssignableBy(tdA, typeSolver) && !tdA.isAssignableBy(tdB, typeSolver)) { 13074026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti oneMoreSpecificFound = true; 13174026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 13274026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti // A is more specific 13374026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (tdA.isAssignableBy(tdB, typeSolver) && !tdB.isAssignableBy(tdA, typeSolver)) { 13474026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return false; 13574026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 13674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 13774026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return oneMoreSpecificFound; 13874026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 13974026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti 14074026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti public static Optional<MethodUsage> findMostApplicableUsage(List<MethodUsage> methods, String name, List<TypeUsage> parameterTypes, TypeSolver typeSolver) { 14174026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti List<MethodUsage> applicableMethods = methods.stream().filter((m) -> isApplicable(m, name, parameterTypes, typeSolver)).collect(Collectors.toList()); 14274026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (applicableMethods.isEmpty()) { 14374026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return Optional.empty(); 14474026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 14574026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (applicableMethods.size() == 1) { 14674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return Optional.of(applicableMethods.get(0)); 14774026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } else { 14874026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti MethodUsage winningCandidate = applicableMethods.get(0); 14974026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti for (int i=1; i<applicableMethods.size(); i++) { 15074026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti MethodUsage other = applicableMethods.get(i); 15174026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (isMoreSpecific(winningCandidate, other, typeSolver)) { 15274026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti // nothing to do 15374026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } else if (isMoreSpecific(other, winningCandidate, typeSolver)) { 15474026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti winningCandidate = other; 15574026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } else { 15674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti if (winningCandidate.declaringType().getQualifiedName().equals(other.declaringType().getQualifiedName())) { 157283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti if (!areOverride(winningCandidate, other)) { 158283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti throw new MethodAmbiguityException("Ambiguous method call: cannot find a most applicable method: " + winningCandidate + ", " + other + ". First declared in " + winningCandidate.declaringType().getQualifiedName()); 159283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti } 16074026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } else { 16174026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti // we expect the methods to be ordered such that inherited methods are later in the list 16274026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 16374026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 16474026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 16574026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti return Optional.of(winningCandidate); 16674026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 16774026e2be2b8b569f1b1644ecb09456c1729ecc1Federico Tomassetti } 168283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti 169283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti private static boolean areOverride(MethodUsage winningCandidate, MethodUsage other) { 170283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti if (!winningCandidate.getName().equals(other.getName())) { 171283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti return false; 172283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti } 173283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti if (winningCandidate.getNoParams() != other.getNoParams()) { 174283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti return false; 175283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti } 176283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti for (int i=0;i<winningCandidate.getNoParams();i++) { 177283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti if (!winningCandidate.getParamTypes().get(i).equals(other.getParamTypes().get(i))) { 178283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti return false; 179283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti } 180283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti } 181283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti return true; 182283204dd5840cb11dd6c73dbbb196e80db79c9a4Federico Tomassetti } 183d420a496cd03b7e9b5c4cce5074d8a74da69f3cdFederico Tomassetti} 184