Java tutorial
/* GRANITE DATA SERVICES Copyright (C) 2007 ADEQUATE SYSTEMS SARL This file is part of Granite Data Services. Granite Data Services is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Granite Data Services is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <http://www.gnu.org/licenses/>. */ package com.exadel.flamingo.flex.messaging.amf.io; import java.io.DataInputStream; import java.io.Externalizable; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInput; import java.io.UTFDataFormatException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; import com.exadel.flamingo.flex.amf.AMF3Constants; import com.exadel.flamingo.flex.messaging.amf.io.util.ActionScriptClassDescriptor; import com.exadel.flamingo.flex.messaging.amf.io.util.DefaultActionScriptClassDescriptor; import com.exadel.flamingo.flex.messaging.amf.io.util.externalizer.Externalizer; import com.exadel.flamingo.flex.messaging.amf.io.util.instanciator.AbstractInstanciator; import com.exadel.flamingo.flex.messaging.util.StringUtil; import com.exadel.flamingo.flex.messaging.util.XMLUtil; /** * @author Franck WOLFF */ public class AMF3Deserializer extends DataInputStream implements ObjectInput, AMF3Constants { /////////////////////////////////////////////////////////////////////////// // Fields. protected static final Log log = LogFactory.getLog(AMF3Deserializer.class); protected static final Log logMore = LogFactory.getLog(AMF3Deserializer.class.getName() + ".MORE"); protected final boolean debug; protected final boolean debugMore; protected final List<String> storedStrings = new ArrayList<String>(); protected final List<Object> storedObjects = new ArrayList<Object>(); protected final List<ActionScriptClassDescriptor> storedClassDescriptors = new ArrayList<ActionScriptClassDescriptor>(); protected final XMLUtil xmlUtil = new XMLUtil(); /////////////////////////////////////////////////////////////////////////// // Constructor. public AMF3Deserializer(InputStream in) { super(in); this.debug = log.isDebugEnabled(); this.debugMore = logMore.isDebugEnabled(); if (debugMore) debug("new AMF3Deserializer(in=", in, ")"); } /////////////////////////////////////////////////////////////////////////// // ObjectInput implementation. public Object readObject() throws IOException { if (debugMore) debug("readObject()..."); int type = readAMF3Integer(); return readObject(type); } /////////////////////////////////////////////////////////////////////////// // AMF3 deserialization. protected Object readObject(int type) throws IOException { if (debugMore) debug("readObject(type=", Integer.valueOf(type), ")"); switch (type) { case AMF3_UNDEFINED: // 0x00; case AMF3_NULL: // 0x01; return null; case AMF3_BOOLEAN_FALSE: // 0x02; return Boolean.FALSE; case AMF3_BOOLEAN_TRUE: // 0x03; return Boolean.TRUE; case AMF3_INTEGER: // 0x04; return Integer.valueOf(readAMF3Integer()); case AMF3_NUMBER: // 0x05; return readAMF3Double(); case AMF3_STRING: // 0x06; return readAMF3String(); case AMF3_XML: // 0x07; return readAMF3Xml(); case AMF3_DATE: // 0x08; return readAMF3Date(); case AMF3_ARRAY: // 0x09; return readAMF3Array(); case AMF3_OBJECT: // 0x0A; return readAMF3Object(); case AMF3_XMLSTRING: // 0x0B; return readAMF3XmlString(); case AMF3_BYTEARRAY: // 0x0C; return readAMF3ByteArray(); default: throw new IllegalArgumentException("Unknown type: " + type); } } protected int readAMF3Integer() throws IOException { int result = 0; int n = 0; int b = readUnsignedByte(); while ((b & 0x80) != 0 && n < 3) { result <<= 7; result |= (b & 0x7f); b = readUnsignedByte(); n++; } if (n < 3) { result <<= 7; result |= b; } else { result <<= 8; result |= b; if ((result & 0x10000000) != 0) result |= 0xe0000000; } if (debugMore) debug("readAMF3Integer() -> ", Integer.valueOf(result)); System.out.println("readAMF3Integer() -> " + Integer.valueOf(result)); return result; } protected Double readAMF3Double() throws IOException { double d = readDouble(); Double result = (Double.isNaN(d) ? null : Double.valueOf(d)); if (debugMore) debug("readAMF3Double() -> ", result); return result; } protected String readAMF3String() throws IOException { String result = null; if (debugMore) debug("readAMF3String()..."); int type = readAMF3Integer(); if ((type & 0x01) == 0) // stored string result = getFromStoredStrings(type >> 1); else { int length = type >> 1; if (debugMore) debug("readAMF3String() - length=", String.valueOf(length)); if (length > 0) { byte[] utfBytes = new byte[length]; char[] utfChars = new char[length]; readFully(utfBytes); int c, c2, c3, iBytes = 0, iChars = 0; while (iBytes < length) { c = utfBytes[iBytes++] & 0xFF; if (c <= 0x7F) utfChars[iChars++] = (char) c; else { switch (c >> 4) { case 12: case 13: c2 = utfBytes[iBytes++]; if ((c2 & 0xC0) != 0x80) throw new UTFDataFormatException("Malformed input around byte " + (iBytes - 2)); utfChars[iChars++] = (char) (((c & 0x1F) << 6) | (c2 & 0x3F)); break; case 14: c2 = utfBytes[iBytes++]; c3 = utfBytes[iBytes++]; if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) throw new UTFDataFormatException("Malformed input around byte " + (iBytes - 3)); utfChars[iChars++] = (char) (((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | ((c3 & 0x3F) << 0)); break; default: throw new UTFDataFormatException("Malformed input around byte " + (iBytes - 1)); } } } result = new String(utfChars, 0, iChars); if (debugMore) debug("readAMF3String() - result=", StringUtil.toString(result)); addToStoredStrings(result); } else result = ""; } if (debugMore) debug("readAMF3String() -> ", StringUtil.toString(result)); return result; } protected Date readAMF3Date() throws IOException { Date result = null; int type = readAMF3Integer(); if ((type & 0x01) == 0) // stored Date result = (Date) getFromStoredObjects(type >> 1); else { result = new Date((long) readDouble()); addToStoredObjects(result); } if (debugMore) debug("readAMF3Date() -> ", result); return result; } protected Object readAMF3Array() throws IOException { Object result = null; int type = readAMF3Integer(); if ((type & 0x01) == 0) // stored array. result = getFromStoredObjects(type >> 1); else { final int size = type >> 1; String key = readAMF3String(); if (key.length() == 0) { Object[] objects = new Object[size]; addToStoredObjects(objects); for (int i = 0; i < size; i++) objects[i] = readObject(); result = objects; } else { Map<Object, Object> map = new HashMap<Object, Object>(); addToStoredObjects(map); while (key.length() > 0) { map.put(key, readObject()); key = readAMF3String(); } for (int i = 0; i < size; i++) map.put(Integer.valueOf(i), readObject()); result = map; } } if (debugMore) debug("readAMF3Array() -> ", result); return result; } protected Object readAMF3Object() throws IOException { if (debug) debug("readAMF3Object()..."); Object result = null; int type = readAMF3Integer(); if (debug) debug("readAMF3Object() - type=", Integer.valueOf(type)); if ((type & 0x01) == 0) // stored object. result = getFromStoredObjects(type >> 1); else { boolean inlineClassDef = (((type >> 1) & 0x01) != 0); if (debug) debug("readAMF3Object() - inlineClassDef=", String.valueOf(inlineClassDef)); // read class decriptor. ActionScriptClassDescriptor desc = null; if (inlineClassDef) { int propertiesCount = type >> 4; if (debug) debug("readAMF3Object() - propertiesCount=", String.valueOf(propertiesCount)); byte encoding = (byte) ((type >> 2) & 0x03); if (debug) debug("readAMF3Object() - encoding=", Byte.valueOf(encoding)); String className = readAMF3String(); if (debug) debug("readAMF3Object() - className=", StringUtil.toString(className)); desc = new DefaultActionScriptClassDescriptor(className, encoding); addToStoredClassDescriptors(desc); if (debug) debug("readAMF3Object() - defining ", String.valueOf(propertiesCount), " properties..."); for (int i = 0; i < propertiesCount; i++) { String name = readAMF3String(); System.out.println("readAMF3Object() - defining property name=" + name); if (debug) debug("readAMF3Object() - defining property name=", name); //bugging out here desc.defineProperty(name); } } else desc = getFromStoredClassDescriptors(type >> 2); if (debug) debug("readAMF3Object() - actionScriptClassDescriptor=", desc); int objectEncoding = desc.getEncoding(); // Find externalizer and create Java instance. Externalizer externalizer = desc.getExternalizer(); if (externalizer != null) { try { result = externalizer.newInstance(desc.getType(), this); } catch (Exception e) { throw new RuntimeException("Could not instantiate type: " + desc.getType(), e); } } else result = desc.newJavaInstance(); int index = addToStoredObjects(result); // read object content... if ((objectEncoding & 0x01) != 0) { // externalizer. if (externalizer != null) { if (debug) debug("readAMF3Object() - using externalizer=", externalizer); try { externalizer.readExternal(result, this); } catch (IOException e) { throw e; } catch (Exception e) { throw new RuntimeException("Could not read externalized object: " + result, e); } } // legacy externalizable. else { if (debug) debug("readAMF3Object() - legacy Externalizable=", result.getClass()); try { ((Externalizable) result).readExternal(this); } catch (IOException e) { throw e; } catch (Exception e) { throw new RuntimeException("Could not read externalizable object: " + result, e); } } } else { // defined values... if (desc.getPropertiesCount() > 0) { if (debug) debug("readAMF3Object() - reading defined properties..."); System.out.println("readAMF3Object() - reading defined properties..."); for (int i = 0; i < desc.getPropertiesCount(); i++) { byte vType = readByte(); Object value = readObject(vType); if (debug) debug("readAMF3Object() - setting defined property: ", desc.getPropertyName(i), "=", StringUtil.toString(value)); desc.setPropertyValue(i, result, value); } } // dynamic values... if (objectEncoding == 0x02) { if (debug) debug("readAMF3Object() - reading dynamic properties..."); while (true) { String name = readAMF3String(); if (name.length() == 0) break; byte vType = readByte(); Object value = readObject(vType); if (debug) debug("readAMF3Object() - setting dynamic property: ", name, "=", StringUtil.toString(value)); desc.setPropertyValue(name, result, value); } } } if (result instanceof AbstractInstanciator) { if (debug) debug("readAMF3Object() - resolving instanciator..."); try { result = ((AbstractInstanciator<?>) result).resolve(); } catch (Exception e) { throw new RuntimeException("Could not instantiate object: " + result, e); } setStoredObject(index, result); } } if (debug) debug("readAMF3Object() -> ", result); return result; } protected Document readAMF3Xml() throws IOException { String xml = readAMF3XmlString(); Document result = xmlUtil.buildDocument(xml); if (debugMore) debug("readAMF3Xml() -> ", result); return result; } protected String readAMF3XmlString() throws IOException { String result = null; int type = readAMF3Integer(); if ((type & 0x01) == 0) // stored String result = getFromStoredStrings(type >> 1); else { byte[] bytes = readBytes(type >> 1); result = new String(bytes, "UTF-8"); addToStoredStrings(result); } if (debugMore) debug("readAMF3XmlString() -> ", StringUtil.toString(result)); return result; } protected byte[] readAMF3ByteArray() throws IOException { byte[] result = null; int type = readAMF3Integer(); if ((type & 0x01) == 0) // stored object. result = (byte[]) getFromStoredObjects(type >> 1); else { result = readBytes(type >> 1); addToStoredObjects(result); } if (debugMore) debug("readAMF3ByteArray() -> ", result); return result; } /////////////////////////////////////////////////////////////////////////// // Cached objects methods. protected void addToStoredStrings(String s) { if (debug) debug("addToStoredStrings(s=", StringUtil.toString(s), ") at index=", String.valueOf(storedStrings.size())); storedStrings.add(s); } protected String getFromStoredStrings(int index) { if (debug) debug("getFromStoredStrings(index=", String.valueOf(index), ")"); String s = storedStrings.get(index); if (debug) debug("getFromStoredStrings() -> ", StringUtil.toString(s)); return s; } protected int addToStoredObjects(Object o) { int index = storedObjects.size(); if (debug) debug("addToStoredObjects(o=", o, ") at index=", String.valueOf(index)); storedObjects.add(o); return index; } protected void setStoredObject(int index, Object o) { if (debug) debug("setStoredObject(index=", String.valueOf(index), ", o=", o, ")"); storedObjects.set(index, o); } protected Object getFromStoredObjects(int index) { if (debug) debug("getFromStoredObjects(index=", String.valueOf(index), ")"); Object o = storedObjects.get(index); if (debug) debug("getFromStoredObjects() -> ", o); return o; } protected void addToStoredClassDescriptors(ActionScriptClassDescriptor desc) { if (debug) debug("addToStoredClassDescriptors(desc=", desc, ") at index=", String.valueOf(storedClassDescriptors.size())); storedClassDescriptors.add(desc); } protected ActionScriptClassDescriptor getFromStoredClassDescriptors(int index) { if (debug) debug("getFromStoredClassDescriptors(index=", String.valueOf(index), ")"); ActionScriptClassDescriptor desc = storedClassDescriptors.get(index); if (debug) debug("getFromStoredClassDescriptors() -> ", desc); return desc; } /////////////////////////////////////////////////////////////////////////// // Utilities. protected byte[] readBytes(int count) throws IOException { byte[] bytes = new byte[count]; readFully(bytes); return bytes; } protected void debug(Object... msgs) { debug(null, msgs); } protected void debug(Throwable t, Object... msgs) { String message = ""; if (msgs != null && msgs.length > 0) { if (msgs.length == 1) message = String.valueOf(msgs[0]); else { StringBuilder sb = new StringBuilder(); for (Object o : msgs) { if (o instanceof String) sb.append(o); else sb.append(StringUtil.toString(o)); } message = sb.toString(); } } if (t != null) log.debug(message, t); else log.debug(message); } }