Project

Profile

Help

Revision 22a9253e

Added by Michael Kay almost 3 years ago

Fix bug #4054 (xsl:next-match with union patterns)

View differences:

latest9.9/hej/net/sf/saxon/om/SelectedElementsSpaceStrippingRule.java
102 102
            throws XPathException {
103 103
        NodeTest test = pattern.getNodeTest();
104 104
        double priority = test.getDefaultPriority();
105
        Rule newRule = new Rule(pattern, action, precedence, minImportPrecedence, priority, sequence++);
105
        Rule newRule = new Rule(pattern, action, precedence, minImportPrecedence, priority, sequence++, 0);
106 106
        int prio = priority == 0 ? 2 : priority == -0.25 ? 1 : 0;
107 107
        newRule.setRank((precedence << 18) + (prio << 16) + sequence);
108 108
        if (test instanceof NodeKindTest) {
latest9.9/hej/net/sf/saxon/pattern/UnionPattern.java
142 142
        return "union";
143 143
    }
144 144

  
145
    /**
146
     * Get the original pattern text
147
     */
148
    @Override
149
    public String toString() {
150
        return super.toString();    //To change body of overridden methods use File | Settings | File Templates.
151
    }
152

  
153 145
    /**
154 146
     * Copy a pattern. This makes a deep copy.
155 147
     *
latest9.9/hej/net/sf/saxon/style/XSLAccumulator.java
208 208
                boolean isPreDescent = !rule.isPostDescent();
209 209
                SimpleMode mode = isPreDescent ? accumulator.getPreDescentRules() : accumulator.getPostDescentRules();
210 210
                AccumulatorRule action = new AccumulatorRule(newValueExp, stackFrameMap, rule.isPostDescent());
211
                mode.addRule(pattern, action, decl.getModule(), decl.getModule().getPrecedence(), 1, true);
211
                mode.addRule(pattern, action, decl.getModule(), decl.getModule().getPrecedence(), 1, 0, 0);
212 212

  
213 213
                checkRuleStreamability(rule, pattern, newValueExp);
214 214

  
latest9.9/hej/net/sf/saxon/style/XSLTemplate.java
902 902
                }
903 903

  
904 904
                double prio = prioritySpecified ? priority : Double.NaN;
905
                mgr.setTemplateRule(match1, rule, mode, module, prio);
905
                mgr.registerRule(match1, rule, mode, module, prio, mgr.allocateSequenceNumber(), 0);
906 906

  
907 907
                if (mode.isDeclaredStreamable()) {
908 908
                    rule.setDeclaredStreamable(true);
......
927 927

  
928 928
                if (mode.getModeName().equals(Mode.OMNI_MODE)) {
929 929
                    compiledTemplateRules.put(Mode.UNNAMED_MODE_NAME, rule);
930
                    mgr.setTemplateRule(match1, rule, mgr.getUnnamedMode(), module, prio);
930
                    mgr.registerRule(match1, rule, mgr.getUnnamedMode(), module, prio, mgr.allocateSequenceNumber(), 0);
931 931
                    for (Mode m : mgr.getAllNamedModes()) {
932 932
                        if (m instanceof SimpleMode) {
933 933
                            TemplateRule ruleCopy = rule.copy();
......
935 935
                                ruleCopy.setDeclaredStreamable(true);
936 936
                            }
937 937
                            compiledTemplateRules.put(m.getModeName(), ruleCopy);
938
                            mgr.setTemplateRule(match1.copy(new RebindingMap()), ruleCopy, m, module, prio);
938
                            mgr.registerRule(match1.copy(new RebindingMap()),
939
                                             ruleCopy, m, module, prio, mgr.allocateSequenceNumber(), 0);
939 940
                        }
940 941
                    }
941 942
                }
latest9.9/hej/net/sf/saxon/trans/Mode.java
351 351
    public Rule getNextMatchRule(Item item, final Rule currentRule, XPathContext context) throws XPathException {
352 352
        SimpleMode.RuleFilter filter = r -> {
353 353
            int comp = r.compareRank(currentRule);
354
            return comp < 0 || (comp == 0 && r.getSequence() < currentRule.getSequence());
354
            if (comp < 0) {
355
                // the rule has lower precedence or priority than the current rule
356
                return true;
357
            } else if (comp == 0) {
358
                int seqComp = Integer.compare(r.getSequence(), currentRule.getSequence());
359
                if (seqComp < 0) {
360
                    // the rule is before the current rule in declaration order
361
                    return true;
362
                } else if (seqComp == 0) {
363
                    // we have two branches of the same union pattern; examine the parent pattern to see which is first
364
                    return r.getPartNumber() < currentRule.getPartNumber();
365
                }
366
            }
367
            return false;
355 368
        };
356 369
        return getRule(item, context, filter);
357 370
    }
latest9.9/hej/net/sf/saxon/trans/SimpleMode.java
284 284

  
285 285
    /**
286 286
     * Add a rule to the Mode.
287
     *
288 287
     * @param pattern      a Pattern
289 288
     * @param action       the Object to return from getRule() when the supplied node matches this Pattern
290 289
     * @param module       the stylesheet module containing the rule
291 290
     * @param precedence   the import precedence of the rule
292 291
     * @param priority     the priority of the rule
293
     * @param explicitMode true if adding a template rule for a specific (default or named) mode;
292
     * @param position     the relative position of the rule in declaration order. If two rules have the same
293
     *                     position in declaration order, this indicates that they were formed by splitting
294
     *                     a single rule whose pattern is a union pattern
295
     * @param part         the relative position of a rule within a family of rules created by splitting a
296
     *                     single rule governed by a union pattern. This is used where the splitting of the
297
     *                     rule was mandated by the XSLT specification, that is, where there is no explicit
298
     *                     priority specified. In cases where Saxon splits a rule for optimization reasons,
299
     *                     the subrules will all have the same subsequence number.
294 300
     */
295 301

  
296 302
    public void addRule(Pattern pattern, RuleTarget action,
297
                        StylesheetModule module, int precedence, double priority, boolean explicitMode) {
303
                        StylesheetModule module, int precedence, double priority, int position, int part) {
298 304

  
299
        if (explicitMode) {
300
            hasRules = true;
301
        }
305
        hasRules = true;
302 306

  
303 307
        // Ignore a pattern that will never match, e.g. "@comment"
304 308

  
......
312 316
        // Each list is sorted in precedence/priority order so we find the highest-priority rule first
313 317

  
314 318
        // This logic is designed to ensure that when a UnionPattern contains multiple branches
315
        // with the same priority, next-match doesn't select the same template twice (override20_047)
319
        // with the same priority, next-match doesn't select the same template twice (next-match-024)
316 320
        int moduleHash = module.hashCode();
317
        int sequence;
318
        if (mostRecentRule == null) {
319
            sequence = 0;
320
        } else if (action == mostRecentRule.getAction() && moduleHash == mostRecentModuleHash) {
321
            sequence = mostRecentRule.getSequence();
322
        } else {
323
            sequence = mostRecentRule.getSequence() + 1;
324
        }
321
//        int sequence;
322
//        if (mostRecentRule == null) {
323
//            sequence = 0;
324
//        } else if (action == mostRecentRule.getAction() && moduleHash == mostRecentModuleHash) {
325
//            sequence = mostRecentRule.getSequence();
326
//        } else {
327
//            sequence = mostRecentRule.getSequence() + 1;
328
//        }
325 329
        //int precedence = module.getPrecedence();
326 330
        int minImportPrecedence = module.getMinImportPrecedence();
327
        Rule newRule = makeRule(pattern, action, precedence, minImportPrecedence, priority, sequence);
331

  
332
        Rule newRule = makeRule(pattern, action, precedence, minImportPrecedence, priority, position, part);
333

  
328 334
        if (pattern instanceof NodeTestPattern) {
329 335
            ItemType test = pattern.getItemType();
330 336
            if (test instanceof AnyNodeTest) {
......
351 357
     * @param pattern             the pattern that this rule matches
352 358
     * @param action              the object invoked by this rule (usually a Template)
353 359
     * @param precedence          the precedence of the rule
354
     * @param minImportPrecedence the minumum import precedence for xsl:apply-imports
360
     * @param minImportPrecedence the minimum import precedence for xsl:apply-imports
355 361
     * @param priority            the priority of the rule
356 362
     * @param sequence            a sequence number for ordering of rules
363
     * @param part                distinguishes rules formed by splitting a rule on a union pattern
357 364
     * @return the newly created rule
358 365
     */
359 366
    public Rule makeRule(/*@NotNull*/ Pattern pattern, /*@NotNull*/ RuleTarget action,
360
                         int precedence, int minImportPrecedence, double priority, int sequence) {
361
        return new Rule(pattern, action, precedence, minImportPrecedence, priority, sequence);
367
                         int precedence, int minImportPrecedence, double priority, int sequence, int part) {
368
        return new Rule(pattern, action, precedence, minImportPrecedence, priority, sequence, part);
362 369
    }
363 370

  
364 371
    public void addRule(Pattern pattern, Rule newRule) {
......
628 635
                } else if (rank == 0) {
629 636
                    // this rule has the same precedence and priority as the matching rule already found
630 637
                    if (ruleMatches(head, item, (XPathContextMajor) context, ruleSearchState)) {
631
                        reportAmbiguity(item, bestRule, head, context);
638
                        if (head.getSequence() != bestRule.getSequence()) {
639
                            reportAmbiguity(item, bestRule, head, context);
640
                        }
632 641
                        // choose whichever one comes last (assuming the error wasn't fatal)
633
                        bestRule = bestRule.getSequence() > head.getSequence() ? bestRule : head;
642
                        int seqComp = Integer.compare(bestRule.getSequence(), head.getSequence());
643
                        if (seqComp > 0) {
644
                            return bestRule;
645
                        } else if (seqComp < 0) {
646
                            return head;
647
                        } else {
648
                            // we're dealing with two rules formed by partitioning a union pattern
649
                            bestRule = bestRule.getPartNumber() > head.getPartNumber() ? bestRule : head;
650
                        }
634 651
                        break;
635 652
                    } else {
636 653
                        // keep searching other rules of the same precedence and priority
latest9.9/hej/net/sf/saxon/trans/rules/Rule.java
28 28

  
29 29
    protected Rule next;              // The next rule after this one in the chain of rules
30 30
    protected int sequence;           // The relative position of this rule, its position in declaration order
31
    protected int part;               // The relative position of this rule relative to others formed by splitting
32
                                      // on a union pattern
31 33
    private boolean alwaysMatches;  // True if the pattern does not need to be tested, because the rule
32 34
    // is on a rule-chain such that the pattern is necessarily satisfied
33 35
    private int rank;               // Indicates the relative precedence/priority of a rule within a mode;
......
46 48
     * @param seq  a sequence number for ordering of rules
47 49
     */
48 50

  
49
    public Rule(/*@NotNull*/ Pattern p, /*@NotNull*/ RuleTarget o, int prec, int min, double prio, int seq) {
51
    public Rule(/*@NotNull*/ Pattern p, /*@NotNull*/ RuleTarget o, int prec, int min, double prio, int seq, int part) {
50 52
        pattern = p;
51 53
        action = o;
52 54
        precedence = prec;
......
54 56
        priority = prio;
55 57
        next = null;
56 58
        sequence = seq;
59
        this.part = part;
57 60
        o.registerRule(this);
58 61
    }
59 62

  
......
71 74
        minImportPrecedence = r.minImportPrecedence;
72 75
        priority = r.priority;
73 76
        sequence = r.sequence;
77
        part = r.part;
74 78
        if (r.next == null || !copyChain) {
75 79
            next = null;
76 80
        } else {
......
89 93
        return sequence;
90 94
    }
91 95

  
96
    public int getPartNumber() {
97
        return part;
98
    }
99

  
92 100
    public void setAction(/*@NotNull*/ RuleTarget action) {
93 101
        this.action = action;
94 102
    }
......
159 167
            out.emitAttribute("prec", getPrecedence() + "");
160 168
            out.emitAttribute("prio", getPriority() + "");
161 169
            out.emitAttribute("seq", getSequence() + "");
170
            if (part != 0) {
171
                out.emitAttribute("part", "" + part);
172
            }
162 173
            out.emitAttribute("rank", "" + getRank());
163 174
            out.emitAttribute("minImp", getMinImportPrecedence() + "");
164 175
            out.emitAttribute("slots", template.getStackFrameMap().getNumberOfVariables() + "");
latest9.9/hej/net/sf/saxon/trans/rules/RuleManager.java
39 39
    private int recoveryPolicy;
40 40
    private boolean unnamedModeExplicit;
41 41
    private CompilerInfo compilerInfo; // We may need access to information on the compilation as distinct from the configuration
42

  
42
    private int nextSequenceNumber = 0;
43 43

  
44 44
    /**
45 45
     * create a RuleManager and initialise variables.
......
184 184
        return omniMode != null;
185 185
    }
186 186

  
187
    public int allocateSequenceNumber() {
188
        return nextSequenceNumber++;
189
    }
190

  
187 191
    /**
188 192
     * Register a template for a particular pattern.
189 193
     *
......
192 196
     * @param mode     The processing mode to which this template applies
193 197
     * @param module   The stylesheet module containing the template rule
194 198
     * @param priority The priority of the rule: if an element matches several patterns, the
195
     *                 one with highest priority is used
199
     *                 one with highest priority is used. The value is NaN if no explicit
200
     *                 priority was specified
201
     * @param position The relative position of the rule in declaration order
202
     * @param part     Zero for a "real" rule; an incremented integer for rules generated
203
     *                 by splitting a real rule on a union pattern, in cases where no user-specified
204
     *                 priority is supplied.
205
     * @return the number of (sub-)rules registered
196 206
     * @see Pattern
197 207
     */
198 208

  
199
    public void setTemplateRule(Pattern pattern, TemplateRule eh,
200
                                Mode mode, StylesheetModule module, double priority) {
209
    public int registerRule(Pattern pattern, TemplateRule eh,
210
                            Mode mode, StylesheetModule module, double priority, int position, int part) {
201 211

  
202 212
        // for a union pattern, register the parts separately
203
        // Note: technically this is only necessary if using default priorities and if the priorities
213
        // Technically this is only necessary if using default priorities and if the priorities
204 214
        // of the two halves are different. However, splitting increases the chance that the pattern
205
        // can be matched by hashing on the element name, so we do it always
215
        // can be matched by hashing on the element name, so we do it always. But we need to do it
216
        // in such a way that next-match only processes the template once (test case next-match-024)
206 217
        if (pattern instanceof UnionPattern) {
207 218
            UnionPattern up = (UnionPattern) pattern;
208 219
            Pattern p1 = up.getLHS();
209 220
            Pattern p2 = up.getRHS();
210
            setTemplateRule(p1, eh, mode, module, priority);
211
            setTemplateRule(p2, eh, mode, module, priority);
212
            return;
221
            int lhsParts = registerRule(p1, eh, mode, module, priority, position, part);
222
            int rhsParts = registerRule(p2, eh, mode, module, priority, position, lhsParts);
223
            return lhsParts + rhsParts;
213 224
        }
214 225
        // some union patterns end up as a CombinedNodeTest. Need to split these.
215 226
        // (Same reasoning as above)
......
220 231
            NodeTest[] nt = cnt.getComponentNodeTests();
221 232
            final NodeTestPattern nt0 = new NodeTestPattern(nt[0]);
222 233
            ExpressionTool.copyLocationInfo(pattern, nt0);
223
            setTemplateRule(nt0, eh, mode, module, priority);
234
            int lhsParts = registerRule(nt0, eh, mode, module, priority, position, part);
224 235
            final NodeTestPattern nt1 = new NodeTestPattern(nt[1]);
225 236
            ExpressionTool.copyLocationInfo(pattern, nt1);
226
            setTemplateRule(nt1, eh, mode, module, priority);
227
            return;
237
            int rhsParts = registerRule(nt1, eh, mode, module, priority, position, lhsParts);
238
            return lhsParts + rhsParts;
228 239
        }
229 240
        if (Double.isNaN(priority)) {
230 241
            priority = pattern.getDefaultPriority();
242
        } else {
243
            // Priorities were user-allocated, so the rule-splitting is purely an optimization, so
244
            // we give all sub-rules the same part number, meaning that next-match will only
245
            // evaluate one of them
246
            part = 0;
231 247
        }
232 248

  
233 249
        if (mode instanceof SimpleMode) {
234
            ((SimpleMode) mode).addRule(pattern, eh, module, module.getPrecedence(), priority, true);
250
            ((SimpleMode) mode).addRule(pattern, eh, module, module.getPrecedence(), priority, position, part);
235 251
        } else {
236
            mode.getActivePart().addRule(pattern, eh, module, mode.getMaxPrecedence(), priority, true);
252
            mode.getActivePart().addRule(pattern, eh, module, mode.getMaxPrecedence(), priority, position, part);
237 253
        }
238

  
254
        return 1;
239 255
    }
240 256

  
241 257
    /**

Also available in: Unified diff