[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