Re: [xsl] Flatten DITA conrefs

Subject: Re: [xsl] Flatten DITA conrefs
From: "Michael Kay michaelkay90@xxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sat, 12 Apr 2025 07:50:19 -0000
 >bJavaScript: The Good Partsb and his counsel was to be as explicit as
possible when programming so I like to use true() and false() when making
comparisons.

There's something to be said for avoiding reliance on "effective boolean
value" conversions, but when you're testing the result of a function like
matches() or doc-available() that always returns a boolean, it makes very
little sense. You wouldn't (I hope) write `test="(@x = 2) = true()"`.

I would encourage you to declare the parameter type and result type of the
function. (Both as="element(ph)", I think). This might point out that when the
innermost xsl:when condition is false, there is no xsl:otherwise so you are
returning an empty sequence. I don't know if that's what you intended.

I would be inclined to write

> <xsl:function name="cp:flattenConref" as="element(ph)">
>         <xsl:param name="ph" as="element(ph)"/>
>         <!-- Split the conref to get the target file. -->
>         <xsl:variable name="conrefData"
select="tokenize($ph/@conref,'#')"/>
                       <xsl:variable name="result" as="element(ph)?">
>
>             <!-- Make sure there is a file and it is available on disk. -->
>             <xsl:if test="count($conrefData)=2">
>                 <xsl:variable name="file"
select="resolve-uri($conrefData[1],base-uri($ph))"/>
>                 <xsl:if test="doc-available($file)=true()">
>                         <!-- Get the target id from the conref. -->
>                         <xsl:variable name="id"
select="tokenize($conrefData[2],'/')[last()]"/>
                                            <xsl:variable name="target"
select="doc($file)//*[@id=$id]"/>
>
>                             <!-- See if the target element exists. -->
>                             <xsl:if test="exists($target)">
>                                 <!-- Return the conref's value. -->
>                                 <ph
outputclass="{$ph/@outputclass}"><xsl:sequence select="$target/text()"/></ph>
>                             </xsl:if>>
>             </xsl:if>
                     </xsl:variable>
                     <xsl:sequence select="($result, $ph)[1]"/>
>     </xsl:function>


> From: Martin Honnen martin.honnen@xxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
> Sent: Friday, April 11, 2025 6:07 PM
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject: Re: [xsl] Flatten DITA conrefs
>
> On 11/04/2025 21:21, rick@xxxxxxxxxxxxxx <mailto: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
>   PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd">
> <concept id="ConrefTest-GAC500-B-15-30-27-02A-131C-A">
>     <title>Conref Test</title>
>     <conbody>
>         <section>
>             <title>Possible Cause</title>
>             <p>
>                 <ph
>
conref="CASMessages.dita#cas-messages/AFM-L-R-CAI-Selected-Off-status"
>                 outputclass="cas-status"/>
>             </p>
>         </section>
>     </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";
<http://www.w3.org/1999/XSL/Transform>
>     xmlns:xs="http://www.w3.org/2001/XMLSchema";
<http://www.w3.org/2001/XMLSchema>
>     xmlns:cp="http://www.frameexpert.com/functions/cp";
<http://www.frameexpert.com/functions/cp>
>     exclude-result-prefixes="xs cp"
>     version="3.0" expand-text="yes">
>
>     <xsl:output indent="yes"/>
>
>     <xsl:template
match="entry/ph[matches(@conref,'CASMessages','i')=true()]">
>         <xsl:sequence select="cp:flattenConref(current())"/>
>     </xsl:template>
>
>     <xsl:mode on-no-match="shallow-copy"/>
>
>     <xsl:function name="cp:flattenConref">
>         <xsl:param name="ph"/>
>         <!-- Split the conref to get the target file. -->
>         <xsl:variable name="conrefData"
select="tokenize($ph/@conref,'#')"/>
>         <xsl:choose>
>             <!-- Make sure there is a file and it is available on disk. -->
>             <xsl:when test="count($conrefData)=2">
>                 <xsl:variable name="file"
select="resolve-uri($conrefData[1],base-uri($ph))"/>
>                 <xsl:choose>
>                     <xsl:when test="doc-available($file)=true()">
>                         <!-- Get the target id from the conref. -->
>                         <xsl:variable name="id"
select="tokenize($conrefData[2],'/')[last()]"/>
>                         <xsl:choose>
>                             <!-- See if the target element exists. -->
>                             <xsl:when test="doc($file)//*[@id=$id]">
>                                 <!-- Return the conref's value. -->
>                                 <ph
outputclass="{$ph/@outputclass}"><xsl:sequence
select="doc($file)//*[@id=$id]/text()"/></ph>
>                             </xsl:when>
>                         </xsl:choose>
>                     </xsl:when>
>                     <!-- The conref file does not exist; return the ph
element. -->
>                     <xsl:otherwise><xsl:sequence
select="$ph"/></xsl:otherwise>
>                 </xsl:choose>
>             </xsl:when>
>             <!-- No # anchor; return the ph element. -->
>             <xsl:otherwise><xsl:sequence select="$ph"/></xsl:otherwise>
>         </xsl:choose>
>     </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.
>
>   xsl:template match="entry/ph[matches(@conref,'CASMessages','i')]"
>
> and
>
>   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
>
>   doc($file)//*[@id=$id]
>
> you could use e.g.
>
>   id($id, doc($file))
>
>
>
>
>
> XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
> EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/612310> (by
email <>)
> XSL-List info and archive <http://www.mulberrytech.com/xsl/xsl-list>
> EasyUnsubscribe <http://lists.mulberrytech.com/unsub/xsl-list/3500899> (by
email <>)

Current Thread