blob: 2362ba537854896ba38ea15978a855f90d2094dc [file] [log] [blame]
package com.fasterxml.jackson.databind;
import java.lang.reflect.Modifier;
import java.util.List;
import com.fasterxml.jackson.core.type.ResolvedType;
import com.fasterxml.jackson.databind.type.TypeBindings;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* Base class for type token classes used both to contain information
* and as keys for deserializers.
*<p>
* Instances can (only) be constructed by
* <code>com.fasterxml.jackson.databind.type.TypeFactory</code>.
*<p>
* Since 2.2 this implements {@link java.lang.reflect.Type} to allow
* it to be pushed through interfaces that only expose that type.
*/
public abstract class JavaType
extends ResolvedType
implements java.io.Serializable, // 2.1
java.lang.reflect.Type // 2.2
{
private static final long serialVersionUID = 1;
/**
* This is the nominal type-erased Class that would be close to the
* type represented (but not exactly type, due to type erasure: type
* instance may have more information on this).
* May be an interface or abstract class, so instantiation
* may not be possible.
*/
protected final Class<?> _class;
protected final int _hash;
/**
* Optional handler (codec) that can be attached to indicate
* what to use for handling (serializing, deserializing) values of
* this specific type.
*<p>
* Note: untyped (i.e. caller has to cast) because it is used for
* different kinds of handlers, with unrelated types.
*/
protected final Object _valueHandler;
/**
* Optional handler that can be attached to indicate how to handle
* additional type metadata associated with this type.
*<p>
* Note: untyped (i.e. caller has to cast) because it is used for
* different kinds of handlers, with unrelated types.
*/
protected final Object _typeHandler;
/**
* Whether entities defined with this type should be handled using
* static typing (as opposed to dynamic runtime type) or not.
*
* @since 2.2
*/
protected final boolean _asStatic;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* @param raw "Raw" (type-erased) class for this type
* @param additionalHash Additional hash code to use, in addition
* to hash code of the class name
*/
protected JavaType(Class<?> raw, int additionalHash,
Object valueHandler, Object typeHandler, boolean asStatic)
{
_class = raw;
_hash = raw.getName().hashCode() + additionalHash;
_valueHandler = valueHandler;
_typeHandler = typeHandler;
_asStatic = asStatic;
}
/**
* Copy-constructor used when refining/upgrading type instances.
*
* @since 2.7
*/
protected JavaType(JavaType base)
{
_class = base._class;
_hash = base._hash;
_valueHandler = base._valueHandler;
_typeHandler = base._typeHandler;
_asStatic = base._asStatic;
}
/**
* "Copy method" that will construct a new instance that is identical to
* this instance, except that it will have specified type handler assigned.
*
* @return Newly created type instance
*/
public abstract JavaType withTypeHandler(Object h);
/**
* Mutant factory method that will construct a new instance that is identical to
* this instance, except that it will have specified content type (element type
* for arrays, value type for Maps and so forth) handler assigned.
*
* @return Newly created type instance, with given
*/
public abstract JavaType withContentTypeHandler(Object h);
/**
* Mutant factory method that will construct a new instance that is identical to
* this instance, except that it will have specified value handler assigned.
*
* @return Newly created type instance
*/
public abstract JavaType withValueHandler(Object h);
/**
* Mutant factory method that will construct a new instance that is identical to
* this instance, except that it will have specified content value handler assigned.
*
* @return Newly created type instance
*/
public abstract JavaType withContentValueHandler(Object h);
/**
* Mutant factory method that will try to copy handlers that the specified
* source type instance had, if any; this must be done recursively where
* necessary (as content types may be structured).
*
* @since 2.8.4
*/
public JavaType withHandlersFrom(JavaType src) {
JavaType type = this;
Object h = src.getTypeHandler();
if (h != _typeHandler) {
type = type.withTypeHandler(h);
}
h = src.getValueHandler();
if (h != _valueHandler) {
type = type.withValueHandler(h);
}
return type;
}
/**
* Mutant factory method that may be called on structured types
* that have a so-called content type (element of arrays, value type
* of Maps, referenced type of referential types),
* and will construct a new instance that is identical to
* this instance, except that it has specified content type, instead of current
* one. If content type is already set to given type, <code>this</code> is returned.
* If type does not have a content type (which is the case with
* <code>SimpleType</code>), {@link IllegalArgumentException}
* will be thrown.
*
* @return Newly created type instance
*
* @since 2.7
*/
public abstract JavaType withContentType(JavaType contentType);
/**
* Method that can be called to get a type instance that indicates
* that values of the type should be handled using "static typing" for purposes
* of serialization (as opposed to "dynamic" aka runtime typing):
* meaning that no runtime information is needed for determining serializers to use.
* The main use case is to allow forcing of specific root value serialization type,
* and specifically in resolving serializers for contained types (element types
* for arrays, Collections and Maps).
*
* @since 2.2
*/
public abstract JavaType withStaticTyping();
/*
/**********************************************************
/* Type coercion fluent factory methods
/**********************************************************
*/
/**
* Mutant factory method that will try to create and return a sub-type instance
* for known parameterized types; for other types will return `null` to indicate
* that no just refinement makes necessary sense, without trying to detect
* special status through implemented interfaces.
*
* @since 2.7
*/
public abstract JavaType refine(Class<?> rawType, TypeBindings bindings,
JavaType superClass, JavaType[] superInterfaces);
/**
* Legacy method used for forcing sub-typing of this type into
* type specified by specific type erasure.
* Deprecated as of 2.7 as such specializations really ought to
* go through {@link TypeFactory}, not directly via {@link JavaType}.
*
* @since 2.7
*/
@Deprecated
public JavaType forcedNarrowBy(Class<?> subclass)
{
if (subclass == _class) { // can still optimize for simple case
return this;
}
return _narrow(subclass);
}
@Deprecated // since 2.7
protected abstract JavaType _narrow(Class<?> subclass);
/*
/**********************************************************
/* Implementation of ResolvedType API
/**********************************************************
*/
@Override
public final Class<?> getRawClass() { return _class; }
/**
* Method that can be used to check whether this type has
* specified Class as its type erasure. Put another way, returns
* true if instantiation of this Type is given (type-erased) Class.
*/
@Override
public final boolean hasRawClass(Class<?> clz) { return _class == clz; }
/**
* Accessor that allows determining whether {@link #getContentType()} should
* return a non-null value (that is, there is a "content type") or not.
* True if {@link #isContainerType()} or {@link #isReferenceType()} return true.
*
* @since 2.8
*/
public boolean hasContentType() {
return true;
}
/**
* @since 2.6
*/
public final boolean isTypeOrSubTypeOf(Class<?> clz) {
return (_class == clz) || clz.isAssignableFrom(_class);
}
/**
* @since 2.9
*/
public final boolean isTypeOrSuperTypeOf(Class<?> clz) {
return (_class == clz) || _class.isAssignableFrom(clz);
}
@Override
public boolean isAbstract() {
return Modifier.isAbstract(_class.getModifiers());
}
/**
* Convenience method for checking whether underlying Java type
* is a concrete class or not: abstract classes and interfaces
* are not.
*/
@Override
public boolean isConcrete() {
int mod = _class.getModifiers();
if ((mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0) {
return true;
}
/* 19-Feb-2010, tatus: Holy mackarel; primitive types
* have 'abstract' flag set...
*/
return _class.isPrimitive();
}
@Override
public boolean isThrowable() { return Throwable.class.isAssignableFrom(_class); }
@Override
public boolean isArrayType() { return false; }
@Override
public final boolean isEnumType() {
// 29-Sep-2019, tatu: `Class.isEnum()` not enough to detect custom subtypes,
// but for some reason this fix will break couple of unit tests:
// return ClassUtil.isEnumType(_class);
return _class.isEnum();
}
@Override
public final boolean isInterface() { return _class.isInterface(); }
@Override
public final boolean isPrimitive() { return _class.isPrimitive(); }
@Override
public final boolean isFinal() { return Modifier.isFinal(_class.getModifiers()); }
/**
* @return True if type represented is a container type; this includes
* array, Map and Collection types.
*/
@Override
public abstract boolean isContainerType();
/**
* @return True if type is either true {@link java.util.Collection} type,
* or something similar (meaning it has at least one type parameter,
* which describes type of contents)
*/
@Override
public boolean isCollectionLikeType() { return false; }
/**
* @return True if type is either true {@link java.util.Map} type,
* or something similar (meaning it has at least two type parameter;
* first one describing key type, second value type)
*/
@Override
public boolean isMapLikeType() { return false; }
/**
* Convenience method, short-hand for
*<code>
* getRawClass() == Object.class
*</code>
* and used to figure if we basically have "untyped" type object.
*
* @since 2.5
*/
public final boolean isJavaLangObject() { return _class == Object.class; }
/**
* Accessor for checking whether handlers for dealing with values of
* this type should use static typing (as opposed to dynamic typing).
* Note that while value of 'true' does mean that static typing is to
* be used, value of 'false' may still be overridden by other settings.
*
* @since 2.2
*/
public final boolean useStaticType() { return _asStatic; }
/*
/**********************************************************
/* Public API, type parameter access; pass-through
/**********************************************************
*/
@Override
public boolean hasGenericTypes() { return containedTypeCount() > 0; }
@Override
public JavaType getKeyType() { return null; }
@Override
public JavaType getContentType() { return null; }
@Override // since 2.6
public JavaType getReferencedType() { return null; }
@Override
public abstract int containedTypeCount();
@Override
public abstract JavaType containedType(int index);
@Deprecated // since 2.7
@Override
public abstract String containedTypeName(int index);
@Deprecated // since 2.7
@Override
public Class<?> getParameterSource() {
return null;
}
/*
/**********************************************************
/* Extended API beyond ResolvedType
/**********************************************************
*/
// NOTE: not defined in Resolved type
/**
* Convenience method that is functionally same as:
*<code>
* JavaType t = containedType(index);
* if (t == null) {
* t = TypeFactory.unknownType();
* }
*</code>
* and typically used to eliminate need for null checks for common case
* where we just want to check if containedType is available first; and
* if not, use "unknown type" (which translates to <code>java.lang.Object</code>
* basically).
*
* @since 2.5
*/
public JavaType containedTypeOrUnknown(int index) {
JavaType t = containedType(index);
return (t == null) ? TypeFactory.unknownType() : t;
}
/**
* @since 2.7
*/
public abstract TypeBindings getBindings();
/**
* Method that may be called to find representation of given type
* within type hierarchy of this type: either this type (if this
* type has given erased type), one of its supertypes that has the
* erased types, or null if target is neither this type or any of its
* supertypes.
*
* @since 2.7
*/
public abstract JavaType findSuperType(Class<?> erasedTarget);
/**
* Accessor for finding fully resolved parent class of this type,
* if it has one; null if not.
*
* @since 2.7
*/
public abstract JavaType getSuperClass();
/**
* Accessor for finding fully resolved interfaces this type implements,
* if any; empty array if none.
*
* @since 2.7
*/
public abstract List<JavaType> getInterfaces();
/**
* Method that may be used to find paramaterization this type has for
* given type-erased generic target type.
*
* @since 2.7
*/
public abstract JavaType[] findTypeParameters(Class<?> expType);
/*
/**********************************************************
/* Semi-public API, accessing handlers
/**********************************************************
*/
/**
* Method for accessing value handler associated with this type, if any
*/
@SuppressWarnings("unchecked")
public <T> T getValueHandler() { return (T) _valueHandler; }
/**
* Method for accessing type handler associated with this type, if any
*/
@SuppressWarnings("unchecked")
public <T> T getTypeHandler() { return (T) _typeHandler; }
/**
* @since 2.7
*/
public Object getContentValueHandler() { return null; }
/**
* @since 2.7
*/
public Object getContentTypeHandler() { return null; }
/**
* @since 2.6
*/
public boolean hasValueHandler() { return _valueHandler != null; }
/**
* Helper method that checks whether this type, or its (optional) key
* or content type has {@link #getValueHandler} or {@link #getTypeHandler()};
* that is, are there any non-standard handlers associated with this
* type object.
*
* @since 2.8
*/
public boolean hasHandlers() {
return (_typeHandler != null) || (_valueHandler != null);
}
/*
/**********************************************************
/* Support for producing signatures
/**********************************************************
*/
//public abstract String toCanonical();
/**
* Method for accessing signature that contains generic
* type information, in form compatible with JVM 1.5
* as per JLS. It is a superset of {@link #getErasedSignature},
* in that generic information can be automatically removed
* if necessary (just remove outermost
* angle brackets along with content inside)
*/
public String getGenericSignature() {
StringBuilder sb = new StringBuilder(40);
getGenericSignature(sb);
return sb.toString();
}
/**
*
* @param sb StringBuilder to append signature to
*
* @return StringBuilder that was passed in; returned to allow
* call chaining
*/
public abstract StringBuilder getGenericSignature(StringBuilder sb);
/**
* Method for accessing signature without generic
* type information, in form compatible with all versions
* of JVM, and specifically used for type descriptions
* when generating byte code.
*/
public String getErasedSignature() {
StringBuilder sb = new StringBuilder(40);
getErasedSignature(sb);
return sb.toString();
}
/**
* Method for accessing signature without generic
* type information, in form compatible with all versions
* of JVM, and specifically used for type descriptions
* when generating byte code.
*
* @param sb StringBuilder to append signature to
*
* @return StringBuilder that was passed in; returned to allow
* call chaining
*/
public abstract StringBuilder getErasedSignature(StringBuilder sb);
/*
/**********************************************************
/* Standard methods; let's make them abstract to force override
/**********************************************************
*/
@Override
public abstract String toString();
@Override
public abstract boolean equals(Object o);
@Override
public final int hashCode() { return _hash; }
}