Package flex.messaging.io.amf

Source Code of flex.messaging.io.amf.Amf0Output

/*************************************************************************
*
* 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;

import flex.messaging.MessageException;

import flex.messaging.io.ArrayCollection;
import flex.messaging.io.PagedRowSet;
import flex.messaging.io.PropertyProxy;
import flex.messaging.io.PropertyProxyRegistry;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.SerializationDescriptor;
import flex.messaging.io.StatusInfoProxy;

import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import javax.sql.RowSet;

/**
* An Amf0 output object.
* @exclude
*/
public class Amf0Output extends AbstractAmfOutput implements AmfTypes
{
    /**
     * 3-byte marker for object end; used for faster serialization
     * than a combination of writeUTF("") and writeByte(kObjectEndType).
     * @exclude
     */
    public static final byte[] OBJECT_END_MARKER = {0, 0, kObjectEndType};

    /**
     * A mapping of object instances to their serialization numbers
     * for storing object references on the stream.
     * @exclude
     */
    protected IdentityHashMap serializedObjects;

    /**
     * Number of serialized objects.
     * @exclude
     */
    protected int serializedObjectCount = 0;

    /**
     * AVM+ Encoding.
     * @exclude
     */
    protected boolean avmPlus;

    /**
     * @exclude
     */
    protected Amf3Output avmPlusOutput;

    /**
     * Construct a serializer without connecting it to an output stream.
     * @param context the context to use
     */
    public Amf0Output(SerializationContext context)
    {
        super(context);
        context.supportDatesByReference = false;

        serializedObjects = new IdentityHashMap(64);
    }

    /**
     * Set to true if the AMF0 stream should switch to use AMF3 on encountering
     * the first complex Object during serialization.
     *
     * @param a Whether the client is running in AVMPlus and can handle AMF3 encoding.
     */
    public void setAvmPlus(boolean a)
    {
        avmPlus = a;
    }

    /**
     * Reset all object reference information allowing the class to be used to
     * write a "new" data structure.
     */
    public void reset()
    {
        super.reset();

        serializedObjects.clear();
        serializedObjectCount = 0;

        if (avmPlusOutput != null)
            avmPlusOutput.reset();
    }

    /**
     * Creates a new Amf3Output instance which is initialized with the
     * current SerializationContext, OutputStream and debug trace settings
     * to switch the version of the AMF protocol mid-stream.
     */
    protected void createAMF3Output()
    {
        avmPlusOutput = new Amf3Output(context);
        avmPlusOutput.setOutputStream(out);
        avmPlusOutput.setDebugTrace(trace);
    }

    //
    // java.io.ObjectOutput implementations
    //

    /**
     * Serialize an Object using AMF 0.
     */
    public void writeObject(Object o) throws IOException
    {
        if (o == null)
        {
            writeAMFNull();
            return;
        }

        if (o instanceof String)
        {
            writeAMFString((String)o);
        }
        else if (o instanceof Number)
        {
            if (!context.legacyBigNumbers &&
                (o instanceof BigInteger || o instanceof BigDecimal))
            {
                // Using double to write big numbers such as BigInteger or
                // BigDecimal can result in information loss so we write
                // them as String by default...
                writeAMFString((o).toString());
            }
            else
            {
                writeAMFDouble(((Number)o).doubleValue());
            }
        }
        else if (o instanceof Boolean)
        {
            writeAMFBoolean(((Boolean)o).booleanValue());
        }
        else if (o instanceof Character)
        {
            String s = o.toString();
            writeAMFString(s);
        }
        else if (o instanceof Date)
        {
            // Note that dates are not considered complex types in AMF 0
            writeAMFDate((Date)o);
        }
        else if (o instanceof Calendar)
        {
            writeAMFDate(((Calendar)o).getTime());
        }
        else
        {
            // We have a complex object.

            // If we're using AMF 3, delegate to AVM+ encoding format
            if (avmPlus)
            {
                if (avmPlusOutput == null)
                {
                    createAMF3Output();
                }

                out.writeByte(kAvmPlusObjectType);
                avmPlusOutput.writeObject(o);
            }
            else
            {
                Class cls = o.getClass();

                if (cls.isArray())
                {
                    writeAMFArray(o, cls.getComponentType());
                }
                else if (o instanceof Map && context.legacyMap && !(o instanceof ASObject))
                {
                        writeMapAsECMAArray((Map)o);
                }
                else if (o instanceof Collection)
                {
                    if (context.legacyCollection)
                        writeCollection((Collection)o, null);
                    else
                        writeArrayCollection((Collection)o, null);
                }
                else if (o instanceof org.w3c.dom.Document)
                {
                    out.write(kXMLObjectType);
                    String xml = documentToString(o);
                    if (isDebug)
                        trace.write(xml);

                    writeUTF(xml, true, false);
                }
                else
                {
                    // Special Case: wrap RowSet in PageableRowSet for Serialization
                    if (o instanceof RowSet)
                    {
                        o = new PagedRowSet((RowSet)o, Integer.MAX_VALUE, false);
                    }
                    else if (o instanceof Throwable && context.legacyThrowable)
                    {
                        o = new StatusInfoProxy((Throwable)o);
                    }

                    writeCustomObject(o);
                }
            }
        }
    }

