Package flex.messaging.io.amf.translator.decoder

Source Code of flex.messaging.io.amf.translator.decoder.DecoderFactory

/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
*  Copyright 2002 - 2007 Adobe Systems Incorporated
*  All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any.  The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated
* and its suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package flex.messaging.io.amf.translator.decoder;

import flex.messaging.io.TypeMarshallingContext;
import flex.messaging.io.amf.ASObject;
import flex.messaging.io.amf.translator.TranslationException;

import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
* Utility class that tries to find an ActionScriptDecoder that will be able
* to convert the encoded object into an instance of the desired class.
*
* @see ActionScriptDecoder
*
* @exclude
*/
public class DecoderFactory
{
    // The identity transform
    private static final NativeDecoder nativeDecoder = new NativeDecoder();

    // Special null transform, always returns null
    private static final NullDecoder nullDecoder = new NullDecoder();

    // Simple types (do not have the concept of a creating a placeholder or 'shell')
    private static final NumberDecoder numberDecoder = new NumberDecoder();
    private static final StringDecoder stringDecoder = new StringDecoder();
    private static final BooleanDecoder booleanDecoder = new BooleanDecoder();
    private static final CharacterDecoder characterDecoder = new CharacterDecoder();

    // Historically dates are simple types, but they are now considered complex
    // via AMF 3 (though they can not have empty placeholders or 'shells')
    private static final DateDecoder dateDecoder = new DateDecoder();
    private static final CalendarDecoder calendarDecoder = new CalendarDecoder();

    // Complex types (can also be used to create empty placeholders to be populated at decode time)
    private static final ArrayDecoder arrayDecoder = new ArrayDecoder();
    private static final MapDecoder mapDecoder = new MapDecoder();
    private static final CollectionDecoder collectionDecoder = new CollectionDecoder();
    private static final TypedObjectDecoder typedObjectDecoder = new TypedObjectDecoder();

    // If we require references to be tracked and restored, we use deep, recursive decoders
    // however these are very expensive in terms of processing time
    private static final ArrayDecoder deepArrayDecoder = new ReferenceAwareArrayDecoder();
    private static final MapDecoder deepMapDecoder = new ReferenceAwareMapDecoder();
    private static final CollectionDecoder deepCollectionDecoder = new ReferenceAwareCollectionDecoder();
    private static final TypedObjectDecoder deepTypedObjectDecoder = new ReferenceAwareTypedObjectDecoder();


    /**
     * A simple method for obtaining a placeholder or 'shell' object that will be subsequently
     * populated by potentially some other deserializer. Decoders contain special logic to implement well
     * known Collections interfaces such as java.util.Map, java.util.Set, etc
     * and also contain utilities to create native Arrays, so this functionality has been
     * made available to other classes besides decoders.
     *
     * @param desiredClass
     * @return The <tt>ActionScriptDecoder</tt> to use for instances of the desired class.
     */
    public static ActionScriptDecoder getDecoderForShell(Class desiredClass)
    {
        if (desiredClass == null)
            return nullDecoder;

        if (Collection.class.isAssignableFrom(desiredClass))
            return collectionDecoder;

        if (Map.class.isAssignableFrom(desiredClass))
            return mapDecoder;

        if (desiredClass.isArray())
            return arrayDecoder;

        return nativeDecoder;
    }

    /**
     * This is a faster implementation than getReferenceAwareDecoder as it doesn't track
     * or care about restoring references in the event that an instance was converted to
     * a new type. Since the all MessageDeserializes now convert directly to strongly
     * typed instances it is less likely that references will need to be tracked.
     *
     * A case where a reference may have needed to be tracked would be that a Class has
     * several properties of a subtype of Object[] (i.e. Array), Collection or Map but these
     * properties were not of the default type returned by the MessageDeserializer, that
     * is ArrayList or HashMap, and that each property had the potential to point to the same
     * instance. If we didn't track the reference, then the conversion to the subtype
     * (say HashMap -> TreeMap) would effectively clone these instances.
     *
     * @param encodedObject
     * @param desiredClass
     * @return The <tt>ActionScriptDecoder</tt> to use for instances of the desired class.
     */
    public static ActionScriptDecoder getDecoder(Object encodedObject, Class desiredClass)
    {
        if (encodedObject != null)
        {
            // If we already have a suitable instance, return immediately!
            if (desiredClass.isAssignableFrom(encodedObject.getClass()))
                return nativeDecoder;

            if (String.class.equals(desiredClass))
                return stringDecoder;

            // We check Number and Boolean here as well as the encodedObejct == null case
            // as they're very common property types...
            if (isNumber(desiredClass))
                return numberDecoder;

            if (isBoolean(desiredClass))
                return booleanDecoder;

            if (Collection.class.isAssignableFrom(desiredClass))
                return collectionDecoder;

            if (Map.class.isAssignableFrom(desiredClass))
                return mapDecoder;

            if (desiredClass.isArray())
                return arrayDecoder;

            // Special Case - we have a typed ASObject and we're expecting it to
            // be converted into a new class... this would be an usual situation
            // for Mistral, however, since we now create strongly typed instances
            // from a stream
            if (isTypedObject(encodedObject))
                return typedObjectDecoder;

            if (Date.class.isAssignableFrom(desiredClass))
                return dateDecoder;

            if (Calendar.class.isAssignableFrom(desiredClass))
                return calendarDecoder;
        }

        // Null may have been sent to a primitive Java type, in which case
        // we create a default value, such as new Integer(0) for int rather
        // than create a null Integer() instance...
        if (isNumber(desiredClass))
            return numberDecoder;

        if (isBoolean(desiredClass))
            return booleanDecoder;

        if (isCharacter(desiredClass))
            return characterDecoder;

        if (encodedObject == null)
            return nullDecoder;

        DecoderFactory.invalidType(encodedObject, desiredClass);

        // Never reached...
        return nativeDecoder;
    }

