../../_images/Revised.png

5.4.4. XML files

MetaFactory produces several kinds of files:

  • java source code,

  • xml files and

  • plain text files like JavaScript, HTML, JSON, YAML.

The <xml> element is the way to create XML files such as Maven’s pom.xml.

Usage

Listing 5.38 <XML files element> Syntax
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<pattern>
...

<xml __filename=""
     __path=""
     __root=""
     __publicid=""
     __systemid=""
     __baseuri=""
     __foreach="package|object|attribute|reference|object.attribute|object.reference"
     __package=""
     __condition=""
     __var0="">

    <your-root-element
        __foreach="package|object|attribute|reference|object.attribute|object.reference"
        __package=""
        __condition=""
        __var0="">

        <some-element someAttribute="any attribute"
            someOtherAttribute="another attribute">
        </some-element>
        <some-other-element ></some-other-element>

        ...any valid xml allowed here

        <some-recurring-element someattribute=""
            __foreach=""
            __package="">
        </some-recurring-element>
    </your-root-element>
</xml>

...
</pattern>

The <xml> element is the root element of the created xml document. Everything is copied into this root element except some predefined attributes.

Namespaces that you define in children of the <xml> element (e.g. the Maven namespace for pom.xml) are copied to its generated XML output. However, the MetaFactory namespaces that belong to the pattern are removed from the result.

Attributes

Table 5.48 attributes of <xml/>

Name

Details

__filename

Used to set the name of the created xml file. Mandatory

__path

Path where the xml file is saved.

__root

Used to set root of a DOCTYPE. Suppose this DOCTYPE must be declared for a xml file:

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
                        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

These attributes of the <xml> element need to be set:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<xml __filename="hibernate.cfg.xml"
     ...
     __root="hibernate-configuration"
     __publicid="-//Hibernate/Hibernate Configuration DTD//EN"
     __systemid="http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"
     ...>

    <hibernate-configuration>
    ...
    </hibernate-configuration>
</xml>

__publicid

Used to set the publicid part of a DTD reference. Used to set a DOCTYPE

__systemid

Used to set the systemid part of a DTD reference. Used to set a DOCTYPE

__baseuri

Set the xml:base attribute of the xml document.

__foreach

Indicates if XML File must be created foreach element in model (value of foreach determines what model element). Possible values: package, object, attribute, reference, object.attribute, object.reference

__condition

An expression that evaluates to true or false. Generation of the current file wil precede if true and otherwise not.

Example:

__condition="${object.name}=Person"

In this case, the package is generated only if the current model object has the name: Person. This works only if all model objects are iterated (foreach=”object”).

__package

The name of the package in the model (model.xml) that must be used to apply the foreach attribute.

__var0 .. __var20

Value that you wish to store in the pre-defined variable with the name var0 (up to var20). Reference to this value can be made later by means of: ${var0} (up to var20). This can be used in both the code_instruction and in a snippet (freemarker or velocity template).

Sub elements

Table 5.49 sub elements of <xml/>

Name

Details

Number

body

Used as root element of the XML file to create, so only 1 child is allowed. Below this child element every well formed xml is allowed.

If iteration is required all child elements can also use these reserved attributes:

__foreach, __condition, __package  and  __var0 till __var5

0 or 1

Namespace

The entire code_instruction has to be in the code_instruction namespace, whereas the xml file to be created does not. The code_instruction namespace is therefore removed from the result. Occasionally no namespace is given for the document to be created, however it does contain elements that also occur in the code_instruction namespace. If, according to the code_instruction, an element is not valid, the xml parser gives an error message. To prevent the parser treating the element as part of the code_instruction namespace, we can use the namespace reserved with xml: as the prefix. Elements with this prefix are created, without this prefix, in the resulting xml document. This xml prefix indicates that the element doesn’t belong to the code_instruction namespace.

Eye S A better way to deal with this problem is defining the code_instruction namespace with a prefix (instead of default namespace).

An example of a situation where this is necessary occurs when creating hibernate xml mapping files for linking classes to database tables. In this mapping document use is made of a class element:

Listing 5.39 <XML element> Syntax
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<xml
    __filename="${object.name}.hbm.xml"
    __foreach="object"
    __package="domain_model"
    __path=""
    __root="hibernate-mapping"
    __systemid="http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

    <hibernate-mapping package="com.metafactory.example.dao">
        <xml:class name="${object.name}" table="${object.name}" lazy="true">
            <id name="${firstLower(${object.name})}Id" column="${allUpper(${object.name}_id)}">
                <generator class="native"></generator>
            </id>
            ....
        </xml:class>
    </hibernate-mapping>
</xml>

Snippets producing xml

Sometimes it is useful for the whole document (or a part) to be created by means of a “snippet”, in order to benefit from the power of velocity or freemarker. To utilise a snippet when creating the document and still maintain well-formed xml, you can use the the reserved element ‘<snippet_to_xml>’:

<snippet_to_xml>${snippet.xml.path.to.file.templatename}</snippet_to_xml>

The advantage is that the snippet itself does not have to make a 1-root element xml. All xml elements created are added as children of the parent of the special <snippet_to_xml> element.

