/home/ooechs/Ecj2Java/src/ac/essex/ooechs/ecj/ecj2java/JavaWriter.java

package ac.essex.ooechs.ecj.ecj2java; 
 
import ec.gp.GPIndividual; 
 
import java.util.Vector; 
import java.util.Date; 
import java.io.File; 
import java.io.BufferedWriter; 
import java.io.FileWriter; 
import java.io.IOException; 
 
import ac.essex.ooechs.ecj.ecj2java.nodes.ParseableNode; 
import ac.essex.ooechs.ecj.ecj2java.lang.Constant; 
 
/** 
 * <p/> 
 * Takes an ECJ individual and outputs it as Java Code. 
 * For this class to work properly, all your GP functions must 
 * implement the functionality in the ParseableNode interface. This 
 * means either extending the ParseableGPNode adapter class (for functions and terminals) 
 * or extending the ParseableERC adapter class (for ERCs) 
 * </p> 
 * <p/> 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 
 * of the License, or (at your option) any later version, 
 * provided that any use properly credits the author. 
 * This program 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 General Public License for more details at http://www.gnu.org 
 * </p> 
 * 
 * @see ParseableNode 
 * @author Olly Oechsle, University of Essex, Date: 05-Sep-2006 
 * @version 1.0 
 */ 
public class JavaWriter { 
 
    /** 
     * The class modifiers, default is public 
     * although you may possibly want to generate a different kind of class, 
     * abstract for example or inner class. 
     */ 
    protected String classModifiers = "public"; 
 
    /** 
     * Comments that appear just before the class. They are entered JavaDoc style. 
     */ 
    protected String comments = ""; 
 
    /** 
     * The package to which this class belongs, set to null if the class is package-less. 
     */ 
    protected String classPackage; 
 
    /** 
     * The name of the class. The class will be saved to ${classname}.java 
     * in the directory of your choosing if you use the <code>saveJavaCode()</code> 
     * function. 
      */ 
    protected String className; 
 
    /** 
     * If this class extends another class. It is quite often useful for the GP generated 
     * class to extend an a abstract class. For example, the GP generated class has an 
     * evaluatePixel(int x, int y) method, and extends an abstract class which has a function 
     * to load an image and then call the evaluatePixel() function on each pixel. 
     */ 
    protected String classExtends; 
 
    /** 
     * Similar to class extends - if this class implements functions defined in an interface. 
     */ 
    protected String classImplements; 
 
    private Vector<String> imports; 
    private Vector<String> functionSignatures; 
    private Vector<Constant> intConstants; 
    private int funcCounter, ercCounter; 
 
    public JavaWriter(String className, String GPFunctionSignature) { 
        this(className, GPFunctionSignature, null, null); 
    } 
 
    public JavaWriter(String className, String GPFunctionSignature, String comments) { 
        this(className, GPFunctionSignature, comments, null); 
    } 
 
    public JavaWriter(String className, String GPFunctionSignature, String comments, String classPackage) { 
 
        imports = new Vector<String>(5); 
        functionSignatures = new Vector<String>(5); 
        intConstants = new Vector<Constant>(5); 
 
        functionSignatures.add(GPFunctionSignature.trim()); 
 
        this.classPackage = classPackage.trim(); 
        this.className = className.trim(); 
        this.comments = comments.trim(); 
 
    } 
 
    /** 
     * Adds a classname to be added to the list of import statements 
     * @param className 
     */ 
    public void addImport(String className) { 
        imports.add(className); 
    } 
 
    public void addComments(String comments) { 
        this.comments += "\n" + comments; 
    } 
 
    public void setImplements(String classImplements) { 
        this.classImplements = classImplements; 
    } 
 
    public void setExtends(String classExtends) { 
        this.classExtends = classExtends; 
    } 
 
    public void addIntConstant(String name, int value) { 
        intConstants.add(new Constant(name, value)); 
    } 
 
    /** 
     * Adds another function signature, in the case that the individual 
     * consists of more than one tree. Function signatures do not need 
     * to include the brace at the end, for example<br /> 
     * <i>public void evaluate(int x, int y)</i> 
     */ 
    public void addFunctionSignature(String functionSignature) { 
        functionSignatures.add(functionSignature); 
    } 
 
