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

/**
 * Program to exemplify reflection.
 * 
 * @author David Matuszek 
 * @version November 21, 2002
 */
public class Spy extends FooBar implements Cloneable, Serializable {
    private static final long serialVersionUID = 1L;
    public Class thisClass;
    public String className;
    public Field[] fields;
    public Method[] methods;
    public boolean atBeginningOfLine = true;
    public int level = 0;
    public String aaaaa = "Not used, really.";
    int packageInt;
    
    private Spy(Class c) {
        thisClass = c;
        className = c.getName();
    }
    
    private Spy(String className) throws ClassNotFoundException {
        this.className = className;
        thisClass = Class.forName(className);
    }

    static Spy getSpy(Class c) {
        return new Spy(c);
    }
    
    static Spy getSpy(String className) {
        try {
            return new Spy(className);
        }
        catch (ClassNotFoundException e) {
            return null;
        }
    }
    
    public static void main(String args[]) {
        Spy thisClassPrinter = getSpy("Spy");
        thisClassPrinter.printAll(args);
    }
    
    void printAll(String[] args) {
        System.out.println("----------------------------");
        if (args.length == 0) {
            className = "Spy";
            thisClass = this.getClass();
            printClass();
        }
        else {
            assert args.length > 0;
            for (int i = 0; i < args.length; i++) {
                try {
                    className = args[i];
                    thisClass = Class.forName(className);
                    printClass(args[i]);
                } catch(ClassNotFoundException e) {
                    level = 0;
                    write("\nClass not found: " + args[i]);
                }
            }
        }
    }
        
    public void write(String s) {
        if (atBeginningOfLine) {
            for (int i = 0; i < level % 10; i++) {
                System.out.print("   ");
            }
            atBeginningOfLine = false;
        }
        System.out.print(s);
    }
    
    public void writeln(String s) {
        write(s);
        System.out.println();
        atBeginningOfLine = true;
    }
    
    public void indent() { level++; }
    
    public void exdent() { level--; }
    
    void printClass() {
        printClass(thisClass);
    }

    public void printClass(String className) throws ClassNotFoundException {
        printClass(Class.forName(className));
    }
    
    public void printClass(Class c) {
        printClassHeader(c);
        printFields(c);
        printMethods(c);
        printClassCloser();
    }

    private void printMethods(Class c) throws SecurityException {
        methods = c.getMethods();
        writeln("// Methods:");
        indent();
        for (int i = 0; i < methods.length; i++) {
            if (c.equals(methods[i].getDeclaringClass())) {
                printMethod(methods[i]);
            }
        }
        exdent();
    }
    
    void printClassHeader(Class c) {
        Class sc = c.getSuperclass();
        int classModifiers = c.getModifiers();
        
        write(Modifier.toString(classModifiers));
        write(" class ");
        write(c.getName());
        if (sc.getName() != "java.lang.Object") {
            write(" extends " + sc.getName());
        }
        Class[] interfaces = c.getInterfaces();
        if (interfaces.length > 0) {
            write(" implements");
            for (int i = 0; i < interfaces.length; i++) {
                write(i == 0 ? " " : ", ");
                write(interfaces[i].getName());
            }
        }
        writeln(" {");
        indent();
    }

    private void printClassCloser() {
        exdent();
        writeln("}");
    }
    
    void printFields(Class c) {
        fields = c.getFields();
        writeln("// Fields:");
        indent();
        for (int i = 0; i < fields.length; i++) {
            printField(fields[i]);
        }
        exdent();
    }
    
    public void printField(Field field) {
        int modifiers = field.getModifiers();
        write(Modifier.toString(modifiers) + " ");
        write(decodeType(field.getType().getName()));
        write(" " + field.getName() + ";");
        String declaredIn = field.getDeclaringClass().getName();
        if (!className.equals(declaredIn)) {
            write("  // inherited from " + declaredIn);
        }
        writeln("");
   }
    
    public String decodeType(String typeCode) {
        int arrayLevel = 0;
        String brackets = "";
        while (typeCode.charAt(arrayLevel) == '[') {
            arrayLevel++;
            brackets += "[]";
        }
        switch (typeCode.charAt(arrayLevel)) {
            case 'B': return "byte" + brackets;
            case 'C': return "char" + brackets;
            case 'D': return "double" + brackets;
            case 'F': return "float" + brackets;
            case 'I': return "int" + brackets;
            case 'J': return "long" + brackets;
            case 'S': return "short" + brackets;
            case 'Z': return "boolean" + brackets;
            case 'V': return "void" + brackets;
            case 'L':
                int last = typeCode.lastIndexOf(".") + 1;
                String type = simplifyName(typeCode);
                return type + brackets;
            default: return typeCode + brackets;
        }
    }
    
    
    public static void printInfo(Field field) {
        System.out.println("    " + field.getName());
int modifiers = field.getModifiers();
        System.out.println("        Modifiers: " + modifiers);
        System.out.println("        Type:      " + field.getType().getName());
    }
    
    public void printMethod(Method method) {
        int modifiers = method.getModifiers();
        write(Modifier.toString(modifiers) + " ");
        write(decodeType(method.getReturnType().getName()));
        write(" " + method.getName() + "(");
        printParameters(method.getParameterTypes());
        writeln(") { }");
    }
    
    public void printParameters(Class[] parameters) {
        if (parameters.length == 0) return;
        write(decodeType(parameters[0].getName()));
        for (int i = 1; i < parameters.length; i++) {
            write(", " + simplifyName(decodeType(parameters[i].getName())));
            write("[" + decodeType(parameters[i].getName()) + "]");
        }
    }
    
    public String simplifyName(String name) {
        int last = name.lastIndexOf(".") + 1;
        if (last < 0) return name;
        return name.substring(last, name.length() - 1);
    }
    
    public static String myGetType(Object obj) {
        return "Not implemented.";
    }
    
    public void aTwoParameterMethod(int foo, Class bar) {}
}