I'll explain in details theses Template Code Generator with a little application. I'll create a web page as output.
Here the specs for the demo.
- The web page template should create a entry for each items in the list received in parameter
- The list could be created dynamically (so the number of items is not fixed)
Before starting to explain how to do it with theses frameworks, I'll use a class : MediaFile that contains the info on a media file. I'll create a html that display 2 items by row and just to kept the demo simpler, I'll use a even number of items.
Take a look at the html that I would like to generate.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<!-- include the required JavaScript file -->
</head>
<body>
<div align="center">
<table width="80%">
<tr>
<td>
<a href="/name1" id="player1">name1
</a>
</td>
<td>
<a href="/name2" id="player2">name2
</a>
</td>
</tr>
<tr>
<td>
<a href="/name3" id="player3">name3
</a>
</td>
<td>
<a href="/name4" id="player4">name4
</a>
</td>
</tr>
<tr>
<td>
<a href="/name5" id="player5">name5
</a>
</td>
<td>
<a href="/name6" id="player6">name6
</a>
</td>
</tr>
</table>
</div>
</body>
</html>
I didn't put <td> and <a> on the same line on purpose. The reason it because it's easier to show the format's problems with a code generator. I'll explain it in more details later.The MediaFile's list will be populated like that :
public void init() {
mediaFileList = new ArrayList();
for (int i = 0; i < 5; i++) {
MediaFile mediaFile = new MediaFile();
mediaFile.setName("name" + i);
mediaFile.setFps("29.997");
mediaFile.setDuration("12:45");
mediaFile.setCodec("H264");
mediaFile.setWidth("320");
mediaFile.setHeight("240");
mediaFile.setStreamSize("2 Megs");
mediaFileList.add(mediaFile);
}
}
Apache VelocityI known the framework by name, but I never used before. I have to say that it wasn't too hard to get it to work. Apache have a good documentation for Velocity API. If you are not familiar with this API, go take a look at the user guide http://velocity.apache.org/engine/devel/user-guide.html
Apache Velocity use it's own language, but it's pretty simple. What I didn't like at first was the lack of nested loop.
I wanted to do something like that (using the iterator in a nested loop):
for(Iterator it = list.iterator();it.hasNext();){
...
for(int i=0;i<2;i++){
Object element = iterator.next();
...
}
...
}
I'm not a expert in Velocity, but after looking around on the web, I didn't find a simple answer, so I had to do it another way. The problem with a template like below, is that it could become hard to read, because if you let some blank lines for the readiness of the code, theses lines will appear in the generated code.Let's take a look at the template in Velocity that will give the desirable layout.
GalleryTemplateVelocity.vm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<!-- include the required JavaScript file -->
</head>
<body>
<div align="center">
<table width="80%">
#set( $maxItembyRow = 2)
#set( $index = 0)
#set( $newLine = true)
#foreach( $mediaFile in $list )
#if( $newLine )
<tr>
#set( $newLine = false)
#end
#if( $index<$maxItembyRow )
<td><a href="/$mediaFile.name" id="player$velocityCount">$mediaFile.name
</a>
</td>
#set($index = $index+1)
#else
#set($index = 0)
#set( $newLine = true)
#end
#end
</tr>
</table>
</div>
</body>
</html>
After that we need the main class that will call the template, and populate the list of MediaFile. If you want to pass variables to Velocity template, you have to put them in the context, like you do for a HttpRequest. SampleVelocity.java
public void generate() {
try {
Velocity.init();
VelocityContext context = new VelocityContext();
context.put("list", mediaFileList);
Template template = Velocity.getTemplate("./templates/GalleryTemplateVelocity.vm");
BufferedWriter writer = writer = new BufferedWriter(new OutputStreamWriter(System.out));
if (template != null)
template.merge(context, writer);
/*
* flush and cleanup
*/
writer.flush();
writer.close();
} catch (Exception e) {
s_logger.error("generate", e);
}
}
Eclipse JETJET can be used as a template code generator. It's was made to run within Eclipse, but you can include two Eclipse jar and that will do the trick. JET is based on the JSP syntax. If you have done scriplet in JSP, you won't find that hard to use.
The main differences between JEt and Velocity, is that JET only take 1 parameter in the template. Yes, only one... You can pass that by creating a java class that will contains all your variables. Yes it's ugly, but when you know it, it's not hard to use.
The second big difference is that JET framework compile the template into a java class, and you use this class in your application.
Take a look at the JET template (look like a JSP with the 2 first lines that you related to JET.
GalleryTemplateJet.jet
<%@ jet package="ca.sebastiendionne.gallery.jet" class="GalleryHtmlJET" imports="java.util.* ca.sebastiendionne.gallery.model.*" %>
<% List<MediaFile> mediaFileList = (List<MediaFile>) argument; %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<!-- include the required JavaScript file -->
</head>
<body>
<div align="center">
<table width="80%">
<%
int maxItembyRow = 2;
int index = 0;
for (Iterator<MediaFile> iterator=mediaFileList.iterator();iterator.hasNext();) {%>
<tr>
<%
for(int j=0;j<maxItembyRow;j++){
MediaFile element = iterator.next();
%>
<td>
<a href="/<%=element.getName()%>" id="player<%=index%>"><%=element.getName()%>
</a>
<%=element%>
</td>
<%
index++;
}%>
</tr>
<%
}
%>
</table>
</div>
</body>
</html>
That will generate the java class : ca.sebastiendionne.gallery.jet.GalleryHtmlJETand you use it like that :
public void generate(){
GalleryHtmlJET galleryJET = new GalleryHtmlJET();
String html = galleryJET.generate(mediaFileList); // the argument is the list
System.out.println(html);
}
The method generate() is simpler and smaller than Velocity, but in other hands... The formatting is more tricky with JSP syntax. Try for fun to look at a html page generated with scriptlet. It's will works, but there will be lot of blank lines and it won't have a nice indentation. That is important when you want to generate java class. You don't want to pass a formatter after the generating job.I had used JET few months ago for my application IbatisGen. I think that it took more time to get the indentation right that coding the rest of the application.
JET have some limitations like the one argument, but that JET2 fixed that.
Eclipse JET2
JET2 differs than JET1 in different ways. JET1 can be used outside Eclipse, but not JET2.
JET2 can be easily use in Eclipse if you have a xml file as input, but if you don't have a fixed input like this demo. It's more complicated.
You will have to create a Eclipse Plugin that will be able to handle dynamic input.
So, if you don't want to create a Eclipse Plugin, stop here, take JET or Velocity.
I won't cover how to create the JET2 Plugin, you can find a good sample here http://www.eclipse.org/articles/Article-JET2/jet_tutorial2.html, but I'll show you how to use a xml input file.
The JET2 templates can you JSP and JSLT syntax like.
sample.xml
<app class="Car">
<property name="model" type="String" initial="Hybrid" />
<property name="horsepower" type="int" initial="140" />
<property name="spareTires" type="boolean" initial="true" />
</app>
The main template (like the java class that contains the main() )main.jet
<%@taglib prefix="ws" id="org.eclipse.jet.workspaceTags" %>
<c:if test="isVariableDefined('org.eclipse.jet.resource.project.name')">
<ws:file template="templates/testCar.jet" path="{$org.eclipse.jet.resource.project.name}/dump2.xml"/>
</c:if>
testCar.jet (the template that will generate the code from sample.xml) (We are creating a getter/setter class)
<%@taglib prefix="ws" id="org.eclipse.jet.workspaceTags" %>
class <c:get select="/app/@class" /> {
<c:iterate select="/app/property" var="p" >
private <c:get select="$p/@type" /> <c:get select="$p/@name" />;
</c:iterate>
public <c:get select="/app/@class" />() {
<c:iterate select="/app/property" var="p" >
this.<c:get select="$p/@name" /> = <c:choose select="$p/@type" >
<c:when test="'String'">"<c:get select="$p/@initial" />"</c:when>
<c:otherwise><c:get select="$p/@initial" /></c:otherwise>
</c:choose>
;
</c:iterate>
}
<c:iterate select="/app/property" var="p" >
public void set<c:get select="camelCase($p/@name)" />(<c:get select="$p/@type" /> <c:get select="$p/@name" />) {
System.out.println("In set<c:get select="camelCase($p/@name)" />()");
this.<c:get select="$p/@name" /> = <c:get select="$p/@name" />;
}
public <c:get select="$p/@type" /> get<c:get select="camelCase($p/@name)" />() {
System.out.println("In get<c:get select="camelCase($p/@name)" />()");
return <c:get select="$p/@name" />;
}
</c:iterate>
}
To run all that you need to "Run as ... JET Transformation" in Eclipse.I won't go into details, but here what the generate() method should look like if you want to handle a dynamic input.
public void generate(){
GalleryHtmlJET galleryJET = new GalleryHtmlJET();
JET2Context context = new JET2Context(null);
context.setVariable("list", mediaFileList);
JET2Writer out = new BodyContentWriter();
context.setTagFactory(new TagFactoryImpl(context));
... the line for the Plugin
galleryJET.generate(context, out);
... end of the Plugin stuff
System.out.println(out);
}
It's look really like Apache Velocity at this point.If I had to choose between them, I don't know if I would choose Velocity or JET. Both of them could work in a plugin (Eclipse or Netbean), support Ant task.
What do you think ? Do you know some framework that you want to share with us ?
You can download the source code here : Velocity/JET and JET2.















