Files
narya/src/main/java/com/threerings/presents/tools/ActionScriptSource.java
T
Michael Bayne 9d2ca42eac Convert Narya (most of the way) over to a Maven Ant task based build. The
ActionScript bits remain belligerent, but the Java stuff is mostly shipshape.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6222 542714f4-19e9-0310-aa3c-eee0fc999fb1
2010-10-22 21:12:29 +00:00

1086 lines
39 KiB
Java

//
// $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.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.samskivert.util.StringUtil;
import com.threerings.util.ActionScript;
/**
* Primitively parses an ActionScriptSource file, allows the addition and replacement of class
* members and handles writing out the file again.
*/
public class ActionScriptSource
{
/**
* Contains the name and text of a particular member declaration.
*/
public static class Member
{
public String name;
public String comment = "";
public String definition;
public String body = "";
public boolean noreplace = false;
public Member (String name, String definition) {
this.name = name;
this.definition = definition;
}
public void setInitialValue (String initValue) {
// if we're a typed array and we're being set to "new Something[0]" then convert that
if (definition.indexOf("TypedArray") != -1) {
Matcher m = ARRAYINIT.matcher(initValue);
if (m.matches()) {
initValue = "TypedArray.create(" + m.group(1) + ")";
}
}
// do not-very-smart array conversion
initValue = initValue.replaceAll("\\{", "[");
initValue = initValue.replaceAll("\\}", "]");
// stick the initial value before the semicolon
definition = definition.substring(0, definition.length()-1) + " = " + initValue + ";";
}
public void setComment (String newComment) {
if (newComment.indexOf("@Override") != -1) {
newComment = newComment.replaceAll("@Override ?", "");
// handle // @Override // comment
newComment = newComment.replaceFirst("// //", "//");
definition = "override " + definition;
}
// trim blank lines from start
while (newComment.startsWith("\n")) {
newComment = newComment.substring(1);
}
comment = StringUtil.isBlank(newComment) ? "" : newComment;
}
public void write (PrintWriter writer) {
writer.print(comment);
writer.println(" " + definition);
writer.print(body);
}
}
public String preamble = "";
public String packageName;
public Set<String> imports = Sets.newHashSet();
public String classComment = "";
public boolean isAbstract;
public String className;
public String superClassName;
public String[] implementedInterfaces;
public ArrayList<Member> publicConstants = Lists.newArrayList();
public ArrayList<Member> publicFields = Lists.newArrayList();
public ArrayList<Member> publicStaticMethods = Lists.newArrayList();
public ArrayList<Member> publicConstructors = Lists.newArrayList();
public ArrayList<Member> publicMethods = Lists.newArrayList();
public ArrayList<Member> protectedConstructors = Lists.newArrayList();
public ArrayList<Member> protectedMethods = Lists.newArrayList();
public ArrayList<Member> protectedStaticMethods = Lists.newArrayList();
public ArrayList<Member> protectedFields = Lists.newArrayList();
public ArrayList<Member> protectedConstants = Lists.newArrayList();
public static String toSimpleName (String name)
{
name = name.substring(name.lastIndexOf(".")+1);
// inner classes are not supported by ActionScript so we _
name = name.replaceAll("\\$", "_");
return name;
}
/**
* Creates and returns a declaration for a field equivalent to the supplied Java field in
* ActionScript.
*/
public String createActionScriptDeclaration (String name, Field field, ActionScript asa)
{
int mods = field.getModifiers();
StringBuilder builder = new StringBuilder();
// what's our visibility?
if (Modifier.isPublic(mods)) {
builder.append("public ");
} else if (Modifier.isProtected(mods)) {
builder.append("protected ");
} else if (Modifier.isPrivate(mods)) {
builder.append("private ");
}
// are we static?
if (Modifier.isStatic(mods)) {
builder.append("static ");
}
// const or variable?
builder.append(Modifier.isFinal(mods) ? "const " : "var ");
// next comes the name
builder.append(name).append(" :");
// now convert the type to an ActionScript type
if (asa != null && !StringUtil.isBlank(asa.type())) {
builder.append(asa.type());
} else {
builder.append(toActionScriptType(field.getType(), !Modifier.isStatic(mods)));
}
builder.append(";");
return builder.toString();
}
public String createActionScriptDeclaration (Constructor<?> ctor, boolean needsNoArg)
{
int mods = ctor.getModifiers();
StringBuilder builder = new StringBuilder();
// what's our visibility?
if (Modifier.isPublic(mods)) {
builder.append("public ");
} else if (Modifier.isProtected(mods)) {
builder.append("protected ");
} else if (Modifier.isPrivate(mods)) {
builder.append("private ");
}
// all constructors are functions
builder.append("function ");
// next comes the name
builder.append(toSimpleName(ctor.getName())).append(" (");
// now the parameters
int idx = 0;
for (Class<?> ptype : ctor.getParameterTypes()) {
if (idx++ > 0) {
builder.append(", ");
}
builder.append("arg").append(idx);
builder.append(" :").append(toActionScriptType(ptype, false));
if (needsNoArg) {
builder.append(" = undef");
}
}
builder.append(")");
return builder.toString();
}
public String createActionScriptDeclaration (String name, Method method)
{
int mods = method.getModifiers();
StringBuilder builder = new StringBuilder();
// TODO: override
// what's our visibility?
if (Modifier.isPublic(mods)) {
builder.append("public ");
} else if (Modifier.isProtected(mods)) {
builder.append("protected ");
} else if (Modifier.isPrivate(mods)) {
builder.append("private ");
}
// are we static const or variable?
if (Modifier.isStatic(mods)) {
builder.append("static ");
}
builder.append("function ");
// next comes the name
builder.append(name).append(" (");
// now the parameters
int idx = 0;
for (Class<?> ptype : method.getParameterTypes()) {
if (idx++ > 0) {
builder.append(", ");
}
builder.append("arg").append(idx);
builder.append(" :").append(toActionScriptType(ptype, false));
}
builder.append(") :");
builder.append(toActionScriptType(method.getReturnType(), false));
return builder.toString();
}
public String toActionScriptType (Class<?> type, boolean isField)
{
if (type.isArray()) {
if (Byte.TYPE.equals(type.getComponentType())) {
imports.add("flash.utils.ByteArray");
return "ByteArray";
}
if (isField) {
imports.add("com.threerings.io.TypedArray");
return "TypedArray";
}
return "Array";
}
if (Integer.TYPE.equals(type) ||
Byte.TYPE.equals(type) ||
Short.TYPE.equals(type) ||
Character.TYPE.equals(type)) {
return "int";
}
if (Float.TYPE.equals(type) ||
Double.TYPE.equals(type)) {
return "Number";
}
if (Long.TYPE.equals(type)) {
imports.add("com.threerings.util.Long");
return "Long";
}
if (Boolean.TYPE.equals(type)) {
return "Boolean";
}
if (!String.class.equals(type)) {
imports.add(type.getName());
}
return toSimpleName(type.getName());
}
public ActionScriptSource (Class<?> jclass)
{
packageName = jclass.getPackage().getName();
className = jclass.getName().substring(packageName.length()+1);
Class<?> sclass = jclass.getSuperclass();
if (sclass != null && !sclass.getName().equals("java.lang.Object")) {
superClassName = toSimpleName(sclass.getName());
}
isAbstract = ((jclass.getModifiers() & Modifier.ABSTRACT) != 0);
// note our implemented interfaces
ArrayList<String> ifaces = Lists.newArrayList();
for (Class<?> iclass : jclass.getInterfaces()) {
// we cannot use the FooCodes interface pattern in ActionScript so we just nix it
if (iclass.getName().endsWith("Codes")) {
continue;
}
// we also nix Serializable and IsSerializable (hackity hack)
if (iclass.getName().endsWith("Serializable")) {
continue;
}
ifaces.add(toSimpleName(iclass.getName()));
}
// convert the field members
for (Field field : jclass.getDeclaredFields()) {
int mods = field.getModifiers();
ArrayList<Member> list;
if (Modifier.isStatic(mods)) {
list = Modifier.isPublic(mods) ? publicConstants : protectedConstants;
} else {
list = Modifier.isPublic(mods) ? publicFields : protectedFields;
}
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, asa);
list.add(new Member(name, decl));
}
METHODS:
for (Method method : jclass.getDeclaredMethods()) {
int mods = method.getModifiers();
ArrayList<Member> list;
if (Modifier.isStatic(mods)) {
list = Modifier.isPublic(mods) ?
publicStaticMethods : protectedStaticMethods;
} else {
list = Modifier.isPublic(mods) ?
publicMethods : protectedMethods;
}
String name = method.getName();
// we have to scan our parent classes for annotations because we may be overriding this
// method and we want to "inherit" annotations
Class<?> cclass = jclass;
do {
try {
Method cmethod = cclass.getMethod(name, method.getParameterTypes());
ActionScript asa = cmethod.getAnnotation(ActionScript.class);
if (asa != null) {
if (asa.omit()) {
continue METHODS;
}
if (!StringUtil.isBlank(asa.name())) {
name = asa.name();
}
}
} catch (NoSuchMethodException nsme) {
// thanks Java for insisting on using exceptions for a potentially expected
// code path; fall through and try the superclass
}
cclass = cclass.getSuperclass();
} while (cclass != null);
// if this is an auto-generated reader or writer method, skip it
if (name.startsWith("readField_") || name.startsWith("writeField_")) {
continue;
}
// see if we already have a member for this method name
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";
list.add(mem);
} else {
// TODO: only overwrite if we have more arguments
mem.definition = decl;
}
}
// if we override hashCode() then add Hashable to our interfaces
if (getMember(publicMethods, "hashCode") != null) {
ifaces.add("Hashable");
}
// finally turn our implemented interfaces into an array
implementedInterfaces = ifaces.toArray(new String[ifaces.size()]);
}
public void absorbJava (File jsource)
throws IOException
{
// parse our Java source file for niggling bits
BufferedReader bin = new BufferedReader(new FileReader(jsource));
String line = null;
Mode mode = Mode.PREAMBLE;
Matcher m;
int braceCount = 0;
boolean seenOpenBrace = false;
StringBuilder accum = new StringBuilder();
while ((line = bin.readLine()) != null) {
line = line.replaceAll("\\s+$", "");
switch (mode) {
case PREAMBLE:
// look for the package declaration
m = JPACKAGE.matcher(line);
if (m.matches()) {
preamble = accum.toString();
accum.setLength(0);
packageName = m.group(1);
mode = Mode.IMPORTS;
} else {
accum.append(line).append("\n");
}
break;
case IMPORTS:
// look for the start of the class comment
if (line.startsWith("/**")) {
accum.setLength(0);
mode = Mode.CLASSCOMMENT;
}
m = IMPORT.matcher(line);
if (m.matches()) {
imports.add(m.group(1));
}
break;
case CLASSCOMMENT:
// look for the class declaration
if (line.startsWith("public ")) {
classComment = accum.toString();
accum.setLength(0);
mode = line.endsWith("{") ? Mode.CLASSBODY : Mode.CLASSDECL;
} else {
accum.append(line).append("\n");
}
break;
case CLASSDECL:
// look for the open brace
if (line.equals("{")) {
mode = Mode.CLASSBODY;
}
break;
case CLASSBODY:
// see if we match a field declaration
if ((m = JFIELD.matcher(line)).matches()) {
// if this line does not end on a semicolon, keep reading until it does
if (line.indexOf(";") == -1) {
line = slurpUntil(bin, line, ";", true);
// now rematch it all on one line
m = JFIELD.matcher(line);
if (!m.matches()) {
System.err.println("J: Pants, no longer match field: " + line);
continue;
}
}
String name = m.group(1);
String comment = accum.toString();
accum.setLength(0);
// strip out any @ActionScript annotation
if (comment.indexOf("@ActionScript") != -1) {
StringBuilder combuf = new StringBuilder();
for (String cline : StringUtil.split(comment, "\n")) {
if (cline.indexOf("@ActionScript") == -1) {
if (combuf.length() > 0) {
combuf.append("\n");
}
combuf.append(cline);
}
}
comment = combuf.toString();
}
// set the comment on the specified field
Member mem = updateComment(publicConstants, name, comment);
if (mem == null) {
mem = updateComment(publicFields, name, comment);
}
if (mem == null) {
mem = updateComment(protectedFields, name, comment);
}
if (mem == null) {
mem = updateComment(protectedConstants, name, comment);
}
if (mem == null) {
continue; // it was omitted, so skip it
}
// extract and clean up any default value
String defval = m.group(2).trim();
if (defval.startsWith("=")) {
defval = defval.substring(1);
}
if (defval.endsWith(";")) {
defval = defval.substring(0, defval.length()-1);
}
defval = defval.trim();
if (defval.length() > 0) {
mem.setInitialValue(defval);
}
// see if we match a constructor declaration
} else if ((m = JCONSTRUCTOR.matcher(line)).matches()) {
// if this line does not contain a close paren, keep reading until it does
if (line.indexOf(")") == -1) {
line = slurpUntil(bin, line, ")", true);
// now rematch it all on one line
m = JCONSTRUCTOR.matcher(line);
if (!m.matches()) {
System.err.println("J: Pants, no longer match ctor: " + line);
continue;
}
}
String name = m.group(1);
String comment = accum.toString();
accum.setLength(0);
// the only annotation that makes sense on a constructor is "omit"
if (comment.indexOf("@ActionScript") == -1) {
// set the comment on the specified method
updateComment(publicConstructors, name, comment);
updateComment(protectedConstructors, name, comment);
}
// switch to METHODBODY to absorb the method
braceCount = (line.indexOf("{") == -1) ? 0 : 1;
seenOpenBrace = (braceCount > 0);
mode = Mode.METHODBODY;
// see if we match a method declaration
} else if ((m = JMETHOD.matcher(line)).matches()) {
// if this line does not contain a close paren, keep reading until it does
if (line.indexOf(")") == -1) {
line = slurpUntil(bin, line, ")", true);
// now rematch it all on one line
m = JMETHOD.matcher(line);
if (!m.matches()) {
System.err.println(
"J: Pants, no longer match method: " + line);
continue;
}
}
String name = m.group(1);
String comment = accum.toString();
accum.setLength(0);
// look through the comment for an @ActionScript annotation; oh the hackery
if (comment.indexOf("@ActionScript") != -1) {
StringBuilder combuf = new StringBuilder();
for (String cline : StringUtil.split(comment, "\n")) {
if ((m = METHOD_ANNOTATION.matcher(cline)).matches()) {
name = m.group(1);
} else {
if (combuf.length() > 0) {
combuf.append("\n");
}
combuf.append(cline);
}
}
comment = combuf.toString();
}
// set the comment on the specified method
updateComment(publicStaticMethods, name, comment);
updateComment(publicMethods, name, comment);
updateComment(protectedMethods, name, comment);
updateComment(protectedStaticMethods, name, comment);
// switch to METHODBODY to absorb the method
braceCount = (line.indexOf("{") == -1) ? 0 : 1;
seenOpenBrace = (braceCount > 0);
mode = Mode.METHODBODY;
} else {
// make sure this is a comment (or annotation)
String tline = line.trim();
if (tline.length() == 0 || tline.startsWith("@") ||
tline.startsWith("//") || tline.startsWith("/*") ||
tline.startsWith("*")) {
accum.append(line).append("\n");
} else if (line.startsWith("}")) {
mode = Mode.POSTCLASS;
} else {
System.err.println("J: Non-comment encountered between members: " + line);
}
}
break;
case METHODBODY:
if (line.indexOf("{") != -1) {
seenOpenBrace = true;
braceCount++;
}
if (line.indexOf("}") != -1) {
braceCount--;
}
if (seenOpenBrace && braceCount == 0) {
mode = Mode.CLASSBODY;
}
break;
case POSTCLASS:
System.err.println("J: Post-class junk: " + line);
break;
case POSTPKG:
System.err.println("J: Post-pkg junk: " + line);
break;
}
}
bin.close();
if (accum.length() > 0) {
System.err.println("J: Leftover accumulated text: " + accum);
}
}
public void absorbActionScript (File assource)
throws IOException
{
// if our action script source file doesn't exist, we're done
if (!assource.exists()) {
return;
}
BufferedReader bin = new BufferedReader(new FileReader(assource));
String line = null;
Mode mode = Mode.PREAMBLE;
Matcher m;
int braceCount = 0;
boolean seenOpenBrace = false;
StringBuilder accum = new StringBuilder();
Member curmem = null;
while ((line = bin.readLine()) != null) {
line = line.replaceAll("\\s+$", "");
// System.err.println(" " + mode + " : " + line);
switch (mode) {
case PREAMBLE:
// look for the package declaration
if (line.startsWith("package ")) {
preamble = accum.toString();
accum.setLength(0);
mode = Mode.IMPORTS;
} else {
accum.append(line).append("\n");
}
break;
case IMPORTS:
// look for the start of the class comment
if (line.startsWith("/**")) {
accum.setLength(0);
mode = Mode.CLASSCOMMENT;
} else if (line.startsWith("public ")) {
accum.setLength(0);
mode = line.endsWith("{") ? Mode.CLASSBODY : Mode.CLASSDECL;
}
m = IMPORT.matcher(line);
if (m.matches()) {
imports.add(m.group(1));
}
break;
case CLASSCOMMENT:
// look for the class declaration
if (line.startsWith("public ")) {
classComment = accum.toString();
accum.setLength(0);
mode = line.endsWith("{") ? Mode.CLASSBODY : Mode.CLASSDECL;
} else {
accum.append(line).append("\n");
}
break;
case CLASSDECL:
// look for the open brace
if (line.equals("{")) {
mode = Mode.CLASSBODY;
}
break;
case CLASSBODY:
// see if we match a field declaration
if ((m = ASFIELD.matcher(line)).matches()) {
// if this line does not contain a semicolon, keep reading until it does
if (line.indexOf(";") == -1) {
line = slurpUntil(bin, line, ";", false);
}
ArrayList<Member> list = publicFields;
boolean isProtected = (line.indexOf("protected ") != -1);
boolean isConst = (line.indexOf("const ") != -1);
list = isProtected ?
(isConst ? protectedConstants : protectedFields) :
(isConst ? publicConstants : publicFields);
// extract the name, replace the declaration
String fieldName = m.group(1);
//log.info("ASFIELD", "name", fieldName);
Member member = getMember(list, fieldName);
if (member == null) {
System.err.println(
"Have ActionScript field with no " +
"Java equivalent: " + fieldName);
member = new Member(fieldName, line.trim());
list.add(member);
}
// TODO: update the comment?
accum.setLength(0);
// see if we match a constructor declaration
} else if (line.indexOf("public function " + className) != -1) {
// if this line does not contain a close paren, keep reading until it does
if (line.indexOf(")") == -1) {
line = slurpUntil(bin, line, ")", false);
}
// look up the public constructor
curmem = getMember(publicConstructors, className);
if (curmem == null) {
System.err.println("Missing public constructor for " +
"update " + className + "().");
} else {
// update the definition and comment with the
// ActionScript versions
curmem.definition = line.trim();
curmem.setComment(accum.toString());
}
accum.setLength(0);
// switch to METHODBODY to absorb the method
braceCount = (line.indexOf("{") == -1) ? 0 : 1;
seenOpenBrace = (braceCount > 0);
mode = Mode.METHODBODY;
// see if we match a method declaration
} else if ((m = ASFUNCTION.matcher(line)).matches()) {
// if this line does not contain a close paren, keep reading until it does
if (line.indexOf(")") == -1) {
line = slurpUntil(bin, line, ")", false);
}
ArrayList<Member> list = publicMethods;
boolean isProtected = (line.indexOf("protected ") != -1);
boolean isStatic = (line.indexOf("static ") != -1);
list = isProtected ?
(isStatic ? protectedStaticMethods : protectedMethods) :
(isStatic ? publicStaticMethods : publicMethods);
// extract the name, replace the declaration
String funcName = m.group(1);
curmem = getMember(list, funcName);
if (curmem == null) {
System.err.println(
"Have ActionScript method with no " +
"Java equivalent: " + funcName + "().");
curmem = new Member(funcName, "");
list.add(curmem);
}
// update the definition and comment with the ActionScript
// versions
curmem.definition = line.trim();
String ncomment = accum.toString();
if (!StringUtil.isBlank(ncomment)) {
curmem.setComment(ncomment);
}
accum.setLength(0);
// switch to METHODBODY to absorb the method
braceCount = (line.indexOf("{") == -1) ? 0 : 1;
seenOpenBrace = (braceCount > 0);
mode = Mode.METHODBODY;
} else {
// make sure this is a comment (or annotation)
String tline = line.trim();
if (tline.length() == 0 || tline.startsWith("@") ||
tline.startsWith("//") || tline.startsWith("/*") ||
tline.startsWith("*")) {
accum.append(line).append("\n");
} else if (tline.equals("}")) {
mode = Mode.POSTCLASS;
} else {
System.err.println("AS: Non-comment encountered between members: " + line);
}
}
break;
case METHODBODY:
if (line.indexOf("{") != -1) {
seenOpenBrace = true;
braceCount++;
}
if (line.indexOf("}") != -1) {
braceCount--;
}
accum.append(line).append("\n");
if (seenOpenBrace && braceCount == 0) {
// update the method body of our currently matched member
if (curmem != null) {
// don't overwrite the auto-generated methods
if (!curmem.noreplace) {
curmem.body = accum.toString();
}
curmem = null;
} else {
System.err.println(
"AS: No matched method for body:\n" + accum);
}
accum.setLength(0);
mode = Mode.CLASSBODY;
}
break;
case POSTCLASS:
if (line.trim().equals("}")) {
mode = Mode.POSTPKG;
} else {
System.err.println("AS: Post-class junk: " + line);
}
break;
case POSTPKG:
System.err.println("AS: Post-package junk: " + line);
break;
}
}
bin.close();
if (accum.length() > 0) {
System.err.println("AS: Leftover accumulated text: " + accum);
}
}
public Member updateComment (
ArrayList<Member> list, String name, String comment)
{
Member mem = getMember(list, name);
if (mem != null) {
mem.setComment(comment);
}
return mem;
}
public Member getMember (ArrayList<Member> list, String name)
{
for (Member member : list) {
if (member.name.equals(name)) {
return member;
}
}
return null;
}
/**
* Writes our class definition to the supplied writer.
*/
public void write (PrintWriter writer)
{
writer.print(preamble);
writer.println("package " + packageName + " {");
if (!imports.isEmpty()) {
writer.println();
for (String imp : imports) {
writer.println("import " + imp + ";");
}
writer.println();
}
writer.print(classComment);
writer.print("public ");
if (isAbstract) {
writer.print("/*abstract*/ ");
}
writer.print("class " + className);
if (superClassName != null) {
writer.print(" extends " + superClassName);
}
writer.println("");
if (implementedInterfaces.length > 0) {
writer.print(" implements ");
for (int ii = 0; ii < implementedInterfaces.length; ii++) {
if (ii > 0) {
writer.print(", ");
}
writer.print(implementedInterfaces[ii]);
}
writer.println("");
}
writer.println("{"); // start class block
for (Member member : publicConstants) {
member.write(writer);
}
writeBlank(publicConstants, writer);
for (Member member : publicFields) {
member.write(writer);
}
writeBlank(publicFields, writer);
for (Member member : publicStaticMethods) {
member.write(writer);
writeBlank(publicStaticMethods, writer);
}
for (Member member : publicConstructors) {
member.write(writer);
writer.println();
}
for (Member member : publicMethods) {
member.write(writer);
writer.println();
}
for (Member member : protectedConstructors) {
member.write(writer);
writer.println();
}
for (Member member : protectedMethods) {
member.write(writer);
writer.println();
}
for (Member member : protectedStaticMethods) {
member.write(writer);
writer.println();
}
for (Member member : protectedFields) {
member.write(writer);
}
writeBlank(protectedFields, writer);
for (Member member : protectedConstants) {
member.write(writer);
}
writer.println("}"); // end class block
writer.println("}"); // end package block
writer.flush();
}
@Override
public String toString ()
{
StringBuilder builder = new StringBuilder();
builder.append("public ");
if (isAbstract) {
builder.append("abstract ");
}
builder.append("class ").append(className);
if (superClassName != null) {
builder.append(" extends ").append(superClassName);
}
return builder.toString();
}
protected String slurpUntil (BufferedReader reader, String text, String token, boolean stripNL)
throws IOException
{
int braces = countChars(text, '{') - countChars(text, '}');
String line;
while ((line = reader.readLine()) != null) {
braces += countChars(line, '{');
braces -= countChars(line, '}');
if (braces < 0) {
System.err.println(
"Too many close braces? [text=" + text + ", line=" + line + "].");
}
line = line.replaceAll("\\s+$", "");
if (!stripNL) {
text += "\n";
}
text += line;
if (braces <= 0 && line.indexOf(token) != -1) {
break;
}
}
return text;
}
protected int countChars (String text, char target)
{
int idx = -1, count = 0;
while ((idx = text.indexOf(target, idx+1)) != -1) {
count++;
}
return count;
}
protected void writeBlank (Collection<?> justWritten, PrintWriter writer)
{
if (!justWritten.isEmpty()) {
writer.println();
}
}
/** Denotes various phases of class file parsing. */
protected static enum Mode { PREAMBLE, IMPORTS, CLASSCOMMENT, CLASSDECL,
CLASSBODY, METHODBODY, POSTCLASS, POSTPKG }
protected static Pattern JPACKAGE = Pattern.compile("package\\s+(\\S+);");
protected static Pattern JFIELD = Pattern.compile(
"\\s+(?:public|protected|private)" +
"(?:\\s+static|\\s+final|\\s+transient)*" +
"\\s+(?:[a-zA-Z\\[\\]<\\?>,]+)" + // type
"\\s+([_a-zA-Z]\\w*)(\\s+=.*|;)");
protected static Pattern JCONSTRUCTOR = Pattern.compile(
"\\s+(?:public|protected|private)\\s+([a-zA-Z]\\w*) \\(.*");
protected static Pattern JMETHOD = Pattern.compile(
"\\s+(?:public|protected|private).* ([a-zA-Z]\\w*) \\(.*");
protected static Pattern METHOD_ANNOTATION = Pattern.compile(
".*@ActionScript\\(name=\"(\\w+)\"\\).*");
protected static Pattern ASFIELD = Pattern.compile(
"\\s+(?:public|protected|private)" +
"(?:\\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)" +
"(?:\\s+static)?" +
"(?:\\s+/\\*\\s*abstract\\s*\\*/)?" +
"\\s+function\\s+((?:get\\s+|set\\s+)?[a-zA-Z]\\w*) \\(.*");
protected static Pattern ARRAYINIT = Pattern.compile("new (\\w+)\\[0\\]");
protected static Pattern IMPORT = Pattern.compile("import (.*);");
}