package com.fasterxml.jackson.databind.deser.std; import java.io.IOException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.deser.CreatorProperty; import com.fasterxml.jackson.databind.deser.SettableBeanProperty; import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; /** * Default {@link ValueInstantiator} implementation, which supports * Creator methods that can be indicated by standard Jackson * annotations. */ public class StdValueInstantiator extends ValueInstantiator implements java.io.Serializable { private static final long serialVersionUID = 1L; /** * Type of values that are instantiated; used * for error reporting purposes. */ protected final String _valueTypeDesc; /** * Are we allowed to convert empty Strings to null objects? */ protected final boolean _cfgEmptyStringsAsObjects; // // // Default (no-args) construction /** * Default (no-argument) constructor to use for instantiation * (with {@link #createUsingDefault}) */ protected AnnotatedWithParams _defaultCreator; // // // With-args (property-based) construction protected AnnotatedWithParams _withArgsCreator; protected CreatorProperty[] _constructorArguments; // // // Delegate construction protected JavaType _delegateType; protected AnnotatedWithParams _delegateCreator; protected CreatorProperty[] _delegateArguments; // // // Scalar construction protected AnnotatedWithParams _fromStringCreator; protected AnnotatedWithParams _fromIntCreator; protected AnnotatedWithParams _fromLongCreator; protected AnnotatedWithParams _fromDoubleCreator; protected AnnotatedWithParams _fromBooleanCreator; /* /********************************************************** /* Life-cycle /********************************************************** */ public StdValueInstantiator(DeserializationConfig config, Class<?> valueType) { _cfgEmptyStringsAsObjects = (config == null) ? false : config.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.getName(); } public StdValueInstantiator(DeserializationConfig config, JavaType valueType) { _cfgEmptyStringsAsObjects = (config == null) ? false : config.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.toString(); } /** * Copy-constructor that sub-classes can use when creating new instances * by fluent-style construction */ protected StdValueInstantiator(StdValueInstantiator src) { _cfgEmptyStringsAsObjects = src._cfgEmptyStringsAsObjects; _valueTypeDesc = src._valueTypeDesc; _defaultCreator = src._defaultCreator; _constructorArguments = src._constructorArguments; _withArgsCreator = src._withArgsCreator; _delegateType = src._delegateType; _delegateCreator = src._delegateCreator; _delegateArguments = src._delegateArguments; _fromStringCreator = src._fromStringCreator; _fromIntCreator = src._fromIntCreator; _fromLongCreator = src._fromLongCreator; _fromDoubleCreator = src._fromDoubleCreator; _fromBooleanCreator = src._fromBooleanCreator; } /** * Method for setting properties related to instantiating values * from JSON Object. We will choose basically only one approach (out of possible * three), and clear other properties */ public void configureFromObjectSettings(AnnotatedWithParams defaultCreator, AnnotatedWithParams delegateCreator, JavaType delegateType, CreatorProperty[] delegateArgs, AnnotatedWithParams withArgsCreator, CreatorProperty[] constructorArgs) { _defaultCreator = defaultCreator; _delegateCreator = delegateCreator; _delegateType = delegateType; _delegateArguments = delegateArgs; _withArgsCreator = withArgsCreator; _constructorArguments = constructorArgs; } public void configureFromStringCreator(AnnotatedWithParams creator) { _fromStringCreator = creator; } public void configureFromIntCreator(AnnotatedWithParams creator) { _fromIntCreator = creator; } public void configureFromLongCreator(AnnotatedWithParams creator) { _fromLongCreator = creator; } public void configureFromDoubleCreator(AnnotatedWithParams creator) { _fromDoubleCreator = creator; } public void configureFromBooleanCreator(AnnotatedWithParams creator) { _fromBooleanCreator = creator; } /* /********************************************************** /* Public API implementation; metadata /********************************************************** */ @Override public String getValueTypeDesc() { return _valueTypeDesc; } @Override public boolean canCreateFromString() { return (_fromStringCreator != null); } @Override public boolean canCreateFromInt() { return (_fromIntCreator != null); } @Override public boolean canCreateFromLong() { return (_fromLongCreator != null); } @Override public boolean canCreateFromDouble() { return (_fromDoubleCreator != null); } @Override public boolean canCreateFromBoolean() { return (_fromBooleanCreator != null); } @Override public boolean canCreateUsingDefault() { return (_defaultCreator != null); } @Override public boolean canCreateUsingDelegate() { return _delegateType != null; } @Override public boolean canCreateFromObjectWith() { return (_withArgsCreator != null); } @Override public JavaType getDelegateType(DeserializationConfig config) { return _delegateType; } @Override public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) { return _constructorArguments; } /* /********************************************************** /* Public API implementation; instantiation from JSON Object /********************************************************** */ @Override public Object createUsingDefault(DeserializationContext ctxt) throws IOException, JsonProcessingException { if (_defaultCreator == null) { // sanity-check; caller should check throw new IllegalStateException("No default constructor for "+getValueTypeDesc()); } try { return _defaultCreator.call(); } catch (ExceptionInInitializerError e) { throw wrapException(e); } catch (Exception e) { throw wrapException(e); } } @Override public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws IOException, JsonProcessingException { if (_withArgsCreator == null) { // sanity-check; caller should check throw new IllegalStateException("No with-args constructor for "+getValueTypeDesc()); } try { return _withArgsCreator.call(args); } catch (ExceptionInInitializerError e) { throw wrapException(e); } catch (Exception e) { throw wrapException(e); } } @Override public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException, JsonProcessingException { if (_delegateCreator == null) { // sanity-check; caller should check throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc()); } try { // First simple case: just delegate, no injectables if (_delegateArguments == null) { return _delegateCreator.call1(delegate); } // And then the case with at least one injectable... final int len = _delegateArguments.length; Object[] args = new Object[len]; for (int i = 0; i < len; ++i) { CreatorProperty prop = _delegateArguments[i]; if (prop == null) { // delegate args[i] = delegate; } else { // nope, injectable: args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null); } } // and then try calling with full set of arguments return _delegateCreator.call(args); } catch (ExceptionInInitializerError e) { throw wrapException(e); } catch (Exception e) { throw wrapException(e); } } /* /********************************************************** /* Public API implementation; instantiation from JSON scalars /********************************************************** */ @Override public Object createFromString(DeserializationContext ctxt, String value) throws IOException, JsonProcessingException { if (_fromStringCreator != null) { try { return _fromStringCreator.call1(value); } catch (Exception e) { throw wrapException(e); } catch (ExceptionInInitializerError e) { throw wrapException(e); } } return _createFromStringFallbacks(ctxt, value); } @Override public Object createFromInt(DeserializationContext ctxt, int value) throws IOException, JsonProcessingException { try { // First: "native" int methods work best: if (_fromIntCreator != null) { return _fromIntCreator.call1(Integer.valueOf(value)); } // but if not, can do widening conversion if (_fromLongCreator != null) { return _fromLongCreator.call1(Long.valueOf(value)); } } catch (Exception e) { throw wrapException(e); } catch (ExceptionInInitializerError e) { throw wrapException(e); } throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc() +" from Integral number; no single-int-arg constructor/factory method"); } @Override public Object createFromLong(DeserializationContext ctxt, long value) throws IOException, JsonProcessingException { try { if (_fromLongCreator != null) { return _fromLongCreator.call1(Long.valueOf(value)); } } catch (Exception e) { throw wrapException(e); } catch (ExceptionInInitializerError e) { throw wrapException(e); } throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc() +" from Long integral number; no single-long-arg constructor/factory method"); } @Override public Object createFromDouble(DeserializationContext ctxt, double value) throws IOException, JsonProcessingException { try { if (_fromDoubleCreator != null) { return _fromDoubleCreator.call1(Double.valueOf(value)); } } catch (Exception e) { throw wrapException(e); } catch (ExceptionInInitializerError e) { throw wrapException(e); } throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc() +" from Floating-point number; no one-double/Double-arg constructor/factory method"); } @Override public Object createFromBoolean(DeserializationContext ctxt, boolean value) throws IOException, JsonProcessingException { try { if (_fromBooleanCreator != null) { return _fromBooleanCreator.call1(Boolean.valueOf(value)); } } catch (Exception e) { throw wrapException(e); } catch (ExceptionInInitializerError e) { throw wrapException(e); } throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc() +" from Boolean value; no single-boolean/Boolean-arg constructor/factory method"); } /* /********************************************************** /* Extended API: configuration mutators, accessors /********************************************************** */ @Override public AnnotatedWithParams getDelegateCreator() { return _delegateCreator; } @Override public AnnotatedWithParams getDefaultCreator() { return _defaultCreator; } @Override public AnnotatedWithParams getWithArgsCreator() { return _withArgsCreator; } /* /********************************************************** /* Internal methods /********************************************************** */ protected Object _createFromStringFallbacks(DeserializationContext ctxt, String value) throws IOException, JsonProcessingException { /* 28-Sep-2011, tatu: Ok this is not clean at all; but since there are legacy * systems that expect conversions in some cases, let's just add a minimal * patch (note: same could conceivably be used for numbers too). */ if (_fromBooleanCreator != null) { String str = value.trim(); if ("true".equals(str)) { return createFromBoolean(ctxt, true); } if ("false".equals(str)) { return createFromBoolean(ctxt, false); } } // and finally, empty Strings might be accepted as null Object... if (_cfgEmptyStringsAsObjects && value.length() == 0) { return null; } throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc() +" from String value; no single-String constructor/factory method"); } protected JsonMappingException wrapException(Throwable t) { while (t.getCause() != null) { t = t.getCause(); } if (t instanceof JsonMappingException) { return (JsonMappingException) t; } return new JsonMappingException("Instantiation of "+getValueTypeDesc()+" value failed: "+t.getMessage(), t); } }