Friday, July 17, 2015

Controlled Attribute Values (Part 2 - Advanced)

Share to Facebook Share to Twitter Email This Share on Google Plus Share on Tumblr

As already presented in Controlled Attribute Values for your DITA Project, Oxygen allows you to add or replace possible values for attributes or elements based on a simple configuration file. A more complex scenario is one in which in order to decide which values to provide, you need more context information. Let's take this DITA fragment:

<metadata>
    <othermeta name="name" content="value"/>
</metadata>

What we want is to offer proposals for @content but the possible values for @content depend on the value of @name. We will see how we can solve this dependency.

The configuration file

The configuration file (cc_value_config.xml) allows calling an XSLT stylesheet and that's just what we will do:
<match elementName="othermeta" attributeName="content">
    <xslt href="meta.xsl" useCache="false"/>
</match>

As you can see, we can't express the dependency between @content and @name inside the configuration file . I also want to mention that because the values for @content are dynamic, we want the XSLT script to execute every time the values are requested (we shouldn't cache the results). We enforce this by setting @useCache to false.

The XSLT script

The XSLT script has access to the XML document (through the documentSystemID parameter) but it lacks any context information, we can't really tell for which othermeta element was the script invoked. To counter this limitation, we will use Java extension functions and we will call Oxygen's Java-based API from the XSLT. Here how it looks:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
    xmlns:tei="http://www.oxygenxml.com/ns/doc/xsl"
    xmlns:prov="java:ro.sync.exml.workspace.api.PluginWorkspaceProvider"
    xmlns:work="java:ro.sync.exml.workspace.api.PluginWorkspace"
    xmlns:editorAccess="java:ro.sync.exml.workspace.api.editor.WSEditor"
    xmlns:saxon="http://saxon.sf.net/"
    xmlns:textpage="java:ro.sync.exml.workspace.api.editor.page.text.xml.WSXMLTextEditorPage"
    xmlns:authorPage="java:ro.sync.exml.workspace.api.editor.page.author.WSAuthorEditorPage"
    xmlns:ctrl="java:ro.sync.ecss.extensions.api.AuthorDocumentController"
    exclude-result-prefixes="xs xd"
    version="2.0">
    <xsl:param name="documentSystemID" as="xs:string"></xsl:param>
    
    <xsl:template name="start">
        <xsl:variable name="workspace" select="prov:getPluginWorkspace()"/>
        <xsl:variable name="editorAccess" select="work:getEditorAccess($workspace, xs:anyURI($documentSystemID), 0)"/>
        <xsl:variable name="pageID" as="xs:string" select="editorAccess:getCurrentPageID($editorAccess)"/>
        
        <xsl:variable name="name" as="xs:string">
            <xsl:choose>
                <xsl:when test="$pageID='Text'">
                    <xsl:variable name="textpage" select="editorAccess:getCurrentPage($editorAccess)"/>
                    <!-- In the text page, the context is the @content attribute -->
                    <xsl:value-of select="textpage:evaluateXPath($textpage, 'xs:string(./parent::node()/@name)')"/>
                </xsl:when>
                <xsl:when test="$pageID='Author'">
                    <xsl:variable name="authorPage" select="editorAccess:getCurrentPage($editorAccess)"/>
                    <xsl:variable name="caretOffset" select="authorPage:getCaretOffset($authorPage)"/>
                    <xsl:variable name="ctrl" select="authorPage:getDocumentController($authorPage)"/>
                    <xsl:variable name="contextNode" select="ctrl:getNodeAtOffset($ctrl, $caretOffset)"/>
                    <!-- In the author page, the context is the "othermeta" element -->
                    <xsl:value-of select="ctrl:evaluateXPath($ctrl, 'xs:string(@name)', $contextNode, false(), false(), false(), false())[1]"/>
                </xsl:when>
            </xsl:choose>
        </xsl:variable>
        
        <items>        
            <xsl:choose>
                <xsl:when test="$name = 'temperatureScale'">
                    <item value="Celsius" annotation="(symbol C)"/>
                    <item value="Fahrenheit" annotation="(symbol F)"/>
                </xsl:when>
                <xsl:when test="$name = 'measurement'">
                    <item value="Metric" annotation="Metric system"/>
                    <item value="Imperial" annotation="Also known as British Imperial"/>
                </xsl:when>
            </xsl:choose>
        </items>
    </xsl:template>    
</xsl:stylesheet>

It is also worth mentioning that in the next Oxygen version (17.1) we will provide a more elegant solution to this situation. The XSLT script will receive a new parameter, an XPath expression that will identify the element for which content completion was invoked. But maybe we will talk about that in a future post...

No comments:

Post a Comment