An Elementary Expander

One of the reasons we write software, is to consolidate and secure knowledge. For instance, writing software to process invoices, consolidates and secures the knowledge of processing invoices. Therefore, in order to consolidate and secure the knowledge of writing software, we should write software to write software. Moreover, it is an excellent exercise in extracting the essence of code, and in reasoning in an inductive way.

Generalizing code to templates

Consider this straightforward Java POJO class to represent a noun concept, Person with two, attributes: name and age.

public class Person {
   private String mName;
   private int mAge;

   public Person() {
   }

   public String getName() {
     return mName;
   }
   public void setName(Strin aName) {
     mName = aName;
   }
   public int getAge() {
     return mAge;
   }
   public void setAge(int aAge) {
     mAge = aAge;
   }
}

In order to generate or expand this source file, we need to extract the essence, i.e. generalize this piece of code to a code template. This means that the actual noun, Person, needs to be generalized, i.e. it needs to become a variable. In order to delineate it properly, we represent this variable as %Noun%. However, as the number of attributes may exceed two, is in general variable as well, we require subtemplates for the code parts dealing with the attributes: the variable declarations and the get- and set-methods. In the main code template NounTemplate.java, we just need to reserve and delineate the space.

public class %Noun% {
  // start:attribute-definitions
  // end:attribute-definitions

  public %Noun%() {
  }

  // start:getset-methods
  // end:getset-methods
}

The code to define an attribute or field, e.g. private String name; can now easily be generalized to a subtemplate FieldDefinitionTemplate.java, using a variable %type% for the attribute type, and a variable %name% for the attribute name:

private %type% m%name%

In the same way, we define a subtemplate FieldGetSetMethodsTemplate.java for the get-set-methods, using the same variables %type% and %name%:

  public %type% get%name%() {
    return m%name%;
  }
  public void set%name%(%type% a%name%) {
    m%name% = a%name%;
  }

The final thing we need before writing the actual expander, is to define a parametrized representation of the noun and its POJO class. For instance we can define the type and name of the various attributes or fields, in a descriptor file, Person.descriptor.

String Name
int Age

Writing the expander

In order to write the actual expander, we need a few basic utility methods. In the rare case that you would not have these available, they can be written very easily. However, they are so elementary, that we just list their definitions here:

  String readFile(String filename);
  void writeFile(String content, String, filename)
  String replaceInString(String content, String target, String replacement)
  String insertInString(String anchor, String insertion)

Now, we just need two specific classes for the expansion of these noun POJO classes. First, we need a data class NounDescriptor.java representing the descriptor of the noun. In this class, we provide a constructor that is able to read the descriptor file, and store the various type-name-pairs of the attributes in two lists of the type ArrayList<String>.

public class NounDescriptor {
  public ArrayList<String> fieldTypes = new ArrayList<String>();
  public ArrayList<String> fieldNames = new ArrayList<String>();

  public NounDescriptor(String aName) {
    // reading the attribute type-name pairs and storing them
  }
}

Finally, we are able to write the actual NounExpander class, reading the noun descriptor, instantiating the main template and the subtemplates, and writing the resulting Java code to a file. The actual noun is passed through the command line via args[0].

public class NounExpander {

  public static void main(String [] args) {
    String noun = args[0];
    String nounCode, definitionCode, getSetCode;
    String nounTemplate = readFile("NounTemplate.java");
    String fieldDefinitionTemplate = readFile("FieldDeclarationTemplate.java");
    String fieldGetSetMethodsTemplate = readFile("FieldGetSetMethodsTemplate.java");

    nounCode = replaceInString(nounTemplate, "%Noun%", noun);
    NounDescriptor nounDescriptor = new NounDescriptor(noun+".descriptor");
    for (int i=0; i < nounDescriptor.size(); i++)
      definitionCode = replaceInString(fieldDefinitionTemplate, "%type%", fieldTypes.get(i));
      definitionCode = replaceInString(fieldDefinitionTemplate, "%name%", fieldNames.get(i));
      nounCode = insertInString(nounCode, definitionCode)
      getSetCode = replaceInString(fieldGetSetMethodsTemplate, "%type%", fieldTypes.get(i));
      getSetCode = replaceInString(fieldGetSetMethodsTemplate, "%name%", fieldNames.get(i));
      nounCode = insertInString(nounCode, getSetCode)
    }
    writeFile(nounCode, noun+".java");
  }
}

And now, we can invoke the noun expander for a Person by simply typing

java NounExpander Person

and create a duplicate of the original Person.java file. But now, we can also do this for any noun, with any number of attributes, defined in a proper descriptor file. We could for instance define a Invoice.descriptor specifying three attributes or fields:

String Reference
String Customer
double amount

References