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
1<pattern>
2...
3
4<xml __filename=""
5 __path=""
6 __root=""
7 __publicid=""
8 __systemid=""
9 __baseuri=""
10 __foreach="package|object|attribute|reference|object.attribute|object.reference"
11 __package=""
12 __condition=""
13 __var0="">
14
15 <your-root-element
16 __foreach="package|object|attribute|reference|object.attribute|object.reference"
17 __package=""
18 __condition=""
19 __var0="">
20
21 <some-element someAttribute="any attribute"
22 someOtherAttribute="another attribute">
23 </some-element>
24 <some-other-element ></some-other-element>
25
26 ...any valid xml allowed here
27
28 <some-recurring-element someattribute=""
29 __foreach=""
30 __package="">
31 </some-recurring-element>
32 </your-root-element>
33</xml>
34
35...
36</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
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<xml __filename="hibernate.cfg.xml"
2 ...
3 __root="hibernate-configuration"
4 __publicid="-//Hibernate/Hibernate Configuration DTD//EN"
5 __systemid="http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"
6 ...>
7
8 <hibernate-configuration>
9 ...
10 </hibernate-configuration>
11</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
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.
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:
1<xml
2 __filename="${object.name}.hbm.xml"
3 __foreach="object"
4 __package="domain_model"
5 __path=""
6 __root="hibernate-mapping"
7 __systemid="http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
8
9 <hibernate-mapping package="com.metafactory.example.dao">
10 <xml:class name="${object.name}" table="${object.name}" lazy="true">
11 <id name="${firstLower(${object.name})}Id" column="${allUpper(${object.name}_id)}">
12 <generator class="native"></generator>
13 </id>
14 ....
15 </xml:class>
16 </hibernate-mapping>
17</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).
This example is a little bit outdated.
The required xml in the pattern appears as follows:
1<xml
2 __filename="${firstLower(${object.name})}${var3}.page.xml"
3 __path="${pattern.property.webapp.directory}"
4 __foreach="object.property.edit.page"
5 __package="domain_model"
6 __var3="${forEachPropertyValue}"
7 __var4="${firstLower(${object.name})}Dao"
8 __skip="true">
9 <page login-required="true"
10 __ns="http://jboss.com/products/seam/pages"
11 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
12 xsi:schemaLocation="http://jboss.com/products/seam/pages
13 http://jboss.com/products/seam/pages-2.2.xsd">
14 <restrict>${snippet.seam.page.edit.hasPermission}</restrict>
15
16 <begin-conversation join="true" flush-mode="MANUAL"/>
17 <action execute="#{${firstLower(${object.name})}Home.wire}"/>
18
19 <description>#{messages['edit.text']}
20 #{messages['${firstLower(${object.name})}.text']}
21 (#{${firstLower(${object.name})}Home.${firstLower(${object.name})}Id})
22 </description>
23
24 <param name="${firstLower(${object.name})}From"/>
25 <param name="${firstLower(${object.name})}Id"
26 value="#{${firstLower(${object.name})}Home.${firstLower(${object.name})}Id}"/>
27 <param name="fromIdName" />
28 <param name="fromIdValue" />
29
30 <param name="${reference.name}From" __foreach="reference"
31 __condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1"/>
32 <param name="${firstLower(${reference.type})}Id"
33 value="#{${firstLower(${reference.type})}Home.${firstLower(${reference.type})}Id}"
34 __foreach="reference"
35 __condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1" />
36
37 <snippet_to_xml>${snippet.seam.page.edit.referencennParams}</snippet_to_xml>
38
39 <param name="firstResult" value="#{${var4}.firstResult}"/>
40 <param name="maxResults" value="#{${var4}.maxResults}" />
41 <param name="sort" value="#{${var4}.orderColumn}"/>
42 <param name="dir" value="#{${var4}.orderDirection}"/>
43
44 <navigation from-action="#{${firstLower(${object.name})}Home.persist}">
45 <rule if-outcome="persisted">
46 <end-conversation/>
47 <redirect view-id="/#{empty ${firstLower(${object.name})}From ?
48 '${firstLower(${object.name})}Search' : ${firstLower(${object.name})}From}.xhtml"/>
49 </rule>
50 </navigation>
51
52 <navigation from-action="#{${firstLower(${object.name})}Home.update}">
53 <rule if-outcome="updated">
54 <end-conversation/>
55 <redirect view-id="/#{empty ${firstLower(${object.name})}From ?
56 '${firstLower(${object.name})}Search' : ${firstLower(${object.name})}From}.xhtml"/>
57 </rule>
58 </navigation>
59
60 <navigation from-action="#{${firstLower(${object.name})}Home.remove}">
61 <rule if-outcome="removed">
62 <end-conversation/>
63 <redirect view-id="/${firstLower(${object.name})}Search.xhtml"/>
64 </rule>
65 </navigation>
66
67 </page>
68</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