flex.messaging.io.amf.Amf3Input Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 flex.messaging.io.amf;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import flex.messaging.io.PropertyProxy;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.SerializationException;
import flex.messaging.io.UnknownTypeException;
import flex.messaging.io.amf.AmfTrace.VectorType;
import flex.messaging.log.Log;
import flex.messaging.log.LogCategories;
import flex.messaging.util.ClassUtil;
import flex.messaging.util.Trace;
/**
* Reads AMF 3 formatted data stream.
*
* This class intends to matches the Flash Player 8 C++ code
* in avmglue/DataIO.cpp
*
*
*
*/
public class Amf3Input extends AbstractAmfInput implements Amf3Types
{
/**
*
*/
protected List objectTable;
/**
*
*/
protected List stringTable;
/**
*
*/
protected List traitsTable;
public Amf3Input(SerializationContext context)
{
super(context);
stringTable = new ArrayList(64);
objectTable = new ArrayList(64);
traitsTable = new ArrayList(10);
}
/**
* Reset should be called before reading a top level object,
* such as a new header or a new body.
*/
@Override
public void reset()
{
super.reset();
stringTable.clear();
objectTable.clear();
traitsTable.clear();
}
public Object saveObjectTable()
{
Object table = objectTable;
objectTable = new ArrayList(64);
return table;
}
public void restoreObjectTable(Object table)
{
objectTable = (ArrayList) table;
}
public Object saveTraitsTable()
{
Object table = traitsTable;
traitsTable = new ArrayList(10);
return table;
}
public void restoreTraitsTable(Object table)
{
traitsTable = (ArrayList) table;
}
public Object saveStringTable()
{
Object table = stringTable;
stringTable = new ArrayList(64);
return table;
}
public void restoreStringTable(Object table)
{
stringTable = (ArrayList) table;
}
/**
* Public entry point to read a top level AMF Object, such as
* a header value or a message body.
* @return Object the object read
* @throws ClassNotFoundException, IOException when reading object process failed
*/
public Object readObject() throws ClassNotFoundException, IOException
{
int type = in.readByte();
Object value = readObjectValue(type);
return value;
}
/**
*
*/
protected Object readObjectValue(int type) throws ClassNotFoundException, IOException
{
Object value = null;
switch (type)
{
case kStringType:
ClassUtil.validateCreation(String.class);
value = readString();
if (isDebug)
trace.writeString((String)value);
break;
case kObjectType:
value = readScriptObject();
break;
case kArrayType:
value = readArray();
break;
case kFalseType:
ClassUtil.validateCreation(Boolean.class);
value = Boolean.FALSE;
if (isDebug)
trace.write(value);
break;
case kTrueType:
ClassUtil.validateCreation(Boolean.class);
value = Boolean.TRUE;
if (isDebug)
trace.write(value);
break;
case kIntegerType:
ClassUtil.validateCreation(Integer.class);
int i = readUInt29();
// Symmetric with writing an integer to fix sign bits for negative values...
i = (i << 3) >> 3;
value = new Integer(i);
if (isDebug)
trace.write(value);
break;
case kDoubleType:
value = Double.valueOf(readDouble());
break;
case kUndefinedType:
if (isDebug)
trace.writeUndefined();
break;
case kNullType:
if (isDebug)
trace.writeNull();
break;
case kXMLType:
case kAvmPlusXmlType:
value = readXml();
break;
case kDateType:
value = readDate();
break;
case kByteArrayType:
value = readByteArray();
break;
case kDictionaryType:
value = readDictionary();
break;
case kTypedVectorInt:
case kTypedVectorUint:
case kTypedVectorDouble:
case kTypedVectorObject:
value = readTypedVector(type);
break;
default:
// Unknown object type tag {type}
UnknownTypeException ex = new UnknownTypeException();
ex.setMessage(10301, new Object[]{new Integer(type)});
throw ex;
}
return value;
}
/** {@inheritDoc} */
@Override
public double readDouble() throws IOException
{
ClassUtil.validateCreation(Double.class);
double d = super.readDouble();
if (isDebug)
trace.write(d);
return d;
}
/**
*
*/
protected String readString() throws IOException
{
int ref = readUInt29();
if ((ref & 1) == 0) // This is a reference
return getStringReference(ref >> 1);
int len = (ref >> 1); // Read the string in
// writeString() special cases the empty string
// to avoid creating a reference.
if (0 == len)
return EMPTY_STRING;
String str = readUTF(len);
stringTable.add(str); // Remember String
return str;
}
/**
* Deserialize the bits of a date-time value w/o a prefixing type byte.
*/
protected Date readDate() throws IOException
{
ClassUtil.validateCreation(Date.class);
int ref = readUInt29();
if ((ref & 1) == 0) // This is a reference
return (Date)getObjectReference(ref >> 1);
long time = (long)in.readDouble();
Date d = new Date(time);
objectTable.add(d); //Remember Date
if (isDebug)
trace.write(d);
return d;
}
protected Object readDictionary() throws IOException, ClassNotFoundException
{
int ref = readUInt29();
if ((ref & 1) == 0) // This is a reference.
return getObjectReference(ref >> 1);
readBoolean(); // usingWeakTypes - irrelevant in Java.
int len = (ref >> 1);
Dictionary dictionary = (Hashtable)ClassUtil.createDefaultInstance(Hashtable.class, null, true /*validate*/);
objectTable.add(dictionary); // Remember the object.
if (isDebug)
trace.startAMFDictionary(objectTable.size() - 1);
for (int i = 0; i < len; i++)
{
if (isDebug) trace.startDictionaryElement();
Object key = readObjectOneLevelDown(true);
if (isDebug) trace.addDictionaryEquals();
Object value = readObjectOneLevelDown(true);
ClassUtil.validateAssignment(dictionary, key != null? key.toString() : null, value);
dictionary.put(key, value);
}
if (isDebug)
trace.endAMFDictionary();
return dictionary;
}
protected Object readTypedVector(int type) throws IOException, ClassNotFoundException
{
int ref = readUInt29();
if ((ref & 1) == 0) // This is a reference.
return getObjectReference(ref >> 1);
int len = (ref >> 1);
boolean fixed = readBoolean();
Object vector = null;
switch (type)
{
case kTypedVectorInt:
vector = readTypedIntVector(len, fixed);
break;
case kTypedVectorUint:
vector = readTypedUintVector(len, fixed);
break;
case kTypedVectorDouble:
vector = readTypedDoubleVector(len, fixed);
break;
case kTypedVectorObject:
vector = readTypedObjectVector(len, fixed);
break;
default:
// Unknown object type tag {type}
UnknownTypeException ex = new UnknownTypeException();
ex.setMessage(10301, new Object[]{Integer.valueOf(type)});
throw ex;
}
return vector;
}
@SuppressWarnings("unchecked")
protected Object readTypedIntVector(int len, boolean fixed) throws IOException
{
// Don't instantiate Array right away with the supplied size if it is more
// than INITIAL_ARRAY_CAPACITY in case the supplied size has been tampered.
boolean useListTemporarily = false;
Object vector;
if (fixed)
{
useListTemporarily = len > INITIAL_COLLECTION_CAPACITY;
if (useListTemporarily)
{
ClassUtil.validateCreation(ArrayList.class);
vector = new ArrayList(INITIAL_COLLECTION_CAPACITY);
}
else
{
ClassUtil.validateCreation(Integer[].class);
vector = new Integer[len];
}
}
else
{
ClassUtil.validateCreation(ArrayList.class);
int initialCapacity = len < INITIAL_COLLECTION_CAPACITY? len : INITIAL_COLLECTION_CAPACITY;
vector = new ArrayList(initialCapacity);
}
int objectId = rememberObject(vector);
if (isDebug)
trace.startAMFVector(objectTable.size() - 1, VectorType.INT);
for (int i = 0; i < len; i++)
{
if (isDebug)
trace.arrayElement(i);
ClassUtil.validateCreation(Integer.class);
int value = readInt();
if (isDebug)
trace.write(value);
Integer item = Integer.valueOf(value);
ClassUtil.validateAssignment(vector, i, item);
if (vector instanceof Integer[])
Array.set(vector, i, item);
else
((List)vector).add(item);
}
if (useListTemporarily)
{
vector = ((ArrayList)vector).toArray();
objectTable.set(objectId, vector);
}
if (isDebug)
trace.endAMFVector();
return vector;
}
@SuppressWarnings("unchecked")
protected Object readTypedUintVector(int len, boolean fixed) throws IOException
{
// Don't instantiate Array right away with the supplied size if it is more
// than INITIAL_ARRAY_CAPACITY in case the supplied size has been tampered.
boolean useListTemporarily = false;
Object vector;
if (fixed)
{
useListTemporarily = len > INITIAL_COLLECTION_CAPACITY;
if (useListTemporarily)
{
ClassUtil.validateCreation(ArrayList.class);
vector = new ArrayList(INITIAL_COLLECTION_CAPACITY);
}
else
{
ClassUtil.validateCreation(Long[].class);
vector = new Long[len];
}
}
else
{
ClassUtil.validateCreation(ArrayList.class);
int initialCapacity = len < INITIAL_COLLECTION_CAPACITY? len : INITIAL_COLLECTION_CAPACITY;
vector = new ArrayList(initialCapacity);
}
int objectId = rememberObject(vector);
if (isDebug)
trace.startAMFVector(objectTable.size() - 1, VectorType.UINT);
for (int i = 0; i < len; i++)
{
if (isDebug)
trace.arrayElement(i);
ClassUtil.validateCreation(Long.class);
long value = (long) (in.readByte() & 0xFF) << 24L;
value += (long) (in.readByte() & 0xFF) << 16L;
value += (long) (in.readByte() & 0xFF) << 8L;
value += (in.readByte() & 0xFF);
if (isDebug)
trace.write(value);
Long item = Long.valueOf(value);
ClassUtil.validateAssignment(vector, i, item);
if (vector instanceof Long[])
Array.set(vector, i, item);
else
((List)vector).add(item);
}
if (useListTemporarily)
{
vector = ((ArrayList)vector).toArray();
objectTable.set(objectId, vector);
}
if (isDebug)
trace.endAMFVector();
return vector;
}
@SuppressWarnings("unchecked")
protected Object readTypedDoubleVector(int len, boolean fixed) throws IOException
{
// Don't instantiate Array right away with the supplied size if it is more
// than INITIAL_ARRAY_CAPACITY in case the supplied size has been tampered.
boolean useListTemporarily = false;
Object vector;
if (fixed)
{
useListTemporarily = len > INITIAL_COLLECTION_CAPACITY;
if (useListTemporarily)
{
ClassUtil.validateCreation(ArrayList.class);
vector = new ArrayList(INITIAL_COLLECTION_CAPACITY);
}
else
{
ClassUtil.validateCreation(Double[].class);
vector = new Double[len];
}
}
else
{
ClassUtil.validateCreation(ArrayList.class);
int initialCapacity = len < INITIAL_COLLECTION_CAPACITY? len : INITIAL_COLLECTION_CAPACITY;
vector = new ArrayList(initialCapacity);
}
int objectId = rememberObject(vector);
if (isDebug)
trace.startAMFVector(objectTable.size() - 1, VectorType.DOUBLE);
for (int i = 0; i < len; i++)
{
if (isDebug)
trace.arrayElement(i);
Double item = Double.valueOf(readDouble());
ClassUtil.validateAssignment(vector, i, item);
if (vector instanceof Double[])
Array.set(vector, i, item);
else
((List)vector).add(item);
}
if (useListTemporarily)
{
vector = ((ArrayList)vector).toArray();
objectTable.set(objectId, vector);
}
if (isDebug)
trace.endAMFVector();
return vector;
}
@SuppressWarnings("unchecked")
protected Object readTypedObjectVector(int len, boolean fixed) throws IOException, ClassNotFoundException
{
// TODO - Class name is always empty for some reason, need to figure out if this is expected.
String className = readString();
if (className == null || className.length() == 0 || className.equals(EMPTY_STRING))
className = Object.class.getName();
// Don't instantiate Array right away with the supplied size if it is more
// than INITIAL_ARRAY_CAPACITY in case the supplied size has been tampered.
boolean useListTemporarily = false;
Object vector;
if (fixed)
{
useListTemporarily = len > INITIAL_COLLECTION_CAPACITY;
if (useListTemporarily)
{
ClassUtil.validateCreation(ArrayList.class);
vector = new ArrayList