    /**
     * @exclude
     */
    public void writeObjectTraits(TraitsInfo traits) throws IOException
    {
        String className = null;
        if (traits != null)
            className = traits.getClassName();

        if (isDebug)
            trace.startAMFObject(className, serializedObjectCount - 1);

        if (className == null || className.length() == 0)
        {
            out.write(kObjectType);
        }
        else
        {
            out.write(kTypedObjectType);
            out.writeUTF(className);
        }
    }

    /**
     * @exclude
     */
    public void writeObjectProperty(String name, Object value) throws IOException
    {
        if (isDebug)
            trace.namedElement(name);

        out.writeUTF(name);
        writeObject(value);
    }

    /**
     * @exclude
     */
    public void writeObjectEnd() throws IOException
    {
        out.write(OBJECT_END_MARKER, 0, OBJECT_END_MARKER.length);

        if (isDebug)
            trace.endAMFObject();
    }


    //
    // AMF SPECIFIC SERIALIZATION METHODS
    //

    /**
     * @exclude
     */
    protected void writeAMFBoolean(boolean b) throws IOException
    {
        if (isDebug)
            trace.write(b);

        out.write(kBooleanType);

        out.writeBoolean(b);
    }

    /**
     * @exclude
     */
    protected void writeAMFDouble(double d) throws IOException
    {
        if (isDebug)
            trace.write(d);

        out.write(kNumberType);

        out.writeDouble(d);
    }

    /**
     * @exclude
     */
    protected void writeAMFDate(Date d) throws IOException
    {
        if (isDebug)
            trace.write(d);

        out.write(kDateType);
        // Write the time as 64bit value in ms
        out.writeDouble((double)d.getTime());
        int nCurrentTimezoneOffset = TimeZone.getDefault().getRawOffset();
        out.writeShort(nCurrentTimezoneOffset / 60000);
    }

    /**
     * @exclude
     */
    protected void writeAMFArray(Object o, Class componentType) throws IOException
    {
        if (componentType.isPrimitive())
        {
            writePrimitiveArray(o);
        }
        else if (componentType.equals(Character.class))
        {
            writeCharArrayAsString((Character[])o);
        }
        else
        {
            writeObjectArray((Object[])o, null);
        }
    }

    /**
     * @exclude
     */
    protected void writeArrayCollection(Collection col, SerializationDescriptor desc) throws IOException
    {
        if (!serializeAsReference(col))
        {
            ArrayCollection ac;

            if (col instanceof ArrayCollection)
            {
                ac = (ArrayCollection)col;
                // TODO: QUESTION: Pete ignoring the descriptor here... not sure if
                // we should modify the user's AC as that could cause corruption?
            }
            else
            {
                // Wrap any Collection in an ArrayCollection
                ac = new ArrayCollection(col);
                if (desc != null)
                    ac.setDescriptor(desc);
            }

            // Then wrap ArrayCollection in PropertyProxy for bean-like serialization
            PropertyProxy proxy = PropertyProxyRegistry.getProxy(ac);
            writePropertyProxy(proxy, ac);
        }
    }

