X2Shell Console Application Language

X2Shell

Originally code named “Batch Runner and Diagnostics for Operating/Networking” (BRANDON), X2Shell was developed as a toolkit to allow sysops and technicians to develop their own apps for performing various tasks. X2Shell apps can access SQL databases, make HTTP calls, send emails, and of course, work with files.

To make your own X2Shell application, simply copy X2Shell.exe to MyAppName.exe and make a new config file called MyAppName.xml. Then, edit the config file and start building as shown below. When you run an X2Shell app, the underlying code creates a memory-only working XML file, called the DataCloud, initially comprised of data collected from the environment. It then walks through the configuration file provided. Any Data elements are copied into the DataCloud(tests permitting) and any Run elements are further stepped through and their executions are performed. Every execution adds its results to the DataCloud and is also able to leverage from the any prior data or execution, as well as the original snapshot of the environment.

By default, the XML-based configuration file, is the given the same name of the EXE that was provided, and can consist of any valid root node, which then contains one or more <Data> or <Run> elements. Both of those elements may contain a ‘test’ attribute. The test should be in the style of an XPath, where a node hit is a pass and a miss is a fail. If no test exists, it is an automatic pass. Each Data element that passes will be copied as is into the Data Cloud. Each Run element can contain any combination of the below Execution Types.


Example (HelloWorld.xml)

<HelloWorld>
<echo>
<line>
Hello World!
</line>
<string>
At the tone, the time will be:
</string>
<string type="XPath">
/X2Shell/run-time/@systemtime
</string>
<line>
BEEP!
</line>
</echo>
</HelloWorld>

Result

C:\> HelloWorld.exe
Hello World!
At the tone, the time will be: 4/18/2020 10:25:42 PM
BEEP!

NOTE: Unless otherwise configured, the resultant data cloud will have a root element named “X2Shell”. To change this, include an attribute named “root-node” with a desired value in the root node of your configuration XML, or include the attribute “use-root” with either “yes” or “true” to use the name of your configuration XML root node as the data cloud root name.

Execution Types

The following are the types of exeuctions that can be performed. They will be the name of the XML element being performed within a Run or batch block.

Basic Commands

If you start by thinking of an X2Shell application as a powerful BAT file, then these commands or their concepts should be somewhat familiar to you. They are just executed a little differently than their BAT file counterparts.

echo

Write some text out to the console, or file (see output)

{ any text to be output }
-OR-
any combination of
<string>
...
</string>
and
<line>
...
</line>
Base Attributes:
new-line=="true|false"

Note: unlike most other commands, the echo execution does not have a <params> node, but instead allows complex strings to be created in the same fashion as complex arguments are creaed in the command process. The desired output is generated from any combination of <string/> or <line/> nodes.


Example

<echo id="ShowUsage" test="(count(/X2Shell/run-time/command-line/arguments/arg) = 1)">
<string>
[
</string>
<string type="xpath" no-preceding-whitespace="true">
/X2Shell/run-time/command-line/arguments/arg
</string>
<string no-preceding-whitespace="true">
] - v1.0
</string>
<line>
This program does some cool stuff
</line>
</echo>

Result

C:\> MyProgram.exe
[MyProgram.exe] - v1.0
This program does some cool stuff

pause

============================================================================== “pause” – Hold processing for some intervention —————————————————————————— Params: method=”console|msgbox|timer” timeout=”number-in-milliseconds” (for timer only) caption=”” text=”” buttons=”ok|ok-cancel|yes-no|yes-no-cancel|retry-cancel| abort-retry-cancel”

path

============================================================================== “path” – Used to get or set the working dir, or list the contents of a path —————————————————————————— Params: command=”get-cwd|set-cwd|list|exists|file-exists|… … copy|move[-file]|move-dir[ectory]|delete|make-directory|make-dir|mkdir” filepath=”somefilepath” (optional – “cwd” is default) destpath=”somefilepath” (only used for “copy” or “move”) This is used to manipulate a local drive or network drive path, or context.

output

============================================================================== “output” – Specify where echo content should be sent —————————————————————————— Params: method=”console|file|both|close” filepath=”somefilepath” filemethod=”create|append” The output command with the file (or both) method, forces all echo statements from that point forward to be sent to the filepath specified. If the filemethod is set to append, then the output file will be opened for and the echo statements will be written at the end, otherwise, any existing content before the output command was issued will be overwritten. The overriding of the echo command will continue until either a new output command is given, or the program terminates.

Spawn Processes

command

============================================================================== “command” – Launch a new process / executable or shell extension —————————————————————————— Params: command=”some.exe” working-directory=”c:\” arguments=”/arg1 /arg2″ (*NOTE: see below) use-shell=”true|false” style=”min|max|hidden|normal” wait-for-exit=”true|false” capture-stdin=”true|false” (**NOTE: see below) capture-stdout=”true|false” capture-stderr=”true|false” * NOTE: Additional arguments may be given as <arg> nodes within the Param. These arg nodes should have the inner text reflecting the value to be used. Further, arg nodes may have “type” and “format” attributes, which will act exactly as the “_type” and “_format” suffixed attributes explained below. For more flexibility, the <arg> nodes also support two additional attributes: “no-preceding-whitespace” and “separator-character”. Giving the first of these a “true” value will simply instruct the argument builder to not add a space between it and the previous arg node. You may instead give your own separator value to be used instead of a space (” “). (i.e. separator-character=”+” ) **NOTE: The option to capture standard input is only used if use-shell = true. Further, an additional node given as <stdin> must be provided within the Param parent node. Like the <arg> nodes above, it supports type and format.

powershell

============================================================================== “powershell” – Run a PowerShell script or applet —————————————————————————— Params: working-directory=”c:\” capture-stdout=”true|false” NOTE: See definition for the “command” action for argument usage

Execution Control

reset