Example

In the following example, the .page.xml document is created of a JBoss Seam web application. In this document, several param elements occur each of which is a child of the root element page. Some param elements appear to have a rather more complex structure, making it impossible (or difficult) to describe these with _foreach. Use should therefore be made of a velocity snippet (or freemarker).

Eye S This example is a little bit outdated.

The required xml in the pattern appears as follows:

Listing 5.40 <XML element> Example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<xml
   __filename="${firstLower(${object.name})}${var3}.page.xml"
   __path="${pattern.property.webapp.directory}"
   __foreach="object.property.edit.page"
   __package="domain_model"
   __var3="${forEachPropertyValue}"
   __var4="${firstLower(${object.name})}Dao"
   __skip="true">
        <page login-required="true"
              __ns="http://jboss.com/products/seam/pages"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://jboss.com/products/seam/pages
                                  http://jboss.com/products/seam/pages-2.2.xsd">
            <restrict>${snippet.seam.page.edit.hasPermission}</restrict>

            <begin-conversation join="true" flush-mode="MANUAL"/>
            <action execute="#{${firstLower(${object.name})}Home.wire}"/>

            <description>#{messages['edit.text']}
                #{messages['${firstLower(${object.name})}.text']}
                (#{${firstLower(${object.name})}Home.${firstLower(${object.name})}Id})
            </description>

            <param name="${firstLower(${object.name})}From"/>
            <param name="${firstLower(${object.name})}Id"
                   value="#{${firstLower(${object.name})}Home.${firstLower(${object.name})}Id}"/>
            <param name="fromIdName" />
            <param name="fromIdValue" />

            <param name="${reference.name}From" __foreach="reference"
                   __condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1"/>
            <param name="${firstLower(${reference.type})}Id"
                   value="#{${firstLower(${reference.type})}Home.${firstLower(${reference.type})}Id}"
                   __foreach="reference"
                   __condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1" />

            <snippet_to_xml>${snippet.seam.page.edit.referencennParams}</snippet_to_xml>

            <param name="firstResult" value="#{${var4}.firstResult}"/>
            <param name="maxResults" value="#{${var4}.maxResults}" />
            <param name="sort" value="#{${var4}.orderColumn}"/>
            <param name="dir" value="#{${var4}.orderDirection}"/>

            <navigation from-action="#{${firstLower(${object.name})}Home.persist}">
                <rule if-outcome="persisted">
                    <end-conversation/>
                    <redirect view-id="/#{empty ${firstLower(${object.name})}From ?
                    '${firstLower(${object.name})}Search' : ${firstLower(${object.name})}From}.xhtml"/>
                </rule>
            </navigation>

            <navigation from-action="#{${firstLower(${object.name})}Home.update}">
                <rule if-outcome="updated">
                    <end-conversation/>
                    <redirect view-id="/#{empty ${firstLower(${object.name})}From ?
                    '${firstLower(${object.name})}Search' : ${firstLower(${object.name})}From}.xhtml"/>
                </rule>
            </navigation>

            <navigation from-action="#{${firstLower(${object.name})}Home.remove}">
                <rule if-outcome="removed">
                    <end-conversation/>
                    <redirect view-id="/${firstLower(${object.name})}Search.xhtml"/>
                </rule>
            </navigation>

        </page>
</xml>

Contents of the velocity template:

##stop if $currentModelObject is null
#if(!$currentModelObject)
  $generator.error("currentModelObject not found in context")
#end
#set($modelobject=$currentModelObject)  ## element of model
#set($modelObjectName=$currentModelObject.getAttributeValue("name"))
#set($modelObjectNameFL=${generator.firstLower($modelObjectName)})
#set($nnReferenceProperties=$generator.getPropertyElements($modelobject,"reference.nn"))
#foreach($nnReferenceProperty in $nnReferenceProperties)
  #set($nnReferencePath=$nnReferenceProperty.getTextNormalize())
  ##// handle nn reference: $nnReferencePath
  #set($tokens=$nnReferencePath.split('\.'))
  #set($tokenList=$generator.asList($tokens))
  #set($startObject=$tokenList.get(0))
  ##find the modelObject with name $startObject => this is just a check, because we only need the name of the object which we already know
  #set($modelPackage=$context.getModelPackage("domain_model"))
  #set($nnReferenceObjectElement=$generator.findChildByAttribute($modelPackage, "object", "name", "$startObject"))
  #set($nnReferenceObjectName=$nnReferenceObjectElement.getAttributeValue("name"))
  #set($nnReferenceObjectNameFL=${generator.firstLower($nnReferenceObjectName)})
        <!-- The ${modelObjectNameFL}Edit page can be called by ${nnReferenceObjectNameFL}View,
                 so we need to remember ${nnReferenceObjectNameFL}From and ${nnReferenceObjectNameFL}Id -->
   <param name="${nnReferenceObjectNameFL}From"/>
   <param name="${nnReferenceObjectNameFL}Id" value="#{${nnReferenceObjectNameFL}Home.${nnReferenceObjectNameFL}Id}"/>
#end