grammar Dto;
// http://www.cs.usfca.edu/~parrt/course/652/lectures/antlr.html
// Good introduction, though some of it is applicable only to ANTLR 2.
// The section on actions is good, though, and the section on ASTs is OK.
//
// @author Daniel Gredler
@header {
import java.io.*;
import java.util.Date;
import java.util.Iterator;
import java.util.regex.*;
import java.text.SimpleDateFormat;
}
@members {
// Main: process all the files in the "input" directory, write results to the "output" directory.
public static void main(String[] args) throws Exception {
File input = new File("input");
File output = new File("output");
for (File file : input.listFiles()) {
if(file.getName().startsWith(".")) {
continue;
}
DtoLexer lex = new DtoLexer(new ANTLRFileStream(file.getAbsolutePath()));
CommonTokenStream tokens = new CommonTokenStream(lex);
DtoParser parser = new DtoParser(tokens);
try {
Clazz c = parser.clazz();
writeStringToFile(toJavaDto(c), new File(output, c.name + ".java"));
writeStringToFile(toJavaTest(c), new File(output, c.name + "Test.java"));
} catch (RecognitionException e) {
e.printStackTrace();
}
}
}
private static void writeStringToFile(String s, File f) {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(f));
writer.write(s);
writer.close();
} catch(IOException e) {
e.printStackTrace();
}
}
private static String toJavaTest(Clazz c) {
// Start things up.
StringBuilder s = new StringBuilder();
addHeader(s);
addPackage(s, c.pkg);
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
s.append("import static com.mycompany.myproject.").append(c.pkg).append(".facade.dto.").append(c.name).append(".").append(p.uuname).append(";\r\n");
}
s.append("\r\n");
addClassJavaDoc(s, "Unit tests for {@link " + c.name + "}.");
s.append("public class ").append(c.name).append("Test extends TestCase {\r\n\r\n");
// Add the setter and getter testing.
s.append(" /**\r\n");
s.append(" * Tests all the setters and getters, including whether or not they fire property change events.\r\n");
s.append(" * @throws Exception if an error occurs\r\n");
s.append(" */\r\n");
s.append(" public void testSettersAndGetters() throws Exception {\r\n");
s.append(" SetterGetterTester t = new SetterGetterTester(").append(c.name).append(".class);\r\n");
s.append(" // TODO: change the initial and alternate values for all the properties.\r\n");
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
s.append(" t.testProperty(").append(p.uuname).append(", false, null, null);\r\n");
}
s.append(" }\r\n\r\n");
// Add the hashCode() and equals() testing.
s.append(" /**\r\n");
s.append(" * Tests the equals(), hashCode() and toString() methods.\r\n");
s.append(" * @throws Exception if an error occurs\r\n");
s.append(" */\r\n");
s.append(" public void testEqualsAndHashcode() throws Exception {\r\n");
s.append(" new EqualsHashCodeTester(").append(c.name).append(".class");
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
if(p.biz) {
s.append(", ").append(p.uuname);
}
}
s.append(").test();\r\n");
s.append(" }\r\n\r\n");
// Finish things up.
s.append("}\r\n");
return s.toString();
}
private static String toJavaDto(Clazz c) {
// Start things up.
StringBuilder s = new StringBuilder();
addHeader(s);
addPackage(s, c.pkg);
addClassJavaDoc(s, "TODO: class description");
s.append("public class ").append(c.name).append(" extends AbstractDto {\r\n\r\n");
s.append(" // TODO: generate serialVersionUID\r\n\r\n");
// Add property name constants.
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
s.append(" /** The property name for ").append(p.desc2).append(". */\r\n");
s.append(" public static final String ").append(p.uuname).append(" = \"").append(p.lname).append("\"").append(";\r\n\r\n");
}
// Add instance variables.
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
s.append(" /** ").append(p.desc).append(" */\r\n");
s.append(" private ").append(p.type).append(" ").append(p.lname).append(";\r\n\r\n");
}
// Add getters and setters.
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
s.append(" /**\r\n");
s.append(" * Returns ").append(p.desc2).append(".\r\n");
s.append(" * @return ").append(p.desc2).append("\r\n");
s.append(" */\r\n");
s.append(" public ").append(p.type).append(" get").append(p.uname).append("() {\r\n");
s.append(" return this.").append(p.lname).append(";\r\n");
s.append(" }\r\n\r\n");
s.append(" /**\r\n");
s.append(" * Sets ").append(p.desc2).append(".\r\n");
s.append(" * @param ").append(p.lname).append(" ").append(p.desc2).append("\r\n");
s.append(" */\r\n");
s.append(" public void set").append(p.uname).append("(").append(p.type).append(" ").append(p.lname).append(") {\r\n");
s.append(" ").append(p.type).append(" oldValue = this.").append(p.lname).append(";\r\n");
s.append(" this.").append(p.lname).append(" = ").append(p.lname).append(";\r\n");
s.append(" this.firePropertyChange(").append(p.uuname).append(", oldValue, this.").append(p.lname).append(");\r\n");
s.append(" }\r\n\r\n");
}
// Add the equals() method.
s.append(" /**\r\n");
s.append(" * {@inheritDoc}\r\n");
s.append(" */\r\n");
s.append(" @Override\r\n");
s.append(" public boolean equals(Object o) {\r\n");
s.append(" if (!(o instanceof ").append(c.name).append(")) {\r\n");
s.append(" return false;\r\n");
s.append(" }\r\n");
s.append(" ").append(c.name).append(" other = (").append(c.name).append(") o;\r\n");
s.append(" return new EqualsBuilder()\r\n");
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
if(p.biz) {
s.append(" .append(this.").append(p.lname).append(", other.").append(p.lname).append(")\r\n");
}
}
s.append(" .isEquals();\r\n");
s.append(" }\r\n\r\n");
// Add the hashCode() method.
s.append(" /**\r\n");
s.append(" * {@inheritDoc}\r\n");
s.append(" */\r\n");
s.append(" @Override\r\n");
s.append(" public int hashCode() {\r\n");
s.append(" return new HashCodeBuilder()\r\n");
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
if(p.biz) {
s.append(" .append(this.").append(p.lname).append(")\r\n");
}
}
s.append(" .toHashCode();\r\n");
s.append(" }\r\n\r\n");
// Add the toString() method.
s.append(" /**\r\n");
s.append(" * {@inheritDoc}\r\n");
s.append(" */\r\n");
s.append(" @Override\r\n");
s.append(" public String toString() {\r\n");
s.append(" return createToStringBuilder(this)\r\n");
for(Iterator i = c.props.iterator(); i.hasNext(); ) {
Property p = (Property) i.next();
if(p.biz) {
s.append(" .append(").append(p.uuname).append(", this.").append(p.lname).append(")\r\n");
}
}
s.append(" .toString();\r\n");
s.append(" }\r\n\r\n");
// Close things up.
s.append("}\r\n");
return s.toString();
}
private static void addHeader(StringBuilder s) {
s.append("/*\r\n");
s.append(" * Copyright My Company\r\n");
s.append(" * \r\n");
s.append(" * Author : TODO created on ").append(new SimpleDateFormat("MM/dd/yyyy").format(new Date())).append("\r\n");
s.append(" */\r\n\r\n");
}
private static void addPackage(StringBuilder s, String pkg) {
s.append("package com.mycompany.myproject.").append(pkg).append(".facade.dto;\r\n\r\n");
}
private static void addClassJavaDoc(StringBuilder s, String comment) {
s.append("/**\r\n");
s.append(" * ").append(comment).append("\r\n");
s.append(" * \r\n");
s.append(" * @author TODO (in header, too)\r\n");
s.append(" */\r\n");
}
private Clazz clazz;
private static class Clazz {
String pkg;
String name;
List props = new ArrayList();
}
private static class Property {
String desc;
String desc2;
String lname;
String uname;
String uuname;
String type;
boolean biz;
public void setDesc(String s) {
// Full description with capitalized first letter and a period at the end.
desc = s;
desc = desc.substring(3);
desc = desc.substring(0, desc.length() - 2);
desc = desc.trim();
if(!Character.isUpperCase(desc.charAt(0))) {
desc = desc.substring(0, 1).toUpperCase() + desc.substring(1);
}
if(desc.charAt(desc.length() - 1) != '.') {
desc += ".";
}
// Full description without a capitalized first letter and without a period at the end.
desc2 = desc.substring(0, 1).toLowerCase() + desc.substring(1, desc.length() - 1);
}
public void setName(String s) {
// Create the camel-cased name with an initial lowercase char, the camel-cased name with
// an initial upper-cased char, and the all-caps name with underscores between words.
lname = s;
uname = lname.substring(0, 1).toUpperCase() + lname.substring(1);
uuname = "PROPERTY_" + makeConstantName(lname);
}
private String makeConstantName(String s) {
// Reference: http://www.palbrattberg.com/2007/06/01/undoing-camelcase/
Pattern p = Pattern.compile("\\p{Lu}");
Matcher m = p.matcher(s);
StringBuffer sb = new StringBuffer();
while(m.find()) {
String sub = m.group();
m.appendReplacement(sb, sub.substring(0, sub.length() - 1) );
sb.append('_');
sb.append(sub.substring(sub.length() - 1));
}
m.appendTail(sb);
return sb.toString().toUpperCase();
}
}
}
/*-----------------------------------------------------------*/
/* Parser Rules */
/*-----------------------------------------------------------*/
clazz returns [Clazz c = new Clazz()]
: i1=Identifier { c.pkg = $i1.text; } '.' i2=Identifier { c.name = $i2.text; } '{' ( property[c] )* '}'
;
property[Clazz c]
: Comment
{
Property p = new Property();
p.setDesc($Comment.text);
}
b='BIZ'?
{
p.biz = (b != null);
}
type
{
p.type = $type.text;
}
Identifier
{
p.setName($Identifier.text);
}
';'
{
c.props.add(p);
}
;
type : Identifier ( '<' Identifier '>' )?
;
/*-----------------------------------------------------------*/
/* Lexer Rules */
/*-----------------------------------------------------------*/
Comment
: '/*' ( options {greedy=false;} : . )* '*/'
;
WS
: ( ' ' | '\t' | '\f' )+ { $channel = HIDDEN; }
;
EOL
: ( '\r' | '\n' )+ { $channel = HIDDEN; }
;
Identifier
: AlphaChar AlphaNumericChar*
;
fragment
AlphaChar
: 'a'..'z'
| 'A'..'Z'
;
fragment
NumericChar
: '0'..'9'
;
fragment
AlphaNumericChar
: ( AlphaChar | NumericChar)
;