[Red5commits] [1650] updated AMF0 MixedArrays? (type 0x08) to evaluate length and max. index and to o
jbauch
luke at codegent.com
Wed Jan 24 11:14:36 EST 2007
updated AMF0 MixedArrays? (type 0x08) to evaluate length and max. index and to output proper values
worked on AMF3 object handling
Timestamp: 01/23/07 18:03:31 EST (17 hours ago)
Change: 1650
Author: jbauch
Files (see diff or trac for details):
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Input.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Output.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java
java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java
Trac: http://mirror1.cvsdude.com/trac/osflash/red5/changeset/1650
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java (revision 1648)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Input.java (revision 1650)
@@ -20,4 +20,5 @@
*/
+import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -283,39 +284,59 @@
Object result = null;
amf3_mode += 1;
+ // Load object properties into map
+ Map<String, Object> properties = new ObjectMap<String, Object>();
+ switch (type & 0x03) {
+ case AMF3.TYPE_OBJECT_PROPERTY:
+ int count = type >> 2;
+ List<String> propertyNames = new ArrayList<String>(count);
+ for (int i=0; i<count; i++) {
+ propertyNames.add(readString());
+ }
+ for (int i=0; i<count; i++) {
+ properties.put(propertyNames.get(i), deserializer.deserialize(this));
+ }
+ break;
+ case AMF3.TYPE_OBJECT_ANONYMOUS_PROPERTY:
+ properties.put("", deserializer.deserialize(this));
+ break;
+ case AMF3.TYPE_OBJECT_VALUE:
+ String key = readString();
+ while (!"".equals(key)) {
+ Object value = deserializer.deserialize(this);
+ properties.put(key, value);
+ key = readString();
+ }
+ break;
+ default:
+ case AMF3.TYPE_OBJECT_UNKNOWN:
+ throw new RuntimeException("Unknown object type: " + (type & 0x03));
+ }
+ amf3_mode -= 1;
+
+ // Create result object based on classname
if ("".equals(className)) {
// "anonymous" object, load as Map
- Map<String, Object> resultMap = new ObjectMap<String, Object>();
- storeReference(resultMap);
- switch (type & 0x03) {
- case AMF3.TYPE_OBJECT_PROPERTY:
- int count = type >> 2;
- List<String> propertyNames = new ArrayList<String>(count);
- for (int i=0; i<count; i++) {
- propertyNames.add(readString());
+ storeReference(properties);
+ result = properties;
+ } else if ("RecordSet".equals(className)) {
+ // TODO: how are RecordSet objects encoded?
+ throw new RuntimeException("Objects of type " + className + " not supported yet.");
+ } else if ("RecordSetPage".equals(className)) {
+ // TODO: how are RecordSetPage objects encoded?
+ throw new RuntimeException("Objects of type " + className + " not supported yet.");
+ } else {
+ // Apply properties to object
+ result = newInstance(className);
+ if (result != null) {
+ storeReference(properties);
+ for (Map.Entry<String, Object> entry: properties.entrySet()) {
+ try {
+ BeanUtils.setProperty(result, entry.getKey(), entry.getValue());
+ } catch (Exception e) {
+ log.error("Error mapping property: " + entry.getKey() + " (" + entry.getValue() + ")");
+ }
}
- for (int i=0; i<count; i++) {
- resultMap.put(propertyNames.get(i), deserializer.deserialize(this));
- }
- break;
- case AMF3.TYPE_OBJECT_ANONYMOUS_PROPERTY:
- resultMap.put("", deserializer.deserialize(this));
- break;
- case AMF3.TYPE_OBJECT_VALUE:
- String key = readString();
- while (!"".equals(key)) {
- Object value = deserializer.deserialize(this);
- resultMap.put(key, value);
- key = readString();
- }
- break;
- default:
- case AMF3.TYPE_OBJECT_UNKNOWN:
- throw new RuntimeException("Unknown object type: " + (type & 0x03));
- }
- result = resultMap;
- } else {
- throw new RuntimeException("Objects of type " + className + " not supported yet.");
- }
- amf3_mode -= 1;
+ } // else fall through
+ }
return result;
}
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java (revision 1648)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf3/Output.java (revision 1650)
@@ -21,10 +21,16 @@
import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-
+import java.util.Set;
+
+import org.apache.commons.collections.BeanMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -33,4 +39,5 @@
import org.red5.io.object.RecordSet;
import org.red5.io.object.Serializer;
+import org.red5.io.object.SerializerOpts.SerializerOption;
/**
@@ -301,4 +308,5 @@
}
+ storeReference(array);
// TODO: we could optimize this by storing the first integer
// keys after the key-value pairs
@@ -318,11 +326,149 @@
/** {@inheritDoc} */
+ protected void writeArbitraryObject(Object object, Serializer serializer) {
+ // If we need to serialize class information...
+ Class objectClass = object.getClass();
+ if (serializer.isOptEnabled(object, SerializerOption.SerializeClassName)) {
+ putString(objectClass.getName());
+ } else {
+ putString("");
+ }
+
+ // Store key/value pairs
+ amf3_mode += 1;
+ // Get public field values
+ Map<String, Object> values = new HashMap<String, Object>();
+ // Iterate thru fields of an object to build "name-value" map from it
+ for (Field field : objectClass.getFields()) {
+ int modifiers = field.getModifiers();
+ if (Modifier.isTransient(modifiers)) {
+ if (log.isDebugEnabled()) {
+ log.debug("Skipping " + field.getName() + " because its transient");
+ }
+ continue;
+ }
+
+ Object value;
+ try {
+ // Get field value
+ value = field.get(object);
+ } catch (IllegalAccessException err) {
+ // Swallow on private and protected properties access exception
+ continue;
+ }
+ // Put field to the map of "name-value" pairs
+ values.put(field.getName(), value);
+ }
+
+ // Output public values
+ Iterator<Map.Entry<String, Object>> it = values.entrySet().iterator();
+ // Iterate thru map and write out properties with separators
+ while (it.hasNext()) {
+ Map.Entry<String, Object> entry = it.next();
+ // Write out prop name
+ putString(entry.getKey());
+ // Write out
+ serializer.serialize(this, entry.getValue());
+ }
+ amf3_mode -= 1;
+ // Write out end of object marker
+ putString("");
+ }
+
+ /** {@inheritDoc} */
public void writeObject(Object object, Serializer serializer) {
- writeString("Not implemented.");
+ writeAMF3();
+ buf.put(AMF3.TYPE_OBJECT);
+ if (hasReference(object)) {
+ putInteger(getReferenceId(object) << 1);
+ return;
+ }
+
+ // We have an inline class that is not a reference.
+ // We store the properties using key/value pairs
+ int type = AMF3.TYPE_OBJECT_VALUE << 2 | 1 << 1 | 1;
+ putInteger(type);
+
+ // Create new map out of bean properties
+ BeanMap beanMap = new BeanMap(object);
+ // Set of bean attributes
+ Set set = beanMap.entrySet();
+ if ((set.size() == 0) || (set.size() == 1 && beanMap.containsKey("class"))) {
+ // BeanMap is empty or can only access "class" attribute, skip it
+ writeArbitraryObject(object, serializer);
+ return;
+ }
+
+ // Write out either start of object marker for class name or "empty" start of object marker
+ Class objectClass = object.getClass();
+ if (serializer.isOptEnabled(object, SerializerOption.SerializeClassName)) {
+ // classname
+ putString(objectClass.getName());
+ } else {
+ putString("");
+ }
+
+ // Store key/value pairs
+ amf3_mode += 1;
+ Iterator it = set.iterator();
+ while (it.hasNext()) {
+ BeanMap.Entry entry = (BeanMap.Entry) it.next();
+ String keyName = entry.getKey().toString();
+ if ("class".equals(keyName)) {
+ continue;
+ }
+
+ // Check if the Field corresponding to the getter/setter pair is transient
+ try {
+ Field field = objectClass.getDeclaredField(keyName);
+ int modifiers = field.getModifiers();
+
+ if (Modifier.isTransient(modifiers)) {
+ if (log.isDebugEnabled()) {
+ log.debug("Skipping " + field.getName() + " because its transient");
+ }
+ continue;
+ }
+ } catch (NoSuchFieldException nfe) {
+ // Ignore this exception and use the default behaviour
+ }
+
+ putString(buf, keyName);
+ serializer.serialize(this, entry.getValue());
+ }
+ amf3_mode -= 1;
+
+ // End of object marker
+ putString("");
}
/** {@inheritDoc} */
public void writeObject(Map<Object, Object> map, Serializer serializer) {
- writeMap(map, serializer);
+ writeAMF3();
+ buf.put(AMF3.TYPE_OBJECT);
+ if (hasReference(map)) {
+ putInteger(getReferenceId(map) << 1);
+ return;
+ }
+
+ storeReference(map);
+ // We have an inline class that is not a reference.
+ // We store the properties using key/value pairs
+ int type = AMF3.TYPE_OBJECT_VALUE << 2 | 1 << 1 | 1;
+ putInteger(type);
+
+ // No classname
+ putString("");
+
+ // Store key/value pairs
+ amf3_mode += 1;
+ for (Map.Entry<Object, Object> entry: map.entrySet()) {
+ putString(entry.getKey().toString());
+ serializer.serialize(this, entry.getValue());
+ }
+ amf3_mode -= 1;
+
+ // End of object marker
+ putString("");
}
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Input.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Input.java (revision 1648)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Input.java (revision 1650)
@@ -326,8 +326,6 @@
}
- boolean allNumbers = true;
Object result;
final Map<Object, Object> mixedResult = new LinkedHashMap<Object, Object>(maxNumber);
- Object length = null;
while (hasMoreProperties()) {
String key = getString(buf);
@@ -339,17 +337,8 @@
log.debug("item: " + item);
}
- try {
- mixedResult.put(Integer.parseInt(key), item);
- } catch (NumberFormatException err) {
- if (length == null && "length".equals(key)) {
- length = item;
- } else {
- mixedResult.put(key, item);
- allNumbers = false;
- }
- }
- }
-
- if (allNumbers) {
+ mixedResult.put(key, item);
+ }
+
+ if (mixedResult.size() <= maxNumber+1 && maxNumber == (Integer) mixedResult.get("length")) {
// MixedArray actually is a regular array
if (log.isDebugEnabled()) {
@@ -358,13 +347,16 @@
final List<Object> listResult = new ArrayList<Object>(maxNumber);
for (int i=0; i<maxNumber; i++) {
- listResult.add(i, mixedResult.get(i));
+ listResult.add(i, mixedResult.get(String.valueOf(i)));
}
result = listResult;
} else {
+ // Convert initial indexes
+ mixedResult.remove("length");
+ for (int i=0; i<maxNumber; i++) {
+ final Object value = mixedResult.remove(String.valueOf(i));
+ mixedResult.put(i, value);
+ }
result = mixedResult;
- if (length != null)
- mixedResult.put("length", length);
- }
-
+ }
storeReference(result);
skipEndObject();
Index: /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Output.java
===================================================================
--- /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Output.java (revision 1639)
+++ /java/server/branches/joachim_amf3_integration/src/org/red5/io/amf/Output.java (revision 1650)
@@ -123,8 +123,23 @@
storeReference(map);
buf.put(AMF.TYPE_MIXED_ARRAY);
- buf.putInt(map.size());
+ int maxInt=-1;
+ for (int i=0; i<map.size(); i++) {
+ if (!map.containsKey(i))
+ break;
+
+ maxInt = i;
+ }
+ buf.putInt(maxInt+1);
for (Map.Entry<Object, Object> entry : map.entrySet()) {
- putString(entry.getKey().toString());
+ final String key = entry.getKey().toString();
+ if ("length".equals(key))
+ continue;
+
+ putString(key);
serializer.serialize(this, entry.getValue());
+ }
+ if (maxInt >= 0) {
+ putString("length");
+ serializer.serialize(this, maxInt+1);
}
buf.put((byte) 0x00);
@@ -140,5 +155,5 @@
storeReference(array);
buf.put(AMF.TYPE_MIXED_ARRAY);
- buf.putInt(array.size());
+ buf.putInt(array.size()+1);
int idx=0;
for (Object item: array) {
@@ -150,4 +165,6 @@
}
}
+ putString("length");
+ serializer.serialize(this, array.size()+1);
buf.put((byte) 0x00);
Note:
Diffs are chopped if more than 25k.
This is to get past the limit on the mailing list.
More information about the Red5commits
mailing list