MENU

  /   SDA voorbeeld waar snippets gebruikt worden

Dit voorbeeld laat zien hoe een template engine gebruikt kan worden om code te produceren. In de vorige HelloWorld voorbeelden werd alle code gemaakt d.m.v. het XML pattern. XML is weliswaar prima om de structuur van een java class of interface vast te leggen, het is echter minder geschikt om de inhoud van een methode te maken. Dat is de reden waarom er in MetaFactory gebruikt kan worden van een snippet (een Freemarker of Velocity template). Een snippet wordt aangeroepen d.m.v. de volgende expressies:

${fmsnippet.pad.naar.jouw.freemarker.template}

of

${snippet.pad.naar.jouw.velocity.template}

Als MetaFactory deze expressie tegenkomt, dan wordt Freemarker of Velocity gebruikt om de code te maken.

Met het volgende pattern worden pojo classes gemaakt op basis van een model en een pattern. Dit pattern gebruikt Freemarker voor het maken van de toString, equals en hashCode methodes:

<?xml version="1.0" encoding="UTF-8"?>
<pattern xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.firstbase.nl/xsd/personaliom/pattern"
  xsi:schemaLocation="http://www.firstbase.nl/xsd/personaliom/pattern http://www.firstbase.nl/xsd/personaliom/pattern.xsd"
>
  <properties>
    <java.main.directory>src/main/java/</java.main.directory>
    <conf.main.directory>src/main/resources</conf.main.directory>
    <java.test.directory>src/test/java/</java.test.directory>
    <conf.test.directory>src/test/resources</conf.test.directory>

    <base>org.metafactory.example.usingsnippets</base>

    <model.implementation.package>${pattern.property.base}.model.implementation</model.implementation.package>
    <model.implementation.class>${object.name}</model.implementation.class>
    <model.implementation.reference>${reference.type}</model.implementation.reference>
    <model.implementation.fqcn>${pattern.property.model.implementation.package}.${pattern.property.model.implementation.class}</model.implementation.fqcn>
  </properties>

