From e023e674020f1a435f7b8c8b9276704f576ea6e5 Mon Sep 17 00:00:00 2001 From: CGantert345 <57003061+CGantert345@users.noreply.github.com> Date: Mon, 29 Mar 2021 14:08:45 +0200 Subject: structure change 1 --- .../uic/barcode/asn1/uper/SimpleTypeResolver.java | 515 +++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 src/main/java/org/uic/barcode/asn1/uper/SimpleTypeResolver.java (limited to 'src/main/java/org/uic/barcode/asn1/uper/SimpleTypeResolver.java') diff --git a/src/main/java/org/uic/barcode/asn1/uper/SimpleTypeResolver.java b/src/main/java/org/uic/barcode/asn1/uper/SimpleTypeResolver.java new file mode 100644 index 0000000..9444e44 --- /dev/null +++ b/src/main/java/org/uic/barcode/asn1/uper/SimpleTypeResolver.java @@ -0,0 +1,515 @@ +package org.uic.barcode.asn1.uper; + +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + + +/** + * Enhanced type resolution utilities. + * + * @author Jonathan Halterman + */ +public final class SimpleTypeResolver { + /** Cache of type variable/argument pairs */ + private static final Map, Reference, Type>>> TYPE_VARIABLE_CACHE = Collections + .synchronizedMap(new WeakHashMap, Reference, Type>>>()); + private static volatile boolean CACHE_ENABLED = true; + private static boolean RESOLVES_LAMBDAS; + private static Method GET_CONSTANT_POOL; + private static Method GET_CONSTANT_POOL_SIZE; + private static Method GET_CONSTANT_POOL_METHOD_AT; + private static final Map OBJECT_METHODS = new HashMap(); + private static final Map, Class> PRIMITIVE_WRAPPERS; + private static final Double JAVA_VERSION; + + static { + JAVA_VERSION = Double.parseDouble(System.getProperty("java.specification.version", "0")); + + try { + + GET_CONSTANT_POOL = Class.class.getDeclaredMethod("getConstantPool"); + String constantPoolName = JAVA_VERSION < 9 ? "sun.reflect.ConstantPool" : "jdk.internal.reflect.ConstantPool"; + Class constantPoolClass = Class.forName(constantPoolName); + GET_CONSTANT_POOL_SIZE = constantPoolClass.getDeclaredMethod("getSize"); + GET_CONSTANT_POOL_METHOD_AT = constantPoolClass.getDeclaredMethod("getMethodAt", int.class); + + // setting the methods as accessible + // additional checks - make sure we get a result when invoking the Class::getConstantPool and + // ConstantPool::getSize on a class + Object constantPool = GET_CONSTANT_POOL.invoke(Object.class); + GET_CONSTANT_POOL_SIZE.invoke(constantPool); + + for (Method method : Object.class.getDeclaredMethods()) + OBJECT_METHODS.put(method.getName(), method); + + RESOLVES_LAMBDAS = true; + } catch (Exception ignore) { + } + + Map, Class> types = new HashMap, Class>(); + types.put(boolean.class, Boolean.class); + types.put(byte.class, Byte.class); + types.put(char.class, Character.class); + types.put(double.class, Double.class); + types.put(float.class, Float.class); + types.put(int.class, Integer.class); + types.put(long.class, Long.class); + types.put(short.class, Short.class); + types.put(void.class, Void.class); + PRIMITIVE_WRAPPERS = Collections.unmodifiableMap(types); + } + + /** An unknown type. */ + public static final class Unknown { + private Unknown() { + } + } + + /** + * Enables the internal caching of resolved TypeVariables. + */ + public static void enableCache() { + CACHE_ENABLED = true; + } + + /** + * Disables the internal caching of resolved TypeVariables. + */ + public static void disableCache() { + TYPE_VARIABLE_CACHE.clear(); + CACHE_ENABLED = false; + } + + /** + * Returns the raw class representing the argument for the {@code type} using type variable information from the + * {@code subType}. If no arguments can be resolved then {@code Unknown.class} is returned. + * + * @param type to resolve argument for + * @param subType to extract type variable information from + * @return argument for {@code type} else {@link Unknown}.class if no type arguments are declared + * @throws IllegalArgumentException if more or less than one argument is resolved for the {@code type} + */ + public static Class resolveRawArgument(Class type, Class subType) { + return resolveRawArgument(resolveGenericType(type, subType), subType); + } + + /** + * Returns the raw class representing the argument for the {@code genericType} using type variable information from + * the {@code subType}. If {@code genericType} is an instance of class, then {@code genericType} is returned. If no + * arguments can be resolved then {@code Unknown.class} is returned. + * + * @param genericType to resolve argument for + * @param subType to extract type variable information from + * @return argument for {@code genericType} else {@link Unknown}.class if no type arguments are declared + * @throws IllegalArgumentException if more or less than one argument is resolved for the {@code genericType} + */ + public static Class resolveRawArgument(Type genericType, Class subType) { + Class[] arguments = resolveRawArguments(genericType, subType); + if (arguments == null) + return Unknown.class; + + if (arguments.length != 1) + throw new IllegalArgumentException( + "Expected 1 argument for generic type " + genericType + " but found " + arguments.length); + + return arguments[0]; + } + + /** + * Returns an array of raw classes representing arguments for the {@code type} using type variable information from + * the {@code subType}. Arguments for {@code type} that cannot be resolved are returned as {@code Unknown.class}. If + * no arguments can be resolved then {@code null} is returned. + * + * @param type to resolve arguments for + * @param subType to extract type variable information from + * @return array of raw classes representing arguments for the {@code type} else {@code null} if no type arguments are + * declared + */ + public static Class[] resolveRawArguments(Class type, Class subType) { + return resolveRawArguments(resolveGenericType(type, subType), subType); + } + + /** + * Returns an array of raw classes representing arguments for the {@code genericType} using type variable information + * from the {@code subType}. Arguments for {@code genericType} that cannot be resolved are returned as + * {@code Unknown.class}. If no arguments can be resolved then {@code null} is returned. + * + * @param genericType to resolve arguments for + * @param subType to extract type variable information from + * @return array of raw classes representing arguments for the {@code genericType} else {@code null} if no type + * arguments are declared + */ + public static Class[] resolveRawArguments(Type genericType, Class subType) { + Class[] result = null; + Class functionalInterface = null; + + // Handle lambdas + if (RESOLVES_LAMBDAS && subType.isSynthetic()) { + Class fi = genericType instanceof ParameterizedType + && ((ParameterizedType) genericType).getRawType() instanceof Class + ? (Class) ((ParameterizedType) genericType).getRawType() + : genericType instanceof Class ? (Class) genericType : null; + if (fi != null && fi.isInterface()) + functionalInterface = fi; + } + + if (genericType instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType) genericType; + Type[] arguments = paramType.getActualTypeArguments(); + result = new Class[arguments.length]; + for (int i = 0; i < arguments.length; i++) + result[i] = resolveRawClass(arguments[i], subType, functionalInterface); + } else if (genericType instanceof TypeVariable) { + result = new Class[1]; + result[0] = resolveRawClass(genericType, subType, functionalInterface); + } else if (genericType instanceof Class) { + TypeVariable[] typeParams = ((Class) genericType).getTypeParameters(); + result = new Class[typeParams.length]; + for (int i = 0; i < typeParams.length; i++) + result[i] = resolveRawClass(typeParams[i], subType, functionalInterface); + } + + return result; + } + + /** + * Returns the generic {@code type} using type variable information from the {@code subType} else {@code null} if the + * generic type cannot be resolved. + * + * @param type to resolve generic type for + * @param subType to extract type variable information from + * @return generic {@code type} else {@code null} if it cannot be resolved + */ + public static Type resolveGenericType(Class type, Type subType) { + Class rawType; + if (subType instanceof ParameterizedType) + rawType = (Class) ((ParameterizedType) subType).getRawType(); + else + rawType = (Class) subType; + + if (type.equals(rawType)) + return subType; + + Type result; + if (type.isInterface()) { + for (Type superInterface : rawType.getGenericInterfaces()) + if (superInterface != null && !superInterface.equals(Object.class)) + if ((result = resolveGenericType(type, superInterface)) != null) + return result; + } + + Type superClass = rawType.getGenericSuperclass(); + if (superClass != null && !superClass.equals(Object.class)) + if ((result = resolveGenericType(type, superClass)) != null) + return result; + + return null; + } + + /** + * Resolves the raw class for the {@code genericType}, using the type variable information from the {@code subType} + * else {@link Unknown} if the raw class cannot be resolved. + * + * @param genericType to resolve raw class for + * @param subType to extract type variable information from + * @return raw class for the {@code genericType} else {@link Unknown} if it cannot be resolved + */ + public static Class resolveRawClass(Type genericType, Class subType) { + return resolveRawClass(genericType, subType, null); + } + + private static Class resolveRawClass(Type genericType, Class subType, Class functionalInterface) { + if (genericType instanceof Class) { + return (Class) genericType; + } else if (genericType instanceof ParameterizedType) { + return resolveRawClass(((ParameterizedType) genericType).getRawType(), subType, functionalInterface); + } else if (genericType instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType) genericType; + Class component = resolveRawClass(arrayType.getGenericComponentType(), subType, functionalInterface); + return Array.newInstance(component, 0).getClass(); + } else if (genericType instanceof TypeVariable) { + TypeVariable variable = (TypeVariable) genericType; + genericType = getTypeVariableMap(subType, functionalInterface).get(variable); + genericType = genericType == null ? resolveBound(variable) + : resolveRawClass(genericType, subType, functionalInterface); + } + + return genericType instanceof Class ? (Class) genericType : Unknown.class; + } + + private static Map, Type> getTypeVariableMap(final Class targetType, + Class functionalInterface) { + Reference, Type>> ref = TYPE_VARIABLE_CACHE.get(targetType); + Map, Type> map = ref != null ? ref.get() : null; + + if (map == null) { + map = new HashMap, Type>(); + + // Populate lambdas + if (functionalInterface != null) + populateLambdaArgs(functionalInterface, targetType, map); + + // Populate interfaces + populateSuperTypeArgs(targetType.getGenericInterfaces(), map, functionalInterface != null); + + // Populate super classes and interfaces + Type genericType = targetType.getGenericSuperclass(); + Class type = targetType.getSuperclass(); + while (type != null && !Object.class.equals(type)) { + if (genericType instanceof ParameterizedType) + populateTypeArgs((ParameterizedType) genericType, map, false); + populateSuperTypeArgs(type.getGenericInterfaces(), map, false); + + genericType = type.getGenericSuperclass(); + type = type.getSuperclass(); + } + + // Populate enclosing classes + type = targetType; + while (type.isMemberClass()) { + genericType = type.getGenericSuperclass(); + if (genericType instanceof ParameterizedType) + populateTypeArgs((ParameterizedType) genericType, map, functionalInterface != null); + + type = type.getEnclosingClass(); + } + + if (CACHE_ENABLED) + TYPE_VARIABLE_CACHE.put(targetType, new WeakReference, Type>>(map)); + } + + return map; + } + + /** + * Populates the {@code map} with with variable/argument pairs for the given {@code types}. + */ + private static void populateSuperTypeArgs(final Type[] types, final Map, Type> map, + boolean depthFirst) { + for (Type type : types) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + if (!depthFirst) + populateTypeArgs(parameterizedType, map, depthFirst); + Type rawType = parameterizedType.getRawType(); + if (rawType instanceof Class) + populateSuperTypeArgs(((Class) rawType).getGenericInterfaces(), map, depthFirst); + if (depthFirst) + populateTypeArgs(parameterizedType, map, depthFirst); + } else if (type instanceof Class) { + populateSuperTypeArgs(((Class) type).getGenericInterfaces(), map, depthFirst); + } + } + } + + /** + * Populates the {@code map} with variable/argument pairs for the given {@code type}. + */ + private static void populateTypeArgs(ParameterizedType type, Map, Type> map, boolean depthFirst) { + if (type.getRawType() instanceof Class) { + TypeVariable[] typeVariables = ((Class) type.getRawType()).getTypeParameters(); + Type[] typeArguments = type.getActualTypeArguments(); + + if (type.getOwnerType() != null) { + Type owner = type.getOwnerType(); + if (owner instanceof ParameterizedType) + populateTypeArgs((ParameterizedType) owner, map, depthFirst); + } + + for (int i = 0; i < typeArguments.length; i++) { + TypeVariable variable = typeVariables[i]; + Type typeArgument = typeArguments[i]; + + if (typeArgument instanceof Class) { + map.put(variable, typeArgument); + } else if (typeArgument instanceof GenericArrayType) { + map.put(variable, typeArgument); + } else if (typeArgument instanceof ParameterizedType) { + map.put(variable, typeArgument); + } else if (typeArgument instanceof TypeVariable) { + TypeVariable typeVariableArgument = (TypeVariable) typeArgument; + if (depthFirst) { + Type existingType = map.get(variable); + if (existingType != null) { + map.put(typeVariableArgument, existingType); + continue; + } + } + + Type resolvedType = map.get(typeVariableArgument); + if (resolvedType == null) + resolvedType = resolveBound(typeVariableArgument); + map.put(variable, resolvedType); + } + } + } + } + + /** + * Resolves the first bound for the {@code typeVariable}, returning {@code Unknown.class} if none can be resolved. + */ + public static Type resolveBound(TypeVariable typeVariable) { + Type[] bounds = typeVariable.getBounds(); + if (bounds.length == 0) + return Unknown.class; + + Type bound = bounds[0]; + if (bound instanceof TypeVariable) + bound = resolveBound((TypeVariable) bound); + + return bound == Object.class ? Unknown.class : bound; + } + + /** + * Populates the {@code map} with variable/argument pairs for the {@code functionalInterface}. + */ + private static void populateLambdaArgs(Class functionalInterface, final Class lambdaType, + Map, Type> map) { + if (RESOLVES_LAMBDAS) { + // Find SAM + for (Method m : functionalInterface.getMethods()) { + if (!isDefaultMethod(m) && !Modifier.isStatic(m.getModifiers()) && !m.isBridge()) { + // Skip methods that override Object.class + Method objectMethod = OBJECT_METHODS.get(m.getName()); + if (objectMethod != null && Arrays.equals(m.getTypeParameters(), objectMethod.getTypeParameters())) + continue; + + // Get functional interface's type params + Type returnTypeVar = m.getGenericReturnType(); + Type[] paramTypeVars = m.getGenericParameterTypes(); + + Member member = getMemberRef(lambdaType); + if (member == null) + return; + + // Populate return type argument + if (returnTypeVar instanceof TypeVariable) { + Class returnType = member instanceof Method ? ((Method) member).getReturnType() + : ((Constructor) member).getDeclaringClass(); + returnType = wrapPrimitives(returnType); + if (!returnType.equals(Void.class)) + map.put((TypeVariable) returnTypeVar, returnType); + } + + Class[] arguments = member instanceof Method ? ((Method) member).getParameterTypes() + : ((Constructor) member).getParameterTypes(); + + // Populate object type from arbitrary object method reference + int paramOffset = 0; + if (paramTypeVars.length > 0 && paramTypeVars[0] instanceof TypeVariable + && paramTypeVars.length == arguments.length + 1) { + Class instanceType = member.getDeclaringClass(); + map.put((TypeVariable) paramTypeVars[0], instanceType); + paramOffset = 1; + } + + // Handle additional arguments that are captured from the lambda's enclosing scope + int argOffset = 0; + if (paramTypeVars.length < arguments.length) { + argOffset = arguments.length - paramTypeVars.length; + } + + // Populate type arguments + for (int i = 0; i + argOffset < arguments.length; i++) { + if (paramTypeVars[i] instanceof TypeVariable) + map.put((TypeVariable) paramTypeVars[i + paramOffset], wrapPrimitives(arguments[i + argOffset])); + } + + return; + } + } + } + } + + private static boolean isDefaultMethod(Method m) { + //CG + return false; + //return JAVA_VERSION >= 1.8 && m.isDefault(); + } + + private static Member getMemberRef(Class type) { + Object constantPool; + try { + constantPool = GET_CONSTANT_POOL.invoke(type); + } catch (Exception ignore) { + return null; + } + + Member result = null; + for (int i = getConstantPoolSize(constantPool) - 1; i >= 0; i--) { + Member member = getConstantPoolMethodAt(constantPool, i); + // Skip SerializedLambda constructors and members of the "type" class + if (member == null + || (member instanceof Constructor + && member.getDeclaringClass().getName().equals("java.lang.invoke.SerializedLambda")) + || member.getDeclaringClass().isAssignableFrom(type)) + continue; + + result = member; + + // Return if not valueOf method + if (!(member instanceof Method) || !isAutoBoxingMethod((Method) member)) + break; + } + + return result; + } + + private static boolean isAutoBoxingMethod(Method method) { + Class[] parameters = method.getParameterTypes(); + return method.getName().equals("valueOf") && parameters.length == 1 && parameters[0].isPrimitive() + && wrapPrimitives(parameters[0]).equals(method.getDeclaringClass()); + } + + private static Class wrapPrimitives(Class clazz) { + return clazz.isPrimitive() ? PRIMITIVE_WRAPPERS.get(clazz) : clazz; + } + + private static int getConstantPoolSize(Object constantPool) { + try { + return (Integer) GET_CONSTANT_POOL_SIZE.invoke(constantPool); + } catch (Exception ignore) { + return 0; + } + } + + private static Member getConstantPoolMethodAt(Object constantPool, int i) { + try { + return (Member) GET_CONSTANT_POOL_METHOD_AT.invoke(constantPool, i); + } catch (Exception ignore) { + return null; + } + } +} + -- cgit v1.2.3