diff --git a/src/java/com/threerings/io/FieldMarshaller.java b/src/java/com/threerings/io/FieldMarshaller.java index ce230a726..eaddb47fb 100644 --- a/src/java/com/threerings/io/FieldMarshaller.java +++ b/src/java/com/threerings/io/FieldMarshaller.java @@ -22,9 +22,13 @@ package com.threerings.io; import java.lang.reflect.Field; +import java.lang.reflect.Method; + import java.util.Date; import java.util.HashMap; +import static com.threerings.NaryaLog.log; + /** * Used to read and write a single field of a {@link Streamable} instance. */ @@ -53,6 +57,25 @@ public abstract class FieldMarshaller createMarshallers(); } + // first look to see if this field has custom reader/writer methods + Method reader = null, writer = null; + try { + reader = field.getDeclaringClass().getMethod( + getReaderMethodName(field.getName()), READER_ARGS); + writer = field.getDeclaringClass().getMethod( + getWriterMethodName(field.getName()), WRITER_ARGS); + return new MethodFieldMarshaller(reader, writer); + } catch (NoSuchMethodException nsme) { + // no problem + } + if ((reader != null || writer != null) && (reader == null || writer == null)) { + log.warning("Class contains one but not both custom field reader and writer " + + "[class=" + field.getDeclaringClass().getName() + + ", field=" + field.getName() + ", reader=" + reader + + ", writer=" + writer + "]."); + // fall through to using reflection on the fields... + } + Class ftype = field.getType(); if (ftype.isInterface()) { // if the class is a pure interface, use Object. @@ -70,6 +93,24 @@ public abstract class FieldMarshaller return fm; } + /** + * Returns the name of the custom reader method which will be used if it exists to stream a + * field with the supplied name. + */ + public static final String getReaderMethodName (String field) + { + return "readField_" + field; + } + + /** + * Returns the name of the custom writer method which will be used if it exists to stream a + * field with the supplied name. + */ + public static final String getWriterMethodName (String field) + { + return "writeField_" + field; + } + /** * Used to marshall and unmarshall classes for which we have a basic {@link Streamer}. */ @@ -110,6 +151,34 @@ public abstract class FieldMarshaller protected Streamer _streamer; } + /** + * Uses custom reader and writer methods to read and write a field. + */ + protected static class MethodFieldMarshaller extends FieldMarshaller + { + public MethodFieldMarshaller (Method reader, Method writer) + { + _reader = reader; + _writer = writer; + } + + // documentation inherited + public void readField (Field field, Object target, ObjectInputStream in) + throws Exception + { + _reader.invoke(target, in); + } + + // documentation inherited + public void writeField (Field field, Object source, ObjectOutputStream out) + throws Exception + { + _writer.invoke(source, out); + } + + protected Method _reader, _writer; + } + /** * Creates instances of all known field marshaller types and populates the {@link * #_marshallers} table with them. This is called the first time a marshaller is requested. @@ -240,4 +309,10 @@ public abstract class FieldMarshaller /** Contains a mapping from field type to field marshaller instance for that type. */ protected static HashMap _marshallers; + + /** Defines the signature to a custom field reader method. */ + protected static final Class[] READER_ARGS = { ObjectInputStream.class }; + + /** Defines the signature to a custom field writer method. */ + protected static final Class[] WRITER_ARGS = { ObjectOutputStream.class }; } diff --git a/src/java/com/threerings/presents/tools/InstrumentStreamableTask.java b/src/java/com/threerings/presents/tools/InstrumentStreamableTask.java index 5eed6a58a..ef621b5cf 100644 --- a/src/java/com/threerings/presents/tools/InstrumentStreamableTask.java +++ b/src/java/com/threerings/presents/tools/InstrumentStreamableTask.java @@ -45,6 +45,7 @@ import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Path; import com.samskivert.io.StreamUtil; +import com.threerings.io.FieldMarshaller; import com.threerings.io.Streamable; /** @@ -154,8 +155,6 @@ public class InstrumentStreamableTask extends Task protected void processStreamable (File source, CtClass clazz) throws NotFoundException { - // System.err.println("Processing " + clazz.getName() + "..."); - ArrayList fields = new ArrayList(); for (CtField field : clazz.getDeclaredFields()) { int modifiers = field.getModifiers(); @@ -173,13 +172,13 @@ public class InstrumentStreamableTask extends Task int added = 0; for (CtField field : fields) { - String rname = "readField_" + field.getName(); + String rname = FieldMarshaller.getReaderMethodName(field.getName()); if (!methods.contains(rname)) { String reader = "public void " + rname + " (com.threerings.io.ObjectInputStream ins) {\n" + " " + field.getName() + " = " + getFieldReader(field) + ";\n" + "}"; - // System.err.println("Adding reader " + clazz.getName() + "." + rname); + // System.out.println("Adding reader " + clazz.getName() + "." + rname); try { clazz.addMethod(CtNewMethod.make(reader, clazz)); added++; @@ -189,9 +188,10 @@ public class InstrumentStreamableTask extends Task System.err.println(reader); } } - String wname = "writeField_" + field.getName(); + + String wname = FieldMarshaller.getWriterMethodName(field.getName()); if (!methods.contains(wname)) { - // System.err.println("Adding writer " + clazz.getName() + "." + wname); + // System.out.println("Adding writer " + clazz.getName() + "." + wname); String writer = "public void " + wname + " (com.threerings.io.ObjectOutputStream out) {\n" + " out." + getFieldWriter(field) + ";\n" + @@ -209,6 +209,7 @@ public class InstrumentStreamableTask extends Task if (added > 0) { try { + System.out.println("Instrumented '" + clazz.getName() + "'."); clazz.writeFile(_outdir.getPath()); } catch (Exception e) { System.err.println("Failed to write instrumented class [class=" + clazz +