org.walkmod.javalang.compiler.reflection.MethodInspector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javalang-compiler Show documentation
Show all versions of javalang-compiler Show documentation
Library of compiler components to processs Java code.
/*
Copyright (C) 2015 Raquel Pau and Albert Coroleu.
Walkmod is free software: you can redistribute it and/or modify
it under the terms of 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.
Walkmod 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.
You should have received a copy of the GNU Lesser General Public License
along with Walkmod. If not, see .*/
package org.walkmod.javalang.compiler.reflection;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.walkmod.javalang.ast.SymbolData;
import org.walkmod.javalang.compiler.ArrayFilter;
import org.walkmod.javalang.compiler.CompositeBuilder;
import org.walkmod.javalang.compiler.Predicate;
import org.walkmod.javalang.compiler.symbols.SymbolType;
import org.walkmod.javalang.exceptions.NoSuchExpressionTypeException;
public class MethodInspector {
private static GenericBuilderFromGenericClasses b1 = new GenericBuilderFromGenericClasses();
public static Method findMethod(Class> scope,
T[] args, String name) {
Map typeMapping = new HashMap();
ArrayFilter filter = new ArrayFilter(null);
CompatibleArgsPredicate pred = new CompatibleArgsPredicate(
args);
filter.appendPredicate(
new MethodsByNamePredicate(name))
.appendPredicate(new InvokableMethodsPredicate())
.appendPredicate(pred);
try {
SymbolType result = MethodInspector.findMethodType(new SymbolType(scope), args, filter, null, typeMapping);
if (result != null){
return result.getMethod();
}
} catch (Exception e) {
throw new NoSuchExpressionTypeException(e);
}
return null;
}
public static SymbolType findMethodType(SymbolType scope,
T[] args, ArrayFilter filter,
CompositeBuilder builder,
Map typeMapping) throws Exception {
SymbolType result = null;
List> bounds = scope.getBoundClasses();
b1.setParameterizedTypes(scope.getParameterizedTypes());
Iterator> it = bounds.iterator();
List> preds = filter.getPredicates();
List> tmp = null;
if (preds != null) {
tmp = new LinkedList>();
for (Predicate pred : preds) {
if (pred instanceof TypeMappingPredicate) {
tmp.add((TypeMappingPredicate) pred);
}
}
}
Class>[] argClasses = SymbolType.toClassArray(args);
while (it.hasNext() && result == null) {
Class> bound = it.next();
if (scope.getArrayCount() != 0) {
bound = Array.newInstance(bound, scope.getArrayCount())
.getClass();
}
b1.setClazz(bound);
Map mapping = b1.build(typeMapping);
if (tmp != null) {
for (TypeMappingPredicate pred : tmp) {
pred.setTypeMapping(mapping);
}
}
result = findMethodType(bound, argClasses, filter, builder,
mapping, false);
if (scope.getArrayCount() != 0 && result != null) {
Method method = result.getMethod();
if (method != null && method.getName().equals("clone")) {
result.setArrayCount(scope.getArrayCount());
}
}
}
if(result != null){
if("getClass".equals(result.getMethod().getName()) && args.length == 0){
List paramTypes = new LinkedList();
paramTypes.add(scope.clone());
result.setParameterizedTypes(paramTypes);
}
}
return result;
}
public static boolean isGeneric(Method m) {
boolean isGeneric = m.getTypeParameters().length > 0
&& !m.getReturnType().equals(void.class);
if (!isGeneric) {
return ClassInspector.isGeneric(m.getGenericReturnType());
}
return isGeneric;
}
public static SymbolType findMethodType(Class> clazz, Class>[] args,
ArrayFilter filter, CompositeBuilder builder,
Map typeMapping, boolean throwException)
throws Exception {
ExecutableSorter sorter = new ExecutableSorter();
List auxList = sorter.sort(clazz.getDeclaredMethods(), args);
Method[] auxArray = new Method[auxList.size()];
auxList.toArray(auxArray);
filter.setElements(auxArray);
Method aux = filter.filterOne();
SymbolType result = null;
if (aux != null) {
if (builder != null) {
builder.build(aux);
}
result = SymbolType.valueOf(aux, typeMapping);
}
if (result == null) {
Class> superClass = clazz.getSuperclass();
if (superClass != null) {
result = findMethodType(superClass, args, filter, builder,
typeMapping, false);
}
if (result == null) {
Type[] types = clazz.getGenericInterfaces();
if (types.length > 0) {
for (int i = 0; i < types.length && result == null; i++) {
Class> type = SymbolType.valueOf(types[i],
typeMapping).getClazz();
result = findMethodType(type, args, filter,
builder, typeMapping, false);
}
}
if (result == null && clazz.isInterface()) {
result = findMethodType(Object.class, args, filter,
builder, typeMapping, false);
}
}
if (result == null) {
if (clazz.isMemberClass()) {
result = findMethodType(clazz.getDeclaringClass(), args,
filter, builder, typeMapping, false);
} else if (clazz.isAnonymousClass()) {
result = findMethodType(clazz.getEnclosingClass(), args,
filter, builder, typeMapping, false);
}
}
}
if (result == null && throwException) {
throw new NoSuchMethodException("The method cannot be found");
}
return result;
}
public static Set getInhertitedDefaultMethods(Class> interf,
Class> clazz) {
Set result = new HashSet();
if (!clazz.isInterface()) {
Method[] declMethods = clazz.getDeclaredMethods();
Set methods = getVisibleDefaultMethods(interf, clazz);
Iterator it = methods.iterator();
while (it.hasNext()) {
Method current = it.next();
boolean found = false;
Class>[] params2 = current.getParameterTypes();
for (int j = 0; j < declMethods.length && !found; j++) {
if (declMethods[j].getName().equals(current.getName())) {
Class>[] params1 = declMethods[j].getParameterTypes();
if (params1.length == params2.length) {
boolean compatible = true;
for (int k = 0; k < params2.length && compatible; k++) {
compatible = params1[k]
.isAssignableFrom(params2[k]);
}
found = compatible;
}
}
}
if (!found) {
result.add(current);
}
}
}
return result;
}
private static Set getVisibleDefaultMethods(Class> clazz,
Class> invocationClass) {
Set result = new HashSet();
HashMap> aux = new HashMap>();
if (clazz == null || clazz.equals(Object.class)) {
return result;
}
Method[] declMethods = clazz.getDeclaredMethods();
for (int i = 0; i < declMethods.length; i++) {
int modifiers = declMethods[i].getModifiers();
boolean isDefault = Modifier.isPublic(modifiers)
&& !Modifier.isStatic(modifiers)
&& !Modifier.isAbstract(modifiers);
boolean isVisible = clazz.getName().equals(
invocationClass.getName());
boolean samePackage = clazz.getPackage() == null
&& invocationClass.getPackage() == null;
samePackage = samePackage
|| (clazz.getPackage() != null
&& invocationClass.getPackage() != null && clazz
.getPackage().getName()
.equals(invocationClass.getPackage().getName()));
isVisible = isVisible || Modifier.isPublic(modifiers)
|| (!Modifier.isPrivate(modifiers) && samePackage);
if (isVisible && isDefault && !declMethods[i].isBridge()
&& !declMethods[i].isSynthetic()) {
result.add(declMethods[i]);
Set auxSet = aux.get(declMethods[i].getName());
if (auxSet == null) {
auxSet = new HashSet();
}
auxSet.add(declMethods[i]);
aux.put(declMethods[i].getName(), auxSet);
}
}
if (clazz.isInterface()) {
Class>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Set auxSet = getVisibleDefaultMethods(interfaces[i],
invocationClass);
result.addAll(auxSet);
}
}
return result;
}
public static Method getLambdaMethod(Class> clazz, int paramsSize) {
if (clazz == null || clazz.equals(Object.class)) {
return null;
}
boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
if (!clazz.isInterface() && !isAbstract) {
// it is an
return null;
}
Method[] declMethods = clazz.getDeclaredMethods();
for (int i = 0; i < declMethods.length; i++) {
int modifiers = declMethods[i].getModifiers();
/*
* If a public, non-abstract, non-static method appears in an
* interface, it must be a default method.
*/
boolean isDefault = Modifier.isPublic(modifiers)
&& !Modifier.isStatic(modifiers)
&& !Modifier.isAbstract(modifiers);
if (!isDefault
&& declMethods[i].getParameterTypes().length == paramsSize) {
return declMethods[i];
}
}
Method result = getLambdaMethod(clazz.getSuperclass(), paramsSize);
if (isAbstract) {
Class>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length && result == null; i++) {
result = getLambdaMethod(interfaces[i], paramsSize);
}
}
return result;
}
public static Set getVisibleMethods(Class> clazz,
Class> invocationClass) {
Set result = new HashSet();
HashMap> aux = new HashMap>();
if (clazz == null || clazz.equals(Object.class)) {
return result;
}
Method[] declMethods = clazz.getDeclaredMethods();
for (int i = 0; i < declMethods.length; i++) {
boolean isVisible = clazz.getName().equals(
invocationClass.getName());
int modifiers = declMethods[i].getModifiers();
boolean samePackage = clazz.getPackage() == null
&& invocationClass.getPackage() == null;
samePackage = samePackage
|| (clazz.getPackage() != null
&& invocationClass.getPackage() != null && clazz
.getPackage().getName()
.equals(invocationClass.getPackage().getName()));
isVisible = isVisible || Modifier.isPublic(modifiers)
|| (!Modifier.isPrivate(modifiers) && samePackage);
if (isVisible && !declMethods[i].isBridge()
&& !declMethods[i].isSynthetic()) {
result.add(declMethods[i]);
Set auxSet = aux.get(declMethods[i].getName());
if (auxSet == null) {
auxSet = new HashSet();
}
auxSet.add(declMethods[i]);
aux.put(declMethods[i].getName(), auxSet);
}
}
if (clazz.isInterface()) {
Class>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Set auxSet = getVisibleMethods(interfaces[i],
invocationClass);
result.addAll(auxSet);
}
} else {
Set superClassMethods = getVisibleMethods(
clazz.getSuperclass(), invocationClass);
for (Method superMethod : superClassMethods) {
Set auxSet = aux.get(superMethod.getName());
boolean found = false;
if (auxSet == null) {
auxSet = new HashSet();
} else {
Class>[] superParams = superMethod.getParameterTypes();
Iterator it = auxSet.iterator();
while (it.hasNext() && !found) {
Method prev = it.next();
Class>[] prevParams = prev.getParameterTypes();
if (prevParams.length == superParams.length) {
if (prevParams.length > 0) {
boolean compatibleArgs = false;
for (int i = 0; i < prevParams.length
&& compatibleArgs; i++) {
compatibleArgs = superParams[i]
.isAssignableFrom(prevParams[i]);
}
found = compatibleArgs;
} else {
found = true;
}
}
}
}
if (!found) {
aux.put(superMethod.getName(), auxSet);
result.add(superMethod);
}
}
}
return result;
}
public static Set getInheritedMethods(Class> clazz) {
Set result = new HashSet();
HashMap> aux = new HashMap>();
if (clazz == null || clazz.equals(Object.class)) {
return result;
}
Method[] declMethods = clazz.getDeclaredMethods();
for (int i = 0; i < declMethods.length; i++) {
if (!Modifier.isPrivate(declMethods[i].getModifiers())
&& !Modifier.isAbstract(declMethods[i].getModifiers())
&& !declMethods[i].isBridge()
&& !declMethods[i].isSynthetic()) {
result.add(declMethods[i]);
Set auxSet = aux.get(declMethods[i].getName());
if (auxSet == null) {
auxSet = new HashSet();
}
auxSet.add(declMethods[i]);
aux.put(declMethods[i].getName(), auxSet);
}
}
Set superClassMethods = getInheritedMethods(clazz
.getSuperclass());
for (Method superMethod : superClassMethods) {
Set auxSet = aux.get(superMethod.getName());
boolean found = false;
if (auxSet == null) {
auxSet = new HashSet();
} else {
Class>[] superParams = superMethod.getParameterTypes();
Iterator it = auxSet.iterator();
while (it.hasNext() && !found) {
Method prev = it.next();
Class>[] prevParams = prev.getParameterTypes();
if (prevParams.length == superParams.length) {
if (prevParams.length > 0) {
boolean compatibleArgs = false;
for (int i = 0; i < prevParams.length
&& compatibleArgs; i++) {
compatibleArgs = superParams[i]
.isAssignableFrom(prevParams[i]);
}
found = compatibleArgs;
} else {
found = true;
}
}
}
}
if (!found) {
aux.put(superMethod.getName(), auxSet);
result.add(superMethod);
}
}
return result;
}
}