Separate GenActionScriptStreamableTask from GenActionScriptTask and add dobject support to GenActionScriptStreamableTask

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6315 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Charlie Groves
2010-11-30 02:39:12 +00:00
parent 220b7a28b5
commit 5a3694aa6f
7 changed files with 305 additions and 251 deletions
@@ -44,6 +44,7 @@ import com.threerings.gwt.util.PopupCallback;
* The main panel of the configuration editor. All service calls are routed through here.
* Subclass this class in your project.
*/
@SuppressWarnings("deprecation")
public abstract class ConfigEditorPanel extends TabPanel
implements ConfigAccessor
{
@@ -0,0 +1,229 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.presents.tools;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.io.File;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.threerings.util.ActionScript;
import com.threerings.util.StreamableArrayList;
import com.threerings.util.StreamableHashMap;
public class ActionScriptUtils
{
protected static boolean needsActionScriptImport (Class<?> type, boolean isField)
{
if (type.isArray()) {
return Byte.TYPE.equals(type.getComponentType()) || isField;
}
return (Long.TYPE.equals(type) || !type.isPrimitive()) && !String.class.equals(type);
}
protected static String addImportAndGetShortType (Class<?> type, boolean isField,
Set<String> imports)
{
String full = toActionScriptType(type, isField);
if (needsActionScriptImport(type, isField)) {
imports.add(full);
}
return Iterables.getLast(DOT_SPLITTER.split(full));
}
public static String toReadObject (Class<?> type)
{
if (type.equals(String.class)) {
return "readField(String)";
} else if (type.equals(Integer.class) ||
type.equals(Short.class) ||
type.equals(Byte.class)) {
String name = ActionScriptSource.toSimpleName(type.getName());
return "readField(" + name + ").value";
} else if (type.equals(Long.class)) {
String name = ActionScriptSource.toSimpleName(type.getName());
return "readField(" + name + ")";
} else if (type.equals(Boolean.TYPE)) {
return "readBoolean()";
} else if (type.equals(Byte.TYPE)) {
return "readByte()";
} else if (type.equals(Short.TYPE) || type.equals(Character.TYPE)) {
return "readShort()";
} else if (type.equals(Integer.TYPE)) {
return "readInt()";
} else if (type.equals(Long.TYPE)) {
return "readLong()";
} else if (type.equals(Float.TYPE)) {
return "readFloat()";
} else if (type.equals(Double.TYPE)) {
return "readDouble()";
} else if (isNaiveMap(type)) {
return "readField(MapStreamer.INSTANCE)";
} else if (isNaiveList(type)) {
return "readField(ArrayStreamer.INSTANCE)";
} else if (type.isArray()) {
if (!type.getComponentType().isPrimitive()) {
return "readObject(TypedArray)";
} else {
if (Double.TYPE.equals(type.getComponentType())) {
return "readField(TypedArray.getJavaType(Number))";
} else if (Boolean.TYPE.equals(type.getComponentType())) {
return "readField(TypedArray.getJavaType(Boolean))";
} else if (Integer.TYPE.equals(type.getComponentType())) {
return "readField(TypedArray.getJavaType(int))";
} else if (Byte.TYPE.equals(type.getComponentType())) {
return "readField(ByteArray)";
} else {
throw new IllegalArgumentException(type
+ " isn't supported to stream to actionscript");
}
}
} else {
return "readObject(" + Iterables.getLast(DOT_SPLITTER.split(toActionScriptType(type, false))) + ")";
}
}
public static String toWriteObject (Class<?> type, String name)
{
if (type.equals(Integer.class)) {
return "writeObject(new Integer(" + name + "))";
} else if (type.equals(Boolean.TYPE)) {
return "writeBoolean(" + name + ")";
} else if (type.equals(Byte.TYPE)) {
return "writeByte(" + name + ")";
} else if (type.equals(Short.TYPE) ||
type.equals(Character.TYPE)) {
return "writeShort(" + name + ")";
} else if (type.equals(Integer.TYPE)) {
return "writeInt(" + name + ")";
} else if (type.equals(Long.TYPE)) {
return "writeLong(" + name + ")";
} else if (type.equals(Float.TYPE)) {
return "writeFloat(" + name + ")";
} else if (type.equals(Double.TYPE)) {
return "writeDouble(" + name + ")";
} else if (type.equals(Long.class) ||
type.equals(String.class) ||
(type.isArray() && type.getComponentType().isPrimitive())) {
return "writeField(" + name + ")";
} else if (isNaiveList(type)) {
return "writeField(" + name + ", ArrayStreamer.INSTANCE)";
} else if (isNaiveMap(type)) {
return "writeField(" + name + ", MapStreamer.INSTANCE)";
} else {
return "writeObject(" + name + ")";
}
}
/** Returns if the given class is an implementation of Map that doesn't know about Streaming */
protected static boolean isNaiveMap (Class<?> type)
{
return Map.class.isAssignableFrom(type) && !type.equals(StreamableHashMap.class);
}
/** Returns if the given class is an implementation of List that doesn't know about Streaming */
protected static boolean isNaiveList (Class<?> type)
{
return List.class.isAssignableFrom(type) && !type.equals(StreamableArrayList.class);
}
public static String toActionScriptType (Class<?> type, boolean isField)
{
if (type.isArray() || isNaiveList(type)) {
if (Byte.TYPE.equals(type.getComponentType())) {
return "flash.utils.ByteArray";
}
if (isField) {
return "com.threerings.io.TypedArray";
}
return "Array";
} else if (isNaiveMap(type)) {
return "com.threerings.util.Map";
} else if (Integer.TYPE.equals(type) ||
Byte.TYPE.equals(type) ||
Short.TYPE.equals(type) ||
Character.TYPE.equals(type)) {
return "int";
} else if (Float.TYPE.equals(type) ||
Double.TYPE.equals(type)) {
return "Number";
} else if (Long.TYPE.equals(type)) {
return "com.threerings.util.Long";
} else if (Boolean.TYPE.equals(type)) {
return "Boolean";
} else {
// inner classes are not supported by ActionScript so we _
return type.getName().replaceAll("\\$", "_");
}
}
public static File createActionScriptPath (File actionScriptRoot, Class<?> sclass)
{
// determine the path to the corresponding action script source file
String path = toActionScriptType(sclass, false).replace(".", File.separator);
return new File(actionScriptRoot, path + ".as");
}
public static boolean hasOmitAnnotation (Class<?> cclass)
{
// if we have an ActionScript(omit=true) annotation, skip this class
do {
ActionScript asa = cclass.getAnnotation(ActionScript.class);
if (asa != null && asa.omit()) {
// System.err.println("Skipping " + sclass.getName() + "...");
return true;
}
cclass = cclass.getSuperclass();
} while (cclass != null);
return false;
}
protected static final Splitter DOT_SPLITTER = Splitter.on('.');
}
@@ -22,6 +22,7 @@
package com.threerings.presents.tools;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
@@ -29,6 +30,8 @@ import java.util.Set;
import java.io.File;
import org.apache.tools.ant.Project;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
@@ -36,15 +39,37 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.samskivert.util.StringUtil;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
public class GenActionScriptStreamableTask extends GenActionScriptTask
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.DObject;
public class GenActionScriptStreamableTask extends GenTask
{
/**
* Configures the path to our ActionScript source files.
*/
public void setAsroot (File asroot)
{
_asroot = asroot;
}
@Override
protected void convert (File javaSource, Class<?> sclass, File outputLocation)
protected void processClass (File javaSource, Class<?> sclass)
throws Exception
{
if (!Streamable.class.isAssignableFrom(sclass)
|| InvocationMarshaller.class.isAssignableFrom(sclass)
|| Modifier.isInterface(sclass.getModifiers())
|| ActionScriptUtils.hasOmitAnnotation(sclass)) {
log("Skipping " + sclass.getName(), Project.MSG_VERBOSE);
return;
}
log("Generating " + sclass.getName(), Project.MSG_VERBOSE);
// Generate the current version of the streamable
StreamableClassRequirements reqs = new StreamableClassRequirements(sclass);
@@ -53,12 +78,13 @@ public class GenActionScriptStreamableTask extends GenActionScriptTask
imports.add(ObjectOutputStream.class.getName());
String extendsName = "";
if (!sclass.getSuperclass().equals(Object.class)) {
extendsName = addImportAndGetShortType(sclass.getSuperclass(), false, imports);
extendsName =
ActionScriptUtils.addImportAndGetShortType(sclass.getSuperclass(), false, imports);
}
Set<String> implemented = Sets.newLinkedHashSet();
for (Class<?> iface : sclass.getInterfaces()) {
implemented.add(addImportAndGetShortType(iface, false, imports));
implemented.add(ActionScriptUtils.addImportAndGetShortType(iface, false, imports));
}
List<ASField> fields = Lists.newArrayList();
for (Field f : reqs.streamedFields) {
@@ -72,8 +98,10 @@ public class GenActionScriptStreamableTask extends GenActionScriptTask
"extends", extendsName,
"implements", Joiner.on(", ").join(implemented),
"superclassStreamable", reqs.superclassStreamable,
"fields", fields);
"fields", fields,
"dobject", DObject.class.isAssignableFrom(sclass));
File outputLocation = ActionScriptUtils.createActionScriptPath(_asroot, sclass);
if (outputLocation.exists()) {
// Merge in the previously generated version
String existing = Files.toString(outputLocation, Charsets.UTF_8);
@@ -92,11 +120,13 @@ public class GenActionScriptStreamableTask extends GenActionScriptTask
public final String simpleType;
public final String reader;
public final String writer;
public final String dobjectField;
public ASField (Field f, Set<String> imports)
{
this.name = f.getName();
this.simpleType = addImportAndGetShortType(f.getType(), true, imports);
name = f.getName();
dobjectField = StringUtil.unStudlyName(name).toUpperCase();
simpleType = ActionScriptUtils.addImportAndGetShortType(f.getType(), true, imports);
// Lists and Maps use their Streamers directly
if (List.class.isAssignableFrom(f.getType())) {
@@ -104,8 +134,11 @@ public class GenActionScriptStreamableTask extends GenActionScriptTask
} else if (Map.class.isAssignableFrom(f.getType())) {
imports.add("com.threerings.io.streamers.MapStreamer");
}
this.reader = toReadObject(f.getType());
this.writer = toWriteObject(f.getType(), name);
reader = ActionScriptUtils.toReadObject(f.getType());
writer = ActionScriptUtils.toWriteObject(f.getType(), name);
}
}
/** The path to our ActionScript source files. */
protected File _asroot;
}
@@ -24,29 +24,15 @@ package com.threerings.presents.tools;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
import com.threerings.util.ActionScript;
import com.threerings.util.StreamableArrayList;
import com.threerings.util.StreamableHashMap;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.DObject;
@@ -64,23 +50,11 @@ public class GenActionScriptTask extends GenTask
_asroot = asroot;
}
/**
* Configures us with a header file that we'll prepend to all generated source files.
*/
public void setHeader (File header)
{
try {
_header = Files.toString(header, Charsets.UTF_8);
} catch (IOException ioe) {
System.err.println("Unabled to load header '" + header + ": " + ioe.getMessage());
}
}
/**
* Processes a resolved Streamable class instance.
*/
@Override
public void processClass (File source, Class<?> sclass)
public void processClass (File javaSource, Class<?> sclass)
throws Exception
{
// make sure we implement Streamable but don't extend DObject or InvocationMarshaller and
@@ -88,33 +62,15 @@ public class GenActionScriptTask extends GenTask
if (!Streamable.class.isAssignableFrom(sclass) ||
DObject.class.isAssignableFrom(sclass) ||
InvocationMarshaller.class.isAssignableFrom(sclass) ||
((sclass.getModifiers() & Modifier.INTERFACE) != 0)) {
((sclass.getModifiers() & Modifier.INTERFACE) != 0) ||
ActionScriptUtils.hasOmitAnnotation(sclass)) {
// System.err.println("Skipping " + sclass.getName() + "...");
return;
}
// if we have an ActionScript(omit=true) annotation, skip this class
Class<?> cclass = sclass;
do {
ActionScript asa = cclass.getAnnotation(ActionScript.class);
if (asa != null && asa.omit()) {
// System.err.println("Skipping " + sclass.getName() + "...");
return;
}
cclass = cclass.getSuperclass();
} while (cclass != null);
// determine the path to the corresponding action script source file
String path = toActionScriptType(sclass, false).replace(".", File.separator);
File asfile = new File(_asroot, path + ".as");
File output = ActionScriptUtils.createActionScriptPath(_asroot, sclass);
System.err.println("Converting " + sclass.getName() + "...");
convert(source, sclass, asfile);
}
protected void convert (File javaSource, Class<?> sclass, File output)
throws Exception
{
// parse the existing ActionScript source and generate what we don't
// have from the Java class
ActionScriptSource assrc = new ActionScriptSource(sclass);
@@ -142,7 +98,7 @@ public class GenActionScriptTask extends GenTask
}
body.append(" ");
body.append(field.getName()).append(" = ins.");
body.append(toReadObject(field.getType()));
body.append(ActionScriptUtils.toReadObject(field.getType()));
body.append(";\n");
added++;
}
@@ -165,7 +121,7 @@ public class GenActionScriptTask extends GenTask
continue;
}
body.append(" out.");
body.append(toWriteObject(field.getType(), field.getName()));
body.append(ActionScriptUtils.toWriteObject(field.getType(), field.getName()));
body.append(";\n");
added++;
}
@@ -191,176 +147,6 @@ public class GenActionScriptTask extends GenTask
return !Modifier.isStatic(mods) && !Modifier.isTransient(mods);
}
protected static String addImportAndGetShortType (Class<?> type, boolean isField,
Set<String> imports)
{
String full = toActionScriptType(type, isField);
if (needsActionScriptImport(type, isField)) {
imports.add(full);
}
return Iterables.getLast(DOT_SPLITTER.split(full));
}
protected static boolean needsActionScriptImport (Class<?> type, boolean isField)
{
if (type.isArray()) {
return Byte.TYPE.equals(type.getComponentType()) || isField;
}
return (Long.TYPE.equals(type) || !type.isPrimitive()) && !String.class.equals(type);
}
/** Returns if the given class is an implementation of Map that doesn't know about Streaming */
protected static boolean isNaiveMap (Class<?> type)
{
return Map.class.isAssignableFrom(type) && !type.equals(StreamableHashMap.class);
}
/** Returns if the given class is an implementation of List that doesn't know about Streaming */
protected static boolean isNaiveList (Class<?> type)
{
return List.class.isAssignableFrom(type) && !type.equals(StreamableArrayList.class);
}
protected static String toActionScriptType (Class<?> type, boolean isField)
{
if (type.isArray() || isNaiveList(type)) {
if (Byte.TYPE.equals(type.getComponentType())) {
return "flash.utils.ByteArray";
}
if (isField) {
return "com.threerings.io.TypedArray";
}
return "Array";
} else if (isNaiveMap(type)) {
return "com.threerings.util.Map";
} else if (Integer.TYPE.equals(type) ||
Byte.TYPE.equals(type) ||
Short.TYPE.equals(type) ||
Character.TYPE.equals(type)) {
return "int";
} else if (Float.TYPE.equals(type) ||
Double.TYPE.equals(type)) {
return "Number";
} else if (Long.TYPE.equals(type)) {
return "com.threerings.util.Long";
} else if (Boolean.TYPE.equals(type)) {
return "Boolean";
} else {
// inner classes are not supported by ActionScript so we _
return type.getName().replaceAll("\\$", "_");
}
}
public static String toReadObject (Class<?> type)
{
if (type.equals(String.class)) {
return "readField(String)";
} else if (type.equals(Integer.class) ||
type.equals(Short.class) ||
type.equals(Byte.class)) {
String name = ActionScriptSource.toSimpleName(type.getName());
return "readField(" + name + ").value";
} else if (type.equals(Long.class)) {
String name = ActionScriptSource.toSimpleName(type.getName());
return "readField(" + name + ")";
} else if (type.equals(Boolean.TYPE)) {
return "readBoolean()";
} else if (type.equals(Byte.TYPE)) {
return "readByte()";
} else if (type.equals(Short.TYPE) || type.equals(Character.TYPE)) {
return "readShort()";
} else if (type.equals(Integer.TYPE)) {
return "readInt()";
} else if (type.equals(Long.TYPE)) {
return "readLong()";
} else if (type.equals(Float.TYPE)) {
return "readFloat()";
} else if (type.equals(Double.TYPE)) {
return "readDouble()";
} else if (isNaiveMap(type)) {
return "readField(MapStreamer.INSTANCE)";
} else if (isNaiveList(type)) {
return "readField(ArrayStreamer.INSTANCE)";
} else if (type.isArray()) {
if (!type.getComponentType().isPrimitive()) {
return "readObject(TypedArray)";
} else {
if (Double.TYPE.equals(type.getComponentType())) {
return "readField(TypedArray.getJavaType(Number))";
} else if (Boolean.TYPE.equals(type.getComponentType())) {
return "readField(TypedArray.getJavaType(Boolean))";
} else if (Integer.TYPE.equals(type.getComponentType())) {
return "readField(TypedArray.getJavaType(int))";
} else if (Byte.TYPE.equals(type.getComponentType())) {
return "readField(ByteArray)";
} else {
throw new IllegalArgumentException(type
+ " isn't supported to stream to actionscript");
}
}
} else {
return "readObject(" + Iterables.getLast(DOT_SPLITTER.split(toActionScriptType(type, false))) + ")";
}
}
public static String toWriteObject (Class<?> type, String name)
{
if (type.equals(Integer.class)) {
return "writeObject(new Integer(" + name + "))";
} else if (type.equals(Boolean.TYPE)) {
return "writeBoolean(" + name + ")";
} else if (type.equals(Byte.TYPE)) {
return "writeByte(" + name + ")";
} else if (type.equals(Short.TYPE) ||
type.equals(Character.TYPE)) {
return "writeShort(" + name + ")";
} else if (type.equals(Integer.TYPE)) {
return "writeInt(" + name + ")";
} else if (type.equals(Long.TYPE)) {
return "writeLong(" + name + ")";
} else if (type.equals(Float.TYPE)) {
return "writeFloat(" + name + ")";
} else if (type.equals(Double.TYPE)) {
return "writeDouble(" + name + ")";
} else if (type.equals(Long.class) ||
type.equals(String.class) ||
(type.isArray() && type.getComponentType().isPrimitive())) {
return "writeField(" + name + ")";
} else if (isNaiveList(type)) {
return "writeField(" + name + ", ArrayStreamer.INSTANCE)";
} else if (isNaiveMap(type)) {
return "writeField(" + name + ", MapStreamer.INSTANCE)";
} else {
return "writeObject(" + name + ")";
}
}
/** A header to put on all generated source files. */
protected String _header;
/** The path to our ActionScript source files. */
protected File _asroot;
@@ -369,6 +155,4 @@ public class GenActionScriptTask extends GenTask
protected static final String WRITE_SIG =
"public function writeObject (out :ObjectOutputStream) :void";
protected static final Splitter DOT_SPLITTER = Splitter.on('.');
}
@@ -25,6 +25,8 @@ import java.util.List;
import java.util.Map;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.tools.ant.AntClassLoader;
@@ -38,6 +40,7 @@ import org.apache.tools.ant.util.ClasspathUtils;
import com.google.common.collect.Maps;
import com.google.common.collect.Lists;
import com.samskivert.io.StreamUtil;
import com.samskivert.mustache.Mustache;
public abstract class GenTask extends Task
@@ -58,6 +61,20 @@ public abstract class GenTask extends Task
_verbose = verbose;
}
/**
* Configures us with a header file that we'll prepend to all
* generated source files.
*/
public void setHeader (File header)
{
try {
_header = StreamUtil.toString(new FileReader(header));
} catch (IOException ioe) {
System.err.println("Unabled to load header '" + header + ": " +
ioe.getMessage());
}
}
/** Configures our classpath which we'll use to load service classes. */
public void setClasspathref (Reference pathref)
{
@@ -160,4 +177,7 @@ public abstract class GenTask extends Task
/** Used to do our own classpath business. */
protected ClassLoader _cloader;
/** A header to put on all generated source files. */
protected String _header;
}
@@ -31,7 +31,6 @@ import java.util.Arrays;
import java.util.List;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
@@ -40,7 +39,6 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.samskivert.io.StreamUtil;
import com.samskivert.util.Logger;
import com.samskivert.util.StringUtil;
import com.threerings.presents.annotation.TransportHint;
@@ -388,20 +386,6 @@ public abstract class InvocationTask extends GenTask
}
}
/**
* Configures us with a header file that we'll prepend to all
* generated source files.
*/
public void setHeader (File header)
{
try {
_header = StreamUtil.toString(new FileReader(header));
} catch (IOException ioe) {
System.err.println("Unabled to load header '" + header + ": " +
ioe.getMessage());
}
}
@Override
public void execute ()
{
@@ -440,9 +424,6 @@ public abstract class InvocationTask extends GenTask
newstr.replace('/', File.separatorChar));
}
/** A header to put on all generated source files. */
protected String _header;
/** {@link InvocationListener} resolved with the proper classloader so
* that we can compare it to loaded derived classes. */
protected Class<?> _ilistener;