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., "1.2.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:

MyTemplate.gsp
// Generated by Gas3 v.${gVersion}.

package ${jClass.as3Type.packageName} {

    public class ${jClass.as3Type.name} {
        ...
    }
}

If you execute this template with Gas3 1.2.0 and Java class named org.test.MyClass, the output will be:

MyClass.as
// Generated by Gas3 v.1.2.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:

interfaceBase.gsp
<%--
  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:

In memory Groovy script
    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:

NamedEntity.java
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:

NamedEntityBase.as
/**
 * 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:

Sample error output
[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

Anonymous says:


Browse Space

- Pages
- Blog
- Labels
- Attachments
- Bookmarks
- Mail
- Advanced

Explore Confluence

- Popular Labels
- Notation Guide

Your Account

Log In

Other Features

Add Content


Copyright © 2011 Granite Data Services S.A.S. All Rights Reserved.