/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

/**
 * Generates a shell of JUnit test code for testing a particular class.
 * <p>
 * The test generator can be run interactively or from the command line.
 * <p>
 * The command line takes a file name: 
 * <pre>     java TestGenerator filelist.txt</pre>
 * The file consists of a list of fully qualified class names 
 * (for example "kahuna.resource.ResourceFactory"), 
 * one class name per line, and produces a test file for each class.
 * <p>
 * To run interactively, omit the file parameter:
 * <pre>     java TestGenerator</pre>
 * You can enter the fully qualified class name,
 * save the resulting test file, 
 * and generate tests for package methods as well as public methods.
 *
 * @version $Revision: 1.1 $, $Date: 2006/03/29 22:04:05 $
 * @author Gordon R. Durand
 */
public class TestGenerator {
    JFrame       frame;
    JTextField   classNameText;
    JCheckBox    includePkgCheck;
    JTextArea    generatedText;
    TestGenClass genClass = null;


    public static void main(String[] args) {
        if (args.length > 0) {
            new TestGenerator(args[0]);
            return;
        }
        new TestGenerator();
    }

    public TestGenerator(String fileName) {
        classNameText = new JTextField("");
        includePkgCheck = new JCheckBox("package", true);

        try {
            BufferedReader br = new BufferedReader(new FileReader(fileName));
    
            File testFile; 
            String className;
            while((className = br.readLine()) != null) {
                classNameText.setText(className);
                getNewGenClass();
                if (genClass != null) {
                    testFile = new File(genClass.getSuggestedFileName());
                    System.out.println("writing " + testFile);
                    writeFile(testFile);
                }
            }
        } catch (Exception e) {
            e.printStackTrace(); // XXX -- ought to do better than this
        }
        System.exit(0);
    }

