Package pushy.internal

Source Code of pushy.internal.Marshal$BooleanHandler

/*
* Copyright (c) 2009, 2011 Andrew Wilkins <axwalk@gmail.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

package pushy.internal;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

import java.lang.reflect.Array;

import java.math.BigInteger;

import java.util.HashMap;
import java.util.Map;

/**
* A class for marshaling to/from the Python marshal format.
*/
public class Marshal
{
    /**
     * Currently only version 0 is supported.
     */
    private static int version = 0;

    // Constants used by getLong.
    private static int LONG_SHIFT = 15;
    private static int LONG_BASE  = (1 << LONG_SHIFT);
    private static int LONG_MASK  = (LONG_BASE-1);
    private static BigInteger BIG_INT_MIN =
        new BigInteger("" + Integer.MIN_VALUE);
    private static BigInteger BIG_INT_MAX =
        new BigInteger("" + Integer.MAX_VALUE);
    private static BigInteger BIG_LONG_MIN =
        new BigInteger("" + Long.MIN_VALUE);
    private static BigInteger BIG_LONG_MAX =
        new BigInteger("" + Long.MAX_VALUE);

    // Handlers (class -> handler)
    private static Map handlers = new HashMap();
    // Types (type code -> class)
    private static Map types = new HashMap();

    /**
     * Object types.
     */
    private static class Type
    {
        private static final byte NULL           = '0';
        private static final byte NONE           = 'N';
        private static final byte FALSE          = 'F';
        private static final byte TRUE           = 'T';
        private static final byte STOPITER       = 'S';
        private static final byte ELLIPSIS       = '.';
        private static final byte INT            = 'i';
        private static final byte INT64          = 'I';
        private static final byte FLOAT          = 'f';
        private static final byte BINARY_FLOAT   = 'g';
        private static final byte COMPLEX        = 'x';
        private static final byte BINARY_COMPLEX = 'y';
        private static final byte LONG           = 'l';
        private static final byte STRING         = 's';
        private static final byte INTERNED       = 't';
        private static final byte STRINGREF      = 'R';
        private static final byte TUPLE          = '(';
        private static final byte LIST           = '[';
        private static final byte DICT           = '{';
        private static final byte CODE           = 'c';
        private static final byte UNICODE        = 'u';
        private static final byte UNKNOWN        = '?';
        private static final byte SET            = '<';
        private static final byte FROZENSET      = '>';
    };

    /**
     * Check if an object can be marshaled.
     */
    public static boolean isMarshallable(Object object)
    {
        if (object == null || isMarshallableType(object.getClass()))
        {
            return true;
        }
        else if (object.getClass().isArray())
        {
            boolean all = true;
            for (int i = 0; all && i < Array.getLength(object); ++i)
                if (!isMarshallable(Array.get(object, i)))
                    all = false;
            return all;
        }
        return false;
    }

    /**
     * Check if instances of the specified class can be marshalled.
     */
    public static boolean isMarshallableType(Class class_)
    {
        return handlers.containsKey(class_);
    }

