2.4 Using JET to customize code generation

 < Day Day Up > 



2.4 Using JET to customize code generation

In this section we provide examples that illustrate how to use the Java Emitter Templates (JET) framework provided with EMF to customize code generation. We describe how JET is used to generate the model, edit, and editor plug-ins that we examine in "The generated plug-ins" on page 45, as well as how to approach customizing this code generation.

For an introduction to JET in general, refer to the two-part JET Tutorial by Remko Popma, available from Eclipse Corner, at:

  • http://eclipse.org/articles/Article-JET/jet_tutorial1.html

  • http://eclipse.org/articles/Article-JET/jet_tutorial2.html

2.4.1 .JET-related GenModel properties

In 2.2.1, "The generated plug-ins" on page 45, we described the model, edit, and editor plug-ins that are generated from EMF models. These plug-ins are generated using JET, and we can control this generation by setting the GenModel properties of the model from which we are generating the plug-ins.

The JET-related GenModel properties are described in Table 2-5. All of these properties are represented at the top-level of the GenModel, and are grouped by the GenModel editor into the Templates & Merge category. Setting these properties allows us to override the default JET templates used to generate the model, edit, and editor plug-ins. Descriptions of the properties are provided by the GenModel editor in the status bar whenever you select one of the properties.

Table 2-5: Templates and Merge GenModel properties

Property

XMI attribute

Default

Dynamic Templates

dynamicTemplates

false

Force Overwrite

forceOverwrite

false

Redirection Pattern

redirection

 

Runtime Jar

runtimeJar

false

Template Directory

templateDirectory

 

Update Classpath

updateClasspath

true

The most interesting of these to us are the dynamicTemplates and template Directory properties:

The dynamicTemplates property indicates that the precompiled templates provided by org.eclipse.emf.codegen.ecore.genmodel should be ignored, and that the template implementation should be translated and compiled from dynamic templates.

The templateDirectory indicates the location to look for new templates. A template placed in this location will override the default template with the same name from org.eclipse.emf.codegen.ecore.genmodel.

2.4.2 Writing JET templates

In this section, we customize the generation of the plug-ins described in 2.2.1, "The generated plug-ins" on page 45. By default, these plug-ins are generated from templates located in:

<ECLIPSEHOME>/plugins/org.eclipse.emf.codegen.ecore_<EMFVERSION>/templates

Where <ECLIPSEHOME> is the location where you installed Eclipse and <EMFVERSION> is the version of the EMF plug-in that you have installed.

The org.eclipse.emf.codegen.ecore templates directory contains sub-directories for the model, edit, and editor plug-ins. The files with the extension javajet are the templates. The file extension follows the JET convention of using the extension of the file that is generated by the template concatenated with jet. In this example, we customize the Java code generated for the model plug-in by providing our own version of some of the model templates.

The header template provides the comment that is located at the head of each generated class file. We begin by creating our own templates directory, and by supplying a new Header.javajet. To do this, perform the following steps:

  1. Add a directory called templates to the WorkflowModel project.

  2. Create a new text file called Header.javajet in the templates directory. If you prefer, you can copy the existing Header.javajet file as a basis for your template.

  3. Edit Header.javajet to contain the comment that is to be included at the top of every generated class file. We edit the file to read as shown in Example 2-38:

    Example 2-38: Our version of Header.javajet

    start example
     /**  * WorkflowModel  *  * Copyright (c) 2000, 2003 IBM Corporation and others.  * All rights reserved. This program and the accompanying materials  * are made available under the terms of the Common Public License v1.0  * which accompanies this distribution, and is available at  * http://www.eclipse.org/legal/cpl-v10.html  *  */ 
    end example

  4. Generate the GenModel for the WorkflowModel. This step may be skipped if you already have a GenModel for the WorkflowModel.

  5. Edit the GenModel properties:

    1. Set the dynamicTemplates property to true.

    2. Set the templateDirectory property to the location of your templates directory, for example, /WorkflowModel/templates.

By default, the header is only generated the first time the code is generated from your model, so if you already have a version of the model plug-in in your project, you will need to override this behavior. The merging of existing content with new content is handled by EMF's jmerge. The rules for merging the model, edit, and editor code generated from EMF models are expressed in the file emf-merge.xml. Copy emf-merge.xml into your templates directory from the org.eclipse.emf.ecore.codegen plug-in's templates directory and modify the file so that it includes an additional rule to set the header each time the code is generated, as shown in Example 2-39.

