VisitorClass {

  // method for converting the Class information into 
  // string, then parsing that into a parse tree.

  public static VisitorClass fromClass(Class visitorClass) {{

    VisitorClass vc = VisitorClass.parse(class2String(visitorClass));     
    return vc;

  }}
  
  public static String class2String(Class c) {{
    String returnString = "";
    
    returnString += "class " + c.getName() + " {\n";
    Method [] m = c.getDeclaredMethods();
    returnString += getMethodString(m) + "}\n";
    
    return returnString;
    
  }}
  
  public static String getMethodString(Method [] m) {{
    String returnString = "";
    
    for(int x=0; x<m.length; x++) {
      String methodName = m[x].getName();
      if (methodName.compareTo("start")==0
        ||methodName.compareTo("finish")==0
        ||methodName.compareTo("before")==0
        ||methodName.compareTo("after")==0
        ||methodName.compareTo("around")==0
        ||methodName.compareTo("returnValue")==0) {
        
        returnString += getMethodString(m[x]) + "\n";
      }   
    }
    
    return returnString;
  }}
  public static String getMethodString(Method m) {{
    
    String returnString = "";
    
    returnString += m.getReturnType().getName() + " ";
    returnString += m.getName() + "(";
    Class [] params = m.getParameterTypes();
    
    if (params.length > 0)
      returnString += params[0].getName();
    for (int x = 1; x < params.length; x++) {
      returnString += "," + params[x].getName();
    }

    returnString += ");";
    
    return returnString;
    
  }}
  
  
  public String toAspectJCode(String tname, String vname, String className) {{
    
    // generate the variable declaration given the traversal name and
    // visitor name. 
    String staticVisitorVarDecl = genVisitorVarDecl(tname, vname);
    
    // generate the wrapper for the traversal
    String traversalWrapper = genTraversalWrapper(tname, vname, className);
    
    // generate the advices for all corresponding advices within
    // the visitor
    String aspectjAdvices = genAdvices(tname, vname);
    
    String returnVal = staticVisitorVarDecl + "\n"
      + traversalWrapper 
      + aspectjAdvices;
    
    return returnVal;
    
  }}
  
  
  protected static String genVisitorVarDecl(String tname, String vname) {{
    
    String visitorVarName = genVisitorVar(tname);
    
    String returnVal = "  static " + vname + " " + visitorVarName + ";";

    return returnVal;
  }}

  protected static String genVisitorVar(String tname) {{
    String returnVal = tname + "_visitor";
    return returnVal;
  }}

  protected String genTraversalWrapper(String tname, String vname, String className) {{
    String methodDecl = "  public void " + className + "." + tname 
      + "(" + vname + " v) {\n";

    String vvar = VisitorClass.genVisitorVar(tname);
    String methodBody = "  " + vvar + "=v;\n";
    
    if (visitorHasStartMethod()) {
      methodBody += "  " + vvar + ".start();\n";
    }
    
    methodBody += "  " + tname + "();\n";
    
    if (visitorHasFinishMethod()) {
      methodBody += "  " + vvar + ".finish();\n";
    }
    
    String returnVal = methodDecl + methodBody + "  }\n";

    return returnVal;     
  }}

  protected boolean visitorHasStartMethod() to MethodName {
    {{ boolean returnVal = false; }}

    before MethodName {{
      String name = host.get_ident().toString();
      if (name.compareTo("start") == 0)
        returnVal = true;
    }}

    return boolean {{ returnVal }}

  }

  protected boolean visitorHasFinishMethod() to MethodName {
    {{ boolean returnVal = false; }}

    before MethodName {{
      String name = host.get_ident().toString();
      if (name.compareTo("finish") == 0)
        returnVal = true;
    }}

    return boolean {{ returnVal }}
  }

  public String getReturnType() to VisitorMethod {
    {{ String returnVal = "void"; }}

    before VisitorMethod {{
      String returnType = host.get_returntype().get_ident().toString();
      String methodName = host.get_methodname().get_ident().toString();
      
      if (methodName.compareTo("returnValue") == 0) {
        returnVal = returnType;
      }

    }}

    return String {{ returnVal }}
  }

  protected String genAdvices(String tname, String vname) to VisitorMethod {
    {{ String returnVal = ""; }}
    {{ String visitorVar = ""; }}

    return String {{ returnVal }}

    before VisitorClass {{
      visitorVar = VisitorClass.genVisitorVar(tname);
    }}
      
    before VisitorMethod {{
      String methodString = "";
      String methodHeader = "";
      String methodFooter = "";
      
      String returnType = host.get_returntype().get_ident().toString();
      String methodName = host.get_methodname().get_ident().toString();
      List argList = host.getArgumentList();
      
      String firstArgType = "";
      
      if (argList.size() > 0) {
        firstArgType= (String) argList.get(0);
      }

      if (methodName.compareTo("before") == 0
        ||methodName.compareTo("after") == 0) {

        methodHeader = "  " + methodName + "(" + firstArgType + " host) : "
          + "call(public void " + tname + "*()) && target(host) {\n";
        methodString = "  " + visitorVar + "." + methodName + "(host);\n";

        methodFooter = "  }\n";
      } else if (methodName.compareTo("around") == 0) {

        methodHeader = "  " + returnType + " " + methodName + "(" + firstArgType + " host) : "
          + "call(public void " + tname + "*()) && target(host) {\n";
        
        methodString = "  return " + visitorVar + "." + methodName + "(host);\n";
        methodFooter = "  }\n";

      } else if (!(methodName.compareTo("start") == 0 
             ||methodName.compareTo("finish") == 0
             ||methodName.compareTo("returnValue")==0)){

        System.out.println("%Warning: unknown method type and skipping advice generation. " );
        PrintVisitor pv = new PrintVisitor(System.out);
        host.universal_trv0(pv);
        pv.finish();
        System.out.println();
      }
      
      returnVal += methodHeader + methodString + methodFooter;
      
    }}
    
  }  
  
} // VisitorClass

VisitorMethod {
  
  public List getArgumentList() to Argument {
    {{ LinkedList returnVal = new LinkedList(); }}

    return List {{ returnVal }}

    before Argument {{
      returnVal.add(host.get_ident().toString());
    }}
  }
}