    /**
     * @exclude
     */
    protected void writeCustomObject(Object o) throws IOException
    {
        PropertyProxy proxy = null;

        if (o instanceof PropertyProxy)
        {
            proxy = (PropertyProxy)o;
            o = proxy.getDefaultInstance();

            // The proxy may wrap a null default instance, if so, short circuit here.
            if (o == null)
            {
                writeAMFNull();
                return;
            }
            // HACK: Short circuit and unwrap if PropertyProxy is wrapping an Array
            // or Collection type since we don't yet have the ability to proxy multiple
            // AMF types. We write an AMF Array directly instead of an AMF Object
            else if (o instanceof Collection)
            {
                if (context.legacyCollection)
                    writeCollection((Collection)o, proxy.getDescriptor());
                else
                    writeArrayCollection((Collection)o, proxy.getDescriptor());
                return;
            }
            else if (o.getClass().isArray())
            {
                writeObjectArray((Object[])o, proxy.getDescriptor());
                return;
            }
            else if (context.legacyMap && o instanceof Map && !(o instanceof ASObject))
            {
                writeMapAsECMAArray((Map)o);
                return;
            }
        }

        if (!serializeAsReference(o))
        {
            if (proxy == null)
            {
                proxy = PropertyProxyRegistry.getProxyAndRegister(o);
            }

            writePropertyProxy(proxy, o);
        }
    }

    /**
     * @exclude
     */
    protected void writePropertyProxy(PropertyProxy pp, Object instance) throws IOException
    {
        /*
         * At this point we substitute the instance we want to serialize.
         */
        Object newInst = pp.getInstanceToSerialize(instance);
        if (newInst != instance)
        {
            // We can't use writeAMFNull here I think since we already added this object
            // to the object table on the server side.  The player won't have any way
            // of knowing we have this reference mapped to null.
            if (newInst == null)
                throw new MessageException("PropertyProxy.getInstanceToSerialize class: " + pp.getClass() + " returned null for instance class: " + instance.getClass().getName());

            // Grab a new proxy if necessary for the new instance
            pp = PropertyProxyRegistry.getProxyAndRegister(newInst);
            instance = newInst;
        }

        //FIXME: Throw exception or use unsupported type for externalizable as it's not supported in AMF 0?
        boolean externalizable = false; //sp.isExternalizable(instance);
        List propertyNames = pp.getPropertyNames(instance);

        TraitsInfo ti = new TraitsInfo(pp.getAlias(instance), pp.isDynamic(), externalizable, propertyNames);
        writeObjectTraits(ti);

        if (propertyNames != null)
        {
            Iterator it = propertyNames.iterator();
            while (it.hasNext())
            {
                String propName = (String)it.next();
                Object value = pp.getValue(instance, propName);
                writeObjectProperty(propName, value);
            }
        }

        writeObjectEnd();
    }

    /**
     * @exclude
     */
    protected void writeMapAsECMAArray(Map m) throws IOException
    {
        if (!serializeAsReference(m))
        {
            if (isDebug)
                trace.startECMAArray(serializedObjectCount - 1);

            out.write(kECMAArrayType);
            out.writeInt(0);

            Iterator it = m.keySet().iterator();
            while (it.hasNext())
            {
                Object key = it.next();
                Object value = m.get(key);
                writeObjectProperty(key.toString(), value);
            }

            writeObjectEnd();
        }
    }

    /**
     * @exclude
     */
    protected void writeAMFNull() throws IOException
    {
        if (isDebug)
            trace.writeNull();

        out.write(kNullType);
    }

    /**
     * @exclude
     */
    protected void writeAMFString(String str) throws IOException
    {
        if (isDebug)
            trace.writeString(str);

        writeUTF(str, false, true);
    }

