blob: 3b216491fd64ad56cbefc00fab6542a6b9de1ad2 [file] [log] [blame]
package org.codehaus.jackson.type;
import java.lang.reflect.Modifier;
/**
* Base class for type token classes used both to contain information
* and as keys for deserializers.
*<p>
* Instances can (only) be constructed by
* {@link org.codehaus.jackson.map.type.TypeFactory}.
*/
public abstract class JavaType
{
/**
* 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 _hashCode;
/**
* 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.
*<p>
* TODO: make final and possible promote to sub-classes
*
* @since 1.3
*/
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.
*<p>
* TODO: make final and possible promote to sub-classes
*
* @since 1.5
*/
protected /*final*/ Object _typeHandler;
/*
/**********************************************************
/* 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)
{
_class = raw;
_hashCode = raw.getName().hashCode() + additionalHash;
_valueHandler = null;
_typeHandler = null;
}
/**
* "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
*
* @since 1.7
*/
public abstract JavaType withTypeHandler(Object h);
/**
* "Copy method" that will construct a new instance that is identical to
* this instance, except that its content type will have specified
* type handler assigned.
*
* @return Newly created type instance
*
* @since 1.7
*/
public abstract JavaType withContentTypeHandler(Object h);
// !!! TODO: in 2.0, change to be abstract method
//public abstract JavaType withValueHandler(Object h);
/**
* @since 1.9
*/
public JavaType withValueHandler(Object h) {
/* 16-Aug-2011, tatu: This is not entirely correct, as we can not
* create new immutable instances. However, sub-classes can,
* so if mapper is version 1.9, things work as expected.
* Otherwise, in 2.0 we can make this method abstract.
*/
setValueHandler(h);
return this;
}
// !!! TODO: in 2.0, change to be abstract method
//public abstract JavaType withContentValueHandler(Object h);
/**
* @since 1.9
*/
public JavaType withContentValueHandler(Object h) {
/* 16-Aug-2011, tatu: This is not entirely correct, as we can not
* create new immutable instances. However, sub-classes can,
* so if mapper is version 1.9, things work as expected.
* Otherwise, in 2.0 we can make this method abstract.
*/
getContentType().setValueHandler(h);
return this;
}
/**
* Method for assigning handler to associate with this type; or
* if null passed, to remove such assignment
*
* @since 1.3
*
* @deprecated Since 1.9, should not be used; instead, use
* <code>withContentTypeHandler</code> and
* <code>withContentValueHandler</code> methods.
*/
@Deprecated
public void setValueHandler(Object h) {
// sanity check, should be assigned just once
if (h != null && _valueHandler != null) {
throw new IllegalStateException("Trying to reset value handler for type ["+toString()
+"]; old handler of type "+_valueHandler.getClass().getName()+", new handler of type "+h.getClass().getName());
}
_valueHandler = h;
}
/*
/**********************************************************
/* Type coercion fluent factory methods
/**********************************************************
*/
/**
* Method that can be called to do a "narrowing" conversions; that is,
* to return a type with a raw class that is assignable to the raw
* class of this type. If this is not possible, an
* {@link IllegalArgumentException} is thrown.
* If class is same as the current raw class, instance itself is
* returned.
*/
public JavaType narrowBy(Class<?> subclass)
{
// First: if same raw class, just return this instance
if (subclass == _class) {
return this;
}
// Otherwise, ensure compatibility
_assertSubclass(subclass, _class);
JavaType result = _narrow(subclass);
// TODO: these checks should NOT actually be needed; above should suffice:
if (_valueHandler != result.getValueHandler()) {
result = result.withValueHandler(_valueHandler);
}
if (_typeHandler != result.getTypeHandler()) {
result = result.withTypeHandler(_typeHandler);
}
return result;
}
/**
* More efficient version of {@link #narrowBy}, called by
* internal framework in cases where compatibility checks
* are to be skipped.
*
* @since 1.5
*/
public JavaType forcedNarrowBy(Class<?> subclass)
{
if (subclass == _class) { // can still optimize for simple case
return this;
}
JavaType result = _narrow(subclass);
// TODO: these checks should NOT actually be needed; above should suffice:
if (_valueHandler != result.getValueHandler()) {
result = result.withValueHandler(_valueHandler);
}
if (_typeHandler != result.getTypeHandler()) {
result = result.withTypeHandler(_typeHandler);
}
return result;
}
/**
* Method that can be called to do a "widening" conversions; that is,
* to return a type with a raw class that could be assigned from this
* type.
* If such conversion is not possible, an
* {@link IllegalArgumentException} is thrown.
* If class is same as the current raw class, instance itself is
* returned.
*/
public JavaType widenBy(Class<?> superclass)
{
// First: if same raw class, just return this instance
if (superclass == _class) {
return this;
}
// Otherwise, ensure compatibility
_assertSubclass(_class, superclass);
return _widen(superclass);
}
protected abstract JavaType _narrow(Class<?> subclass);
/**
*<p>
* Default implementation is just to call {@link #_narrow}, since
* underlying type construction is usually identical
*/
protected JavaType _widen(Class<?> superclass) {
return _narrow(superclass);
}
public abstract JavaType narrowContentsBy(Class<?> contentClass);
/**
* @since 1.8
*/
public abstract JavaType widenContentsBy(Class<?> contentClass);
/*
/**********************************************************
/* Public API, simple accessors
/**********************************************************
*/
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.
*/
public final boolean hasRawClass(Class<?> clz) {
return _class == clz;
}
public boolean isAbstract() {
return Modifier.isAbstract(_class.getModifiers());
}
/**
* @since 1.3
*/
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...
*/
if (_class.isPrimitive()) {
return true;
}
return false;
}
public boolean isThrowable() {
return Throwable.class.isAssignableFrom(_class);
}
public boolean isArrayType() { return false; }
public final boolean isEnumType() { return _class.isEnum(); }
public final boolean isInterface() { return _class.isInterface(); }
public final boolean isPrimitive() { return _class.isPrimitive(); }
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.
*/
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)
*
* @since 1.8
*/
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)
*
* @since 1.8
*/
public boolean isMapLikeType() { return false; }
/*
/**********************************************************
/* Public API, type parameter access
/**********************************************************
*/
/**
* Method that can be used to find out if the type directly declares generic
* parameters (for its direct super-class and/or super-interfaces).
*
* @since 1.6
*/
public boolean hasGenericTypes()
{
return containedTypeCount() > 0;
}
/**
* Method for accessing key type for this type, assuming type
* has such a concept (only Map types do)
*/
public JavaType getKeyType() { return null; }
/**
* Method for accessing content type of this type, if type has
* such a thing: simple types do not, structured types do
* (like arrays, Collections and Maps)
*/
public JavaType getContentType() { return null; }
/**
* Method for checking how many contained types this type
* has. Contained types are usually generic types, so that
* generic Maps have 2 contained types.
*
* @since 1.5
*/
public int containedTypeCount() { return 0; }
/**
* Method for accessing definitions of contained ("child")
* types.
*
* @param index Index of contained type to return
*
* @return Contained type at index, or null if no such type
* exists (no exception thrown)
*
* @since 1.5
*/
public JavaType containedType(int index) { return null; }
/**
* Method for accessing name of type variable in indicated
* position. If no name is bound, will use placeholders (derived
* from 0-based index); if no type variable or argument exists
* with given index, null is returned.
*
* @param index Index of contained type to return
*
* @return Contained type at index, or null if no such type
* exists (no exception thrown)
*
* @since 1.5
*/
public String containedTypeName(int index) { return null; }
/*
/**********************************************************
/* Semi-public API, accessing handlers
/**********************************************************
*/
/**
* Method for accessing value handler associated with this type, if any
*
* @since 1.3
*/
@SuppressWarnings("unchecked")
public <T> T getValueHandler() { return (T) _valueHandler; }
/**
* Method for accessing type handler associated with this type, if any
*
* @since 1.5
*/
@SuppressWarnings("unchecked")
public <T> T getTypeHandler() { return (T) _typeHandler; }
/*
/**********************************************************
/* Support for producing signatures (1.6+)
/**********************************************************
*/
/**
* Method that can be used to serialize type into form from which
* it can be fully deserialized from at a later point (using
* <code>TypeFactory</code> from mapper package).
* For simple types this is same as calling
* {@link Class#getName}, but for structured types it may additionally
* contain type information about contents.
*
* @since 1.5
*/
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)
*
* @since 1.6
*/
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
*
* @since 1.6
*/
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.
*
* @since 1.6
*/
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
*
* @since 1.6
*/
public abstract StringBuilder getErasedSignature(StringBuilder sb);
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
protected void _assertSubclass(Class<?> subclass, Class<?> superClass)
{
if (!_class.isAssignableFrom(subclass)) {
throw new IllegalArgumentException("Class "+subclass.getName()+" is not assignable to "+_class.getName());
}
}
/*
/**********************************************************
/* 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 _hashCode; }
}