MagicTree Documentation: Report Generation Explained

Generating Reports | Table of Contents | Sample Report Templates >

MagicTree generates a report by using a report template and filling in the data from the tree. Currently supported are report templates in Microsoft Word 2007 .docx format (Open XML) and OpenOffice Writer .odt format (OpenDocument).

A report template can contain arbitrary text, images and formatting. It should also contain placeholders that are replaced by the data from the tree. Placeholders are surrounded by two sets of curly brackets and consist of an XPath expression and optonal flags, separated by a pipe ("|" - that's a pipe in double quotes, not a smiley):

{{XPath_expression|flags}}

XPath expression can be any syntactically correct XPath expression. Flags can be "leaf","hidden","bookmark" and "external".

MagicTree report generator reads a report template paragraph by paragraph until it encounters a paragraph containing an XPath placeholder. After that it calculates what we call a scope of the placeholder. A scope is a part of the document (one or more paragraphs) that will be processed using the results of the XPath expression evaluation.

A scope is actually a rather intuitive concept. A document usually consists of one or more sections each having a heading. Each section may have subsections, also with headings, and so on. Consider a document like this:

Section 1 (Heading Level 1)

Subsection 1.1 (Heading Level 2)

Text of subsection 1.1

Subsection 1.2 (Heading Level 2)

Text of subsection 1.2

Section 2 (Heading Level 1)

Some more text.

The part of the document to which "Section 1" heading applies is everything from the start up to, but not including "Section 2" heading. We call it a scope. The scope of "Section 1" heading starts from the heading itself and includes all paragraphs up to and including "Text of subsection 1.2", but not including the next heading of the same level as the scope heading.

Similarly, the scope of "Subsection 1.2" heading is the heading itself and the "Text of subsection 1.2".

To define scope more formally, a scope is a part of the document consisting of one or more consecutive paragraphs, where the first paragraph is called the scope heading.

If the first paragraph of the scope (scope heading) is really a heading, the scope includes all paragraphs up to (but not including) the next heading of the same or higher level than the scope heading (level 1 being the highest heading level).

If the first paragraph of the scope (scope heading) is a normal paragraph, not a heading, the scope includes that paragraph only.

If the first paragraph of the scope (scope heading) is a list item, the scope includes this item and all following list items of lower level. For example, in the following list, the scope of "item 1" will include item 1, item 1.1, item 1.1.2 and item 1.2, but not item 2.

  • item 1

    • item 1.1

      • item 1.1.2

    • item 1.2

  • item 2

If the XPath placeholder is found inside a table, it's scope is the whole table row.

After establishing the scope of the expression, the report generator evaluates the expression. The result of the evaluation will be zero, one or more values.

If no flags were specified in the XPath placeholder, the report generator will create N copies of the current scope, replacing the palceholder in each copy with the result of the expression evaluation. For example, let's say we have a following template:

{{//host}}

and the tree contains three hosts: 192.168.1.1, 192.168.1.2, and 192.168.1.3.

The expression "//host" will evaluate to three values. The report generator will make three copies of the paragraph and replace the placeholder with the value in each one of them, so the resulting documen t will look like this:

192.168.1.1

192.168.1.2

192.168.1.3

If the expression evaluation has returned zero values, the whole scope containing the expression will be removed from the document (unless the "leaf" flag was specified, in which case, the paragraph will remain, but the placeholder itself will be removed).

After replacing the placeholder with a values, the report generator will recursively process the newly created scope, splitting it into paragraphs and looking for XPath placeholders. However it will use the value returned in the previous step as a base for the evaluation of the next XPath expression. Consider the following example template:

{{//host}} {{ipproto}} {{port}}

Suppose the tree contains the following data:

<host>192.168.1.1
<ipproto>tcp
<port>80</port>
<port>443</port>
</ipproto>
</host>
<host>192.168.1.2
<ipproto>udp</ipproto>
</host>
<host>192.168.1.3</host>

The report generator will see the first (and only) paragraph in the template and the first XPath placeholder - "{{//host}}". It will evaluate the expression "//host" and get back three hosts - 192.168.1.1 , 192.168.1.2 and 192.168.1.3. For each value, it will create a copy of the paragraph and replace the placeholder with the value. At this point the generated document will look like this:

192.168.1.1 {{ipproto}} {{port}}

192.168.1.2 {{ipproto}} {{port}}

192.168.1.3 {{ipproto}} {{port}}

After that it will proceed recursively to each newly created scope, using the evaluation result as the base. So, it will look at the first paragraph "192.168.1.1 {{ipproto}} {{port}}", locate the first placeholder - "{{ipproto}}", and evaluate it, using the node "192.168.1.1" as the base. The evaluation will return one node - "tcp", so it will be used to replace the second placeholder. At this point the document will look like this:

192.168.1.1 tcp {{port}}

192.168.1.2 {{ipproto}} {{port}}

192.168.1.3 {{ipproto}} {{port}}

Continuing in the same manner untill all placeholderers are replaced will result in the following document:

192.168.1.1 tcp 80

192.168.1.1 tcp 443

Note that the second and the third host do not appear in the results at all. The second host does not appear because it does not have any ports and the third one has neither ipprotos, nor ports. If we want the hosts to appear in the listing, even if they don't have any ipprotos or ports, we could use the following template:

{{//host}} {{ipproto|leaf}} {{port|leaf}}

Processing this template with the same data will result in the following document:

192.168.1.1 tcp 80

192.168.1.1 tcp 443

192.168.1.2 udp

192.168.1.3

Generating Reports | Table of Contents | Sample Report Templates >