    /**
     * @exclude
     */
    protected void writeObjectArray(Object[] values, SerializationDescriptor descriptor) throws IOException
    {
        if (!serializeAsReference(values))
        {
            if (isDebug)
                trace.startAMFArray(serializedObjectCount - 1);

            out.write(kStrictArrayType);
            out.writeInt(values.length);
            for (int i = 0; i < values.length; ++i)
            {
                if (isDebug)
                    trace.arrayElement(i);

                Object item = values[i];
                if (item != null && descriptor != null && !(item instanceof String)
                        && !(item instanceof Number) && !(item instanceof Boolean)
                        && !(item instanceof Character))
                {

                    PropertyProxy proxy = PropertyProxyRegistry.getProxy(item);
                    proxy = (PropertyProxy)proxy.clone();
                    proxy.setDescriptor(descriptor);
                    item = proxy;
                }
                writeObject(item);
            }

            if (isDebug)
                trace.endAMFArray();
        }
    }

    /**
     * Serialize a Collection.
     *
     * @param c Collection to be serialized as an array.
     * @throws java.io.IOException The exception can be generated by the output stream
     * @exclude
     */
    protected void writeCollection(Collection c, SerializationDescriptor descriptor) throws IOException
    {
        if (!serializeAsReference(c))
        {
            if (isDebug)
                trace.startAMFArray(serializedObjectCount - 1);

            out.write(kStrictArrayType);
            out.writeInt(c.size());
            Iterator it = c.iterator();
            int i = 0;
            while (it.hasNext())
            {
                if (isDebug)
                    trace.arrayElement(i++);

                Object item = it.next();
                if (item != null && descriptor != null && !(item instanceof String)
                        && !(item instanceof Number) && !(item instanceof Boolean)
                        && !(item instanceof Character))
                {
                    PropertyProxy proxy = PropertyProxyRegistry.getProxy(item);
                    proxy = (PropertyProxy)proxy.clone();
                    proxy.setDescriptor(descriptor);
                    item = proxy;
                }
                writeObject(item);
            }

            if (isDebug)
                trace.endAMFArray();
        }
    }

    /**
     * Serialize an array of primitives.
     * <p>
     * Primitives include the following:
     * boolean, char, double, float, long, int, short, byte
     * </p>
     * @param obj An array of primitives
     * @exclude
     */
    protected void writePrimitiveArray(Object obj) throws IOException
    {
        Class aType = obj.getClass().getComponentType();

        //Treat char[] as a String
        if (aType.equals(Character.TYPE))
        {
            char[] c = (char[])obj;
            writeCharArrayAsString(c);
        }
        else if (!serializeAsReference(obj))
        {
            if (aType.equals(Boolean.TYPE))
            {
                out.write(kStrictArrayType);

                boolean[] b = (boolean[])obj;
                out.writeInt(b.length);

                if (isDebug)
                {
                    trace.startAMFArray(serializedObjectCount - 1);

                    for (int i = 0; i < b.length; i++)
                    {
                        trace.arrayElement(i);
                        writeAMFBoolean(b[i]);
                    }

                    trace.endAMFArray();
                }
                else
                {
                    for (int i = 0; i < b.length; i++)
                    {
                        writeAMFBoolean(b[i]);
                    }
                }
            }
            else
            {
                //We have a primitive number, either a double, float, long, int, short or byte.
                //We write all of these as doubles...
                out.write(kStrictArrayType);

                int length = Array.getLength(obj);
                out.writeInt(length);

                if (isDebug)
                {
                    trace.startAMFArray(serializedObjectCount - 1);

                    for (int i = 0; i < length; i++)
                    {
                        trace.arrayElement(i);
                        double v = Array.getDouble(obj, i);
                        writeAMFDouble(v);
                    }

                    trace.endAMFArray();
                }
                else
                {
                    for (int i = 0; i < length; i++)
                    {
                        double v = Array.getDouble(obj, i);
                        writeAMFDouble(v);
                    }
                }
            }
        }
    }