============================================================================== “reset” – Resets the DataCloud to remove all prior execution information —————————————————————————— Params: method=”soft|hard” Both resets will clear the DataCloud, but use a different manner. The soft reset will remove all prior exeuction nodes. The hard reset will rebuild the DataCloud from scratch. A hard reset will also update the systemtime.

run-batch

============================================================================== “run-batch” – Process another batch of commands (typically from transform) —————————————————————————— Params: { List of commands to be run }

dump-log

============================================================================== “dump-log” – Used to dump the current datacloud out to a file or console —————————————————————————— Params: filepath=”output.xml” (or empty for console) rootnode-xpath=”xpath-to-element” (default = “/”) Write out all or part of the XML of the datacloud

stop-if

============================================================================== “stop-if” – Stop processing if this command is run —————————————————————————— Params: method=”this-run|all”

skip-to

============================================================================== “skip-to” – Skip processing if this command is run —————————————————————————— Params: nextCommandType=”{ any valid command type }” (i.e. “pause”) nextCommandId=”{ id of an upcoming command }” end=”true” (actual value is ignored, must be non-empty text)

Transformations

transform

============================================================================== “transform” – Used to apply an external XSL file to a portion of the datacloud —————————————————————————— Params: filepath=”somefile.xsl” source-filepath=”somedocument.xml” -OR- rootnode-xpath=”/datacloud/xpath” The XSL will be applied to the datacloud, optionally at the rootnode-xpath. -OR- By specifying a source-filepath, an external document will be used. NOTICE: An XSLT may be embedded inline as well by NOT specifying a filepath, and instead including an <inline> element with a full XSLT contained Example: <transform> <params rootnode-xpath=”/datacloud/xpath”> <inline> <xsl:stylesheet version=”1.0″ xmlns:xsl=”http://www.w3.org…”> … </xsl:stylesheet> </inline> </transform>

x2tl

============================================================================== “x2tl” – Apply an {{ X2 Templating Language }} Template against the datacloud —————————————————————————— Params: execute-results=”true|FALSE” Template: (The first element in the commend that is NOT the params) Example: {{ Template }} ==============================================================================

In-Memory Byte Array

clear-bytes

============================================================================== “clear-bytes” – Used to empty out the binary data byte array (bytes) —————————————————————————— Clears out the “bytes” byte array when the contents are no longer needed.

compress-bytes

============================================================================== “compress-bytes” – Used to compress the binary data byte array (bytes) —————————————————————————— Performs ZIP compression on the “bytes” byte array, compressing the contents

decompress-bytes

============================================================================== “decompress-bytes” – Used to empty out the binary data byte array (bytes) —————————————————————————— Performs ZIP decompression on the “bytes” byte array, exploding the contents

User Interface

form

============================================================================== “form” – Build a GUI Dialog Box —————————————————————————— gui: definition gui Attributes: background-color=”” control-box=”true|false” minimize-box=”true|false” maximize-box=”true|false” auto-scroll=”true|false” show-icon=”true|false” show-in-taskbar=”true|false” icon=”” background-image=”” width=”300″ height=”200″ Note: unlike others, the “form” execution does not have a <params> node, but instead allows for a complete UI form to be defined in the XML as a child element called “gui”. A “gui” element can have one or more “layout” elements with attributes: layout Attributes: left=”0″ top=”0″ A “layout” can then have any of the following elements with attributes: label: label Attributes: use-mnemonic=”false” border-style=”none” textbox: textbox Attributes: border-style=”none” scroll-bars=”both” multi-line=”true” password=”false” password-char=”” read-only=”false” button: button Attributes: use-mnemonic=”false” style=”flat” use-style-backcolor=”true” type=”none|yes|no|abort|retry|cancel|ignore” form-accept=”false” form-cancel=”false” image: image Attributes: path=”” border-style=”none” checkbox: checkbox Attributes: auto-check=”true|false” appearance”normal” auto-ellipsis=”true|false” checked=”false|true” check-state=”checked|unchecked” radio: radio Attributes: auto-check=”true|false” appearance”normal” auto-ellipsis=”true|false” checked=”false|true” combobox: combobox Attributes: drop-down-width=”” A “combobox” should contain one or more “item” elements item Attributes: caption=”” listbox: listbox Attributes: allow-multiple=”false|true” A “listbox” should contain one or more “item” elements (see above) checked-listbox: A “checked-listbox” should contain one or more “item” elements (see above) numeric: numeric Attributes: (N/A) date: date Attributes: min-date=”” max-date=”” show-up-down=”false|true”

External Data

http

============================================================================== “http” – Used to get or post contents via HTTP web request —————————————————————————— Params: method=”get|post” uri=”http://….” post-rootnode-xpath=”/” (if method is “post”, what is sent) post-format=”text|xml” (and what format is it sent in)

mail-to

============================================================================== “mail-to” – Used to send results to one or more e-mail recipients. —————————————————————————— Params: smtp-host=”” (either IP address or DNS name of SMTP server) tcpip-port=”25″ (optional – can change for custom port) from=”” (email address of sender) to=”” (one or more email addresses of recipients) subject=”” credentials=”network|none” (if server requires authorization) uri=”http://….” rootnode-xpath=”/” (path to what is being sent) format=”text|xml” (and what format is it sent in)

msmq

============================================================================== “msmq” – Used to send, receive or list contents of a Microsoft Message Queue. —————————————————————————— Params: queue-name=”.\Private$\MyQueue” (path/name of MSMQ) method=”send|receive|list” (note: “list” is non-destructive) rootnode-xpath=”/” (if “send”, path to what is being sent) format=”text|xml” (and what format is it sent in) priority=”lowest|very-low|low|normal|above-normal| high|very-high|highest” (default is “normal”)

sql

