org.server.AppGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.server.AppGenerator.java

Source

/* Xholon Runtime Framework - executes event-driven & dynamic applications
 * Copyright (C) 2013 Ken Webb
 *
 * 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 org.server;

import java.io.PrintWriter;

//import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import java.util.ArrayList;
import java.util.List;

/**
 * Generate a new IApplication subclass with app-specific methods.
 * This is called during the development and production mode builds.
 * @author <a href="mailto:ken@primordion.com">Ken Webb</a>
 * @see <a href="http://www.primordion.com/Xholon">Xholon Project website</a>
 * @since 0.9.0 (Created on August 6, 2013)
 */
public class AppGenerator extends Generator {

    /**
     * The name of every generated class will end with this suffix.
     * The suffix needs to be unusual enough that no application class would use it.
     */
    protected static final String CLASSNAME_SUFFIX = "GWTGEN"; // "Gen"

    @Override
    public String generate(TreeLogger logger, GeneratorContext context, String typeName)
            throws UnableToCompleteException {

        //System.out.println("Starting AppGenerator.generate( typeName=" + typeName);

        TypeOracle oracle = context.getTypeOracle();
        JClassType classType = oracle.findType(typeName);
        JClassType iXholonType = oracle.findType(org.primordion.xholon.base.IXholon.class.getName());

        String packageName = classType.getPackage().getName();
        String simpleName = classType.getSimpleSourceName() + CLASSNAME_SUFFIX;
        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, simpleName);
        composer.setSuperclass(classType.getName());

        // add imports needed by composer

        composer.addImport("org.primordion.xholon.app.IApplication");
        composer.addImport("org.primordion.xholon.base.IControl");
        composer.addImport("org.primordion.xholon.base.IMechanism");
        composer.addImport("org.primordion.xholon.base.IXholon");
        composer.addImport("org.primordion.xholon.base.IXholonClass");
        composer.addImport("java.util.ArrayList");
        composer.addImport("java.util.List");

        PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName);
        SourceWriter src = null;
        if (printWriter == null) {
            // the code has already been generated
            return typeName + CLASSNAME_SUFFIX;
        } else {
            src = composer.createSourceWriter(context, printWriter);
        }

        System.out.println("Starting AppGenerator.generate( typeName=" + typeName);

        //                                 Add appSpecific methods.
        JClassType[] types = getPackageTypes(classType);

        // classes and objects
        src.println(writeMakeAppSpecificNode(packageName, classType.getSimpleSourceName(), types, iXholonType));
        src.println(writeFindAppSpecificClass(packageName, classType.getSimpleSourceName(), types, iXholonType));
        src.println(
                writeIsAppSpecificClassFindable(packageName, classType.getSimpleSourceName(), types, iXholonType));

        // constants
        src.println(writeFindAppSpecificConstantValue(packageName, classType.getSimpleSourceName(), types,
                iXholonType));

        // ports
        src.println(writeGetAppSpecificObjectVal(packageName, classType.getSimpleSourceName(), types, iXholonType,
                oracle));
        src.println(writeSetAppSpecificObjectVal(packageName, classType.getSimpleSourceName(), types, iXholonType));
        // port arrays
        src.println(writeSetAppSpecificObjectArrayVal(packageName, classType.getSimpleSourceName(), types,
                iXholonType));

        // attributes
        src.println(writeGetAppSpecificAttribute(packageName, types, iXholonType, oracle));
        src.println(writeSetAppSpecificAttribute(packageName, types, iXholonType, oracle));
        src.println(writeGetAppSpecificAttributes(packageName, types, iXholonType, oracle));
        src.println(writeIsAppSpecificAttribute(packageName, types, iXholonType, oracle));

        src.commit(logger);
        return typeName + CLASSNAME_SUFFIX;
    }

    /**
     * Get an array of class types that belong to the application's package.
     */
    protected JClassType[] getPackageTypes(JClassType classType) {
        JClassType[] typesIn = classType.getPackage().getTypes();
        // filter out any generated types that are already in the package (ex: "App...GWTGEN")
        //List typeListIn = Arrays.asList(typesIn);
        List<JClassType> typeListOut = new ArrayList<JClassType>();
        for (int i = 0; i < typesIn.length; i++) {
            JClassType type = typesIn[i];
            String typeName = type.getName();
            //System.out.println("in:" + typeName);
            if (!typeName.endsWith(CLASSNAME_SUFFIX)) {
                typeListOut.add(type);

                //JClassType supertype = type.getSuperclass();
                //if (supertype != null) {
                //  System.out.println(typeName + " " + supertype.getName());
                //}

            }
        }
        JClassType[] typesOut = typeListOut.toArray(new JClassType[0]);
        // return the filtered array
        //for (int j = 0; j < typesOut.length; j++) {
        //  System.out.println("out:" + typesOut[j].getName());
        //}
        return typesOut;
    }

    /* Write a makeAppSpecificNode() method.
     * Example:
    <code>
    public IXholon makeAppSpecificNode(String implName) {
    if ("org.primordion.user.app.PingPong.XhPingPong".equals(implName)) {
      return new XhPingPong();
    }
    return super.makeAppSpecificNode(implName);
    }
    </code>
    */
    protected String writeMakeAppSpecificNode(String appPackageName, String appSimpleName, JClassType[] types,
            JClassType iXholonType) {
        String xhSimpleName = null;

        StringBuilder sbt = new StringBuilder();
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if (type.isDefaultInstantiable() && (type.isAssignableTo(iXholonType))) {
                //we have determined that the IXholon class can be constructed using a simple new operation
                xhSimpleName = type.getName();
                if (!xhSimpleName.startsWith("App")) {
                    sbt.append("  if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(implName)) {\n");
                    sbt.append("    return new ").append(xhSimpleName).append("();\n");
                    sbt.append("  }\n");
                }
            }
        }

        StringBuilder sb = new StringBuilder().append("public IXholon makeAppSpecificNode(String implName) {\n")
                .append(sbt.toString()).append("  return super.makeAppSpecificNode(implName);\n").append("}\n");
        //System.out.println(sb.toString());
        return sb.toString();
    }

    /* Write a findAppSpecificClass() method.
     * Example:
    <code>
    public Class<IXholon> findAppSpecificClass(String className) {
    if ("org.primordion.user.app.PingPong.XhPingPong".equals(className)) {
      Class clazz = org.primordion.user.app.PingPong.XhPingPong.class;
      return (Class<IXholon>)clazz;
    }
    return super.findAppSpecificClass(className);
    }
    </code>
    */
    protected String writeFindAppSpecificClass(String appPackageName, String appSimpleName, JClassType[] types,
            JClassType iXholonType) {
        // assume that the Xh class name is related to the App class name
        // ex: AppPingPong XhPingPong
        //String xhSimpleName = "Xh" + appSimpleName.substring(3);

        String xhSimpleName = null;

        StringBuilder sbt = new StringBuilder();
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if (type.isDefaultInstantiable() && (type.isAssignableTo(iXholonType))) {
                //we have determined that the IXholon class can be constructed using a simple new operation
                xhSimpleName = type.getName();
                if (!xhSimpleName.startsWith("App")) {
                    sbt.append("  if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(className)) {\n")
                            //.append("    println(\"findAppSpecificClass(\" + className + \")\");\n")
                            .append("    Class clazz = ").append(appPackageName + "." + xhSimpleName)
                            .append(".class;\n").append("    return (Class<IXholon>)clazz;\n").append("  }\n");
                }
            }
        }

        StringBuilder sb = new StringBuilder()
                .append("public Class<IXholon> findAppSpecificClass(String className) {\n").append(sbt.toString())
                .append("  return super.findAppSpecificClass(className);\n").append("}\n");
        //System.out.println(sb.toString());
        return sb.toString();
    }

    /* Write a findAppSpecificConstantValue() method.
     * Example:
    <code>
    public int findAppSpecificConstantValue(Class clazz, String constName) {
    if (clazz == null) {return 0;}
    else if ("org.primordion.xholon.xmiapps.XhTestFsm".equals(clazz.getName())) {
      if ("P_PARTNER".equals(constName)) {return XhTestFsm.P_PARTNER;}
    }
    return super.findAppSpecificConstantValue(clazz, constName);
    }
    </code>
    */
    protected String writeFindAppSpecificConstantValue(String appPackageName, String appSimpleName,
            JClassType[] types, JClassType iXholonType) {
        String xhSimpleName = null;
        StringBuilder sb = new StringBuilder(128);
        StringBuilder sbt = new StringBuilder(128);
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if (
            //type.isDefaultInstantiable() &&
            type.isAssignableTo(iXholonType)) {
                //we have determined that the IXholon class can be constructed using a simple new operation
                xhSimpleName = type.getName();
                if (!xhSimpleName.startsWith("App")) {
                    JField[] fields = type.getFields();
                    StringBuilder sbf = new StringBuilder(128);
                    for (int j = 0; j < fields.length; j++) {
                        JField field = fields[j];
                        JType fieldType = field.getType();
                        if (field.isStatic() && field.isFinal() && field.isPublic()
                                && "int".equals(fieldType.getSimpleSourceName())) {
                            String fieldName = field.getName();
                            sbf.append("    if (\"").append(fieldName).append("\".equals(constName)) {return ")
                                    .append(xhSimpleName).append(".").append(fieldName).append(";}\n");
                        }
                    }
                    if (sbf.length() > 0) {
                        sbt.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                                .append("\".equals(clazz.getName())) {\n").append(sbf.toString()).append("  }\n");
                    }
                }
            }
        }

        if (sbt.length() > 0) {
            sb.append("public int findAppSpecificConstantValue(Class clazz, String constName) {\n")
                    .append("  if (clazz == null) {return 0;}\n").append(sbt.toString())

                    .append("  Class superclass = clazz.getSuperclass();\n")
                    .append("  if ((superclass != null) && superclass.getName().startsWith(\"")
                    .append(appPackageName).append("\")) {\n")
                    .append("    return findAppSpecificConstantValue(superclass, constName);\n").append("  }\n")

                    .append("  return super.findAppSpecificConstantValue(clazz, constName);\n").append("}\n");
        }
        //System.out.println(sb.toString());
        return sb.toString();
    }

    /* Write getAppSpecificObjectVal() and getAppSpecificObjectValNames() methods.
     * Example:
    <code>
    public IXholon getAppSpecificObjectVal(IXholon node, Class<IXholon> clazz, String attrName) {
    if (node == null) {return null;}
    else if ("org.primordion.user.app.testNodePorts.XhtestNodePorts".equals(clazz.getName())) {
      if ("three".equalsIgnoreCase(attrName)) {
        return ((org.primordion.user.app.testNodePorts.XhtestNodePorts)node).getThree();
      }
    }
    Class superclass = clazz.getSuperclass();
    if (superclass.getName().startsWith("org.primordion.user.app.testNodePorts")) {
      return getAppSpecificObjectVal(node, superclass, attrName);
    }
    return super.getAppSpecificObjectVal(node, clazz, attrName);
    }
        
    public String getAppSpecificObjectValNames(IXholon node, Class<IXholon> clazz) {
    String names = "";
    if (node == null) {return null;}
    else if ("org.primordion.user.app.testNodePorts.XhtestNodePorts".equals(clazz.getName())) {
      names = "three,";
    }
    Class superclass = clazz.getSuperclass();
    if (superclass.getName().startsWith("org.primordion.user.app.testNodePorts")) {
      names = names + getAppSpecificObjectValNames(node, superclass);
    }
    else {
      names = names + super.getAppSpecificObjectValNames(node, clazz);
    }
    return names;
    }
    </code>
    */
    protected String writeGetAppSpecificObjectVal(String appPackageName, String appSimpleName, JClassType[] types,
            JClassType iXholonType, TypeOracle oracle) {
        String xhSimpleName = null;

        StringBuilder sbt = new StringBuilder(128);
        StringBuilder sbfnT = new StringBuilder(); // for use with getAppSpecificObjectValNames()
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            xhSimpleName = type.getName();

            if (!xhSimpleName.startsWith("App")) {
                // get all public getters
                JMethod[] methods = type.getMethods();
                StringBuilder sbm = new StringBuilder(128);
                StringBuilder sbfnM = new StringBuilder();
                for (int j = 0; j < methods.length; j++) {
                    JMethod method = methods[j];
                    JClassType rType = oracle.findType(method.getReturnType().getQualifiedSourceName());
                    // rType = null if oracle is given a primitive type (int etc.)
                    JType[] paramTypes = method.getParameterTypes();
                    if (method.isPublic() && (method.getName().startsWith("get")) && (method.getName().length() > 3)
                            && ((rType != null) && (rType.isAssignableTo(iXholonType)))
                            && (paramTypes.length == 0)) {

                        // the paramType can be any class or interface assignable to IXholon
                        //if (paramTypes.length == 0) {
                        //JClassType paramType = paramTypes[0].isClassOrInterface();
                        //if ((paramType != null) && (paramType.isAssignableTo(iXholonType))) {

                        String fieldName = method.getName().substring(3);
                        if (fieldName.length() == 1) {
                            // ex: "getK" becomes "k"
                            fieldName = "" + Character.toLowerCase(fieldName.charAt(0));
                        } else {
                            // ex: "getSpace" becomes "space"
                            fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);
                        }

                        sbm.append("    if (\"").append(fieldName).append("\".equalsIgnoreCase(attrName)) {\n")
                                .append("      return ((").append(appPackageName + "." + xhSimpleName)
                                .append(")node).").append(method.getName()).append("();\n")
                                //.append(paramType.getName())
                                //.append(")val);\n")
                                //.append("      return true;\n")
                                .append("    }\n");

                        sbfnM.append(fieldName).append(",");
                        //}
                        //}
                    }
                } // end for
                if (sbm.length() > 0) {
                    sbt.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n").append(sbm.toString())
                            //.append("    return null;\n")
                            .append("  }\n");
                }
                if (sbfnM.length() > 0) {
                    sbfnT.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n").append("    names = \"")
                            .append(sbfnM.toString()).append("\";\n").append("  }\n");
                }
            }
        }

        StringBuilder sb = new StringBuilder(128).append(
                "public IXholon getAppSpecificObjectVal(IXholon node, Class<IXholon> clazz, String attrName) {\n")
                .append("  if (node == null) {return null;}\n").append(sbt.toString())
                .append("  Class superclass = clazz.getSuperclass();\n")
                .append("  if (superclass.getName().startsWith(\"").append(appPackageName).append("\")) {\n")
                .append("    return getAppSpecificObjectVal(node, superclass, attrName);\n").append("  }\n")
                .append("  return super.getAppSpecificObjectVal(node, clazz, attrName);\n").append("}\n");

        sb.append("\npublic String getAppSpecificObjectValNames(IXholon node, Class<IXholon> clazz) {\n")
                .append("  String names = \"\";\n").append("  if (node == null) {return null;}\n")
                .append(sbfnT.toString()).append("  Class superclass = clazz.getSuperclass();\n")
                .append("  if (superclass.getName().startsWith(\"").append(appPackageName).append("\")) {\n")
                .append("    names = names + getAppSpecificObjectValNames(node, superclass);\n").append("  }\n")
                .append("  else {\n")
                .append("    names = names + super.getAppSpecificObjectValNames(node, clazz);\n").append("  }\n")
                .append("  return names;\n").append("}\n");

        return sb.toString();
    } // end writeGetAppSpecificObjectVal()

    /* Write a setAppSpecificObjectVal() method.
     * Example:
    <code>
    public boolean setAppSpecificObjectVal(IXholon node, Class<IXholon> clazz, String attrName, IXholon val) {
    if (node == null) {return false;}
    else if ("org.primordion.user.app.testNodePorts.XhtestNodePorts".equals(clazz.getName())) {
      if ("three".equalsIgnoreCase(attrName)) {
        ((org.primordion.user.app.testNodePorts.XhtestNodePorts)node).setThree((IXholon)val);
        return true;
      }
    }
    Class superclass = clazz.getSuperclass();
    if (superclass.getName().startsWith("org.primordion.user.app.testNodePorts")) {
      return setAppSpecificObjectVal(node, superclass, attrName, val);
    }
    return super.setAppSpecificObjectVal(node, clazz, attrName, val);
    }
    </code>
    */
    protected String writeSetAppSpecificObjectVal(String appPackageName, String appSimpleName, JClassType[] types,
            JClassType iXholonType) {
        String xhSimpleName = null;

        StringBuilder sbt = new StringBuilder(128);
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            xhSimpleName = type.getName();

            if (!xhSimpleName.startsWith("App")) {
                // get all public setters with a single IXholon arg
                JMethod[] methods = type.getMethods();
                StringBuilder sbm = new StringBuilder(128);
                for (int j = 0; j < methods.length; j++) {
                    JMethod method = methods[j];
                    if (method.isPublic() && (method.getName().startsWith("set"))
                            && (method.getName().length() > 3)) {
                        JType[] paramTypes = method.getParameterTypes();
                        // the paramType can be any class or interface assignable to IXholon
                        if (paramTypes.length == 1) {
                            JClassType paramType = paramTypes[0].isClassOrInterface();
                            if ((paramType != null) && (paramType.isAssignableTo(iXholonType))) {

                                String fieldName = method.getName().substring(3);
                                if (fieldName.length() == 1) {
                                    // ex: "setK" becomes "k"
                                    fieldName = "" + Character.toLowerCase(fieldName.charAt(0));
                                } else {
                                    // ex: "setSpace" becomes "space"
                                    fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);
                                }

                                sbm.append("    if (\"").append(fieldName)
                                        .append("\".equalsIgnoreCase(attrName)) {\n").append("      ((")
                                        .append(appPackageName + "." + xhSimpleName).append(")node).")
                                        .append(method.getName()).append("((").append(paramType.getName())
                                        .append(")val);\n").append("      return true;\n").append("    }\n");
                            }
                        }
                    }
                }
                if (sbm.length() != 0) {
                    sbt.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n").append(sbm.toString())
                            //.append("    return false;\n")
                            .append("  }\n");
                }
            }
        }

        StringBuilder sb = new StringBuilder(128).append(
                "public boolean setAppSpecificObjectVal(IXholon node, Class<IXholon> clazz, String attrName, IXholon val) {\n")
                .append("  if (node == null) {return false;}\n").append(sbt.toString())
                .append("  Class superclass = clazz.getSuperclass();\n")
                .append("  if (superclass.getName().startsWith(\"").append(appPackageName).append("\")) {\n")
                .append("    return setAppSpecificObjectVal(node, superclass, attrName, val);\n").append("  }\n")
                .append("  return super.setAppSpecificObjectVal(node, clazz, attrName, val);\n").append("}\n");
        //System.out.println(sb.toString());
        return sb.toString();
    } // end writeSetAppSpecificObjectVal()

    /* Write a isAppSpecificClassFindable() method.
     * Example:
    <code>
    public boolean isAppSpecificClassFindable(String implName) {
    if ("org.primordion.user.app.PingPong.XhPingPong".equals(implName)) {
      return true;
    }
    return super.isAppSpecificClassFindable(implName);
    }
    </code>
    */
    protected String writeIsAppSpecificClassFindable(String appPackageName, String appSimpleName,
            JClassType[] types, JClassType iXholonType) {
        String xhSimpleName = null;

        StringBuilder sbt = new StringBuilder();
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if (type.isDefaultInstantiable() && (type.isAssignableTo(iXholonType))) {
                //we have determined that the IXholon class can be constructed using a simple new operation
                xhSimpleName = type.getName();
                if (!xhSimpleName.startsWith("App")) {
                    sbt.append("  if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(implName)) {\n").append("    return true;\n").append("  }\n");
                }
            }
        }

        StringBuilder sb = new StringBuilder()
                .append("public boolean isAppSpecificClassFindable(String implName) {\n").append(sbt.toString())
                .append("  return super.isAppSpecificClassFindable(implName);\n").append("}\n");
        //System.out.println(sb.toString());
        return sb.toString();
    }

    /* Write a getAppSpecificAttribute() method.
     * Example:
    <code>
    public Object getAppSpecificAttribute(IXholon node, Class<IXholon> clazz, String attrName) {
    if (node == null) {return null;}
    else if ("org.primordion.xholon.xmiapps.XhTestFsm".equals(clazz.getName())) {
      if ("ADouble".equals(attrName)) {return ((XhTestFsm)node).getADouble();}
      if ("AString".equals(attrName)) {return ((XhTestFsm)node).getAString();}
      if ("ABooleanTrue".equals(attrName)) {return ((XhTestFsm)node).getABooleanTrue();}
      if ("AChar".equals(attrName)) {return ((XhTestFsm)node).getAChar();}
      if ("AByte".equals(attrName)) {return ((XhTestFsm)node).getAByte();}
      if ("AShort".equals(attrName)) {return ((XhTestFsm)node).getAShort();}
      if ("AFloat".equals(attrName)) {return ((XhTestFsm)node).getAFloat();}
      if ("AnInteger".equals(attrName)) {return ((XhTestFsm)node).getAnInteger();}
      if ("MyInteger".equals(attrName)) {return ((XhTestFsm)node).getMyInteger();}
      if ("ABooleanFalse".equals(attrName)) {return ((XhTestFsm)node).getABooleanFalse();}
      if ("State".equals(attrName)) {return ((XhTestFsm)node).getState();}
    }
    Class superclass = clazz.getSuperclass();
    if ((superclass != null) && superclass.getName().startsWith("org.primordion.xholon.xmiapps")) {
      return getAppSpecificAttribute(node, superclass, attrName);
    }
    return null;
    }
    </code>
    */
    protected String writeGetAppSpecificAttribute(String appPackageName, JClassType[] types, JClassType iXholonType,
            TypeOracle oracle) {
        String xhSimpleName = null;
        StringBuilder sb = new StringBuilder();
        StringBuilder sbt = new StringBuilder();
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if ((type.isClass() != null) && (type.isAssignableTo(iXholonType))) {
                //we have determined that the type is an IXholon class (concrete or abstract, not an interface)
                xhSimpleName = type.getName();
                //if (!xhSimpleName.startsWith("App")) {
                // retrieve getter methods that don't return an IXholon
                JMethod[] methods = type.getMethods();
                StringBuilder sbm = new StringBuilder();
                for (int j = 0; j < methods.length; j++) {
                    JMethod m = methods[j];
                    String mName = m.getName();
                    JClassType rType = oracle.findType(m.getReturnType().getQualifiedSourceName());
                    // rType = null if oracle is given a primitive type (int etc.)
                    if (m.isPublic() && !m.isAbstract() && !m.isStatic() && (m.getParameterTypes().length == 0)
                            && ((rType == null) || !(rType.isAssignableTo(iXholonType)))
                            && (((mName.startsWith("get")) && (mName.length() > 3))
                                    || ((mName.startsWith("is")) && (mName.length() > 2)))) {
                        // ex: if ("ADouble".equals(attrName)) {return ((XhTestFsm)node).getADouble();}
                        sbm.append("    if (\"")
                                .append(mName.startsWith("get") ? mName.substring(3) : mName.substring(2))
                                .append("\".equalsIgnoreCase(attrName)) {return ((").append(xhSimpleName)
                                .append(")node).").append(mName).append("();}\n");
                    }
                } // end for(j
                if (sbm.length() > 0) {
                    sbt.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n")
                            //.append("    System.out.println(\"AppGenerator getAppSpecificAttribute2: \" + clazz.getName());\n")
                            .append(sbm.toString()).append("  }\n");
                }
                //} // end if App
            } // end if
        } // end for(i

        if (sbt.length() > 0) {
            sb.append(
                    "public Object getAppSpecificAttribute(IXholon node, Class<IXholon> clazz, String attrName) {\n")
                    //.append("  System.out.println(\"AppGenerator getAppSpecificAttribute1: \" + node + \" \" + clazz.getName() + \" \" + attrName);\n")
                    .append("  if (node == null) {return null;}\n").append(sbt.toString())
                    .append("  Class superclass = clazz.getSuperclass();\n")
                    .append("  if ((superclass != null) && superclass.getName().startsWith(\"")
                    .append(appPackageName).append("\")) {\n")
                    .append("    return getAppSpecificAttribute(node, superclass, attrName);\n").append("  }\n")
                    .append("  return super.getAppSpecificAttribute(node, clazz, attrName);\n").append("}\n");
            //System.out.println(sb.toString());
        }
        return sb.toString();
    }

    /* Write a setAppSpecificAttribute() method.
     * Example:
    <code>
    public void setAppSpecificAttribute(IXholon node, Class<IXholon> clazz, String attrName, Object attrVal) {
    try {
      if (node == null) {return;}
      else if ("org.primordion.xholon.xmiapps.XhTestFsm".equals(clazz.getName())) {
        if ("ADouble".equals(attrName)) {((XhTestFsm)node).setADouble(Double.parseDouble((String)attrVal));return;}
        if ("AString".equals(attrName)) {((XhTestFsm)node).setAString((String)attrVal);return;}
        if ("ABooleanTrue".equals(attrName)) {((XhTestFsm)node).setABooleanTrue(Boolean.parseBoolean((String)attrVal));return;}
        if ("AChar".equals(attrName)) {((XhTestFsm)node).setAChar(((String)attrVal).length() > 0 ? ((String)attrVal).charAt(0) : ' ');return;}
        if ("AByte".equals(attrName)) {((XhTestFsm)node).setAByte(Byte.parseByte((String)attrVal));return;}
        if ("AShort".equals(attrName)) {((XhTestFsm)node).setAShort(Short.parseShort((String)attrVal));return;}
        if ("AFloat".equals(attrName)) {((XhTestFsm)node).setAFloat(Float.parseFloat((String)attrVal));return;}
        if ("AnInteger".equals(attrName)) {((XhTestFsm)node).setAnInteger(Integer.parseInt((String)attrVal));return;}
        if ("MyInteger".equals(attrName)) {((XhTestFsm)node).setMyInteger(Integer.parseInt((String)attrVal));return;}
        if ("ABooleanFalse".equals(attrName)) {((XhTestFsm)node).setABooleanFalse(Boolean.parseBoolean((String)attrVal));return;}
        if ("State".equals(attrName)) {((XhTestFsm)node).setState(Integer.parseInt((String)attrVal));return;}
      }
    } catch(java.lang.NumberFormatException e) {return;}
    Class superclass = clazz.getSuperclass();
    if (superclass.getName().startsWith("org.primordion.xholon.xmiapps")) {
      setAppSpecificAttribute(node, superclass, attrName, attrVal);
    }
    }
    </code>
    */
    protected String writeSetAppSpecificAttribute(String appPackageName, JClassType[] types, JClassType iXholonType,
            TypeOracle oracle) {
        String xhSimpleName = null;
        StringBuilder sb = new StringBuilder();
        StringBuilder sbt = new StringBuilder();
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if ((type.isClass() != null) && (type.isAssignableTo(iXholonType))) {
                //we have determined that the type is an IXholon class (concrete or abstract, not an interface)
                xhSimpleName = type.getName();
                //if (!xhSimpleName.startsWith("App")) {
                // retrieve getter methods that don't return an IXholon
                JMethod[] methods = type.getMethods();
                StringBuilder sbm = new StringBuilder();
                for (int j = 0; j < methods.length; j++) {
                    JMethod m = methods[j];
                    String mName = m.getName();
                    JType[] paramTypes = m.getParameterTypes();
                    if (m.isPublic() && !m.isAbstract() && !m.isStatic() && (paramTypes.length == 1)
                            && (paramTypes[0].isArray() == null) && (mName.startsWith("set"))) {
                        JClassType paramType = paramTypes[0].isClassOrInterface();
                        if ((paramType != null) && (paramType.isAssignableTo(iXholonType))) {
                            // the param is an IXholon, so ignore it
                            continue;
                        }
                        // ex: if ("ADouble".equals(attrName)) {((XhTestFsm)node).setADouble(Double.parseDouble((String)attrVal));return;}
                        sbm.append("      if (\"").append(mName.substring(3))
                                .append("\".equalsIgnoreCase(attrName)) {((").append(xhSimpleName).append(")node).")
                                .append(mName).append("(");
                        String paramTypeStr = paramTypes[0].getQualifiedSourceName();
                        if (paramTypeStr == null) {
                        } else if (paramTypeStr.equals(JPrimitiveType.BOOLEAN.toString())
                                || (paramTypeStr.equals("java.lang.Boolean"))) {
                            sbm.append("Boolean.parseBoolean((String)attrVal));");
                        } else if (paramTypeStr.equals(JPrimitiveType.BYTE.toString())
                                || (paramTypeStr.equals("java.lang.Byte"))) {
                            sbm.append("Byte.parseByte((String)attrVal));");
                        } else if (paramTypeStr.equals(JPrimitiveType.CHAR.toString())
                                || (paramTypeStr.equals("java.lang.Character"))) {
                            sbm.append("((String)attrVal).length() > 0 ? ((String)attrVal).charAt(0) : ' ');");
                        } else if (paramTypeStr.equals(JPrimitiveType.DOUBLE.toString())
                                || (paramTypeStr.equals("java.lang.Double"))) {
                            sbm.append("Double.parseDouble((String)attrVal));");
                        } else if (paramTypeStr.equals(JPrimitiveType.FLOAT.toString())
                                || (paramTypeStr.equals("java.lang.Float"))) {
                            sbm.append("Float.parseFloat((String)attrVal));");
                        } else if (paramTypeStr.equals(JPrimitiveType.INT.toString())
                                || (paramTypeStr.equals("java.lang.Integer"))) {
                            sbm.append("Integer.parseInt((String)attrVal));");
                        } else if (paramTypeStr.equals(JPrimitiveType.LONG.toString())
                                || (paramTypeStr.equals("java.lang.Long"))) {
                            sbm.append("Long.parseLong((String)attrVal));");
                        } else if (paramTypeStr.equals(JPrimitiveType.SHORT.toString())
                                || (paramTypeStr.equals("java.lang.Boolean"))) {
                            sbm.append("Short.parseShort((String)attrVal));");
                        } else if (paramTypeStr.equals("java.lang.String")) {
                            sbm.append("(String)attrVal);");
                        } else if (paramTypeStr.equals("java.lang.Object")) {
                            // it's not clear what to do here
                            sbm.append("attrVal);");
                        } else {
                            // this shouldn't happen
                            sbm.append("null);");
                        }
                        sbm.append("return;}\n");
                    }
                } // end for(j
                if (sbm.length() > 0) {
                    sbt.append("    else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n").append(sbm.toString()).append("    }\n");
                }
                //} // end if APP
            } // end if
        } // end for(i

        if (sbt.length() > 0) {
            sb.append(
                    "public void setAppSpecificAttribute(IXholon node, Class<IXholon> clazz, String attrName, Object attrVal) {\n")
                    .append("  try {\n").append("    if (node == null) {return;}\n").append(sbt.toString())
                    .append("  } catch(java.lang.NumberFormatException e) {return;}\n")
                    .append("  Class superclass = clazz.getSuperclass();\n")
                    .append("  if (superclass.getName().startsWith(\"").append(appPackageName).append("\")) {\n")
                    .append("    setAppSpecificAttribute(node, superclass, attrName, attrVal); return;\n")
                    .append("  }\n").append("  super.setAppSpecificAttribute(node, clazz, attrName, attrVal);")
                    .append("}\n");
            //System.out.println(sb.toString());
        }
        return sb.toString();
    }

    /* Write a getAppSpecificAttributes() method.
     * Example:
    <code>
    public Object[][] getAppSpecificAttributes(IXholon node, Class<IXholon> clazz, boolean returnAll) {
    List names = new ArrayList();
    List values = new ArrayList();
    List types = new ArrayList();
    if (node == null) {return new Object[0][0];}
    else if ("org.primordion.xholon.xmiapps.XhTestFsm".equals(clazz.getName())) {
      names.add("ADouble");values.add(((XhTestFsm)node).getADouble());types.add(Double.class);
      names.add("AString");values.add(((XhTestFsm)node).getAString());types.add(String.class);
      names.add("ABooleanTrue");values.add(((XhTestFsm)node).getABooleanTrue());types.add(Boolean.class);
      names.add("AChar");values.add(((XhTestFsm)node).getAChar());types.add(Character.class);
      names.add("AByte");values.add(((XhTestFsm)node).getAByte());types.add(Byte.class);
      names.add("AShort");values.add(((XhTestFsm)node).getAShort());types.add(Short.class);
      names.add("AFloat");values.add(((XhTestFsm)node).getADouble());types.add(Float.class);
      names.add("AnInteger");values.add(((XhTestFsm)node).getAnInteger());types.add(Integer.class);
      names.add("MyInteger");values.add(((XhTestFsm)node).getMyInteger());types.add(Integer.class);
      names.add("ABooleanFalse");values.add(((XhTestFsm)node).getABooleanFalse());types.add(Boolean.class);
      names.add("State");values.add(((XhTestFsm)node).getState());types.add(Integer.class);
    }
    if (returnAll) {
      Class superclass = clazz.getSuperclass();
      if (superclass.getName().startsWith("org.primordion.xholon.xmiapps")) {
        Object[][] scAttrs = getAppSpecificAttributes(node, superclass, returnAll);
        if (scAttrs != null) {
    for (int i = 0; i < scAttrs.length; i++) {
      names.add(scAttrs[i][0]);values.add(scAttrs[i][1]);types.add(scAttrs[i][2]);
    }
        }
      }
    }
    Object attributes[][] = new Object[names.size()][3];
    for (int attrIx = 0; attrIx < names.size(); attrIx++) {
      attributes[attrIx][0] = names.get(attrIx);
      attributes[attrIx][1] = values.get(attrIx);
      attributes[attrIx][2] = types.get(attrIx);
    }
    return attributes;
    }
    </code>
    */
    protected String writeGetAppSpecificAttributes(String appPackageName, JClassType[] types,
            JClassType iXholonType, TypeOracle oracle) {
        String xhSimpleName = null;
        StringBuilder sb = new StringBuilder();
        StringBuilder sbt = new StringBuilder();
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if ((type.isClass() != null) && (type.isAssignableTo(iXholonType))) {
                //we have determined that the type is an IXholon class (concrete or abstract, not an interface)
                xhSimpleName = type.getName();
                //if (!xhSimpleName.startsWith("App")) {
                // retrieve getter methods that don't return an IXholon
                JMethod[] methods = type.getMethods();
                StringBuilder sbm = new StringBuilder();
                for (int j = 0; j < methods.length; j++) {
                    JMethod m = methods[j];
                    String mName = m.getName();
                    JClassType rType = oracle.findType(m.getReturnType().getQualifiedSourceName());
                    // rType = null if oracle is given a primitive type (int etc.)
                    if (m.isPublic() && !m.isAbstract() && !m.isStatic() && (m.getParameterTypes().length == 0)
                            && ((rType == null) || !(rType.isAssignableTo(iXholonType)))
                            && ((mName.startsWith("get")) || (mName.startsWith("is")))) {
                        // ex: names.add("ADouble");values.add(((XhTestFsm)node).getADouble());types.add(Double.class);
                        sbm.append("    names.add(\"")
                                .append(mName.startsWith("get") ? mName.substring(3) : mName.substring(2))
                                .append("\");values.add(((").append(xhSimpleName).append(")node).").append(mName)
                                .append("());types.add(");
                        sbm.append(m.getReturnType().getQualifiedSourceName());
                        sbm.append(".class);\n");
                    }
                } // end for(j
                if (sbm.length() > 0) {
                    sbt.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n").append(sbm.toString()).append("  }\n");
                }
                //} // end if "App"
            } // end if
        } // end for(i

        if (sbt.length() > 0) {
            sb.append(
                    "public Object[][] getAppSpecificAttributes(IXholon node, Class<IXholon> clazz, boolean returnAll) {\n")
                    .append("  List names = new ArrayList();\n").append("  List values = new ArrayList();\n")
                    .append("  List types = new ArrayList();\n")
                    .append("  if (node == null) {return new Object[0][0];}\n").append(sbt.toString())
                    .append("  if (returnAll) {\n").append("    Class superclass = clazz.getSuperclass();\n")
                    .append("    if (superclass.getName().startsWith(\"").append(appPackageName).append("\")) {\n")
                    .append("      Object[][] scAttrs = getAppSpecificAttributes(node, superclass, returnAll);\n")
                    .append("      if (scAttrs != null) {\n")
                    .append("        for (int i = 0; i < scAttrs.length; i++) {\n")
                    .append("          names.add(scAttrs[i][0]);values.add(scAttrs[i][1]);types.add(scAttrs[i][2]);\n")
                    .append("        }\n").append("      }\n").append("    }\n")

                    .append("    else {\n")
                    .append("      Object[][] scAttrs = super.getAppSpecificAttributes(node, clazz, returnAll);\n")
                    .append("      if (scAttrs != null) {\n")
                    .append("        for (int i = 0; i < scAttrs.length; i++) {\n")
                    .append("          names.add(scAttrs[i][0]);values.add(scAttrs[i][1]);types.add(scAttrs[i][2]);\n")
                    .append("        }\n").append("      }\n").append("    }\n")

                    .append("  }\n").append("  Object attributes[][] = new Object[names.size()][3];\n")
                    .append("  for (int attrIx = 0; attrIx < names.size(); attrIx++) {\n")
                    .append("    attributes[attrIx][0] = names.get(attrIx);\n")
                    .append("    attributes[attrIx][1] = values.get(attrIx);\n")
                    .append("    attributes[attrIx][2] = types.get(attrIx);\n").append("  }\n")
                    .append("  return attributes;\n").append("}\n");
            //System.out.println(sb.toString());
        }
        return sb.toString();
    }

    /**
     * Write an isAppSpecificAttribute() method.
     */
    protected String writeIsAppSpecificAttribute(String appPackageName, JClassType[] types, JClassType iXholonType,
            TypeOracle oracle) {
        String xhSimpleName = null;
        StringBuilder sb = new StringBuilder();
        StringBuilder sbt = new StringBuilder();
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            if ((type.isClass() != null) && (type.isAssignableTo(iXholonType))) {
                //we have determined that the type is an IXholon class (concrete or abstract, not an interface)
                xhSimpleName = type.getName();
                //if (!xhSimpleName.startsWith("App")) {
                // retrieve getter methods that don't return an IXholon
                JMethod[] methods = type.getMethods();
                StringBuilder sbm = new StringBuilder();
                for (int j = 0; j < methods.length; j++) {
                    JMethod m = methods[j];
                    String mName = m.getName();
                    JType[] paramTypes = m.getParameterTypes();
                    if (m.isPublic() && !m.isAbstract() && !m.isStatic() && (paramTypes.length == 1)
                            && (paramTypes[0].isArray() == null) && (mName.startsWith("set"))) {
                        JClassType paramType = paramTypes[0].isClassOrInterface();
                        if ((paramType != null) && (paramType.isAssignableTo(iXholonType))) {
                            // the param is an IXholon, so ignore it
                            continue;
                        }
                        // ex: if ("ADouble".equals(attrName)) {return true;}
                        sbm.append("    if (\"").append(mName.substring(3))
                                .append("\".equalsIgnoreCase(attrName)) {return true;}\n");
                    }
                } // end for(j
                if (sbm.length() > 0) {
                    sbt.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n").append(sbm.toString()).append("  }\n");
                }
                //} // end if App
            } // end if
        } // end for(i

        if (sbt.length() > 0) {
            sb.append(
                    "public boolean isAppSpecificAttribute(IXholon node, Class<IXholon> clazz, String attrName) {\n")
                    .append("  if (node == null) {return false;}\n").append(sbt.toString())
                    .append("  Class superclass = clazz.getSuperclass();\n")
                    .append("  if ((superclass != null) && superclass.getName().startsWith(\"")
                    .append(appPackageName).append("\")) {\n")
                    .append("    return isAppSpecificAttribute(node, superclass, attrName);\n").append("  }\n")
                    .append("  return false;\n").append("}\n");
            //System.out.println(sb.toString());
        }
        return sb.toString();
    }

    /* Write a setAppSpecificObjectArrayVal() method.
     * Don't write this method if there are no port arrays.
     * Example:
    <code>
    public boolean setAppSpecificObjectArrayVal(IXholon node, Class<IXholon> clazz, String attrName, int index, IXholon val) {
    if (node == null) {return false;}
    else if ("org.primordion.user.app.testNodePorts.XhtestNodePorts".equals(clazz.getName())) {
      if ("ff".equalsIgnoreCase(attrName)) {
        //if (index >= 2) {return false;}
        ((org.primordion.user.app.testNodePorts.XhtestNodePorts)node).setFf(index, (IXholon)val);
        return true;
      }
    }
    return false;
    }
    </code>
    */
    protected String writeSetAppSpecificObjectArrayVal(String appPackageName, String appSimpleName,
            JClassType[] types, JClassType iXholonType) {
        String xhSimpleName = null;

        StringBuilder sbt = new StringBuilder(128);
        for (int i = 0; i < types.length; i++) {
            JClassType type = types[i];
            xhSimpleName = type.getName();

            if (!xhSimpleName.startsWith("App")) {
                // get all public setters with a single IXholon arg
                JMethod[] methods = type.getMethods();
                StringBuilder sbm = new StringBuilder(128);
                for (int j = 0; j < methods.length; j++) {
                    JMethod method = methods[j];
                    if (method.isPublic() && (method.getName().startsWith("set"))
                            && (method.getName().length() > 3)) {
                        JType[] paramTypes = method.getParameterTypes();
                        // the paramType can be any class or interface assignable to IXholon
                        // ex: public void setFf(int index, IXholon reffedNode) {ff[index] = reffedNode;}
                        // TODO also check if first param is an int ?
                        if (paramTypes.length == 2) {
                            JClassType paramType = paramTypes[1].isClassOrInterface();
                            if ((paramType != null) && (paramType.isAssignableTo(iXholonType))) {

                                String fieldName = method.getName().substring(3);
                                if (fieldName.length() == 1) {
                                    // ex: "setK" becomes "k"
                                    fieldName = "" + Character.toLowerCase(fieldName.charAt(0));
                                } else {
                                    // ex: "setSpace" becomes "space"
                                    fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);
                                }

                                sbm.append("    if (\"").append(fieldName)
                                        .append("\".equalsIgnoreCase(attrName)) {\n").append("      ((")
                                        .append(appPackageName + "." + xhSimpleName).append(")node).")
                                        .append(method.getName()).append("(index, (").append(paramType.getName())
                                        .append(")val);\n").append("      return true;\n").append("    }\n");
                            }
                        }
                    }
                }
                if (sbm.length() != 0) {
                    sbt.append("  else if (\"").append(appPackageName + "." + xhSimpleName)
                            .append("\".equals(clazz.getName())) {\n").append(sbm.toString()).append("  }\n");
                }
            }
        }
        if (sbt.length() == 0) {
            return "";
        }

        StringBuilder sb = new StringBuilder(128).append(
                "public boolean setAppSpecificObjectArrayVal(IXholon node, Class<IXholon> clazz, String attrName, int index, IXholon val) {\n")
                .append("  if (node == null) {return false;}\n").append(sbt.toString()).append("  return false;\n")
                .append("}\n");
        return sb.toString();
    } // end writeSetAppSpecificObjectArrayVal()

}