JClientSerializationStreamWriter.java :  » GWT » gwtrpc4j » org » gwtrpc4j » stream » Java Open Source

Java Open Source » GWT » gwtrpc4j 
gwtrpc4j » org » gwtrpc4j » stream » JClientSerializationStreamWriter.java
package org.gwtrpc4j.stream;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
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.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.impl.Serializer;
import com.google.gwt.user.server.Base64Utils;
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.TypeNameObfuscator;

public class JClientSerializationStreamWriter extends
    com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter {

  /**
   * Translate from javascript Quote characters in a user-supplied string to
   * make sure they are safe to send to the server.
   * 
   * @param str
   *            string to quote
   * @return quoted string
   */
  public static String quoteString(String str) {
    final String regex = "[\u0000\\|\\\uD800-\uFFFF]";
    int idx = 0;
    String out = "";
    Pattern p = Pattern.compile(regex);

    Matcher m = p.matcher(str);

    while (m.find()) {
      out += str.substring(idx, m.start());
      idx = m.end();
      char ch = str.charAt(m.start());
      ;
      if (ch == 0) {
        out += "\\0";
      } else if (ch == 92) { // backslash
        out += "\\\\";
      } else if (ch == 124) { // vertical bar
        // 124 = "|" = AbstractSerializationStream.RPC_SEPARATOR_CHAR
        out += "\\!";
      } else {
        String hex = Integer.toHexString(ch);
        out += "\\u0000".substring(0, 6 - hex.length()) + hex;
      }
    }
    return out + str.substring(idx);
  }

  @Override
  public int addString(String string) {
    return super.addString(string);
  }

  private static void append(StringBuffer sb, String token) {
    assert token != null;
    sb.append(token);
    sb.append(RPC_SEPARATOR_CHAR);
  }

  private StringBuffer encodeBuffer;

  private final String moduleBaseURL;

  private final String serializationPolicyStrongName;

  /**
   * Constructs a <code>ClientSerializationStreamWriter</code> using the
   * specified module base URL and the serialization policy.
   * 
   * @param serializer
   *            the {@link Serializer} to use
   * @param moduleBaseURL
   *            the location of the module
   * @param serializationPolicyStrongName
   *            the strong name of serialization policy
   */
  public JClientSerializationStreamWriter(SerializationPolicy sp,
      String moduleBaseURL, String serializationPolicyStrongName) {

    this.serializationPolicy = sp;
    this.moduleBaseURL = moduleBaseURL;
    this.serializationPolicyStrongName = serializationPolicyStrongName;
  }

  /**
   * Call this method before attempting to append any tokens. This method
   * implementation <b>must</b> be called by any overridden version.
   */
  @Override
  public void prepareToWrite() {
    super.prepareToWrite();
    encodeBuffer = new StringBuffer();

    // Write serialization policy info
    writeString(moduleBaseURL);
    writeString(serializationPolicyStrongName);
  }

  @Override
  public String toString() {
    StringBuffer buffer = new StringBuffer();
    writeHeader(buffer);
    writeStringTable(buffer);
    writePayload(buffer);
    return buffer.toString();
  }

  @Override
  public void writeLong(long fieldValue) {
    /*
     * Client code represents longs internally as an array of two Numbers.
     * In order to make serialization of longs faster, we'll send the
     * component parts so that the value can be directly reconstituted on
     * the server.
     */
    double[] parts;
    parts = makeLongComponents((int) (fieldValue >> 32), (int) fieldValue);
    assert parts.length == 2;
    writeDouble(parts[0]);
    writeDouble(parts[1]);
  }

  /**
   * Appends a token to the end of the buffer.
   */
  @Override
  protected void append(String token) {
    append(encodeBuffer, token);
  }

  //
  // @Override
  // protected String getObjectTypeSignature(Object o) {
  // Class<?> clazz = o.getClass();
  //
  // if (o instanceof Enum) {
  // Enum<?> e = (Enum<?>) o;
  // clazz = e.getDeclaringClass();
  // }
  //
  // return serializer.getSerializationSignature(clazz);
  // }

  private void writeHeader(StringBuffer buffer) {
    append(buffer, String.valueOf(getVersion()));
    append(buffer, String.valueOf(getFlags()));
  }

  private void writePayload(StringBuffer buffer) {
    buffer.append(encodeBuffer.toString());
  }

  private StringBuffer writeStringTable(StringBuffer buffer) {
    List<String> stringTable = getStringTable();
    append(buffer, String.valueOf(stringTable.size()));
    for (String s : stringTable) {
      append(buffer, quoteString(s));
    }
    return buffer;
  }

  /**
   * Enumeration used to provided typed instance writers.
   */
  private enum ValueWriter {
    BOOLEAN {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeBoolean(((Boolean) instance).booleanValue());
      }
    },
    BYTE {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeByte(((Byte) instance).byteValue());
      }
    },
    CHAR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeChar(((Character) instance).charValue());
      }
    },
    DOUBLE {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeDouble(((Double) instance).doubleValue());
      }
    },
    FLOAT {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeFloat(((Float) instance).floatValue());
      }
    },
    INT {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeInt(((Integer) instance).intValue());
      }
    },
    LONG {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeLong(((Long) instance).longValue());
      }
    },
    OBJECT {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance)
          throws SerializationException {
        stream.writeObject(instance);
      }
    },
    SHORT {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeShort(((Short) instance).shortValue());
      }
    },
    STRING {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        stream.writeString((String) instance);
      }
    };

    abstract void write(JClientSerializationStreamWriter stream,
        Object instance) throws SerializationException;
  }

  /**
   * Enumeration used to provided typed vector writers.
   */
  private enum VectorWriter {
    BOOLEAN_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        boolean[] vector = (boolean[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeBoolean(vector[i]);
        }
      }
    },
    BYTE_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        byte[] vector = (byte[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeByte(vector[i]);
        }
      }
    },
    CHAR_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        char[] vector = (char[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeChar(vector[i]);
        }
      }
    },
    DOUBLE_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        double[] vector = (double[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeDouble(vector[i]);
        }
      }
    },
    FLOAT_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        float[] vector = (float[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeFloat(vector[i]);
        }
      }
    },
    INT_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        int[] vector = (int[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeInt(vector[i]);
        }
      }
    },
    LONG_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        long[] vector = (long[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeLong(vector[i]);
        }
      }
    },
    OBJECT_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance)
          throws SerializationException {
        Object[] vector = (Object[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeObject(vector[i]);
        }
      }
    },
    SHORT_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        short[] vector = (short[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeShort(vector[i]);
        }
      }
    },
    STRING_VECTOR {
      @Override
      void write(JClientSerializationStreamWriter stream, Object instance) {
        String[] vector = (String[]) instance;
        stream.writeInt(vector.length);
        for (int i = 0, n = vector.length; i < n; ++i) {
          stream.writeString(vector[i]);
        }
      }
    };

    abstract void write(JClientSerializationStreamWriter stream,
        Object instance) throws SerializationException;
  }

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

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

  /**
   * Number of escaped JS Chars.
   */
  private static final int NUMBER_OF_JS_ESCAPED_CHARS = 128;

  /**
   * A list of any characters that need escaping when printing a JavaScript
   * string literal. Contains a 0 if the character does not need escaping,
   * otherwise contains the character to escape with.
   */
  private static final char[] JS_CHARS_ESCAPED = new char[NUMBER_OF_JS_ESCAPED_CHARS];

  /**
   * This defines the character used by JavaScript to mark the start of an
   * escape sequence.
   */
  private static final char JS_ESCAPE_CHAR = '\\';

  /**
   * This defines the character used to enclose JavaScript strings.
   */
  private static final char JS_QUOTE_CHAR = '\"';

  /**
   * Index into this array using a nibble, 4 bits, to get the corresponding
   * hexa-decimal character representation.
   */
  private static final char NIBBLE_TO_HEX_CHAR[] = { '0', '1', '2', '3', '4',
      '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

  private static final char NON_BREAKING_HYPHEN = '\u2011';

  static {
    /*
     * NOTE: The JS VM in IE6 & IE7 do not interpret \v correctly. They
     * convert JavaScript Vertical Tab character '\v' into 'v'. As such, we
     * do not use the short form of the unicode escape here.
     */
    JS_CHARS_ESCAPED['\u0000'] = '0';
    JS_CHARS_ESCAPED['\b'] = 'b';
    JS_CHARS_ESCAPED['\t'] = 't';
    JS_CHARS_ESCAPED['\n'] = 'n';
    JS_CHARS_ESCAPED['\f'] = 'f';
    JS_CHARS_ESCAPED['\r'] = 'r';
    JS_CHARS_ESCAPED[JS_ESCAPE_CHAR] = JS_ESCAPE_CHAR;
    JS_CHARS_ESCAPED[JS_QUOTE_CHAR] = JS_QUOTE_CHAR;

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

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

  /**
   * This method takes a string and outputs a JavaScript string literal. The
   * data is surrounded with quotes, and any contained characters that need to
   * be escaped are mapped onto their escape sequence.
   * 
   * Assumptions: We are targeting a version of JavaScript that that is later
   * than 1.3 that supports unicode strings.
   */
  public static String escapeString(String toEscape) {
    // make output big enough to escape every character (plus the quotes)
    char[] input = toEscape.toCharArray();
    CharVector charVector = new CharVector(input.length * 2 + 2,
        input.length);

    charVector.add(JS_QUOTE_CHAR);

    for (int i = 0, n = input.length; i < n; ++i) {
      char c = input[i];
      if (needsUnicodeEscape(c)) {
        unicodeEscape(c, charVector);
      } else {
        charVector.add(c);
      }
    }

    charVector.add(JS_QUOTE_CHAR);
    return String.valueOf(charVector.asArray(), 0, charVector.getSize());
  }

  /**
   * Returns the {@link Class} instance to use for serialization. Enumerations
   * are serialized as their declaring class while all others are serialized
   * using their true class instance.
   */
  private static Class<?> getClassForSerialization(Object instance) {
    assert instance != null;

    if (instance instanceof Enum) {
      Enum<?> e = (Enum<?>) instance;
      return e.getDeclaringClass();
    } else {
      return instance.getClass();
    }
  }

  /**
   * Returns <code>true</code> if the character requires the \\uXXXX unicode
   * character escape sequence. This is necessary if the raw character could
   * be consumed and/or interpreted as a special character when the JSON
   * encoded response is evaluated. For example, 0x2028 and 0x2029 are
   * alternate line endings for JS per ECMA-232, which are respected by
   * Firefox and Mozilla.
   * <p>
   * Notes:
   * <ol>
   * <li>The following cases are a more conservative set of cases which are
   * are in the future proofing space as opposed to the required minimal set.
   * We could remove these and still pass our tests.
   * <ul>
   * <li>UNASSIGNED - 6359</li>
   * <li>NON_SPACING_MARK - 530</li>
   * <li>ENCLOSING_MARK - 10</li>
   * <li>COMBINING_SPACE_MARK - 131</li>
   * <li>SPACE_SEPARATOR - 19</li>
   * <li>CONTROL - 65</li>
   * <li>PRIVATE_USE - 6400</li>
   * <li>DASH_PUNCTUATION - 1</li>
   * <li>Total Characters Escaped: 13515</li>
   * </ul>
   * </li>
   * <li>The following cases are the minimal amount of escaping required to
   * prevent test failure.
   * <ul>
   * <li>LINE_SEPARATOR - 1</li>
   * <li>PARAGRAPH_SEPARATOR - 1</li>
   * <li>FORMAT - 32</li>
   * <li>SURROGATE - 2048</li>
   * <li>Total Characters Escaped: 2082</li></li>
   * </ul> </li>
   * </ol>
   * 
   * @param ch
   *            character to check
   * @return <code>true</code> if the character requires the \\uXXXX unicode
   *         character escape
   */
  private static boolean needsUnicodeEscape(char ch) {
    switch (ch) {
    case ' ':
      // ASCII space gets caught in SPACE_SEPARATOR below, but does not
      // need to be escaped
      return false;
    case JS_QUOTE_CHAR:
    case JS_ESCAPE_CHAR:
      // these must be quoted or they will break the protocol
      return true;
    case NON_BREAKING_HYPHEN:
      // This can be expanded into a break followed by a hyphen
      return true;
    default:
      switch (Character.getType(ch)) {
      // Conservative
      case Character.COMBINING_SPACING_MARK:
      case Character.ENCLOSING_MARK:
      case Character.NON_SPACING_MARK:
      case Character.UNASSIGNED:
      case Character.PRIVATE_USE:
      case Character.SPACE_SEPARATOR:
      case Character.CONTROL:

        // Minimal
      case Character.LINE_SEPARATOR:
      case Character.FORMAT:
      case Character.PARAGRAPH_SEPARATOR:
      case Character.SURROGATE:
        return true;

      default:
        break;
      }
      break;
    }
    return false;
  }

  /**
   * Writes a safe escape sequence for a character. Some characters have a
   * short form, such as \n for U+000D, while others are represented as \\xNN
   * or \\uNNNN.
   * 
   * @param ch
   *            character to unicode escape
   * @param charVector
   *            char vector to receive the unicode escaped representation
   */
  private static void unicodeEscape(char ch, CharVector charVector) {
    charVector.add(JS_ESCAPE_CHAR);
    if (ch < NUMBER_OF_JS_ESCAPED_CHARS && JS_CHARS_ESCAPED[ch] != 0) {
      charVector.add(JS_CHARS_ESCAPED[ch]);
    } else if (ch < 256) {
      charVector.add('x');
      charVector.add(NIBBLE_TO_HEX_CHAR[ch >> 4 & 0x0F]);
      charVector.add(NIBBLE_TO_HEX_CHAR[ch & 0x0F]);
    } else {
      charVector.add('u');
      charVector.add(NIBBLE_TO_HEX_CHAR[ch >> 12 & 0x0F]);
      charVector.add(NIBBLE_TO_HEX_CHAR[ch >> 8 & 0x0F]);
      charVector.add(NIBBLE_TO_HEX_CHAR[ch >> 4 & 0x0F]);
      charVector.add(NIBBLE_TO_HEX_CHAR[ch & 0x0F]);
    }
  }

  private final SerializationPolicy serializationPolicy;

  public void serializeValue(Object value, Class<?> type)
      throws SerializationException {
    ValueWriter valueWriter = CLASS_TO_VALUE_WRITER.get(type);
    if (valueWriter != null) {
      valueWriter.write(this, value);
    } else {
      // Arrays of primitive or reference types need to go through
      // writeObject.
      ValueWriter.OBJECT.write(this, value);
    }
  }

  @Override
  protected String getObjectTypeSignature(Object instance)
      throws SerializationException {
    assert instance != null;

    Class<?> clazz = getClassForSerialization(instance);
    if (hasFlags(FLAG_ELIDE_TYPE_NAMES)) {
      if (serializationPolicy instanceof TypeNameObfuscator) {
        return ((TypeNameObfuscator) serializationPolicy)
            .getTypeIdForClass(clazz);
      }

      throw new SerializationException(
          "The GWT module was compiled with RPC "
              + "type name elision enabled, but "
              + serializationPolicy.getClass().getName()
              + " does not implement "
              + TypeNameObfuscator.class.getName());
    } else {
      return SerializabilityUtil.encodeSerializedInstanceReference(clazz,
          serializationPolicy);
    }
  }

  @Override
  protected void serialize(Object instance, String typeSignature)
      throws SerializationException {
    assert instance != null;

    Class<?> clazz = getClassForSerialization(instance);

    try {
      serializationPolicy.validateSerialize(clazz);
    } catch (SerializationException e) {
      throw new SerializationException(e.getMessage() + ": instance = "
          + instance);
    }
    serializeImpl(instance, clazz);
  }

  /**
   * Serialize an instance that is an array. Will default to serializing the
   * instance as an Object vector if the instance is not a vector of
   * primitives, Strings or Object.
   * 
   * @param instanceClass
   * @param instance
   * @throws SerializationException
   */
  private void serializeArray(Class<?> instanceClass, Object instance)
      throws SerializationException {
    assert instanceClass.isArray();

    VectorWriter instanceWriter = CLASS_TO_VECTOR_WRITER.get(instanceClass);
    if (instanceWriter != null) {
      instanceWriter.write(this, instance);
    } else {
      VectorWriter.OBJECT_VECTOR.write(this, instance);
    }
  }

  private void serializeClass(Object instance, Class<?> instanceClass)
      throws SerializationException {
    assert instance != null;
    Field[] serializableFields = SerializabilityUtil
        .applyFieldSerializationPolicy(instanceClass);

    /**
     * If clientFieldNames is non-null, identify any additional server-only
     * fields and serialize them separately. Java serialization is used to
     * construct a byte array, which is encoded as a String and written
     * prior to the rest of the field data.
     */
    Set<String> clientFieldNames = serializationPolicy
        .getClientFieldNamesForEnhancedClass(instanceClass);
    if (clientFieldNames != null) {
      List<Field> serverFields = new ArrayList<Field>();
      for (Field declField : serializableFields) {
        assert declField != null;

        // Identify server-only fields
        if (!clientFieldNames.contains(declField.getName())) {
          serverFields.add(declField);
          continue;
        }
      }

      // Serialize the server-only fields into a byte array and encode as
      // a String
      try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeInt(serverFields.size());
        for (Field f : serverFields) {
          oos.writeObject(f.getName());
          f.setAccessible(true);
          Object fieldData = f.get(instance);
          oos.writeObject(fieldData);
        }
        oos.close();

        byte[] serializedData = baos.toByteArray();
        String encodedData = Base64Utils.toBase64(serializedData);
        writeString(encodedData);
      } catch (IllegalAccessException e) {
        throw new SerializationException(e);
      } catch (IOException e) {
        throw new SerializationException(e);
      }
    }

    // Write the client-visible field data
    for (Field declField : serializableFields) {
      if (clientFieldNames != null
          && !clientFieldNames.contains(declField.getName())) {
        // Skip server-only fields
        continue;
      }

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

      Object value;
      try {
        value = declField.get(instance);
        serializeValue(value, declField.getType());

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

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

    Class<?> superClass = instanceClass.getSuperclass();
    if (serializationPolicy.shouldSerializeFields(superClass)) {
      serializeImpl(instance, superClass);
    }
  }

  private void serializeImpl(Object instance, Class<?> instanceClass)
      throws SerializationException {
    assert instance != null;

    Class<?> customSerializer = SerializabilityUtil
        .hasCustomFieldSerializer(instanceClass);
    if (customSerializer != null) {
      // Use custom field serializer
      serializeWithCustomSerializer(customSerializer, instance,
          instanceClass);
    } else if (instanceClass.isArray()) {
      serializeArray(instanceClass, instance);
    } else if (instanceClass.isEnum()) {
      writeInt(((Enum<?>) instance).ordinal());
    } else {
      // Regular class instance
      serializeClass(instance, instanceClass);
    }
  }

  private void serializeWithCustomSerializer(Class<?> customSerializer,
      Object instance, Class<?> instanceClass)
      throws SerializationException {

    try {
      assert !instanceClass.isArray();

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

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

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

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

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

}
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.