Search code examples
xmlxsltsaxonxslt-3.0xmlspy

Accumulator is not applicable to the current document with Saxon PE 9-9-1-5 Java


I have an input XML:

<?xml version='1.0' encoding='UTF-8'?>
<MessageFormat name='TransportEvent' version='2.02'>
    <StructFormat name='TransportEventHeader' >
        <TagField type='String' value='H0 '/>
        <FieldFormat name='NotificationTriggeringOU' length='10'/>
        <FieldFormat name='NotificationTriggeringIT' length='8'/>
        <FieldFormat name='NotificationReference' length='1'/>
        <FieldFormat name='NotificationCode' length='3'/>
        <FieldFormat name='NotificationType' length='8'/>
        <FieldFormat name='NotificationStatus' length='1'/>
        <FieldFormat name='NotificationTripType' length='1'/>
        <FieldFormat name='ProducingRailwayUndertaking' length='4'/>
        <FieldFormat name='ExternalPartner' length='35'/>
        <FieldFormat name='ActualNumberOfWagons' length='3' />
        <FieldFormat name='ProcessingTime' length='26'/>
        <StructFormat name='NotificationFunctionalClassification'>
            <FieldFormat name='OrderRelevant' length='1'/>
            <FieldFormat name='TimetableRelevant' length='1'/>
            <FieldFormat name='CapacityRelevant' length='1'/>
            <FieldFormat name='IntermodalRelevant' length='1'/>
            <FieldFormat name='XrailRelevant' length='1'/>
            <FieldFormat name='NotificationLocationRelevant' length='1'/>
        </StructFormat>
        <FieldFormat name='Reserve' length='1'/>
    </StructFormat>
    <StructFormat name='NotificationLocation' >
        <TagField type='String' value='M1 '/>
        <StructFormat name='CurrentLocation'>
            <FieldFormat name='CurrentLocationRL100' length='5'/>
            <FieldFormat name='CurrentLocationLocationType' length='1'/>
            <FieldFormat name='CurrentUICRailAuthorityNumber' length='6'/>
            <FieldFormat name='CurrentNetworkLocationNumber' length='6'/>
            <FieldFormat name='CurrentLocationSatelliteNumber' length='2'/>
            <FieldFormat name='CurrentFreightCarLocationNumber' length='4'/>
        </StructFormat>
        <StructFormat name='NextLocation'>
            <FieldFormat name='NextLocationRL100' length='5'/>
            <FieldFormat name='NextLocationLocationType' length='1'/>
            <FieldFormat name='NextUICRailAuthorityNumber' length='6'/>
            <FieldFormat name='NextNetworkLocationNumber' length='6'/>
            <FieldFormat name='NextLocationSatelliteNumber' length='2'/>
            <FieldFormat name='NextFreightCarLocationNumber' length='4'/>
        </StructFormat>
    </StructFormat>
    <StructFormat name='NotificationTime' >
        <TagField type='String' value='M2 '/>
        <FieldFormat name='ActualTime' length='18'/>
    </StructFormat>
    <StructFormat name='Trip' >
        <TagField type='String' value='Z1 '/>
        <FieldFormat name='TripNumber' length='6'/>
        <FieldFormat name='RegionNetz' length='2'/>
        <FieldFormat name='NationalProductionDate' length='10'/>
        <FieldFormat name='TrainTypeMainNumber' length='2'/>
        <FieldFormat name='TrainTypeSubNumber' length='1'/>
        <FieldFormat name='DepartureStationRL100' length='5'/>
        <FieldFormat name='DepartureStationUICRailAuthority' length='6'/>
        <FieldFormat name='DepartureStationNetworkLocation' length='6'/>
        <FieldFormat name='TargetTime' length='18'/>
        <FieldFormat name='RelativeTime' length='5' />
    </StructFormat>
    <StructFormat name='HandoverTakeover' >
        <TagField type='String' value='U1 '/>
        <FieldFormat name='HandoverTakeoverFlag' length='1'/>
        <FieldFormat name='ConsigningRU' length='4'/>
        <FieldFormat name='AcceptingRU' length='4'/>
        <FieldFormat name='UICBorderCode' length='3'/>
    </StructFormat>
</MessageFormat>

