Friday, April 20, 2007

Source code generator

Recently I had to write some code in Delphi .NET . Instead of using 'getters' and 'setters' like in java using properties is preferred. But writing the properties by hand is very painful.. Imagine having to write all the getters and setters in java by hand and your IDE ( I preferably use Eclipse ) not generate it. So I decided to write a code generator.

The first decision I had to make was which language to use. I can code in Java, C# and Delphi. I did not find any feature present in any of the languages that would make them preferable for the job, so I used Java since I am much more comfortable using this language.

The I had to decide what would be the input and output.

Input would be like :-
 // Make these properties!!!!!
id : integer; // the id

//blah blah comment
id2 : integer;
// so whats up

line_code : string;
rate : integer;



Output would be like:-

   //Variables :::::::

_id : integer;
_id2 : integer;
_line_code : string;
_rate : integer;


//Properties :::::::
Property id : integer
read _id
write _id ;
Property id2 : integer
read _id2
write _id2 ;
Property line_code : string
read _line_code
write _line_code ;
Property rate : integer
read _rate
write _rate ;




I also decided the coolest way to get the input and write the output was the clipboard. So I had to just copy the text into the clipboard and execute the java program. This would write the output into the clipboard.

Now the implementation specific details begin.
The first thing I needed to do was to remove the comments. Also what was needed was to get two lists, one containing the variable names and the other containing the variable types.

And then output it in any way wanted... My complete code is given below..

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SashClipboard
{

public static final String NEWLINE = "\n";

public static String TAB = " ";

public static void main(String[] args) throws UnsupportedFlavorException,
IOException
{

//gets data from clipboard
String data = copyFromClipboard();

// System.out.println(data);

//get the diff lies
String[] lines = data.split(NEWLINE);

// System.out.println(Arrays.asList(lines));
//remove comments '//' and if two stmts exist on same line
ArrayList linesList = removeSingleLineCommentAndMultiStmt(lines);

// System.out.println(linesList);

ArrayList varNameList = new ArrayList();
ArrayList varTypeList = new ArrayList();

getVarAndType(linesList, varNameList, varTypeList);

String output = createOutput(varNameList, varTypeList);

System.out.println(output);

//copy data to clipboard
copyToClipboard(output);
}

private static void copyToClipboard(String output)
{
StringSelection ss = new StringSelection(output);
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
cb.setContents(ss, null);
}

public static String copyFromClipboard() throws UnsupportedFlavorException,
IOException
{
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable contents = cb.getContents(null);
Object transferData = contents.getTransferData(DataFlavor.stringFlavor);
String data = transferData.toString();
return data;
}

private static String createOutput(ArrayList varNameList,
ArrayList varTypeList)
{
StringBuilder output = new StringBuilder();

output.append("//Variables :::::::\n\n");
for (int i = 0; i < varNameList.size(); i++)
{
output.append(TAB + "_" + varNameList.get(i) + " : "
+ varTypeList.get(i) + ";" + NEWLINE);
}

output.append("\n\n//Properties :::::::\n");

for (int i = 0; i < varNameList.size(); i++)
{
output.append(TAB + "Property " + varNameList.get(i) + ":"
+ varTypeList.get(i) + NEWLINE + TAB + TAB + " read " + "_"
+ varNameList.get(i) + NEWLINE + TAB + TAB + " write "
+ "_" + varNameList.get(i) + ";" + NEWLINE);
}
return output.toString();
}

private static void getVarAndType(ArrayList linesList,
ArrayList varNameList, ArrayList varTypeList)
{
for (int i = 0; i < linesList.size(); i++)
{
String line = linesList.get(i);
String[] colonSeperated = line.split(":");
if (colonSeperated.length != 2)
{
System.out.println("Something wrong " + line);
continue;
}

String[] commaSeperated = colonSeperated[0].split(",");

for (String var : commaSeperated)
{
varNameList.add(var);
varTypeList.add(colonSeperated[1]);
}
}
}

/**
* Remove comments '//' and if two stmts exist on same line
* e.g a: integer; b:string;
*
* if input on a line is
* a:integer; b:integer; //to do something
*
* it will give two lines i.e
* a:integer;
* b:integer
*
* @param lines multi lines
* @return a list containg different lines
* and without single line comments
*/
private static ArrayList removeSingleLineCommentAndMultiStmt(
String[] lines)
{
ArrayList linesList = new ArrayList();
for (int i = 0; i < lines.length; i++)
{
String[] comment = lines[i].split("//");

String[] multiStmt = comment[0].split(";");
for (String str : multiStmt)
{
if (!str.trim().equals(""))
{
linesList.add(str.trim());
}
}
}
return linesList;
}
}


It was not just the fact that I code generate the properties and save time, I also loved writing this code..

I think the code is self explanatory. If any doubts please do post your doubts.