    /**
     * A considerably slower entry point for decoders as it both changes the
     * assumptions we can make about type translation and also keeps track of
     * a lot of information when a complex type is converted.
     *
     * @param encodedObject
     * @param desiredClass
     * @return The <tt>ActionScriptDecoder</tt> to use for instances of the desired class.
     */
    public static ActionScriptDecoder getReferenceAwareDecoder(Object encodedObject, Class desiredClass)
    {
        if (encodedObject != null)
        {
            if (String.class.equals(desiredClass))
                return stringDecoder;

            // We check Number and Boolean here as well as the encodedObejct == null case
            // as they're very common property types...
            if (isNumber(desiredClass))
                return numberDecoder;

            if (isBoolean(desiredClass))
                return booleanDecoder;

            if (Collection.class.isAssignableFrom(desiredClass))
                return deepCollectionDecoder;

            if (Map.class.isAssignableFrom(desiredClass))
                return deepMapDecoder;

            if (desiredClass.isArray())
                return deepArrayDecoder;

            // Special Case - we have a typed ASObject and we're expecting it to
            // be converted into a new class... this would be an usual situation
            // for Mistral, however, since we now create strongly typed instances
            // from a stream.
            if (isTypedObject(encodedObject))
                return deepTypedObjectDecoder;

            if (Date.class.isAssignableFrom(desiredClass))
                return dateDecoder;

            if (Calendar.class.isAssignableFrom(desiredClass))
                return calendarDecoder;

            if (isCharacter(desiredClass))
                return characterDecoder;

            // Last resort, just try and return the object undecoded if it's the right type
            // We do this last because at this stage if it is a complex object we won't catch
            // any Typed Object translations for properties on this type...
            if (desiredClass.isAssignableFrom(encodedObject.getClass()))
                return nativeDecoder;
        }

        // Null may have been sent to a primitive Java type, in which case
        // we create a default value, such as new Integer(0) for int rather than create
        // a null Integer() instance...
        if (isNumber(desiredClass))
            return numberDecoder;

        if (isBoolean(desiredClass))
            return booleanDecoder;

        if (isCharacter(desiredClass))
            return characterDecoder;

        if (encodedObject == null)
            return nullDecoder;

        DecoderFactory.invalidType(encodedObject, desiredClass);

        // Never reached...
        return nativeDecoder;
    }

    public static boolean isNumber(Class desiredClass)
    {
        boolean isNum = false;

        if (desiredClass.isPrimitive())
        {
            if (desiredClass.equals(Integer.TYPE)
                    || desiredClass.equals(Double.TYPE)
                    || desiredClass.equals(Long.TYPE)
                    || desiredClass.equals(Float.TYPE)
                    || desiredClass.equals(Short.TYPE)
                    || desiredClass.equals(Byte.TYPE))
            {
                isNum = true;
            }
        }
        else if (Number.class.isAssignableFrom(desiredClass))
        {
            isNum = true;
        }

        return isNum;
    }

    public static boolean isCharacter(Class desiredClass)
    {
        boolean isChar = false;

        if (desiredClass.isPrimitive() && desiredClass.equals(Character.TYPE))
        {
            isChar = true;
        }
        else if (desiredClass.equals(Character.class))
        {
            isChar = true;
        }

        return isChar;
    }

    public static boolean isBoolean(Class desiredClass)
    {
        boolean isBool = false;

        if (desiredClass.isPrimitive() && desiredClass.equals(Boolean.TYPE))
        {
            isBool = true;
        }
        else if (desiredClass.equals(Boolean.class))
        {
            isBool = true;
        }

        return isBool;
    }

    public static boolean isCharArray(Class desiredClass)
    {
        boolean isCharArray = false;

        if (desiredClass.isArray())
        {
            Class type = desiredClass.getComponentType();
            if (type != null && type.equals(Character.TYPE))
            {
                isCharArray = true;
            }
        }

        return isCharArray;
    }

    public static boolean isTypedObject(Object encodedObject)
    {
        boolean typed = false;

        if (encodedObject instanceof ASObject)
        {
            typed = TypeMarshallingContext.getType((ASObject)encodedObject) != null;
        }

        return typed;
    }

    public static void invalidType(Object object, Class desiredClass)
    {
        String inputType = null;

        if (object != null)
        {
            inputType = object.getClass().getName();
        }

        StringBuffer message = new StringBuffer("Cannot convert ");
        if (inputType != null)
        {
            message.append("type ").append(inputType).append(" ");
        }

        if (object != null && (object instanceof String
                || object instanceof Number
                || object instanceof Boolean
                || object instanceof Date))
        {
            message.append("with value '").append(object.toString()).append("' ");
        }
        else if (object instanceof ASObject)
        {
            ASObject aso = (ASObject)object;
            message.append("with remote type specified as '").append(aso.getType()).append("' ");
        }

        message.append("to an instance of ").append(desiredClass.toString());

        TranslationException ex = new TranslationException(message.toString());
        ex.setCode("Client.Message.Deserialize.InvalidType");
        throw ex;
    }
}
TOP

Related Classes of flex.messaging.io.amf.translator.decoder.DecoderFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.