com.gdevelop.gwt.syncrpc.SyncClientSerializationStreamReader.java Source code

Java tutorial

Introduction

Here is the source code for com.gdevelop.gwt.syncrpc.SyncClientSerializationStreamReader.java

Source

/*
 * Copyright www.gdevelop.com.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.gdevelop.gwt.syncrpc;

import android.util.Log;

import com.gdevelop.gwt.syncrpc.RemoteServiceSyncProxy.DummySerializationPolicy;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader;

import com.google.gwt.user.server.rpc.SerializationPolicy;

import com.google.gwt.user.server.rpc.impl.SerializabilityUtil;
import com.google.gwt.user.server.rpc.impl.SerializedInstanceReference;

import java.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.LogFactory;

/**
 * @see com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter
 * @see com.google.gwt.user.client.rpc.impl.ClientSerializationStreamReader
 * @see com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter
 * @see com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader
 */
public class SyncClientSerializationStreamReader extends AbstractSerializationStreamReader {
    private static final org.apache.commons.logging.Log LOG = LogFactory
            .getLog(SyncClientSerializationStreamReader.class);
    private static final char JS_ESCAPE_CHAR = '\\';

    private static final String POSTLUDE = "])";
    private static final String PRELUDE = "].concat([";

    private String origEncoded;

    /**
     * Used to accumulate elements while deserializing array types. The generic
     * type of the BoundedList will vary from the component type of the array it
     * is intended to create when the array is of a primitive type.
     * 
     * @param <T> The type of object used to hold the data in the buffer
     */
    private static class BoundedList<T> extends LinkedList<T> {
        private final Class<?> componentType;
        private final int expectedSize;

        public BoundedList(Class<?> componentType, int expectedSize) {
            this.componentType = componentType;
            this.expectedSize = expectedSize;
        }

        @Override
        public boolean add(T o) {
            assert size() < getExpectedSize();
            return super.add(o);
        }

        public Class<?> getComponentType() {
            return componentType;
        }

        public int getExpectedSize() {
            return expectedSize;
        }
    }

