diff --git a/src/java/com/threerings/crowd/data/PlaceConfig.java b/src/java/com/threerings/crowd/data/PlaceConfig.java index f06e131e7..6bc97b6d1 100644 --- a/src/java/com/threerings/crowd/data/PlaceConfig.java +++ b/src/java/com/threerings/crowd/data/PlaceConfig.java @@ -24,6 +24,7 @@ package com.threerings.crowd.data; import com.samskivert.util.StringUtil; import com.threerings.io.SimpleStreamableObject; +import com.threerings.util.ActionScript; import com.threerings.crowd.Log; import com.threerings.crowd.client.PlaceController; @@ -89,6 +90,7 @@ public abstract class PlaceConfig extends SimpleStreamableObject public abstract String getManagerClassName (); // documentation inherited + @ActionScript(name="toStringBuilder") protected void toString (StringBuilder buf) { buf.append("type=").append(StringUtil.shortClassName(this)); diff --git a/src/java/com/threerings/io/SimpleStreamableObject.java b/src/java/com/threerings/io/SimpleStreamableObject.java index 2ddbc0396..29c3f6874 100644 --- a/src/java/com/threerings/io/SimpleStreamableObject.java +++ b/src/java/com/threerings/io/SimpleStreamableObject.java @@ -22,6 +22,7 @@ package com.threerings.io; import com.samskivert.util.StringUtil; +import com.threerings.util.ActionScript; /** * A simple serializable object implements the {@link Streamable} @@ -44,6 +45,7 @@ public class SimpleStreamableObject implements Streamable * Handles the toString-ification of all public members. Derived * classes can override and include non-public members if desired. */ + @ActionScript(name="toStringBuilder") protected void toString (StringBuilder buf) { StringUtil.fieldsToString(buf, this); diff --git a/src/java/com/threerings/presents/tools/ActionScriptSource.java b/src/java/com/threerings/presents/tools/ActionScriptSource.java index dd5e21123..051c1f9ff 100644 --- a/src/java/com/threerings/presents/tools/ActionScriptSource.java +++ b/src/java/com/threerings/presents/tools/ActionScriptSource.java @@ -37,6 +37,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import com.samskivert.util.StringUtil; +import com.threerings.util.ActionScript; /** * Primitively parses an ActionScriptSource file, allows the addition and @@ -135,7 +136,8 @@ public class ActionScriptSource * Creates and returns a declaration for a field equivalent to the supplied * Java field in ActionScript. */ - public static String createActionScriptDeclaration (Field field) + public static String createActionScriptDeclaration ( + String name, Field field) { int mods = field.getModifiers(); StringBuilder builder = new StringBuilder(); @@ -158,7 +160,7 @@ public class ActionScriptSource builder.append(Modifier.isFinal(mods) ? "const " : "var "); // next comes the name - builder.append(field.getName()).append(" :"); + builder.append(name).append(" :"); // now convert the type to an ActionScript type builder.append(toActionScriptType(field.getType(), @@ -207,7 +209,8 @@ public class ActionScriptSource return builder.toString(); } - public static String createActionScriptDeclaration (Method method) + public static String createActionScriptDeclaration ( + String name, Method method) { int mods = method.getModifiers(); StringBuilder builder = new StringBuilder(); @@ -230,7 +233,7 @@ public class ActionScriptSource builder.append("function "); // next comes the name - builder.append(getName(method)).append(" ("); + builder.append(name).append(" ("); // now the parameters int idx = 0; @@ -271,23 +274,6 @@ public class ActionScriptSource return toSimpleName(tname); } - protected static String getName (Method method) - { - // yay for the hackery; we can't overload methods in ActionScript so we - // do some crazy conversions based on signature to handle some of our - // common patterns - String name = method.getName(); - Class[] ptypes = method.getParameterTypes(); - - // toString(StringBuilder) -> toStringBuilder(StringBuilder) - if (name.equals("toString") && ptypes.length == 1 && - ptypes[0].getName().equals("java.lang.StringBuilder")) { - return "toStringBuilder"; - } - - return name; - } - public ActionScriptSource (Class jclass) { packageName = jclass.getPackage().getName(); @@ -319,8 +305,18 @@ public class ActionScriptSource } else { list = Modifier.isPublic(mods) ? publicFields : protectedFields; } - list.add(new Member(field.getName(), - createActionScriptDeclaration(field))); + String name = field.getName(); + ActionScript asa = field.getAnnotation(ActionScript.class); + if (asa != null) { + if (asa.omit()) { + continue; + } + if (!StringUtil.isBlank(asa.name())) { + name = asa.name(); + } + } + String decl = createActionScriptDeclaration(name, field); + list.add(new Member(name, decl)); } // ActionScript only supports one constructor so we find the one with @@ -329,6 +325,10 @@ public class ActionScriptSource Constructor mainctor = null; boolean needsNoArg = false; for (Constructor ctor : jclass.getConstructors()) { + ActionScript asa = ctor.getAnnotation(ActionScript.class); + if (asa != null && asa.omit()) { + continue; + } int params = ctor.getParameterTypes().length; if (mainctor == null || params > mainctor.getParameterTypes().length) { @@ -359,10 +359,20 @@ public class ActionScriptSource publicMethods : protectedMethods; } + String name = method.getName(); + ActionScript asa = method.getAnnotation(ActionScript.class); + if (asa != null) { + if (asa.omit()) { + continue; + } + if (!StringUtil.isBlank(asa.name())) { + name = asa.name(); + } + } + // see if we already have a member for this method name - String name = getName(method); - String decl = createActionScriptDeclaration(method); Member mem = getMember(list, name); + String decl = createActionScriptDeclaration(name, method); if (mem == null) { mem = new Member(name, decl); mem.body = " {\n TODO: IMPLEMENT ME\n }\n"; @@ -944,11 +954,13 @@ public class ActionScriptSource protected static Pattern ASFIELD = Pattern.compile( "\\s+(?:public|protected|private)" + - "(?:\\s+var|\\s+static\\s+const)?" + + "(?:\\s+static)?(?:\\s+var|\\s+const)?" + "\\s+([_a-zA-Z]\\w*)" + // variable name "\\s+(?::[a-zA-Z\\[\\]<>,]+)" + // type "(\\s+=.*|;)"); protected static Pattern ASFUNCTION = Pattern.compile( - ".*(?:public|protected) function ([a-zA-Z]\\w*) \\(.*"); + ".*(?:public|protected)" + + "(?:\\s+/\\*\\s*abstract\\s*\\*/)?" + + "\\s+function\\s+([a-zA-Z]\\w*) \\(.*"); } diff --git a/src/java/com/threerings/presents/tools/GenActionScriptTask.java b/src/java/com/threerings/presents/tools/GenActionScriptTask.java index 9040d370a..1629bede1 100644 --- a/src/java/com/threerings/presents/tools/GenActionScriptTask.java +++ b/src/java/com/threerings/presents/tools/GenActionScriptTask.java @@ -93,40 +93,17 @@ public class GenActionScriptTask extends Task } } - /** - * Configures the classpath that we'll use to load classes. - */ - public void setClasspathref (Reference pathref) - { - _cloader = ClasspathUtils.getClassLoaderForPath(getProject(), pathref); - } - /** * Performs the actual work of the task. */ public void execute () throws BuildException { - if (_cloader == null) { - String errmsg = "This task requires a 'classpathref' attribute " + - "to be set to the project's classpath."; - throw new BuildException(errmsg); - } - try { _velocity = VelocityUtil.createEngine(); } catch (Exception e) { throw new BuildException("Failure initializing Velocity", e); } - // resolve the Streamable class using our classloader - try { - _sclass = _cloader.loadClass(Streamable.class.getName()); - _doclass = _cloader.loadClass(DObject.class.getName()); - _imclass = _cloader.loadClass(InvocationMarshaller.class.getName()); - } catch (Exception e) { - throw new BuildException("Can't resolve Streamable", e); - } - for (FileSet fs : _filesets) { DirectoryScanner ds = fs.getDirectoryScanner(getProject()); File fromDir = fs.getDir(getProject()); @@ -155,7 +132,11 @@ public class GenActionScriptTask extends Task } try { - processClass(source, _cloader.loadClass(name)); + // 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 + ".\n" + @@ -176,9 +157,9 @@ public class GenActionScriptTask extends Task { // make sure we implement Streamable but don't extend DObject or // InvocationMarshaller and that we're a class not an interface - if (!_sclass.isAssignableFrom(sclass) || - _doclass.isAssignableFrom(sclass) || - _imclass.isAssignableFrom(sclass) || + if (!Streamable.class.isAssignableFrom(sclass) || + DObject.class.isAssignableFrom(sclass) || + InvocationMarshaller.class.isAssignableFrom(sclass) || ((sclass.getModifiers() & Modifier.INTERFACE) != 0)) { // System.err.println("Skipping " + sclass.getName() + "..."); return; @@ -273,7 +254,7 @@ public class GenActionScriptTask extends Task protected boolean isStreamable (Class clazz) { for (Class iface : clazz.getInterfaces()) { - if (_sclass.equals(iface)) { + if (Streamable.class.equals(iface)) { return true; } } @@ -349,24 +330,9 @@ public class GenActionScriptTask extends Task /** The path to our ActionScript source files. */ protected File _asroot; - /** Used to do our own classpath business. */ - protected ClassLoader _cloader; - /** Used to generate source files from templates. */ protected VelocityEngine _velocity; - /** {@link Streamable} resolved with the proper classloader so that we can - * compare it to loaded derived classes. */ - protected Class _sclass; - - /** {@link DObject} resolved with the proper classloader so that we - * can compare it to loaded derived classes. */ - protected Class _doclass; - - /** {@link InvocationMarshaller} resolved with the proper classloader so - * that we can compare it to loaded derived classes. */ - protected Class _imclass; - protected static final String READ_SIG = "public function readObject (ins :ObjectInputStream) :void"; protected static final String WRITE_SIG = diff --git a/src/java/com/threerings/util/ActionScript.java b/src/java/com/threerings/util/ActionScript.java new file mode 100644 index 000000000..c0e8a0673 --- /dev/null +++ b/src/java/com/threerings/util/ActionScript.java @@ -0,0 +1,48 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2006 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.util; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +/** + * An annotation that controls ActionScript code generation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, + ElementType.METHOD, ElementType.TYPE}) +public @interface ActionScript +{ + /** + * Indicates whether this field, method or class should be omitted from the + * ActionScript translation. + */ + boolean omit () default false; + + /** + * Indicates a custom name to be used for the ActionScript version of this + * field, method or class. + */ + String name () default ""; +}