29f06663c6
* Add importGroups template variable, set to the two groups: one for java, one for threerings. * Omit Client import the client-side generated code. This was removed some time ago. * Omit the blanket int -> Integer replacement, this was only needed for the marshaller * Add the as boxing imports when generating the marshaller (this gets the Integer import for int types in marshaller signatures) * Change the templates to put space between each group of imports. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6677 542714f4-19e9-0310-aa3c-eee0fc999fb1
309 lines
11 KiB
Java
309 lines
11 KiB
Java
//
|
|
// $Id$
|
|
//
|
|
// Narya library - tools for developing networked games
|
|
// Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved
|
|
// http://code.google.com/p/narya/
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License as published
|
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
package com.threerings.presents.tools;
|
|
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import java.io.File;
|
|
|
|
import com.google.common.base.Splitter;
|
|
import com.google.common.collect.Iterables;
|
|
|
|
import com.threerings.util.ActionScript;
|
|
import com.threerings.util.StreamableArrayList;
|
|
import com.threerings.util.StreamableHashMap;
|
|
import com.threerings.util.StreamableHashSet;
|
|
|
|
public class ActionScriptUtils
|
|
{
|
|
/**
|
|
* Adds an existing ActionScript file's imports to the given ImportSet.
|
|
* @param asFile a String containing the contents of an ActionScript file
|
|
*/
|
|
public static void addExistingImports (String asFile, ImportSet imports)
|
|
{
|
|
// Discover the location of the 'public class' declaration.
|
|
// We won't search past there.
|
|
Matcher m = AS_PUBLIC_CLASS_DECL.matcher(asFile);
|
|
int searchTo = asFile.length();
|
|
if (m.find()) {
|
|
searchTo = m.start();
|
|
}
|
|
|
|
m = AS_IMPORT.matcher(asFile.substring(0, searchTo));
|
|
while (m.find()) {
|
|
imports.add(m.group(3));
|
|
}
|
|
}
|
|
|
|
public static String addImportAndGetShortType (Class<?> type, boolean isField,
|
|
ImportSet imports)
|
|
{
|
|
String full = toActionScriptType(type, isField);
|
|
if (needsActionScriptImport(type, isField)) {
|
|
imports.add(full);
|
|
}
|
|
return Iterables.getLast(DOT_SPLITTER.split(full));
|
|
}
|
|
|
|
public static String toSimpleName (Class<?> type)
|
|
{
|
|
String name = type.getName().substring(type.getName().lastIndexOf(".") + 1);
|
|
// inner classes are not supported by ActionScript so we _
|
|
return name.replaceAll("\\$", "_");
|
|
}
|
|
|
|
public static String toReadObject (Class<?> type)
|
|
{
|
|
if (type.equals(String.class)) {
|
|
return "readField(String)";
|
|
|
|
} else if (type.equals(Integer.class) ||
|
|
type.equals(Short.class) ||
|
|
type.equals(Byte.class)) {
|
|
return "readField(" + toSimpleName(type) + ").value";
|
|
|
|
} else if (type.equals(Long.class)) {
|
|
return "readField(" + toSimpleName(type) + ")";
|
|
|
|
} else if (type.equals(Boolean.TYPE)) {
|
|
return "readBoolean()";
|
|
|
|
} else if (type.equals(Byte.TYPE)) {
|
|
return "readByte()";
|
|
|
|
} else if (type.equals(Short.TYPE) || type.equals(Character.TYPE)) {
|
|
return "readShort()";
|
|
|
|
} else if (type.equals(Integer.TYPE)) {
|
|
return "readInt()";
|
|
|
|
} else if (type.equals(Long.TYPE)) {
|
|
return "readLong()";
|
|
|
|
} else if (type.equals(Float.TYPE)) {
|
|
return "readFloat()";
|
|
|
|
} else if (type.equals(Double.TYPE)) {
|
|
return "readDouble()";
|
|
|
|
} else if (isNaiveMap(type)) {
|
|
return "readField(MapStreamer.INSTANCE)";
|
|
|
|
} else if (isNaiveList(type)) {
|
|
return "readField(ArrayStreamer.INSTANCE)";
|
|
|
|
} else if (isNaiveSet(type)) {
|
|
return "readField(SetStreamer.INSTANCE)";
|
|
|
|
} else if (type.isArray()) {
|
|
if (!type.getComponentType().isPrimitive()) {
|
|
return "readObject(TypedArray)";
|
|
} else {
|
|
if (Double.TYPE.equals(type.getComponentType())) {
|
|
return "readField(TypedArray.getJavaType(Number))";
|
|
} else if (Boolean.TYPE.equals(type.getComponentType())) {
|
|
return "readField(TypedArray.getJavaType(Boolean))";
|
|
} else if (Integer.TYPE.equals(type.getComponentType())) {
|
|
return "readField(TypedArray.getJavaType(int))";
|
|
} else if (Byte.TYPE.equals(type.getComponentType())) {
|
|
return "readField(ByteArray)";
|
|
} else {
|
|
throw new IllegalArgumentException(type
|
|
+ " isn't supported to stream to actionscript");
|
|
}
|
|
}
|
|
} else {
|
|
return "readObject(" + Iterables.getLast(DOT_SPLITTER.split(toActionScriptType(type, false))) + ")";
|
|
}
|
|
}
|
|
|
|
public static String toWriteObject (Class<?> type, String name)
|
|
{
|
|
if (type.equals(Integer.class)) {
|
|
return "writeObject(new Integer(" + name + "))";
|
|
|
|
} else if (type.equals(Boolean.TYPE)) {
|
|
return "writeBoolean(" + name + ")";
|
|
|
|
} else if (type.equals(Byte.TYPE)) {
|
|
return "writeByte(" + name + ")";
|
|
|
|
} else if (type.equals(Short.TYPE) ||
|
|
type.equals(Character.TYPE)) {
|
|
return "writeShort(" + name + ")";
|
|
|
|
} else if (type.equals(Integer.TYPE)) {
|
|
return "writeInt(" + name + ")";
|
|
|
|
} else if (type.equals(Long.TYPE)) {
|
|
return "writeLong(" + name + ")";
|
|
|
|
} else if (type.equals(Float.TYPE)) {
|
|
return "writeFloat(" + name + ")";
|
|
|
|
} else if (type.equals(Double.TYPE)) {
|
|
return "writeDouble(" + name + ")";
|
|
|
|
} else if (type.equals(Long.class) ||
|
|
type.equals(String.class) ||
|
|
(type.isArray() && type.getComponentType().isPrimitive())) {
|
|
return "writeField(" + name + ")";
|
|
|
|
} else if (isNaiveList(type)) {
|
|
return "writeField(" + name + ", ArrayStreamer.INSTANCE)";
|
|
|
|
} else if (isNaiveMap(type)) {
|
|
return "writeField(" + name + ", MapStreamer.INSTANCE)";
|
|
|
|
} else if (isNaiveSet(type)) {
|
|
return "writeField(" + name + ", SetStreamer.INSTANCE)";
|
|
|
|
} else {
|
|
return "writeObject(" + name + ")";
|
|
}
|
|
}
|
|
|
|
public static String toActionScriptType (Class<?> type, boolean isField)
|
|
{
|
|
if (type.isArray() || isNaiveList(type)) {
|
|
if (Byte.TYPE.equals(type.getComponentType())) {
|
|
return "flash.utils.ByteArray";
|
|
} else if (isField) {
|
|
return "com.threerings.io.TypedArray";
|
|
} else {
|
|
return "Array";
|
|
}
|
|
} else if (isNaiveMap(type)) {
|
|
return "com.threerings.util.Map";
|
|
} else if (isNaiveSet(type)) {
|
|
return "com.threerings.util.Set";
|
|
} else if (Integer.TYPE.equals(type) ||
|
|
Byte.TYPE.equals(type) ||
|
|
Short.TYPE.equals(type) ||
|
|
Character.TYPE.equals(type)) {
|
|
return "int";
|
|
} else if (Float.TYPE.equals(type) ||
|
|
Double.TYPE.equals(type)) {
|
|
return "Number";
|
|
} else if (Long.TYPE.equals(type)) {
|
|
return "com.threerings.util.Long";
|
|
} else if (Boolean.TYPE.equals(type)) {
|
|
return "Boolean";
|
|
} else if (Cloneable.class.equals(type)) {
|
|
return "com.threerings.util.Cloneable";
|
|
} else if (Comparable.class.equals(type)) {
|
|
return "com.threerings.util.Comparable";
|
|
} else {
|
|
// inner classes are not supported by ActionScript so we _
|
|
return type.getName().replaceAll("\\$", "_");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts java types to their actionscript equivalents for ooo-style streaming.
|
|
*/
|
|
public static void convertBaseClasses (ImportSet imports)
|
|
{
|
|
// replace primitive types with OOO types (required for unboxing)
|
|
imports.replace("byte", "com.threerings.util.Byte");
|
|
imports.replace("boolean", "com.threerings.util.langBoolean");
|
|
imports.replace("[B", "flash.utils.ByteArray");
|
|
imports.replace("float", "com.threerings.util.Float");
|
|
imports.replace("long", "com.threerings.util.Long");
|
|
|
|
if (imports.removeAll("[*") > 0) {
|
|
imports.add("com.threerings.io.TypedArray");
|
|
}
|
|
|
|
// convert java primitive boxes to their ooo counterparts
|
|
imports.replace(Integer.class, "com.threerings.util.Integer");
|
|
|
|
// convert some java.util types to their ooo counterparts
|
|
imports.replace(Map.class, "com.threerings.util.Map");
|
|
|
|
// get rid of java.lang stuff and any remaining primitives
|
|
imports.removeGlobals();
|
|
|
|
// get rid of remaining arrays
|
|
imports.removeArrays();
|
|
}
|
|
|
|
public static File createActionScriptPath (File actionScriptRoot, Class<?> sclass)
|
|
{
|
|
// determine the path to the corresponding action script source file
|
|
String path = toActionScriptType(sclass, false).replace(".", File.separator);
|
|
return new File(actionScriptRoot, path + ".as");
|
|
}
|
|
|
|
public static boolean hasOmitAnnotation (Class<?> cclass)
|
|
{
|
|
|
|
// if we have an ActionScript(omit=true) annotation, skip this class
|
|
do {
|
|
ActionScript asa = cclass.getAnnotation(ActionScript.class);
|
|
if (asa != null && asa.omit()) {
|
|
// System.err.println("Skipping " + sclass.getName() + "...");
|
|
return true;
|
|
}
|
|
cclass = cclass.getSuperclass();
|
|
} while (cclass != null);
|
|
return false;
|
|
}
|
|
|
|
/** Returns if the given class is an implementation of Map that doesn't know about Streaming */
|
|
protected static boolean isNaiveMap (Class<?> type)
|
|
{
|
|
return Map.class.isAssignableFrom(type) && !type.equals(StreamableHashMap.class);
|
|
}
|
|
|
|
protected static boolean isNaiveSet (Class<?> type)
|
|
{
|
|
return Set.class.isAssignableFrom(type) && !type.equals(StreamableHashSet.class);
|
|
}
|
|
|
|
/** Returns if the given class is an implementation of List that doesn't know about Streaming */
|
|
protected static boolean isNaiveList (Class<?> type)
|
|
{
|
|
return List.class.isAssignableFrom(type) && !type.equals(StreamableArrayList.class);
|
|
}
|
|
|
|
protected static boolean needsActionScriptImport (Class<?> type, boolean isField)
|
|
{
|
|
if (type.isArray()) {
|
|
return Byte.TYPE.equals(type.getComponentType()) || isField;
|
|
}
|
|
return (Long.TYPE.equals(type) || !type.isPrimitive()) && !String.class.equals(type);
|
|
}
|
|
|
|
protected static final Splitter DOT_SPLITTER = Splitter.on('.');
|
|
|
|
protected static final Pattern AS_PUBLIC_CLASS_DECL = Pattern.compile("^public class(.*)$",
|
|
Pattern.MULTILINE);
|
|
protected static final Pattern AS_IMPORT = Pattern.compile("^(\\s*)import(\\s+)([^;]*);",
|
|
Pattern.MULTILINE);
|
|
}
|