Files
narya/src/as/com/threerings/io/ObjectInputStream.as
T
Ray Greenwell 789fe8e433 Found the streaming bug:
I was looking up streamers by java name, but passing in the actionscript
name. This was only affecting Object[]'s containing String elements
due to the addition of String to Translations.

Previously, 'java.lang.String' wasn't getting translated into the
actionscript equivalent, so it just worked. Two bugs can cancel each other out.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4567 542714f4-19e9-0310-aa3c-eee0fc999fb1
2007-02-12 21:29:57 +00:00

243 lines
6.9 KiB
ActionScript

package com.threerings.io {
import flash.errors.IOError;
import flash.errors.MemoryError;
import flash.utils.ByteArray;
import flash.utils.IDataInput;
import com.threerings.util.ClassUtil;
public class ObjectInputStream
{
private static const log :Log = Log.getLog(ObjectInputStream);
public function ObjectInputStream (source :IDataInput = null)
{
if (source == null) {
source = new ByteArray();
}
_source = source;
}
/**
* Set a new source from which to read our data.
*/
public function setSource (source :IDataInput) :void
{
_source = source;
}
public function readObject () :Object
//throws IOError
{
var DEBUG_ID :String = "[" + (++_tempy) + "] ";
try {
// read in the class code for this instance
var code :int = readShort();
// a zero code indicates a null value
if (code == 0) {
if (DEBUG) log.debug(DEBUG_ID + "Read null");
return null;
}
var cmap :ClassMapping;
// if the code is negative, that means we've never seen it
// before and class metadata follows
if (code < 0) {
// first swap the code into positive land
code *= -1;
// read in the class metadata
var jname :String = readUTF();
// log.debug("read jname: " + jname);
var streamer :Streamer = Streamer.getStreamerByJavaName(jname);
if (streamer == Streamer.BAD_STREAMER) {
log.warning("OMG, cannot stream " + jname);
return null;
}
// log.debug("Got streamer (" + streamer + ")");
var cname :String = Translations.getFromServer(jname);
cmap = new ClassMapping(code, cname, streamer);
_classMap[code] = cmap;
if (DEBUG) log.debug(DEBUG_ID +
"Created mapping: (" + code + "): " + cname);
} else {
cmap = (_classMap[code] as ClassMapping);
if (null == cmap) {
throw new IOError("Read object for which we have no " +
"registered class metadata [code=" + code + "].");
}
if (DEBUG) log.debug(DEBUG_ID + "Read known code: (" +
code + ": " + cmap.classname + ")");
}
// log.debug("Creating object sleeve...");
var target :Object;
if (cmap.streamer === null) {
var clazz :Class = ClassUtil.getClassByName(cmap.classname);
target = new clazz();
} else {
target = cmap.streamer.createObject(this);
}
//log.debug("Reading object...");
readBareObjectImpl(target, cmap.streamer);
if (DEBUG) log.debug(DEBUG_ID + "Read object: " + target);
return target;
} catch (me :MemoryError) {
throw new IOError("out of memory" + me.message);
}
return null; // not reached: compiler dumb
}
public function readBareObject (obj :Object) :void
//throws IOError
{
readBareObjectImpl(obj, Streamer.getStreamer(obj));
}
public function readBareObjectImpl (obj :Object, streamer :Streamer) :void
{
// streamable objects
if (streamer == null) {
(obj as Streamable).readObject(this);
return;
}
_current = obj;
_streamer = streamer;
try {
_streamer.readObject(obj, this);
} finally {
// clear out our current object references
_current = null;
_streamer = null;
}
}
/**
* @param type either a String representing the java type,
* or a Class object representing the actionscript type.
*/
// TODO: this is the equivalent of marshalling something for which
// we have a basic streamer. Fill out with all the java types
public function readField (type :Object) :Object
//throws IOError
{
if (readBoolean()) {
var streamer :Streamer = (type is Class)
? Streamer.getStreamerByClass(type as Class)
: Streamer.getStreamerByJavaName(type as String);
if (streamer == Streamer.BAD_STREAMER) {
throw new Error("Cannot field stream " + type);
}
var obj :Object;
if (streamer != null) {
obj = streamer.createObject(this);
} else {
// create the streamable object, either by class or name
var c :Class;
if (type is Class) {
c = (type as Class);
} else {
c = ClassUtil.getClassByName(
Translations.getFromServer(type as String));
}
obj = new c();
}
readBareObjectImpl(obj, streamer);
return obj;
}
return null;
}
public function defaultReadObject () :void
//throws IOError
{
_streamer.readObject(_current, this);
}
public function readBoolean () :Boolean
//throws IOError
{
return _source.readBoolean();
}
public function readByte () :int
//throws IOError
{
return _source.readByte();
}
public function readBytes (bytes :ByteArray, offset :uint = 0,
length :uint = 0) :void
//throws IOError
{
// IDataInput reads all available bytes if a length is not passed
// in. Protect against an easy error to make by using the length of
// the array
if (length === 0) {
length = bytes.length;
}
_source.readBytes(bytes, offset, length);
}
public function readDouble () :Number
//throws IOError
{
return _source.readDouble();
}
public function readFloat () :Number
//throws IOError
{
return _source.readFloat();
}
public function readInt () :int
//throws IOError
{
return _source.readInt();
}
public function readShort () :int
//throws IOError
{
return _source.readShort();
}
public function readUTF () :String
//throws IOError
{
return _source.readUTF();
}
/** The target DataInput that we route input from. */
protected var _source :IDataInput;
/** The object currently being read from the stream. */
protected var _current :Object;
/** The stramer being used currently. */
protected var _streamer :Streamer;
/** A map of short class code to ClassMapping info. */
protected var _classMap :Array = new Array();
public static const DEBUG :Boolean = false;
private static var _tempy :int = 0;
}
}