xsl-list
[Top] [All Lists]

Re: [xsl] XSLT 3.0 try/catch doubts

2019-02-25 03:58:50
Hi Mike,
    Thanks for a detailed explanation about my XSLT use case. I've now
tried new ways of using XSLT try/catch syntax, and below are my new
examples where try/catch appear to be more useful in the context of my
examples. I'm using Saxon 9.9.

Input XML:

<?xml version="1.0" encoding="UTF-8"?>
<test>
   <val>5</val>
   <val>4</val>
   <val>3</val>
   <val>0</val>
   <val>2</val>
   <val>0</val>
   <val>1</val>
</test>

Stylesheet 1:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";

                         xmlns:err="http://www.w3.org/2005/xqt-errors";
                         xmlns:fn="http://xslt-functions";
                         xmlns:xs="http://www.w3.org/2001/XMLSchema";
                         exclude-result-prefixes="err fn xs"
                         version="3.0">

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

    <xsl:template match="/">
       <result>
          <xsl:for-each select="test/val">
             <xsl:copy-of select="fn:div(5, ., position())"/>
          </xsl:for-each>
       </result>
    </xsl:template>

    <xsl:function name="fn:div" as="element()">
       <xsl:param name="numerator" as="xs:integer"/>
       <xsl:param name="denominator" as="xs:integer"/>
       <xsl:param name="pos" as="xs:integer"/>

       <xsl:try>
          <xsl:sequence>
     <div num="{$numerator}" denom="{$denominator}"><xsl:value-of
select="$numerator div $denominator"/></div>
          </xsl:sequence>
          <xsl:catch errors="*">
             <xsl:sequence>
       <error val_pos="{$pos}" document="{$err:module}" code="{$err:code}"
description="{$err:description}" location="line:{$err:line-number},
col:{$err:column-number}" />
     </xsl:sequence>
  </xsl:catch>
       </xsl:try>
    </xsl:function>

</xsl:stylesheet>

The above stylesheet, when applied to the input XML document, produces
following output,

<?xml version="1.0" encoding="UTF-8"?>
<result>
   <div num="5" denom="5">1</div>
   <div num="5" denom="4">1.25</div>
   <div num="5" denom="3">1.666666666666666667</div>
   <error val_pos="4"
          document="file:/E:/saxon/../foo.xsl"
          code="err:FOAR0001"
          description="Integer division by zero"
          location="line:26, col:0"/>
   <div num="5" denom="2">2.5</div>
   <error val_pos="6"
          document="file:/E:/saxon/../foo.xsl"
          code="err:FOAR0001"
          description="Integer division by zero"
          location="line:26, col:0"/>
   <div num="5" denom="1">5</div>
</result>

The above output is, what I wanted to achieve (i.e either emit the "div"
tag if div operation is successful, or emit an "error" tag if div by 0
error occurs). For each iteration of loop, my numerator is fixed (its also
non zero, and 5 in my example) and denominator is variable (it can be zero.
that div error I want to catch via try/catch, and continue processing
subsequent items in the loop).
I would have been more happy, if an error message could show line numbers
of the offending nodes from input XML document. For that, Saxon's extension
function line-number(..) seems to do the job well.

Below is another stylesheet (Stylesheet 2) processing things a little
differently, and working on the same input XML document,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";

                         xmlns:err="http://www.w3.org/2005/xqt-errors";
                         xmlns:fn="http://xslt-functions";
                         xmlns:xs="http://www.w3.org/2001/XMLSchema";
                         exclude-result-prefixes="err fn xs"
                         version="3.0">

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

    <xsl:template match="/">
       <result>
          <xsl:try>
             <xsl:for-each select="test/val">
                <xsl:copy-of select="fn:div(5, .)"/>
             </xsl:for-each>
             <xsl:catch errors="*">
  <error document="{$err:module}" code="{$err:code}"
description="{$err:description}" location="line:{$err:line-number},
col:{$err:column-number}" />      </xsl:catch>
          </xsl:try>
       </result>
    </xsl:template>

    <xsl:function name="fn:div" as="element()">
       <xsl:param name="numerator" as="xs:integer"/>
       <xsl:param name="denominator" as="xs:integer"/>

       <xsl:sequence>
  <div num="{$numerator}" denom="{$denominator}"><xsl:value-of
select="$numerator div $denominator"/></div>
       </xsl:sequence>
    </xsl:function>

</xsl:stylesheet>

In the above stylesheet, I use the try/catch at a higher level (I enclose
the for-each loop within a try/catch). I get following output with this
XSLT transformation,

<?xml version="1.0" encoding="UTF-8"?>
<result>
   <error document="file:/E:/saxon/../foo1.xsl"
          code="err:FOAR0001"
          description="Integer division by zero"
          location="line:29, col:0"/>
</result>

This is expected. The transformation concludes after first failure, and
roll backs previous successful outcomes.

I'm happy with what I've learnt.

On Sat, Feb 23, 2019 at 11:37 PM Michael Kay mike(_at_)saxonica(_dot_)com <
xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:

Division by zero is a dynamic error, but if the operands are constants,
then most processors will be able to detect it statically, because
evaluating constant subexpressions is a very simple optimization.

So what do the rules say about dynamic errors? The detailed rules are in
§2.14 https://www.w3.org/TR/xslt-30/#errors -- which doesn't actually
mention try/catch.

The key paragraph is

<quote>
An implementation may signal a dynamic error before any source document is
available, but only if it can determine that the error would be signaled
for every possible source document and every possible set of parameter
values. For example, some circularity errors fall into this category: see
9.11 Circular Definitions.
</quote>

This isn't quite as rigorous as it might be. In your second example, there
are clearly source documents for which the error will never be raised
dynamically (those where count(/test/hello) is zero) and therefore it must
not be raised statically. In the first example, the error will always be
raised so long as the match="/" template is executed, that is, if
evaluation starts in the unnamed mode; that leaves some scope for
interpretation of exactly what the above paragraph is supposed to mean
(should it be read as saying "and for every possible initial mode"?). Also
the cited paragraph isn't really clear as to what "would be signaled"
means: if an error happens, but is not reported because it is caught by
try/catch, then is it "signaled" for the purpose of this rule? The whole
thing is a bit fuzzy round the edges.

What's happening in Saxon, though, is quite wrong. Saxon goes to great
lengths to avoid raising the error statically; however the optimizer
somehow rewrites the logic so that the constant expression (1 div 0) is
evaluated outside the try/catch and the error therefore occurs at run-time,
but isn't caught. I'll pursue that as a bug, even though the spec is a
little fuzzy.





-- 
Regards,
Mukul Gandhi

alt address : mukulgandhi(_at_)acm(_dot_)org
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--
<Prev in Thread] Current Thread [Next in Thread>