$(xslt_eval(...)} editor variable

Are you missing a feature? Request its implementation here.
Krille
Posts: 33
Joined: Thu Nov 12, 2020 12:24 pm

$(xslt_eval(...)} editor variable

Post by Krille »

Hi,

I find myself regularly writing author mode actions with complex XPath expressions. These expressions generate values and labels for comboboxes or radio buttom in user dialogs from project content. E.g. like in this author mode action which inserts an element and an attribute to encode a bibliographic reference.

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<a:authorAction xmlns:a="http://www.oxygenxml.com/ns/author/external-action"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.oxygenxml.com/ns/author/external-action http://www.oxygenxml.com/ns/author/external-action/authorAction.xsd"
  id="edit_bibref">
  <a:name>Edit bibliographic reference</a:name>
  <a:description>Inserts or changes a bibliographic reference.</a:description>
  <a:operations>
    <a:operation id="InsertFragmentOperation">
      <a:xpathCondition>self::note</a:xpathCondition>
      <a:arguments>
        <a:argument name="fragment"><![CDATA[<bibl xmlns="http://www.tei-c.org/ns/1.0" corresp="${ask('Choose entry from bibliography', combobox, (${xpath_eval(let $biblio := doc('${framework}/samples/biblio.xml') return string-join(for $entry in $biblio//*:body//*:listBibl//*:bibl return concat("'", '#', $entry/@xml:id, "':'", replace(normalize-space($entry), "'", ' '), "'"), ';'))}), '')}"/>]]></a:argument>
      </a:arguments>
    </a:operation>
    <a:operation id="ChangeAttributeOperation">
      <a:xpathCondition>self::bibl[not(parent::listBibl)]</a:xpathCondition>
      <a:arguments>
        <a:argument name="elementLocation">self::bibl</a:argument>
        <a:argument name="name">corresp</a:argument>
        <a:argument name="value"><![CDATA[${ask('Choose entry from bibliography', combobox, (${xpath_eval(let $biblio := doc('${framework}/samples/biblio.xml') return string-join(for $entry in $biblio//*:body//*:listBibl//*:bibl return concat("'", '#', $entry/@xml:id, "':'", replace(normalize-space($entry), "'", ' '), "'"), ';'))}), '${xpath_eval(@corresp)}')}]]></a:argument>
      </a:arguments>
    </a:operation>
  </a:operations>
  <a:accessKey/>
</a:authorAction>
(The path ${framework}/samples/biblio.xml is actually rewritten using an xml catalog.)

However, these XPath-Expressions are really a mess when it comes to debugging and mainting code. It would be much more maintainable when one would be able to source out the code that generates the string for values and labels into an external script. IMO XSLT or XQuery would be best suited for this and it could be tested with Xspec.

So instead of

Code: Select all

${xpath_eval(...)}
it would be great to write

Code: Select all

${xslt_eval(...)}
or more precisely

Code: Select all

${xslt_eval('stylesheet.xsl', 'start-template', (param1: value1, param2: value2, ...))}
Could you imagine adding a feature like this? Are there alternatives?

Regards,
Chris
alex_jitianu
Posts: 1008
Joined: Wed Nov 16, 2005 11:11 am

Re: $(xslt_eval(...)} editor variable

Post by alex_jitianu »

Hello,

I think this is a good idea, so I've recorded an issue to add this variable. These XPaths tend to be quite complex so an XSLT would be easier to understand. Being able to test it with an XSpec is a great bonus too. We will post here when it gets released.

Meanwhile, perhaps you can have an XSLT that generates the XPath expressions. You can have XSpec tests to ensure there are no regressions and when you want to change something in the XPath, you can make the change in the XSLT and execute it with various parameters (like in your ${xslt_eval(...)} example) to obtain the new XPath. It's just an idea, I'm not entirely sure how much it would help, so I'm interesting in hearing your opinion on this as well.

Best regards,
Alex
Krille
Posts: 33
Joined: Thu Nov 12, 2020 12:24 pm

Re: $(xslt_eval(...)} editor variable

Post by Krille »

Hi Alex,

thanks for recording the issue!

Writing XSLT for generating the XPath expression and inserting it a) into the author-mode-action and b) into something I can run in a test on is a cool idea. I'll definitively try that out. I will run into trouble with quoting an expression, that's already a quoting monster - but nevermind! Having a test is worth it.

I'll report my experiences.

Best regards,
Chris
Krille
Posts: 33
Joined: Thu Nov 12, 2020 12:24 pm

Re: $(xslt_eval(...)} editor variable

Post by Krille »

Hi Alex,

I managed to write XSLT that extracts the XPath expressions and puts them into an XSL stylesheet. This stylesheet can then be tested with Xspec. Despite the intensive use of quoting in the XPath expression, it has shown to be executable in the test without the need of applying a refacturing function first.

The XSLT is generic. So I put it here for reuse by who-ever-wants-it for regression testing of external author actions.

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<!-- make a testable xsl stylesheet with the xpath expressions from an external author action -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"
    xmlns:a="http://www.oxygenxml.com/ns/author/external-action" xmlns="http://test.it"
    exclude-result-prefixes="xs a" version="3.0">

    <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>

    <xsl:output method="xml" indent="yes"/>

    <!-- A list of xpath expressions used as match conditions for the tests.
        Separated by double pipe, i.e. '||'. If a position is empty, the
        xpathCondition of the operation is used instead.
        Example value for link-person: 'persName|person||prefixDef' -->
    <xsl:param name="match-conditions" as="xs:string" select="''" required="no"/>

    <xsl:mode on-no-match="shallow-skip"/>

    <xsl:template match="/">
        <axsl:stylesheet version="3.0" xpath-default-namespace="http://www.tei-c.org/ns/1.0">
            <axsl:output method="xml" indent="yes"/>
            <axsl:template match="/">
                <tests>
                    <axsl:apply-templates/>
                </tests>
            </axsl:template>
            <xsl:apply-templates/>
            <axsl:template match="text()"/>
        </axsl:stylesheet>
    </xsl:template>

    <!-- extract the contents of ${xpath_eval(...)} -->
    <xsl:template match="text()[matches(., '\$\{xpath_eval')]">
        <xsl:variable name="n" select="count(preceding::text()[matches(., '\$\{xpath_eval')]) + 1"/>
        <xsl:variable name="match-condition" select="
                let $cond := normalize-space(tokenize($match-conditions, '\|\|')[$n])
                return
                    if ($cond ne '') then
                        $cond
                    else
                        ancestor::a:operation/a:xpathCondition"/>
        <axsl:template match="{$match-condition}">
            <test class-number="{$n}" class-type="{ancestor::a:operation/@id}"
                class-match="{$match-condition}">
                <axsl:attribute name="case" select="name(.)"/>
                <xsl:analyze-string select="." regex="xpath_eval\(([^\}}]+)\)\}}">
                    <xsl:matching-substring>
                        <case>
                            <axsl:value-of>
                                <xsl:attribute name="select" select="regex-group(1)"/>
                            </axsl:value-of>
                            <!--axsl:text>$$$</axsl:text-->
                        </case>
                    </xsl:matching-substring>
                </xsl:analyze-string>
                <!--            
                <xsl:value-of select="."/>
                <xsl:text>&#xa;&#xa;</xsl:text>
                -->
            </test>
        </axsl:template>
    </xsl:template>

    <xsl:template match="text()"/>

</xsl:stylesheet>
Cheers,
Chris
alex_jitianu
Posts: 1008
Joined: Wed Nov 16, 2005 11:11 am

Re: $(xslt_eval(...)} editor variable

Post by alex_jitianu »

Hi Chris,

Thank you for sharing your experience with the community!

Best regards,
Alex
Post Reply