============================================================================== “sql” – Perform a Microsoft SQL Server Execution —————————————————————————— Params: method=”recordset|xml|bytes|execute-only” server=”sqlserver\instance” connect=”DataSource=Connection_String” query=”exec my_query @param1, @param2″ row-label=”mydataitem” (default: row) For standard recordsets, the row-label (default is “row”) will be used for each entry in the resulting recordset, with the field names as attributes. NOTE: When using recordset (default) method, do NOT use spaces or special characters for the names of the columns, or you will generate an error. NOTE: Use of the bytes return type will store the resulting single image response in the general byte array “bytes”. If the result will be XML (for instance using the FOR XML option of SQL) use the xml method, and the row-label to indicate the wrapper element. NOTE: If your query or stored procedure has parameters, include them as “param” child elements of the “params” element. Give them a “name” attribute, a “value” attribute, and optionally a “test” attribute. No value attribute will indicate a NULL. A value format of “bytes” to use the general byte array. Example: <sql id=”…” test=”” xpath=””> <params server=”” connect=”” query=”exec my_sproc @p1, @p2, @pImage”> <param name=”@p1″ value=”1″/> <param name=”@p2″ /> <param name=”@pImage” value_type=”bytes”/> </params> </sql>

registry

============================================================================== “registry” – Manage the machine registry (get, set, create, list) ** NOTICE: USE WITH CAUTION!! ** —————————————————————————— Params: hkey=”HKLM|HKCU|HKCR|HKU|HKCC|HKEY_LOCAL_MACHINE|HKEY_CURRENT_USER| HKEY_CLASSES_ROOT|HKEY_USERS|HKEY_CURRENT_CONFIG” subkey=”” method=”get(-value)|set(-value)|create(-key)|list” value-name=”” value-type=”REG_SZ|REG_DWORD” value-data=”” format=”text|xml” Either get, set, create or list a subkey within a branch of the registry.

Compress / Decompress

compress

============================================================================== “compress” – Used to ZIP a file —————————————————————————— Params: filepath=”compressed.zip” mode=”create|update” source-filepath=”some-file-to-compress” saveas-filepath=”directory/path/file_name.ext” delete-onsuccess=”false|true” Either create a new or update an existing ZIP file by adding the source file and saving it in the ZIP as the specified filepath. If delete-onsuccess, then the source file will be deleted after compression.

zip

============================================================================== “zip” – (aka zip-files) Creates or Updates a ZIP file from one or more files —————————————————————————— Params: filepath=”some-files-to-zip*.*” destpath=”my-zip-file.zip” action=”ADD | move” overwrite=”true | FALSE” If the action is set to “move”, the source file will be deleted once added If overwrite is set to “true” then any pre-existing zip will be overwritten Example: <zip id=”…” test=”” xpath=””> <params filepath=”” destpath=”” action=”” overwrite=”” /> </zip>

unzip

============================================================================== “unzip” – (aka unzip-files) Expands a ZIP file to a specified directory —————————————————————————— Params: filepath=”source-file-name.zip” destpath=”file-path-to-extract-into” Example: <unzip id=”…” test=”” xpath=””> <params filepath=”” destpath=”” /> </unzip>

list-zip

============================================================================== “list-zip” – (aka read-zip) Lists the contents of a ZIP file —————————————————————————— Params: filepath=”source-file-name.zip” Example: <list-zip id=”…” test=”” xpath=””> <params filepath=”” /> </list-zip>

Miscellaneous

new-id (guid)

============================================================================== “new-id” – (aka guid) Used to generate one or more GUIDs —————————————————————————— Params: count=”1″


X2TL Templating Language

X2TL

X2TL is a language that applies custom built templating to an XML document. The templates are purely string based, and therefore can be any format, from straight text to XML to JSON. They utilize the double-brace aka {{ mustaches }} to embed commands, which are applied against the source XML document or the current context node within that XML.

Delimeters and Fragments

A template is comprised of Command Fragments and Text Fragments. Command Fragments are determined by finding blocks of text wrappered in a start and end delimiter, {{ and }} respectively, referred to in the templating community as Mustaches. Within the {{ mustaches }} are commands that are evaluated and processed against the context node. Anything outside of the {{ mustaches }} is considered a Text Fragment and is copied as-is. Following is a simple example, using a snippet of HTML.

Given the following XML Document

<User>
<row lastName="Fine" firstName="Howard" userName="HFINE" />
</User>

This Template

<span>
{{ = row/@lastName}}, {{ = row/@firstName }} ({{ = row/@userName }})
</span>

Results in the following output

<span>
Fine, Howard (HFINE)
</span>
By combining and embedding commands, and even libraries of supporting templates, this becomes a very powerful tool for generating XML sourced output.

Escaped Delimiters

There may come a time that you actually wish to include the delimiters {{ or }} within your template and NOT have them process as a command. This is referred to as escaping the delimiters. The X2TL handles this as many languages do, by allowing the author of the template to ‘double-delimit’. This concept should be familiar to anyone that has needed to double-quote a double-quote in other languages. In X2TL, you only need to double-up the start delimiter {{. The end delimiter }} can remain intact and will be considered part of the surrounding Text Fragment.

Template

