Re: [xsl] Flatten DITA conrefs

Subject: Re: [xsl] Flatten DITA conrefs
From: "Martin Honnen martin.honnen@xxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 11 Apr 2025 22:06:38 -0000
On 11/04/2025 21:21, rick@xxxxxxxxxxxxxx wrote:
>
> Hi All,
>
> I am working with a DITA transformation and I want to flatten some
> conrefs. I have created a function to do this, and it is working, but
> I am looking for feedback to see if there is a better approach. Here
> is my XML:
>
> <?xml version="1.0" encoding="UTF-8"?>
>
> <!DOCTYPE concept
>
> B  PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
>
> <concept id="ConrefTest-GAC500-B-15-30-27-02A-131C-A">
>
> B B B  <title>Conref Test</title>
>
> B B B  <conbody>
>
> B B B B B B B  <section>
>
> B B B B B B B B B B B  <title>Possible Cause</title>
>
> B B B B B B B B B B B  <p>
>
> B B B B B B B B B B B B B B B  <ph
>
> conref="CASMessages.dita#cas-messages/AFM-L-R-CAI-Selected-Off-status"
>
> outputclass="cas-status"/>
>
> B B B B B B B B B B B  </p>
>
> B B B B B B B  </section>
>
> B B B  </conbody>
>
> </concept>
>
> Here is my XSLT. If the conref doesnbt resolve, I am just passing the
> parent element through as is.
>
> <?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:cp="http://www.frameexpert.com/functions/cp";
>
> B B B  exclude-result-prefixes="xs cp"
>
> B B B  version="3.0" expand-text="yes">
>
> B B B B <xsl:output indent="yes"/>
>
> B B B B <xsl:template
> match="entry/ph[matches(@conref,'CASMessages','i')=true()]">
>
> B B B B B B B  <xsl:sequence select="cp:flattenConref(current())"/>
>
> B B B  </xsl:template>
>
> B B B B <xsl:mode on-no-match="shallow-copy"/>
>
> B B B B <xsl:function name="cp:flattenConref">
>
> B B B B B B B  <xsl:param name="ph"/>
>
> B B B B B B B  <!-- Split the conref to get the target file. -->
>
> B B B B B B B  <xsl:variable name="conrefData"
> select="tokenize($ph/@conref,'#')"/>
>
> B B B B B B B  <xsl:choose>
>
> B B B B B B B B B B B  <!-- Make sure there is a file and it is available on
> disk. -->
>
> B B B B B B B B B B B  <xsl:when test="count($conrefData)=2">
>
> B B B B B B B B B B B B B B B  <xsl:variable name="file"
> select="resolve-uri($conrefData[1],base-uri($ph))"/>
>
> B B B B B B B B B B B B B B B  <xsl:choose>
>
> B B B B B B B B B B B B B B B B B B B  <xsl:when
test="doc-available($file)=true()">
>
> B B B B B B B B B B B B B B B B B B B B B B B  <!-- Get the target id from
the conref. -->
>
> B B B B B B B B B B B B B B B B B B B B B B B  <xsl:variable name="id"
> select="tokenize($conrefData[2],'/')[last()]"/>
>
> B B B B B B B B B B B B B B B B B B B B B B B  <xsl:choose>
>
> B B B B B B B B B B B B B B B B B B B B B B B B B B B  <!-- See if the
target element exists. -->
>
> B B B B B B B B B B B B B B B B B B B B B B B B B B B  <xsl:when
test="doc($file)//*[@id=$id]">
>
> B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B  <!-- Return
the conref's value. -->
>
> B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B  <ph
> outputclass="{$ph/@outputclass}"><xsl:sequence
> select="doc($file)//*[@id=$id]/text()"/></ph>
>
> </xsl:when>
>
> B B B B B B B B B B B B B B B B B B B B B B B  </xsl:choose>
>
> B B B B B B B B B B B B B B B B B B B  </xsl:when>
>
> B B B B B B B B B B B B B B B B B B B  <!-- The conref file does not exist;
return the ph
> element. -->
>
> <xsl:otherwise><xsl:sequence select="$ph"/></xsl:otherwise>
>
> B B B B B B B B B B B B B B B  </xsl:choose>
>
> B B B B B B B B B B B  </xsl:when>
>
> B B B B B B B B B B B  <!-- No # anchor; return the ph element. -->
>
> <xsl:otherwise><xsl:sequence select="$ph"/></xsl:otherwise>
>
> B B B B B B B  </xsl:choose>
>
> B B B  </xsl:function>
>
> </xsl:stylesheet>
>

Two small comments, I never get why people use a comparison to true or
false, I would just go with e.g.

 B  xsl:template match="entry/ph[matches(@conref,'CASMessages','i')]"

and

 B  xsl:when test="doc-available($file)"


And I wonder whether DITA sets up the id attribute as a DTD ID or schema
xs:ID so that instead of

 B  doc($file)//*[@id=$id]

you could use e.g.

 B  id($id, doc($file))

Current Thread