Files
narya/src/main/java/com/threerings/presents/tools/ActionScriptUtils.java
T
Jamie Doornbos 29f06663c6 Various tweaks to get import ordering to match the ooo convention of putting java and javax imports first, then threerings imports.
* 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
2011-07-06 21:06:55 +00:00

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);
}