<span>
This example has {{{{ mustaches }} in it.
</span>
<span>
This one has lots of {{{{{{{{ mustaches }}}} in it.
</span>

Resulting Output

<span>
This example has {{ mustaches }} in it.
</span>
<span>
This one has lots of {{{{ mustaches }}}} in it.
</span>

Command Fragments

The X2TL has a limited command set, however, its flexibility allow for countless variations. The X2TL currently supports the following commands, with the syntax for each command being described in detail below. Please also note that some commands provide for one or more “shorthand” alternatives. These are shown in parenthesis following the command itself (i.e. Result (=)). Shorthand versions of commands are treated identical to the engine, and it does not distinguish between either writing of the command. It should be noted that X2TL command words themselves are NOT case-sensitive, however, of course, their XPath parameters are. With that, the following are multiple ways of writing the same command, each of which will provide identical output.

Template Variations

{{ RESULT node/@attrib }} {{ Result node/@attrib }} {{ rESULT node/@attrib }} {{ result node/@attrib }} {{ = node/@attrib }}

Command Delimiters

A command itself may be broken down into parameters, the first of which is always the command name. Some commands do not require any parameters, and some can take any number of parameters. To properly determine the parameters within the command fragment, X2TL understands the following command delimiters: double-quotes ( ), parenthesis ( ( and ) ), and whitespace ( space, carriage return, line feed, tab ). Any text contained within a quoted-string, or within an outer set of parenthesis, is considered its own parameter. Any other text is broken down via whitespace. This will become more clear in the following examples.

X2TL Command Set

Value-Replacement Commands

Result (=)

The Result command, which has a shorthand equivalent of =, is the most commonly used command, as value-replacement is primarily the point of a templating language. It expects a single parameter being an XPath within the current node context to locate the value that you wish resulted. It should be noted that the resulting value of the XML node will be XML-escaped before it is added to the resulting string. (see RawResult for more information)

See Also: Format String (String)

NOTE: This is “value-replacement” only. For full “node-replacement”, please refer to the Copy (*) command.


Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<CheckIn status="CHECKED-IN" availability="Busy" lastUpdate="08/31/2010 16:18" />
</User>

Template

<div id="User_{{ = @userNo }}">
<span>
{{ result @lastName }}, {{ result @firstName }} ({{ = @userName }})
</span>
<span>
User is {{ = CheckIn/@status }} - ( {{ = /User/CheckIn/@availability }} ) as of {{ Result "*/@lastUpdate" }}
</span>
</div>

Resulting Output

<div id="User_1001">
<span>
Fine, Howard (hfine)
</span>
<span>
User is CHECKED-IN - ( Busy ) as of 08/31/2010 16:18
</span>
</div>
The above template was written in several different ways in order to illustrate the flexibility of the language. With the understanding of XPath and its commands, we can simplify and empower some of the above as follows, which will generate the same result.

Template (Alternative)

<div id="User_{{ = @userNo }}">
<span>
{{ = ( concat(@lastName, ', ', @firstName, ' (', @userName, ')' ) ) }}
</span>
<span>
User is {{ = "concat( CheckIn/@status, ' - ( '= CheckIn/@availability, ' ) as of ', CheckIn/@lastUpdate )" }}
</span>
</div>

RawResult (==, raw)

The RawResult command, which has two shorthand equivalents of == and raw, will be lesser used than its counterpart Result, as RawResult provides back its resulting data as-is in string form, without any XML escaping performed. To better illustrate the use of this command, it will be compared to the Result command in the example below.

Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<Pref favURL="http://www.google.com/#hl=en&source=hp&q=RelWare&aq=f" />
</User>

Template

<div>
<span>
User's favorite URL is: {{ Result Pref/@favURL }}
</span>
<span>
This is Invalid XML: {{ RawResult Pref/@favURL }}
</span>
<span>
User's favorite URL is: {{ = Pref/@favURL }}
</span>
<span>
This is Invalid XML: {{ == Pref/@favURL }}
</span>
</div>

Resulting Output

<div>
<span>
User's favorite URL is: http://www.google.com/#hl=en&amp;source=hp&amp;q=RelWare&amp;aq=f
</span>
<span>
This is Invalid XML: http://www.google.com/#hl=en&source=hp&q=RelWare&aq=f
</span>
<span>
User's favorite URL is: http://www.google.com/#hl=en&amp;source=hp&amp;q=RelWare&amp;aq=f
</span>
<span>
This is Invalid XML: http://www.google.com/#hl=en&source=hp&q=RelWare&aq=f
</span>
</div>

Replace (~)

The Replace command, which has a shorthand of ~ is used to perform a regular-expression based string replacement on either an Xpath or variable value. This command takes (3) parameters: regExp, replaceText, input. The first two of these are as defined by Microsoft .Net framework relating to the RegEx Replace method. (see also .NET Framework Regular Expressions)


Source XML

<node>
<data d1="08/26/2010" />
</node>

Template

<div>
<span>
Original: {{ = data/@d1 }} Updated: {{ ~ "\b(?<month>\d{1,2})/(?<day>\d{1,2})/(?<year>\d{2,4})\b" "${day}-${month}-${year}" data/@d1 }}
</span>
</div>

Resulting Output

<span>
Original: 08/26/2010 Updated: 26-08-2010
</span>

FromURL

There are many times that we need to obtain data from a URL, whose encoding can be awkard to deal with as a simple string. The FromURL command fixes issues, such as %20 being used for a space.


Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<Pref favURL="http://example.org/data?this%20value%20has%20encoded%20data%22%23%24" />
</User>

Template

<div>
<span>
Original: {{ = Pref/@favURL }}
</span>
<span>
FromURL: {{ FromURL Pref/@favURL }}
</span>
</div>

Resulting Output

<div>
<span>
Original: http://example.org/data?this%20value%20has%20encoded%20data%22%23%24
</span>
<span>
FromURL: http://example.org/data?this value has encoded data"#$
</span>
</div>

Format (?)

The Format command, which has a shorthand equivalent of ?, is similar to the Result command, except that it is used to specifically change the output format of a given value, or in the case of a String format, multiple variables. The Format command expects (3) parameters: datatype (date, number, string), format (see .Net Framework Format Strings), and value (XPath reference to node value). Unlike other commands, Format also has three ‘shortcuts’, being that you can omit the Format (or ?) portion of the command, and simply use the commands: Date, Number or String, each of which are described in more detail below. Other than the obvious lack of initial parameter, they operate identically.

Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<CheckIn status="CHECKED-IN" availability="Busy" lastUpdate="08/31/2010 16:18" phoneNum="123456789012345" />
</User>

Template

<span>
User is {{ = CheckIn/@status }} as of {{ Format Date MM/dd/yyyy CheckIn/@lastUpdate }} and can be reached at {{ ? Number "#(###)###-#### x.####" CheckIn/@phoneNum }}
</span>

Resulting Output

<span>
User is CHECKED-IN as of 08/31/2010 and can be reached at 1(234)567-8901 x.2345
</span>

Format Date (Date)

The Date command is a shortcut to the fully qualified Format Date command. It expects to receive a valid date format string, and an XPath value that points to a date value. There is no error handling for this, nor “invalid date message”. Please use with care.

Note: While the command name only specifies Date, it is truly a DateTime format, and supports any .Net supported date/time string. (See also: DateAdd)

(see Standard Date Format Strings and Custom Date Format Strings)


Source XML

<SystemTime utc="8/26/2010 5:44:47 PM" local24="8/26/2010 13:44:47" />

Template

<span>
UTC: {{ Format Date MM/dd/yyyy @utc }} - Local: {{ ? Date MM/dd/yyyy @local24 }} - English: {{ Date @local24 "MMMM dd, yyyy at h:mm:ss tt" }}
</span>

Resulting Output

<span>
UTC: 08/26/2010 - Local: 08/26/2010 - English: August 26, 2010 at 1:44:47 PM
</span>

Format Number (Number)

The Number command is a shortcut to the fully qualified Format Number command. It expects to receive a valid numeric format string, and an XPath value that points to a numeric value. There is no error handling for this, nor “invalid number message”. Please use with care.

(see Standard Numeric Format Strings and Custom Numeric Strings)


Source XML

<data n0="0" n1="123" n2="1234567890" n3="-765.4321" n4="12345678901.2345" />

Template

<div>
<span>
Original: {{ = data/@n1 }} Zero-padded: {{ format Number 000000 data/@n1 }}
</span>
<span>
Comma-Separated: {{ ? Number "#,#" data/@n2 }}
</span>
<span>
Negative currency: {{ Number "$#,#.##" data/@n3 }}
</span>
<span>
Telephone Ext: {{ Number "#(###)###-#### x.####" data/@n4 }}
</span>
<span>
Pos: {{ number "##.#;(##.#);**Zero**" data/@n1 }} Neg: {{ number "##.#;(##.#);**Zero**" data/@n3 }}
</span>
<span>
Dec: {{ = data/@n1 }} Hex: {{ ? number X data/@n1 }}
</span>
</div>

Resulting Output

<div>
<span>
Original: 123 Zero-padded: 000123
</span>
<span>
Comma-Separated: 1,234,567,890
</span>
<span>
Negative currency: -$765.43
</span>
<span>
Telephone Ext: 1(234)567-8901 x.2345
</span>
<span>
Pos: 123 Neg: (765.4) Zero: **Zero**
</span>
<span>
Dec: 123 Hex: 7B
</span>
</div>

Format String (String)

The String command is a shortcut to the fully qualified Format String command. It expects to receive a format string, and unlike the other commands, any number of XPath values, each pointing to their own string value nodes. This command uses the .Net String.Format() method for its internal resulting. There is no error handling for invalid format strings. Please use with care.

(see .Net Custom String Formatting)


Source XML

<User username="HFINE" first="Howard" last="Fine" />

Template

<span>
{{ Format String "{0}, {1} ({2})" @last @first @username }}
</span>

Resulting Output

<span>
Fine, Howard (HFINE)
</span>

DateAdd

Simple parsing of date strings is quite simple, especially if you combine a MultiVar with a Format Date as its child. However, doing any math with that becomes a far more difficult task. The DateAdd command allows you to take advantage of the undelying .Net DateAdd function to generate ranges, cut-offs, etc. DateAdd takes 3 parameters, and optionally a 4th. The first is the XPath to the date value. As noted with Format Date please take care with the date contexts. The second and third parameters are the Date Interval and the positive or negative number to be added (or subtracted). (See .Net DateAdd Date Intervals) The fourth parameter, which is optional, is the output format. (See also Format Date as well as .Net Standard Date/Time Format strings and .Net Custom Date/Time Format strings)


Source XML

<SystemTime utc="8/26/2010 2:30:51 PM" local24="08/26/2010 10:30:51" />

Template

<div>
<span>
Original: {{ = SystemTime/@utc }}
</span>
<span>
+60 Days: {{ dateadd SystemTime/@utc D 60 }} ({{ dateadd SystemTime/@utc D 60 (h:mm:ss tt) }})
</span>
<span>
+60 Hours: {{ DateAdd SystemTime/@utc h 60 }}
</span>
</div>

Resulting Output

<div>
<span>
Original: 8/26/2010 2:30:51 PM
</span>
<span>
+60 Days: 10/25/2010 14:30:51 (2:30:51 PM)
</span>
<span>
+60 Hours: 08/29/2010 02:30:51
</span>
</div>

The following shows how involving variables and the optional output formatting can perform complex tasks.


Complex Template

<div>
<span>
Original: {{ = SystemTime/@utc }}
</span>
{{ mvar TwoMonths }}{{ dateadd SystemTime/@utc M 2 (MM\/dd\/yyyy h:mm:ss tt) }}{{ /mvar}}
<span>
End of Next Month: {{ dateadd $TwoMonths D -1 (MM\/dd\/yyyy h:mm:ss tt) }}
</span>
</div>

Resulting Output

<div>
<span>
Original: 1/01/2016 12:34:56 PM
</span>
<span>
End of Next Month: 02/29/2016 12:34:56 PM
</span>
</div>

MD5

The MD5 command is used to generate an MD5 Hash value of a given string or XML node. This can be used for ensuring data integrity and more. The only parameter is the XPath of the data to be hashed.


Source XML

<html>
<div unquoted=1 specials="<>&amp;&quot;"><br></br><BR><textarea>&lt;&gt;&amp;&quot;</textarea><HR empty ><hr />&lt;&gt;&amp;&quot;</div>
</html>

Template

<div>
{{md5 /html }}
</div>

Resulting Output

<div>
f2e35c11b7bbee227c6201a534c02010
</div>

SHA1

The SHA1 command is nearly identical to the MD5 command, except that it generates an SHA-1 hash instead of MD5. The only parameter is the XPath of the data to be hashed.


Source XML

<html>
<div unquoted=1 specials="<>&amp;&quot;"><br></br><BR><textarea>&lt;&gt;&amp;&quot;</textarea><HR empty ><hr />&lt;&gt;&amp;&quot;</div>
</html>

Template

<div>
{{sha1 /html }}
</div>

Resulting Output

<div>
31d69b566323ed39bc018799b6616ad7b2c51458
</div>

Node-Replacement Commands

Copy (*)

The Copy command, which has a shorthand equivalent of *, is used to bring a copy of the actual XML of a selected node into the result. Unlike the above Value-Replacement commands, which work only on the requested node’s inner text / value, this actually brings forward full “outer-XML” of a requested node. With that, no additional formatting options apply.

NOTE: This is a full “XML node-copy”. For simple “value-replacement”, please refer to the Result (=) command.


Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<CheckIn status="CHECKED-IN" availability="Busy" lastUpdate="08/31/2010 16:18" />
</User>

Template

<xml id="User_{{ = @userNo }}">
{{ copy CheckIn }}
</xml>

Resulting Output

<xml id="User_1001">
<CheckIn status="CHECKED-IN" availability="Busy" lastUpdate="08/31/2010 16:18" />
</xml>

CopyEncoded (*=)

The CopyEncoded command, which has a shorthand equivalent of *=, is used to bring an XML encoded copy selected node into the result. This operates exactly like the above Copy command, except that the XML is encoded for display instead of the raw xml (for instance, < becomes &lt;). Again, no additional formatting options apply.

NOTE: This is a full “XML node-copy”. For simple “value-replacement”, please refer to the Result (=) command.


Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<CheckIn status="CHECKED-IN" availability="Busy" lastUpdate="08/31/2010 16:18" />
</User>

Template

<xml id="User_{{ = @userNo }}">
{{ copyEncoded CheckIn }}
</xml>

Resulting Output

<xml id="User_1001">
&lt;CheckIn status=&quot;CHECKED-IN&quot; availability=&quot;Busy&quot; lastUpdate=&quot;08/31/2010 16:18&quot; /&gt;
</xml>

CopyDecoded (=*)

The CopyDecoded(or its shorthand =*) is used for the exact opposite case of the above CopyEncoded (*=) command. As you can probably already guess, it is for when XML has been Encoded for display, but now needs to be Decoded for XML use.


Source XML

<textarea>
&lt;p&gt;asdasd&lt;/p&gt;&lt;p&gt;asd&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;u&gt;&lt;em&gt;asasdas&lt;/em&gt;&lt;/u&gt;&lt;/li&gt;&lt;li&gt;a&lt;u&gt;&lt;strong&gt;sdadsdaasd&lt;/strong&gt;&lt;/u&gt;as&lt;/li&gt;&lt;li&gt;&lt;strong&gt;asdsadad&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;assadad&lt;/p&gt;
</textarea>

Template

<template>
<html>
{{ CopyDecoded /textarea }}
</html>
</template>

Resulting Output

<html>
<p>
asdasd
</p>
<p>
asd
</p>
<ul>
<li>
<u>
<em>
asasdas
</em>
</u>
</li>
<li>
a
<u>
<strong>
sdadsdaasd
</strong>
</u>
as
</li>
<li>
<strong>
asdsadad
</strong>
</li>
</ul>
<p>
assadad
</p>
</html>

XHTML

The XHTML command is used to take straight HTML and make it XML (aka XHTML) encoded. For instance, in HTML, tags like br and hr are open-ended. They exist as <br> and <hr> with no closing indicator. In XML, everything must have a closing tag, or be self-closing, such as <br /> and <hr />. Additionally, HTML allows for attribute definitions without values. The XHTML command converts these to loadable XML. The only parameter is an XPath to the container of the HTML string.


Source XML

<html>
<div unquoted=1 specials="<>&amp;&quot;"><br></br><BR><textarea>&lt;&gt;&amp;&quot;</textarea><HR empty ><hr />&lt;&gt;&amp;&quot;</div>
</html>

Template

<div>
{{xhtml /html }}
</div>

Resulting Output

<div>
<div unquoted="1" specials="<>&"">
<br />
<BR />
<textarea>
<>&"
</textarea>
<HR empty="empty" />
<hr />
<>&"
</div>
</div>

Conditional Commands

If … Else … EndIf (/If)

Any language worth its salt has some form of the If//Else/EndIf logic, and X2TL is no different. In X2TL, the If command takes an XPath parameter that is used to perform a “does-node- exist” test. The test can consist of a simple relative XPath, a root-node based XPath, or a logic-based XPath, each of which will be shown below. Like most languages, the Else command is optional, however, the EndIf(or its shorthand equivalent /If ) is required. In the future, an error message will most likely result in this situation, however at present, it may simply provide unpredictable results.


Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<CheckIn status="CHECKED-IN" availability="Busy" lastUpdate="8/31/2010 16:18">
<message>
In meetings until lunch
</message>
</CheckIn>
</User>

Template

<div id="User_{{ = @userNo }}">
<span>
{{ string "{0}, {1} ({2})" @last @first @username }})
</span>
{{ IF /User/CheckIn }}
<span>
User is {{ = CheckIn/@status }} {{ If CheckIn/@facility }}at the {{ = CheckIn/@facility }}{{ Else }}but whereabouts are unknown{{ EndIF }}
</span>
{{ if (count(*/message)!=0) }}
<div>
{{ = */message }}
</div>
{{ /if }} {{ ENDIF }}
</div>

Resulting Output

<div id="User_1001">
<span>
Fine, Howard (hfine)
</span>
<span>
User is CHECKED-IN but whereabouts are unknown
</span>
<div>
In meetings until lunch
</div>
</div>

NOTE: In the above example, we see each of the three types of XPath tests: root-node based ( IF /User/Check-In ), relative( If Check-In/@facility ), and logical( if (count(*/message)!=0) ).

Looping Commands

Each … EndEach (/Each)

As with the simple conditional logic, one cannot get far in templating without providing a simple looping logic. More complex conditional looping commands such as Do…Until and While…EndWhile may come later, but until then, the simple Each command is provided. Each acts almost identically to XSL’s for-each command, taking an XPath as its parameter, which can either be a relative XPath against the context node, or a root-based XPath which will be applied against the context node’s document root element. Each XmlNode matching the given XPath will then be “looped”, with any Text or Command Fragments contained within the Each … EndEach block (or its shorthand equivalent of /Each) being repeated against the looped node as their context node. As with If…EndIf above, there is presently no error checking on un-matched Each…EndEach. While nesting of Each commands is valid, if you miss an EndEach(or /Each) command, the results may be unpredictable.


Source XML

<Patient lastName="Brown" firstName="Charlie">
<Allergies>
<row AllergyNo="1001" description="PEANUTS" reaction="Hives" />
<row AllergyNo="1002" description="LUCYVANPELT" reaction="Irritation" />
</Allergies>
</Patient>

Template

<div>
{{ = @lastName}}, {{ = @firstName }}
<table>
<tbody>
{{ EACH * }}
<tr>
<td colspan="2">
{{ = "translate(name(),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" }}
</td>
</tr>
{{ EACH row }}
<tr>
<td>
{{ = @description }}
</td>
<td>
{{ = @reaction }}
</td>
</tr>
{{ ENDEACH }} {{ /EACH }}
</tbody>
</table>
</div>

Resulting Output

<div>
Brown, Charlie
<table>
<tbody>
<tr>
<td colspan="2">
ALLERGIES
</td>
</tr>
<tr>
<td>
PEANUTS
</td>
<td>
Hives
</td>
</tr>
<tr>
<td>
LUCYVANPELT
</td>
<td>
Irritation
</td>
</tr>
</tbody>
</table>
</div>

Variable Commands

Variable (var, :=)

The Variable command, which has the shorthand equivalents of var and :=, is used to store a textual value as a (global) variable that can be used later. This command takes (2) parameters: name, xpath. The first parameter gives it a name to be used, and the second indicates what value should be stored. A named variable can then be referenced at any point that a single xpath expression is expected, by simply providing the variable name prefixed by the variable delimiter (default being $).

Source XML

<node>
<data d1="08/26/2010" />
</node>

Template

<div>
{{ := myVar data/@d1 }}
<span>
From Xpath: {{ = data/@d1 }} From Variable: {{ = $myVar }}
</span>
</div>

Resulting Output

<div>
<span>
From Xpath: 08/26/2010 From Variable: 08/26/2010
</span>
</div>

NOTE: As these are global variables, they can also be used in supporting templates.


Template

<div>
{{ := InputDate data/@d1 }}
<span>
Original: {{ = $InputDate }} EuroDate: {{ % EuroDate }}
</span>
</div>

Supporting Templates

<templates>
<template name="EuroDate">
{{ Date $InputDate "dd-MM-yyyy" }}
</template>
</templates>

Resulting Output

<div>
<span>
Original: 08/26/2010 EuroDate: 26-08-2010
</span>
</div>

MultiVar … EndMultiVar (MVar,::= … EndMVar,/MVar,/:==)

The MultiVar command is used to start creating a variable out of multiple pieces, and is completed with an EndMultiVar command. Any data that would have been resulted that is contained within a multivar start and end command will be stored as the contents of the variable. The start command has two shorthand equivalents: mvar, ::=, and several shorthand versions of the end command: endmvar, /MultiVar, /mvar, /::=. This command takes a single parameter of the name, and the resulting contents are considered the value, which can contain any number of other commands. Once created, Multipart Variables act identically to standard variables.


Source XML

<node>
<data att1="some" att2="more" att3="other" att4="weird">
stuff
</data>
</node>

Template

<div>
Multipart: {{ mvar totalstuff }}{{ each data/@* }}{{ = . }} {{ /each }}{{ = data }}{{ /mvar }}
<span>
{{ = $totalstuff }}
</span>
</div>

Resulting Output

<div>
Multipart:
<span>
some more other weird stuff
</span>
</div>

Load (:::)

Sometimes you need to include external data to assist in decision making, data validation, lookup values, etc. This is the purpose of the Load statement (or its shorthand version :::). Load takes (2) parameters, the first being the variable name, just like in the Var and MultiVar commands. The second paramter, however, is the path (relative, full, or UNC) to an XML file that should be loaded into memory and used as the variable from this point on. Please note that these variables support full XPath and similar functions, including Each looping.


Source XML

<User userNo="1001" lastName="Fine" firstName="Howard" userName="hfine">
<CheckIn status="CHECKED-IN" availability="Busy" lastUpdate="8/31/2010 16:18" facilityID="HQN" />
</User>

External XML

<Facilities>
<facility id="HQN" name="HeadQuarters - North" />
</Facilities>

Template

<div>
{{ Load Facilities "../DataFiles/Facilities.xml" }} {{ Var FacID CheckIn/@facilityID }}
<span>
User: {{ = @userName }} - {{ = CheckIn/@status }} @ {{ = ($Facilities/facility[@id=$FacID]/@name) }} ({{ = $FacID }})
</span>
</div>

Resulting Output

<div>
<span>
User: HFINE - CHECKED-IN @ HeadQuarters - North (HQN)
</span>
</div>

Template Commands

Both of the Template Commands, Apply (%%) and ParamApply (%%) expect that a separate XML block containing named template elements will exist. The Supporting Templates should be a single XML document, with any-named root element, and containing a simple list of any-named child elements, having a name attribute. These will be considered “named templates” that can be used by the Apply (%%) and ParamApply (%%) commands.

Apply (%)

While templates as described so far, provide for powerful formatting options, the true potential is unlocked when allowing them to Apply(with shorthand equivalent of %) other templates, either to the current context node, or against a different yet relative context. Apply always expects the name of a supporting template to be provided. Optionally, you can provide an XPath to apply the template against a different node context. Otherwise, the template will be applied against the current node context.


Source XML

<Patient lastName="Brown" firstName="Charlie">
<Diagnoses>
<row DiagnosisNo="1001" description="Depression" />
</Diagnoses>
<Allergies>
<row AllergyNo="1001" description="PEANUTS" reaction="Hives" />
<row AllergyNo="1002" description="LUCYVANPELT" reaction="Irritation" />
</Allergies>
<Medications />
</Patient>

Template

<div>
<h1>
{{ % FullName }}
</h1>
{{ Apply PatientDiagnoses Diagnoses }} {{ Apply PatientAllergies Allergies }} {{ Apply PatientMedications Medications }}
</div>

Supporting Templates

<templates>
<t name="FullName">
{{ = @lastName}}, {{ = @firstName }}
</t>
<t name="PatientDiagnoses">
{{ if (count(row)!=0) }}
<table>
<tbody>
{{ % PatientDiagnosisRow row }}
</tbody>
</table>
{{ else }}
<span>
No active diagnoses
</span>
{{ /if }}
</t>
<t name="PatientDiagnosisRow">
<tr>
<td>
{{ = @description }}
</td>
</tr>
</t>
<t name="PatientAllergies">
{{ if (count(row)!=0) }}
<table>
<tbody>
{{ % PatientAllergyRow row }}
</tbody>
</table>
{{ else }}
<span>
No documented allergies
</span>
{{ /if }}
</t>
<t name="PatientAllergyRow">
<tr>
<td>
{{ = @description }}
</td>
<td>
{{ = @reaction }}
</td>
</tr>
</t>
<t name="PatientMedications">
{{ if (count(row)!=0) }}
<table>
<tbody>
{{ % PatientMedicationRow row }}
</tbody>
</table>
{{ else }}
<span>
No active medications
</span>
{{ /if }}
</t>
<t name="PatientMedicationRow">
<tr>
<td>
{{ = @description }}
</td>
<td>
{{ = @strength }}
</td>
</tr>
</t>
</templates>

NOTE: The actual name of the child elements for a Supporting Templates block is irrelevant and ignored by the engine. The child tag <t> is used for illustration purposes only, to show that it is unimportant.


Resulting Output

<div>
<h1>
Brown, Charlie
</h1>
<table>
<tbody>
<tr>
<td>
Depression
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td>
PEANUTS
</td>
<td>
Hives
</td>
</tr>
<tr>
<td>
LUCYVANPELT
</td>
<td>
Irritation
</td>
</tr>
</tbody>
</table>
<span>
No active medications
</span>
</div>

ParamApply (%%)

In some cases, you will find a desire to pass parameters to supporting templates, which can be accomplished with the ParamApply command (with shorthand equivalent of %%). Unlike the Apply command, the ParamApply command requires an end command, being EndParamApply(or its shorthand options /ParamApply or /%%). Within the begin and end wrapper commands, you can specify either Variable (:=) or MultiVar (::=) commands, which will be scoped to within this template application. These variable declarations will act exactly like standard variables, however they will fall out of scope immediately after the template call.


Source XML

<Root>
<input click="this.focus();" desc="text field" />
<emptyEl />
<button click="alert(this.desc);" desc="stuff" />
</Root>

Template

<div>
{{ := onclick "'return void();'" }}{{ := description "'Howdy!'" }} {{ apply BuildElement }} {{ EACH * }} {{ paramapply BuildElement . }} {{ := element "name(.)" }}{{ := onclick @click }}{{ := description @desc }} {{ /paramapply }} {{ /EACH }} {{ apply BuildElement }}
</div>

Supporting Templates

<templates>
<t name="BuildElement">
<span onclick="{{ = $onclick }}" sourceEl="{{ = $element }}">
{{ = $description }}
</span>
</t>
</templates>

NOTE: The actual name of the child elements for a Supporting Templates block is irrelevant and ignored by the engine. The child tag <t> is used for illustration purposes only, to show that it is unimportant.


Resulting Output

<div>
<span onclick="return void();" sourceEl="">
Howdy!
</span>
<span onclick="this.focus();" sourceEl="input">
text field
</span>
<span onclick="" sourceEl="emptyEl" />
<span onclick="alert(this.desc);" sourceEl="button">
stuff
</span>
<span onclick="return void();" sourceEl="">
Howdy!
</span>
</div>

Miscellaneous Commands

NewID (GUID)

The NewID(or its alternate and more popular, GUID) command is used to generate a Globally Unique ID, and takes no parameters.


Source XML

<dontCare />

Template

<span>
{{ GUID }}
</span>

Resulting Output

<span>
4f3f639f-8891-4721-9f73-189773b3c4ae
</span>

Version (ver)

The Version command (or its shorthand ver) is used to simply display the current version information of the X2TL engine, and takes no parameters.


Source XML

<dontCare />

Template

<span>
{{ Version }}
</span>

Resulting Output

<span>
X2TL v1.1.0 rev.0
</span>