Project

Profile

Help

Revision 6d49c596

Added by Michael Kay 11 months ago

Fix bug #4636; and add support for separator on xsl:for-each and xsl:apply-templates

View differences:

latest10/hej/net/sf/saxon/expr/Operand.java
71 71

  
72 72
    public void setChildExpression(Expression childExpression) {
73 73
        if (childExpression != this.childExpression) {
74
            if (role.isConstrainedClass() && this.childExpression != null && childExpression.getClass() != this.childExpression.getClass()) {
75
                throw new AssertionError();
74
            if (role.isConstrainedClass()) {
75
                if (role.getConstraint() != null) {
76
                    if (!role.getConstraint().test(childExpression)) {
77
                        throw new AssertionError();
78
                    }
79
                } else if (this.childExpression != null && childExpression.getClass() != this.childExpression.getClass()) {
80
                    throw new AssertionError();
81
                }
76 82
            }
77 83
            this.childExpression = childExpression;
78 84
            parentExpression.adoptChildExpression(childExpression);
latest10/hej/net/sf/saxon/expr/OperandRole.java
8 8
package net.sf.saxon.expr;
9 9

  
10 10

  
11
import net.sf.saxon.expr.flwor.TupleExpression;
12
import net.sf.saxon.pattern.Pattern;
11 13
import net.sf.saxon.type.FunctionItemType;
12 14
import net.sf.saxon.type.ItemType;
13 15
import net.sf.saxon.type.PlainType;
14 16
import net.sf.saxon.value.SequenceType;
15 17

  
18
import java.util.function.Predicate;
19

  
16 20
/**
17 21
 * Defines the role of a child expression relative to its parent expression. The properties
18 22
 * of an operand role depend only on the kind of expression and not on the actual arguments
......
47 51
    public final static OperandRole REPEAT_NAVIGATE =
48 52
            new OperandRole(OperandRole.HIGHER_ORDER, OperandUsage.NAVIGATION, SequenceType.ANY_SEQUENCE);
49 53
    public final static OperandRole REPEAT_NAVIGATE_CONSTRAINED =
50
            new OperandRole(OperandRole.HIGHER_ORDER | OperandRole.CONSTRAINED_CLASS, OperandUsage.NAVIGATION, SequenceType.ANY_SEQUENCE);
54
            new OperandRole(OperandRole.HIGHER_ORDER | OperandRole.CONSTRAINED_CLASS,
55
                            OperandUsage.NAVIGATION,
56
                            SequenceType.ANY_SEQUENCE,
57
                            expr -> expr instanceof TupleExpression);
51 58
    public final static OperandRole SINGLE_ATOMIC =
52 59
            new OperandRole(0, OperandUsage.ABSORPTION,  SequenceType.SINGLE_ATOMIC);
53 60
    public final static OperandRole ATOMIC_SEQUENCE =
......
55 62
    public final static OperandRole NEW_FOCUS_ATOMIC =
56 63
            new OperandRole(OperandRole.USES_NEW_FOCUS | OperandRole.HIGHER_ORDER, OperandUsage.ABSORPTION, SequenceType.ATOMIC_SEQUENCE);
57 64
    public final static OperandRole PATTERN =
58
            new OperandRole(OperandRole.USES_NEW_FOCUS | OperandRole.HIGHER_ORDER | OperandRole.CONSTRAINED_CLASS, OperandUsage.ABSORPTION, SequenceType.ATOMIC_SEQUENCE);
65
            new OperandRole(OperandRole.USES_NEW_FOCUS | OperandRole.HIGHER_ORDER | OperandRole.CONSTRAINED_CLASS,
66
                            OperandUsage.ABSORPTION,
67
                            SequenceType.ATOMIC_SEQUENCE,
68
                            expr -> expr instanceof Pattern)
69
    ;
59 70

  
60 71
    int properties;
61 72
    private OperandUsage usage;
62 73
    private SequenceType requiredType = SequenceType.ANY_SEQUENCE;
74
    private Predicate<Expression> constraint;
63 75

  
64 76
    public OperandRole(int properties, OperandUsage usage) {
65 77
        this.properties = properties;
......
72 84
        this.requiredType = requiredType;
73 85
    }
74 86

  
87
    public OperandRole(int properties, OperandUsage usage, SequenceType requiredType, Predicate<Expression> constraint) {
88
        this.properties = properties;
89
        this.usage = usage;
90
        this.requiredType = requiredType;
91
        this.constraint = constraint;
92
    }
93

  
75 94
    /**
76 95
     * Ask whether the child expression sets the focus for evaluation of other child expressions
77 96
     * @return true if the child expression is evaluated with the same focus as its parent expression
......
122 141

  
123 142
     public boolean isConstrainedClass() { return (properties & CONSTRAINED_CLASS) != 0; }
124 143

  
144
    /**
145
     * Set a constraint on the expression that can be associated with this operand type
146
     */
147

  
148
    public void setConstraint(Predicate<Expression> constraint) {
149
        this.constraint = constraint;
150
    }
151

  
152
    /**
153
     * Get any constraint on the expression that can be associated with this operand type
154
     * @return any constraint that has been registered, or null
155
     */
156

  
157
    public Predicate<Expression> getConstraint() {
158
        return constraint;
159
    }
160

  
125 161
    /**
126 162
     * Get the usage of the operand
127 163
     * @return the usage
latest10/hej/net/sf/saxon/expr/instruct/ApplyTemplates.java
17 17
import net.sf.saxon.trans.*;
18 18
import net.sf.saxon.trans.rules.RuleManager;
19 19
import net.sf.saxon.tree.iter.EmptyIterator;
20
import net.sf.saxon.tree.util.Orphan;
21
import net.sf.saxon.type.Type;
22
import org.jetbrains.annotations.NotNull;
20 23

  
21 24
import java.util.ArrayList;
22 25
import java.util.List;
......
29 32
public class ApplyTemplates extends Instruction implements ITemplateCall, ComponentInvocation {
30 33

  
31 34
    private Operand selectOp;
35
    private Operand separatorOp;
32 36
    private WithParam[] actualParams;
33 37
    private WithParam[] tunnelParams;
34 38

  
......
90 94
        this.mode = target;
91 95
    }
92 96

  
97
    /**
98
     * Set the separator expression (Saxon extension)
99
     */
100

  
101
    public void setSeparatorExpression(Expression separator) {
102
        separatorOp = new Operand(this, separator, OperandRole.SINGLE_ATOMIC);
103
    }
104

  
105
    public Expression getSeparatorExpression() {
106
        return separatorOp == null ? null : separatorOp.getChildExpression();
107
    }
108

  
93 109
    /**
94 110
     * Get the actual parameters passed to the called template
95 111
     *
......
124 140
    public Iterable<Operand> operands() {
125 141
        List<Operand> operanda = new ArrayList<>();
126 142
        operanda.add(selectOp);
143
        if (separatorOp != null) {
144
            operanda.add(separatorOp);
145
        }
127 146
        WithParam.gatherOperands(this, getActualParams(), operanda);
128 147
        WithParam.gatherOperands(this, getTunnelParams(), operanda);
129 148
        return operanda;
......
236 255
        a2.setTunnelParams(WithParam.copy(a2, getTunnelParams(), rebindings));
237 256
        ExpressionTool.copyLocationInfo(this, a2);
238 257
        a2.ruleManager = ruleManager;
258
        if (separatorOp != null) {
259
            a2.setSeparatorExpression(getSeparatorExpression().copy(rebindings));
260
        }
239 261
        return a2;
240 262
    }
241 263

  
......
257 279
        return apply(output, context, useTailRecursion);
258 280
    }
259 281

  
282

  
283
    @NotNull
284
    protected NodeInfo makeSeparator(XPathContext context) throws XPathException {
285
        NodeInfo separator;
286
        CharSequence sepValue = separatorOp.getChildExpression().evaluateAsString(context);
287
        Orphan orphan = new Orphan(context.getConfiguration());
288
        orphan.setNodeKind(Type.TEXT);
289
        orphan.setStringValue(sepValue);
290
        separator = orphan;
291
        return separator;
292
    }
293

  
260 294
    protected TailCall apply(Outputter output, XPathContext context, boolean returnTailCall) throws XPathException {
261 295

  
262 296
        Component.M targetMode = getTargetMode(context);
263 297
        Mode thisMode = targetMode.getActor();
264 298

  
299
        NodeInfo separator = null;
300
        if (separatorOp != null) {
301
            separator = makeSeparator(context);
302
        }
303

  
265 304
        // handle parameters if any
266 305

  
267 306
        ParameterSet params = assembleParams(context, getActualParams());
......
272 311
            c2.setOrigin(this);
273 312
            return new ApplyTemplatesPackage(
274 313
                    ExpressionTool.lazyEvaluate(getSelect(), context, false),
275
                    targetMode, params, tunnels, output, c2, getLocation());
314
                    targetMode, params, tunnels, separator, output, c2, getLocation());
276 315
        }
277 316

  
278 317
        // Get an iterator to iterate through the selected nodes in original order
......
299 338
        pipe.setXPathContext(c2);
300 339

  
301 340
        try {
302
            TailCall tc = thisMode.applyTemplates(params, tunnels, output, c2, getLocation());
341
            TailCall tc = thisMode.applyTemplates(params, tunnels, separator, output, c2, getLocation());
303 342
            while (tc != null) {
304 343
                tc = tc.processLeavingTail();
305 344
            }
......
467 506
        out.emitAttribute("bSlot", "" + getBindingSlot());
468 507
        out.setChildRole("select");
469 508
        getSelect().export(out);
509
        if (separatorOp != null) {
510
            out.setChildRole("separator");
511
            getSeparatorExpression().export(out);
512
        }
470 513
        if (getActualParams().length != 0) {
471 514
            WithParam.exportParameters(getActualParams(), out, false);
472 515
        }
......
535 578
        private Component.M targetMode;
536 579
        private ParameterSet params;
537 580
        private ParameterSet tunnelParams;
581
        private NodeInfo separator;
538 582
        private XPathContextMajor evaluationContext;
539 583
        private Outputter output;
540 584
        private Location locationId;
......
543 587
                              Component.M targetMode,
544 588
                              ParameterSet params,
545 589
                              ParameterSet tunnelParams,
590
                              NodeInfo separator,
546 591
                              Outputter output,
547 592
                              XPathContextMajor context,
548 593
                              Location locationId) {
......
550 595
            this.targetMode = targetMode;
551 596
            this.params = params;
552 597
            this.tunnelParams = tunnelParams;
598
            this.separator = separator;
553 599
            this.output = output;
554 600
            evaluationContext = context;
555 601
            this.locationId = locationId;
......
559 605
            evaluationContext.trackFocus(selectedItems.iterate());
560 606
            evaluationContext.setCurrentMode(targetMode);
561 607
            evaluationContext.setCurrentComponent(targetMode);
562
            return targetMode.getActor().applyTemplates(params, tunnelParams, output, evaluationContext, locationId);
608
            return targetMode.getActor().applyTemplates(params, tunnelParams, separator, output, evaluationContext, locationId);
563 609
        }
564 610
    }
565 611

  
latest10/hej/net/sf/saxon/expr/instruct/ForEach.java
13 13
import net.sf.saxon.expr.*;
14 14
import net.sf.saxon.expr.parser.*;
15 15
import net.sf.saxon.lib.TraceListener;
16
import net.sf.saxon.om.FocusIterator;
17
import net.sf.saxon.om.Item;
18
import net.sf.saxon.om.SequenceIterator;
19
import net.sf.saxon.om.StandardNames;
16
import net.sf.saxon.om.*;
20 17
import net.sf.saxon.trace.ExpressionPresenter;
21 18
import net.sf.saxon.trans.XPathException;
22
import net.sf.saxon.type.ErrorType;
23
import net.sf.saxon.type.ItemType;
24
import net.sf.saxon.type.SchemaType;
25
import net.sf.saxon.type.UType;
19
import net.sf.saxon.tree.iter.PrependSequenceIterator;
20
import net.sf.saxon.tree.util.Orphan;
21
import net.sf.saxon.type.*;
26 22
import net.sf.saxon.value.Cardinality;
23
import org.jetbrains.annotations.NotNull;
27 24

  
28 25

  
29 26
/**
......
37 34
    protected boolean containsTailCall;
38 35
    protected Operand selectOp;
39 36
    protected Operand actionOp;
37
    protected Operand separatorOp;
40 38
    protected Operand threadsOp;
41 39
    protected boolean isInstruction;
42 40

  
......
69 67
        this.containsTailCall = containsTailCall && action instanceof TailCallReturner;
70 68
    }
71 69

  
70
    /**
71
     * Set the separator expression (Saxon extension)
72
     */
73

  
74
    public void setSeparatorExpression(Expression separator) {
75
        separatorOp = new Operand(this, separator, OperandRole.SINGLE_ATOMIC);
76
    }
77

  
78
    public Expression getSeparatorExpression() {
79
        return separatorOp == null ? null : separatorOp.getChildExpression();
80
    }
81

  
72 82
    /**
73 83
     * Say whether this ForEach expression originates as an XSLT instruction
74 84
     *
......
161 171

  
162 172
    @Override
163 173
    public Iterable<Operand> operands() {
164
        if (threadsOp == null) {
165
            return operandList(selectOp, actionOp);
166
        } else {
167
            return operandList(selectOp, actionOp, threadsOp);
168
        }
174
        return operandSparseList(selectOp, actionOp, separatorOp, threadsOp);
169 175
    }
170 176

  
171 177
    /**
......
367 373
    /*@NotNull*/
368 374
    public Expression copy(RebindingMap rebindings) {
369 375
        ForEach f2 = new ForEach(getSelect().copy(rebindings), getAction().copy(rebindings), containsTailCall, getThreads());
376
        if (separatorOp != null) {
377
            f2.setSeparatorExpression(getSeparatorExpression().copy(rebindings));
378
        }
370 379
        ExpressionTool.copyLocationInfo(this, f2);
371 380
        f2.setInstruction(isInstruction());
372 381
        return f2;
......
476 485
        } else {
477 486
            PipelineConfiguration pipe = output.getPipelineConfiguration();
478 487
            pipe.setXPathContext(c2);
479
            if (controller.isTracing()) {
488
            NodeInfo separator = null;
489
            if (separatorOp != null) {
490
                separator = makeSeparator(context);
491
            }
492
            if (controller.isTracing() || separator != null) {
480 493
                TraceListener listener = controller.getTraceListener();
481
                assert listener != null;
482 494
                Item item;
495
                boolean first = true;
483 496
                while ((item = iter.next()) != null) {
484
                    listener.startCurrentItem(item);
497
                    if (controller.isTracing()) {
498
                        assert listener != null;
499
                        listener.startCurrentItem(item);
500
                    }
501
                    if (separator != null) {
502
                        if (first) {
503
                            first = false;
504
                        } else {
505
                            output.append(separator);
506
                        }
507
                    }
485 508
                    action.process(output, c2);
486
                    listener.endCurrentItem(item);
509
                    if (controller.isTracing()) {
510
                        listener.endCurrentItem(item);
511
                    }
487 512
                }
488 513
            } else {
489 514
                iter.forEachOrFail(item -> action.process(output, c2));
......
493 518
        return null;
494 519
    }
495 520

  
521
    @NotNull
522
    protected NodeInfo makeSeparator(XPathContext context) throws XPathException {
523
        NodeInfo separator;
524
        CharSequence sepValue = separatorOp.getChildExpression().evaluateAsString(context);
525
        Orphan orphan = new Orphan(context.getConfiguration());
526
        orphan.setNodeKind(Type.TEXT);
527
        orphan.setStringValue(sepValue);
528
        separator = orphan;
529
        return separator;
530
    }
531

  
496 532
    /**
497 533
     * Return an Iterator to iterate over the values of the sequence.
498 534
     *
......
507 543
    public SequenceIterator iterate(XPathContext context) throws XPathException {
508 544
        XPathContextMinor c2 = context.newMinorContext();
509 545
        c2.trackFocus(getSelect().iterate(context));
510
        return new ContextMappingIterator(this, c2);
546
        if (separatorOp == null) {
547
            return new ContextMappingIterator(this, c2);
548
        } else {
549
            NodeInfo separator = makeSeparator(context);
550
            ContextMappingFunction mapper = cxt -> {
551
               if (cxt.getCurrentIterator().position() == 1) {
552
                   return ForEach.this.map(cxt);
553
               } else {
554
                   return new PrependSequenceIterator(separator, ForEach.this.map(cxt));
555
               }
556
            };
557
            return new ContextMappingIterator(mapper, c2);
558
        }
511 559
    }
512 560

  
513 561
    /**
......
553 601
        out.startElement("forEach", this);
554 602
        getSelect().export(out);
555 603
        getAction().export(out);
604
        if (separatorOp != null) {
605
            out.setChildRole("separator");
606
            separatorOp.getChildExpression().export(out);
607
        }
556 608
        explainThreads(out);
557 609
        out.endElement();
558 610
    }
latest10/hej/net/sf/saxon/expr/instruct/IterateInstr.java
50 50
        selectOp = new Operand(this, select, OperandRole.FOCUS_CONTROLLING_SELECT);
51 51
        actionOp = new Operand(this, action, OperandRole.FOCUS_CONTROLLED_ACTION);
52 52
        initiallyOp = new Operand(this, initiallyExp,
53
                                  new OperandRole(OperandRole.CONSTRAINED_CLASS, OperandUsage.NAVIGATION, SequenceType.ANY_SEQUENCE));
53
                                  new OperandRole(OperandRole.CONSTRAINED_CLASS,
54
                                                  OperandUsage.NAVIGATION,
55
                                                  SequenceType.ANY_SEQUENCE,
56
                                                  expr -> expr instanceof LocalParamBlock));
54 57
        onCompletionOp = new Operand(this, onCompletion,
55 58
                                     new OperandRole(OperandRole.USES_NEW_FOCUS, OperandUsage.TRANSMISSION));
56 59
    }
latest10/hej/net/sf/saxon/expr/sort/ConditionalSorter.java
39 39
    }
40 40

  
41 41
    private final static OperandRole DOC_SORTER_ROLE =
42
            new OperandRole(OperandRole.CONSTRAINED_CLASS, OperandUsage.TRANSMISSION, SequenceType.ANY_SEQUENCE);
42
            new OperandRole(OperandRole.CONSTRAINED_CLASS,
43
                            OperandUsage.TRANSMISSION,
44
                            SequenceType.ANY_SEQUENCE,
45
                            expr -> expr instanceof DocumentSorter);
43 46

  
44 47
    /**
45 48
     * Create a conditional document sorter
latest10/hej/net/sf/saxon/functions/Doc_2.java
14 14
import net.sf.saxon.expr.*;
15 15
import net.sf.saxon.expr.accum.Accumulator;
16 16
import net.sf.saxon.expr.accum.AccumulatorRegistry;
17
import net.sf.saxon.expr.parser.RetainedStaticContext;
17 18
import net.sf.saxon.lib.ParseOptions;
18 19
import net.sf.saxon.lib.Validation;
19 20
import net.sf.saxon.ma.map.MapItem;
......
51 52
    public static OptionsParameter makeOptionsParameter() {
52 53
        SequenceType listOfQNames = SequenceType.makeSequenceType(BuiltInAtomicType.QNAME, StaticProperty.ALLOWS_ZERO_OR_MORE);
53 54
        OptionsParameter op = new OptionsParameter();
55
        op.addAllowedOption("base-uri", SequenceType.SINGLE_STRING);
54 56
        op.addAllowedOption("validation", SequenceType.SINGLE_STRING);
55
        op.setAllowedValues("validation", "SXZZ0001", "strict", "lax", "preserve", "skip");
57
        op.setAllowedValues("validation", "SXZZ0001", "strict", "lax", "preserve", "strip", "skip");
56 58
        op.addAllowedOption("type", SequenceType.SINGLE_QNAME);
57 59
        op.addAllowedOption("strip-space", SequenceType.SINGLE_STRING);
58
        op.setAllowedValues("strip-space", "SXZZ0001", "none", "all", "package-defined");
60
        op.setAllowedValues("strip-space", "SXZZ0001", "none", "all", "ignorable", "package-defined", "default");
59 61
        op.addAllowedOption("stable", SequenceType.SINGLE_BOOLEAN);
60 62
        op.addAllowedOption("dtd-validation", SequenceType.SINGLE_BOOLEAN);
61 63
        op.addAllowedOption("accumulators", listOfQNames);
......
64 66
    }
65 67

  
66 68

  
67
    private ParseOptions setParseOptions(
68
            Map<String, Sequence> checkedOptions, XPathContext context) throws XPathException {
69
    public static ParseOptions setParseOptions(
70
            RetainedStaticContext rsc, Map<String, Sequence> checkedOptions, XPathContext context) throws XPathException {
69 71
        ParseOptions result = new ParseOptions(context.getConfiguration().getParseOptions());
70 72

  
71 73
        Sequence value = checkedOptions.get("validation");
......
96 98
                case "none":
97 99
                    result.setSpaceStrippingRule(NoElementsSpaceStrippingRule.getInstance());
98 100
                    break;
101
                case "ignorable":
102
                    result.setSpaceStrippingRule(IgnorableSpaceStrippingRule.getInstance());
103
                    break;
99 104
                case "package-defined":
100
                    PackageData data = getRetainedStaticContext().getPackageData();
105
                case "default":
106
                    PackageData data = rsc.getPackageData();
101 107
                    if (data instanceof StylesheetPackage) {
102 108
                        result.setSpaceStrippingRule(((StylesheetPackage) data).getSpaceStrippingRule());
103 109
                    }
......
110 116
        }
111 117
        value = checkedOptions.get("accumulators");
112 118
        if (value != null) {
113
            AccumulatorRegistry reg = getRetainedStaticContext().getPackageData().getAccumulatorRegistry();
119
            AccumulatorRegistry reg = rsc.getPackageData().getAccumulatorRegistry();
114 120
            Set<Accumulator> accumulators = new HashSet<>();
115 121
            SequenceIterator iter = value.iterate();
116 122

  
......
138 144
     * @return the result of the evaluation, in the form of a SequenceIterator
139 145
     * @throws XPathException if a dynamic error occurs during the evaluation of the expression
140 146
     */
141
    public ZeroOrOne call(XPathContext context, Sequence[] arguments) throws XPathException {
147
    public ZeroOrOne<NodeInfo> call(XPathContext context, Sequence[] arguments) throws XPathException {
142 148
        AtomicValue hrefVal = (AtomicValue) arguments[0].head();
143 149
        if (hrefVal == null) {
144 150
            return ZeroOrOne.empty();
......
147 153
        Item param = arguments[1].head();
148 154
        Map<String, Sequence> checkedOptions =
149 155
                getDetails().optionDetails.processSuppliedOptions((MapItem) param, context);
150
        ParseOptions parseOptions = setParseOptions(checkedOptions, context);
156
        ParseOptions parseOptions = setParseOptions(getRetainedStaticContext(), checkedOptions, context);
151 157

  
152 158
        NodeInfo item = fetch(href, parseOptions, context).getRootNode();
153 159
        if (item == null) {
......
155 161
            throw new XPathException("Failed to load document " + href, "FODC0002", context);
156 162
        }
157 163
        Controller controller = context.getController();
158
        if (parseOptions != null && controller instanceof XsltController) {
164
        if (controller instanceof XsltController) {
159 165
            ((XsltController) controller).getAccumulatorManager().setApplicableAccumulators(
160 166
                    item.getTreeInfo(), parseOptions.getApplicableAccumulators()
161 167
            );
162 168
        }
163
        return new ZeroOrOne(item);
169
        return new ZeroOrOne<>(item);
164 170
    }
165 171

  
166 172
    private TreeInfo fetch(String href, ParseOptions options, XPathContext context) throws XPathException {
......
184 190
            if (b instanceof TinyBuilder) {
185 191
                ((TinyBuilder) b).setStatistics(config.getTreeStatistics().SOURCE_DOCUMENT_STATISTICS);
186 192
            }
187

  
188 193
            b.setPipelineConfiguration(b.getPipelineConfiguration());
189 194
            try {
190 195
                Sender.send(source, b, options);
latest10/hej/net/sf/saxon/functions/ParseXml.java
66 66
            Source source = new SAXSource(is);
67 67
            source.setSystemId(baseURI);
68 68

  
69
            Builder b = TreeModel.TINY_TREE.makeBuilder(controller.makePipelineConfiguration());
69
            Builder b = controller.makeBuilder();
70 70
            Receiver s = b;
71 71
            ParseOptions options = new ParseOptions(config.getParseOptions());
72 72
            PackageData pd = getRetainedStaticContext().getPackageData();
latest10/hej/net/sf/saxon/functions/registry/VendorFunctionSetHE.java
39 39
 */
40 40
public class VendorFunctionSetHE extends BuiltInFunctionSet {
41 41

  
42
    private static VendorFunctionSetHE THE_INSTANCE = new VendorFunctionSetHE();
42
    private final static VendorFunctionSetHE THE_INSTANCE = new VendorFunctionSetHE();
43 43

  
44 44
    public static VendorFunctionSetHE getInstance() {
45 45
        return THE_INSTANCE;
latest10/hej/net/sf/saxon/style/XSLApplyTemplates.java
33 33
public class XSLApplyTemplates extends StyleElement {
34 34

  
35 35
    /*@Nullable*/ private Expression select;
36
    private Expression separator;
36 37
    private StructuredQName modeName;   // null if no name specified or if conventional values such as #current used
37 38
    private boolean useCurrentMode = false;
38 39
    private boolean useTailRecursion = false;
......
68 69
                    select = makeExpression(selectAtt, att);
69 70
                    defaultedSelectExpression = false;
70 71
                    break;
72
                case "separator":
73
                    requireSyntaxExtensions("separator");
74
                    separator = makeAttributeValueTemplate(value, att);
75
                    break;
71 76
                default:
72 77
                    checkUnknownAttribute(attName);
73 78
                    break;
......
158 163

  
159 164
        select = typeCheck("select", select);
160 165

  
166
        if (separator != null) {
167
            separator = typeCheck("separator", separator);
168
        }
169

  
161 170
    }
162 171

  
163 172
    /**
......
193 202
                rm);
194 203
        app.setActualParams(getWithParamInstructions(app, compilation, decl, false));
195 204
        app.setTunnelParams(getWithParamInstructions(app, compilation, decl, true));
205
        if (separator != null) {
206
            app.setSeparatorExpression(separator);
207
        }
196 208
        return app;
197 209
    }
198 210

  
latest10/hej/net/sf/saxon/style/XSLForEach.java
31 31
    /*@Nullable*/ private Expression select = null;
32 32
    private boolean containsTailCall = false;
33 33
    private Expression threads = null;
34
    private Expression separator = null;
34 35

  
35 36
    /**
36 37
     * Determine whether this node is an instruction.
......
82 83
            if (f.equals("select")) {
83 84
                selectAtt = value;
84 85
                select = makeExpression(selectAtt, att);
86
            } else if (f.equals("separator")) {
87
                requireSyntaxExtensions("separator");
88
                separator = makeAttributeValueTemplate(value, att);
85 89
            } else if (attName.getLocalPart().equals("threads") && attName.hasURI(NamespaceConstant.SAXON)) {
86 90
                String threadsAtt = Whitespace.trim(value);
87 91
                threads = makeAttributeValueTemplate(threadsAtt, att);
......
109 113
    public void validate(ComponentDeclaration decl) throws XPathException {
110 114
        checkSortComesFirst(false);
111 115
        select = typeCheck("select", select);
116
        if (separator != null) {
117
            separator = typeCheck("separator", separator);
118
        }
119
        if (threads != null) {
120
            threads = typeCheck("threads", threads);
121
        }
112 122
        if (!hasChildNodes()) {
113 123
            compileWarning("An empty xsl:for-each instruction has no effect", SaxonErrorCode.SXWN9009);
114 124
        }
......
130 140
            ForEach result = new ForEach(sortedSequence, block.simplify(), containsTailCall, threads);
131 141
            result.setInstruction(true);
132 142
            result.setLocation(allocateLocation());
143
            if (separator != null) {
144
                result.setSeparatorExpression(separator);
145
            }
133 146
            return result;
134 147
        } catch (XPathException err) {
135 148
            compileError(err);
latest10/hej/net/sf/saxon/testdriver/Environment.java
386 386
            if (defaultAtt != null && (defaultAtt.trim().equals("true") || defaultAtt.trim().equals("1"))) {
387 387
                environment.xpathCompiler.declareDefaultCollation(uri);
388 388
                environment.xqueryCompiler.declareDefaultCollation(uri);
389
                environment.xsltCompiler.declareDefaultCollation(uri);
389
                if (environment.xsltCompiler != null) {
390
                    environment.xsltCompiler.declareDefaultCollation(uri);
391
                }
390 392
            }
391 393
        }
392 394
    }
latest10/hej/net/sf/saxon/trans/Mode.java
420 420
     * @param tunnelParameters A ParameterSet containing the parameters to
421 421
     *                         the handler/template being invoked. Specify null if there are no
422 422
     *                         parameters.
423
     * @param separator        Text node to be inserted between the output of successive input items;
424
     *                         may be null
423 425
     * @param output           The destination for the result of the selected templates
424 426
     * @param context          A newly-created context object (this must be freshly created by the caller,
425 427
     *                         as it will be modified by this method). The nodes to be processed are those
......
435 437
    public TailCall applyTemplates(
436 438
            ParameterSet parameters,
437 439
            ParameterSet tunnelParameters,
440
            NodeInfo separator,
438 441
            Outputter output,
439 442
            XPathContextMajor context,
440 443
            Location locationId)
......
457 460

  
458 461
        boolean lookahead = iterator.getProperties().contains(SequenceIterator.Property.LOOKAHEAD);
459 462
        TemplateRule previousTemplate = null;
463
        boolean first = true;
464

  
460 465
        while (true) {
461 466

  
462 467
            // process any tail calls returned from previous nodes. We need to do this before changing
......
479 484
                break;
480 485
            }
481 486

  
487
            if (separator != null) {
488
                if (first) {
489
                    first = false;
490
                } else {
491
                    output.append(separator);
492
                }
493
            }
494

  
482 495
            if (mustBeTyped) {
483 496
                if (item instanceof NodeInfo) {
484 497
                    int kind = ((NodeInfo) item).getNodeKind();
latest10/hej/net/sf/saxon/trans/PackageLoaderHE.java
1565 1565

  
1566 1566
            ApplyTemplates inst = new ApplyTemplates(
1567 1567
                    select, useCurrentMode, useTailRecursion, implicitSelect, inStreamableConstruct, mode, loader.packStack.peek().getRuleManager());
1568
            Expression sep = loader.getExpressionWithRole(element, "separator");
1569
            if (sep != null) {
1570
                inst.setSeparatorExpression(sep);
1571
            }
1568 1572
            WithParam[] actuals = loader.loadWithParams(element, inst, false);
1569 1573
            WithParam[] tunnels = loader.loadWithParams(element, inst, true);
1570 1574
            inst.setActualParams(actuals);
......
2350 2354
            if (threads == null) {
2351 2355
                return new ForEach(lhs, rhs);
2352 2356
            } else {
2353
                Expression forEach = new ForEach(lhs, rhs, false, threads);
2357
                ForEach forEach = new ForEach(lhs, rhs, false, threads);
2358
                Expression sep = loader.getExpressionWithRole(element, "separator");
2359
                if (sep != null) {
2360
                    forEach.setSeparatorExpression(sep);
2361
                } 
2354 2362
                return loader.getConfiguration().obtainOptimizer().generateMultithreadedInstruction(forEach);
2355 2363
            }
2356 2364
        });
latest10/hej/net/sf/saxon/trans/XsltController.java
656 656
            initialContext.setCurrentMode(initialMode);
657 657
            initialContext.setCurrentComponent(initialMode);
658 658

  
659
            TailCall tc = mode.applyTemplates(ordinaryParams, tunnelParams, dest, initialContext, Loc.NONE);
659
            TailCall tc = mode.applyTemplates(ordinaryParams, tunnelParams, null, dest, initialContext, Loc.NONE);
660 660
            while (tc != null) {
661 661
                tc = tc.processLeavingTail();
662 662
            }
latest10/hej/net/sf/saxon/trans/rules/DeepSkipRuleSet.java
56 56
            c2.setOrigin(this);
57 57
            c2.trackFocus(((NodeInfo) item).iterateAxis(AxisInfo.CHILD));
58 58
            c2.setCurrentComponent(c2.getCurrentMode());
59
            TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, output, c2, locationId);
59
            TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, output, c2, locationId);
60 60
            while (tc != null) {
61 61
                tc = tc.processLeavingTail();
62 62
            }
latest10/hej/net/sf/saxon/trans/rules/ShallowCopyRuleSet.java
69 69
                    c2.trackFocus(node.iterateAxis(AxisInfo.CHILD));
70 70
                    c2.setCurrentComponent(c2.getCurrentMode());  // Bug 3508
71 71
                    pipe.setXPathContext(c2);
72
                    TailCall tc = context.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, out, c2, locationId);
72
                    TailCall tc = context.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, out, c2, locationId);
73 73
                    while (tc != null) {
74 74
                        tc = tc.processLeavingTail();
75 75
                    }
......
96 96
                    if (attributes != EmptyIterator.ofNodes()) {
97 97
                        c2.setOrigin(this);
98 98
                        c2.trackFocus(attributes);
99
                        TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, out, c2, locationId);
99
                        TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, out, c2, locationId);
100 100
                        while (tc != null) {
101 101
                            tc = tc.processLeavingTail();
102 102
                        }
......
105 105
                    // apply-templates to all children
106 106
                    if (node.hasChildNodes()) {
107 107
                        c2.trackFocus(node.iterateAxis(AxisInfo.CHILD));
108
                        TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, out, c2, locationId);
108
                        TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, out, c2, locationId);
109 109
                        while (tc != null) {
110 110
                            tc = tc.processLeavingTail();
111 111
                        }
latest10/hej/net/sf/saxon/trans/rules/ShallowSkipRuleSet.java
64 64
                    c2.setOrigin(this);
65 65
                    c2.trackFocus(node.iterateAxis(AxisInfo.ATTRIBUTE));
66 66
                    c2.setCurrentComponent(c2.getCurrentMode());  // Bug 3508
67
                    TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, output, c2, locationId);
67
                    TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, output, c2, locationId);
68 68
                    while (tc != null) {
69 69
                        tc = tc.processLeavingTail();
70 70
                    }
......
74 74
                    c2.setOrigin(this);
75 75
                    c2.trackFocus(node.iterateAxis(AxisInfo.CHILD));
76 76
                    c2.setCurrentComponent(c2.getCurrentMode());  // Bug 3508
77
                    TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, output, c2, locationId);
77
                    TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, output, c2, locationId);
78 78
                    while (tc != null) {
79 79
                        tc = tc.processLeavingTail();
80 80
                    }
......
94 94
            c2.setOrigin(this);
95 95
            c2.trackFocus(members);
96 96
            c2.setCurrentComponent(c2.getCurrentMode());  // Bug 3508
97
            TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, output, c2, locationId);
97
            TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, output, c2, locationId);
98 98
            while (tc != null) {
99 99
                tc = tc.processLeavingTail();
100 100
            }
latest10/hej/net/sf/saxon/trans/rules/TextOnlyCopyRuleSet.java
68 68
                    c2.setOrigin(this);
69 69
                    c2.trackFocus(node.iterateAxis(AxisInfo.CHILD));
70 70
                    c2.setCurrentComponent(c2.getCurrentMode());  // Bug 3508
71
                    TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, output, c2, locationId);
71
                    TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, output, c2, locationId);
72 72
                    while (tc != null) {
73 73
                        tc = tc.processLeavingTail();
74 74
                    }
......
91 91
            c2.setOrigin(this);
92 92
            c2.trackFocus(members);
93 93
            c2.setCurrentComponent(c2.getCurrentMode());  // Bug 3508
94
            TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, output, c2, locationId);
94
            TailCall tc = c2.getCurrentMode().getActor().applyTemplates(parameters, tunnelParams, null, output, c2, locationId);
95 95
            while (tc != null) {
96 96
                tc = tc.processLeavingTail();
97 97
            }

Also available in: Unified diff