Java tutorial
// // $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2012 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); }