    /**
     * @exclude
     */
    protected void writeCharArrayAsString(Character[] ca) throws IOException
    {
        int length = ca.length;
        char[] chars = new char[length];

        for (int i = 0; i < length; i++)
        {
            Character c = ca[i];
            if (c == null)
                chars[i] = 0;
            else
                chars[i] = ca[i].charValue();
        }
        writeCharArrayAsString(chars);
    }

    /**
     * @exclude
     */
    protected void writeCharArrayAsString(char[] ca) throws IOException
    {
        writeAMFString(new String(ca));
    }

    /**
     * @exclude
     */
    protected void writeUTF(String str, boolean forceLong, boolean writeType) throws IOException
    {
        int strlen = str.length();
        int utflen = 0;
        int c, count = 0;

        char[] charr = getTempCharArray(strlen);
        str.getChars(0, strlen, charr, 0);

        for (int i = 0; i < strlen; i++)
        {
            c = charr[i];
            if (c <= 0x007F)
            {
                utflen++;
            }
            else if (c > 0x07FF)
            {
                utflen += 3;
            }
            else
            {
                utflen += 2;
            }
        }

        int type;
        if (forceLong)
        {
            type = kLongStringType;
        }
        else
        {
            if (utflen <= 65535)
                type = kStringType;
            else
                type = kLongStringType;
        }

        byte[] bytearr;
        if (writeType)
        {
            bytearr = getTempByteArray(utflen + (type == kStringType ? 3 : 5));
            bytearr[count++] = (byte)(type);
        }
        else
            bytearr = getTempByteArray(utflen + (type == kStringType ? 2 : 4));

        if (type == kLongStringType)
        {
            bytearr[count++] = (byte)((utflen >>> 24) & 0xFF);
            bytearr[count++] = (byte)((utflen >>> 16) & 0xFF);
        }
        bytearr[count++] = (byte)((utflen >>> 8) & 0xFF);
        bytearr[count++] = (byte)((utflen) & 0xFF);
        for (int i = 0; i < strlen; i++)
        {
            c = charr[i];
            if (c <= 0x007F)
            {
                bytearr[count++] = (byte)c;
            }
            else if (c > 0x07FF)
            {
                bytearr[count++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte)(0x80 | ((c >> 6) & 0x3F));
                bytearr[count++] = (byte)(0x80 | ((c) & 0x3F));
            }
            else
            {
                bytearr[count++] = (byte)(0xC0 | ((c >> 6) & 0x1F));
                bytearr[count++] = (byte)(0x80 | ((c) & 0x3F));
            }
        }
        out.write(bytearr, 0, count);
    }

    /**
     * Remember the object's serialization number so that it can be referred to
     * as a reference later. Only complex ojects should be stored as references.
     *
     * @exclude
     */
    protected void rememberObjectReference(Object obj)
    {
        serializedObjects.put(obj, new Integer(serializedObjectCount++));
    }

    /**
     * Attempts to serialize the object as a reference.
     * If the object cannot be serialized as a reference, it is stored
     * in the reference collection for potential future encounter.
     *
     * @return Success/failure indicator as to whether the object could be
     *         serialized as a reference.
     * @exclude
     */
    protected boolean serializeAsReference(Object obj) throws IOException
    {
        Object ref = serializedObjects.get(obj);
        if (ref != null)
        {
            try
            {
                int refNum = ((Integer)ref).intValue();
                out.write(kReferenceType);
                out.writeShort(refNum);

                if (isDebug)
                    trace.writeRef(refNum);
            }
            catch (ClassCastException e)
            {
                throw new IOException("Object reference value is not an Integer");
            }
        }
        else
        {
            rememberObjectReference(obj);
        }
        return (ref != null);
    }

    /**
    protected void writeUnsupported() throws IOException
    {
        if (isDebug)
            trace.write("UNSUPPORTED");

        out.write(kUnsupportedType);
    }
    */
}
 
TOP

Related Classes of flex.messaging.io.amf.Amf0Output

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.