    /**
     * Marshal an object, and return the byte array.
     */
    public static byte[]
    dump(Object object) throws IOException, MarshalException
    {
        try
        {
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            dump(object, stream);
            return stream.toByteArray();
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    /**
     * Unmarshal a byte array into an object.
     */
    public static Object
    load(byte[] bytes) throws IOException, MarshalException
    {
        try
        {
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
            return load(stream);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    /**
     * Marshal an object to an output stream.
     */
    public static void
    dump(Object object, OutputStream stream)
        throws IOException, MarshalException
    {
        // Handle null values.
        if (object == null)
        {
            stream.write(Type.NONE);
            return;
        }

        // Handle arrays.
        if (object.getClass().isArray())
        {
            // XXX
            // Herein lies a conundrum. If we say it's a tuple, then Python
            // thinks it can't modify any elements. If we say it's a list, then
            // the other side thinks it can modify the list structure. For now,
            // prefer the former.
            int size = Array.getLength(object);
            stream.write(Type.TUPLE);
            putInt32(stream, size);
            for (int i = 0; i < size; ++i)
                dump(Array.get(object, i), stream);
            return;
        }

        // Handle all other types.
        Handler handler = (Handler)handlers.get(object.getClass());
        if (handler != null)
        {
            handler.dump(stream, object);
        }
        else
        {
            throw new MarshalException(
                          "unsupported type: " + object.getClass());
        }
    }

    /**
     * Unmarshal an object from an input stream.
     */
    public static Object
    load(InputStream stream) throws IOException, MarshalException
    {
        int type = stream.read();
        if (type == -1)
            throw new EOFException();

        if (type == Type.NULL || type == Type.NONE)
            return null;

        // Load a tuple: size followed by size*objects.
        if (type == Type.TUPLE)
        {
            int size = getInt32(stream);
            java.util.List items = new java.util.ArrayList();
            for (int i = 0; i < size; ++i)
                items.add(load(stream));
            return createArray(items);
        }

        // Handle all other type codes.
        Class class_ = (Class)types.get(new Integer(type));
        if (class_ == null)
            throw new MarshalException("unsupported type: " + (char)type);
        return ((Handler)handlers.get(class_)).load(stream, type);
    }

    /**
     * Write a 8-bit integer (byte).
     */
    private static void
    putInt8(OutputStream stream, byte value) throws IOException
    {
        stream.write(value);
    }

    /**
     * Get an 8-bit integer (byte).
     */
    private static byte getInt8(InputStream stream) throws IOException
    {
        return (byte)stream.read();
    }

    /**
     * Get a little-endian 16-bit integer.
     */
    private static short getInt16(InputStream stream) throws IOException
    {
        return (short)(stream.read() | (stream.read() << 8));
    }

    /**
     * Write a little-endian 16-bit integer.
     */
    private static void
    putInt16(OutputStream stream, short value) throws IOException
    {
        putInt8(stream, (byte)value);
        putInt8(stream, (byte)(value >> 8));
    }

    /**
     * Get a little-endian 32-bit integer.
     */
    private static int getInt32(InputStream stream) throws IOException
    {
        int b0 = stream.read();
        int b1 = stream.read();
        int b2 = stream.read();
        int b3 = stream.read();
        return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
    }

    /**
     * Write a little-endian 32-bit integer.
     */
    private static void
    putInt32(OutputStream stream, int value) throws IOException
    {
        putInt8(stream, (byte)value);
        putInt8(stream, (byte)(value >> 8));
        putInt8(stream, (byte)(value >> 16));
        putInt8(stream, (byte)(value >> 24));
    }

    /**
     * Get a little-endian 64-bit integer.
     */
    private static long getInt64(InputStream stream) throws IOException
    {
        long lo4 = (long)getInt32(stream);
        long hi4 = (long)getInt32(stream);
        return ((hi4 << 32) & 0xFFFFFFFF00000000L) | (lo4 & 0xFFFFFFFFL);
    }

    /**
     * Write a little-endian 64-bit integer.
     */
    private static void
    putInt64(OutputStream stream, long value) throws IOException
    {
        putInt32(stream, (int)(value & 0xFFFFFFFFL));
        putInt32(stream, (int)((value >> 32) & 0xFFFFFFFFL));
    }

    /**
     * Get an arbitrary-precision integer.
     * @return A Integer, Long, or BigInteger.
     */
    private static Number getLong(InputStream stream) throws IOException
    {
        int ndigits = getInt32(stream);
        if (ndigits == 0)
            return new Integer(0);

        boolean negative = ndigits < 0;
        ndigits = Math.abs(ndigits);

        // Calculate the absolute value of the integer.
        BigInteger bigint = new BigInteger("0");
        short[] digits = new short[ndigits];
        for (int i = 0; i < ndigits; ++i)
            digits[i] = getInt16(stream);
        for (int i = 0; i < ndigits; ++i)
        {
            bigint = bigint.shiftLeft(LONG_SHIFT);
            bigint = bigint.add(new BigInteger("" + digits[ndigits-i-1]));
        }

        // Negate, if necessary. The sign is conveyed in the size (ndigits).
        if (negative)
            bigint = bigint.negate();

        // Get the smallest of Integer, Long or BigInteger.
        int cmp = bigint.compareTo(BIG_INT_MAX);
        if (cmp > 0)
        {
            cmp = bigint.compareTo(BIG_LONG_MAX);
            if (cmp > 0)
                return bigint;
            return new Long(bigint.longValue());
        }
        else if (cmp == 0)
        {
            return new Integer(bigint.intValue());
        }
        else
        {
            cmp = bigint.compareTo(BIG_INT_MIN);
            if (cmp < 0)
            {
                cmp = bigint.compareTo(BIG_LONG_MIN);
                if (cmp < 0)
                    return bigint;
                return new Long(bigint.longValue());
            }
            else
            {
                return new Integer(bigint.intValue());
            }
        }
    }

    /**
     * Write an arbitrary-precision integer.
     */
    private static void
    putLong(OutputStream stream, BigInteger bigint) throws IOException
    {
        int ndigits = 0;
        boolean negative = bigint.compareTo(BigInteger.ZERO) < 0;
        if (negative)
            bigint = bigint.abs();

        // Count the number of digits.
        BigInteger t = new BigInteger(bigint.toString());
        for (; !t.equals(BigInteger.ZERO); ++ndigits)
            t = t.shiftRight(LONG_SHIFT);

        // Write the size.
        putInt32(stream, negative ? -ndigits : ndigits);

        // Write the digits.
        t = new BigInteger(bigint.toString());
        for (int i = 0; i < ndigits; ++i)
        {
            putInt16(stream, (short)(t.shortValue() & LONG_MASK));
            t = t.shiftRight(LONG_SHIFT);
        }
    }

    /**
     * Read a string.
     */
    private static String getString(InputStream stream) throws IOException
    {
        return getString(stream, "ISO-8859-1"); // LATIN-1
    }

    /**
     * Read a string with the specified character set.
     */
    private static String
    getString(InputStream stream, String charset) throws IOException
    {
        int size = getInt32(stream);
        if (size == 0)
            return "";
        byte[] buf = getBytes(stream, size);
        return new String(buf, charset);
    }

    private static byte[]
    getBytes(InputStream stream, int size) throws IOException
    {
        byte[] buf = new byte[size];
        if (size == 0)
            return buf;
        int nread = 0;
        do
        {
            int partial = stream.read(buf, nread, buf.length-nread);
            if (partial == -1)
                throw new java.io.EOFException();
            nread += partial;
        } while (nread < buf.length);
        return buf;
    }

    /**
     * Write a string.
     */
    private static void
    putString(OutputStream stream, String string) throws IOException
    {
        putInt32(stream, string.length());
        byte[] bytes = string.getBytes("ISO-8859-1");
        stream.write(bytes);
    }

    private static Map primitiveTypes = new HashMap();
    static
    {
        primitiveTypes.put(Boolean.class,   Boolean.TYPE);
        primitiveTypes.put(Byte.class,      Byte.TYPE);
        primitiveTypes.put(Character.class, Character.TYPE);
        primitiveTypes.put(Double.class,    Double.TYPE);
        primitiveTypes.put(Float.class,     Float.TYPE);
        primitiveTypes.put(Integer.class,   Integer.TYPE);
        primitiveTypes.put(Long.class,      Long.TYPE);
        primitiveTypes.put(Short.class,     Short.TYPE);
    }

    /**
     * Convert an object list to an array of the most specific, primitive
     * array.
     */
    public static Object createArray(java.util.List list)
    {
        // Convert to a type-specific array if all elements are of the
        // same type, otherwise just return an Object array.
        if (list.isEmpty())
            return new Object[]{};

        // Find the first non-null object in the array. Remember whether any
        // null objects have been seen, so we know whether or not we can cast
        // to a primitive array.
        java.util.Iterator iter = list.iterator();
        boolean haveNull = false;
        Object obj = iter.next();
        while (obj == null && iter.hasNext())
        {
            haveNull = true;
            obj = iter.next();
        }

        // Check that all elements in the array are of the same type, or null.
        Class compType = Object.class;
        if (obj != null)
        {
            compType = obj.getClass();
            while (!compType.equals(Object.class) && iter.hasNext())
            {
                obj = iter.next();
                if (obj == null)
                    haveNull = true;
                else if (!obj.getClass().equals(compType))
                    compType = Object.class;
            }
        }

        // Either we have an array full of Objects, or an array of
        // heterogeneously typed objects.
        if (compType.equals(Object.class))
            return list.toArray(new Object[]{});

        // If the type is an Object type corresponding to primitive
        // type (e.g. Integer), get the primitive type.
        if (!haveNull)
        {
            Class primitiveType = (Class)primitiveTypes.get(compType);
            if (primitiveType != null)
                compType = primitiveType;
        }

        // Create the array.
        Object array = Array.newInstance(compType, list.size());
        iter = list.iterator();
        for (int i = 0; iter.hasNext(); ++i)
            Array.set(array, i, iter.next());
        return array;
    }

    // Define type handlers and type mappings.
    static
    {
        handlers.put(Short.class,      new ShortHandler());
        handlers.put(Integer.class,    new IntegerHandler());
        handlers.put(Long.class,       new LongHandler());
        handlers.put(BigInteger.class, new BigIntegerHandler());
        handlers.put(String.class,     new StringHandler());
        handlers.put(Boolean.class,    new BooleanHandler());
        handlers.put(Double.class,     new FloatHandler());
        handlers.put(Float.class,      new FloatHandler());

        types.put(new Integer(Type.INT),      Integer.class);
        types.put(new Integer(Type.INT64),    Long.class);
        types.put(new Integer(Type.LONG),     BigInteger.class);
        types.put(new Integer(Type.STRING),   String.class);
        types.put(new Integer(Type.INTERNED), String.class);
        types.put(new Integer(Type.UNICODE),  String.class);
        types.put(new Integer(Type.TRUE),     Boolean.class);
        types.put(new Integer(Type.FALSE),    Boolean.class);
        types.put(new Integer(Type.FLOAT),    Double.class);
    }

    /**
     * Defines an interface for a type handler.
     */
    private static interface Handler
    {
        public void dump(OutputStream stream, Object value) throws IOException;
        public Object load(InputStream stream, int type) throws IOException;
    }

    private static class IntegerHandler implements Handler
    {
        public void dump(OutputStream stream, Object value) throws IOException
        {
            stream.write(Type.INT);
            putInt32(stream, ((Integer)value).intValue());
        }

        public Object load(InputStream stream, int type) throws IOException
        {
            return new Integer(getInt32(stream));
        }
    }

    private static class ShortHandler extends IntegerHandler
    {
        public void dump(OutputStream stream, Object value) throws IOException
        {
            super.dump(stream, new Integer(((Short)value).shortValue()));
        }
    }

    private static class LongHandler implements Handler
    {
        public void dump(OutputStream stream, Object value) throws IOException
        {
            stream.write(Type.INT64);
            putInt64(stream, ((Long)value).longValue());
        }

        public Object load(InputStream stream, int type) throws IOException
        {
            return new Long(getInt64(stream));
        }
    }

    private static class BigIntegerHandler implements Handler
    {
        public void dump(OutputStream stream, Object value) throws IOException
        {
            stream.write(Type.LONG);
            putLong(stream, (BigInteger)value);
        }

        public Object load(InputStream stream, int type) throws IOException
        {
            return getLong(stream);
        }
    }

    private static class BooleanHandler implements Handler
    {
        public void dump(OutputStream stream, Object value) throws IOException
        {
            stream.write(value.equals(Boolean.TRUE) ? Type.TRUE : Type.FALSE);
        }

        public Object load(InputStream stream, int type) throws IOException
        {
            return type == Type.TRUE ? Boolean.TRUE : Boolean.FALSE;
        }
    }

    private static class StringHandler implements Handler
    {
        public void dump(OutputStream stream, Object value) throws IOException
        {
            stream.write(Type.STRING);
            putString(stream, (String)value);
        }

        public Object load(InputStream stream, int type) throws IOException
        {
            if (type == Type.UNICODE)
                return getString(stream, "UTF-8");
            return getString(stream);
        }
    }

    private static class FloatHandler implements Handler
    {
        public void dump(OutputStream stream, Object value) throws IOException
        {
            stream.write(Type.FLOAT);

            String s = format(((Number)value).doubleValue());
            byte[] bytes = s.getBytes("ISO-8859-1");
            putInt8(stream, (byte)bytes.length);
            stream.write(bytes);
        }

        public Object load(InputStream stream, int type) throws IOException
        {
            int n = getInt8(stream);
            byte[] buf = getBytes(stream, n);
            if (n == 3 && buf[0] == 'n' && buf[1] == 'a' && buf[2] == 'n')
            {
                return new Double(Double.NaN);
            }
            else if (n == 3 && buf[0] == 'i' && buf[1] == 'n' && buf[2] == 'f')
            {
                return new Double(Double.POSITIVE_INFINITY);
            }
            else if (n == 4 && buf[0] == '-' && buf[1] == 'i' &&
                     buf[2] == 'n' && buf[3] == 'f')
            {
                return new Double(Double.NEGATIVE_INFINITY);
            }
            else
            {
                String repr = new String(buf, "ISO-8859-1");
                return new Double(repr);
            }
        }

        private String format(double value)
        {
            if (Double.isNaN(value))
                return "nan";
            else if (Double.isInfinite(value))
                return value < 0 ? "-inf" : "inf";
            return Double.toString(value);
        }
    }
}
TOP

Related Classes of pushy.internal.Marshal$BooleanHandler

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.