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