Home | History | Annotate | Download | only in contexts
      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