Last night, I read about the Mustache templating library (a derivative of

Google's CTemplate) and got all hot and bothered, because it seemed like it
would be sufficiently powerful to replace Velocity for our Narya templating
needs.

I then sought out a Java implementation and found one, and quickly discovered
that it lacked the right level of awesome for my needs. So I foolishly decided
to write my own, because Mustache seemed simple enough that I could reimplement
it in a few hours.

A few hours later, I had a neat and tidy reimplementation of Mustache and then
set about to converting Narya to using it. Then the fun began.

I discovered that Mustache wants everything in a hash, but sometimes you just
want to iterate over the elements of a list, and print them into the template
whole-hog. So I extended Mustache with the special "this" variable for printing
the whole context instead of pulling values out of it by name.

Then I remembered that Velocity allows you to do a deep dive into objects,
calling methods and calling methods on the return values of those methods.
Velocity even allows you to pass constants as arguments to those methods (true,
strings, integers). Well, I reimplemented the compound keys, so that you can
call foo.bar.baz, but I didn't go so far as to support constant arguments. That
seemed a step too far into complexity land and to be the sort of thing that
Mustache tries to avoid. So with compound keys, I just had to add a few
alternative versions of methods we were already calling, since we only ever
passed true/false as an argument.

Then I realized that Mustache doesn't do any smart trimming of newlines, so if
you have:

{{#stuff}}
blah blah
{{/stuff}}

You get the annoying newline after the open-tag and after the close-tag. So I
modified my implementation to trim newlines in those circumstances, so that
template authors don't have to do a bunch of template-weirding whitespace
jockeying.

Then I discovered that Mustache doesn't support any notion of scope. So when
you're inside a so-called section, the only variables visible are those bound
by that section. The stuff outside the section is totally invisible. Well,
that's not how Velocity works, you can reference things outside your loop
iterations. It seemed no terribly affront to Mustache to make things work that
way as well, so I did that.

Then I discovered a problem with the fact that Mustache implicitly binds the
loop object to the root of the namespace, so if you have {{name}} outside the
loop and then your loop object also contains a {{name}} field, then you can't
see the outside {{name}} anymore because it's shadowed. Tough titties, in this
case, I just changed our code to not shadow the name.

Then I encountered a bunch of uses of $vidx to put a space before all but the
first element of a list. So I added two more special variables -first and -last
which allow you to do just that sort of thing (and more) in a more template
friendly way because you don't need a Turing complete language just to decide
whether or not you need to mind the gap.

Then I encoutered some uses of $vidx directly, where we were using it to assign
constants in invocation service related classes. So I added another special
variable -index which resolves to basically the same thing that $vidx resolves
to (a 1-based counter indicating which element you're on in your list
iteration). I rationalized to myself that if you wanted to automatically number
laundry lists in your templates, having -index would be nice.

Finally, I have Narya's templating stuff producing character-for-character
replicas of what it used to do with Velocity. Well, actually there's one
newline in a place where there didn't used to be one, but I think that newline
makes sense and it was maybe some sort of Velocity bug that caused it not to
exist.

I've tested the gendobj, genservice and genreceiver tasks. I have no tested
whatever uses streamable_as.tmpl, but I'm pretty confident that it will work
exactly as before because I modified hundreds of lines of other templates in
exactly the same ways and they all work just fine.

So the world gets yet another templating library:

http://code.google.com/p/jmustache/


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6218 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2010-10-22 06:42:47 +00:00
parent c61038277c
commit 4b443fe148
23 changed files with 524 additions and 515 deletions
-1
View File
@@ -9,7 +9,6 @@
</accessrules>
</classpathentry>
<classpathentry combineaccessrules="false" exported="true" kind="src" path="/depot"/>
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/commons-lang.jar"/>
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/gwt-user.jar"/>
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/atunit.jar">
<accessrules>
+4 -9
View File
@@ -228,17 +228,12 @@
<include name="com/threerings/util/**"/>
</fileset>
<zipfileset src="${deploy.dir}/lib/samskivert.jar"/>
<zipfileset src="${deploy.dir}/lib/velocity-dev.jar"/>
<zipfileset src="${deploy.dir}/lib/javassist.jar"/>
<zipfileset src="${deploy.dir}/lib/guava.jar"/>
<zipfileset src="${deploy.dir}/lib/commons-collections.jar"/>
<zipfileset src="${deploy.dir}/lib/commons-lang.jar"/>
<rule pattern="com.google.**" result="cgc.@1"/>
<!-- renaming velocity borks their annoying reflective bullshit -->
<!-- <rule pattern="org.apache.velocity.**" result="oav.@1"/> -->
<rule pattern="org.apache.commons.**" result="oac.@1"/>
<zipfileset src="${deploy.dir}/lib/jmustache-1.0.jar"/>
<rule pattern="com.samskivert.**" result="cs.@1"/>
<zipfileset src="${deploy.dir}/lib/javassist.jar"/>
<rule pattern="javassist.**" result="ja.@1"/>
<zipfileset src="${deploy.dir}/lib/guava.jar"/>
<rule pattern="com.google.**" result="cgc.@1"/>
</jarjar>
<antcall target="dist-gwt"/>
</target>
+1 -3
View File
@@ -5,8 +5,6 @@
<include name="aopalliance.jar"/>
<include name="ant.jar"/>
<include name="asc.jar"/>
<include name="commons-collections.jar"/>
<include name="commons-lang.jar"/>
<include name="depot.jar"/>
<include name="ehcache.jar"/>
<include name="flexTasks.jar"/>
@@ -16,12 +14,12 @@
<include name="gwt-utils.jar"/>
<include name="javassist.jar"/>
<include name="jarjar-1.0.jar"/>
<include name="jmustache-1.0.jar"/>
<include name="jsr250-api.jar"/>
<include name="junit4.jar"/>
<include name="retroweaver-all-1.2.2.jar"/>
<include name="samskivert.jar"/>
<include name="swfutils-ooo.jar"/>
<include name="velocity-dev.jar"/>
<include name="aspirin.swc"/>
<include name="asunit.swc"/>
<include name="corelib.swc"/>
@@ -81,7 +81,8 @@ public class LocationMarshaller extends InvocationMarshaller
// from interface LocationService
public void leavePlace (Client arg1)
{
sendRequest(arg1, LEAVE_PLACE, new Object[] {});
sendRequest(arg1, LEAVE_PLACE, new Object[] {
});
}
/** The method id used to dispatch {@link #moveTo} requests. */
@@ -123,6 +123,7 @@ public class GenDObjectTask extends GenTask
// if this field is an array, we need its component types
if (ftype.isArray()) {
Class<?> etype = ftype.getComponentType();
data.put("have_elem", true);
data.put("elemtype", GenUtil.simpleName(etype));
data.put("wrapelem", GenUtil.boxArgument(etype, "value"));
data.put("wrapoelem", GenUtil.boxArgument(etype, "ovalue"));
@@ -24,7 +24,9 @@ package com.threerings.presents.tools;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -32,9 +34,9 @@ import java.io.File;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.samskivert.util.ComparableArrayList;
import com.samskivert.util.StringUtil;
import com.threerings.util.ActionScript;
@@ -64,8 +66,7 @@ public class GenServiceTask extends InvocationTask
{
public Class<?> listener;
public ComparableArrayList<ServiceMethod> methods =
new ComparableArrayList<ServiceMethod>();
public List<ServiceMethod> methods = Lists.newArrayList();
/** Contains all imports required for the parameters of the methods in this listener. */
public ImportSet imports = new ImportSet();
@@ -99,7 +100,7 @@ public class GenServiceTask extends InvocationTask
}
}
}
methods.sort();
Collections.sort(methods);
}
protected void addInterfaces (Class<?> listener, Set<Class<?>> ifaces)
@@ -125,7 +126,7 @@ public class GenServiceTask extends InvocationTask
});
}
public String getName ()
public String getListenerName ()
{
String name = GenUtil.simpleName(listener);
name = name.replace("Listener", "");
@@ -133,10 +134,18 @@ public class GenServiceTask extends InvocationTask
return name.substring(didx+1);
}
public String adapterCtorArgs () {
StringBuilder sb = new StringBuilder();
for (ServiceMethod m : methods) {
sb.append(m.method.getName() + " :Function, ");
}
return sb.toString();
}
// from interface Comparable<ServiceListener>
public int compareTo (ServiceListener other)
{
return getName().compareTo(other.getName());
return getListenerName().compareTo(other.getListenerName());
}
@Override
@@ -377,7 +386,7 @@ public class GenServiceTask extends InvocationTask
ctx.put("imports", imports.toList());
ctx.put("listener", listener);
String aslpath = _asroot + File.separator + mppath +
File.separator + mname + "_" + listener.getName() + "Marshaller.as";
File.separator + mname + "_" + listener.getListenerName() + "Marshaller.as";
writeFile(aslpath, mergeTemplate(AS_LISTENER_MARSHALLER_TMPL, ctx));
}
@@ -460,13 +469,13 @@ public class GenServiceTask extends InvocationTask
ctx.put("imports", imports.toList());
ctx.put("listener", listener);
String aslpath = _asroot + File.separator + sppath +
File.separator + sname + "_" + listener.getName() + "Listener.as";
String aslpath = _asroot + File.separator + sppath + File.separator +
sname + "_" + listener.getListenerName() + "Listener.as";
writeFile(aslpath, mergeTemplate(AS_LISTENER_SERVICE_TMPL, ctx));
if (_aslistenerAdapters.contains(sname)) {
String aslapath = _asroot + File.separator + sppath +
File.separator + sname + "_" + listener.getName() + "ListenerAdapter.as";
String aslapath = _asroot + File.separator + sppath + File.separator +
sname + "_" + listener.getListenerName() + "ListenerAdapter.as";
writeFile(aslapath, mergeTemplate(AS_LISTENER_ADAPTER_SERVICE_TMPL, ctx));
}
}
@@ -600,10 +609,8 @@ public class GenServiceTask extends InvocationTask
public String sname;
public String spackage;
public ImportSet imports = new ImportSet();
public ComparableArrayList<ServiceMethod> methods =
new ComparableArrayList<ServiceMethod>();
public ComparableArrayList<ServiceListener> listeners =
new ComparableArrayList<ServiceListener>();
public List<ServiceMethod> methods = Lists.newArrayList();
public List<ServiceListener> listeners = Lists.newArrayList();
public final boolean skipAS;
public ServiceDescription (Class<?> serviceClass)
@@ -641,8 +648,8 @@ public class GenServiceTask extends InvocationTask
StringUtil.toString(imports));
}
}
listeners.sort();
methods.sort();
Collections.sort(listeners);
Collections.sort(methods);
}
/**
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.io.File;
import java.io.InputStreamReader;
import java.io.StringWriter;
import org.apache.tools.ant.AntClassLoader;
@@ -34,24 +35,14 @@ import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.ClasspathUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import com.google.common.collect.Maps;
import com.google.common.collect.Lists;
import com.samskivert.velocity.VelocityUtil;
import com.samskivert.mustache.Mustache;
public abstract class GenTask extends Task
{
public GenTask ()
{
try {
_velocity = VelocityUtil.createEngine();
} catch (Exception e) {
throw new BuildException("Failure initializing Velocity", e);
}
}
/**
* Adds a nested &lt;fileset&gt; element which enumerates service declaration source files.
*/
@@ -109,7 +100,7 @@ public abstract class GenTask extends Task
protected String mergeTemplate (String template, Object... data)
throws Exception
{
VelocityContext ctx = new VelocityContext();
Map<String, Object> ctx = Maps.newHashMap();
for (int ii = 0; ii < data.length; ii += 2) {
ctx.put((String)data[ii], data[ii+1]);
}
@@ -124,23 +115,8 @@ public abstract class GenTask extends Task
protected String mergeTemplate (String template, Map<String, Object> data)
throws Exception
{
VelocityContext ctx = new VelocityContext();
for (Map.Entry<String, Object> entry : data.entrySet()) {
ctx.put(entry.getKey(), entry.getValue());
}
return mergeTemplate(template, ctx);
}
/**
* A helper function for {@link #mergeTemplate(String, Map<String, Object>)} and friends. Don't
* use this directly as you'll end up depending on Velocity and your code won't build.
*/
protected String mergeTemplate (String template, VelocityContext ctx)
throws Exception
{
StringWriter writer = new StringWriter();
_velocity.mergeTemplate(template, "UTF-8", ctx, writer);
return writer.toString();
return Mustache.compiler().escapeHTML(false).compile(new InputStreamReader(
getClass().getClassLoader().getResourceAsStream(template), "UTF-8")).execute(data);
}
/**
@@ -185,8 +161,4 @@ public abstract class GenTask extends Task
/** Used to do our own classpath business. */
protected ClassLoader _cloader;
/** Used to generate source files from templates. Don't use this directly from derived classes,
* use {@link #mergeTemplate}. */
protected VelocityEngine _velocity;
}
@@ -56,18 +56,14 @@ public abstract class InvocationTask extends GenTask
/** Used to keep track of invocation service method listener arguments. */
public class ListenerArgument
{
public int index;
public Class<?> listener;
public ListenerArgument (int index, Class<?> listener)
{
this.index = index+1;
public ListenerArgument (int index, Class<?> listener) {
this.listener = listener;
_index = index;
}
public String getMarshaller ()
{
public String getMarshaller () {
String name = GenUtil.simpleName(listener);
// handle ye olde special case
if (name.equals("InvocationService.InvocationListener")) {
@@ -77,8 +73,7 @@ public abstract class InvocationTask extends GenTask
return name.replace("Listener", "Marshaller");
}
public String getActionScriptMarshaller ()
{
public String getActionScriptMarshaller () {
// handle ye olde special case
String name = listener.getName();
if (name.endsWith("InvocationService$InvocationListener")) {
@@ -87,13 +82,22 @@ public abstract class InvocationTask extends GenTask
return getMarshaller().replace('.', '_');
}
}
public int getIndex () {
return _index+1;
}
public int getIndexSkipFirst () {
return _index;
}
protected int _index;
}
/** Used to keep track of invocation service methods or listener methods. */
public class ServiceMethod implements Comparable<ServiceMethod>
{
public Method method;
public List<ListenerArgument> listenerArgs = Lists.newArrayList();
/**
@@ -101,8 +105,7 @@ public abstract class InvocationTask extends GenTask
* @param method the method to inspect
* @param imports will be filled with the types required by the method
*/
public ServiceMethod (Method method, ImportSet imports)
{
public ServiceMethod (Method method, ImportSet imports) {
this.method = method;
// if this method has listener arguments, we need to add listener argument info for them
@@ -130,8 +133,220 @@ public abstract class InvocationTask extends GenTask
}
}
protected void addImportsForType (Type type, ImportSet imports)
{
public String getCode () {
return StringUtil.unStudlyName(method.getName()).toUpperCase();
}
public String getSenderMethodName () {
String mname = method.getName();
if (mname.startsWith("received")) {
return "send" + mname.substring("received".length());
} else {
return mname;
}
}
public String getArgListSkipFirst () {
return getArgList(true);
}
public String getArgList () {
return getArgList(false);
}
public String getArgList (boolean skipFirst) {
StringBuilder buf = new StringBuilder();
Type[] ptypes = method.getGenericParameterTypes();
for (int ii = skipFirst ? 1 : 0; ii < ptypes.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
String simpleName = GenUtil.simpleName(ptypes[ii]);
if (method.isVarArgs() && ii == ptypes.length - 1) {
// Switch [] with ... for varargs
buf.append(simpleName.substring(0, simpleName.length() - 2)).append("...");
} else {
buf.append(simpleName);
}
buf.append(" arg").append(skipFirst ? ii : ii+1);
}
return buf.toString();
}
public String getASArgListSkipFirst () {
return getASArgList(true);
}
public String getASArgList () {
return getASArgList(false);
}
public String getASArgList (boolean skipFirst) {
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = skipFirst ? 1 : 0; ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("arg").append(skipFirst ? ii : ii+1).append(" :");
buf.append(GenUtil.simpleASName(args[ii]));
}
return buf.toString();
}
public String getASInvokArgList () {
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = 0; ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("arg").append(ii);
}
return buf.toString();
}
public String getWrappedArgListSkipFirst () {
return getWrappedArgList(true);
}
public String getWrappedArgList () {
return getWrappedArgList(false);
}
public String getWrappedArgList (boolean skipFirst) {
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = (skipFirst ? 1 : 0); ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append(boxArgument(args[ii], ii+1));
}
return buf.toString();
}
public String getASWrappedArgListSkipFirst () {
return getASWrappedArgList(true);
}
public String getASWrappedArgList () {
return getASWrappedArgList(false);
}
public String getASWrappedArgList (boolean skipFirst) {
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = (skipFirst ? 1 : 0); ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
String index = String.valueOf(skipFirst ? ii : (ii+1));
String arg;
if (_ilistener.isAssignableFrom(args[ii])) {
arg = GenUtil.boxASArgument(args[ii], "listener" + index);
} else {
arg = GenUtil.boxASArgument(args[ii], "arg" + index);
}
buf.append(arg);
}
return buf.toString();
}
public boolean hasArgsSkipFirst () {
return (method.getParameterTypes().length > 1);
}
public boolean hasArgs () {
return (method.getParameterTypes().length > 0);
}
public boolean hasParameterizedArgs () {
return Iterables.any(
Arrays.asList(method.getGenericParameterTypes()), new Predicate<Type>() {
public boolean apply (Type type) {
// TODO: might eventually need to handle generic arrays and wildcard types
return (type instanceof ParameterizedType);
}
});
}
public String getUnwrappedArgListAsListeners () {
return getUnwrappedArgList(true);
}
public String getUnwrappedArgList () {
return getUnwrappedArgList(false);
}
public String getUnwrappedArgList (boolean listenerMode) {
StringBuilder buf = new StringBuilder();
Type[] ptypes = method.getGenericParameterTypes();
for (int ii = (listenerMode ? 0 : 1); ii < ptypes.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append(unboxArgument(ptypes[ii], listenerMode ? ii : ii-1, listenerMode));
}
return buf.toString();
}
public String getASUnwrappedArgListAsListeners () {
return getASUnwrappedArgList(true);
}
public String getASUnwrappedArgList () {
return getASUnwrappedArgList(false);
}
public String getASUnwrappedArgList (boolean listenerMode) {
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = (listenerMode ? 0 : 1); ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
String arg;
int argidx = listenerMode ? ii : ii-1;
if (listenerMode && _ilistener.isAssignableFrom(args[ii])) {
arg = "listener" + argidx;
} else {
arg = GenUtil.unboxASArgument(args[ii], "args[" + argidx + "]");
}
buf.append(arg);
}
return buf.toString();
}
public String getTransport () {
TransportHint hint = method.getAnnotation(TransportHint.class);
if (hint == null) {
// inherit hint from interface annotation
hint = method.getDeclaringClass().getAnnotation(TransportHint.class);
}
if (hint == null) {
return "";
}
return ", Transport.getInstance(Transport.Type." +
hint.type().name() + ", " + hint.channel() + ")";
}
// from interface Comparator<ServiceMethod>
public int compareTo (ServiceMethod other) {
return getCode().compareTo(other.getCode());
}
@Override // from Object
public boolean equals (Object other) {
return (other instanceof ServiceMethod) && compareTo((ServiceMethod)other) == 0;
}
@Override // from Object
public int hashCode () {
return getCode().hashCode();
}
protected void addImportsForType (Type type, ImportSet imports) {
if (type instanceof Class<?>) {
imports.add((Class<?>)type);
} else if (type instanceof ParameterizedType) {
@@ -155,171 +370,7 @@ public abstract class InvocationTask extends GenTask
}
}
public String getCode ()
{
return StringUtil.unStudlyName(method.getName()).toUpperCase();
}
public String getSenderMethodName ()
{
String mname = method.getName();
if (mname.startsWith("received")) {
return "send" + mname.substring("received".length());
} else {
return mname;
}
}
public String getArgList (boolean skipFirst)
{
StringBuilder buf = new StringBuilder();
Type[] ptypes = method.getGenericParameterTypes();
for (int ii = skipFirst ? 1 : 0; ii < ptypes.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
String simpleName = GenUtil.simpleName(ptypes[ii]);
if (method.isVarArgs() && ii == ptypes.length - 1) {
// Switch [] with ... for varargs
buf.append(simpleName.substring(0, simpleName.length() - 2)).append("...");
} else {
buf.append(simpleName);
}
buf.append(" arg").append(skipFirst ? ii : ii+1);
}
return buf.toString();
}
public String getASArgList (boolean skipFirst)
{
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = skipFirst ? 1 : 0; ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append("arg").append(skipFirst ? ii : ii+1).append(" :");
buf.append(GenUtil.simpleASName(args[ii]));
}
return buf.toString();
}
public String getWrappedArgList (boolean skipFirst)
{
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = (skipFirst ? 1 : 0); ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append(boxArgument(args[ii], ii+1));
}
return buf.toString();
}
public String getASWrappedArgList (boolean skipFirst)
{
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = (skipFirst ? 1 : 0); ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
String index = String.valueOf(skipFirst ? ii : (ii+1));
String arg;
if (_ilistener.isAssignableFrom(args[ii])) {
arg = GenUtil.boxASArgument(args[ii], "listener" + index);
} else {
arg = GenUtil.boxASArgument(args[ii], "arg" + index);
}
buf.append(arg);
}
return buf.toString();
}
public boolean hasArgs (boolean skipFirst)
{
return (method.getParameterTypes().length > (skipFirst ? 1 : 0));
}
public boolean hasParameterizedArgs ()
{
return Iterables.any(
Arrays.asList(method.getGenericParameterTypes()), new Predicate<Type>() {
public boolean apply (Type type) {
// TODO: might eventually need to handle generic arrays and wildcard types
return (type instanceof ParameterizedType);
}
});
}
public String getUnwrappedArgList (boolean listenerMode)
{
StringBuilder buf = new StringBuilder();
Type[] ptypes = method.getGenericParameterTypes();
for (int ii = (listenerMode ? 0 : 1); ii < ptypes.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append(unboxArgument(ptypes[ii], listenerMode ? ii : ii-1, listenerMode));
}
return buf.toString();
}
public String getASUnwrappedArgList (boolean listenerMode)
{
StringBuilder buf = new StringBuilder();
Class<?>[] args = method.getParameterTypes();
for (int ii = (listenerMode ? 0 : 1); ii < args.length; ii++) {
if (buf.length() > 0) {
buf.append(", ");
}
String arg;
int argidx = listenerMode ? ii : ii-1;
if (listenerMode && _ilistener.isAssignableFrom(args[ii])) {
arg = "listener" + argidx;
} else {
arg = GenUtil.unboxASArgument(args[ii], "args[" + argidx + "]");
}
buf.append(arg);
}
return buf.toString();
}
public String getTransport ()
{
TransportHint hint = method.getAnnotation(TransportHint.class);
if (hint == null) {
// inherit hint from interface annotation
hint = method.getDeclaringClass().getAnnotation(TransportHint.class);
}
if (hint == null) {
return "";
}
return ", Transport.getInstance(Transport.Type." +
hint.type().name() + ", " + hint.channel() + ")";
}
// from interface Comparator<ServiceMethod>
public int compareTo (ServiceMethod other)
{
return getCode().compareTo(other.getCode());
}
@Override // from Object
public boolean equals (Object other)
{
return (other instanceof ServiceMethod) && compareTo((ServiceMethod)other) == 0;
}
@Override // from Object
public int hashCode ()
{
return getCode().hashCode();
}
protected String boxArgument (Class<?> clazz, int index)
{
protected String boxArgument (Class<?> clazz, int index) {
if (_ilistener.isAssignableFrom(clazz)) {
return GenUtil.boxArgument(clazz, "listener" + index);
} else {
@@ -327,8 +378,7 @@ public abstract class InvocationTask extends GenTask
}
}
protected String unboxArgument (Type type, int index, boolean listenerMode)
{
protected String unboxArgument (Type type, int index, boolean listenerMode) {
if (listenerMode && (type instanceof Class<?>) &&
_ilistener.isAssignableFrom((Class<?>)type)) {
return "listener" + index;
@@ -1,28 +1,28 @@
package $package;
package {{package}};
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* Dispatches calls to a {@link ${name}Receiver} instance.
* Dispatches calls to a {@link {{name}}Receiver} instance.
*/
public class ${name}Decoder extends InvocationDecoder
public class {{name}}Decoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "$receiver_code";
public static final String RECEIVER_CODE = "{{receiver_code}}";
#foreach ($m in $methods)
/** The method id used to dispatch {@link ${name}Receiver#$m.method.name}
{{#methods}}
/** The method id used to dispatch {@link {{name}}Receiver#{{method.name}}}
* notifications. */
public static final int $m.code = $velocityCount;
public static final int {{code}} = {{-index}};
#end
{{/methods}}
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public ${name}Decoder (${name}Receiver receiver)
public {{name}}Decoder ({{name}}Receiver receiver)
{
this.receiver = receiver;
}
@@ -37,14 +37,14 @@ public class ${name}Decoder extends InvocationDecoder
public void dispatchNotification (int methodId, Object[] args)
{
switch (methodId) {
#foreach ($m in $methods)
case $m.code:
((${name}Receiver)receiver).${m.method.name}(
$m.getUnwrappedArgList(true)
{{#methods}}
case {{code}}:
(({{name}}Receiver)receiver).{{method.name}}(
{{getUnwrappedArgListAsListeners}}
);
return;
#end
{{/methods}}
default:
super.dispatchNotification(methodId, args);
return;
@@ -1,30 +1,30 @@
package $package;
package {{package}};
import javax.annotation.Generated;
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* Dispatches requests to the {@link ${name}Provider}.
* Dispatches requests to the {@link {{name}}Provider}.
*/
${generated}
public class ${name}Dispatcher extends InvocationDispatcher<${name}Marshaller>
{{generated}}
public class {{name}}Dispatcher extends InvocationDispatcher<{{name}}Marshaller>
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public ${name}Dispatcher (${name}Provider provider)
public {{name}}Dispatcher ({{name}}Provider provider)
{
this.provider = provider;
}
@Override
public ${name}Marshaller createMarshaller ()
public {{name}}Marshaller createMarshaller ()
{
return new ${name}Marshaller();
return new {{name}}Marshaller();
}
@Override
@@ -33,14 +33,14 @@ public class ${name}Dispatcher extends InvocationDispatcher<${name}Marshaller>
throws InvocationException
{
switch (methodId) {
#foreach ($m in $methods)
case ${name}Marshaller.$m.code:
((${name}Provider)provider).${m.method.name}(
source#if ($m.hasArgs(true)), #end$m.getUnwrappedArgList(false)
{{#methods}}
case {{name}}Marshaller.{{code}}:
(({{name}}Provider)provider).{{method.name}}(
source{{#hasArgsSkipFirst}}, {{/hasArgsSkipFirst}}{{getUnwrappedArgList}}
);
return;
#end
{{/methods}}
default:
super.dispatchRequest(source, methodId, args);
return;
@@ -1,36 +1,34 @@
/**
* Requests that the <code>$field</code> field be set to the
* Requests that the <code>{{field}}</code> field be set to the
* specified value. The local value will be updated immediately and an
* event will be propagated through the system to notify all listeners
* that the attribute did change. Proxied copies of this object (on
* clients) will apply the value change when they received the
* attribute changed notification.
*/
${generated}
public void set$upfield ($type value)
{{generated}}
public void set{{upfield}} ({{type}} value)
{
$type ovalue = this.$field;
{{type}} ovalue = this.{{field}};
requestAttributeChange(
$capfield, $wrapfield, $wrapofield$transport);
this.$field = $clonefield;
}
#if ($elemtype)
{{capfield}}, {{wrapfield}}, {{wrapofield}}{{transport}});
this.{{field}} = {{clonefield}};
}{{#have_elem}}
/**
* Requests that the <code>index</code>th element of
* <code>$field</code> field be set to the specified value.
* <code>{{field}}</code> field be set to the specified value.
* The local value will be updated immediately and an event will be
* propagated through the system to notify all listeners that the
* attribute did change. Proxied copies of this object (on clients)
* will apply the value change when they received the attribute
* changed notification.
*/
${generated}
public void set${upfield}At ($elemtype value, int index)
{{generated}}
public void set{{upfield}}At ({{elemtype}} value, int index)
{
$elemtype ovalue = this.$field[index];
{{elemtype}} ovalue = this.{{field}}[index];
requestElementUpdate(
$capfield, index, $wrapelem, $wrapoelem$transport);
this.$field[index] = value;
}
#end
{{capfield}}, index, {{wrapelem}}, {{wrapoelem}}{{transport}});
this.{{field}}[index] = value;
}{{/have_elem}}
@@ -1,3 +1,3 @@
/** The field name of the <code>$field</code> field. */
${generated}
public static final String $capfield = "$field";
/** The field name of the <code>{{field}}</code> field. */
{{generated}}
public static final String {{capfield}} = "{{field}}";
@@ -1,21 +1,21 @@
/**
* Requests that <code>oid</code> be added to the <code>$field</code>
* Requests that <code>oid</code> be added to the <code>{{field}}</code>
* oid list. The list will not change until the event is actually
* propagated through the system.
*/
${generated}
public void addTo$upfield (int oid)
{{generated}}
public void addTo{{upfield}} (int oid)
{
requestOidAdd($capfield, oid);
requestOidAdd({{capfield}}, oid);
}
/**
* Requests that <code>oid</code> be removed from the
* <code>$field</code> oid list. The list will not change until the
* <code>{{field}}</code> oid list. The list will not change until the
* event is actually propagated through the system.
*/
${generated}
public void removeFrom$upfield (int oid)
{{generated}}
public void removeFrom{{upfield}} (int oid)
{
requestOidRemove($capfield, oid);
requestOidRemove({{capfield}}, oid);
}
@@ -1,38 +1,38 @@
/**
* Requests that the specified entry be added to the
* <code>$field</code> set. The set will not change until the event is
* <code>{{field}}</code> set. The set will not change until the event is
* actually propagated through the system.
*/
${generated}
public void addTo$upfield ($etype elem)
{{generated}}
public void addTo{{upfield}} ({{etype}} elem)
{
requestEntryAdd($capfield, $field, elem);
requestEntryAdd({{capfield}}, {{field}}, elem);
}
/**
* Requests that the entry matching the supplied key be removed from
* the <code>$field</code> set. The set will not change until the
* the <code>{{field}}</code> set. The set will not change until the
* event is actually propagated through the system.
*/
${generated}
public void removeFrom$upfield (Comparable<?> key)
{{generated}}
public void removeFrom{{upfield}} (Comparable<?> key)
{
requestEntryRemove($capfield, $field, key);
requestEntryRemove({{capfield}}, {{field}}, key);
}
/**
* Requests that the specified entry be updated in the
* <code>$field</code> set. The set will not change until the event is
* <code>{{field}}</code> set. The set will not change until the event is
* actually propagated through the system.
*/
${generated}
public void update$upfield ($etype elem)
{{generated}}
public void update{{upfield}} ({{etype}} elem)
{
requestEntryUpdate($capfield, $field, elem$transport);
requestEntryUpdate({{capfield}}, {{field}}, elem{{transport}});
}
/**
* Requests that the <code>$field</code> field be set to the
* Requests that the <code>{{field}}</code> field be set to the
* specified value. Generally one only adds, updates and removes
* entries of a distributed set, but certain situations call for a
* complete replacement of the set value. The local value will be
@@ -41,10 +41,10 @@
* change. Proxied copies of this object (on clients) will apply the
* value change when they received the attribute changed notification.
*/
${generated}
public void set$upfield ($type value)
{{generated}}
public void set{{upfield}} ({{type}} value)
{
requestAttributeChange($capfield, value, this.$field);
$type clone = $clonefield;
this.$field = clone;
requestAttributeChange({{capfield}}, value, this.{{field}});
{{type}} clone = {{clonefield}};
this.{{field}} = clone;
}
@@ -1,58 +1,58 @@
package $package;
package {{package}};
import javax.annotation.Generated;
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* Provides the implementation of the {@link ${name}Service} interface
* Provides the implementation of the {@link {{name}}Service} interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
${generated}
public class ${name}Marshaller extends InvocationMarshaller
implements ${name}Service
{{generated}}
public class {{name}}Marshaller extends InvocationMarshaller
implements {{name}}Service
{
#foreach ($l in $listeners)
{{#listeners}}
/**
* Marshalls results to implementations of {@link ${name}Service.${l.name}Listener}.
* Marshalls results to implementations of {@link {{name}}Service.{{listenerName}}Listener}.
*/
public static class ${l.name}Marshaller extends ListenerMarshaller
implements ${l.name}Listener
public static class {{listenerName}}Marshaller extends ListenerMarshaller
implements {{listenerName}}Listener
{
#foreach ($lm in $l.methods)
/** The method id used to dispatch {@link #$lm.method.name}
{{#methods}}
/** The method id used to dispatch {@link #{{method.name}}}
* responses. */
public static final int $lm.code = $velocityCount;
public static final int {{code}} = {{-index}};
// from interface ${l.name}Marshaller
public void $lm.method.name ($lm.getArgList(false))
// from interface {{listenerName}}Marshaller
public void {{method.name}} ({{getArgList}})
{
_invId = null;
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, $lm.code,
new Object[] { $lm.getWrappedArgList(false) }, transport));
callerOid, requestId, {{code}},
new Object[] { {{getWrappedArgList}} }, transport));
}
#end
{{/methods}}
@Override // from InvocationMarshaller
#if ($l.hasParameterizedMethodArgs())
{{#hasParameterizedMethodArgs}}
@SuppressWarnings("unchecked")
#end
{{/hasParameterizedMethodArgs}}
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
#foreach ($lm in $l.methods)
case $lm.code:
((${l.name}Listener)listener).${lm.method.name}(
${lm.getUnwrappedArgList(true)});
{{#methods}}
case {{code}}:
(({{listenerName}}Listener)listener).{{method.name}}(
{{getUnwrappedArgListAsListeners}});
return;
#end
{{/methods}}
default:
super.dispatchResponse(methodId, args);
return;
@@ -60,25 +60,26 @@ public class ${name}Marshaller extends InvocationMarshaller
}
}
#end
#foreach ($m in $methods)
#if ($velocityCount > 1)
{{/listeners}}
{{#methods}}
{{^-first}}
#end
/** The method id used to dispatch {@link #$m.method.name} requests. */
public static final int $m.code = $velocityCount;
{{/-first}}
/** The method id used to dispatch {@link #{{method.name}}} requests. */
public static final int {{code}} = {{-index}};
// from interface ${name}Service
public void $m.method.name ($m.getArgList(false))
// from interface {{name}}Service
public void {{method.name}} ({{getArgList}})
{
#foreach ($la in $m.listenerArgs)
$la.marshaller listener$la.index = new ${la.marshaller}();
listener${la.index}.listener = arg$la.index;
#end
sendRequest(arg1, $m.code, new Object[] {#if($m.hasArgs(true))
$m.getWrappedArgList(true)
#end}$m.transport);
{{#listenerArgs}}
{{marshaller}} listener{{index}} = new {{marshaller}}();
listener{{index}}.listener = arg{{index}};
{{/listenerArgs}}
sendRequest(arg1, {{code}}, new Object[] {
{{#hasArgsSkipFirst}}
{{getWrappedArgListSkipFirst}}
{{/hasArgsSkipFirst}}
}{{transport}});
}
#end
{{/methods}}
}
@@ -1,38 +1,37 @@
package $package {
package {{package}} {
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* Provides the implementation of the <code>${name}Service</code> interface
* Provides the implementation of the <code>{{name}}Service</code> interface
* that marshalls the arguments and delivers the request to the provider
* on the server. Also provides an implementation of the response listener
* interfaces that marshall the response arguments and deliver them back
* to the requesting client.
*/
public class ${name}Marshaller extends InvocationMarshaller
implements ${name}Service
public class {{name}}Marshaller extends InvocationMarshaller
implements {{name}}Service
{
#foreach ($m in $methods)
#if ($velocityCount > 1)
{{#methods}}
{{^-first}}
#end
/** The method id used to dispatch <code>$m.method.name</code> requests. */
public static const $m.code :int = $velocityCount;
{{/-first}}
/** The method id used to dispatch <code>{{method.name}}</code> requests. */
public static const {{code}} :int = {{-index}};
// from interface ${name}Service
public function $m.method.name ($m.getASArgList(true)) :void
// from interface {{name}}Service
public function {{method.name}} ({{getASArgListSkipFirst}}) :void
{
#foreach ($la in $m.listenerArgs)
#set ($argIdx = $la.index - 1)
var listener$argIdx :$la.actionScriptMarshaller = new ${la.actionScriptMarshaller}();
listener${argIdx}.listener = arg$argIdx;
#end
sendRequest($m.code, [
$m.getASWrappedArgList(true)
{{#listenerArgs}}
var listener{{indexSkipFirst}} :{{actionScriptMarshaller}} = new {{actionScriptMarshaller}}();
listener{{indexSkipFirst}}.listener = arg{{indexSkipFirst}};
{{/listenerArgs}}
sendRequest({{code}}, [
{{getASWrappedArgListSkipFirst}}
]);
}
#end
{{/methods}}
}
}
@@ -1,31 +1,31 @@
package $package {
package {{package}} {
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* Marshalls instances of the ${name}Service_${listener.name}Marshaller interface.
* Marshalls instances of the {{name}}Service_{{listener.listenerName}}Marshaller interface.
*/
public class ${name}Marshaller_${listener.name}Marshaller
public class {{name}}Marshaller_{{listener.listenerName}}Marshaller
extends InvocationMarshaller_ListenerMarshaller
{
#foreach ($lm in $listener.methods)
/** The method id used to dispatch <code>$lm.method.name</code> responses. */
public static const $lm.code :int = $velocityCount;
{{#listener.methods}}
/** The method id used to dispatch <code>{{method.name}}</code> responses. */
public static const {{code}} :int = {{-index}};
#end
{{/listener.methods}}
// from InvocationMarshaller_ListenerMarshaller
override public function dispatchResponse (methodId :int, args :Array) :void
{
switch (methodId) {
#foreach ($lm in $listener.methods)
case $lm.code:
(listener as ${name}Service_${listener.name}Listener).${lm.method.name}(
${lm.getASUnwrappedArgList(true)});
{{#listener.methods}}
case {{code}}:
(listener as {{name}}Service_{{listener.listenerName}}Listener).{{method.name}}(
{{getASUnwrappedArgListAsListeners}});
return;
#end
{{/listener.methods}}
default:
super.dispatchResponse(methodId, args);
return;
@@ -1,26 +1,26 @@
package $package;
package {{package}};
import javax.annotation.Generated;
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* Defines the server-side of the {@link ${name}Service}.
* Defines the server-side of the {@link {{name}}Service}.
*/
${generated}
public interface ${name}Provider extends InvocationProvider
{{generated}}
public interface {{name}}Provider extends InvocationProvider
{
#foreach ($m in $methods)
#if ($velocityCount > 1)
{{#methods}}
{{^-first}}
#end
{{/-first}}
/**
* Handles a {@link ${name}Service#$m.method.name} request.
* Handles a {@link {{name}}Service#{{method.name}}} request.
*/
void $m.method.name (ClientObject caller#if ($m.hasArgs(true)), #end$m.getArgList(true))#if ($m.listenerArgs.size() > 0)
void {{method.name}} (ClientObject caller{{#hasArgsSkipFirst}}, {{/hasArgsSkipFirst}}{{getArgListSkipFirst}}){{^listenerArgs.isEmpty}}
throws InvocationException#end;
#end
throws InvocationException{{/listenerArgs.isEmpty}};
{{/methods}}
}
@@ -1,27 +1,27 @@
package $package;
package {{package}};
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* Used to issue notifications to a {@link ${name}Receiver} instance on a
* Used to issue notifications to a {@link {{name}}Receiver} instance on a
* client.
*/
public class ${name}Sender extends InvocationSender
public class {{name}}Sender extends InvocationSender
{
#foreach ($m in $methods)
{{#methods}}
/**
* Issues a notification that will result in a call to {@link
* ${name}Receiver#$m.method.name} on a client.
* {{name}}Receiver#{{method.name}}} on a client.
*/
public static void $m.senderMethodName (
ClientObject target#if ($m.hasArgs(false)), #end$m.getArgList(false))
public static void {{senderMethodName}} (
ClientObject target{{#hasArgs}}, {{/hasArgs}}{{getArgList}})
{
sendNotification(
target, ${name}Decoder.RECEIVER_CODE, ${name}Decoder.$m.code,
new Object[] { $m.getWrappedArgList(false) }$m.transport);
target, {{name}}Decoder.RECEIVER_CODE, {{name}}Decoder.{{code}},
new Object[] { {{getWrappedArgList}} }{{transport}});
}
#end
{{/methods}}
}
@@ -1,20 +1,20 @@
package $package {
package {{package}} {
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* An ActionScript version of the Java ${name}Service interface.
* An ActionScript version of the Java {{name}}Service interface.
*/
public interface ${name}Service extends InvocationService
public interface {{name}}Service extends InvocationService
{
#foreach ($m in $methods)
#if ($velocityCount > 1)
{{#methods}}
{{^-first}}
#end
// from Java interface ${name}Service
function $m.method.name ($m.getASArgList(true)) :void;
#end
{{/-first}}
// from Java interface {{name}}Service
function {{method.name}} ({{getASArgListSkipFirst}}) :void;
{{/methods}}
}
}
@@ -1,48 +1,37 @@
package $package {
package {{package}} {
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* A functional adapter for the ${name}Service_${listener.name}Listener interface.
* A functional adapter for the {{name}}Service_{{listener.listenerName}}Listener interface.
*/
public class ${name}Service_${listener.name}ListenerAdapter
implements ${name}Service_${listener.name}Listener
public class {{name}}Service_{{listener.listenerName}}ListenerAdapter
implements {{name}}Service_{{listener.listenerName}}Listener
{
#set ($cparams = "")
#foreach ($lm in $listener.methods)
#set ($cparams = $cparams + "$lm.method.name :Function, ")
#end
/**
* Creates a new $name service $listener.name listener that will delegate to the given
* function(s). Any Function that is null will simply not be called.
* Creates a new {{name}} service {{listener.listenerName}} listener that will delegate to the
* given function(s). Any Function that is null will simply not be called.
*/
public function ${name}Service_${listener.name}ListenerAdapter (
${cparams}failed :Function)
public function {{name}}Service_{{listener.listenerName}}ListenerAdapter (
{{adapterCtorArgs}}failed :Function)
{
#foreach ($lm in $listener.methods)
_$lm.method.name = $lm.method.name;
#end
{{#listener.methods}}
_{{method.name}} = {{method.name}};
{{/listener.methods}}
_failed = failed;
}
#foreach ($lm in $listener.methods)
{{#listener.methods}}
// from Java ${name}Service_${listener.name}Listener
public function $lm.method.name ($lm.getASArgList(false)) :void
// from Java {{name}}Service_{{listener.listenerName}}Listener
public function {{method.name}} ({{getASArgList}}) :void
{
#set ($alist = "")
#foreach ($type in $lm.method.getParameterTypes())
#if ($velocityCount > 1)
#set ($alist = $alist + ", ")
#end
#set ($alist = $alist + "arg$velocityCount")
#end
if (_$lm.method.name != null) {
_${lm.method.name}($alist);
if (_{{method.name}} != null) {
_{{method.name}}({{getASInvokeArgList}});
}
}
#end
{{/listener.methods}}
// from InvocationService_InvocationListener
public function requestFailed (cause :String) :void
@@ -52,9 +41,9 @@ public class ${name}Service_${listener.name}ListenerAdapter
}
}
#foreach ($lm in $listener.methods)
protected var _$lm.method.name :Function;
#end
{{#listener.methods}}
protected var _{{method.name}} :Function;
{{/listener.methods}}
protected var _failed :Function;
}
}
@@ -1,21 +1,21 @@
package $package {
package {{package}} {
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
/**
* An ActionScript version of the Java ${name}Service_${listener.name}Listener interface.
* An ActionScript version of the Java {{name}}Service_{{listener.listenerName}}Listener interface.
*/
public interface ${name}Service_${listener.name}Listener
public interface {{name}}Service_{{listener.listenerName}}Listener
extends InvocationService_InvocationListener
{
#foreach ($lm in $listener.methods)
#if ($velocityCount > 1)
{{#listener.methods}}
{{^-first}}
#end
// from Java ${name}Service_${listener.name}Listener
function $lm.method.name ($lm.getASArgList(false)) :void
#end
{{/-first}}
// from Java {{name}}Service_{{listener.listenerName}}Listener
function {{method.name}} ({{getASArgList}}) :void
{{/listener.methods}}
}
}
@@ -1,44 +1,43 @@
// GENERATED PREAMBLE START
${header}
package ${package} {
{{header}}
package {{package}} {
#foreach ($import in $imports)
import $import;
#end
{{#imports}}
import {{this}};
{{/imports}}
// GENERATED PREAMBLE END
// GENERATED CLASSDECL START
public class ${classname} #if (!${extends.isEmpty()})extends $extends
#end
#if (!${implements.isEmpty()})
#if (!${extends.isEmpty()}) #{end}implements $implements
#end
public class {{classname}} {{^extends.isEmpty}}extends {{#extends}}
{{/extends.isEmpty}}
{{^implements.isEmpty}}{{^extends.isEmpty}} {{/extends.isEmpty}}implements {{#implements}}
{{/implements.isEmpty}}
{
// GENERATED CLASSDECL END
// GENERATED STREAMING START
#foreach ($field in $fields)
public var $field.name :${field.simpleType};
#end
{{#fields}}
public var {{name}} :{{simpleType}};
{{/fields}}
#if ($superclassStreamable)override #{end}public function readObject (ins :ObjectInputStream) :void
{{#superclassStreamable}}override {{/superclassStreamable}}public function readObject (ins :ObjectInputStream) :void
{
#if ($superclassStreamable)
{{#superclassStreamable}}
super.readObject(ins);
#end
#foreach ($field in $fields)
$field.name = ins.${field.reader};
#end
{{/superclassStreamable}}
{{#fields}}
{{name}} = ins.{{reader}};
{{/fields}}
}
#if ($superclassStreamable)override #{end}public function writeObject (out :ObjectOutputStream) :void
{{#superclassStreamable}}override {{/superclassStreamable}}public function writeObject (out :ObjectOutputStream) :void
{
#if ($superclassStreamable)
{{#superclassStreamable}}
super.writeObject(out);
#end
#foreach ($field in $fields)
out.${field.writer};
#end
{{/superclassStreamable}}
{{#fields}}
out.{{writer}};
{{/fields}}
}
// GENERATED STREAMING END