    /** 
     * Converts the individual to a String, containing a complete class declaration 
     * that can then be saved to disk or do whatever you want with. You need to 
     * pass it a GPIndividual class from within ECJ, preferably from the <code>describe</code> 
     * method in your Problem class. 
     **/ 
    public String getJavaCode(GPIndividual ind) { 
 
        StringBuffer buffer = new StringBuffer(1024); 
 
        // package declaration 
        if (classPackage != null) { 
            buffer.append("package "); 
            buffer.append(classPackage); 
            if (!classPackage.endsWith(";")) buffer.append(';'); 
            buffer.append("\n\n"); 
        } 
 
        // import statements 
        for (int i = 0; i < imports.size(); i++) { 
            String importStatement = imports.elementAt(i).trim(); 
            buffer.append("import "); 
            buffer.append(importStatement); 
            if (!importStatement.endsWith(";")) buffer.append(';'); 
            buffer.append('\n'); 
        } 
 
        // put in a new line for good luck after imports 
        if (imports.size() > 0) buffer.append('\n'); 
 
        // comments 
        buffer.append("/**\n"); 
        buffer.append(comments); 
        buffer.append("\nGenerated by ECJ2Java on "); 
        buffer.append(new Date().toString()); 
        buffer.append("\n**/\n\n"); 
 
        // start the class 
        buffer.append(classModifiers); 
        buffer.append(" class "); 
        buffer.append(className); 
 
        if (classExtends != null) { 
            buffer.append(" extends "); 
            buffer.append(classExtends); 
        } 
 
        if (classImplements != null) { 
            buffer.append(" implements "); 
            buffer.append(classImplements); 
        } 
 
        buffer.append(" {\n\n"); 
 
        // add any constants 
 
        for (int i = 0; i < intConstants.size(); i++) { 
            Constant constant = intConstants.elementAt(i); 
            buffer.append("\t"); 
            buffer.append(constant.toString()); 
            buffer.append(";\n"); 
        } 
 
        if (intConstants.size() > 0) { 
            buffer.append("\n"); 
        } 
 
        // count the trees 
        for (int i = 0; i < ind.trees.length; i++) { 
 
            // Extract the first tree 
            ac.essex.ooechs.ecj.ecj2java.nodes.ParseableNode tree = (ParseableNode) ((GPIndividual) ind).trees[i].child; 
 
            // Start with the function signature for this tree 
            String functionSignature = functionSignatures.elementAt(i); 
 
            append(buffer, functionSignature, 1); 
            buffer.append(" {\n"); 
 
            // create variable names 
            funcCounter = 0; 
            ercCounter = 0; 
            nameChildren(tree, 0); 
 
            // now append the rest of the code in the tree 
            appendCode(tree, buffer); 
 
            append(buffer, "}\n\n", 1); 
 
        } 
 
        // finish the class 
        buffer.append("}"); 
 
        return buffer.toString(); 
 
    } 
 
    /** 
     * Generates the java code (using getJavaCode) saves the code in a given directory. Since there is no flexibility 
     * in the naming of the Java file (it will always be "${classname}.java", the function doesn't allow the filename 
     * to be specified. 
     */ 
    public void saveJavaCode(GPIndividual ind, File directory) throws IOException { 
        if (directory.isDirectory()) { 
 
            File f = new File(directory, className + ".java"); 
 
            BufferedWriter writer = new BufferedWriter(new FileWriter(f)); 
 
            String output = getJavaCode(ind); 
 
            writer.write(output); 
 
            writer.close(); 
 
        } else { 
            System.err.println("Cannot save Java Code - not a directory"); 
        } 
    } 
 
    private void nameChildren(ac.essex.ooechs.ecj.ecj2java.nodes.ParseableNode tree, int depth) { 
 
        // name the children first 
        if (tree.countChildren() > 0) { 
            for (int i = 0; i < tree.countChildren(); i++) 
                nameChildren(tree.getChild(i), depth + 1); 
        } 
 
        // assist in readability by assigning names to non void functions 
        // prefix functions with "func_" and ERC values with "val_" 
        switch (tree.getType()) { 
            case ac.essex.ooechs.ecj.ecj2java.nodes.ParseableNode.FUNCTION: 
                funcCounter++; 
                tree.setVariableName("func_" + funcCounter); 
                break; 
            case ParseableNode.ERC: 
                ercCounter++; 
                tree.setVariableName("val_" + ercCounter); 
                break; 
        } 
 
        if (depth == 0 && tree.getType() != ac.essex.ooechs.ecj.ecj2java.nodes.ParseableGPNode.VOID) { 
            tree.setType(ac.essex.ooechs.ecj.ecj2java.nodes.ParseableNode.RETURN); 
        } 
 
    } 
 
 
    private void appendCode(ac.essex.ooechs.ecj.ecj2java.nodes.ParseableNode tree, StringBuffer buffer) { 
 
        if (tree.countChildren() > 0) { 
            for (int i = 0; i < tree.countChildren(); i++) { 
 
                /** 
                 * Abbreviation - if we just declare a variable 
                 * which is redundant, we can abbreviate it. 
                 */ 
 
                ParseableNode child = tree.getChild(i); 
 
                if (child.countChildren() == 0) { 
                    // replace the variable name with the java code 
                    // and don't print it out on its own line. 
                    child.setVariableName(child.getJavaCode()); 
                } else { 
                    appendCode(tree.getChild(i), buffer); 
                } 
 
            } 
        } 
 
        String java = tree.getJavaCode(); 
 
        // now print out the node 
        switch (tree.getType()) { 
            case ParseableNode.RETURN: 
                append(buffer, "return ", 2); 
                buffer.append(java); 
                if (!java.endsWith(";")) buffer.append(";"); 
                if (tree.getLineComment() != null) { 
                    buffer.append(" //"); 
                    buffer.append(tree.getLineComment()); 
                } 
                buffer.append("\n"); 
                break; 
            default: 
                switch (tree.getObjectType()) { 
                    case ParseableNode.DOUBLE: 
                        append(buffer, "double", 2); 
                        break; 
                    case ParseableNode.BOOLEAN: 
                        append(buffer, "boolean", 2); 
                        break; 
                    case ParseableNode.INT: 
                        append(buffer, "int", 2); 
                        break; 
                } 
                buffer.append(' '); 
                buffer.append(tree.getVariableName()); 
                buffer.append(" = "); 
                buffer.append(java); 
                if (!java.endsWith(";")) buffer.append(";"); 
                if (tree.getLineComment() != null) { 
                    buffer.append(" //"); 
                    buffer.append(tree.getLineComment()); 
                } 
                buffer.append("\n"); 
                break; 
        } 
 
    } 
 
    private void append(StringBuffer buffer, String text, int tabs) { 
        for (int i = 0; i < tabs; i++) { 
            buffer.append('\t'); 
        } 
        buffer.append(text); 
    } 
 
}