Page 1 of 1

$(xslt_eval(...)} editor variable

Posted: Thu Oct 14, 2021 1:22 pm
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

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

Posted: Fri Oct 15, 2021 4:43 pm
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

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

Posted: Tue Oct 19, 2021 11:48 pm
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

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

Posted: Sat Dec 04, 2021 2:32 pm
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

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

Posted: Mon Dec 06, 2021 12:44 pm
by alex_jitianu
Hi Chris,

Thank you for sharing your experience with the community!

Best regards,
Alex