From ec2b50c15346b972cad2b6dca4cb782e42e29539 Mon Sep 17 00:00:00 2001 From: Ray Greenwell Date: Thu, 4 Sep 2008 01:27:18 +0000 Subject: [PATCH] - Changed some stuff around with streaming. - Before, the Input/OutputStream classes handled Streamables directly and Streamers were never created for them. Simplified the code somewhat by always creating a Streamer. It's now more like the Java side, too. - No more BAD_STREAMER, since null now means "bad". - Built-in support for streaming enums. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5352 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- src/as/com/threerings/io/ClassMapping.as | 5 +- src/as/com/threerings/io/ObjectInputStream.as | 67 +++++---------- .../com/threerings/io/ObjectOutputStream.as | 14 +--- src/as/com/threerings/io/Streamer.as | 81 ++++++++++++------- .../threerings/io/streamers/EnumStreamer.as | 52 ++++++++++++ 5 files changed, 128 insertions(+), 91 deletions(-) create mode 100644 src/as/com/threerings/io/streamers/EnumStreamer.as diff --git a/src/as/com/threerings/io/ClassMapping.as b/src/as/com/threerings/io/ClassMapping.as index 38bbd5a69..4ee93f09c 100644 --- a/src/as/com/threerings/io/ClassMapping.as +++ b/src/as/com/threerings/io/ClassMapping.as @@ -24,14 +24,11 @@ package com.threerings.io { public class ClassMapping { public var code :int; - public var classname :String; public var streamer :Streamer; - public function ClassMapping (code :int, classname :String, - streamer :Streamer) + public function ClassMapping (code :int, streamer :Streamer) { this.code = code; - this.classname = classname; this.streamer = streamer; } } diff --git a/src/as/com/threerings/io/ObjectInputStream.as b/src/as/com/threerings/io/ObjectInputStream.as index bdab88069..366500e14 100644 --- a/src/as/com/threerings/io/ObjectInputStream.as +++ b/src/as/com/threerings/io/ObjectInputStream.as @@ -79,16 +79,15 @@ public class ObjectInputStream var jname :String = readUTF(); // log.debug("read jname: " + jname); var streamer :Streamer = Streamer.getStreamerByJavaName(jname); - if (streamer == Streamer.BAD_STREAMER) { + if (streamer == null) { log.warning("OMG, cannot stream " + jname); return null; } if (DEBUG) log.debug(DEBUG_ID + "Got streamer (" + streamer + ")"); - var cname :String = Translations.getFromServer(jname); - cmap = new ClassMapping(code, cname, streamer); + cmap = new ClassMapping(code, streamer); _classMap[code] = cmap; - if (DEBUG) log.debug(DEBUG_ID + "Created mapping: (" + code + "): " + cname); + if (DEBUG) log.debug(DEBUG_ID + "Created mapping: (" + code + "): " + jname); } else { cmap = (_classMap[code] as ClassMapping); @@ -97,19 +96,13 @@ public class ObjectInputStream "registered class metadata [code=" + code + "]."); } if (DEBUG) { - log.debug(DEBUG_ID + "Read known code: (" + code + ": " + cmap.classname + ")"); + log.debug(DEBUG_ID + "Read known code: (" + code + ": " + + cmap.streamer.getJavaClassName() + ")"); } } // 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); - } + var target :Object = cmap.streamer.createObject(this); //log.debug("Reading object..."); readBareObjectImpl(target, cmap.streamer); if (DEBUG) log.debug(DEBUG_ID + "Read object: " + target); @@ -129,12 +122,6 @@ public class ObjectInputStream public function readBareObjectImpl (obj :Object, streamer :Streamer) :void { - // streamable objects - if (streamer == null) { - (obj as Streamable).readObject(this); - return; - } - _current = obj; _streamer = streamer; try { @@ -156,35 +143,21 @@ public class ObjectInputStream 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; + if (!readBoolean()) { + return null; } - return null; + + var streamer :Streamer = (type is Class) + ? Streamer.getStreamerByClass(type as Class) + : Streamer.getStreamerByJavaName(type as String); + + if (streamer == null) { + throw new Error("Cannot field stream " + type); + } + + var obj :Object = streamer.createObject(this); + readBareObjectImpl(obj, streamer); + return obj; } public function defaultReadObject () :void diff --git a/src/as/com/threerings/io/ObjectOutputStream.as b/src/as/com/threerings/io/ObjectOutputStream.as index 94110cf16..85a38ec22 100644 --- a/src/as/com/threerings/io/ObjectOutputStream.as +++ b/src/as/com/threerings/io/ObjectOutputStream.as @@ -59,12 +59,11 @@ public class ObjectOutputStream // create a class mapping if we've not got one if (cmap == null) { var streamer :Streamer = Streamer.getStreamer(obj); - // streamer may be null to indicate a Streamable object - if (streamer == Streamer.BAD_STREAMER) { + if (streamer == null) { throw new Error("Unable to stream " + cname); } - cmap = new ClassMapping(_nextCode++, cname, streamer); + cmap = new ClassMapping(_nextCode++, streamer); _classMap.put(cname, cmap); if (_nextCode > Short.MAX_VALUE) { @@ -76,8 +75,7 @@ public class ObjectOutputStream } writeShort(-cmap.code); - writeUTF((streamer == null) ? Translations.getToServer(cname) - : streamer.getJavaClassName()); + writeUTF(streamer.getJavaClassName()); } else { writeShort(cmap.code); @@ -94,12 +92,6 @@ public class ObjectOutputStream public function writeBareObjectImpl (obj :Object, streamer :Streamer) :void { - // if it's Streamable, it goes straight through - if (streamer == null) { - obj.writeObject(this); // obj is a Streamable - return; - } - // otherwise, stream it! _current = obj; _streamer = streamer; diff --git a/src/as/com/threerings/io/Streamer.as b/src/as/com/threerings/io/Streamer.as index 4c83c8e3a..73928e46c 100644 --- a/src/as/com/threerings/io/Streamer.as +++ b/src/as/com/threerings/io/Streamer.as @@ -24,10 +24,12 @@ package com.threerings.io { import flash.utils.ByteArray; import com.threerings.util.ClassUtil; +import com.threerings.util.Enum; import com.threerings.io.streamers.ArrayStreamer; import com.threerings.io.streamers.ByteStreamer; import com.threerings.io.streamers.ByteArrayStreamer; +import com.threerings.io.streamers.EnumStreamer; import com.threerings.io.streamers.FloatStreamer; import com.threerings.io.streamers.IntegerStreamer; import com.threerings.io.streamers.LongStreamer; @@ -37,14 +39,8 @@ import com.threerings.io.streamers.StringStreamer; public class Streamer { - public static const BAD_STREAMER :Streamer = new Streamer(null, null); - public static function getStreamer (obj :Object) :Streamer { - if (obj is Streamable) { - return null; - } - initStreamers(); var streamer :Streamer; @@ -54,34 +50,54 @@ public class Streamer } } + // from here on out we're creating new streamers + if (obj is TypedArray) { streamer = new ArrayStreamer((obj as TypedArray).getJavaType()); - _streamers.push(streamer); - return streamer; + + } else if (obj is Enum) { + streamer = new EnumStreamer(ClassUtil.getClass(obj)); + + } else if (obj is Streamable) { + streamer = new Streamer(ClassUtil.getClass(obj)); + + } else { + return null; } - return BAD_STREAMER; + // add the new streamer and return it + _streamers.push(streamer); + return streamer; } public static function getStreamerByClass (clazz :Class) :Streamer { initStreamers(); - if (ClassUtil.isAssignableAs(Streamable, clazz)) { - return null; // Streamable - } - if (clazz === TypedArray) { throw new Error("Broken, TODO"); } - for each (var streamer :Streamer in _streamers) { + var streamer :Streamer; + for each (streamer in _streamers) { if (streamer.isStreamerForClass(clazz)) { return streamer; } } - return BAD_STREAMER; + if (ClassUtil.isAssignableAs(Enum, clazz)) { + streamer = new EnumStreamer(clazz); + + } else if (ClassUtil.isAssignableAs(Streamable, clazz)) { + streamer = new Streamer(clazz); + + } else { + return null; + } + + // add the new streamer and return it + _streamers.push(streamer); + return streamer; } public static function getStreamerByJavaName (jname :String) :Streamer @@ -103,26 +119,33 @@ public class Streamer // see if it's an array that we unstream using an ArrayStreamer if (jname.charAt(0) === "[") { streamer = new ArrayStreamer(jname); - _streamers.push(streamer); - return streamer; + + } else { + // otherwise see if it represents a Streamable + var clazz :Class = ClassUtil.getClassByName(Translations.getFromServer(jname)); + + if (ClassUtil.isAssignableAs(Enum, clazz)) { + streamer = new EnumStreamer(clazz, jname); + + } else if (ClassUtil.isAssignableAs(Streamable, clazz)) { + streamer = new Streamer(clazz, jname); + + } else { + return null; + } } - // otherwise see if it represents a Streamable - var clazz :Class = ClassUtil.getClassByName( - Translations.getFromServer(jname)); - if (ClassUtil.isAssignableAs(Streamable, clazz)) { - return null; // it's streamable - } - - return BAD_STREAMER; + // add the good new streamer + _streamers.push(streamer); + return streamer; } /** This should be a protected constructor. */ - public function Streamer (targ :Class, jname :String) + public function Streamer (targ :Class, jname :String = null) //throws IOError { _target = targ; - _jname = jname; + _jname = (jname != null) ? jname : Translations.getToServer(ClassUtil.getClassName(targ)); } public function isStreamerFor (obj :Object) :Boolean @@ -146,7 +169,7 @@ public class Streamer public function writeObject (obj :Object, out :ObjectOutputStream) :void //throws IOError { - throw new Error("Abstract"); + (obj as Streamable).writeObject(out); } public function createObject (ins :ObjectInputStream) :Object @@ -159,7 +182,7 @@ public class Streamer public function readObject (obj :Object, ins :ObjectInputStream) :void //throws IOError { - throw new Error("Abstract"); + (obj as Streamable).readObject(ins); } /** diff --git a/src/as/com/threerings/io/streamers/EnumStreamer.as b/src/as/com/threerings/io/streamers/EnumStreamer.as new file mode 100644 index 000000000..4fa7fafdb --- /dev/null +++ b/src/as/com/threerings/io/streamers/EnumStreamer.as @@ -0,0 +1,52 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/narya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.io.streamers { + +import com.threerings.util.Enum; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.Streamer; + +public class EnumStreamer extends Streamer +{ + public function EnumStreamer (enumClass :Class, jname :String = null) + { + super(enumClass, jname); + } + + override public function createObject (ins :ObjectInputStream) :Object + { + return Enum.valueOf(_target, ins.readUTF()); + } + + override public function writeObject (obj :Object, out :ObjectOutputStream) :void + { + out.writeUTF((obj as Enum).name()); + } + + override public function readObject (obj :Object, ins :ObjectInputStream) :void + { + // unneeded, done in createObject + } +} +}