And in my xslt template I want to count (FieldFormat/@length + string-length(TagField/@value) of each StructFormat direct under MessageFormat:

<?xml version="1.0" encoding="UTF-8"?>
<?altova_samplexml format.xml?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:array="http://www.w3.org/2005/xpath-functions/array" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:err="http://www.w3.org/2005/xqt-errors" xmlns:saxon="http://saxon.sf.net/" exclude-result-prefixes="array fn map math xhtml xs err" version="3.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:variable name="msg">H0 EVU_DBSRD PVG     W14 PVGERL  I                                        0142019-12-08-07.46.52.436704NNJNNJNM1           80270611 04401                        M2 08.12.201907:50:00Z1 BAU1    08.12.2019                    08.12.201907:50:00 0000R1 00131806940728280201912063946287201912061215058175346965                                   000NNJNNN                                  80270611 04401R1 00231806940476880201912063946287201912061215059494346965                                   000NNJNNN                                  80270611 04401R1 00331806948174180201912063946287201912061215050425346965                                   000NNJNNN                                  80270611 04401R1 00431806948098280201912063946287201912061215051864346965                                   000NNJNNN                                  80270611 04401</xsl:variable>
    <xsl:accumulator name="position-count" as="xs:double" initial-value="1" streamable="no">
        <xsl:accumulator-rule match="/MessageFormat/StructFormat" phase="end" select="$value +sum(.//FieldFormat/@length) + string-length(./TagField/@value)" />
    </xsl:accumulator>
    <xsl:template match="/">
        <output>
            <xsl:for-each select="/MessageFormat/StructFormat">
            <xsl:element name="{./@name}">
                <xsl:attribute name="start" select="fn:accumulator-before('position-count')"/>
                <xsl:attribute name="end" select="fn:accumulator-after('position-count')" />
                </xsl:element>
            </xsl:for-each>
        </output>
    </xsl:template>
</xsl:stylesheet>

When I run it with XML Spy Professional 2020 SP1, I get the output:

<?xml version="1.0" encoding="UTF-8"?>
<output xmlns:saxon="http://saxon.sf.net/">
    <TransportEventHeader start="1" end="111"/>
    <NotificationLocation start="111" end="162"/>
    <NotificationTime start="162" end="183"/>
    <Trip start="183" end="247"/>
    <HandoverTakeover start="247" end="262"/>
</output>

But when I run it with Saxon 9-9-1-5 Java java -cp /d/SaxonPE9-9-1-5J/saxon9pe.jar net.sf.saxon.Transform -s:format.xml -xsl:accumulator.xslt -o:output.xml I get error:

Error evaluating (fn:accumulator-before(...)) in xsl:attribute/@select on line 13 column 83 of accumulator.xslt: XTDE3362: Accumulator position-count is not applicable to the current document invoked by unknown caller (class net.sf.saxon.expr.instruct.ForEach) at file:/D:/xslt/accumulator.xslt#11 In template rule with match="/" on line 9 of accumulator.xslt Accumulator position-count is not applicable to the current document

What did I wrong with my accumulator? Why does it work with XML Spy, not with Saxon 9.9?

Actually I want to add a condition in my accumulator to check, if the value TagField/@value in variable $msg at a right position occures.

For example, for StructFormat[@name='TransportEventHeader'], the first 3 characters in $msg are "H0 ", so it matches StructFormat[@name='TransportEventHeader']/TagField/@value, in this case the length shoud be added to my accumulator, if not, then it should not be added. I don't know how to implement this in accumulator.


Solution

  • The rules are here: https://www.w3.org/TR/xslt-30/#applicability-of-accumulators

    Rule 5 says: For a document containing nodes supplied in the initial match selection, the accumulators that are applicable are those determined by the xsl:mode declaration of the initial mode. This means that in the absence of an xsl:mode declaration, no accumulators are applicable.

    So you need to add

    <xsl:mode use-accumulators="position-count"/>
    

    The reason for these rules in the spec is that (a) with streaming, it is expensive to maintain an accumulator for a streamed document if it is not going to be used on that document, and (b) although the same overhead does not exist with non-streamed documents, because accumulators can be evaluated when first used, it was thought desirable to have the same rule whether the document is streamed or unstreamed.

    Altova does not implement streaming so they probably thought (and with some justification, I would concur) that implementing the rules is a lot of effort that delivers little value to users.