/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);
}
}