package mhfc.net.common.util.reflection; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; public class ClassHelper { // primitive class -> super types private final static Map<Class<?>, Set<Class<?>>> PRIMITIVE_SUPERTYPE_LOOKUP; // primitive class -> boxed private final static Map<Class<?>, Class<?>> BOXED_LOOKUP; // boxed -> primitve class private final static Map<Class<?>, Class<?>> UNBOXED_LOOKUP; private static void ADD_BOXING_RELATION(Class<?> primitive, Class<?> boxed) { BOXED_LOOKUP.put(primitive, boxed); UNBOXED_LOOKUP.put(boxed, primitive); } static { // 4.10.1 PRIMITIVE_SUPERTYPE_LOOKUP = new HashMap<>(); List<Class<?>> tempList = new ArrayList<>(); PRIMITIVE_SUPERTYPE_LOOKUP.put(double.class, new HashSet<>(tempList)); tempList.add(double.class); PRIMITIVE_SUPERTYPE_LOOKUP.put(float.class, new HashSet<>(tempList)); tempList.add(float.class); PRIMITIVE_SUPERTYPE_LOOKUP.put(long.class, new HashSet<>(tempList)); tempList.add(long.class); PRIMITIVE_SUPERTYPE_LOOKUP.put(int.class, new HashSet<>(tempList)); tempList.add(int.class); PRIMITIVE_SUPERTYPE_LOOKUP.put(char.class, new HashSet<>(tempList)); PRIMITIVE_SUPERTYPE_LOOKUP.put(short.class, new HashSet<>(tempList)); tempList.add(short.class); PRIMITIVE_SUPERTYPE_LOOKUP.put(byte.class, new HashSet<>(tempList)); PRIMITIVE_SUPERTYPE_LOOKUP.put(void.class, Collections.emptySet()); BOXED_LOOKUP = new HashMap<>(); UNBOXED_LOOKUP = new HashMap<>(); ADD_BOXING_RELATION(boolean.class, Boolean.class); ADD_BOXING_RELATION(byte.class, Byte.class); ADD_BOXING_RELATION(short.class, Short.class); ADD_BOXING_RELATION(char.class, Character.class); ADD_BOXING_RELATION(int.class, Integer.class); ADD_BOXING_RELATION(long.class, Long.class); ADD_BOXING_RELATION(float.class, Float.class); ADD_BOXING_RELATION(double.class, Double.class); } /** * �4.10, T <: S. Or in laymans terms: the class T is considered to be a subtype of class S, or if T is a primitive * class, widening convertible. Every class is its own subtype. * * @param clazzS * @param clazzT * @return */ public static boolean isSubtype(Class<?> clazzS, Class<?> clazzT) { Objects.requireNonNull(clazzS); Objects.requireNonNull(clazzT); if (isIdentityConvertible(clazzS, clazzT)) { return true; } // 4.10.3 if (clazzT.isArray()) { if (clazzS.isArray()) { return isSubtype(clazzS.getComponentType(), clazzT.getComponentType()); } return isSubtype(Object.class, clazzS) || isSubtype(Cloneable.class, clazzS) || isSubtype(Serializable.class, clazzS); } // 4.10.2 if (!clazzT.isPrimitive()) { return isWideningReferenceConvertible(clazzS, clazzT); } // 4.10.1 return isWideningPrimitiveConvertible(clazzS, clazzT); } /** * �5.1.1 T is the class to convert from, S the class to convert to * * @param clazzS * @param clazzT * @return */ public static boolean isIdentityConvertible(Class<?> clazzS, Class<?> clazzT) { return clazzS.equals(clazzT); } /** * �5.1.2 T is the class to convert from, S the class to convert to * * @param clazzS * @param clazzT * @return */ public static boolean isWideningPrimitiveConvertible(Class<?> clazzS, Class<?> clazzT) { return clazzT.isPrimitive() && PRIMITIVE_SUPERTYPE_LOOKUP.get(clazzT).contains(clazzS); } /** * �5.1.5 T is the class to convert from, S the class to convert to * * @param clazzS * @param clazzT * @return */ public static boolean isWideningReferenceConvertible(Class<?> clazzS, Class<?> clazzT) { return !clazzT.isPrimitive() && clazzS.isAssignableFrom(clazzT); } public static Optional<Class<?>> asBoxed(Class<?> primitive) { return BOXED_LOOKUP.containsKey(primitive) ? Optional.of(BOXED_LOOKUP.get(primitive)) : Optional.empty(); } public static Optional<Class<?>> asUnboxed(Class<?> boxed) { return UNBOXED_LOOKUP.containsKey(boxed) ? Optional.of(UNBOXED_LOOKUP.get(boxed)) : Optional.empty(); } /** * �5.3 T is the class to convert from, S the class to convert to * * @param clazzS * @param clazzT * @return */ public static boolean isMethodInvocationConvertible(Class<?> clazzS, Class<?> clazzT) { if (isIdentityConvertible(clazzS, clazzT) // || isWideningPrimitiveConvertible(clazzS, clazzT) // || isWideningReferenceConvertible(clazzS, clazzT)) { return true; } Optional<Class<?>> boxedT = asBoxed(clazzT); if (boxedT.isPresent()) { return isWideningReferenceConvertible(clazzS, boxedT.get()); } Optional<Class<?>> unboxedT = asUnboxed(clazzT); if (unboxedT.isPresent()) { return isWideningPrimitiveConvertible(clazzS, unboxedT.get()); } return false; } }