SettableBeanProperty.java :  » Remote » android-xbmcremote » org » codehaus » jackson » map » deser » Android Open Source

Android Open Source » Remote » android xbmcremote 
android xbmcremote » org » codehaus » jackson » map » deser » SettableBeanProperty.java
package org.codehaus.jackson.map.deser;

import java.io.IOException;
import java.lang.reflect.*;

import org.codehaus.jackson.*;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.util.InternCache;

/**
 * Base class for settable properties of a bean: contains
 * both type and name definitions, and reflection-based set functionality.
 * Concrete sub-classes implement details, so that both field- and
 * setter-backed properties can be handled
 */
public abstract class SettableBeanProperty
{
    /**
     * Logical name of the property (often but not always derived
     * from the setter method name)
     */
    final String _propName;

    final JavaType _type;

    protected JsonDeserializer<Object> _valueDeserializer;

    /**
     * If value will contain type information (to support
     * polymorphic handling), this is the type deserializer
     * used to handle type resolution.
     */
    protected TypeDeserializer _valueTypeDeserializer;
    
    /**
     * Value to be used when 'null' literal is encountered in Json.
     * For most types simply Java null, but for primitive types must
     * be a non-null value (like Integer.valueOf(0) for int).
     */
    protected Object _nullValue;

    /*
    /******************************************************
    /* Life-cycle (construct & configure)
    /******************************************************
     */

    protected SettableBeanProperty(String propName, JavaType type,
            TypeDeserializer typeDeser)
    {
        /* 09-Jan-2009, tatu: Intern()ing makes sense since Jackson parsed
         *   field names are (usually) interned too, hence lookups will be faster.
         */
        // 23-Oct-2009, tatu: should this be disabled wrt [JACKSON-180]?
        if (propName == null || propName.length() == 0) {
            _propName = "";
        } else {
            _propName = InternCache.instance.intern(propName);
        }
        _type = type;
        _valueTypeDeserializer = typeDeser;
    }

    public void setValueDeserializer(JsonDeserializer<Object> deser)
    {
        if (_valueDeserializer != null) { // sanity check
            throw new IllegalStateException("Already had assigned deserializer for property '"+_propName+"' (class "+getDeclaringClass().getName()+")");
        }
        _valueDeserializer = deser;
        _nullValue = _valueDeserializer.getNullValue();
    }

    protected abstract Class<?> getDeclaringClass();

    /*
    /******************************************************
    /* Accessors
    /******************************************************
     */

    public String getPropertyName() { return _propName; }
    public JavaType getType() { return _type; }

    public boolean hasValueDeserializer() { return (_valueDeserializer != null); }

    /**
     * Method to use for accessing index of the property (related to
     * other properties in the same context); currently only applicable
     * to "Creator properties".
     *<p>
     * Base implementation returns -1 to indicate that no index exists
     * for the property.
     */
    public int getCreatorIndex() { return -1; }

    /*
    /******************************************************
    /* Public API
    /******************************************************
     */