Example 2-39: Merge rules for code generation from WorkflowModel

start example
 <merge:options ... >    ... existing content ...    <merge:pull sourceGet="CompilationUnit/getHeader"       targetPut="CompilationUnit/setHeader"/> </merge:options> 
end example

Now when you generate the model plug-in and take a look at the generated code, the contents of Header.javajet should appear in place of the default header.

Tip 

Whenever you modify a template, you may need to close and then re-open the GenModel file before regenerating code so that the new version of the template is used.

JET templates use a simplified Java Server Pages (JSP) syntax. You can get a feel for how JET templates work by examining and modifying the templates used to generate the interface and implementation corresponding to each class in a model. Begin by making a copy of the templates into the WorkflowModel project's templates directory:

  1. Create a model sub-directory within the templates directory in the WorkflowModel project.

  2. Copy the files Interface.javajet Class.javajet and from the model directory in the org.eclipse.emf.codegen.ecore plug-in's templates directory to the directory created in the previous step.

We are mirroring the templates directory structure used by the org.eclipse.emf.codegen.ecore plug-in, as we are essentially replacing its templates with our own versions.

At the first line in Interface.javajet, we see the tag shown in Example 2-40.

Example 2-40: The jet directive

start example
 <%@ jet package="org.eclipse.emf.codegen.ecore.templates.model" imports="java.util.* org.eclipse.emf.codegen.ecore.genmodel.*"  %> 
end example

The tags used within JET templates are identified by an opening <% and a closing %>. Inside the tags, you can use Java code to script what is generated from the template, or you can use special tags to represent JET directives or expressions. The jet tag shown in Example 2-40 is a directive. Expression tags are used to create values based on expressions in the files generated from the templates.

Directives start with <%@ and a name that identifies them, and expressions start with <%=. We can see examples of each of these types of tags just a few lines further down in Interface.javajet, as shown in Example 2-41.

Example 2-41: JET scriptlet, directive and expression tags

start example
 <%GenClass genClass = (GenClass)argument; GenPackage genPackage = genClass.getGenPackage(); GenModel genModel=genPackage.getGenModel();%> <%@ include file="../Header.javajet"%> package <%=genPackage.getInterfacePackageName()%>; 
end example

The first tag shown in Example 2-41, is a scriptlet that declares and initializes variables that can be referenced from other tags in the rest of the template. In this case, we see genClass, which represents the class for which the interface is being generated using this template, genPackage, which represents its containing package, and genModel, which is the model that contains genPackage.

The second tag shown in Example 2-41 is another directive; this one indicates that the code produced from the Header.javajet template is included at this point in the code generated from this Interface template. The include directive has a single attribute, file, that indicates the location of the file to be included. There are two directives that can be used within JET templates; the include directive, seen in this example, and the jet directive seen in Example 2-40. The jet directive may appear only on the first line of a template, and every template must have a jet directive. The attributes of the jet directive are described in the JET Tutorial part one, (Introduction to JET). Note Header.javajet did not have a jet directive, because it is just a fragment included into other templates.

The third tag from Example 2-41 is an expression tag, which in this case provides the expression used to get the package name for the interface that is generated using the template.

You may notice that the names of most of the types and methods that end up in the generated code come from expression tags that call methods provided by the GenModel. The reason for this is that the code is generated from GenModel objects that are provided as arguments to each template. We can change the structure or the literal content of the generated code by editing the templates, however changing the names of the methods and types in the generated code would require providing our own implementation of the interfaces in org.eclipse.emf.codegen.ecore.genmodel and then providing those objects as arguments to the templates. For our additions to the generated code, we edit the templates only. If you would like to find out more about providing different objects as arguments to a JET template, please refer to the JET Tutorial.

We modify the templates to add additional methods for multi-valued features to get an element from the list of values by position. Because WorkflowModelElement is the supertype of every other class in the WorkflowModel, and it has an name attribute, we know that every object in the model can have a name, hence we also add template methods to get list members by name.

