Allow streaming Class references.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5524 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Andrzej Kapolka
2008-11-08 01:57:42 +00:00
parent 4b9477ca91
commit 28c7609df8
3 changed files with 97 additions and 52 deletions
@@ -44,6 +44,7 @@ public class BasicStreamers
Long.class,
Float.class,
Double.class,
Class.class,
String.class,
boolean[].class,
byte[].class,
@@ -68,6 +69,7 @@ public class BasicStreamers
new LongStreamer(),
new FloatStreamer(),
new DoubleStreamer(),
new ClassStreamer(),
new StringStreamer(),
new BooleanArrayStreamer(),
new ByteArrayStreamer(),
@@ -225,6 +227,24 @@ public class BasicStreamers
}
}
/** Streams {@link Class} instances. */
public static class ClassStreamer extends BasicStreamer
{
@Override
public Object createObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
return in.readClassMapping().sclass;
}
@Override
public void writeObject (Object object, ObjectOutputStream out, boolean useWriter)
throws IOException
{
out.writeClassMapping((Class)object);
}
}
/** Streams {@link String} instances. */
public static class StringStreamer extends BasicStreamer
{
@@ -76,59 +76,14 @@ public class ObjectInputStream extends DataInputStream
public Object readObject ()
throws IOException, ClassNotFoundException
{
ClassMapping cmap;
// create our classmap if necessary
if (_classmap == null) {
_classmap = new ArrayList<ClassMapping>();
// insert a zeroth element
_classmap.add(null);
}
try {
// read in the class code for this instance
short code = readShort();
// a zero code indicates a null value
if (code == 0) {
// read the class mapping
ClassMapping cmap = readClassMapping();
if (cmap == null) {
if (STREAM_DEBUG) {
log.info(hashCode() + ": Read null.");
}
return null;
// if the code is negative, that means that we've never seen it before and class
// metadata follows
} else if (code < 0) {
// first swap the code into positive-land
code *= -1;
// read in the class metadata
String cname = readUTF();
// if we have a translation (used to cope when serialized classes are renamed) use
// it
if (_translations != null) {
String tname = _translations.get(cname);
if (tname != null) {
cname = tname;
}
}
// create the class mapping
cmap = mapClass(code, cname);
} else {
cmap = _classmap.get(code);
// sanity check
if (cmap == null) {
// this will help with debugging
log.warning("Internal stream error, no class metadata", "code", code,
"ois", this, new Exception());
log.warning("ObjectInputStream mappings", "map", _classmap);
String errmsg = "Read object code for which we have no registered class " +
"metadata [code=" + code + "]";
throw new RuntimeException(errmsg);
}
}
if (STREAM_DEBUG) {
@@ -145,6 +100,65 @@ public class ObjectInputStream extends DataInputStream
}
}
/**
* Reads a class mapping from the stream.
*
* @return the class mapping, or <code>null</code> to represent a null value.
*/
protected ClassMapping readClassMapping ()
throws IOException, ClassNotFoundException
{
// create our classmap if necessary
if (_classmap == null) {
_classmap = new ArrayList<ClassMapping>();
// insert a zeroth element
_classmap.add(null);
}
// read in the class code for this instance
short code = readShort();
// a zero code indicates a null value
if (code == 0) {
return null;
// if the code is negative, that means that we've never seen it before and class
// metadata follows
} else if (code < 0) {
// first swap the code into positive-land
code *= -1;
// read in the class metadata
String cname = readUTF();
// if we have a translation (used to cope when serialized classes are renamed) use
// it
if (_translations != null) {
String tname = _translations.get(cname);
if (tname != null) {
cname = tname;
}
}
// create the class mapping
return mapClass(code, cname);
} else {
ClassMapping cmap = _classmap.get(code);
// sanity check
if (cmap == null) {
// this will help with debugging
log.warning("Internal stream error, no class metadata", "code", code,
"ois", this, new Exception());
log.warning("ObjectInputStream mappings", "map", _classmap);
String errmsg = "Read object code for which we have no registered class " +
"metadata [code=" + code + "]";
throw new RuntimeException(errmsg);
}
return cmap;
}
}
/**
* Creates, adds, and returns the class mapping for the specified code and class name.
*/
@@ -71,13 +71,25 @@ public class ObjectOutputStream extends DataOutputStream
return;
}
// otherwise, write the class mapping, then the bare object
Class<?> sclass = Streamer.getStreamerClass(object);
ClassMapping cmap = writeClassMapping(sclass);
writeBareObject(object, cmap.streamer, true);
}
/**
* Retrieves or creates the class mapping for the supplied class, writes it out to the stream,
* and returns a reference to it.
*/
protected ClassMapping writeClassMapping (Class<?> sclass)
throws IOException
{
// create our classmap if necessary
if (_classmap == null) {
_classmap = new HashMap<Class<?>, ClassMapping>();
}
// otherwise, look up the class mapping record
Class<?> sclass = Streamer.getStreamerClass(object);
// look up the class mapping record
ClassMapping cmap = _classmap.get(sclass);
// create a class mapping for this class if we've not got one
@@ -103,8 +115,7 @@ public class ObjectOutputStream extends DataOutputStream
} else {
writeExistingClassMapping(cmap);
}
writeBareObject(object, cmap.streamer, true);
return cmap;
}
/**