Edit online

Controlled Attribute Values (Part 2 - Advanced)

Read time: 4 minute(s)

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>