Controlled Attribute Values (Part 2 - Advanced)
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.
Note: Starting with Oxygen 17.1 there is a simpler way to achieve the use case presented in this post. The contextElementXPathExpression parameter will be bound to an XPath expression that identifies the element in the context of which the content completion was invoked.
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: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>