    /**
     * Enumeration used to provided typed instance readers.
     */
    private enum ValueReader {
        BOOLEAN {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readBoolean();
            }
        },
        BYTE {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readByte();
            }
        },
        CHAR {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readChar();
            }
        },
        DOUBLE {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readDouble();
            }
        },
        FLOAT {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readFloat();
            }
        },
        INT {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readInt();
            }
        },
        LONG {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readLong();
            }
        },
        OBJECT {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readObject();
            }
        },
        SHORT {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readShort();
            }
        },
        STRING {
            Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException {
                return stream.readString();
            }
        };

        abstract Object readValue(SyncClientSerializationStreamReader stream) throws SerializationException;
    }

    /**
     * Enumeration used to provided typed instance readers for vectors.
     */
    private enum VectorReader {
        BOOLEAN_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readBoolean();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setBoolean(array, index, (Boolean) value);
            }
        },
        BYTE_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readByte();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setByte(array, index, (Byte) value);
            }
        },
        CHAR_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readChar();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setChar(array, index, (Character) value);
            }
        },
        DOUBLE_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readDouble();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setDouble(array, index, (Double) value);
            }
        },
        FLOAT_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readFloat();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setFloat(array, index, (Float) value);
            }
        },
        INT_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readInt();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setInt(array, index, (Integer) value);
            }
        },
        LONG_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readLong();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setLong(array, index, (Long) value);
            }
        },
        OBJECT_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readObject();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.set(array, index, value);
            }
        },
        SHORT_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readShort();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.setShort(array, index, (Short) value);
            }
        },
        STRING_VECTOR {
            protected Object readSingleValue(SyncClientSerializationStreamReader stream)
                    throws SerializationException {
                return stream.readString();
            }

            protected void setSingleValue(Object array, int index, Object value) {
                Array.set(array, index, value);
            }
        };

        protected abstract Object readSingleValue(SyncClientSerializationStreamReader stream)
                throws SerializationException;

        protected abstract void setSingleValue(Object array, int index, Object value);

        /**
         * Convert a BoundedList to an array of the correct type. This
         * implementation consumes the BoundedList.
         */
        protected Object toArray(Class<?> componentType, BoundedList<Object> buffer) throws SerializationException {
            if (buffer.getExpectedSize() != buffer.size()) {
                throw new SerializationException("Inconsistent number of elements received. Received "
                        + buffer.size() + " but expecting " + buffer.getExpectedSize());
            }

            Object arr = Array.newInstance(componentType, buffer.size());

            for (int i = 0, n = buffer.size(); i < n; i++) {
                setSingleValue(arr, i, buffer.removeFirst());
            }

            return arr;
        }

        Object read(SyncClientSerializationStreamReader stream, BoundedList<Object> instance)
                throws SerializationException {
            for (int i = 0, n = instance.getExpectedSize(); i < n; ++i) {
                instance.add(readSingleValue(stream));
            }

            return toArray(instance.getComponentType(), instance);
        }
    }

    /**
     * Map of {@link Class} objects to {@link ValueReader}s.
     */
    private static final Map<Class<?>, ValueReader> CLASS_TO_VALUE_READER = new IdentityHashMap<Class<?>, ValueReader>();

    /**
     * Map of {@link Class} objects to {@link VectorReader}s.
     */
    private static final Map<Class<?>, VectorReader> CLASS_TO_VECTOR_READER = new IdentityHashMap<Class<?>, VectorReader>();

    {
        CLASS_TO_VECTOR_READER.put(boolean[].class,
                SyncClientSerializationStreamReader.VectorReader.BOOLEAN_VECTOR);
        CLASS_TO_VECTOR_READER.put(byte[].class, SyncClientSerializationStreamReader.VectorReader.BYTE_VECTOR);
        CLASS_TO_VECTOR_READER.put(char[].class, SyncClientSerializationStreamReader.VectorReader.CHAR_VECTOR);
        CLASS_TO_VECTOR_READER.put(double[].class, SyncClientSerializationStreamReader.VectorReader.DOUBLE_VECTOR);
        CLASS_TO_VECTOR_READER.put(float[].class, SyncClientSerializationStreamReader.VectorReader.FLOAT_VECTOR);
        CLASS_TO_VECTOR_READER.put(int[].class, SyncClientSerializationStreamReader.VectorReader.INT_VECTOR);
        CLASS_TO_VECTOR_READER.put(long[].class, SyncClientSerializationStreamReader.VectorReader.LONG_VECTOR);
        CLASS_TO_VECTOR_READER.put(Object[].class, SyncClientSerializationStreamReader.VectorReader.OBJECT_VECTOR);
        CLASS_TO_VECTOR_READER.put(short[].class, SyncClientSerializationStreamReader.VectorReader.SHORT_VECTOR);
        CLASS_TO_VECTOR_READER.put(String[].class, SyncClientSerializationStreamReader.VectorReader.STRING_VECTOR);

        CLASS_TO_VALUE_READER.put(boolean.class, SyncClientSerializationStreamReader.ValueReader.BOOLEAN);
        CLASS_TO_VALUE_READER.put(byte.class, SyncClientSerializationStreamReader.ValueReader.BYTE);
        CLASS_TO_VALUE_READER.put(char.class, SyncClientSerializationStreamReader.ValueReader.CHAR);
        CLASS_TO_VALUE_READER.put(double.class, SyncClientSerializationStreamReader.ValueReader.DOUBLE);
        CLASS_TO_VALUE_READER.put(float.class, SyncClientSerializationStreamReader.ValueReader.FLOAT);
        CLASS_TO_VALUE_READER.put(int.class, SyncClientSerializationStreamReader.ValueReader.INT);
        CLASS_TO_VALUE_READER.put(long.class, SyncClientSerializationStreamReader.ValueReader.LONG);
        CLASS_TO_VALUE_READER.put(Object.class, SyncClientSerializationStreamReader.ValueReader.OBJECT);
        CLASS_TO_VALUE_READER.put(short.class, SyncClientSerializationStreamReader.ValueReader.SHORT);
        CLASS_TO_VALUE_READER.put(String.class, SyncClientSerializationStreamReader.ValueReader.STRING);
    }

    private List<String> results = new ArrayList<String>();
    private int index;

    private List<String> stringTable = new ArrayList<String>();

    private SerializationPolicy serializationPolicy;

    public SyncClientSerializationStreamReader(SerializationPolicy serializationPolicy) {
        this.serializationPolicy = serializationPolicy;
    }

    @Override
    public void prepareToRead(String encoded) throws SerializationException {
        origEncoded = encoded;
        encoded = parse(encoded);
        index = results.size();
        Log.d("PROBLEMA", "Init Index: " + index);
        boolean success = false;
        try {
            super.prepareToRead(encoded);
            success = true;
        } finally {
            if (!success) {
                LOG.error("Error decoding response: " + encoded);
            }
        }

        if (getVersion() != SERIALIZATION_STREAM_VERSION) {
            throw new IncompatibleRemoteServiceException("Expecting version " + SERIALIZATION_STREAM_VERSION
                    + " from server, got " + getVersion() + ".");
        }

        buildStringTable();
    }

    protected Object deserialize(String typeSignature) throws SerializationException {
        Object instance = null;
        SerializedInstanceReference serializedInstRef = SerializabilityUtil
                .decodeSerializedInstanceReference(typeSignature);

        try {
            // Class<?> instanceClass = Class.forName(serializedInstRef.getName(),
            //    false, null);
            Class<?> instanceClass = Class.forName(serializedInstRef.getName());

            assert (serializationPolicy != null);

            serializationPolicy.validateDeserialize(instanceClass);

            //TODO validateTypeVersions(instanceClass, serializedInstRef);

            Class<?> customSerializer = SerializabilityUtil.hasCustomFieldSerializer(instanceClass);

            int index = reserveDecodedObjectIndex();

            instance = instantiate(customSerializer, instanceClass);

            rememberDecodedObject(index, instance);

            Object replacement = deserializeImpl(customSerializer, instanceClass, instance);

            // It's possible that deserializing an object requires the original proxy
            // object to be replaced.
            if (instance != replacement) {
                rememberDecodedObject(index, replacement);
                instance = replacement;
            }

            return instance;

        } catch (ClassNotFoundException e) {
            throw new SerializationException(e);

        } catch (InstantiationException e) {
            throw new SerializationException(e);

        } catch (IllegalAccessException e) {
            throw new SerializationException(e);

        } catch (IllegalArgumentException e) {
            throw new SerializationException(e);

        } catch (InvocationTargetException e) {
            throw new SerializationException(e);

        } catch (NoSuchMethodException e) {
            throw new SerializationException(e);
        }
    }

    protected String getString(int index) {
        if (index == 0) {
            return null;
        }
        // index is 1-based
        assert (index > 0);
        assert (index <= stringTable.size());

        // index is 1-based
        return this.stringTable.get(index - 1);
    }

    public boolean readBoolean() {
        Log.d("PROBLEMA", "Before parsing readBoolean: " + index);
        return !results.get(--index).equals("0");
    }

    public byte readByte() {
        Log.d("PROBLEMA", "Before parsing readChar: " + index);
        return Byte.parseByte(results.get(--index));
    }

    public char readChar() {
        Log.d("PROBLEMA", "Before parsing readChar: " + index);
        return (char) Integer.parseInt(results.get(--index));
    }

    public double readDouble() {
        Log.d("PROBLEMA", "Before parsing readDouble: " + index);
        return Double.parseDouble(results.get(--index));
    }

    public float readFloat() {
        Log.d("PROBLEMA", "Before parsing readFloat: " + index);
        return Float.parseFloat(results.get(--index));
    }

    public int readInt() {
        Log.d("PROBLEMA", "Before parsing readInt: " + index);
        try {
            return Integer.parseInt(results.get(--index));
        } catch (NumberFormatException nfe) {
            LOG.error("Serialiazation error " + nfe.getMessage() + " - " + origEncoded);
            throw nfe;
        }
    }

    public long readLong() {
        Log.d("PROBLEMA", "Before parsing readLong: " + index);
        return (long) readDouble() + (long) readDouble();
    }

    public short readShort() {
        Log.d("PROBLEMA", "Before parsing readShort: " + index);
        return Short.parseShort(results.get(--index));
    }

    public String readString() {
        Log.d("PROBLEMA", "Before parsing readString: " + index);
        return getString(readInt());
    }

    /**
     * Parse response from GWT RPC
     * example: [3,23456,0,2,0,0,0,1,1,["dab.rpp.client.Person/1455343364","My dad name","GWT User"],0,5]
     * @param encoded
     */
    private String parse(String encoded) {
        encoded = deconcat(encoded);
        encoded = encoded.substring(1, encoded.length() - 1);
        StringBuffer token = new StringBuffer();
        for (int i = 0; i < encoded.length(); i++) {
            char ch = encoded.charAt(i);
            if (ch == ',') {
                results.add(token.toString());
                token = new StringBuffer();
                continue;
            }
            if (ch == ']' && encoded.indexOf('[', i) == i + 2 && encoded.charAt(i + 1) == ',') {
                i += 2;
                results.add(token.toString());
                token = new StringBuffer();
                continue;
            }
            if (ch == '[') {
                int pos = encoded.lastIndexOf(']');
                if (pos < 0) {
                    // TODO: throw exeption
                }
                results.add(encoded.substring(i + 1, pos));
                i = pos + 1;
                continue;
            }
            token.append(ch);
        }
        if (token.length() > 0) {
            results.add(token.toString());
        }
        return encoded;
    }

    private String deconcat(String encoded) {
        if (encoded.endsWith(POSTLUDE)) {
            int prelude = encoded.indexOf(PRELUDE);
            if (prelude > 0) {
                StringBuffer ret = new StringBuffer(encoded.length() - PRELUDE.length());
                ret.append(encoded.substring(0, prelude));
                ret.append(",");
                ret.append(encoded.substring(prelude + PRELUDE.length(), encoded.length() - 1));
                return deconcat(ret.toString());
            }
        }
        return encoded;
    }

    private Object instantiate(Class<?> customSerializer, Class<?> instanceClass)
            throws InstantiationException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, NoSuchMethodException, SerializationException {
        if (customSerializer != null) {
            for (Method method : customSerializer.getMethods()) {
                if ("instantiate".equals(method.getName())) {
                    return method.invoke(null, this);
                }
            }
            // Ok to not have one.
        }

        if (instanceClass.isArray()) {
            int length = readInt();
            // We don't pre-allocate the array; this prevents an allocation attack
            return new BoundedList<Object>(instanceClass.getComponentType(), length);
        } else if (instanceClass.isEnum()) {
            Enum<?>[] enumConstants = (Enum[]) instanceClass.getEnumConstants();
            int ordinal = readInt();
            assert (ordinal >= 0 && ordinal < enumConstants.length);
            return enumConstants[ordinal];
        } else {
            Constructor<?> constructor = instanceClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            return constructor.newInstance();
        }
    }

    private Object deserializeImpl(Class<?> customSerializer, Class<?> instanceClass, Object instance)
            throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
            InvocationTargetException, SerializationException, ClassNotFoundException {

        if (customSerializer != null) {
            deserializeWithCustomFieldDeserializer(customSerializer, instanceClass, instance);
        } else if (instanceClass.isArray()) {
            instance = deserializeArray(instanceClass, instance);
        } else if (instanceClass.isEnum()) {
            // Enums are deserialized when they are instantiated
        } else {
            deserializeClass(instanceClass, instance);
        }

        return instance;
    }

    private void deserializeWithCustomFieldDeserializer(Class<?> customSerializer, Class<?> instanceClass,
            Object instance) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        assert (!instanceClass.isArray());

        for (Method method : customSerializer.getMethods()) {
            if ("deserialize".equals(method.getName())) {
                method.invoke(null, this, instance);
                return;
            }
        }
        throw new NoSuchMethodException("deserialize");
    }

    /**
     * Deserialize an instance that is an array. Will default to deserializing as
     * an Object vector if the instance is not a primitive vector.
     * 
     * @param instanceClass
     * @param instance
     * @throws SerializationException
     */
    @SuppressWarnings("unchecked")
    private Object deserializeArray(Class<?> instanceClass, Object instance) throws SerializationException {
        assert (instanceClass.isArray());

        BoundedList<Object> buffer = (BoundedList<Object>) instance;
        VectorReader instanceReader = CLASS_TO_VECTOR_READER.get(instanceClass);
        if (instanceReader != null) {
            return instanceReader.read(this, buffer);
        } else {
            return SyncClientSerializationStreamReader.VectorReader.OBJECT_VECTOR.read(this, buffer);
        }
    }

    private void deserializeClass(Class<?> instanceClass, Object instance) throws SerializationException,
            IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
        Field[] serializableFields = SerializabilityUtil.applyFieldSerializationPolicy(instanceClass);

        for (Field declField : serializableFields) {
            assert (declField != null);

            Object value = deserializeValue(declField.getType());

            boolean isAccessible = declField.isAccessible();
            boolean needsAccessOverride = !isAccessible && !Modifier.isPublic(declField.getModifiers());
            if (needsAccessOverride) {
                // Override access restrictions
                declField.setAccessible(true);
            }

            declField.set(instance, value);
        }

        Class<?> superClass = instanceClass.getSuperclass();
        if (serializationPolicy.shouldDeserializeFields(superClass)) {
            deserializeImpl(SerializabilityUtil.hasCustomFieldSerializer(superClass), superClass, instance);
        }
    }

    public Object deserializeValue(Class<?> type) throws SerializationException {
        ValueReader valueReader = CLASS_TO_VALUE_READER.get(type);
        if (valueReader != null) {
            Log.d("PROBLEMA", "Before parsing " + type.toString() + ": " + index);
            Object object = valueReader.readValue(this);
            Log.d("PROBLEMA", "After " + type.toString() + ": " + index);
            return object;
        } else {
            // Arrays of primitive or reference types need to go through readObject.
            return SyncClientSerializationStreamReader.ValueReader.OBJECT.readValue(this);
        }
    }

    private void buildStringTable() {
        String raw = results.get(--index);
        Log.d("PROBLEMA", "BuildStringTable: " + index);
        byte b1;
        byte b2;
        byte b3;
        byte b4;

        boolean startNewString = true;
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < raw.length(); i++) {
            char ch = raw.charAt(i);
            if (startNewString) {
                assert ch == '\"';
                startNewString = false;
                continue;
            }
            if (ch == '\"') { // end-of-string
                this.stringTable.add(buffer.toString());

                buffer.setLength(0);
                startNewString = true;

                if (i != raw.length() - 1) {
                    assert raw.charAt(i + 1) == ',';
                    i++;
                }
                continue;
            }
            if (ch == JS_ESCAPE_CHAR) {
                i++;
                ch = raw.charAt(i);
                switch (ch) {
                case '0': // \0
                    buffer.append('\u0000');
                    break;
                case 'b': // \b
                    buffer.append('\b');
                    break;
                case 't': // \t
                    buffer.append('\t');
                    break;
                case 'n': // \n
                    buffer.append('\n');
                    break;
                case 'f': // \f
                    buffer.append('\f');
                    break;
                case 'r': // \r
                    buffer.append('\r');
                    break;
                case '\"': // \"
                    buffer.append('\"');
                    break;
                case '\\': // \\
                    buffer.append('\\');
                    break;
                case 'x': // \\xNN
                    b1 = hex2byte(raw.charAt(++i));
                    b2 = hex2byte(raw.charAt(++i));
                    ch = (char) (b1 * 16 + b2);
                    buffer.append(ch);
                    break;
                case 'u': // \\uNNNN
                    b1 = hex2byte(raw.charAt(++i));
                    b2 = hex2byte(raw.charAt(++i));
                    b3 = hex2byte(raw.charAt(++i));
                    b4 = hex2byte(raw.charAt(++i));
                    ch = (char) (b1 * 16 * 16 * 16 + b2 * 16 * 16 + b3 * 16 + b4);
                    buffer.append(ch);
                    break;
                default:
                    // TODO:
                    System.out.println("???");
                }
            } else {
                buffer.append(ch);
            }
        }
    }

    private byte hex2byte(char ch) {
        if ((ch >= '0') && (ch <= '9')) {
            return (byte) (ch - '0');
        }
        if ((ch >= 'A') && (ch <= 'F')) {
            return (byte) (ch - 'A' + 10);
        }
        if ((ch >= 'a') && (ch <= 'f')) {
            return (byte) (ch - 'a' + 10);
        }

        return -1;
    }
}