In Interface.javajet, we can see that the section of the template that generates accessor methods for features is contained within a for-loop that iterates over the features of the class. We are adding template to generate additional methods for some features, so we make our additions within this loop.

Example 2-42 shows concrete examples of the method signatures that we are adding to the generated interfaces. In this case, the methods are for the inputs feature of the class WorkflowNode.

Example 2-42: Concrete example of additional method signatures

start example
 InputPort getInputs(int index); InputPort getInputs(String name); 
end example

To generate similar methods for all multi-valued features using the templates, we substitute types and methods specific to the inputs reference with expression tags. We use method calls on genFeature, which represents each feature, to provide the values. We also add @generated to indicate that these methods are now generated. Example 2-43 shows the code that we add to Interface.javajet to generate the extra methods in the interface for each generated type. We wrap the template for the new methods inside conditions, to make sure that we only generate these methods for multi-valued features, that is, features that are regular list types.

Example 2-43: Interface template fragment for additional methods

start example
 <%for (Iterator i=genClass.getGenFeatures().iterator(); i.hasNext();) {GenFeature genFeature = (GenFeature)i.next();%>    ... existing content ... <%if (genFeature.isListType()) {%> <%if (!genFeature.isMapType()){%>    /**     * Get an item from the list by position     * @generated     */    <%=genFeature.getQualifiedListItemType()%> <%=genFeature.getGetAccessor()%>(int index);    /**     * Get an item from the list by name     * @generated     */    <%=genFeature.getQualifiedListItemType()%> <%=genFeature.getGetAccessor()%>(String name); <%}//if%> <%}//if%> <%}//for%> 
end example

We use a similar process to template the implementation of the additional methods in Class.javajet. Example 2-44 shows concrete examples of the implementation of the methods that we wish to add.

Example 2-44: Concrete example of additional methods

start example
 public InputPort getInputs(int index) {    return (InputPort) this.getInputs().get(index); } public InputPort getInputs(String name) {    Iterator i = this.getInputs().iterator();    while (i.hasNext()) {       InputPort input = (InputPort) i.next();       if (true == name.equals(input.getName()))          return input;    }    return null; } 
end example

Again, we generalize by substituting expression tags for the parts of the method implementations that are specific to the feature. Example 2-45 shows the code that we add to Class.javajet. We add the method templates to the existing for-loop that iterates over all of the implemented features. Notice that because we introduce the class java.util.Iterator into the generated code in the second additional method, we need to use the getImportedName method from the GenModel to make sure it is added to the imports in the generated class.

Example 2-45: Class template fragment for additional methods

start example
 <%for (Iterator i=genClass.getImplementedGenFeatures().iterator(); i.hasNext();) {GenFeature genFeature = (GenFeature)i.next();%>    ... existing content ... <%if (genFeature.isListType()) {%> <%if (!genFeature.isMapType()){%>    /**     * Get an item from the list by index     * @generated     */    public <%=genFeature.getQualifiedListItemType()%> <%=genFeature.getGetAccessor()%>(int index){       return (<%=genFeature.getQualifiedListItemType()%>) this.<%=genFeature.getGetAccessor()%>().get(index);    }    /**     * Get an item from the list by name     * @generated     */    public <%=genFeature.getQualifiedListItemType()%> <%=genFeature.getGetAccessor()%>(String name){       <%=genModel.getImportedName("java.util.Iterator")%> i = this.<%=genFeature.getGetAccessor()%>().iterator();       while (i.hasNext()) {          <%=genFeature.getQualifiedListItemType()%> l = (<%=genFeature.getQualifiedListItemType()%>) i.next();          if (name.equals(l.getName()))             return l;       }       return null;    } <%}//if%> <%}//if%> <%}//for%> 
end example

Now, when you generate the model plug-in, you should see the additional methods in the generated interfaces and implementation classes. You may also notice a new project .JETEmitters appear in your workspace in the resource view. This project is created by default when the templates are translated, as described in the JET Tutorial, Part Two, and it contains the actual implementations of our templates.



 < Day Day Up > 



Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Eclipse Development Using the Graphical Editing Framework And the Eclipse Modeling Framework
ISBN: 0738453161
EAN: 2147483647
Year: 2004
Pages: 70
Authors: IBM Redbooks

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net