1 /* 2 * Copyright 2016 Federico Tomassetti 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.github.javaparser.symbolsolver.javaparsermodel.contexts; 18 19 import com.github.javaparser.ast.body.Parameter; 20 import com.github.javaparser.ast.body.VariableDeclarator; 21 import com.github.javaparser.ast.expr.Expression; 22 import com.github.javaparser.ast.expr.LambdaExpr; 23 import com.github.javaparser.ast.expr.MethodCallExpr; 24 import com.github.javaparser.resolution.MethodUsage; 25 import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; 26 import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration; 27 import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration; 28 import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; 29 import com.github.javaparser.resolution.types.ResolvedLambdaConstraintType; 30 import com.github.javaparser.resolution.types.ResolvedType; 31 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; 32 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory; 33 import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic; 34 import com.github.javaparser.symbolsolver.logic.InferenceContext; 35 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; 36 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 37 import com.github.javaparser.symbolsolver.model.resolution.Value; 38 import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl; 39 import com.github.javaparser.symbolsolver.reflectionmodel.MyObjectProvider; 40 import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator; 41 42 import java.util.*; 43 44 import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; 45 46 /** 47 * @author Federico Tomassetti 48 */ 49 public class LambdaExprContext extends AbstractJavaParserContext<LambdaExpr> { 50 51 public LambdaExprContext(LambdaExpr wrappedNode, TypeSolver typeSolver) { 52 super(wrappedNode, typeSolver); 53 } 54 55 @Override 56 public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) { 57 for (Parameter parameter : wrappedNode.getParameters()) { 58 SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver); 59 int index = 0; 60 for (ResolvedValueDeclaration decl : sb.getSymbolDeclarations()) { 61 if (decl.getName().equals(name)) { 62 if (requireParentNode(wrappedNode) instanceof MethodCallExpr) { 63 MethodCallExpr methodCallExpr = (MethodCallExpr) requireParentNode(wrappedNode); 64 MethodUsage methodUsage = JavaParserFacade.get(typeSolver).solveMethodAsUsage(methodCallExpr); 65 int i = pos(methodCallExpr, wrappedNode); 66 ResolvedType lambdaType = methodUsage.getParamTypes().get(i); 67 68 // Get the functional method in order for us to resolve it's type arguments properly 69 Optional<MethodUsage> functionalMethodOpt = FunctionalInterfaceLogic.getFunctionalMethod(lambdaType); 70 if (functionalMethodOpt.isPresent()){ 71 MethodUsage functionalMethod = functionalMethodOpt.get(); 72 InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE); 73 74 // Resolve each type variable of the lambda, and use this later to infer the type of each 75 // implicit parameter 76 inferenceContext.addPair(lambdaType, new ReferenceTypeImpl(lambdaType.asReferenceType().getTypeDeclaration(), typeSolver)); 77 78 // Find the position of this lambda argument 79 boolean found = false; 80 int lambdaParamIndex; 81 for (lambdaParamIndex = 0; lambdaParamIndex < wrappedNode.getParameters().size(); lambdaParamIndex++){ 82 if (wrappedNode.getParameter(lambdaParamIndex).getName().getIdentifier().equals(name)){ 83 found = true; 84 break; 85 } 86 } 87 if (!found) { return Optional.empty(); } 88 89 // Now resolve the argument type using the inference context 90 ResolvedType argType = inferenceContext.resolve(inferenceContext.addSingle(functionalMethod.getParamType(lambdaParamIndex))); 91 92 ResolvedLambdaConstraintType conType; 93 if (argType.isWildcard()){ 94 conType = ResolvedLambdaConstraintType.bound(argType.asWildcard().getBoundedType()); 95 } else { 96 conType = ResolvedLambdaConstraintType.bound(argType); 97 } 98 Value value = new Value(conType, name); 99 return Optional.of(value); 100 } else{ 101 return Optional.empty(); 102 } 103 } else if (requireParentNode(wrappedNode) instanceof VariableDeclarator) { 104 VariableDeclarator variableDeclarator = (VariableDeclarator) requireParentNode(wrappedNode); 105 ResolvedType t = JavaParserFacade.get(typeSolver).convertToUsageVariableType(variableDeclarator); 106 Optional<MethodUsage> functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t); 107 if (functionalMethod.isPresent()) { 108 ResolvedType lambdaType = functionalMethod.get().getParamType(index); 109 110 // Replace parameter from declarator 111 Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes = new HashMap<>(); 112 if (lambdaType.isReferenceType()) { 113 for (com.github.javaparser.utils.Pair<ResolvedTypeParameterDeclaration, ResolvedType> entry : lambdaType.asReferenceType().getTypeParametersMap()) { 114 if (entry.b.isTypeVariable() && entry.b.asTypeParameter().declaredOnType()) { 115 ResolvedType ot = t.asReferenceType().typeParametersMap().getValue(entry.a); 116 lambdaType = lambdaType.replaceTypeVariables(entry.a, ot, inferredTypes); 117 } 118 } 119 } else if (lambdaType.isTypeVariable() && lambdaType.asTypeParameter().declaredOnType()) { 120 lambdaType = t.asReferenceType().typeParametersMap().getValue(lambdaType.asTypeParameter()); 121 } 122 123 Value value = new Value(lambdaType, name); 124 return Optional.of(value); 125 } else { 126 throw new UnsupportedOperationException(); 127 } 128 } else { 129 throw new UnsupportedOperationException(); 130 } 131 } 132 index++; 133 } 134 } 135 136 // if nothing is found we should ask the parent context 137 return getParent().solveSymbolAsValue(name, typeSolver); 138 } 139 140 @Override 141 public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) { 142 for (Parameter parameter : wrappedNode.getParameters()) { 143 SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver); 144 SymbolReference<ResolvedValueDeclaration> symbolReference = solveWith(sb, name); 145 if (symbolReference.isSolved()) { 146 return symbolReference; 147 } 148 } 149 150 // if nothing is found we should ask the parent context 151 return getParent().solveSymbol(name, typeSolver); 152 } 153 154 @Override 155 public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) { 156 return getParent().solveType(name, typeSolver); 157 } 158 159 @Override 160 public SymbolReference<ResolvedMethodDeclaration> solveMethod( 161 String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) { 162 return getParent().solveMethod(name, argumentsTypes, false, typeSolver); 163 } 164 165 /// 166 /// Protected methods 167 /// 168 169 protected final Optional<Value> solveWithAsValue(SymbolDeclarator symbolDeclarator, String name, TypeSolver typeSolver) { 170 for (ResolvedValueDeclaration decl : symbolDeclarator.getSymbolDeclarations()) { 171 if (decl.getName().equals(name)) { 172 173 throw new UnsupportedOperationException(); 174 } 175 } 176 return Optional.empty(); 177 } 178 179 /// 180 /// Private methods 181 /// 182 183 private int pos(MethodCallExpr callExpr, Expression param) { 184 int i = 0; 185 for (Expression p : callExpr.getArguments()) { 186 if (p == param) { 187 return i; 188 } 189 i++; 190 } 191 throw new IllegalArgumentException(); 192 } 193 } 194