| package com.fasterxml.jackson.databind.type; |
| |
| import java.util.*; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.lang.reflect.*; |
| |
| import com.fasterxml.jackson.core.type.TypeReference; |
| import com.fasterxml.jackson.databind.JavaType; |
| import com.fasterxml.jackson.databind.JsonNode; |
| import com.fasterxml.jackson.databind.util.ArrayBuilders; |
| import com.fasterxml.jackson.databind.util.ClassUtil; |
| import com.fasterxml.jackson.databind.util.LRUMap; |
| |
| /** |
| * Class used for creating concrete {@link JavaType} instances, |
| * given various inputs. |
| *<p> |
| * Instances of this class are accessible using {@link com.fasterxml.jackson.databind.ObjectMapper} |
| * as well as many objects it constructs (like |
| * {@link com.fasterxml.jackson.databind.DeserializationConfig} and |
| * {@link com.fasterxml.jackson.databind.SerializationConfig})), |
| * but usually those objects also |
| * expose convenience methods (<code>constructType</code>). |
| * So, you can do for example: |
| *<pre> |
| * JavaType stringType = mapper.constructType(String.class); |
| *</pre> |
| * However, more advanced methods are only exposed by factory so that you |
| * may need to use: |
| *<pre> |
| * JavaType stringCollection = mapper.getTypeFactory().constructCollectionType(List.class, String.class); |
| *</pre> |
| */ |
| @SuppressWarnings({"rawtypes" }) |
| public class TypeFactory // note: was final in 2.9, removed from 2.10 |
| implements java.io.Serializable |
| { |
| private static final long serialVersionUID = 1L; |
| |
| private final static JavaType[] NO_TYPES = new JavaType[0]; |
| |
| /** |
| * Globally shared singleton. Not accessed directly; non-core |
| * code should use per-ObjectMapper instance (via configuration objects). |
| * Core Jackson code uses {@link #defaultInstance} for accessing it. |
| */ |
| protected final static TypeFactory instance = new TypeFactory(); |
| |
| protected final static TypeBindings EMPTY_BINDINGS = TypeBindings.emptyBindings(); |
| |
| /* |
| /********************************************************** |
| /* Constants for "well-known" classes |
| /********************************************************** |
| */ |
| |
| // // // Let's assume that a small set of core primitive/basic types |
| // // // will not be modified, and can be freely shared to streamline |
| // // // parts of processing |
| |
| private final static Class<?> CLS_STRING = String.class; |
| private final static Class<?> CLS_OBJECT = Object.class; |
| |
| private final static Class<?> CLS_COMPARABLE = Comparable.class; |
| private final static Class<?> CLS_CLASS = Class.class; |
| private final static Class<?> CLS_ENUM = Enum.class; |
| private final static Class<?> CLS_JSON_NODE = JsonNode.class; // since 2.10 |
| |
| private final static Class<?> CLS_BOOL = Boolean.TYPE; |
| private final static Class<?> CLS_INT = Integer.TYPE; |
| private final static Class<?> CLS_LONG = Long.TYPE; |
| |
| /* |
| /********************************************************** |
| /* Cached pre-constructed JavaType instances |
| /********************************************************** |
| */ |
| |
| // note: these are primitive, hence no super types |
| protected final static SimpleType CORE_TYPE_BOOL = new SimpleType(CLS_BOOL); |
| protected final static SimpleType CORE_TYPE_INT = new SimpleType(CLS_INT); |
| protected final static SimpleType CORE_TYPE_LONG = new SimpleType(CLS_LONG); |
| |
| // and as to String... well, for now, ignore its super types |
| protected final static SimpleType CORE_TYPE_STRING = new SimpleType(CLS_STRING); |
| |
| // @since 2.7 |
| protected final static SimpleType CORE_TYPE_OBJECT = new SimpleType(CLS_OBJECT); |
| |
| /** |
| * Cache {@link Comparable} because it is both parameteric (relatively costly to |
| * resolve) and mostly useless (no special handling), better handle directly |
| * |
| * @since 2.7 |
| */ |
| protected final static SimpleType CORE_TYPE_COMPARABLE = new SimpleType(CLS_COMPARABLE); |
| |
| /** |
| * Cache {@link Enum} because it is parametric AND self-referential (costly to |
| * resolve) and useless in itself (no special handling). |
| * |
| * @since 2.7 |
| */ |
| protected final static SimpleType CORE_TYPE_ENUM = new SimpleType(CLS_ENUM); |
| |
| /** |
| * Cache {@link Class} because it is nominally parametric, but has no really |
| * useful information. |
| * |
| * @since 2.7 |
| */ |
| protected final static SimpleType CORE_TYPE_CLASS = new SimpleType(CLS_CLASS); |
| |
| /** |
| * Cache {@link JsonNode} because it is no critical path of simple tree model |
| * reading and does not have things to override |
| * |
| * @since 2.10 |
| */ |
| protected final static SimpleType CORE_TYPE_JSON_NODE = new SimpleType(CLS_JSON_NODE); |
| |
| /** |
| * Since type resolution can be expensive (specifically when resolving |
| * actual generic types), we will use small cache to avoid repetitive |
| * resolution of core types |
| */ |
| protected final LRUMap<Object,JavaType> _typeCache; |
| |
| /* |
| /********************************************************** |
| /* Configuration |
| /********************************************************** |
| */ |
| |
| /** |
| * Registered {@link TypeModifier}s: objects that can change details |
| * of {@link JavaType} instances factory constructs. |
| */ |
| protected final TypeModifier[] _modifiers; |
| |
| protected final TypeParser _parser; |
| |
| /** |
| * ClassLoader used by this factory [databind#624]. |
| */ |
| protected final ClassLoader _classLoader; |
| |
| /* |
| /********************************************************** |
| /* Life-cycle |
| /********************************************************** |
| */ |
| |
| private TypeFactory() { |
| this(null); |
| } |
| |
| /** |
| * @since 2.8 |
| */ |
| protected TypeFactory(LRUMap<Object,JavaType> typeCache) { |
| if (typeCache == null) { |
| typeCache = new LRUMap<Object,JavaType>(16, 200); |
| } |
| _typeCache = typeCache; |
| _parser = new TypeParser(this); |
| _modifiers = null; |
| _classLoader = null; |
| } |
| |
| protected TypeFactory(LRUMap<Object,JavaType> typeCache, TypeParser p, |
| TypeModifier[] mods, ClassLoader classLoader) |
| { |
| if (typeCache == null) { |
| typeCache = new LRUMap<Object,JavaType>(16, 200); |
| } |
| _typeCache = typeCache; |
| // As per [databind#894] must ensure we have back-linkage from TypeFactory: |
| _parser = p.withFactory(this); |
| _modifiers = mods; |
| _classLoader = classLoader; |
| } |
| |
| /** |
| * "Mutant factory" method which will construct a new instance with specified |
| * {@link TypeModifier} added as the first modifier to call (in case there |
| * are multiple registered). |
| */ |
| public TypeFactory withModifier(TypeModifier mod) |
| { |
| LRUMap<Object,JavaType> typeCache = _typeCache; |
| TypeModifier[] mods; |
| if (mod == null) { // mostly for unit tests |
| mods = null; |
| // 30-Jun-2016, tatu: for some reason expected semantics are to clear cache |
| // in this case; can't recall why, but keeping the same |
| typeCache = null; |
| } else if (_modifiers == null) { |
| mods = new TypeModifier[] { mod }; |
| // 29-Jul-2019, tatu: Actually I think we better clear cache in this case |
| // as well to ensure no leakage occurs (see [databind#2395]) |
| typeCache = null; |
| } else { |
| // but may keep existing cache otherwise |
| mods = ArrayBuilders.insertInListNoDup(_modifiers, mod); |
| } |
| return new TypeFactory(typeCache, _parser, mods, _classLoader); |
| } |
| |
| /** |
| * "Mutant factory" method which will construct a new instance with specified |
| * {@link ClassLoader} to use by {@link #findClass}. |
| */ |
| public TypeFactory withClassLoader(ClassLoader classLoader) { |
| return new TypeFactory(_typeCache, _parser, _modifiers, classLoader); |
| } |
| |
| /** |
| * Mutant factory method that will construct new {@link TypeFactory} with |
| * identical settings except for different cache; most likely one with |
| * bigger maximum size. |
| * |
| * @since 2.8 |
| */ |
| public TypeFactory withCache(LRUMap<Object,JavaType> cache) { |
| return new TypeFactory(cache, _parser, _modifiers, _classLoader); |
| } |
| |
| /** |
| * Method used to access the globally shared instance, which has |
| * no custom configuration. Used by <code>ObjectMapper</code> to |
| * get the default factory when constructed. |
| */ |
| public static TypeFactory defaultInstance() { return instance; } |
| |
| /** |
| * Method that will clear up any cached type definitions that may |
| * be cached by this {@link TypeFactory} instance. |
| * This method should not be commonly used, that is, only use it |
| * if you know there is a problem with retention of type definitions; |
| * the most likely (and currently only known) problem is retention |
| * of {@link Class} instances via {@link JavaType} reference. |
| * |
| * @since 2.4.1 |
| */ |
| public void clearCache() { |
| _typeCache.clear(); |
| } |
| |
| public ClassLoader getClassLoader() { |
| return _classLoader; |
| } |
| |
| /* |
| /********************************************************** |
| /* Static methods for non-instance-specific functionality |
| /********************************************************** |
| */ |
| |
| /** |
| * Method for constructing a marker type that indicates missing generic |
| * type information, which is handled same as simple type for |
| * <code>java.lang.Object</code>. |
| */ |
| public static JavaType unknownType() { |
| return defaultInstance()._unknownType(); |
| } |
| |
| /** |
| * Static helper method that can be called to figure out type-erased |
| * call for given JDK type. It can be called statically since type resolution |
| * process can never change actual type-erased class; thereby static |
| * default instance is used for determination. |
| */ |
| public static Class<?> rawClass(Type t) { |
| if (t instanceof Class<?>) { |
| return (Class<?>) t; |
| } |
| // Should be able to optimize bit more in future... |
| return defaultInstance().constructType(t).getRawClass(); |
| } |
| |
| /* |
| /********************************************************** |
| /* Low-level helper methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Low-level lookup method moved from {@link com.fasterxml.jackson.databind.util.ClassUtil}, |
| * to allow for overriding of lookup functionality in environments like OSGi. |
| * |
| * @since 2.6 |
| */ |
| public Class<?> findClass(String className) throws ClassNotFoundException |
| { |
| if (className.indexOf('.') < 0) { |
| Class<?> prim = _findPrimitive(className); |
| if (prim != null) { |
| return prim; |
| } |
| } |
| // Two-phase lookup: first using context ClassLoader; then default |
| Throwable prob = null; |
| ClassLoader loader = this.getClassLoader(); |
| if (loader == null) { |
| loader = Thread.currentThread().getContextClassLoader(); |
| } |
| if (loader != null) { |
| try { |
| return classForName(className, true, loader); |
| } catch (Exception e) { |
| prob = ClassUtil.getRootCause(e); |
| } |
| } |
| try { |
| return classForName(className); |
| } catch (Exception e) { |
| if (prob == null) { |
| prob = ClassUtil.getRootCause(e); |
| } |
| } |
| ClassUtil.throwIfRTE(prob); |
| throw new ClassNotFoundException(prob.getMessage(), prob); |
| } |
| |
| protected Class<?> classForName(String name, boolean initialize, |
| ClassLoader loader) throws ClassNotFoundException { |
| return Class.forName(name, true, loader); |
| } |
| |
| protected Class<?> classForName(String name) throws ClassNotFoundException { |
| return Class.forName(name); |
| } |
| |
| protected Class<?> _findPrimitive(String className) |
| { |
| if ("int".equals(className)) return Integer.TYPE; |
| if ("long".equals(className)) return Long.TYPE; |
| if ("float".equals(className)) return Float.TYPE; |
| if ("double".equals(className)) return Double.TYPE; |
| if ("boolean".equals(className)) return Boolean.TYPE; |
| if ("byte".equals(className)) return Byte.TYPE; |
| if ("char".equals(className)) return Character.TYPE; |
| if ("short".equals(className)) return Short.TYPE; |
| if ("void".equals(className)) return Void.TYPE; |
| return null; |
| } |
| |
| /* |
| /********************************************************** |
| /* Type conversion, parameterization resolution methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Factory method for creating a subtype of given base type, as defined |
| * by specified subclass; but retaining generic type information if any. |
| * Can be used, for example, to get equivalent of "HashMap<String,Integer>" |
| * from "Map<String,Integer>" by giving <code>HashMap.class</code> |
| * as subclass. |
| */ |
| public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass) |
| { |
| // simple optimization to avoid costly introspection if type-erased type does NOT differ |
| final Class<?> rawBase = baseType.getRawClass(); |
| if (rawBase == subclass) { |
| return baseType; |
| } |
| JavaType newType; |
| |
| // also: if we start from untyped, not much to save |
| do { // bogus loop to be able to break |
| if (rawBase == Object.class) { |
| newType = _fromClass(null, subclass, EMPTY_BINDINGS); |
| break; |
| } |
| if (!rawBase.isAssignableFrom(subclass)) { |
| throw new IllegalArgumentException(String.format( |
| "Class %s not subtype of %s", subclass.getName(), baseType)); |
| } |
| // A few special cases where we can simplify handling: |
| |
| // (1) A small set of "well-known" List/Map subtypes where can take a short-cut |
| if (baseType.isContainerType()) { |
| if (baseType.isMapLikeType()) { |
| if ((subclass == HashMap.class) |
| || (subclass == LinkedHashMap.class) |
| || (subclass == EnumMap.class) |
| || (subclass == TreeMap.class)) { |
| newType = _fromClass(null, subclass, |
| TypeBindings.create(subclass, baseType.getKeyType(), baseType.getContentType())); |
| break; |
| } |
| } else if (baseType.isCollectionLikeType()) { |
| if ((subclass == ArrayList.class) |
| || (subclass == LinkedList.class) |
| || (subclass == HashSet.class) |
| || (subclass == TreeSet.class)) { |
| newType = _fromClass(null, subclass, |
| TypeBindings.create(subclass, baseType.getContentType())); |
| break; |
| } |
| // 29-Oct-2015, tatu: One further shortcut: there are variants of `EnumSet`, |
| // but they are impl details and we basically do not care... |
| if (rawBase == EnumSet.class) { |
| return baseType; |
| } |
| } |
| } |
| // (2) Original target type has no generics -- just resolve subtype |
| if (baseType.getBindings().isEmpty()) { |
| newType = _fromClass(null, subclass, EMPTY_BINDINGS); |
| break; |
| } |
| |
| // (3) Sub-class does not take type parameters -- just resolve subtype |
| int typeParamCount = subclass.getTypeParameters().length; |
| if (typeParamCount == 0) { |
| newType = _fromClass(null, subclass, EMPTY_BINDINGS); |
| break; |
| } |
| // (4) If all else fails, do the full traversal using placeholders |
| TypeBindings tb = _bindingsForSubtype(baseType, typeParamCount, subclass); |
| newType = _fromClass(null, subclass, tb); |
| |
| } while (false); |
| |
| // 25-Sep-2016, tatu: As per [databind#1384] also need to ensure handlers get |
| // copied as well |
| newType = newType.withHandlersFrom(baseType); |
| return newType; |
| } |
| |
| private TypeBindings _bindingsForSubtype(JavaType baseType, int typeParamCount, Class<?> subclass) |
| { |
| PlaceholderForType[] placeholders = new PlaceholderForType[typeParamCount]; |
| for (int i = 0; i < typeParamCount; ++i) { |
| placeholders[i] = new PlaceholderForType(i); |
| } |
| TypeBindings b = TypeBindings.create(subclass, placeholders); |
| // First: pseudo-resolve to get placeholders in place: |
| JavaType tmpSub = _fromClass(null, subclass, b); |
| // Then find super-type |
| JavaType baseWithPlaceholders = tmpSub.findSuperType(baseType.getRawClass()); |
| if (baseWithPlaceholders == null) { // should be found but... |
| throw new IllegalArgumentException(String.format( |
| "Internal error: unable to locate supertype (%s) from resolved subtype %s", baseType.getRawClass().getName(), |
| subclass.getName())); |
| } |
| // and traverse type hierarchies to both verify and to resolve placeholders |
| String error = _resolveTypePlaceholders(baseType, baseWithPlaceholders); |
| if (error != null) { |
| throw new IllegalArgumentException("Failed to specialize base type "+baseType.toCanonical()+" as " |
| +subclass.getName()+", problem: "+error); |
| } |
| |
| final JavaType[] typeParams = new JavaType[typeParamCount]; |
| for (int i = 0; i < typeParamCount; ++i) { |
| JavaType t = placeholders[i].actualType(); |
| // 18-Oct-2017, tatu: Looks like sometimes we have incomplete bindings (even if not |
| // common, it is possible if subtype is type-erased class with added type |
| // variable -- see test(s) with "bogus" type(s)). |
| if (t == null) { |
| t = unknownType(); |
| } |
| typeParams[i] = t; |
| } |
| return TypeBindings.create(subclass, typeParams); |
| } |
| |
| private String _resolveTypePlaceholders(JavaType sourceType, JavaType actualType) |
| throws IllegalArgumentException |
| { |
| List<JavaType> expectedTypes = sourceType.getBindings().getTypeParameters(); |
| List<JavaType> actualTypes = actualType.getBindings().getTypeParameters(); |
| for (int i = 0, len = expectedTypes.size(); i < len; ++i) { |
| JavaType exp = expectedTypes.get(i); |
| JavaType act = actualTypes.get(i); |
| |
| if (!_verifyAndResolvePlaceholders(exp, act)) { |
| // 14-May-2018, tatu: As per [databind#2034] it seems we better relax assignment |
| // rules further -- at least likely "raw" (untyped, non-generic) base should probably |
| // allow specialization. |
| if (exp.hasRawClass(Object.class)) { |
| continue; |
| } |
| // 19-Apr-2018, tatu: Hack for [databind#1964] -- allow type demotion |
| // for `java.util.Map` key type if (and only if) target type is |
| // `java.lang.Object` |
| // 19-Aug-2019, tatu: Further, allow for all Map-like types, with assumption |
| // first argument would be key; initially just because Scala Maps have |
| // some issues (see [databind#2422]) |
| if (i == 0) { |
| if (sourceType.isMapLikeType() |
| && act.hasRawClass(Object.class)) { |
| continue; |
| } |
| } |
| // 19-Nov-2018, tatu: To solve [databind#2155], let's allow type-compatible |
| // assignment for interfaces at least... |
| if (exp.isInterface()) { |
| if (exp.isTypeOrSuperTypeOf(act.getRawClass())) { |
| continue; |
| } |
| } |
| return String.format("Type parameter #%d/%d differs; can not specialize %s with %s", |
| (i+1), len, exp.toCanonical(), act.toCanonical()); |
| } |
| } |
| return null; |
| } |
| |
| private boolean _verifyAndResolvePlaceholders(JavaType exp, JavaType act) |
| { |
| // See if we have an actual type placeholder to resolve; if yes, replace |
| if (act instanceof PlaceholderForType) { |
| ((PlaceholderForType) act).actualType(exp); |
| return true; |
| } |
| // if not, try to verify compatibility. But note that we can not |
| // use simple equality as we need to resolve recursively |
| if (exp.getRawClass() != act.getRawClass()) { |
| return false; |
| } |
| // But we can check type parameters "blindly" |
| List<JavaType> expectedTypes = exp.getBindings().getTypeParameters(); |
| List<JavaType> actualTypes = act.getBindings().getTypeParameters(); |
| for (int i = 0, len = expectedTypes.size(); i < len; ++i) { |
| JavaType exp2 = expectedTypes.get(i); |
| JavaType act2 = actualTypes.get(i); |
| if (!_verifyAndResolvePlaceholders(exp2, act2)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Method similar to {@link #constructSpecializedType}, but that creates a |
| * less-specific type of given type. Usually this is as simple as simply |
| * finding super-type with type erasure of <code>superClass</code>, but |
| * there may be need for some additional work-arounds. |
| * |
| * @param superClass |
| * |
| * @since 2.7 |
| */ |
| public JavaType constructGeneralizedType(JavaType baseType, Class<?> superClass) |
| { |
| // simple optimization to avoid costly introspection if type-erased type does NOT differ |
| final Class<?> rawBase = baseType.getRawClass(); |
| if (rawBase == superClass) { |
| return baseType; |
| } |
| JavaType superType = baseType.findSuperType(superClass); |
| if (superType == null) { |
| // Most likely, caller did not verify sub/super-type relationship |
| if (!superClass.isAssignableFrom(rawBase)) { |
| throw new IllegalArgumentException(String.format( |
| "Class %s not a super-type of %s", superClass.getName(), baseType)); |
| } |
| // 01-Nov-2015, tatu: Should never happen, but ch |
| throw new IllegalArgumentException(String.format( |
| "Internal error: class %s not included as super-type for %s", |
| superClass.getName(), baseType)); |
| } |
| return superType; |
| } |
| |
| /** |
| * Factory method for constructing a {@link JavaType} out of its canonical |
| * representation (see {@link JavaType#toCanonical()}). |
| * |
| * @param canonical Canonical string representation of a type |
| * |
| * @throws IllegalArgumentException If canonical representation is malformed, |
| * or class that type represents (including its generic parameters) is |
| * not found |
| */ |
| public JavaType constructFromCanonical(String canonical) throws IllegalArgumentException |
| { |
| return _parser.parse(canonical); |
| } |
| |
| /** |
| * Method that is to figure out actual type parameters that given |
| * class binds to generic types defined by given (generic) |
| * interface or class. |
| * This could mean, for example, trying to figure out |
| * key and value types for Map implementations. |
| * |
| * @param type Sub-type (leaf type) that implements <code>expType</code> |
| */ |
| public JavaType[] findTypeParameters(JavaType type, Class<?> expType) |
| { |
| JavaType match = type.findSuperType(expType); |
| if (match == null) { |
| return NO_TYPES; |
| } |
| return match.getBindings().typeParameterArray(); |
| } |
| |
| /** |
| * @deprecated Since 2.7 resolve raw type first, then find type parameters |
| */ |
| @Deprecated // since 2.7 |
| public JavaType[] findTypeParameters(Class<?> clz, Class<?> expType, TypeBindings bindings) { |
| return findTypeParameters(constructType(clz, bindings), expType); |
| } |
| |
| /** |
| * @deprecated Since 2.7 resolve raw type first, then find type parameters |
| */ |
| @Deprecated // since 2.7 |
| public JavaType[] findTypeParameters(Class<?> clz, Class<?> expType) { |
| return findTypeParameters(constructType(clz), expType); |
| } |
| |
| /** |
| * Method that can be called to figure out more specific of two |
| * types (if they are related; that is, one implements or extends the |
| * other); or if not related, return the primary type. |
| * |
| * @param type1 Primary type to consider |
| * @param type2 Secondary type to consider |
| * |
| * @since 2.2 |
| */ |
| public JavaType moreSpecificType(JavaType type1, JavaType type2) |
| { |
| if (type1 == null) { |
| return type2; |
| } |
| if (type2 == null) { |
| return type1; |
| } |
| Class<?> raw1 = type1.getRawClass(); |
| Class<?> raw2 = type2.getRawClass(); |
| if (raw1 == raw2) { |
| return type1; |
| } |
| // TODO: maybe try sub-classing, to retain generic types? |
| if (raw1.isAssignableFrom(raw2)) { |
| return type2; |
| } |
| return type1; |
| } |
| |
| /* |
| /********************************************************** |
| /* Public factory methods |
| /********************************************************** |
| */ |
| |
| public JavaType constructType(Type type) { |
| return _fromAny(null, type, EMPTY_BINDINGS); |
| } |
| |
| public JavaType constructType(Type type, TypeBindings bindings) { |
| return _fromAny(null, type, bindings); |
| } |
| |
| public JavaType constructType(TypeReference<?> typeRef) |
| { |
| // 19-Oct-2015, tatu: Simpler variant like so should work |
| return _fromAny(null, typeRef.getType(), EMPTY_BINDINGS); |
| |
| // but if not, due to funky sub-classing, type variables, what follows |
| // is a more complete processing a la Java ClassMate. |
| |
| /* |
| final Class<?> refdRawType = typeRef.getClass(); |
| JavaType type = _fromClass(null, refdRawType, EMPTY_BINDINGS); |
| JavaType genType = type.findSuperType(TypeReference.class); |
| if (genType == null) { // sanity check; shouldn't occur |
| throw new IllegalArgumentException("Unparameterized GenericType instance ("+refdRawType.getName()+")"); |
| } |
| TypeBindings b = genType.getBindings(); |
| JavaType[] params = b.typeParameterArray(); |
| if (params.length == 0) { |
| throw new IllegalArgumentException("Unparameterized GenericType instance ("+refdRawType.getName()+")"); |
| } |
| return params[0]; |
| */ |
| } |
| |
| /** |
| * @deprecated Since 2.7 (accidentally removed in 2.7.0; added back in 2.7.1) |
| */ |
| @Deprecated |
| public JavaType constructType(Type type, Class<?> contextClass) { |
| JavaType contextType = (contextClass == null) ? null : constructType(contextClass); |
| return constructType(type, contextType); |
| } |
| |
| /** |
| * @deprecated Since 2.7 (accidentally removed in 2.7.0; added back in 2.7.1) |
| */ |
| @Deprecated |
| public JavaType constructType(Type type, JavaType contextType) { |
| TypeBindings bindings; |
| if (contextType == null) { |
| bindings = EMPTY_BINDINGS; |
| } else { |
| bindings = contextType.getBindings(); |
| // 16-Nov-2016, tatu: Unfortunately as per [databind#1456] this can't |
| // be made to work for some cases used to work (even if accidentally); |
| // however, we can try a simple heuristic to increase chances of |
| // compatibility from 2.6 code |
| if (type.getClass() != Class.class) { |
| // Ok: so, ideally we would test super-interfaces if necessary; |
| // but let's assume most if not all cases are for classes. |
| while (bindings.isEmpty()) { |
| contextType = contextType.getSuperClass(); |
| if (contextType == null) { |
| break; |
| } |
| bindings = contextType.getBindings(); |
| } |
| } |
| } |
| return _fromAny(null, type, bindings); |
| } |
| |
| /* |
| /********************************************************** |
| /* Direct factory methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Method for constructing an {@link ArrayType}. |
| *<p> |
| * NOTE: type modifiers are NOT called on array type itself; but are called |
| * for element type (and other contained types) |
| */ |
| public ArrayType constructArrayType(Class<?> elementType) { |
| return ArrayType.construct(_fromAny(null, elementType, null), null); |
| } |
| |
| /** |
| * Method for constructing an {@link ArrayType}. |
| *<p> |
| * NOTE: type modifiers are NOT called on array type itself; but are called |
| * for contained types. |
| */ |
| public ArrayType constructArrayType(JavaType elementType) { |
| return ArrayType.construct(elementType, null); |
| } |
| |
| /** |
| * Method for constructing a {@link CollectionType}. |
| *<p> |
| * NOTE: type modifiers are NOT called on Collection type itself; but are called |
| * for contained types. |
| */ |
| public CollectionType constructCollectionType(Class<? extends Collection> collectionClass, |
| Class<?> elementClass) { |
| return constructCollectionType(collectionClass, |
| _fromClass(null, elementClass, EMPTY_BINDINGS)); |
| } |
| |
| /** |
| * Method for constructing a {@link CollectionType}. |
| *<p> |
| * NOTE: type modifiers are NOT called on Collection type itself; but are called |
| * for contained types. |
| */ |
| public CollectionType constructCollectionType(Class<? extends Collection> collectionClass, |
| JavaType elementType) |
| { |
| TypeBindings bindings = TypeBindings.createIfNeeded(collectionClass, elementType); |
| CollectionType result = (CollectionType) _fromClass(null, collectionClass, bindings); |
| // 17-May-2017, tatu: As per [databind#1415], we better verify bound values if (but only if) |
| // type being resolved was non-generic (i.e.element type was ignored) |
| if (bindings.isEmpty() && (elementType != null)) { |
| JavaType t = result.findSuperType(Collection.class); |
| JavaType realET = t.getContentType(); |
| if (!realET.equals(elementType)) { |
| throw new IllegalArgumentException(String.format( |
| "Non-generic Collection class %s did not resolve to something with element type %s but %s ", |
| ClassUtil.nameOf(collectionClass), elementType, realET)); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Method for constructing a {@link CollectionLikeType}. |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type itself; but are called |
| * for contained types. |
| */ |
| public CollectionLikeType constructCollectionLikeType(Class<?> collectionClass, Class<?> elementClass) { |
| return constructCollectionLikeType(collectionClass, |
| _fromClass(null, elementClass, EMPTY_BINDINGS)); |
| } |
| |
| /** |
| * Method for constructing a {@link CollectionLikeType}. |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type itself; but are called |
| * for contained types. |
| */ |
| public CollectionLikeType constructCollectionLikeType(Class<?> collectionClass, JavaType elementType) { |
| JavaType type = _fromClass(null, collectionClass, |
| TypeBindings.createIfNeeded(collectionClass, elementType)); |
| if (type instanceof CollectionLikeType) { |
| return (CollectionLikeType) type; |
| } |
| return CollectionLikeType.upgradeFrom(type, elementType); |
| } |
| |
| /** |
| * Method for constructing a {@link MapType} instance |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type itself; but are called |
| * for contained types. |
| */ |
| public MapType constructMapType(Class<? extends Map> mapClass, |
| Class<?> keyClass, Class<?> valueClass) { |
| JavaType kt, vt; |
| if (mapClass == Properties.class) { |
| kt = vt = CORE_TYPE_STRING; |
| } else { |
| kt = _fromClass(null, keyClass, EMPTY_BINDINGS); |
| vt = _fromClass(null, valueClass, EMPTY_BINDINGS); |
| } |
| return constructMapType(mapClass, kt, vt); |
| } |
| |
| /** |
| * Method for constructing a {@link MapType} instance |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type itself; but are called |
| * for contained types. |
| */ |
| public MapType constructMapType(Class<? extends Map> mapClass, JavaType keyType, JavaType valueType) { |
| TypeBindings bindings = TypeBindings.createIfNeeded(mapClass, new JavaType[] { keyType, valueType }); |
| MapType result = (MapType) _fromClass(null, mapClass, bindings); |
| // 17-May-2017, tatu: As per [databind#1415], we better verify bound values if (but only if) |
| // type being resolved was non-generic (i.e.element type was ignored) |
| if (bindings.isEmpty()) { |
| JavaType t = result.findSuperType(Map.class); |
| JavaType realKT = t.getKeyType(); |
| if (!realKT.equals(keyType)) { |
| throw new IllegalArgumentException(String.format( |
| "Non-generic Map class %s did not resolve to something with key type %s but %s ", |
| ClassUtil.nameOf(mapClass), keyType, realKT)); |
| } |
| JavaType realVT = t.getContentType(); |
| if (!realVT.equals(valueType)) { |
| throw new IllegalArgumentException(String.format( |
| "Non-generic Map class %s did not resolve to something with value type %s but %s ", |
| ClassUtil.nameOf(mapClass), valueType, realVT)); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Method for constructing a {@link MapLikeType} instance |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type itself; but are called |
| * for contained types. |
| */ |
| public MapLikeType constructMapLikeType(Class<?> mapClass, Class<?> keyClass, Class<?> valueClass) { |
| return constructMapLikeType(mapClass, |
| _fromClass(null, keyClass, EMPTY_BINDINGS), |
| _fromClass(null, valueClass, EMPTY_BINDINGS)); |
| } |
| |
| /** |
| * Method for constructing a {@link MapLikeType} instance |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type itself; but are called |
| * for contained types. |
| */ |
| public MapLikeType constructMapLikeType(Class<?> mapClass, JavaType keyType, JavaType valueType) { |
| // 19-Oct-2015, tatu: Allow case of no-type-variables, since it seems likely to be |
| // a valid use case here |
| JavaType type = _fromClass(null, mapClass, |
| TypeBindings.createIfNeeded(mapClass, new JavaType[] { keyType, valueType })); |
| if (type instanceof MapLikeType) { |
| return (MapLikeType) type; |
| } |
| return MapLikeType.upgradeFrom(type, keyType, valueType); |
| } |
| |
| /** |
| * Method for constructing a type instance with specified parameterization. |
| *<p> |
| * NOTE: was briefly deprecated for 2.6. |
| */ |
| public JavaType constructSimpleType(Class<?> rawType, JavaType[] parameterTypes) { |
| return _fromClass(null, rawType, TypeBindings.create(rawType, parameterTypes)); |
| } |
| |
| /** |
| * Method for constructing a type instance with specified parameterization. |
| * |
| * @since 2.6 |
| * |
| * @deprecated Since 2.7 |
| */ |
| @Deprecated |
| public JavaType constructSimpleType(Class<?> rawType, Class<?> parameterTarget, |
| JavaType[] parameterTypes) |
| { |
| return constructSimpleType(rawType, parameterTypes); |
| } |
| |
| /** |
| * @since 2.6 |
| */ |
| public JavaType constructReferenceType(Class<?> rawType, JavaType referredType) |
| { |
| return ReferenceType.construct(rawType, null, // no bindings |
| null, null, // or super-class, interfaces? |
| referredType); |
| } |
| |
| /** |
| * Method that use by core Databind functionality, and that should NOT be called |
| * by application code outside databind package. |
| *<p> |
| * Unchecked here not only means that no checks are made as to whether given class |
| * might be non-simple type (like {@link CollectionType}) but also that most of supertype |
| * information is not gathered. This means that unless called on primitive types or |
| * {@link java.lang.String}, results are probably not what you want to use. |
| * |
| * @deprecated Since 2.8, to indicate users should never call this method. |
| */ |
| @Deprecated // since 2.8 |
| public JavaType uncheckedSimpleType(Class<?> cls) { |
| // 18-Oct-2015, tatu: Not sure how much problem missing super-type info is here |
| return _constructSimple(cls, EMPTY_BINDINGS, null, null); |
| } |
| |
| /** |
| * Factory method for constructing {@link JavaType} that |
| * represents a parameterized type. For example, to represent |
| * type {@code List<Set<Integer>>}, you could |
| * call |
| *<pre> |
| * JavaType inner = TypeFactory.constructParametricType(Set.class, Set.class, Integer.class); |
| * return TypeFactory.constructParametricType(ArrayList.class, List.class, inner); |
| *</pre> |
| *<p> |
| * The reason for first two arguments to be separate is that parameterization may |
| * apply to a super-type. For example, if generic type was instead to be |
| * constructed for {@code ArrayList<Integer>}, the usual call would be: |
| *<pre> |
| * TypeFactory.constructParametricType(ArrayList.class, List.class, Integer.class); |
| *</pre> |
| * since parameterization is applied to {@link java.util.List}. |
| * In most cases distinction does not matter, but there are types where it does; |
| * one such example is parameterization of types that implement {@link java.util.Iterator}. |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type. |
| * |
| * @param parametrized Actual full type |
| * @param parameterClasses Type parameters to apply |
| * |
| * @since 2.5 NOTE: was briefly deprecated for 2.6 |
| */ |
| public JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) { |
| int len = parameterClasses.length; |
| JavaType[] pt = new JavaType[len]; |
| for (int i = 0; i < len; ++i) { |
| pt[i] = _fromClass(null, parameterClasses[i], EMPTY_BINDINGS); |
| } |
| return constructParametricType(parametrized, pt); |
| } |
| |
| /** |
| * Factory method for constructing {@link JavaType} that |
| * represents a parameterized type. For example, to represent |
| * type {@code List<Set<Integer>>}, you could call |
| *<pre> |
| * JavaType inner = TypeFactory.constructParametricType(Set.class, Set.class, Integer.class); |
| * return TypeFactory.constructParametricType(ArrayList.class, List.class, inner); |
| *</pre> |
| *<p> |
| * The reason for first two arguments to be separate is that parameterization may |
| * apply to a super-type. For example, if generic type was instead to be |
| * constructed for {@code ArrayList<Integer>}, the usual call would be: |
| *<pre> |
| * TypeFactory.constructParametricType(ArrayList.class, List.class, Integer.class); |
| *</pre> |
| * since parameterization is applied to {@link java.util.List}. |
| * In most cases distinction does not matter, but there are types where it does; |
| * one such example is parameterization of types that implement {@link java.util.Iterator}. |
| *<p> |
| * NOTE: type modifiers are NOT called on constructed type. |
| * |
| * @param rawType Actual type-erased type |
| * @param parameterTypes Type parameters to apply |
| * |
| * @since 2.5 NOTE: was briefly deprecated for 2.6 |
| */ |
| public JavaType constructParametricType(Class<?> rawType, JavaType... parameterTypes) |
| { |
| return _fromClass(null, rawType, TypeBindings.create(rawType, parameterTypes)); |
| } |
| |
| /** |
| * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7) |
| * |
| * @deprecated since 2.9 Use {@link #constructParametricType(Class,JavaType...)} instead |
| */ |
| @Deprecated |
| public JavaType constructParametrizedType(Class<?> parametrized, Class<?> parametersFor, |
| JavaType... parameterTypes) |
| { |
| return constructParametricType(parametrized, parameterTypes); |
| } |
| |
| /** |
| * @since 2.5 -- but will probably deprecated in 2.7 or 2.8 (not needed with 2.7) |
| * |
| * @deprecated since 2.9 Use {@link #constructParametricType(Class,Class...)} instead |
| */ |
| @Deprecated |
| public JavaType constructParametrizedType(Class<?> parametrized, Class<?> parametersFor, |
| Class<?>... parameterClasses) |
| { |
| return constructParametricType(parametrized, parameterClasses); |
| } |
| |
| /* |
| /********************************************************** |
| /* Direct factory methods for "raw" variants, used when |
| /* parameterization is unknown |
| /********************************************************** |
| */ |
| |
| /** |
| * Method that can be used to construct "raw" Collection type; meaning that its |
| * parameterization is unknown. |
| * This is similar to using <code>Object.class</code> parameterization, |
| * and is equivalent to calling: |
| *<pre> |
| * typeFactory.constructCollectionType(collectionClass, typeFactory.unknownType()); |
| *</pre> |
| *<p> |
| * This method should only be used if parameterization is completely unavailable. |
| */ |
| public CollectionType constructRawCollectionType(Class<? extends Collection> collectionClass) { |
| return constructCollectionType(collectionClass, unknownType()); |
| } |
| |
| /** |
| * Method that can be used to construct "raw" Collection-like type; meaning that its |
| * parameterization is unknown. |
| * This is similar to using <code>Object.class</code> parameterization, |
| * and is equivalent to calling: |
| *<pre> |
| * typeFactory.constructCollectionLikeType(collectionClass, typeFactory.unknownType()); |
| *</pre> |
| *<p> |
| * This method should only be used if parameterization is completely unavailable. |
| */ |
| public CollectionLikeType constructRawCollectionLikeType(Class<?> collectionClass) { |
| return constructCollectionLikeType(collectionClass, unknownType()); |
| } |
| |
| /** |
| * Method that can be used to construct "raw" Map type; meaning that its |
| * parameterization is unknown. |
| * This is similar to using <code>Object.class</code> parameterization, |
| * and is equivalent to calling: |
| *<pre> |
| * typeFactory.constructMapType(collectionClass, typeFactory.unknownType(), typeFactory.unknownType()); |
| *</pre> |
| *<p> |
| * This method should only be used if parameterization is completely unavailable. |
| */ |
| public MapType constructRawMapType(Class<? extends Map> mapClass) { |
| return constructMapType(mapClass, unknownType(), unknownType()); |
| } |
| |
| /** |
| * Method that can be used to construct "raw" Map-like type; meaning that its |
| * parameterization is unknown. |
| * This is similar to using <code>Object.class</code> parameterization, |
| * and is equivalent to calling: |
| *<pre> |
| * typeFactory.constructMapLikeType(collectionClass, typeFactory.unknownType(), typeFactory.unknownType()); |
| *</pre> |
| *<p> |
| * This method should only be used if parameterization is completely unavailable. |
| */ |
| public MapLikeType constructRawMapLikeType(Class<?> mapClass) { |
| return constructMapLikeType(mapClass, unknownType(), unknownType()); |
| } |
| |
| /* |
| /********************************************************** |
| /* Low-level factory methods |
| /********************************************************** |
| */ |
| |
| private JavaType _mapType(Class<?> rawClass, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) |
| { |
| JavaType kt, vt; |
| |
| // 28-May-2015, tatu: Properties are special, as per [databind#810]; fake "correct" parameter sig |
| if (rawClass == Properties.class) { |
| kt = vt = CORE_TYPE_STRING; |
| } else { |
| List<JavaType> typeParams = bindings.getTypeParameters(); |
| // ok to have no types ("raw") |
| switch (typeParams.size()) { |
| case 0: // acceptable? |
| kt = vt = _unknownType(); |
| break; |
| case 2: |
| kt = typeParams.get(0); |
| vt = typeParams.get(1); |
| break; |
| default: |
| throw new IllegalArgumentException("Strange Map type "+rawClass.getName()+": cannot determine type parameters"); |
| } |
| } |
| return MapType.construct(rawClass, bindings, superClass, superInterfaces, kt, vt); |
| } |
| |
| private JavaType _collectionType(Class<?> rawClass, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) |
| { |
| List<JavaType> typeParams = bindings.getTypeParameters(); |
| // ok to have no types ("raw") |
| JavaType ct; |
| if (typeParams.isEmpty()) { |
| ct = _unknownType(); |
| } else if (typeParams.size() == 1) { |
| ct = typeParams.get(0); |
| } else { |
| throw new IllegalArgumentException("Strange Collection type "+rawClass.getName()+": cannot determine type parameters"); |
| } |
| return CollectionType.construct(rawClass, bindings, superClass, superInterfaces, ct); |
| } |
| |
| private JavaType _referenceType(Class<?> rawClass, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) |
| { |
| List<JavaType> typeParams = bindings.getTypeParameters(); |
| // ok to have no types ("raw") |
| JavaType ct; |
| if (typeParams.isEmpty()) { |
| ct = _unknownType(); |
| } else if (typeParams.size() == 1) { |
| ct = typeParams.get(0); |
| } else { |
| throw new IllegalArgumentException("Strange Reference type "+rawClass.getName()+": cannot determine type parameters"); |
| } |
| return ReferenceType.construct(rawClass, bindings, superClass, superInterfaces, ct); |
| } |
| |
| /** |
| * Factory method to call when no special {@link JavaType} is needed, |
| * no generic parameters are passed. Default implementation may check |
| * pre-constructed values for "well-known" types, but if none found |
| * will simply call {@link #_newSimpleType} |
| * |
| * @since 2.7 |
| */ |
| protected JavaType _constructSimple(Class<?> raw, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) |
| { |
| if (bindings.isEmpty()) { |
| JavaType result = _findWellKnownSimple(raw); |
| if (result != null) { |
| return result; |
| } |
| } |
| return _newSimpleType(raw, bindings, superClass, superInterfaces); |
| } |
| |
| /** |
| * Factory method that is to create a new {@link SimpleType} with no |
| * checks whatsoever. Default implementation calls the single argument |
| * constructor of {@link SimpleType}. |
| * |
| * @since 2.7 |
| */ |
| protected JavaType _newSimpleType(Class<?> raw, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) |
| { |
| return new SimpleType(raw, bindings, superClass, superInterfaces); |
| } |
| |
| protected JavaType _unknownType() { |
| /* 15-Sep-2015, tatu: Prior to 2.7, we constructed new instance for each call. |
| * This may have been due to potential mutability of the instance; but that |
| * should not be issue any more, and creation is somewhat wasteful. So let's |
| * try reusing singleton/flyweight instance. |
| */ |
| return CORE_TYPE_OBJECT; |
| } |
| |
| /** |
| * Helper method called to see if requested, non-generic-parameterized |
| * type is one of common, "well-known" types, instances of which are |
| * pre-constructed and do not need dynamic caching. |
| * |
| * @since 2.7 |
| */ |
| protected JavaType _findWellKnownSimple(Class<?> clz) { |
| if (clz.isPrimitive()) { |
| if (clz == CLS_BOOL) return CORE_TYPE_BOOL; |
| if (clz == CLS_INT) return CORE_TYPE_INT; |
| if (clz == CLS_LONG) return CORE_TYPE_LONG; |
| } else { |
| if (clz == CLS_STRING) return CORE_TYPE_STRING; |
| if (clz == CLS_OBJECT) return CORE_TYPE_OBJECT; // since 2.7 |
| if (clz == CLS_JSON_NODE) return CORE_TYPE_JSON_NODE; // since 2.10 |
| } |
| return null; |
| } |
| |
| /* |
| /********************************************************** |
| /* Actual type resolution, traversal |
| /********************************************************** |
| */ |
| |
| /** |
| * Factory method that can be used if type information is passed |
| * as Java typing returned from <code>getGenericXxx</code> methods |
| * (usually for a return or argument type). |
| */ |
| protected JavaType _fromAny(ClassStack context, Type type, TypeBindings bindings) |
| { |
| JavaType resultType; |
| |
| // simple class? |
| if (type instanceof Class<?>) { |
| // Important: remove possible bindings since this is type-erased thingy |
| resultType = _fromClass(context, (Class<?>) type, EMPTY_BINDINGS); |
| } |
| // But if not, need to start resolving. |
| else if (type instanceof ParameterizedType) { |
| resultType = _fromParamType(context, (ParameterizedType) type, bindings); |
| } |
| else if (type instanceof JavaType) { // [databind#116] |
| // no need to modify further if we already had JavaType |
| return (JavaType) type; |
| } |
| else if (type instanceof GenericArrayType) { |
| resultType = _fromArrayType(context, (GenericArrayType) type, bindings); |
| } |
| else if (type instanceof TypeVariable<?>) { |
| resultType = _fromVariable(context, (TypeVariable<?>) type, bindings); |
| } |
| else if (type instanceof WildcardType) { |
| resultType = _fromWildcard(context, (WildcardType) type, bindings); |
| } else { |
| // sanity check |
| throw new IllegalArgumentException("Unrecognized Type: "+((type == null) ? "[null]" : type.toString())); |
| } |
| // 21-Feb-2016, nateB/tatu: as per [databind#1129] (applied for 2.7.2), |
| // we do need to let all kinds of types to be refined, esp. for Scala module. |
| if (_modifiers != null) { |
| TypeBindings b = resultType.getBindings(); |
| if (b == null) { |
| b = EMPTY_BINDINGS; |
| } |
| for (TypeModifier mod : _modifiers) { |
| JavaType t = mod.modifyType(resultType, type, b, this); |
| if (t == null) { |
| throw new IllegalStateException(String.format( |
| "TypeModifier %s (of type %s) return null for type %s", |
| mod, mod.getClass().getName(), resultType)); |
| } |
| resultType = t; |
| } |
| } |
| return resultType; |
| } |
| |
| /** |
| * @param bindings Mapping of formal parameter declarations (for generic |
| * types) into actual types |
| */ |
| protected JavaType _fromClass(ClassStack context, Class<?> rawType, TypeBindings bindings) |
| { |
| // Very first thing: small set of core types we know well: |
| JavaType result = _findWellKnownSimple(rawType); |
| if (result != null) { |
| return result; |
| } |
| // Barring that, we may have recently constructed an instance |
| final Object key; |
| if ((bindings == null) || bindings.isEmpty()) { |
| key = rawType; |
| } else { |
| key = bindings.asKey(rawType); |
| } |
| result = _typeCache.get(key); // ok, cache object is synced |
| if (result != null) { |
| return result; |
| } |
| |
| // 15-Oct-2015, tatu: recursive reference? |
| if (context == null) { |
| context = new ClassStack(rawType); |
| } else { |
| ClassStack prev = context.find(rawType); |
| if (prev != null) { |
| // Self-reference: needs special handling, then... |
| ResolvedRecursiveType selfRef = new ResolvedRecursiveType(rawType, EMPTY_BINDINGS); |
| prev.addSelfReference(selfRef); |
| return selfRef; |
| } |
| // no, but need to update context to allow for proper cycle resolution |
| context = context.child(rawType); |
| } |
| |
| // First: do we have an array type? |
| if (rawType.isArray()) { |
| result = ArrayType.construct(_fromAny(context, rawType.getComponentType(), bindings), |
| bindings); |
| } else { |
| // If not, need to proceed by first resolving parent type hierarchy |
| |
| JavaType superClass; |
| JavaType[] superInterfaces; |
| |
| if (rawType.isInterface()) { |
| superClass = null; |
| superInterfaces = _resolveSuperInterfaces(context, rawType, bindings); |
| } else { |
| // Note: even Enums can implement interfaces, so cannot drop those |
| superClass = _resolveSuperClass(context, rawType, bindings); |
| superInterfaces = _resolveSuperInterfaces(context, rawType, bindings); |
| } |
| |
| // 19-Oct-2015, tatu: Bit messy, but we need to 'fix' java.util.Properties here... |
| if (rawType == Properties.class) { |
| result = MapType.construct(rawType, bindings, superClass, superInterfaces, |
| CORE_TYPE_STRING, CORE_TYPE_STRING); |
| } |
| // And then check what flavor of type we got. Start by asking resolved |
| // super-type if refinement is all that is needed? |
| else if (superClass != null) { |
| result = superClass.refine(rawType, bindings, superClass, superInterfaces); |
| } |
| // if not, perhaps we are now resolving a well-known class or interface? |
| if (result == null) { |
| result = _fromWellKnownClass(context, rawType, bindings, superClass, superInterfaces); |
| if (result == null) { |
| result = _fromWellKnownInterface(context, rawType, bindings, superClass, superInterfaces); |
| if (result == null) { |
| // but if nothing else, "simple" class for now: |
| result = _newSimpleType(rawType, bindings, superClass, superInterfaces); |
| } |
| } |
| } |
| } |
| context.resolveSelfReferences(result); |
| // 16-Jul-2016, tatu: [databind#1302] is solved different way, but ideally we shouldn't |
| // cache anything with partially resolved `ResolvedRecursiveType`... so maybe improve |
| if (!result.hasHandlers()) { |
| _typeCache.putIfAbsent(key, result); // cache object syncs |
| } |
| return result; |
| } |
| |
| protected JavaType _resolveSuperClass(ClassStack context, Class<?> rawType, TypeBindings parentBindings) |
| { |
| Type parent = ClassUtil.getGenericSuperclass(rawType); |
| if (parent == null) { |
| return null; |
| } |
| return _fromAny(context, parent, parentBindings); |
| } |
| |
| protected JavaType[] _resolveSuperInterfaces(ClassStack context, Class<?> rawType, TypeBindings parentBindings) |
| { |
| Type[] types = ClassUtil.getGenericInterfaces(rawType); |
| if (types == null || types.length == 0) { |
| return NO_TYPES; |
| } |
| int len = types.length; |
| JavaType[] resolved = new JavaType[len]; |
| for (int i = 0; i < len; ++i) { |
| Type type = types[i]; |
| resolved[i] = _fromAny(context, type, parentBindings); |
| } |
| return resolved; |
| } |
| |
| /** |
| * Helper class used to check whether exact class for which type is being constructed |
| * is one of well-known base interfaces or classes that indicates alternate |
| * {@link JavaType} implementation. |
| */ |
| protected JavaType _fromWellKnownClass(ClassStack context, Class<?> rawType, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) |
| { |
| if (bindings == null) { |
| bindings = EMPTY_BINDINGS; |
| } |
| |
| // Quite simple when we resolving exact class/interface; start with that |
| if (rawType == Map.class) { |
| return _mapType(rawType, bindings, superClass, superInterfaces); |
| } |
| if (rawType == Collection.class) { |
| return _collectionType(rawType, bindings, superClass, superInterfaces); |
| } |
| // and since 2.6 one referential type |
| if (rawType == AtomicReference.class) { |
| return _referenceType(rawType, bindings, superClass, superInterfaces); |
| } |
| // 01-Nov-2015, tatu: As of 2.7, couple of potential `CollectionLikeType`s (like |
| // `Iterable`, `Iterator`), and `MapLikeType`s (`Map.Entry`) are not automatically |
| // detected, related to difficulties in propagating type upwards (Iterable, for |
| // example, is a weak, tag-on type). They may be detectable in future. |
| return null; |
| } |
| |
| protected JavaType _fromWellKnownInterface(ClassStack context, Class<?> rawType, TypeBindings bindings, |
| JavaType superClass, JavaType[] superInterfaces) |
| { |
| // But that's not all: may be possible current type actually implements an |
| // interface type. So... |
| final int intCount = superInterfaces.length; |
| |
| for (int i = 0; i < intCount; ++i) { |
| JavaType result = superInterfaces[i].refine(rawType, bindings, superClass, superInterfaces); |
| if (result != null) { |
| return result; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * This method deals with parameterized types, that is, |
| * first class generic classes. |
| */ |
| protected JavaType _fromParamType(ClassStack context, ParameterizedType ptype, |
| TypeBindings parentBindings) |
| { |
| // Assumption here is we'll always get Class, not one of other Types |
| Class<?> rawType = (Class<?>) ptype.getRawType(); |
| |
| // 29-Oct-2015, tatu: For performance reasons, let's streamline handling of |
| // couple of not-so-useful parametric types |
| if (rawType == CLS_ENUM) { |
| return CORE_TYPE_ENUM; |
| } |
| if (rawType == CLS_COMPARABLE) { |
| return CORE_TYPE_COMPARABLE; |
| } |
| if (rawType == CLS_CLASS) { |
| return CORE_TYPE_CLASS; |
| } |
| |
| // First: what is the actual base type? One odd thing is that 'getRawType' |
| // returns Type, not Class<?> as one might expect. But let's assume it is |
| // always of type Class: if not, need to add more code to resolve it to Class. |
| Type[] args = ptype.getActualTypeArguments(); |
| int paramCount = (args == null) ? 0 : args.length; |
| TypeBindings newBindings; |
| |
| if (paramCount == 0) { |
| newBindings = EMPTY_BINDINGS; |
| } else { |
| JavaType[] pt = new JavaType[paramCount]; |
| for (int i = 0; i < paramCount; ++i) { |
| pt[i] = _fromAny(context, args[i], parentBindings); |
| } |
| newBindings = TypeBindings.create(rawType, pt); |
| } |
| return _fromClass(context, rawType, newBindings); |
| } |
| |
| protected JavaType _fromArrayType(ClassStack context, GenericArrayType type, TypeBindings bindings) |
| { |
| JavaType elementType = _fromAny(context, type.getGenericComponentType(), bindings); |
| return ArrayType.construct(elementType, bindings); |
| } |
| |
| protected JavaType _fromVariable(ClassStack context, TypeVariable<?> var, TypeBindings bindings) |
| { |
| // ideally should find it via bindings: |
| final String name = var.getName(); |
| if (bindings == null) { |
| throw new IllegalArgumentException("Null `bindings` passed (type variable \""+name+"\")"); |
| } |
| JavaType type = bindings.findBoundType(name); |
| if (type != null) { |
| return type; |
| } |
| // but if not, use bounds... note that approach here is simplistic; not taking |
| // into account possible multiple bounds, nor consider upper bounds. |
| if (bindings.hasUnbound(name)) { |
| return CORE_TYPE_OBJECT; |
| } |
| bindings = bindings.withUnboundVariable(name); |
| |
| final Type[] bounds; |
| |
| // 15-Jan-2019, tatu: As weird as this looks, apparently on some platforms (Arm CPU, mobile |
| // devices), unsynchronized internal access can lead to issues, see: |
| // |
| // https://vmlens.com/articles/java-lang-reflect-typevariable-getbounds-is-not-thread-safe/ |
| // |
| // No good way to reproduce but since this should not be on critical path, let's add |
| // syncing as it seems potentially necessary. |
| synchronized (var) { |
| bounds = var.getBounds(); |
| } |
| return _fromAny(context, bounds[0], bindings); |
| } |
| |
| protected JavaType _fromWildcard(ClassStack context, WildcardType type, TypeBindings bindings) |
| { |
| /* Similar to challenges with TypeVariable, we may have multiple upper bounds. |
| * But it is also possible that if upper bound defaults to Object, we might |
| * want to consider lower bounds instead. |
| * For now, we won't try anything more advanced; above is just for future reference. |
| */ |
| return _fromAny(context, type.getUpperBounds()[0], bindings); |
| } |
| } |