diff --git a/build.xml b/build.xml
index 4fb8f7220..829c9adbc 100644
--- a/build.xml
+++ b/build.xml
@@ -85,6 +85,14 @@
+
+
+
+
+
+
+
readObject() and writeObject() methods for {@link
+ * Streamable} classes that have protected or private members so that they can be used in a
+ * sandboxed environment.
+ */
+public class GenStreamableTask extends Task
+{
+ /**
+ * Adds a nested <fileset> element which enumerates streamable source files.
+ */
+ public void addFileset (FileSet set)
+ {
+ _filesets.add(set);
+ }
+
+ /**
+ * Performs the actual work of the task.
+ */
+ public void execute () throws BuildException
+ {
+ for (FileSet fs : _filesets) {
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ File fromDir = fs.getDir(getProject());
+ String[] srcFiles = ds.getIncludedFiles();
+ for (int f = 0; f < srcFiles.length; f++) {
+ processClass(new File(fromDir, srcFiles[f]));
+ }
+ }
+ }
+
+ /**
+ * Processes a {@link Streamable} source file.
+ */
+ protected void processClass (File source)
+ {
+ // load up the file and determine it's package and classname
+ String name = null;
+ try {
+ name = GenUtil.readClassName(source);
+ } catch (Exception e) {
+ System.err.println("Failed to parse " + source + ": " + e.getMessage());
+ return;
+ }
+
+ System.err.println("Considering " + name + "...");
+
+ try {
+ // in order for annotations to work, this task and all the classes it uses must be
+ // loaded from the same class loader as the classes on which we are going to
+ // introspect; this is non-ideal but unavoidable
+ processClass(source, getClass().getClassLoader().loadClass(name));
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("Failed to load " + name + ".\nMissing class: " + cnfe.getMessage());
+ System.err.println("Be sure to set the 'classpathref' attribute to a classpath\n" +
+ "that contains your projects invocation service classes.");
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ }
+ }
+
+ /**
+ * Processes a resolved {@link Streamable} class instance.
+ */
+ protected void processClass (File source, Class> sclass)
+ throws IOException
+ {
+ // we must implement Streamable, not be a DObject and not be an interface ourselves
+ if (!Streamable.class.isAssignableFrom(sclass) || DObject.class.isAssignableFrom(sclass) ||
+ Modifier.isInterface(sclass.getModifiers())) {
+ // System.err.println("Skipping " + sclass.getName() + "...");
+ return;
+ }
+
+ // add readObject() and writeObject() definitions
+ StringBuffer readbuf = new StringBuffer(READ_OPEN);
+ StringBuffer writebuf = new StringBuffer(WRITE_OPEN);
+
+ // see if our parent also implements Streamable
+ Class supster = sclass.getSuperclass();
+ do {
+ if (isStreamable(supster)) {
+ readbuf.append(" super.readObject(ins);\n");
+ writebuf.append(" super.writeObject(out);\n");
+ break;
+ }
+ supster = supster.getSuperclass();
+ } while (supster != null);
+
+ int added = 0;
+ for (Field field : sclass.getDeclaredFields()) {
+ if (!isStreamableField(field)) {
+ continue;
+ }
+ readbuf.append(" ");
+ readbuf.append(field.getName()).append(" = ");
+ readbuf.append(toReadObject(field));
+ readbuf.append(";\n");
+
+ writebuf.append(" out.");
+ writebuf.append(toWriteObject(field));
+ writebuf.append(";\n");
+ added++;
+ }
+ if (added == 0) {
+ return; // nothing to do
+ }
+
+ readbuf.append(READ_CLOSE);
+ writebuf.append(WRITE_CLOSE);
+
+ System.err.println("Converting " + sclass.getName() + "...");
+
+ SourceFile sfile = new SourceFile();
+ try {
+ sfile.readFrom(source);
+ // don't overwrite an existing readObject() or writeObject()
+ String readsec =
+ sfile.containsString("public void readObject") ? "" : readbuf.toString();
+ String writesec =
+ sfile.containsString("public void writeObject") ? "" : writebuf.toString();
+ sfile.writeTo(source, readsec, writesec);
+ } catch (IOException ioe) {
+ System.err.println("Error processing " + source + ": " + ioe);
+ }
+ }
+
+ protected boolean isStreamable (Class clazz)
+ {
+ for (Class iface : clazz.getInterfaces()) {
+ if (Streamable.class.equals(iface)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean isStreamableField (Field field)
+ {
+ int mods = field.getModifiers();
+ return !Modifier.isStatic(mods) && !Modifier.isTransient(mods);
+ }
+
+ protected String toReadObject (Field field)
+ {
+ Class> type = field.getType();
+ if (type.equals(String.class)) {
+ return "ins.readUTF()";
+ } else if (type.equals(Boolean.TYPE) || type.equals(Boolean.class)) {
+ return "ins.readBoolean()";
+ } else if (type.equals(Byte.TYPE) || type.equals(Byte.class)) {
+ return "ins.readByte()";
+ } else if (type.equals(Short.TYPE) || type.equals(Short.class)) {
+ return "ins.readShort()";
+ } else if (type.equals(Integer.TYPE) || type.equals(Integer.class)) {
+ return "ins.readInt()";
+ } else if (type.equals(Long.TYPE) || type.equals(Long.class)) {
+ return "ins.readLong()";
+ } else if (type.equals(Float.TYPE) || type.equals(Float.class)) {
+ return "ins.readFloat()";
+ } else if (type.equals(Double.TYPE) || type.equals(Double.class)) {
+ return "ins.readDouble()";
+ } else {
+ return "(" + GenUtil.simpleName(field) + ")ins.readObject()";
+ }
+ }
+
+ protected String toWriteObject (Field field)
+ {
+ Class> type = field.getType();
+ String name = field.getName();
+ if (type.equals(Boolean.TYPE) || type.equals(Boolean.class)) {
+ return "writeBoolean(" + name + ")";
+ } else if (type.equals(Byte.TYPE) || type.equals(Byte.class)) {
+ return "writeByte(" + name + ")";
+ } else if (type.equals(Short.TYPE) || type.equals(Short.class)) {
+ return "writeShort(" + name + ")";
+ } else if (type.equals(Integer.TYPE) || type.equals(Integer.class)) {
+ return "writeInt(" + name + ")";
+ } else if (type.equals(Long.TYPE) || type.equals(Long.class)) {
+ return "writeLong(" + name + ")";
+ } else if (type.equals(Float.TYPE) || type.equals(Float.class)) {
+ return "writeFloat(" + name + ")";
+ } else if (type.equals(Double.TYPE) || type.equals(Double.class)) {
+ return "writeDouble(" + name + ")";
+ } else if (type.equals(String.class)) {
+ return "writeUTF(" + name + ")";
+ } else {
+ return "writeObject(" + name + ")";
+ }
+ }
+
+ /** A list of filesets that contain tile images. */
+ protected ArrayList _filesets = new ArrayList();
+
+ protected static final String READ_OPEN =
+ " // from interface Streamable\n" +
+ " public void readObject (ObjectInputStream ins)\n" +
+ " throws IOException, ClassNotFoundException\n" +
+ " {\n";
+ protected static final String READ_CLOSE = " }\n";
+
+ protected static final String WRITE_OPEN =
+ " // from interface Streamable\n" +
+ " public void writeObject (ObjectOutputStream out)\n" +
+ " throws IOException\n" +
+ " {\n";
+ protected static final String WRITE_CLOSE = " }\n";
+}