import java.lang.reflect.*;
import java.util.*;
import java.io.*;

import org.apache.bcel.Repository;
import org.apache.bcel.classfile.*;

import com.techtrader.modules.tools.bytecode.*;


public class GetParamNamesBench3 {
  public static void main(String[] args) throws Exception {
    Class cls;
    java.lang.reflect.Method[] methods;
    Date d1, d2;
    String[] result;
    int time, i;
    HashMap ttClassCache;
    BCClass bclass;
    boolean printout;
    
    if(args.length != 2) {
      System.out.println("Usage: GetParamNamesBench3 className printout(true|false)");
      System.exit(0);
    }
    
    printout = new Boolean(args[1]).booleanValue();
    
    cls = Class.forName(args[0]);
    methods = cls.getMethods();
    System.out.println("Number of methods: " + methods.length);
    
    // bcel
    d1 = new Date();
    for(i = 0; i < methods.length; i++) {
      result = executeBcel(methods[i]);
      if(printout)
        printResult(methods[i], result);
    }
    d2 = new Date();
    System.out.println("bcel: " + (d2.getTime() - d1.getTime()) + "ms");
    
    if(printout)
      System.out.println();
    
    // tt-bytecode
    ttClassCache = new HashMap();
    d1 = new Date();
    for(i = 0; i < methods.length; i++) {
      cls = methods[i].getDeclaringClass();
      bclass = (BCClass)ttClassCache.get(cls);
      if(bclass == null) {
        try {
          bclass = new BCClass(cls);
          ttClassCache.put(cls, bclass);
        } catch (IOException e) {
          if(printout)
            printResult(methods[i], null);
          continue;
        }
      }
      
      result = executeTTbytecode(bclass, methods[i]);
      if(printout)
        printResult(methods[i], result);
    }
    d2 = new Date();
    System.out.println("tt-bytecode: " + (d2.getTime() - d1.getTime()) + "ms");
    
  }
  
  private static String[] executeBcel(java.lang.reflect.Method reflectMethod) throws Exception {
    Class cls;
    JavaClass javaClass;
    org.apache.bcel.classfile.Method [] methods;
    org.apache.bcel.classfile.Method method;
    LocalVariableTable localVarTable;
    org.apache.bcel.classfile.LocalVariable[] localVars;
    org.apache.bcel.classfile.LocalVariable localVar;
    String[] result = null;
    int i, j, numParams, k;
    Vector temp;
    
    numParams = reflectMethod.getParameterTypes().length;
    if (numParams == 0)
      return null;
    
    cls = reflectMethod.getDeclaringClass();
    javaClass = Repository.lookupClass(cls.getName());
    if(javaClass == null)
      throw new Exception("bcel could not load class!");
    
    methods = javaClass.getMethods();
    
    for(i = 0; i < methods.length; i++) {
      method = methods[i];
      
      if(method.getName().equals(reflectMethod.getName())) {
        localVarTable = method.getLocalVariableTable();
        
        if(localVarTable != null) {
          localVars = localVarTable.getLocalVariableTable();
          
          result = new String[numParams + 1];
          temp = new Vector();
          
          for(j = 0; j < localVars.length; j++) {
            localVar = localVars[j];
            //            System.out.println(reflectMethod.getName() + "/" + localVar.getIndex() + "/" + localVar.getName() + "/" + localVars.length + "/" + localVarTable.getTableLength());
            if(! localVar.getName().equals("this")) {
              if(temp.size() < localVar.getIndex() + 1)
                temp.setSize(localVar.getIndex() + 1);
              temp.setElementAt(localVar.getName(), localVar.getIndex());
            }
          }
          k = 0;
          for(j = 0; j < temp.size(); j++) {
            if(temp.elementAt(j) != null) {
              k++;
              result[k] = (String)temp.elementAt(j);
              if(k + 1 == result.length)
                break;
            }
          }
        }
      }
    }
    return(result);
  } // executeBcel
  
  private static String[] executeTTbytecode(BCClass bclass, java.lang.reflect.Method reflectMethod) throws Exception {
    Class cls;
    BCMethod bmeth;
    //    BCClass bclass;
    com.techtrader.modules.tools.bytecode.Code code;
    LocalVariableTableAttribute attr;
    com.techtrader.modules.tools.bytecode.LocalVariable[] vars;
    com.techtrader.modules.tools.bytecode.LocalVariable var;
    String[] result = null;
    int numParams, i, j, k;
    Vector temp;
    
    numParams = reflectMethod.getParameterTypes().length;
    if(numParams == 0)
      return null;
    
    //    cls = reflectMethod.getDeclaringClass();
    //
    //    try {
    //      bclass = new BCClass(cls);
    //    } catch (IOException e) {
    //      return null;
    //    }
    
    bmeth = bclass.getMethod(reflectMethod.getName(), reflectMethod.getParameterTypes());
    if (bmeth == null)
      return null;
    
    code = bmeth.getCode();
    if (code == null)
      return null;
    
    attr = (LocalVariableTableAttribute)code.getAttribute(Constants.ATTR_LOCALS);
    if (attr == null)
      return null;
    
    vars = attr.getLocalVariables();
    
    result = new String[numParams + 1];
    
    
    //    for (i = 0; i < vars.length; i++) {
    //      var = vars[i];
    //      if (var.getIndex() <= numParams) {
    //        if (var.getName().equals("this"))
    //          continue;
    //        result[var.getIndex()] = var.getName();
    //      }
    //    }
    
    temp = new Vector();
    
    for(j = 0; j < vars.length; j++) {
      var = vars[j];
      if(! var.getName().equals("this")) {
        if(temp.size() < var.getIndex() + 1)
          temp.setSize(var.getIndex() + 1);
        temp.setElementAt(var.getName(), var.getIndex());
      }
    }
    k = 0;
    for(j = 0; j < temp.size(); j++) {
      if(temp.elementAt(j) != null) {
        k++;
        result[k] = (String)temp.elementAt(j);
        if(k + 1 == result.length)
          break;
      }
    }
    return(result);
  } // executeTTbytecode
  
  private static void printResult(java.lang.reflect.Method method, String[] result) {
    int j;
    boolean first;
    
    System.out.print(method.getName() + "(");
    if(result != null) {
      first = true;
      for(j = 1; j < result.length; j++) {
        if(first)
          first = false;
        else
          System.out.print(", ");
        System.out.print(result[j]);
      }
    }
    System.out.println(")");
  } // printResult
} // GetParamNamesBench
