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