com.github.javaparser.resolution.logic.MethodResolutionLogic Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stubparser Show documentation
Show all versions of stubparser Show documentation
This project contains a parser for the Checker Framework's stub files: https://checkerframework.org/manual/#stub . It is a fork of the JavaParser project.
The newest version!
/*
* Copyright (C) 2015-2016 Federico Tomassetti
* Copyright (C) 2017-2024 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
package com.github.javaparser.resolution.logic;
import com.github.javaparser.resolution.MethodAmbiguityException;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.TypeSolver;
import com.github.javaparser.resolution.declarations.*;
import com.github.javaparser.resolution.model.SymbolReference;
import com.github.javaparser.resolution.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.resolution.types.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author Federico Tomassetti
*/
public class MethodResolutionLogic {
private static String JAVA_LANG_OBJECT = Object.class.getCanonicalName();
private static List groupVariadicParamValues(
List argumentsTypes, int startVariadic, ResolvedType variadicType) {
List res = new ArrayList<>(argumentsTypes.subList(0, startVariadic));
List variadicValues = argumentsTypes.subList(startVariadic, argumentsTypes.size());
if (variadicValues.isEmpty()) {
// TODO if there are no variadic values we should default to the bound of the formal type
res.add(variadicType);
} else {
ResolvedType componentType = findCommonType(variadicValues);
res.add(convertToVariadicParameter(componentType));
}
return res;
}
private static ResolvedType findCommonType(List variadicValues) {
if (variadicValues.isEmpty()) {
throw new IllegalArgumentException();
}
// TODO implement this decently
return variadicValues.get(0);
}
public static boolean isApplicable(
ResolvedMethodDeclaration method, String name, List argumentsTypes, TypeSolver typeSolver) {
return isApplicable(method, name, argumentsTypes, typeSolver, false);
}
/**
* Note the specific naming here -- parameters are part of the method declaration,
* while arguments are the values passed when calling a method.
* Note that "needle" refers to that value being used as a search/query term to match against.
*
* @return true, if the given ResolvedMethodDeclaration matches the given name/types (normally obtained from a MethodUsage)
*
* @see {@link MethodResolutionLogic#isApplicable(MethodUsage, String, List, TypeSolver)}
*/
private static boolean isApplicable(
ResolvedMethodDeclaration methodDeclaration,
String needleName,
List needleArgumentTypes,
TypeSolver typeSolver,
boolean withWildcardTolerance) {
if (!methodDeclaration.getName().equals(needleName)) {
return false;
}
// The index of the final method parameter (on the method declaration).
int countOfMethodParametersDeclared = methodDeclaration.getNumberOfParams();
// The index of the final argument passed (on the method usage).
int countOfNeedleArgumentsPassed = needleArgumentTypes.size();
boolean methodIsDeclaredWithVariadicParameter = methodDeclaration.hasVariadicParameter();
if (!methodIsDeclaredWithVariadicParameter
&& (countOfNeedleArgumentsPassed != countOfMethodParametersDeclared)) {
// If it is not variadic, and the number of parameters/arguments are unequal -- this is not a match.
return false;
}
if (methodIsDeclaredWithVariadicParameter) {
if (countOfNeedleArgumentsPassed <= (countOfMethodParametersDeclared - 2)) {
// If it is variadic, and the number of arguments are short by **two or more** -- this is not a match.
// Note that omitting the variadic parameter is treated as an empty array
// (thus being short of only 1 argument is fine, but being short of 2 or more is not).
return false;
}
// If the method declaration we're considering has a variadic parameter,
// attempt to convert the given list of arguments to fit this pattern
// e.g. foo(String s, String... s2) {} --- consider the first argument, then group the remainder as an array
ResolvedType expectedVariadicParameterType =
methodDeclaration.getLastParam().getType();
for (ResolvedTypeParameterDeclaration tp : methodDeclaration.getTypeParameters()) {
expectedVariadicParameterType = replaceTypeParam(expectedVariadicParameterType, tp, typeSolver);
}
if (countOfNeedleArgumentsPassed > countOfMethodParametersDeclared) {
// If it is variadic, and we have an "excess" of arguments, group the "trailing" arguments into an
// array.
// Confirm all of these grouped "trailing" arguments have the required type -- if not, this is not a
// valid type. (Maybe this is also done later..?)
for (int variadicArgumentIndex = countOfMethodParametersDeclared;
variadicArgumentIndex < countOfNeedleArgumentsPassed;
variadicArgumentIndex++) {
ResolvedType currentArgumentType = needleArgumentTypes.get(variadicArgumentIndex);
boolean argumentIsAssignableToVariadicComponentType = expectedVariadicParameterType
.asArrayType()
.getComponentType()
.isAssignableBy(currentArgumentType);
if (!argumentIsAssignableToVariadicComponentType) {
// If any of the arguments are not assignable to the expected variadic type, this is not a
// match.
return false;
}
}
}
needleArgumentTypes = groupTrailingArgumentsIntoArray(
methodDeclaration, needleArgumentTypes, expectedVariadicParameterType);
}
// The index of the final argument passed (on the method usage).
int countOfNeedleArgumentsPassedAfterGrouping = needleArgumentTypes.size();
// If variadic parameters are possible then they will have been "grouped" into a single argument.
// At this point, therefore, the number of arguments must be equal -- if they're not, then there is no match.
if (countOfNeedleArgumentsPassedAfterGrouping != countOfMethodParametersDeclared) {
return false;
}
Map matchedParameters = new HashMap<>();
boolean needForWildCardTolerance = false;
for (int i = 0; i < countOfMethodParametersDeclared; i++) {
ResolvedType expectedDeclaredType = methodDeclaration.getParam(i).getType();
ResolvedType actualArgumentType = needleArgumentTypes.get(i);
if ((expectedDeclaredType.isTypeVariable() && !(expectedDeclaredType.isWildcard()))
&& expectedDeclaredType.asTypeParameter().declaredOnMethod()) {
matchedParameters.put(expectedDeclaredType.asTypeParameter().getName(), actualArgumentType);
continue;
}
// if this is a variable arity method and we are trying to evaluate the last parameter
// then we consider that an array of objects can be assigned by any array
// for example:
// The method call expression String.format("%d", new int[] {1})
// must refer to the method String.format(String, Object...)
// even if an array of primitive type cannot be assigned to an array of Object
if (methodDeclaration.getParam(i).isVariadic()
&& (i == countOfMethodParametersDeclared - 1)
&& isArrayOfObject(expectedDeclaredType)
&& actualArgumentType.isArray()) {
continue;
}
boolean isAssignableWithoutSubstitution = expectedDeclaredType.isAssignableBy(actualArgumentType)
|| (methodDeclaration.getParam(i).isVariadic()
&& convertToVariadicParameter(expectedDeclaredType).isAssignableBy(actualArgumentType));
if (!isAssignableWithoutSubstitution
&& expectedDeclaredType.isReferenceType()
&& actualArgumentType.isReferenceType()) {
isAssignableWithoutSubstitution = isAssignableMatchTypeParameters(
expectedDeclaredType.asReferenceType(),
actualArgumentType.asReferenceType(),
matchedParameters);
}
if (!isAssignableWithoutSubstitution) {
List typeParameters = methodDeclaration.getTypeParameters();
typeParameters.addAll(methodDeclaration.declaringType().getTypeParameters());
for (ResolvedTypeParameterDeclaration tp : typeParameters) {
expectedDeclaredType = replaceTypeParam(expectedDeclaredType, tp, typeSolver);
}
if (!expectedDeclaredType.isAssignableBy(actualArgumentType)) {
if (actualArgumentType.isWildcard()
&& withWildcardTolerance
&& !expectedDeclaredType.isPrimitive()) {
needForWildCardTolerance = true;
continue;
}
// if the expected is java.lang.Math.max(double,double) and the type parameters are defined with
// constrain
// for example LambdaConstraintType{bound=TypeVariable {ReflectionTypeParameter{typeVariable=T}}},
// LambdaConstraintType{bound=TypeVariable {ReflectionTypeParameter{typeVariable=U}}}
// we want to keep this method for future resolution
if (actualArgumentType.isConstraint()
&& withWildcardTolerance
&& expectedDeclaredType.isPrimitive()) {
needForWildCardTolerance = true;
continue;
}
if (methodIsDeclaredWithVariadicParameter && i == countOfMethodParametersDeclared - 1) {
if (convertToVariadicParameter(expectedDeclaredType).isAssignableBy(actualArgumentType)) {
continue;
}
}
return false;
}
}
}
return !withWildcardTolerance || needForWildCardTolerance;
}
private static boolean isArrayOfObject(ResolvedType type) {
return type.isArray()
&& type.asArrayType().getComponentType().isReferenceType()
&& type.asArrayType().getComponentType().asReferenceType().isJavaLangObject();
}
private static ResolvedArrayType convertToVariadicParameter(ResolvedType type) {
return type.isArray() ? type.asArrayType() : new ResolvedArrayType(type);
}
/*
* Returns the last parameter index
*/
private static int getLastParameterIndex(int countOfMethodParametersDeclared) {
return Math.max(0, countOfMethodParametersDeclared - 1);
}
private static List groupTrailingArgumentsIntoArray(
ResolvedMethodDeclaration methodDeclaration,
List needleArgumentTypes,
ResolvedType expectedVariadicParameterType) {
// The index of the final method parameter (on the method declaration).
int countOfMethodParametersDeclared = methodDeclaration.getNumberOfParams();
int lastMethodParameterIndex = getLastParameterIndex(countOfMethodParametersDeclared);
// The index of the final argument passed (on the method usage).
int countOfNeedleArgumentsPassed = needleArgumentTypes.size();
int lastNeedleArgumentIndex = getLastParameterIndex(countOfNeedleArgumentsPassed);
if (countOfNeedleArgumentsPassed > countOfMethodParametersDeclared) {
// If it is variadic, and we have an "excess" of arguments, group the "trailing" arguments into an array.
// Here we are sure that all of these grouped "trailing" arguments have the required type
needleArgumentTypes = groupVariadicParamValues(
needleArgumentTypes,
lastMethodParameterIndex,
methodDeclaration.getLastParam().getType());
}
if (countOfNeedleArgumentsPassed == (countOfMethodParametersDeclared - 1)) {
// If it is variadic and we are short of **exactly one** parameter, this is a match.
// Note that omitting the variadic parameter is treated as an empty array
// (thus being short of only 1 argument is fine, but being short of 2 or more is not).
// thus group the "empty" value into an empty array...
needleArgumentTypes = groupVariadicParamValues(
needleArgumentTypes,
lastMethodParameterIndex,
methodDeclaration.getLastParam().getType());
} else if (countOfNeedleArgumentsPassed == countOfMethodParametersDeclared) {
ResolvedType actualArgumentType = needleArgumentTypes.get(lastNeedleArgumentIndex);
boolean finalArgumentIsArray = actualArgumentType.isArray()
&& expectedVariadicParameterType.isAssignableBy(
actualArgumentType.asArrayType().getComponentType());
if (finalArgumentIsArray) {
// Treat as an array of values -- in which case the expected parameter type is the common type of this
// array.
// no need to do anything
// expectedVariadicParameterType = actualArgumentType.asArrayType().getComponentType();
} else {
// Treat as a single value -- in which case, the expected parameter type is the same as the single
// value.
needleArgumentTypes = groupVariadicParamValues(
needleArgumentTypes,
lastMethodParameterIndex,
methodDeclaration.getLastParam().getType());
}
} else {
// Should be unreachable.
}
return needleArgumentTypes;
}
public static boolean isAssignableMatchTypeParameters(
ResolvedType expected, ResolvedType actual, Map matchedParameters) {
if (expected.isReferenceType() && actual.isReferenceType()) {
return isAssignableMatchTypeParameters(
expected.asReferenceType(), actual.asReferenceType(), matchedParameters);
}
if (expected.isReferenceType() && ResolvedPrimitiveType.isBoxType(expected) && actual.isPrimitive()) {
ResolvedPrimitiveType expectedType = ResolvedPrimitiveType.byBoxTypeQName(
expected.asReferenceType().getQualifiedName())
.get()
.asPrimitive();
return expected.isAssignableBy(actual);
}
if (expected.isTypeVariable()) {
matchedParameters.put(expected.asTypeParameter().getName(), actual);
return true;
}
if (expected.isArray()) {
matchedParameters.put(expected.asArrayType().getComponentType().toString(), actual);
return true;
}
throw new UnsupportedOperationException(
expected.getClass().getCanonicalName() + " " + actual.getClass().getCanonicalName());
}
public static boolean isAssignableMatchTypeParameters(
ResolvedReferenceType expected, ResolvedReferenceType actual, Map matchedParameters) {
if (actual.getQualifiedName().equals(expected.getQualifiedName())) {
return isAssignableMatchTypeParametersMatchingQName(expected, actual, matchedParameters);
} else {
List ancestors = actual.getAllAncestors();
for (ResolvedReferenceType ancestor : ancestors) {
if (isAssignableMatchTypeParametersMatchingQName(expected, ancestor, matchedParameters)) {
return true;
}
}
}
return false;
}
private static boolean isAssignableMatchTypeParametersMatchingQName(
ResolvedReferenceType expected, ResolvedReferenceType actual, Map matchedParameters) {
if (!expected.getQualifiedName().equals(actual.getQualifiedName())) {
return false;
}
if (expected.typeParametersValues().size()
!= actual.typeParametersValues().size()) {
throw new UnsupportedOperationException();
// return true;
}
for (int i = 0; i < expected.typeParametersValues().size(); i++) {
ResolvedType expectedParam = expected.typeParametersValues().get(i);
ResolvedType actualParam = actual.typeParametersValues().get(i);
// In the case of nested parameterizations eg. List <-> List
// we should peel off one layer and ensure R <-> Integer
if (expectedParam.isReferenceType() && actualParam.isReferenceType()) {
ResolvedReferenceType r1 = expectedParam.asReferenceType();
ResolvedReferenceType r2 = actualParam.asReferenceType();
// we can have r1=A and r2=A.B (with B extends A and B is an inner class of A)
// in this case we want to verify expected parameter from the actual parameter ancestors
return isAssignableMatchTypeParameters(r1, r2, matchedParameters);
}
if (expectedParam.isArray() && actualParam.isArray()) {
ResolvedType r1 = expectedParam.asArrayType().getComponentType();
ResolvedType r2 = actualParam.asArrayType().getComponentType();
// try to verify the component type of each array
return isAssignableMatchTypeParameters(r1, r2, matchedParameters);
}
if (expectedParam.isTypeVariable()) {
String expectedParamName = expectedParam.asTypeParameter().getName();
if (!actualParam.isTypeVariable()
|| !actualParam.asTypeParameter().getName().equals(expectedParamName)) {
return matchTypeVariable(expectedParam.asTypeVariable(), actualParam, matchedParameters);
}
// actualParam is a TypeVariable and actualParam has the same name as expectedParamName
// We should definitely consider that types are assignable
return true;
} else if (expectedParam.isReferenceType()) {
if (actualParam.isTypeVariable()) {
return matchTypeVariable(actualParam.asTypeVariable(), expectedParam, matchedParameters);
}
if (!expectedParam.equals(actualParam)) {
return false;
}
}
if (expectedParam.isWildcard()) {
if (expectedParam.asWildcard().isExtends()) {
// trying to compare with unbounded wildcard type parameter >
if (actualParam.isWildcard() && !actualParam.asWildcard().isBounded()) {
return true;
}
if (actualParam.isTypeVariable()) {
return matchTypeVariable(
actualParam.asTypeVariable(),
expectedParam.asWildcard().getBoundedType(),
matchedParameters);
}
return isAssignableMatchTypeParameters(
expectedParam.asWildcard().getBoundedType(), actualParam, matchedParameters);
}
// TODO verify super bound
return true;
}
throw new UnsupportedOperationException(expectedParam.describe());
}
return true;
}
private static boolean matchTypeVariable(
ResolvedTypeVariable typeVariable, ResolvedType type, Map matchedParameters) {
String typeParameterName = typeVariable.asTypeParameter().getName();
if (matchedParameters.containsKey(typeParameterName)) {
ResolvedType matchedParameter = matchedParameters.get(typeParameterName);
if (matchedParameter.isAssignableBy(type)) {
return true;
}
if (type.isAssignableBy(matchedParameter)) {
// update matchedParameters to contain the more general type
matchedParameters.put(typeParameterName, type);
return true;
}
return false;
} else {
matchedParameters.put(typeParameterName, type);
}
return true;
}
public static ResolvedType replaceTypeParam(
ResolvedType type, ResolvedTypeParameterDeclaration tp, TypeSolver typeSolver) {
if (type.isTypeVariable() || type.isWildcard()) {
if (type.describe().equals(tp.getName())) {
List bounds = tp.getBounds();
if (bounds.size() > 1) {
throw new UnsupportedOperationException();
}
if (bounds.size() == 1) {
return bounds.get(0).getType();
}
return new ReferenceTypeImpl(typeSolver.solveType(JAVA_LANG_OBJECT));
}
return type;
}
if (type.isPrimitive()) {
return type;
}
if (type.isArray()) {
return new ResolvedArrayType(replaceTypeParam(type.asArrayType().getComponentType(), tp, typeSolver));
}
if (type.isReferenceType()) {
ResolvedReferenceType result = type.asReferenceType();
result = result.transformTypeParameters(typeParam -> replaceTypeParam(typeParam, tp, typeSolver))
.asReferenceType();
return result;
}
throw new UnsupportedOperationException("Replacing " + type + ", param " + tp + " with "
+ type.getClass().getCanonicalName());
}
/**
* Note the specific naming here -- parameters are part of the method declaration,
* while arguments are the values passed when calling a method.
* Note that "needle" refers to that value being used as a search/query term to match against.
*
* @return true, if the given MethodUsage matches the given name/types (normally obtained from a ResolvedMethodDeclaration)
*
* @see {@link MethodResolutionLogic#isApplicable(ResolvedMethodDeclaration, String, List, TypeSolver)} }
* @see {@link MethodResolutionLogic#isApplicable(ResolvedMethodDeclaration, String, List, TypeSolver, boolean)}
*/
public static boolean isApplicable(
MethodUsage methodUsage,
String needleName,
List needleParameterTypes,
TypeSolver typeSolver) {
if (!methodUsage.getName().equals(needleName)) {
return false;
}
// The index of the final method parameter (on the method declaration).
int countOfMethodUsageArgumentsPassed = methodUsage.getNoParams();
int lastMethodUsageArgumentIndex = getLastParameterIndex(countOfMethodUsageArgumentsPassed);
// The index of the final argument passed (on the method usage).
int needleParameterCount = needleParameterTypes.size();
// TODO: Does the method usage have a declaration at this point..?
boolean methodIsDeclaredWithVariadicParameter =
methodUsage.getDeclaration().hasVariadicParameter();
// If the counts do not match and the method is not variadic, this is not a match.
if (!methodIsDeclaredWithVariadicParameter && !(needleParameterCount == countOfMethodUsageArgumentsPassed)) {
return false;
}
// If the counts do not match and we have provided too few arguments, this is not a match. Note that variadic
// parameters
// allow you to omit the vararg, which would allow a difference of one, but a difference in count of 2 or more
// is not a match.
if (!(needleParameterCount == countOfMethodUsageArgumentsPassed)
&& needleParameterCount < lastMethodUsageArgumentIndex) {
return false;
}
// Iterate over the arguments given to the method, and compare their types against the given method's declared
// parameter types
for (int i = 0; i < needleParameterCount; i++) {
ResolvedType actualArgumentType = needleParameterTypes.get(i);
ResolvedType expectedArgumentType;
boolean reachedVariadicParam = methodIsDeclaredWithVariadicParameter && i >= lastMethodUsageArgumentIndex;
if (!reachedVariadicParam) {
// Not yet reached the variadic parameters -- the expected type is just whatever is at that position.
expectedArgumentType = methodUsage.getParamType(i);
} else {
// We have reached the variadic parameters -- the expected type is the type of the last declared
// parameter.
expectedArgumentType = methodUsage.getParamType(lastMethodUsageArgumentIndex);
// Note that the given variadic value might be an array - if so, use the array's component type rather.
// This is only valid if ONE argument has been given to the vararg parameter.
// Example: {@code void test(String... s) {}} and {@code test(stringArray)} -- {@code String... is
// assignable by stringArray}
// Example: {@code void test(String[]... s) {}} and {@code test(stringArrayArray)} -- {@code String[]...
// is assignable by stringArrayArray}
boolean argumentIsArray = (needleParameterCount == countOfMethodUsageArgumentsPassed)
&& expectedArgumentType.isAssignableBy(actualArgumentType);
if (!argumentIsArray) {
// Get the component type of the declared parameter type.
expectedArgumentType = expectedArgumentType.asArrayType().getComponentType();
}
}
// Consider type parameters directly on the method declaration, and ALSO on the enclosing type (e.g. a
// class)
List typeParameters =
methodUsage.getDeclaration().getTypeParameters();
typeParameters.addAll(methodUsage.declaringType().getTypeParameters());
ResolvedType expectedTypeWithoutSubstitutions = expectedArgumentType;
ResolvedType expectedTypeWithInference = expectedArgumentType;
Map derivedValues = new HashMap<>();
// For each declared parameter, infer the types that will replace generics (type parameters)
for (int j = 0; j < countOfMethodUsageArgumentsPassed; j++) {
ResolvedParameterDeclaration parameter =
methodUsage.getDeclaration().getParam(j);
ResolvedType parameterType = parameter.getType();
if (parameter.isVariadic()) {
// Don't continue if a vararg parameter is reached and there are no arguments left
if (needleParameterCount == j) {
break;
}
parameterType = parameterType.asArrayType().getComponentType();
}
inferTypes(needleParameterTypes.get(j), parameterType, derivedValues);
}
for (Map.Entry entry : derivedValues.entrySet()) {
ResolvedTypeParameterDeclaration tp = entry.getKey();
expectedTypeWithInference = expectedTypeWithInference.replaceTypeVariables(tp, entry.getValue());
}
// Consider cases where type variables can be replaced (e.g. add(E element) vs add(String element))
for (ResolvedTypeParameterDeclaration tp : typeParameters) {
if (tp.getBounds().isEmpty()) {
// expectedArgumentType = expectedArgumentType.replaceTypeVariables(tp.getName(), new
// ReferenceTypeUsageImpl(typeSolver.solveType(JAVA_LANG_OBJECT), typeSolver));
expectedArgumentType = expectedArgumentType.replaceTypeVariables(
tp,
ResolvedWildcard.extendsBound(
new ReferenceTypeImpl(typeSolver.solveType(JAVA_LANG_OBJECT))));
} else if (tp.getBounds().size() == 1) {
ResolvedTypeParameterDeclaration.Bound bound =
tp.getBounds().get(0);
if (bound.isExtends()) {
// expectedArgumentType = expectedArgumentType.replaceTypeVariables(tp.getName(),
// bound.getType());
expectedArgumentType = expectedArgumentType.replaceTypeVariables(
tp, ResolvedWildcard.extendsBound(bound.getType()));
} else {
// expectedArgumentType = expectedArgumentType.replaceTypeVariables(tp.getName(), new
// ReferenceTypeUsageImpl(typeSolver.solveType(JAVA_LANG_OBJECT), typeSolver));
expectedArgumentType = expectedArgumentType.replaceTypeVariables(
tp, ResolvedWildcard.superBound(bound.getType()));
}
} else {
throw new UnsupportedOperationException();
}
}
// Consider cases where type variables involve bounds e.g. super/extends
ResolvedType expectedTypeWithSubstitutions = expectedTypeWithoutSubstitutions;
for (ResolvedTypeParameterDeclaration tp : typeParameters) {
if (tp.getBounds().isEmpty()) {
expectedTypeWithSubstitutions = expectedTypeWithSubstitutions.replaceTypeVariables(
tp, new ReferenceTypeImpl(typeSolver.solveType(JAVA_LANG_OBJECT)));
} else if (tp.getBounds().size() == 1) {
ResolvedTypeParameterDeclaration.Bound bound =
tp.getBounds().get(0);
if (bound.isExtends()) {
expectedTypeWithSubstitutions =
expectedTypeWithSubstitutions.replaceTypeVariables(tp, bound.getType());
} else {
expectedTypeWithSubstitutions = expectedTypeWithSubstitutions.replaceTypeVariables(
tp, new ReferenceTypeImpl(typeSolver.solveType(JAVA_LANG_OBJECT)));
}
} else {
throw new UnsupportedOperationException();
}
}
// If the given argument still isn't applicable even after considering type arguments/generics, this is not
// a match.
if (!expectedArgumentType.isAssignableBy(actualArgumentType)
&& !expectedTypeWithSubstitutions.isAssignableBy(actualArgumentType)
&& !expectedTypeWithInference.isAssignableBy(actualArgumentType)
&& !expectedTypeWithoutSubstitutions.isAssignableBy(actualArgumentType)) {
return false;
}
}
// If the checks above haven't failed, then we've found a match.
return true;
}
/**
* Filters by given function {@param keyExtractor} using a stateful filter mechanism.
*
*
* persons.stream().filter(distinctByKey(Person::getName))
*
*
* The example above would return a distinct list of persons containing only one person per name.
*/
private static Predicate distinctByKey(Function super T, ?> keyExtractor) {
Set
© 2015 - 2024 Weber Informatics LLC | Privacy Policy