    public TestGenerator() {
        frame = new JFrame("Test Generator");
        generatedText = new JTextArea();
        generatedText.setFont(new Font("Monospaced", Font.PLAIN, 12));
        generatedText.setMargin(new Insets(5, 5, 5, 5));

        JPanel input = new JPanel(new FlowLayout());
        input.add(new JLabel("Class:"));
        classNameText = new JTextField("java.lang.String", 30);
        input.add(classNameText);
        JButton saveButton = new JButton("Save As...");
        input.add(saveButton);
        includePkgCheck = new JCheckBox("Test Package-Level Methods");
        input.add(includePkgCheck);

        frame.getContentPane().add(input, BorderLayout.NORTH);
        frame.getContentPane().add(new JScrollPane(generatedText));

        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent event) {
                System.exit(0);
            } 
        });

        classNameText.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                generatedText.setText(null);
                getNewGenClass();
                if (genClass != null) {
                    generatedText.setText(genClass.getGeneratedCode());
                }
            }
        });

        includePkgCheck.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                generatedText.setText(null);
                getNewGenClass();
                if (genClass != null) {
                    generatedText.setText(genClass.getGeneratedCode());
                }
            }
        });

        saveButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                saveAs();
            }
        });

        getNewGenClass();
        if (genClass != null) {
            generatedText.setText(genClass.getGeneratedCode());
        }

        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();

        frame.setBounds(200, 200, screen.width - 400, screen.height - 400);
        frame.setVisible(true);
    }

    private void getNewGenClass() {
        genClass = null;
        Class c = null;
        
        try {
            c = Class.forName(classNameText.getText());
        } catch (ClassNotFoundException e) {
            // do nothing
        }

        if (c != null) {
            genClass = new TestGenClass(c);
        }
    }
 
    private void saveAs() {
        final JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));

        chooser.setFileFilter(new FileFilter() {
            public boolean accept(File f) {
                if (f.isDirectory()) {
                    return true;
                } 
                String path = f.getPath().toLowerCase();
                if (path.endsWith(".java")) {
                    return true;
                } 
                return false;
            } 
            public String getDescription() {
                return "Java files (*.java)";
            } 
        });

        chooser.setSelectedFile(new File(genClass.getSuggestedFileName()));

        if(chooser.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
            File f = chooser.getSelectedFile();
            writeFile(f);
        }
    }

    private void writeFile(File f) {
        if (genClass == null) {
            System.out.println("No class");
        }

        try {
            FileWriter writer = new FileWriter(f);

            writer.write(genClass.getGeneratedCode());

            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }

    public class TestGenClass {
        private final String IN = "    "; // indent

        private String suggestedFileName;
        private String testClassVar = null;
        private String generatedCode = null;
    
        public TestGenClass(Class cl) {
            suggestedFileName = stripPackage(cl.getName()) + "Test.java";
            generatedCode = generateCode(cl);
        }

        public String getGeneratedCode() {
            return generatedCode;
        }
    
        public String getSuggestedFileName() {
            return suggestedFileName;
        } 
    
        private String generateCode(Class cl) {
            String oldSeparator = setSeparatorProperty("\n");  // fix PrintWriter

            StringWriter stringWriter = new StringWriter();
            PrintWriter code = new PrintWriter(stringWriter);
    
            generateBoilerplate(code);
            generateClassCode(code, cl);
            generateTestBedCode(code, cl);
            generateTestMethods(code, cl);
            generateEndPlate(code);
    
            setSeparatorProperty(oldSeparator);

            return stringWriter.toString();
        }

        private String setSeparatorProperty(String lineSep) {
            final String KEY = "line.separator";
            Properties props = System.getProperties();
            String oldSep = props.getProperty(KEY);
            props.setProperty(KEY, lineSep);
            return oldSep;
        }
    
        private void generateBoilerplate(PrintWriter code) {
            code.println("/**");
            code.println(" * File: \u0024RCSfile: \u0024");
            code.println(" * Version: \u0024Revision: \u0024, \u0024Date: \u0024");
            code.println(" *");
            code.println(" * Copyright 2006 Activant Solutions Inc.");
            code.println(" */");
            code.println();
        }
    
        private void generateEndPlate(PrintWriter code) {
            code.println("}");
        }
    
        private void generateClassCode(PrintWriter code, Class cl) {
            String testClassName   = stripPackage(cl.getName()) + "Test";
            Package pkg = cl.getPackage();
            
            if (pkg != null) {
                String testPackageName = pkg.getName();
    
                code.println("package " + testPackageName + ";");
                code.println();
            }
    
            code.println("import junit.framework.*;");
            code.println();
            code.println("public class " + testClassName + " extends TestCase {");
            code.println();
            code.println(IN + "public " + testClassName + "(String name) {");
            code.println(IN + IN + "super(name);");
            code.println(IN + "}");
            code.println();
        }    
    
    
        private void generateTestBedCode(PrintWriter code, Class cl) {
            String testClassType = stripPackage(cl.getName());
            testClassVar = makeUpVarName(testClassType);
            Constructor[] constrs = cl.getDeclaredConstructors();
    
            code.println(IN + testClassType + " " + testClassVar + ";");
            code.println();
            code.println(IN + "protected void setUp() throws Exception {");
    
            for (int i = 0; i < constrs.length; i++) {
                if (i > 0) {
                    code.println();
                }

                String parms = generateParameters(code, constrs[i].getParameterTypes());
    
                code.println(IN + IN + testClassVar + " = new " + testClassType + "(" + parms + ");");
            } 
    
            code.println(IN + "}");
            code.println();
            code.println(IN + "protected void tearDown() throws Exception {");
            code.println(IN + IN + testClassVar + " = null;");
            code.println(IN + "}");
            code.println();
        }
     
        private String generateParameters(PrintWriter code, Class[] parms) {
            String parmStr = "";
    
            for (int i = 0; i < parms.length; i++) {
                if (i > 0) {
                    parmStr += ", ";
                } 
    
                String typeName = getTypeName(parms[i]);
                String varName  = makeUpVarName(typeName);
    
                code.print(IN + IN);
                generateParmDecl(code, typeName, varName);
    
                parmStr += varName;
            }
     
            if (parms.length > 0) {
                code.println();
            }
    
            return parmStr;
        }

        private void generateParmDecl(PrintWriter code, String typeName, String varName) {
            code.print(typeName + " " + varName + " = ");
            if (typeName.equals("String")) {
                code.println("\"testValue\";");
            } else if (typeName.indexOf("[") > -1) {
                code.println("new " + typeName + " { item1, item2, item3 };");
            } else if (Character.isUpperCase(typeName.charAt(0))) {
                code.println("new " + typeName + "();");
            } else {
                code.println("testValue;");
            }
        }
    
        private String getTypeName(Class cl) {
            String typeName = cl.getName();
            int    arrayDims = 0;
    
            while (typeName.charAt(arrayDims) == '[') {
                arrayDims++;
            } 
    
            String brackets = "";
    
            if (arrayDims > 0) {
                typeName = cl.getComponentType().getName();
            } 
    
            for (int d = 0; d < arrayDims; d++) {
                brackets += "[]";
            }
    
            return stripPackage(typeName) + brackets;
        }
     
        private String makeUpVarName(String typeName) {
    
            String varName;
            
            varName = typeName.substring(0, 1);
    
            char c = typeName.charAt(0);
    
            if (Character.isUpperCase(c)) {
                varName = varName.toLowerCase() + typeName.substring(1);
            }

            if (varName.endsWith("[]")) {
                while (varName.endsWith("[]")) {
                    varName = varName.substring(0, varName.length() - 2);
                }
                varName += "s";
            }
    
            return varName;
        }
    
    
        private void generateTestMethods(PrintWriter code, Class cl) {
            Method[] methods = cl.getDeclaredMethods();
    
            for (int i = 0; i < methods.length; i++) {
                Method method = methods[i];
    
                int modifiers = method.getModifiers();
    
                if (include(modifiers)) {
                    String methodName = method.getName();

                    // skip compiler-generated accessor methods
                    if (methodName.indexOf("$") > -1) {
                        continue;
                    }

                    String testMethodName = "test" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
    
                    code.println("    public void " + testMethodName + "() {");
                    String parms = generateParameters(code, method.getParameterTypes());
    
                    code.print(IN + IN);
                    
                    String typeName = getTypeName(method.getReturnType());
    
                    boolean hasReturnValue = !typeName.equals("void");
    
                    if (hasReturnValue) {
                        generateParmDecl(code, typeName, "expected");
                        code.println();
                        code.print(IN + IN + typeName + " returned = ");
                    }
    
                    code.println(testClassVar + "." + methodName + "(" + parms + ");");
                    code.println();
    
                    if (hasReturnValue) {
                        code.println(IN + IN + "assertEquals(expected, returned);");
                    } else {
                        code.println(IN + IN + "assertTrue(something);");
                    }
                    code.println(IN + "}");
                    code.println();
                } 
            } 
        } 
    
        boolean include(int modifiers) {
            if (includePkgCheck.isSelected()) {
                return !Modifier.isPrivate(modifiers);
            } else {
                return Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers);
            }
        }
    
        private String stripPackage(String typeName) {
            String name;
            int    pos = typeName.lastIndexOf(".");
    
            if (pos > -1) {
                name = typeName.substring(pos + 1);
            } else {
                name = typeName;
            } 
    
            if (name.endsWith(";")) {
                name = name.substring(0, name.length() - 1);
            } 
    
            return name;
        } 
    
        private String parameterString(Class[] parms) {
            String parmStr = "(";
    
            for (int i = 0; i < parms.length; i++) {
                if (i > 0) {
                    parmStr += ", ";
                } 
    
                String name = parms[i].getName();
                int    arrayDims = 0;
    
                while (name.charAt(arrayDims) == '[') {
                    arrayDims++;
                } 
    
                String brackets = "";
    
                if (arrayDims > 0) {
                    name = parms[i].getComponentType().getName();
                } 
    
                for (int d = 0; d < arrayDims; d++) {
                    brackets += "[]";
                } 
    
                parmStr += stripPackage(name) + brackets;
            } 
    
            parmStr += ")";
    
            return parmStr;
        } 
    
    }

}



/*--- formatting done in "Java Code Conventions" style on 06-28-2000 ---*/