<!--
  A pattern package for creating simple pojo's with equals, hashcode, toString, but without jpa annotations.
 -->
  <package
    name="${pattern.property.model.implementation.package}"
    path="${pattern.property.java.main.directory}"
    package="domain_model"
    skip="true">
    <class name="${pattern.property.model.implementation.class}" visibility="public" enum="${model.property.object.enum}" foreach="object">
      <libraries>
      </libraries>
      <enumConstants condition="${model.property.object.enum}==true">${fmsnippet.java.pojo.enum.enumConstants}</enumConstants>
      <attribute name="${attribute.name}" foreach="attribute" access="rw">
        <datatype>${attribute.type}</datatype>
        <apicomment>${fmsnippet.java.pojo.attribute.apicomment}</apicomment>
        <body condition="${propertyExists(${model.property.attribute.entity.default.value})}">${model.property.attribute.entity.default.value}</body>
        <setterBody>${fmsnippet.java.pojo.operation.setter}</setterBody>
      </attribute>
      <attribute name="${reference.name}" foreach="reference" access="rw" condition="${fmsnippet.condition.reference_1_and_enum}">
        <datatype>${pattern.property.model.implementation.reference}</datatype>
      </attribute>
      <attribute name="${reference.name}" foreach="reference" access="rw" condition="${fmsnippet.condition.reference_1_no_enum}"> <!-- ${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1 -->
        <datatype>${pattern.property.model.implementation.reference}</datatype>
      </attribute>
      <attribute name="${reference.name}Set" foreach="reference" access="rw" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <library>java.util.HashSet</library>
        <library>java.util.Set</library>
        <datatype>
          <![CDATA[Set<${pattern.property.model.implementation.reference}>]]>
        </datatype>
        <body>
          <![CDATA[new HashSet<${pattern.property.model.implementation.reference}>()]]>
        </body>
      </attribute>
      <operation name="constructor" visibility="${fmsnippet.attribute.visibility_of_constructor}" foreach="currentModelObject.property.createConstructor" var0="${forEachPropertyValue}">
        <parameter name="${attribute.name}" foreach="attribute.property.useInConstructor" condition="${forEachPropertyValue}=${var0}">
          <datatype> ${attribute.type}</datatype>
        </parameter>
        <parameter name="${reference.name}" foreach="reference.property.useInConstructor" condition="${forEachPropertyValue}=${var0}">
          <datatype> ${reference.type}</datatype>
        </parameter>
        <body>${fmsnippet.java.pojo.operation.constructor.constructor-use-fields} </body>
      </operation>

      <operation name="addTo${firstUpper(${reference.name})}" foreach="reference" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <parameter name="${firstLower(${reference.name})}">
          <datatype>${pattern.property.model.implementation.reference}</datatype>
        </parameter>
        <body>${fmsnippet.java.pojo.operation.addToSet}</body>
      </operation>
      <operation name="deleteFrom${firstUpper(${reference.name})}" foreach="reference" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <parameter name="${firstLower(${reference.name})}">
          <datatype>${pattern.property.model.implementation.reference}</datatype>
        </parameter>
        <body>${fmsnippet.java.pojo.operation.deleteFromSet}</body>
      </operation>
      <operation name="equals" visibility="public" condition="${model.property.object.enum}!=true">
        <!--  condition="${model.property.object.enum}!=true" -->
        <apicomment>FIXME define businesskeys in model</apicomment>
        <parameter name="return">
          <datatype>boolean</datatype>
        </parameter>
        <parameter name="${firstLower(${object.name})}">
          <datatype>${pattern.property.model.implementation.class}</datatype>
        </parameter>
        <body>${fmsnippet.java.pojo.operation.equals.equals-use-businesskey}</body>
      </operation>
      <operation name="hashCode" condition="${model.property.object.enum}!=true">
        <apicomment>FIXME define businesskeys in model</apicomment>
        <parameter name="return">
          <datatype>integer</datatype>
        </parameter>
        <body>${fmsnippet.java.pojo.operation.hashcode.hashCode-use-businesskey}</body>
      </operation>
      <operation name="toString">
        <parameter name="return">
          <datatype>String</datatype>
        </parameter>
        <!--
        <body>${fmsnippet.java.pojo.operation.tostring.toString-use-businesskey}</body>
         -->
        <body>${fmsnippet.java.pojo.operation.tostring.toString-use-all-attributes}</body>
      </operation>
    </class>
  </package>

</pattern>

Dit is het model dat gebruikt is:

<?xml version="1.0" encoding="UTF-8"?>
<model xmlns="http://www.firstbase.nl/xsd/personaliom/model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.firstbase.nl/xsd/personaliom/model http://www.firstbase.nl/xsd/personaliom/model.xsd">
  <package name="domain_model">
    <object name="Person">
      <properties>
        <enum>false</enum>
        <name.plural>Persons</name.plural>
      </properties>
      <attribute name="firstName" type="String" length="50" notnull="true" >
        <properties>
          <businesskey>2</businesskey>
        </properties>
      </attribute>
      <attribute name="lastName" type="String" length="50" notnull="true" >
        <properties>
          <businesskey>1</businesskey>
        </properties>
      </attribute>
      <reference name="homeAddress" type="Address" notnull="true" multiplicity="1..1" />
      <reference name="workAddress" type="Address" notnull="false" multiplicity="0..1" />
      <reference name="phone" type="Phone" notnull="false" multiplicity="0..n" />
    </object>
    <object name="Address">
      <properties>
        <enum>false</enum>
        <name.plural>Addresses</name.plural>
      </properties>
      <attribute name="streetName" type="String" length="100" notnull="true" />
      <attribute name="zipCode" type="String" length="10" notnull="false" >
        <properties>
          <businesskey>1</businesskey>
        </properties>
      </attribute>
      <attribute name="city" type="String" length="50" notnull="true" />
      <attribute name="country" type="String" length="50" notnull="false" >
        <properties>
          <businesskey>2</businesskey>
        </properties>
      </attribute>
    </object>
    <object name="Phone">
      <properties>
        <enum>false</enum>
        <name.plural>Phones</name.plural>
      </properties>
      <attribute name="number" type="String" length="30" notnull="true" >
        <properties>
          <businesskey>1</businesskey>
        </properties>
      </attribute>
      <attribute name="description" type="String" length="100" notnull="true" />
      <reference name="person" type="Person" notnull="true" multiplicity="1..1" />
    </object>
  </package>
</model>

Voor de volledigheid, dit is de MetaFactory project file:

<?xml version="1.0" encoding="UTF-8"?>
<personal-iom-project xsi:schemaLocation="http://www.firstbase.nl/xsd/personaliom/project http://www.firstbase.nl/xsd/personaliom/project.xsd" allowDeprecated="true" xmlns="http://www.firstbase.nl/xsd/personaliom/project" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <model>model.xml</model>
  <pattern>pattern.xml</pattern>
  <snippetsFolder>snippets</snippetsFolder>
  <output>
    <path type="java"></path>
    <path type="xml"></path>
    <path type="file"></path>
  </output>
  <businessStyle>
    <createDefaultJavaDocForClass>true</createDefaultJavaDocForClass>
    <addAuthorToDefaultJavaDocForClass>true</addAuthorToDefaultJavaDocForClass>
    <createDefaultJavaDocForInterface>true</createDefaultJavaDocForInterface>
    <addAuthorToDefaultJavaDocForInterface>true</addAuthorToDefaultJavaDocForInterface>
  </businessStyle>
</personal-iom-project>

Dit is de door MetaFactory geproduceerde Phone class, de toString methode is highlighted:

package org.metafactory.example.usingsnippets.model.implementation;

/**
 * Phone - Created by MetaFactory: Automation of Software Development
 *
 * @author - marnix
 */
public class Phone
{
  private String number;

  private String description;

  private Person person;

// Getters and setters omitted

  /**
   * toString -
   *
   * @return String
   */
  public String toString()
  {
    StringBuilder result = new StringBuilder("Phone: ");

    result.append("number=" + number);
    result.append(", ");

    result.append("description=" + description);

    return result.toString();
  }

  /**
   * equals - Fields used as businesskey: 1) number.
   *
   * @param phone
   * @return boolean
   */
  public boolean equals(final Phone phone)
  {
    boolean result = true;
    // If all business keys are null, return false
    if (this.getNumber() == null)
    {
      result = false;
    }
    else
    {
      if (this.getNumber() != null && !this.getNumber().equals(phone.getNumber()))
      {
        result = false;
      }
    }
    return result;
  }

  /**
   * hashCode - Fields used as businesskey: 1) number.
   *
   * @return integer
   */
  public int hashCode()
  {
    int result;
    result = 14;

    if (number != null)
    {
      result = 29 * result + this.getNumber().hashCode();
    }

    return result;
  }
}

Deze freemarker template is gebruikt voor het maken van de toString methode. Deze template wordt aangeroepen op regel 100 in het pattern:

<#--stop if $currentModelObject is null-->
<#if !(currentModelObject)??>  <#stop "currentModelObject not found in context" ></#if>

<#assign modelObjectName = currentModelObject.getAttributeValue("name")>
<#assign modelObjectNameFL = modelObjectName?uncap_first>

  StringBuilder result = new StringBuilder("${modelObjectName}: ");

<#assign attributes = currentModelObject.getChildren("attribute", nsModel)>
<#list attributes as attribute>
  <#assign attributeName = attribute.getAttributeValue( "name")>
  <#assign attributeType = attribute.getAttributeValue( "type")>
  <#assign javaType = generator.getJavaType(attributeType)>
  <#-- Add a , after first iteration of this loop using index variable created by freemarker -->
  <#if (attribute_index > 0) >
    result.append(", ");
  </#if>

  <#if (generator.isPrimitiveJavaType(attributeType)) >
    result.append("${attributeName}=" + ${attributeName}); // I am primitive
  <#else>
  <#-- handle primitive types other than objects -->
    <#if (javaType == "String") >
      result.append("${attributeName}=" + ${attributeName});
    <#else>
      result.append("${attributeName}=" + ${attributeName}.toString());
    </#if>
  </#if>
</#list>

return result.toString();

Met bovenstaand pattern en freemarker template heeft de ontwikkelaar zelf precies geprogrammeerd hoe hij z’n toString methode wil hebben. Op meta niveau is deze methode beschreven voor alle classes. Met SDA (Software Development Automation) is de ontwikkelaar in staat om zijn eigen programmeerwerk volledig te automatiseren.

Het MetaFactory project voor dit voorbeeld kan hier gedownload worden: example-using-snippets