Now we have a cool @ActionScript annotation that allows us to specify different

field/method names for the ActionScript version of a class (or to omit
something entirely). This removes the need for special case hackery for
toStringBuilder().

In order for annotations to work, however, we have to require that the
GenActionScriptTask be loaded from the same classloader that loads the classes
to be reflected upon. Before we only reflected on the target classes, never
instantiated them. Annotations are actually instantiated, so we have to be able
to create an instance of the ActionScript.class that is compiled into our
target classes and assign it to a reference that is compiled into
GenActionScriptTask. Beware the complexities of dealing with multiple class
loaders.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4405 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2006-10-04 23:35:41 +00:00
parent ed51df0807
commit 4a9fde7830
5 changed files with 100 additions and 70 deletions
@@ -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*) \\(.*");
}
@@ -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 =