package com.fasterxml.jackson.databind;
import java.lang.reflect.Type;
import java.util.Locale;
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.databind.util.Converter;
/**
* Shared base class for {@link DeserializationContext} and
* {@link SerializerProvider}, context objects passed through data-binding
* process. Designed so that some of implementations can rely on shared
* aspects like access to secondary contextual objects like type factories
* or handler instantiators.
*
* @since 2.2
*/
public abstract class DatabindContext
{
/**
* Let's limit length of error messages, for cases where underlying data
* may be very large -- no point in spamming logs with megabytes of meaningless
* data.
*
* @since 2.9
*/
private final static int MAX_ERROR_STR_LEN = 500;
/*
/**********************************************************
/* Generic config access
/**********************************************************
*/
/**
* Accessor to currently active configuration (both per-request configs
* and per-mapper config).
*/
public abstract MapperConfig<?> getConfig();
/**
* Convenience method for accessing serialization view in use (if any); equivalent to:
*<pre>
* getConfig().getAnnotationIntrospector();
*</pre>
*/
public abstract AnnotationIntrospector getAnnotationIntrospector();
/*
/**********************************************************
/* Access to specific config settings
/**********************************************************
*/
/**
* Convenience method for checking whether specified serialization
* feature is enabled or not.
* Shortcut for:
*<pre>
* getConfig().isEnabled(feature);
*</pre>
*/
public abstract boolean isEnabled(MapperFeature feature);
/**
* Convenience method for accessing serialization view in use (if any); equivalent to:
*<pre>
* getConfig().canOverrideAccessModifiers();
*</pre>
*/
public abstract boolean canOverrideAccessModifiers();
/**
* Accessor for locating currently active view, if any;
* returns null if no view has been set.
*/
public abstract Class<?> getActiveView();
/**
* @since 2.6
*/
public abstract Locale getLocale();
/**
* @since 2.6
*/
public abstract TimeZone getTimeZone();
/**
* @since 2.7
*/
public abstract JsonFormat.Value getDefaultPropertyFormat(Class<?> baseType);
/*
/**********************************************************
/* Generic attributes (2.3+)
/**********************************************************
*/
/**
* Method for accessing attributes available in this context.
* Per-call attributes have highest precedence; attributes set
* via {@link ObjectReader} or {@link ObjectWriter} have lower
* precedence.
*
* @param key Key of the attribute to get
* @return Value of the attribute, if any; null otherwise
*
* @since 2.3
*/
public abstract Object getAttribute(Object key);
/**
* Method for setting per-call value of given attribute.
* This will override any previously defined value for the
* attribute within this context.
*
* @param key Key of the attribute to set
* @param value Value to set attribute to
*
* @return This context object, to allow chaining
*
* @since 2.3
*/
public abstract DatabindContext setAttribute(Object key, Object value);
/*
/**********************************************************
/* Type instantiation/resolution
/**********************************************************
*/
/**
* Convenience method for constructing {@link JavaType} for given JDK
* type (usually {@link java.lang.Class})
*/
public JavaType constructType(Type type) {
if (type == null) {
return null;
}
return getTypeFactory().constructType(type);
}
/**
* Convenience method for constructing subtypes, retaining generic
* type parameter (if any)
*/
public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass) {
// simple optimization to avoid costly introspection if type-erased type does NOT differ
if (baseType.getRawClass() == subclass) {
return baseType;
}
return getConfig().constructSpecializedType(baseType, subclass);
}
/**
* Lookup method called when code needs to resolve class name from input;
* usually simple lookup
*
* @since 2.9
*/
public JavaType resolveSubType(JavaType baseType, String subClass)
throws JsonMappingException
{
// 30-Jan-2010, tatu: Most ids are basic class names; so let's first
// check if any generics info is added; and only then ask factory
// to do translation when necessary
if (subClass.indexOf('<') > 0) {
// note: may want to try combining with specialization (esp for EnumMap)?
return getTypeFactory().constructFromCanonical(subClass);
}
Class<?> cls;
try {
cls = getTypeFactory().findClass(subClass);
} catch (ClassNotFoundException e) { // let caller handle this problem
return null;
} catch (Exception e) {
throw invalidTypeIdException(baseType, subClass, String.format(
"problem: (%s) %s",
e.getClass().getName(), e.getMessage()));
}
if (baseType.isTypeOrSuperTypeOf(cls)) {
return getTypeFactory().constructSpecializedType(baseType, cls);
}
throw invalidTypeIdException(baseType, subClass, "Not a subtype");
}
/**
* Helper method for constructing exception to indicate that given type id
* could not be resolved to a valid subtype of specified base type.
* Most commonly called during polymorphic deserialization.
*<p>
* Note that most of the time this method should NOT be called directly: instead,
* method <code>handleUnknownTypeId()</code> should be called which will call this method
* if necessary.
*
* @since 2.9
*/
protected abstract JsonMappingException invalidTypeIdException(JavaType baseType, String typeId,
String extraDesc);
public abstract TypeFactory getTypeFactory();
/*
/**********************************************************
/* Helper object construction
/**********************************************************
*/
public ObjectIdGenerator<?> objectIdGeneratorInstance(Annotated annotated,
ObjectIdInfo objectIdInfo)
throws JsonMappingException
{
Class<?> implClass = objectIdInfo.getGeneratorType();
final MapperConfig<?> config = getConfig();
HandlerInstantiator hi = config.getHandlerInstantiator();
ObjectIdGenerator<?> gen = (hi == null) ? null : hi.objectIdGeneratorInstance(config, annotated, implClass);
if (gen == null) {
gen = (ObjectIdGenerator<?>) ClassUtil.createInstance(implClass,
config.canOverrideAccessModifiers());
}
return gen.forScope(objectIdInfo.getScope());
}
public ObjectIdResolver objectIdResolverInstance(Annotated annotated, ObjectIdInfo objectIdInfo)
{
Class<? extends ObjectIdResolver> implClass = objectIdInfo.getResolverType();
final MapperConfig<?> config = getConfig();
HandlerInstantiator hi = config.getHandlerInstantiator();
ObjectIdResolver resolver = (hi == null) ? null : hi.resolverIdGeneratorInstance(config, annotated, implClass);
if (resolver == null) {
resolver = ClassUtil.createInstance(implClass, config.canOverrideAccessModifiers());
}
return resolver;
}
/**
* Helper method to use to construct a {@link Converter}, given a definition
* that may be either actual converter instance, or Class for instantiating one.
*
* @since 2.2
*/
@SuppressWarnings("unchecked")
public Converter<Object,Object> converterInstance(Annotated annotated,
Object converterDef)
throws JsonMappingException
{
if (converterDef == null) {
return null;
}
if (converterDef instanceof Converter<?,?>) {
return (Converter<Object,Object>) converterDef;
}
if (!(converterDef instanceof Class)) {
throw new IllegalStateException("AnnotationIntrospector returned Converter definition of type "
+converterDef.getClass().getName()+"; expected type Converter or Class<Converter> instead");
}
Class<?> converterClass = (Class<?>)converterDef;
// there are some known "no class" markers to consider too:
if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) {
return null;
}
if (!Converter.class.isAssignableFrom(converterClass)) {
throw new IllegalStateException("AnnotationIntrospector returned Class "
+converterClass.getName()+"; expected Class<Converter>");
}
final MapperConfig<?> config = getConfig();
HandlerInstantiator hi = config.getHandlerInstantiator();
Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(config, annotated, converterClass);
if (conv == null) {
conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,
config.canOverrideAccessModifiers());
}
return (Converter<Object,Object>) conv;
}
/*
/**********************************************************
/* Error reporting
/**********************************************************
*/
/**
* Helper method called to indicate a generic problem that stems from type
* definition(s), not input data, or input/output state; typically this
* means throwing a {@link com.fasterxml.jackson.databind.exc.InvalidDefinitionException}.
*
* @since 2.9
*/
public abstract <T> T reportBadDefinition(JavaType type, String msg) throws JsonMappingException;
/**
* @since 2.9
*/
public <T> T reportBadDefinition(Class<?> type, String msg) throws JsonMappingException {
return reportBadDefinition(constructType(type), msg);
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
/**
* @since 2.9
*/
protected final String _format(String msg, Object... msgArgs) {
if (msgArgs.length > 0) {
return String.format(msg, msgArgs);
}
return msg;
}
/**
* @since 2.9
*/
protected final String _truncate(String desc) {
if (desc == null) {
return "";
}
if (desc.length() <= MAX_ERROR_STR_LEN) {
return desc;
}
return desc.substring(0, MAX_ERROR_STR_LEN) + "]...[" + desc.substring(desc.length() - MAX_ERROR_STR_LEN);
}
/**
* @since 2.9
*/
protected String _quotedString(String desc) {
if (desc == null) {
return "[N/A]";
}
// !!! should we quote it? (in case there are control chars, linefeeds)
return String.format("\"%s\"", _truncate(desc));
}
/**
* @since 2.9
*/
protected String _colonConcat(String msgBase, String extra) {
if (extra == null) {
return msgBase;
}
return msgBase + ": " + extra;
}
/**
* @since 2.9
*/
protected String _calcName(Class<?> cls) {
if (cls.isArray()) {
return _calcName(cls.getComponentType())+"[]";
}
return cls.getName();
}
/**
* @since 2.9
*/
protected String _desc(String desc) {
if (desc == null) {
return "[N/A]";
}
// !!! should we quote it? (in case there are control chars, linefeeds)
return _truncate(desc);
}
}