DOM processors

When creating reports from a run log, or package documentation from a test suite, QF-Test operates in a two-step process. The first step creates an XML document which is transformed to an HTML document in the second step. Both transformations are done using XSLT stylesheets.

Note The term DOM (for Document Object Model) also applies to XML documents, not only to HTML web pages. This section is all about XML and XSLT and not about the DOM of a web SUT.

However, XSLT stylesheets are not very useful when it comes to parsing plain text. The 'Comment' fields of 'Test set', 'Test case', 'Test step', 'Package' or 'Procedure' nodes often contain some internal structure that XSLT cannot make use of. Additionally, the internal structures employed by users may vary, depending on the conventions used. A typical example is the use of JavaDoc tags to describe parameters of 'Procedure' nodes. Here's an example 'Comment' for the 'Procedure' qfs.swing.menu.select from our standard library after the first step of the transformation:

<comment>Select an item from a menu.
For example: for the File -> Open action, the QF-Test component ID
 "File" is the menu, and the QF-Test component ID "Open" is the item.

@param  client  The name of the SUT client.
@param  menu    The QF-Test ID of the menu.
@param  item    The QF-Test ID of the menu item.</comment>
Example 53.40:  Example 'Comment' after first step transformation

It is very difficult to make use of the @param tags with XSLT alone. This is where DOM processors enter the scene. Between the first and second transformation, QF-Test can optionally run an additional transformation directly on the DOM tree of the XML document generated by the first step. During that extra transformation, QF-Test traverses the DOM tree, calling the registered DOM processors for each node to give them a chance to manipulate the DOM.

Note For JDK 1.4 the XML Document Object Model (DOM) is part of the standard API. For earlier JDK versions it is provided by XML parser xerces (from the Apache project) which QF-Test includes. The API documentation for the DOM is available at http://download.oracle.com/javase/1.5.0/docs/api/org/w3c/dom/package-summary.html.

The DOMProcessor interface

The interface that must be implemented is de.qfs.apps.qftest.extensions.DOMProcessor. It is quite trivial:

 
 
Element process(Element node)
Process one element node.
Returns An element node or null. If null is returned, the child nodes of the node are processed normally. Otherwise, the child nodes are not processed. If a node other than the original node is returned, the original node is replaced with the return value.
 
 

In the process method, the processor is free to do whatever it likes, as long as it constrains itself to the node passed in and its sub-nodes. The node can be replaced simply by returning some different element node.

Note To remove an element node from the DOM, the DOMProcessor must be registered on an ancestor of the node, its parent node, for example. The current node may not be removed from the DOM in the process method.

QF-Test provides two example implementations of DOM processors. The ParagraphProcessor is available in the misc directory for illustration. It is used internally to break comments which contain empty lines into paragraphs.

Also to be found in the misc directory is the DocTagProcessor which is used to transform JavaDoc tags like @param or @author to an XML DOM sub-tree. After processing, the above example would look as follows:

<comment>Select an item from a menu.
For example: for the File -> Open action, the QF-Test component ID
 "File" is the menu, and the QF-Test component ID "Open" is the item.</comment>
<param name="client">The name of the SUT client.</param>
<param name="menu">The QF-Test component ID of the menu.</param>
<param name="item">The QF-Test component ID of the menu item.</param>
Example 53.41:  Example comment after DOM processing

Transforming the above into useful HTML during the second stage transformation is now straightforward.

The DOMProcessorRegistry

Before a DOM processor can be used, it must be registered for the kind of node(s) it applies to. This is done through the DOMProcessorRegistry.

There is one DOMProcessorRegistry instance object per kind of transformation, each identified by a string. Currently these identifiers are "report" for report generation and "testdoc" and "pkgdoc" for test set and package documentation plus their variants for transforming the respective summary documents named "report-summary", "testdoc-summary" and "pkgdoc-summary". To get hold of a registry instance, use the static instance method:

 
 
DOMProcessorRegistry instance(String identifier)
Get hold of a registry instance.
Parameters
identifierThe identifier for the kind of transformation.
 
 

The rest of the methods consist of the typical set of register/unregister variants:

 
 
void registerDOMProcessor(DOMProcessor processor)
Register a generic DOM processor that will be called for all kinds of nodes.
Parameters
processorThe processor to register.
 
void registerDOMProcessor(String node, DOMProcessor processor)
Register a DOM processor for a specific kind of node.
Parameters
nameThe type of the node.
processorThe processor to register.
 
void unregisterDOMProcessor(DOMProcessor processor)
Unregister a generic DOM processor.
Parameters
processorThe processor to unregister.
 
void unregisterDOMProcessor(String node, DOMProcessor processor)
Unregister a DOM processor for a specific kind of node.
Parameters
nameThe type of the node.
processorThe processor to unregister.
 
void unregisterDOMProcessors()
Unregister all DOM processors.
 
 

Error handling

Exceptions raised during DOM processing by the process method of a DOMProcessor are caught and duly reported, the transformation is stopped in that case.