Resolving DITA Cross-References in Refactoring Operations
We needed a way to resolve DITA <xref>
and <link>
elements to their target element in a refactoring operation. We also needed to determine
whether the link was a local or peer-map reference. Fortunately, Oxygen v25.1 provided
us with the solution!
The Missing Piece - Resolving a @keyref
For <xref>
and <link>
elements, we use a mix
of @href
and @keyref
references in our content. In
XSLT refactoring operations, we could resolve @href
references to
the target file (and optionally, an element in that file) by using the XPath
document()
function. However, we had no way of resolving
@keyref
references because a refactoring operation only
processes the current file (it doesn't consider Oxygen's map context or the keys
defined within it). In addition, writing such a resolver in XSLT that properly
considers keyscopes would be enormously difficult.
In the Oxygen v25.1 release, new API functions were introduced to provide
information about @keyref
references:
-
The
getKeyRefInfo()
function returns information about a reference's type (local, peer, unresolved, and so on). -
The
getKeyRefAbsoluteReference()
function resolves a@keyref
to its equivalent@href
reference URL.
There were subsequent improvements to the API functions in Oxygen v25.1 build 2023070306, so you should use that release or later for best results.
Thanks to this new API, we could use a simple @href
resolver written in
XSLT for @keyref
references too!
Resolving Cross-References in Refactoring Operations
The attached test case provides the following XSLT file that you can include in your own refactoring operations:
frameworks/dita/refactoring/util-get-referenced-element.xsl
This file defines a mode="get-referenced-element"
template that,
when applied to any element with an @href
or
@keyref
attribute, returns the referenced element. If the
reference cannot be resolved, the template returns an empty sequence.
The template works as follows:
-
References with
always return an empty sequence.@scope
="external" -
References with
@format
set to a value other than"dita"
always return an empty sequence. -
If the reference has a
@keyref
, it is converted to an@href
value usinggetKeyRefAbsoluteReference()
. -
The
@href
value is parsed into its components as follows:[file]#topic_id[/element_id]
-
The target document is obtained as follows:
-
If no file is specified, the in-memory document that contains the cross-reference element is used.
-
If a file is specified and that file contains the cross-reference element, the in-memory document that contains the cross-reference element is used.
-
Otherwise, the specified file document is loaded from disk using the XPath
document()
function.
This heuristic approach ensures that in multiple-pass refactoring operations, the in-memory version of the content is preferred over the on-disk version.
-
-
The topic that matches the topic_id value is obtained from the target document.
-
If an element_id is specified, the element in the topic that matches the element_id value is obtained.
Because non-topic
@id
values do not need to be unique, the code ensures that no subtopics within the matching topic are searched to avoid incorrect matches.
To view the XSLT stylesheet without downloading the archive, click on the following link:
util-get-referenced-element.xsl
There are comments in the code to explain how it works.
The @keyref
API functions require that a map context be active in
Oxygen. If no context is available (for example, no map is open in the
DITA Maps Manager), there will be no key information
available to resolve the reference.
Example Test Case
The following Oxygen project provides examples of how cross-reference resolutions can be used in refactoring operations and Schematron checks:
resolving_refs_refactoring.zip
Specifically, it provides the following:
-
An "Update Cross References" refactoring operation is provided that:
-
Sets (or updates) the
@type
attribute for<xref>
and<link>
elements.
-
Populates the target text for
<xref>
and<link>
elements that reference topics in peer maps (i.e. cross-book links).
-
-
Schematron checks are provided that:
-
Warn about
<xref>
and<link>
elements that reference topics in peer maps (i.e. cross-book links) but do not contain any target text.
This check also offers a "quick fix" that populates the target text for you.
-
Show the value of the
getKeyRefInfo()
andgetKeyRefAbsoluteReference()
API calls for any element with a@keyref
attribute.
These informational checks are commented out by default. You can uncomment them in the following file:
frameworks/dita/sch/checks.sch
-
When target text is added to a peer map (cross-book) reference, an
<?oxy-peertext?>
processing instruction is added to
indicate that the text was updated automatically. If you remove this processing
instruction and customize the target text, your customized text will not be
disturbed by future automatic updates.