    /**
     * Method called to deserialize appropriate value, given parser (and
     * context), and set it using appropriate mechanism.
     * Pre-condition is that passed parser must point to the first token
     * that should be consumed to produce the value (the only value for
     * scalars, multiple for Objects and Arrays).
     */
    public abstract void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
                                           Object instance)
        throws IOException, JsonProcessingException;

    public abstract void set(Object instance, Object value)
        throws IOException;

    /**
     * This method is needed by some specialized bean deserializers,
     * and also called by some {@link #deserializeAndSet} implementations.
     *<p>
     * Pre-condition is that passed parser must point to the first token
     * that should be consumed to produce the value (the only value for
     * scalars, multiple for Objects and Arrays).
     */
    public final Object deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException
    {
        JsonToken t = jp.getCurrentToken();
        if (t == JsonToken.VALUE_NULL) {
            return _nullValue;
        }
        if (_valueTypeDeserializer != null) {
            return _valueTypeDeserializer.deserializeTypedFromObject(jp, ctxt);
        }
        return _valueDeserializer.deserialize(jp, ctxt);
    }

    /*
    ////////////////////////////////////////////////////////
    // Helper methods
    ////////////////////////////////////////////////////////
     */

    /**
     * Method that takes in exception of any type, and casts or wraps it
     * to an IOException or its subclass.
     */
    protected void _throwAsIOE(Exception e, Object value)
        throws IOException
    {
        if (e instanceof IllegalArgumentException) {
            String actType = (value == null) ? "[NULL]" : value.getClass().getName();
            StringBuilder msg = new StringBuilder("Problem deserializing property '").append(getPropertyName());
            msg.append("' (expected type: ").append(getType());
            msg.append("; actual type: ").append(actType).append(")");
            String origMsg = e.getMessage();
            if (origMsg != null) {
                msg.append(", problem: ").append(origMsg);
            } else {
                msg.append(" (no error message provided)");
            }
            throw new JsonMappingException(msg.toString(), null, e);
        }
        _throwAsIOE(e);
    }

    protected IOException _throwAsIOE(Exception e)
        throws IOException
    {
        if (e instanceof IOException) {
            throw (IOException) e;
        }
        if (e instanceof RuntimeException) {
            throw (RuntimeException) e;
        }
        // let's wrap the innermost problem
        Throwable th = e;
        while (th.getCause() != null) {
            th = th.getCause();
        }
        throw new JsonMappingException(th.getMessage(), null, th);
    }
    
    @Override public String toString() { return "[property '"+_propName+"']"; }

    /*
    ////////////////////////////////////////////////////////
    // Impl classes
    ////////////////////////////////////////////////////////
     */

    /**
     * This concrete sub-class implements property that is set
     * using regular "setter" method.
     */
    public final static class MethodProperty
        extends SettableBeanProperty
    {
        /**
         * Setter method for modifying property value; used for
         * "regular" method-accessible properties.
         */
        protected final Method _setter;

        public MethodProperty(String propName, JavaType type, TypeDeserializer typeDeser,
                Method setter)
        {
            super(propName, type, typeDeser);
            _setter = setter;
        }

        protected Class<?> getDeclaringClass()
        {
            return _setter.getDeclaringClass();
        }

        public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
                                      Object instance)
            throws IOException, JsonProcessingException
        {
            set(instance, deserialize(jp, ctxt));
        }

        public final void set(Object instance, Object value)
            throws IOException
        {
            try {
                _setter.invoke(instance, value);
            } catch (Exception e) {
                _throwAsIOE(e, value);
            }
        }
    }

    /**
     * This concrete sub-class implements Collection or Map property that is
     * indirectly by getting the property value and directly modifying it.
     */
    public final static class SetterlessProperty
        extends SettableBeanProperty
    {
        /**
         * Get method for accessing property value used to access property
         * (of Collection or Map type) to modify.
         */
        protected final Method _getter;

        public SetterlessProperty(String propName, JavaType type, TypeDeserializer typeDeser,
                                  Method getter)
        {
            super(propName, type, typeDeser);
            _getter = getter;
        }

        protected Class<?> getDeclaringClass()
        {
            return _getter.getDeclaringClass();
        }
        
        public final void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
                                            Object instance)
            throws IOException, JsonProcessingException
        {
            JsonToken t = jp.getCurrentToken();
            if (t == JsonToken.VALUE_NULL) {
                /* Hmmh. Is this a problem? We won't be setting anything, so it's
                 * equivalent of empty Collection/Map in this case
                 */
                return;
            }

            // Ok: then, need to fetch Collection/Map to modify:
            Object toModify;
            try {
                toModify = _getter.invoke(instance);
            } catch (Exception e) {
                _throwAsIOE(e);
                return; // never gets here
            }
            /* Note: null won't work, since we can't then inject anything
             * in. At least that's not good in common case. However,
             * theoretically the case where we get Json null might
             * be compatible. If so, implementation could be changed.
             */
            if (toModify == null) {
                throw new JsonMappingException("Problem deserializing 'setterless' property '"+getPropertyName()+"': get method returned null");
            }
            _valueDeserializer.deserialize(jp, ctxt, toModify);
        }

        public final void set(Object instance, Object value)
            throws IOException
        {
            throw new UnsupportedOperationException("Should never call 'set' on setterless property");
        }
    }

    /**
     * This concrete sub-class implements property that is set
     * directly assigning to a Field.
     */
    public final static class FieldProperty
        extends SettableBeanProperty
    {
        /**
         * Actual field to set when deserializing this property.
         */
        protected final Field _field;

        public FieldProperty(String propName, JavaType type, TypeDeserializer typeDeser,
                             Field f)
        {
            super(propName, type, typeDeser);
            _field = f;
        }

        protected Class<?> getDeclaringClass()
        {
            return _field.getDeclaringClass();
        }

        public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
                                      Object instance)
            throws IOException, JsonProcessingException
        {
            set(instance, deserialize(jp, ctxt));
        }

        public final void set(Object instance, Object value)
            throws IOException
        {
            try {
                _field.set(instance, value);
            } catch (Exception e) {
                _throwAsIOE(e, value);
            }
        }
    }

    /**
     * This concrete sub-class implements property that is passed
     * via Creator (constructor or static factory method).
     */
    public final static class CreatorProperty
        extends SettableBeanProperty
    {
        final Class<?> _declaringClass;

        /**
         * Index of the property
         */
        final int _index;

        public CreatorProperty(String propName, JavaType type,
                               TypeDeserializer typeDeser,
                               Class<?> declaringClass, int index)
        {
            super(propName, type, typeDeser);
            _declaringClass = declaringClass;
            _index = index;
        }

        /**
         * Method to use for accessing index of the property (related to
         * other properties in the same context); currently only applicable
         * to "Creator properties".
         *<p>
         * Base implementation returns -1 to indicate that no index exists
         * for the property.
         */
        public int getCreatorIndex() { return _index; }
        
        protected Class<?> getDeclaringClass()
        {
            return _declaringClass;
        }

        public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
                                      Object instance)
            throws IOException, JsonProcessingException
        {
            set(instance, deserialize(jp, ctxt));
        }

        public void set(Object instance, Object value)
            throws IOException
        {
            /* Hmmmh. Should we return quietly (NOP), or error?
             * For now, let's just bail out without fuss.
             */
            //throw new IllegalStateException("Method should never be called on a "+getClass().getName());
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.