
org.walkmod.javalang.compiler.reflection.ClassInspector 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.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.walkmod.javalang.compiler.symbols.SymbolType;
import org.walkmod.javalang.compiler.types.Types;
import org.walkmod.javalang.exceptions.InvalidTypeException;
import static java.util.Collections.singletonList;
public class ClassInspector {
private static final List> LIST_OF_OBJECT_CLASS = singletonList(Object.class);
public static List getInterfaceOrSuperclassImplementations(Type implementation, Class> interf) {
List result = new LinkedList();
Set types = getEquivalentParametrizableClasses(implementation);
if (types != null) {
Iterator it = types.iterator();
boolean found = false;
while (it.hasNext() && !found) {
Type type = it.next();
if (type instanceof Class>) {
Class> clazz = (Class>) type;
if (interf.isAssignableFrom(clazz)) {
result.add(clazz);
// found = interf.isInterface();
}
} else if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
Type rawType = ptype.getRawType();
if (rawType instanceof Class>) {
Class> auxClass = (Class>) rawType;
if (interf.isAssignableFrom(auxClass)) {
result.add(ptype);
found = interf.isInterface();
}
}
}
}
}
return result;
}
public static void updateTypeMappingOfInterfaceSubclass(Class> subclass, Class> interfaceClass,
Map mapping) throws InvalidTypeException {
List types = getInterfaceOrSuperclassImplementations(subclass, interfaceClass);
Iterator it = types.iterator();
while (it.hasNext()) {
Type current = it.next();
if (current instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) current;
Map update = new HashMap();
SymbolType.valueOf(ptype, null, update, mapping);
mapping.putAll(update);
}
}
}
/** @return intersection of raw types of classes and all super classes and interfaces */
public static List extends Class> > intersectRawTypes(List > classes1, List> classes2) {
// most typical case
if (classes1.size() == 1 && classes2.size() == 1) {
return intersectRawTypes(classes1.get(0), classes2.get(0));
} else {
return list(removeSubClasses(intersection(classesAndInterfaces(classes1), classesAndInterfaces(classes2))));
}
}
private static List list(final Collection coll) {
return Collections.unmodifiableList(new ArrayList<>(coll));
}
/** @return intersection of raw types of classes and all super classes and interfaces */
public static List extends Class>> intersectRawTypes(Class> clazz1,
Class> clazz2) {
if (clazz2 == null) {
clazz2 = Object.class;
}
if (clazz1 == null) {
clazz1 = Object.class;
}
if (clazz1.isPrimitive()) {
clazz1 = Types.getWrapperClass(clazz1.getName());
}
if (clazz2.isPrimitive()) {
clazz2 = Types.getWrapperClass(clazz2.getName());
}
if (clazz1.equals(clazz2)) {
return singletonList(clazz1);
}
if (Types.isAssignable(clazz2, clazz1)) {
return singletonList(clazz1);
}
if (Types.isAssignable(clazz1, clazz2)) {
return singletonList(clazz2);
}
final Set> common = commonClasses(clazz1, clazz2);
final List> list = list(removeSubClasses(common));
return list.isEmpty() ? LIST_OF_OBJECT_CLASS : list;
}
private static Set> commonClasses(Class> clazz1, Class> clazz2) {
return intersection(classesAndInterfaces(clazz1), classesAndInterfaces(clazz2));
}
private static Collection> removeSubClasses(Collection> common) {
if (common.size() < 2) {
return common;
}
final Set> reduced = new LinkedHashSet<>(common.size());
for (Class> clazz : common) {
if (!containsSuperClass(common, clazz)) {
reduced.add(clazz);
}
}
return reduced;
}
private static boolean containsSuperClass(Collection> common, Class> clazz) {
for (Class> other : common) {
if (clazz != other && clazz.isAssignableFrom(other)) {
return true;
}
}
return false;
}
private static Set> classesAndInterfaces(Class> clazz) {
Set> result = new LinkedHashSet<>();
addSuperAndInterfaces(result, clazz);
return result;
}
private static Set> classesAndInterfaces(List> classes) {
Set> result = new LinkedHashSet<>();
for (Class> clazz : classes) {
addSuperAndInterfaces(result, clazz);
}
return result;
}
// TODO: breadth first depth first. I guess depth first because of raw type rule selecting first bound.
private static void addSuperAndInterfaces(Set> classes, Class> clazz) {
if (clazz != null) {
classes.add(clazz);
addSuperAndInterfaces(classes, clazz.getSuperclass());
for (Class> aClass : clazz.getInterfaces()) {
addSuperAndInterfaces(classes, aClass);
}
}
}
private static Set intersection(Set set1, Set set2) {
final Set set = new LinkedHashSet<>(set1);
set.retainAll(set2);
return set;
}
/** @deprecated use {@link #intersectRawTypes} */
@Deprecated
public static Class> getTheNearestSuperClass(Class> clazz1, Class> clazz2) {
return intersectRawTypes(clazz1, clazz2).get(0);
}
/** @deprecated use {@link #intersectRawTypes} */
@Deprecated
public static List> getTheNearestSuperClasses(
List> classes, List> otherClasses) {
return new ArrayList<>(intersectRawTypes(classes, otherClasses));
}
public static Class> findClassMember(Package pkg, String name, Class> clazz) {
if (clazz == null || clazz.equals(Object.class)) {
return null;
}
Class> result = validateInnerClasses(pkg, name, clazz);
boolean found = result != null;
if (!found) {
result = findClassMember(pkg, name, clazz.getSuperclass());
if (result == null) {
Class>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length && !found; i++) {
result = findClassMember(pkg, name, interfaces[i]);
found = result != null;
}
}
}
return result;
}
private static Class> validateInnerClasses(Package pkg, String name, Class> clazz) {
if (clazz == null || clazz.equals(Object.class)) {
return null;
}
Class>[] innerClasses = clazz.getDeclaredClasses();
Class> result = null;
boolean found = false;
for (int i = 0; i < innerClasses.length && !found; i++) {
String fullName = innerClasses[i].getName();
String simpleName = innerClasses[i].getSimpleName();
int index = fullName.indexOf('$');
String uniqueName = simpleName;
if (index != -1) {
int index2 = fullName.indexOf('$', index + 1);
if (index2 != -1) {
uniqueName = fullName.substring(index + 1);
uniqueName = uniqueName.replaceAll("\\$", ".");
}
}
int modifiers = innerClasses[i].getModifiers();
boolean validModifiers =
Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || (!Modifier.isPrivate(modifiers)
&& innerClasses[i].getPackage() != null && innerClasses[i].getPackage().equals(pkg));
found = validModifiers && (uniqueName.equals(name) || simpleName.equals(name));
if (found) {
result = innerClasses[i];
}
found = result != null;
}
if (!found) {
for (int i = 0; i < innerClasses.length && !found; i++) {
result = validateInnerClasses(pkg, name, innerClasses[i]);
found = result != null;
}
}
return result;
}
public static Set> getNonPrivateClassMembers(Class> clazz) {
Set> result = new HashSet>();
if (clazz == null || clazz.equals(Object.class)) {
return result;
}
Class>[] declClasses = clazz.getDeclaredClasses();
for (int i = 0; i < declClasses.length; i++) {
if (!Modifier.isPrivate(declClasses[i].getModifiers())) {
result.add(declClasses[i]);
}
}
result.addAll(getNonPrivateClassMembers(clazz.getSuperclass()));
Class>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
result.addAll(getNonPrivateClassMembers(interfaces[i]));
}
return result;
}
public static Set getEquivalentParametrizableClasses(Type clazz) {
Set result = new LinkedHashSet();
Class> classToAnalyze = null;
if (clazz instanceof ParameterizedType) {
result.add(clazz);
ParameterizedType ptype = (ParameterizedType) clazz;
Type rawType = ptype.getRawType();
if (rawType instanceof Class>) {
classToAnalyze = (Class>) rawType;
}
} else if (clazz instanceof Class>) {
Class> type = (Class>) clazz;
if (type.getTypeParameters().length > 0) {
result.add(type);
}
classToAnalyze = type;
}
if (classToAnalyze != null) {
result.addAll(getEquivalentParametrizableClasses(classToAnalyze.getGenericSuperclass()));
Type[] interfaces = classToAnalyze.getGenericInterfaces();
for (int i = 0; i < interfaces.length; i++) {
result.addAll(getEquivalentParametrizableClasses(interfaces[i]));
}
}
return result;
}
public static boolean isGeneric(Type type) {
if (type instanceof Class>) {
Class> clazz = (Class>) type;
Type[] params = clazz.getTypeParameters();
boolean isGeneric = false;
for (int i = 0; i < params.length && !isGeneric; i++) {
isGeneric = isGeneric(params[i]);
}
return isGeneric;
} else if (type instanceof TypeVariable>) {
return true;
} else if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
boolean isGeneric = isGeneric(paramType.getRawType());
if (!isGeneric) {
Type[] typeArgs = paramType.getActualTypeArguments();
for (int i = 0; i < typeArgs.length && !isGeneric; i++) {
isGeneric = isGeneric(typeArgs[i]);
}
}
return isGeneric;
} else if (type instanceof GenericArrayType) {
GenericArrayType arrayType = (GenericArrayType) type;
return isGeneric(arrayType.getGenericComponentType());
} else if (type instanceof WildcardType) {
WildcardType wt = (WildcardType) type;
Type[] bounds = wt.getUpperBounds();
boolean isGeneric = false;
for (int i = 0; i < bounds.length && !isGeneric; i++) {
isGeneric = isGeneric(bounds[i]);
}
if (!isGeneric) {
bounds = wt.getLowerBounds();
for (int i = 0; i < bounds.length && !isGeneric; i++) {
isGeneric = isGeneric(bounds[i]);
}
}
return isGeneric;
}
return false;
}
public static int getClassHierarchyHeight(Class> clazz) {
if (clazz == null || Object.class.equals(clazz)) {
return 0;
} else {
return getClassHierarchyHeight(clazz.getSuperclass()) + 1;
}
}
public static boolean isAssignable(Class> clazz2, Class> clazz1) {
boolean isMethod2First = true;
Integer order2 = Types.basicTypeEvaluationOrder(clazz2);
Integer order1 = Types.basicTypeEvaluationOrder(clazz1);
if (order1 != null && order2 != null) {
return order2 <= order1;
}
boolean isAssignable = Types.isAssignable(clazz2, clazz1);
if (!isAssignable) {
if (Types.isAssignable(clazz1, clazz2)) {
isMethod2First = false;
} else {
int h2 = ClassInspector.getClassHierarchyHeight(clazz2);
int h1 = ClassInspector.getClassHierarchyHeight(clazz1);
isMethod2First = h2 > h1;
}
} else {
isMethod2First = true;
}
return isMethod2First;
}
public static boolean isMoreSpecficFor(Class> clazz2, Class> clazz1, Class> reference) {
boolean isMethod2First = true;
// we compare, in case these are basic types, their evaluation order.
Integer order2 = Types.basicTypeEvaluationOrder(clazz2);
Integer order1 = Types.basicTypeEvaluationOrder(clazz1);
if (order1 != null && order2 != null) {
// if both are primitive or wrapper classes, we check class2 vs
// class1 or if the reference and the class2 and reference are not
// primitive (ex clazz2 = Object, clazz1 = int, ref = Integer)
if ((clazz2.isPrimitive() && clazz1.isPrimitive()) || (!clazz2.isPrimitive() && !clazz1.isPrimitive())
|| reference == null) {
return (order2 <= order1);
}
boolean referenceIsPrimitive = reference != null && reference.isPrimitive();
boolean clazz2First = (referenceIsPrimitive && clazz2.isPrimitive() && !clazz1.isPrimitive())
|| ((!referenceIsPrimitive) && !clazz2.isPrimitive() && clazz1.isPrimitive());
return clazz2First;
}
// we check if clazz2 is subtype of clazz1. Notice that Object is supper
// type of every interface.
boolean isAssignable = Types.isAssignable(clazz2, clazz1);
if (!isAssignable) {
if (Types.isAssignable(clazz1, clazz2)) {
isMethod2First = false;
} else {
int h2 = ClassInspector.getClassHierarchyHeight(clazz2);
int h1 = ClassInspector.getClassHierarchyHeight(clazz1);
if (h1 == h2) {
// our priority are arrays vs non array types and classes vs
// interfaces
isMethod2First = clazz2 != null && clazz1 != null && (clazz2.isArray() && !clazz1.isArray()
|| (!clazz2.isInterface() && clazz1.isInterface()));
if (!isMethod2First) {
// we compare if the class is assignable to reference.
// If so, it has priority
isMethod2First = clazz2 != null && reference != null && !clazz2.isArray()
&& !clazz2.isPrimitive() && clazz2.isAssignableFrom(reference);
}
} else {
isMethod2First = h2 > h1;
}
}
} else {
isMethod2First = true;
}
return isMethod2First;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy