All Downloads are FREE. Search and download functionalities are using the official Maven repository.

flex.messaging.io.amf.Amf3Output 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 flex.messaging.MessageException;
import flex.messaging.io.ArrayCollection;
import flex.messaging.io.BeanProxy;
import flex.messaging.io.PagedRowSet;
import flex.messaging.io.PropertyProxy;
import flex.messaging.io.PropertyProxyRegistry;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.SerializationDescriptor;
import flex.messaging.io.StatusInfoProxy;
import flex.messaging.io.amf.AmfTrace.VectorType;
import flex.messaging.util.Trace;
import org.w3c.dom.Document;

import javax.sql.RowSet;
import java.io.Externalizable;
import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Serializes data to an output stream using the new
 * AMF 3 format.
 * 

* This class intends to match the Flash Player 8 C++ code * in avmglue/DataIO.cpp *

* * */ public class Amf3Output extends AbstractAmfOutput implements Amf3Types { /** * */ protected IdentityHashMap objectTable; /** * */ protected HashMap traitsTable; /** * */ protected HashMap stringTable; public Amf3Output(SerializationContext context) { super(context); context.supportDatesByReference = true; } public void reset() { super.reset(); if (objectTable != null) objectTable.clear(); if (traitsTable != null) traitsTable.clear(); if (stringTable != null) stringTable.clear(); } // // java.io.ObjectOutput IMPLEMENTATIONS // /** * Serialize an Object using AMF 3. * @param o the object to write * @throws IOException if the write failed */ public void writeObject(Object o) throws IOException { if (o == null) { writeAMFNull(); return; } if (!context.legacyExternalizable && o instanceof Externalizable) { writeCustomObject(o); } else if (o instanceof String || o instanceof Character) { String s = o.toString(); writeAMFString(s); } else if (o instanceof Number) { if (o instanceof Integer || o instanceof Short || o instanceof Byte) { int i = ((Number)o).intValue(); writeAMFInt(i); } else if (!context.legacyBigNumbers && (o instanceof BigInteger || o instanceof BigDecimal)) { // Using double to write big numbers such as BigInteger or // BigDecimal can result in information loss so we write // them as String by default... writeAMFString(o.toString()); } else { double d = ((Number)o).doubleValue(); writeAMFDouble(d); } } else if (o instanceof Boolean) { writeAMFBoolean(((Boolean)o).booleanValue()); } // We have a complex type... else if (o instanceof Date) { writeAMFDate((Date)o); } else if (o instanceof Calendar) { writeAMFDate(((Calendar)o).getTime()); } else if (o instanceof Document) { if (context.legacyXMLDocument) out.write(kXMLType); // Legacy flash.xml.XMLDocument Type else out.write(kAvmPlusXmlType); // New E4X XML Type if (!byReference(o)) { String xml = documentToString(o); if (isDebug) trace.write(xml); writeAMFUTF(xml); } } // If there is a proxy for this,write it as a custom object so the default // behavior can be overriden. else if (o instanceof Enum && PropertyProxyRegistry.getRegistry().getProxy(o.getClass()) == null) { Enum enumValue = (Enum)o; writeAMFString(enumValue.name()); } else { // We have an Object or Array type... Class cls = o.getClass(); if (context.legacyMap && o instanceof Map && !(o instanceof ASObject)) { writeMapAsECMAArray((Map)o); } else if (!context.legacyDictionary && o instanceof Dictionary) { writeDictionary((Dictionary)o); } else if (o instanceof Collection) { if (o instanceof List && context.preferVectors) writeListAsTypedVector((List)o); else if (context.legacyCollection) writeCollection((Collection)o, null); else writeArrayCollection((Collection)o, null); } else if (cls.isArray()) { Class componentType = cls.getComponentType(); // Convert to vector if requested, except for character and byte arrays if (context.preferVectors && !(componentType.equals(Byte.class) || componentType.equals(byte.class))&& !(componentType.equals(Character.class) || componentType.equals(char.class))) writeArrayAsTypedVector(o, componentType); else writeAMFArray(o, componentType); } else { //Special Case: wrap RowSet in PageableRowSet for Serialization if (o instanceof RowSet) { o = new PagedRowSet((RowSet)o, Integer.MAX_VALUE, false); } else if (context.legacyThrowable && o instanceof Throwable) { o = new StatusInfoProxy((Throwable)o); } writeCustomObject(o); } } } public void writeObjectTraits(TraitsInfo ti) throws IOException { String className = ti.getClassName(); if (isDebug) { if (ti.isExternalizable()) trace.startExternalizableObject(className, getObjectTableSize()); else trace.startAMFObject(className, getObjectTableSize()); } if (!byReference(ti)) { int count = 0; List propertyNames = null; boolean externalizable = ti.isExternalizable(); if (!externalizable) { propertyNames = ti.getProperties(); if (propertyNames != null) count = propertyNames.size(); } boolean dynamic = ti.isDynamic(); writeUInt29(3 | (externalizable ? 4 : 0) | (dynamic ? 8 : 0) | (count << 4)); writeStringWithoutType(className); if (!externalizable && propertyNames != null) { for (int i = 0; i < count; i++) { String propName = ti.getProperty(i); writeStringWithoutType(propName); } } } } public void writeObjectProperty(String name, Object value) throws IOException { if (isDebug) trace.namedElement(name); increaseNestObjectLevel(); writeObject(value); decreaseNestObjectLevel(); } public void writeObjectEnd() throws IOException { // No action required for AMF 3 if (isDebug) trace.endAMFObject(); } // // AMF SPECIFIC SERIALIZATION IMPLEMENTATIONS // /** * */ protected void writeAMFBoolean(boolean b) throws IOException { if (isDebug) trace.write(b); if (b) out.write(kTrueType); else out.write(kFalseType); } /** * */ protected void writeAMFDate(Date d) throws IOException { out.write(kDateType); if (!byReference(d)) { if (isDebug) trace.write(d); //Write out an invalid reference writeUInt29(1); // Write the time as 64bit value in ms out.writeDouble((double)d.getTime()); } } /** * */ protected void writeAMFDouble(double d) throws IOException { if (isDebug) trace.write(d); out.write(kDoubleType); out.writeDouble(d); } /** * */ protected void writeAMFInt(int i) throws IOException { if (i >= INT28_MIN_VALUE && i <= INT28_MAX_VALUE) { if (isDebug) trace.write(i); // We have to be careful when the MSB is set, as (value >> 3) will sign extend. // We know there are only 29-bits of precision, so truncate. This requires // similar care when reading an integer. //i = ((i >> 3) & UINT29_MASK); i = i & UINT29_MASK; // Mask is 2^29 - 1 out.write(kIntegerType); writeUInt29(i); } else { // Promote large int to a double writeAMFDouble(i); } } protected void writeDictionary(Dictionary dictionary) throws IOException { out.write(kDictionaryType); if (byReference(dictionary)) return; writeUInt29((dictionary.size() << 1) | 1); writeAMFBoolean(false /*usingWeakKeys*/); if (isDebug) trace.startAMFDictionary(objectTable.size() - 1); Enumeration keys = dictionary.keys(); while (keys.hasMoreElements()) { if (isDebug) trace.startDictionaryElement(); Object key = keys.nextElement(); increaseNestObjectLevel(); writeObject(key); decreaseNestObjectLevel(); if (isDebug) trace.addDictionaryEquals(); Object value = dictionary.get(key); increaseNestObjectLevel(); writeObject(value); decreaseNestObjectLevel(); } if (isDebug) trace.endAMFDictionary(); } protected void writeArrayAsTypedVector(Object array, Class componentType) throws IOException { int vecType = kTypedVectorObject; if (componentType.isPrimitive()) { if (int.class.equals(componentType)) vecType = kTypedVectorInt; else if (double.class.equals(componentType)) vecType = kTypedVectorDouble; } else { if (Integer.class.equals(componentType)) vecType = kTypedVectorInt; else if (Double.class.equals(componentType)) vecType = kTypedVectorDouble; } out.write(vecType); if (byReference(array)) return; int length = Array.getLength(array); writeUInt29((length << 1) | 1); writeBoolean(true /*fixed*/); switch (vecType) { case kTypedVectorInt: if (isDebug) trace.startAMFVector(objectTable.size() - 1, VectorType.INT); for (int i = 0; i < length; i++) { if (isDebug) trace.arrayElement(i); Object element = Array.get(array, i); int value = ((Integer)element).intValue(); if (isDebug) trace.write(value); writeInt(value); } break; case kTypedVectorDouble: if (isDebug) trace.startAMFVector(objectTable.size() - 1, VectorType.DOUBLE); for (int i = 0; i < length; i++) { if (isDebug) trace.arrayElement(i); Object element = Array.get(array, i); double value = ((Double)element).doubleValue(); if (isDebug) trace.write(value); writeDouble(value); } break; case kTypedVectorObject: if (isDebug) trace.startAMFVector(objectTable.size() - 1, VectorType.OBJECT); // TODO - I don't think this className is used properly on the client currently. String className = componentType.getName(); writeStringWithoutType(className); for (int i = 0; i < length; i++) { if (isDebug) trace.arrayElement(i); Object element = Array.get(array, i); increaseNestObjectLevel(); writeObject(element); decreaseNestObjectLevel(); } break; default: break; } if (isDebug) trace.endAMFVector(); } protected void writeListAsTypedVector(List list) throws IOException { // Peek at the first three elements of the list to figure out what type // of Vector it should be sent as. int vecType = -1; Class initialElementClass = null; int peekSize = Math.min(list.size(), 3); for (int i = 0; i < peekSize; i++) { Object element = list.get(i); if (i == 0) { initialElementClass = element != null? element.getClass() : null; } else { Class currentElementClass = element != null? element.getClass() : null; if (initialElementClass != currentElementClass) { vecType = kTypedVectorObject; break; } } } if (vecType == -1) { if (initialElementClass == Integer.class) vecType = kTypedVectorInt; else if (initialElementClass == Double.class) vecType = kTypedVectorDouble; else vecType = kTypedVectorObject; } out.write(vecType); if (byReference(list)) return; int length = list.size(); writeUInt29((length << 1) | 1); writeBoolean(false /*fixed*/); switch (vecType) { case kTypedVectorInt: if (isDebug) trace.startAMFVector(objectTable.size() - 1, VectorType.INT); for (int i = 0; i < length; i++) { if (isDebug) trace.arrayElement(i); Object element = list.get(i); int value = ((Integer)element).intValue(); if (isDebug) trace.write(value); writeInt(value); } break; case kTypedVectorDouble: if (isDebug) trace.startAMFVector(objectTable.size() - 1, VectorType.DOUBLE); for (int i = 0; i < length; i++) { if (isDebug) trace.arrayElement(i); Object element = list.get(i); double value = ((Double)element).doubleValue(); if (isDebug) trace.write(value); writeDouble(value); } break; case kTypedVectorObject: if (isDebug) trace.startAMFVector(objectTable.size() - 1, VectorType.OBJECT); // TODO - I don't think this className is used properly on the client currently. String className = initialElementClass != null? initialElementClass.getName() : ""; writeStringWithoutType(className); for (int i = 0; i < length; i++) { if (isDebug) trace.arrayElement(i); Object element = list.get(i); increaseNestObjectLevel(); writeObject(element); decreaseNestObjectLevel(); } break; default: break; } if (isDebug) trace.endAMFVector(); } /** * */ protected void writeMapAsECMAArray(Map map) throws IOException { out.write(kArrayType); if (!byReference(map)) { if (isDebug) trace.startECMAArray(getObjectTableSize()); writeUInt29((0 << 1) | 1); Iterator it = map.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); if (key != null) { String propName = key.toString(); writeStringWithoutType(propName); if (isDebug) trace.namedElement(propName); increaseNestObjectLevel(); writeObject(map.get(key)); decreaseNestObjectLevel(); } } writeStringWithoutType(EMPTY_STRING); if (isDebug) trace.endAMFArray(); } } /** * */ protected void writeAMFNull() throws IOException { if (isDebug) trace.writeNull(); out.write(kNullType); } /** * */ protected void writeAMFString(String s) throws IOException { out.write(kStringType); writeStringWithoutType(s); if (isDebug) { trace.writeString(s); } } /** * */ protected void writeStringWithoutType(String s) throws IOException { if (s.length() == 0) { // don't create a reference for the empty string, // as it's represented by the one byte value 1 // len = 0, ((len << 1) | 1). writeUInt29(1); return; } if (!byReference(s)) { writeAMFUTF(s); return; } } /** * */ protected void writeAMFArray(Object o, Class componentType) throws IOException { if (componentType.isPrimitive()) { writePrimitiveArray(o); } else if (componentType.equals(Byte.class)) { writeAMFByteArray((Byte[])o); } else if (componentType.equals(Character.class)) { writeCharArrayAsString((Character[])o); } else { writeObjectArray((Object[])o, null); } } /** * */ protected void writeArrayCollection(Collection col, SerializationDescriptor desc) throws IOException { out.write(kObjectType); if (!byReference(col)) { ArrayCollection ac; if (col instanceof ArrayCollection) { ac = (ArrayCollection)col; // TODO: QUESTION: Pete, ignoring the descriptor here... not sure if // we should modify the user's AC as that could cause corruption? } else { // Wrap any Collection in an ArrayCollection ac = new ArrayCollection(col); if (desc != null) ac.setDescriptor(desc); } // Then wrap ArrayCollection in PropertyProxy for bean-like serialization PropertyProxy proxy = PropertyProxyRegistry.getProxy(ac); writePropertyProxy(proxy, ac); } } /** * */ protected void writeCustomObject(Object o) throws IOException { PropertyProxy proxy = null; if (o instanceof PropertyProxy) { proxy = (PropertyProxy)o; o = proxy.getDefaultInstance(); // The proxy may wrap a null default instance, if so, short circuit here. if (o == null) { writeAMFNull(); return; } // HACK: Short circuit and unwrap if PropertyProxy is wrapping an Array // or Collection or Map with legacyMap as true since we don't yet have // the ability to proxy multiple AMF types. We write an AMF Array directly // instead of an AMF Object... else if (o instanceof Collection) { if (context.legacyCollection) writeCollection((Collection)o, proxy.getDescriptor()); else writeArrayCollection((Collection)o, proxy.getDescriptor()); return; } else if (o.getClass().isArray()) { writeObjectArray((Object[])o, proxy.getDescriptor()); return; } else if (context.legacyMap && o instanceof Map && !(o instanceof ASObject)) { writeMapAsECMAArray((Map)o); return; } } out.write(kObjectType); if (!byReference(o)) { if (proxy == null) { proxy = PropertyProxyRegistry.getProxyAndRegister(o); } writePropertyProxy(proxy, o); } } /** * */ protected void writePropertyProxy(PropertyProxy proxy, Object instance) throws IOException { /* * At this point we substitute the instance we want to serialize. */ Object newInst = proxy.getInstanceToSerialize(instance); if (newInst != instance) { // We can't use writeAMFNull here I think since we already added this object // to the object table on the server side. The player won't have any way // of knowing we have this reference mapped to null. if (newInst == null) throw new MessageException("PropertyProxy.getInstanceToSerialize class: " + proxy.getClass() + " returned null for instance class: " + instance.getClass().getName()); // Grab a new proxy if necessary for the new instance proxy = PropertyProxyRegistry.getProxyAndRegister(newInst); instance = newInst; } List propertyNames = null; boolean externalizable = proxy.isExternalizable(instance); if (!externalizable) { propertyNames = proxy.getPropertyNames(instance); // filter write-only properties if (proxy instanceof BeanProxy) { BeanProxy bp = (BeanProxy)proxy; if (propertyNames != null && !propertyNames.isEmpty()) { List propertiesToRemove = null; for (int i = 0; i < propertyNames.size(); i++) { String propName = (String)propertyNames.get(i); if (bp.isWriteOnly(instance, propName)) { if (propertiesToRemove == null) propertiesToRemove = new ArrayList(); propertiesToRemove.add(propName); } } if (propertiesToRemove != null) propertyNames.removeAll(propertiesToRemove); } } } TraitsInfo ti = new TraitsInfo(proxy.getAlias(instance), proxy.isDynamic(), externalizable, propertyNames); writeObjectTraits(ti); if (externalizable) { // Call user defined serialization ((Externalizable)instance).writeExternal(this); } else if (propertyNames != null && !propertyNames.isEmpty()) { for (int i = 0; i < propertyNames.size(); i++) { String propName = (String)propertyNames.get(i); Object value = proxy.getValue(instance, propName); writeObjectProperty(propName, value); } } writeObjectEnd(); } /** * Serialize an array of primitives. *

* Primitives include the following: * boolean, char, double, float, long, int, short, byte *

* * @param obj An array of primitives * */ protected void writePrimitiveArray(Object obj) throws IOException { Class aType = obj.getClass().getComponentType(); if (aType.equals(Character.TYPE)) { //Treat char[] as a String char[] c = (char[])obj; writeCharArrayAsString(c); } else if (aType.equals(Byte.TYPE)) { writeAMFByteArray((byte[])obj); } else { out.write(kArrayType); if (!byReference(obj)) { if (aType.equals(Boolean.TYPE)) { boolean[] b = (boolean[])obj; // Write out an invalid reference, storing the length in the unused 28-bits. writeUInt29((b.length << 1) | 1); // Send an empty string to imply no named keys writeStringWithoutType(EMPTY_STRING); if (isDebug) { trace.startAMFArray(getObjectTableSize()); for (int i = 0; i < b.length; i++) { trace.arrayElement(i); writeAMFBoolean(b[i]); } trace.endAMFArray(); } else { for (int i = 0; i < b.length; i++) { writeAMFBoolean(b[i]); } } } else if (aType.equals(Integer.TYPE) || aType.equals(Short.TYPE)) { //We have a primitive number, either an int or short //We write all of these as Integers... int length = Array.getLength(obj); // Write out an invalid reference, storing the length in the unused 28-bits. writeUInt29((length << 1) | 1); // Send an empty string to imply no named keys writeStringWithoutType(EMPTY_STRING); if (isDebug) { trace.startAMFArray(getObjectTableSize()); for (int i = 0; i < length; i++) { trace.arrayElement(i); int v = Array.getInt(obj, i); writeAMFInt(v); } trace.endAMFArray(); } else { for (int i = 0; i < length; i++) { int v = Array.getInt(obj, i); writeAMFInt(v); } } } else { //We have a primitive number, either a double, float, or long //We write all of these as doubles... int length = Array.getLength(obj); // Write out an invalid reference, storing the length in the unused 28-bits. writeUInt29((length << 1) | 1); // Send an empty string to imply no named keys writeStringWithoutType(EMPTY_STRING); if (isDebug) { trace.startAMFArray(getObjectTableSize()); for (int i = 0; i < length; i++) { trace.arrayElement(i); double v = Array.getDouble(obj, i); writeAMFDouble(v); } trace.endAMFArray(); } else { for (int i = 0; i < length; i++) { double v = Array.getDouble(obj, i); writeAMFDouble(v); } } } } } } /** * */ protected void writeAMFByteArray(byte[] ba) throws IOException { out.write(kByteArrayType); if (!byReference(ba)) { int length = ba.length; // Write out an invalid reference, storing the length in the unused 28-bits. writeUInt29((length << 1) | 1); if (isDebug) { trace.startByteArray(getObjectTableSize(), length); } out.write(ba, 0, length); } } /** * */ protected void writeAMFByteArray(Byte[] ba) throws IOException { out.write(kByteArrayType); if (!byReference(ba)) { int length = ba.length; // Write out an invalid reference, storing the length in the unused 28-bits. writeUInt29((length << 1) | 1); if (isDebug) { trace.startByteArray(getObjectTableSize(), length); } for (int i = 0; i < ba.length; i++) { Byte b = ba[i]; if (b == null) out.write(0); else out.write(b.byteValue()); } } } /** * */ protected void writeCharArrayAsString(Character[] ca) throws IOException { int length = ca.length; char[] chars = new char[length]; for (int i = 0; i < length; i++) { Character c = ca[i]; if (c == null) chars[i] = 0; else chars[i] = ca[i].charValue(); } writeCharArrayAsString(chars); } /** * */ protected void writeCharArrayAsString(char[] ca) throws IOException { String str = new String(ca); writeAMFString(str); } /** * */ protected void writeObjectArray(Object[] values, SerializationDescriptor descriptor) throws IOException { out.write(kArrayType); if (!byReference(values)) { if (isDebug) trace.startAMFArray(getObjectTableSize()); writeUInt29((values.length << 1) | 1); // Send an empty string to imply no named keys writeStringWithoutType(EMPTY_STRING); for (int i = 0; i < values.length; ++i) { if (isDebug) trace.arrayElement(i); Object item = values[i]; if (item != null && descriptor != null && !(item instanceof String) && !(item instanceof Number) && !(item instanceof Boolean) && !(item instanceof Character)) { PropertyProxy proxy = PropertyProxyRegistry.getProxy(item); proxy = (PropertyProxy)proxy.clone(); proxy.setDescriptor(descriptor); proxy.setDefaultInstance(item); item = proxy; } increaseNestObjectLevel(); writeObject(item); decreaseNestObjectLevel(); } if (isDebug) trace.endAMFArray(); } } /** * */ protected void writeCollection(Collection c, SerializationDescriptor descriptor) throws IOException { out.write(kArrayType); // Note: We process Collections independently of Object[] // as we want the reference to be based on the actual // Collection. if (!byReference(c)) { if (isDebug) trace.startAMFArray(getObjectTableSize()); writeUInt29((c.size() << 1) | 1); // Send an empty string to imply no named keys writeStringWithoutType(EMPTY_STRING); Iterator it = c.iterator(); int i = 0; while (it.hasNext()) { if (isDebug) trace.arrayElement(i); Object item = it.next(); if (item != null && descriptor != null && !(item instanceof String) && !(item instanceof Number) && !(item instanceof Boolean) && !(item instanceof Character)) { PropertyProxy proxy = PropertyProxyRegistry.getProxy(item); proxy = (PropertyProxy)proxy.clone(); proxy.setDescriptor(descriptor); proxy.setDefaultInstance(item); item = proxy; } increaseNestObjectLevel(); writeObject(item); decreaseNestObjectLevel(); i++; } if (isDebug) trace.endAMFArray(); } } /** * */ protected void writeUInt29(int ref) throws IOException { // Represent smaller integers with fewer bytes using the most // significant bit of each byte. The worst case uses 32-bits // to represent a 29-bit number, which is what we would have // done with no compression. // 0x00000000 - 0x0000007F : 0xxxxxxx // 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx // 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx // 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx // 0x40000000 - 0xFFFFFFFF : throw range exception if (ref < 0x80) { // 0x00000000 - 0x0000007F : 0xxxxxxx out.writeByte(ref); } else if (ref < 0x4000) { // 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx out.writeByte(((ref >> 7) & 0x7F) | 0x80); out.writeByte(ref & 0x7F); } else if (ref < 0x200000) { // 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx out.writeByte(((ref >> 14) & 0x7F) | 0x80); out.writeByte(((ref >> 7) & 0x7F) | 0x80); out.writeByte(ref & 0x7F); } else if (ref < 0x40000000) { // 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx out.writeByte(((ref >> 22) & 0x7F) | 0x80); out.writeByte(((ref >> 15) & 0x7F) | 0x80); out.writeByte(((ref >> 8) & 0x7F) | 0x80); out.writeByte(ref & 0xFF); } else { // 0x40000000 - 0xFFFFFFFF : throw range exception throw new MessageException("Integer out of range: " + ref); } } /** * */ public void writeAMFUTF(String s) throws IOException { int strlen = s.length(); int utflen = 0; int c, count = 0; char[] charr = getTempCharArray(strlen); s.getChars(0, strlen, charr, 0); for (int i = 0; i < strlen; i++) { c = charr[i]; if (c <= 0x007F) { utflen++; } else if (c > 0x07FF) { utflen += 3; } else { utflen += 2; } } writeUInt29((utflen << 1) | 1); byte[] bytearr = getTempByteArray(utflen); for (int i = 0; i < strlen; i++) { c = charr[i]; if (c <= 0x007F) { bytearr[count++] = (byte)c; } else if (c > 0x07FF) { bytearr[count++] = (byte)(0xE0 | ((c >> 12) & 0x0F)); bytearr[count++] = (byte)(0x80 | ((c >> 6) & 0x3F)); bytearr[count++] = (byte)(0x80 | ((c >> 0) & 0x3F)); } else { bytearr[count++] = (byte)(0xC0 | ((c >> 6) & 0x1F)); bytearr[count++] = (byte)(0x80 | ((c >> 0) & 0x3F)); } } out.write(bytearr, 0, utflen); } /** * Attempts to serialize the object as a reference. * If the object cannot be serialized as a reference, it is stored * in the reference collection for potential future encounter. * * @return Success/failure indicator as to whether the object could be * serialized as a reference. * */ protected boolean byReference(Object o) throws IOException { if (objectTable != null && objectTable.containsKey(o)) { try { int refNum = objectTable.get(o).intValue(); if (isDebug) trace.writeRef(refNum); writeUInt29(refNum << 1); return true; } catch (ClassCastException e) { throw new IOException("Object reference is not an Integer"); } } if (objectTable == null) objectTable = new IdentityHashMap(64); objectTable.put(o, Integer.valueOf(objectTable.size())); return false; } /** * */ public void addObjectReference(Object o) throws IOException { byReference(o); } /** * */ protected boolean byReference(String s) throws IOException { if (stringTable != null && stringTable.containsKey(s)) { try { int refNum = stringTable.get(s).intValue(); writeUInt29(refNum << 1); if (isDebug && Trace.amf) trace.writeStringRef(refNum); return true; } catch (ClassCastException e) { throw new IOException("String reference is not an Integer"); } } if (stringTable == null) stringTable = new HashMap(64); stringTable.put(s, Integer.valueOf(stringTable.size())); return false; } /** * */ protected boolean byReference(TraitsInfo ti) throws IOException { if (traitsTable != null && traitsTable.containsKey(ti)) { try { int refNum = traitsTable.get(ti).intValue(); writeUInt29((refNum << 2) | 1); if (isDebug && Trace.amf) trace.writeTraitsInfoRef(refNum); return true; } catch (ClassCastException e) { throw new IOException("TraitsInfo reference is not an Integer"); } } if (traitsTable == null) traitsTable = new HashMap(10); traitsTable.put(ti, Integer.valueOf(traitsTable.size())); return false; } protected int getObjectTableSize() { return objectTable != null? objectTable.size() - 1 : 0; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy