3. Gas3 Template Language
Overview
Gas3 templates are based on a specific implementation of Groovy Templates. As such, all Groovy template documentation should apply.
The reason why the standard Groovy implementation was not used was the lack of comments support (<%-- ... --%>) and some formatting issues, especially with platform specific carriage returns. This may now be fixed but it was not at that time.
While the language itself is already documented on Groovy site, there are two specific bindings (i.e., global variables used in Gas3 templates) that should be referenced.
Template execution is a two-phase process. First, the template is transformed to a standard Groovy script (mainly with expressions like print(...)); second, the Groovy script is compiled and executed. Of course, the result of the first transformation and the compiled script is cached, so further executions with the same template are much faster.
Template Bindings
There are two bindings available in Gas3 templates:
| Name | Type | Description |
|---|---|---|
| gVersion | String | Version number of the generator, e.g., "2.0.0" |
| jClass | Implementation of the JavaType interface | An object describing the Java class for which the generator is writting an ActionScript3 class |
Possible implementations of the above JavaType interface are:
| Type | Description |
|---|---|
| JavaEntityBean | Class that describes an EJB 3 entity bean (i.e., a class annotated with a @Entity or a @MappedSuperclass persistence annotation) |
| JavaEnum | Class that describes a Java enum class |
| JavaInterface | Class that describes a Java interface |
| JavaBean | Class that describes all other Java classes |
Those two bindings may be used in your templates as any other variables. For example:
// Generated by Gas3 v.${gVersion}.
package ${jClass.as3Type.packageName} {
public class ${jClass.as3Type.name} {
...
}
}
If you execute this template with Gas3 2.0.0 and Java class named org.test.MyClass, the output will be:
// Generated by Gas3 v.2.0.0.
package org.test {
public class MyClass {
...
}
}
If you plan to write custom templates, you must look at standard GDS templates and API documentation of the four JavaType implementations listed above.
Sample Template
Let's have a look to the standard GDS template for Java interfaces. You may also see all templates here:
<%--
GRANITE DATA SERVICES
Copyright (C) 2007-2008 ADEQUATE SYSTEMS SARL
This file is part of Granite Data Services.
Granite Data Services is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or (at your
option) any later version.
Granite Data Services is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, see <http://www.gnu.org/licenses/>.
--%><%
Set as3Imports = new TreeSet();
for (jImport in jClass.imports) {
if (jImport.as3Type.hasPackage() &&
jImport.as3Type.packageName != jClass.as3Type.packageName)
as3Imports.add(jImport.as3Type.qualifiedName);
}
%>/**
* Generated by Gas3 v${gVersion} (Granite Data Services).
*
* WARNING: DO NOT CHANGE THIS FILE. IT MAY BE OVERWRITTEN EACH TIME YOU USE
* THE GENERATOR. INSTEAD, EDIT THE INHERITED INTERFACE (${jClass.as3Type.name}.as).
*/
package ${jClass.as3Type.packageName} {<%
///////////////////////////////////////////////////////////////////////////
// Write Import Statements.
if (as3Imports.size() > 0) {%>
<%
}
for (as3Import in as3Imports) {%>
import ${as3Import};<%
}
///////////////////////////////////////////////////////////////////////////
// Write Interface Declaration.%>
public interface ${jClass.as3Type.name}Base<%
if (jClass.hasSuperInterfaces()) {
%> extends <%
boolean first = true;
for (jInterface in jClass.superInterfaces) {
if (first) {
first = false;
} else {
%>, <%
}
%>${jInterface.as3Type.name}<%
}
}
%> {<%
///////////////////////////////////////////////////////////////////////////
// Write Public Getter/Setter.
for (jProperty in jClass.properties) {
if (jProperty.readable || jProperty.writable) {%>
<%
if (jProperty.writable) {%>
function set ${jProperty.name}(value:${jProperty.as3Type.name}):void;<%
}
if (jProperty.readable) {%>
function get ${jProperty.name}():${jProperty.as3Type.name};<%
}
}
}%>
}
}
The code for this template is rather simple, but it can be very tricky to distinguish between JSP-like expressions, Groovy code, and outputted ActionScript3 code.
The first block, enclosed with <%-- --%>, is a template comment: it will be completely ignored at transformation (Groovy template to Groovy script) time.
The second block, enclosed with <% %>, is plain Groovy code and will be outputed as is at transformation time. Its purpose is to collect and sort all references to other classes so we can later write ActionScript3 import statements.
Then, the ActionsScript3 code template really begins with a comment (Gas3 version and warning) and is followed by a package and interface definition with superinterfaces, if any, and finally, by a loop over interface getters/setters. Note that comments like:
/////////////////////////////////////////////////////////////////////////// // Write Import Statements.
... are Groovy script comments. They will be in the Groovy script but you will not find them in the outputted ActionScript3 file.
After the first transformation (Groovy template to Groovy script), the rendered code will be as follows:
Set as3Imports = new TreeSet();
for (jImport in jClass.imports) {
if (jImport.as3Type.hasPackage() &&
jImport.as3Type.packageName != jClass.as3Type.packageName)
as3Imports.add(jImport.as3Type.qualifiedName);
}
print("/**\n");
print(" * Generated by Gas3 v${gVersion} (Granite Data Services).\n");
print(" *\n");
print(" * WARNING: DO NOT CHANGE THIS FILE. IT MAY BE OVERWRITTEN EACH TIME YOU USE\n");
print(" * THE GENERATOR. INSTEAD, EDIT THE INHERITED INTERFACE
(${jClass.as3Type.name}.as).\n");
print(" */\n");
print("\n");
print("package ${jClass.as3Type.packageName} {");
///////////////////////////////////////////////////////////////////////////
// Write Import Statements.
if (as3Imports.size() > 0) {
print("\n");
}
for (as3Import in as3Imports) {
print("\n");
print(" import ${as3Import};");
}
///////////////////////////////////////////////////////////////////////////
// Write Interface Declaration.
print("\n");
print("\n");
print(" public interface ${jClass.as3Type.name}Base");
if (jClass.hasSuperInterfaces()) {
print(" extends ");
boolean first = true;
for (jInterface in jClass.superInterfaces) {
if (first) {
first = false;
} else {
print(", ");
}
print("${jInterface.as3Type.name}");
}
}
print(" {");
///////////////////////////////////////////////////////////////////////////
// Write Public Getter/Setter.
for (jProperty in jClass.properties) {
if (jProperty.readable || jProperty.writable) {
print("\n");
if (jProperty.writable) {
print("\n");
print(" function set ${jProperty.name}(value:${jProperty.as3Type.name}):void;");
}
if (jProperty.readable) {
print("\n");
print(" function get ${jProperty.name}():${jProperty.as3Type.name};");
}
}
}
print("\n");
print(" }\n");
print("}");
As you can notice, ${...} expressions are resolved by the Groovy engine rather than the JSP-like engine. It would have been possible to use expressions like <%= ... %>, that will result in a script where:
print("package ${jClass.as3Type.packageName} {");
... would have been split into three lines:
print("package "); print(jClass.as3Type.packageName); print(" {");
This is just informative, as it does not change anything in the final result.
Then, for this Java source code:
package test.granite.ejb3.entity.types; public interface NamedEntity { public String getFirstName(); public void setFirstName(String firstName); public String getLastName(); public void setLastName(String lastName); public String getFullName(); }
... you will get this output:
/**
* Generated by Gas3 v1.2.0 (Granite Data Services).
*
* WARNING: DO NOT CHANGE THIS FILE. IT MAY BE OVERWRITTEN EACH TIME YOU USE
* THE GENERATOR. INSTEAD, EDIT THE INHERITED INTERFACE (NamedEntity.as).
*/
package test.granite.ejb3.entity.types {
public interface NamedEntityBase {
function set firstName(value:String):void;
function get firstName():String;
function get fullName():String;
function set lastName(value:String):void;
function get lastName():String;
}
}
Template Compilation & Execution Errors
Because of the two transformation steps of the template (Groovy template to Groovy script source, then Groovy script source to pre-compiled Groovy script), there are two possible sources of error:
- JSP-like syntax errors (first transformation): e.g., unclosed <% expression.
- Groovy syntax errors (second transformation): e.g., now TreeSet(); instead of new TreeSet();
However, since Groovy is an interpreted language, you may get some other errors at execution time:
- Mispelled expressions: e.g., jClass.neme instead of jClass.name.
- Runtime exceptions: e.g., 0 / 0.
Whenever these kinds of errors occur, you'll find comprehensive error log in your Shell or Eclipse console.
Note that when the error occurs after the first transformation, the Groovy script is printed with line numbers, as well as the Groovy compiler message. It is easy to find the erroneous line in the printed Groovy script, but you have to figure out the corresponding line in the original template:
[gas3] Generating: C:\workspace34\graniteds_ejb3\as3\test\granite\ejb3\entity\
types\NamedEntityBase.as (output file is outdated)
[gas3] org.granite.generator.exception.TemplateCompilationException:
Could not compile template: /interfaceBase.gsp
[gas3] 1 |
[gas3] 2 | Set as3Imports = now TreeSet();
[gas3] 3 |
[gas3] 4 | for (jImport in jClass.imports) {
[gas3] 5 | if (jImport.as3Type.hasPackage() &&
jImport.as3Type.packageName != jClass.as3Type.packageName)
[gas3] 6 | as3Imports.add(jImport.as3Type.qualifiedName);
[gas3] 7 | }
[gas3] 8 |
[gas3] 9 |
[gas3] 10 | print("/**\n");
[gas3] 11 | print(" * Generated by Gas3 v${gVersion} (Granite Data Services).\n");
[gas3] 12 | print(" *\n");
[gas3] 13 | print(" * WARNING: DO NOT CHANGE THIS FILE. IT MAY BE OVERWRITTEN EACH TIME
YOU USE\n");
[gas3] 14 | print(" * THE GENERATOR. INSTEAD, EDIT THE INHERITED INTERFACE
(${jClass.as3Type.name}.as).\n");
[gas3] 15 | print(" */\n");
[gas3] 16 | print("\n");
[gas3] 17 | print("package ${jClass.as3Type.packageName} {");
[gas3] 18 |
[gas3] 19 |
[gas3] 20 | ///////////////////////////////////////////////////////////////////////////
[gas3] 21 | // Write Import Statements.
[gas3] 22 |
[gas3] 23 | if (as3Imports.size() > 0) {
[gas3] 24 | print("\n");
[gas3] 25 |
[gas3] 26 | }
[gas3] 27 | for (as3Import in as3Imports) {
[gas3] 28 | print("\n");
[gas3] 29 | print(" import ${as3Import};");
[gas3] 30 |
[gas3] 31 | }
[gas3] 32 |
[gas3] 33 | ///////////////////////////////////////////////////////////////////////////
[gas3] 34 | // Write Interface Declaration.
[gas3] 35 | print("\n");
[gas3] 36 | print("\n");
[gas3] 37 | print(" public interface ${jClass.as3Type.name}Base");
[gas3] 38 |
[gas3] 39 |
[gas3] 40 | if (jClass.hasSuperInterfaces()) {
[gas3] 41 |
[gas3] 42 | print(" extends ");
[gas3] 43 |
[gas3] 44 | boolean first = true;
[gas3] 45 | for (jInterface in jClass.superInterfaces) {
[gas3] 46 | if (first) {
[gas3] 47 | first = false;
[gas3] 48 | } else {
[gas3] 49 |
[gas3] 50 | print(", ");
[gas3] 51 |
[gas3] 52 | }
[gas3] 53 |
[gas3] 54 | print("${jInterface.as3Type.name}");
[gas3] 55 |
[gas3] 56 | }
[gas3] 57 | }
[gas3] 58 |
[gas3] 59 |
[gas3] 60 | print(" {");
[gas3] 61 |
[gas3] 62 |
[gas3] 63 | ///////////////////////////////////////////////////////////////////////////
[gas3] 64 | // Write Public Getter/Setter.
[gas3] 65 |
[gas3] 66 | for (jProperty in jClass.properties) {
[gas3] 67 |
[gas3] 68 | if (jProperty.readable || jProperty.writable) {
[gas3] 69 | print("\n");
[gas3] 70 |
[gas3] 71 | if (jProperty.writable) {
[gas3] 72 | print("\n");
[gas3] 73 | print(" function set ${jProperty.name}(
value:${jProperty.as3Type.name}):void;");
[gas3] 74 |
[gas3] 75 | }
[gas3] 76 | if (jProperty.readable) {
[gas3] 77 | print("\n");
[gas3] 78 | print(" function get ${jProperty.name}():${jProperty.as3Type.name};");
[gas3] 79 |
[gas3] 80 | }
[gas3] 81 | }
[gas3] 82 | }
[gas3] 83 | print("\n");
[gas3] 84 | print(" }\n");
[gas3] 85 | print("}");
[gas3]
[gas3] at org.granite.generator.gsp.GroovyTemplate.compile(GroovyTemplate.java:143)
[gas3] at org.granite.generator.gsp.GroovyTemplate.execute(GroovyTemplate.java:157)
[gas3] at org.granite.generator.as3.JavaAs3GroovyTransformer.
generate(JavaAs3GroovyTransformer.java:119)
[gas3] at org.granite.generator.as3.JavaAs3GroovyTransformer.
generate(JavaAs3GroovyTransformer.java:1)
[gas3] at org.granite.generator.Transformer.generate(Transformer.java:71)
[gas3] at org.granite.generator.Generator.generate(Generator.java:83)
[gas3] at org.granite.generator.ant.AntJavaAs3Task.execute(AntJavaAs3Task.java:327)
[gas3] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
[gas3] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[gas3] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[gas3] at sun.reflect.DelegatingMethodAccessorImpl.
invoke(DelegatingMethodAccessorImpl.java:25)
[gas3] at java.lang.reflect.Method.invoke(Method.java:597)
[gas3] at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
[gas3] at org.apache.tools.ant.Task.perform(Task.java:348)
[gas3] at org.apache.tools.ant.Target.execute(Target.java:357)
[gas3] at org.apache.tools.ant.Target.performTasks(Target.java:385)
[gas3] at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1329)
[gas3] at org.apache.tools.ant.Project.executeTarget(Project.java:1298)
[gas3] at org.apache.tools.ant.helper.DefaultExecutor.
executeTargets(DefaultExecutor.java:41)
[gas3] at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.
executeTargets(EclipseDefaultExecutor.java:32)
[gas3] at org.apache.tools.ant.Project.executeTargets(Project.java:1181)
[gas3] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.
run(InternalAntRunner.java:423)
[gas3] at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.
main(InternalAntRunner.java:137)
[gas3] Caused by: org.codehaus.groovy.control.
MultipleCompilationErrorsException: startup failed,
Script1.groovy: 2: expecting EOF, found 'TreeSet' @ line 2, column 26.
[gas3] 1 error
The error at line 2, column 26 is:
[gas3] 2 | Set as3Imports = now TreeSet();
Finding the corresponding line in the original template should be straightforward.
Comments ( Hide )
Anonymous says:Jun 28, 2009 15:58 ( Permalink ) |
Anonymous says:ok, i ended up creating a custom Transformer which adds all info i need into the Groovy template context. its better anyway to have as little java code as possible in the template.. cheers, marcel |
Anonymous says:I'm trying to do a similiar thing, I am using XStream and trying to use GraniteDS to auto-generate Actionscript bindings to JSON objects. I want to use the @XStreamAlias annotation in my Java class which lets me shorten the full Java type name to something more reasonable, but I need my Actionscript generated classes to support that as well. For example: JAVA: I want my Actionscript to be: [Bindable] Is this possible with the groovy templates? |

i'm trying to modify one of the templates so it will add metadata generated from hibernate annotations made in the java beans.
but i'm having a hard time to do that because i just cannot get the validation classes into the groovy compilation. groovy says:
unable to resolve class org.hibernate.validator.Length
i have tried both the eclipse plugin and the ant task without luck so far (i have my bets on the and task though, since i've had a look at the BuilderParentClassLoader), could someone please point me in the correct direction?
thanks, marcel