OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [xml/] [transform/] [Stylesheet.java] - Blame information for rev 858

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* Stylesheet.java --
2
   Copyright (C) 2004,2006 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
package gnu.xml.transform;
39
 
40
import gnu.java.lang.CPStringBuilder;
41
 
42
import java.text.DecimalFormat;
43
import java.text.DecimalFormatSymbols;
44
import java.util.ArrayList;
45
import java.util.Collection;
46
import java.util.Collections;
47
import java.util.HashSet;
48
import java.util.Iterator;
49
import java.util.LinkedHashMap;
50
import java.util.LinkedHashSet;
51
import java.util.LinkedList;
52
import java.util.List;
53
import java.util.Map;
54
import java.util.Set;
55
import java.util.StringTokenizer;
56
import javax.xml.XMLConstants;
57
import javax.xml.namespace.NamespaceContext;
58
import javax.xml.namespace.QName;
59
import javax.xml.transform.Source;
60
import javax.xml.transform.TransformerConfigurationException;
61
import javax.xml.transform.TransformerException;
62
import javax.xml.xpath.XPathFunction;
63
import javax.xml.xpath.XPathFunctionResolver;
64
import javax.xml.xpath.XPathExpressionException;
65
import org.w3c.dom.Attr;
66
import org.w3c.dom.Document;
67
import org.w3c.dom.DOMException;
68
import org.w3c.dom.Element;
69
import org.w3c.dom.NamedNodeMap;
70
import org.w3c.dom.Node;
71
import org.w3c.dom.Text;
72
import org.w3c.dom.UserDataHandler;
73
import gnu.xml.xpath.Expr;
74
import gnu.xml.xpath.NameTest;
75
import gnu.xml.xpath.NodeTypeTest;
76
import gnu.xml.xpath.Pattern;
77
import gnu.xml.xpath.Selector;
78
import gnu.xml.xpath.Root;
79
import gnu.xml.xpath.Test;
80
import gnu.xml.xpath.XPathImpl;
81
 
82
/**
83
 * An XSL stylesheet.
84
 *
85
 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
86
 */
87
class Stylesheet
88
  implements NamespaceContext, XPathFunctionResolver, UserDataHandler, Cloneable
89
{
90
 
91
  static final String XSL_NS = "http://www.w3.org/1999/XSL/Transform";
92
  private static final NameTest STYLESHEET_PRESERVE_TEXT =
93
    new NameTest(new QName(XSL_NS, "text"), false, false);
94
 
95
  static final int OUTPUT_XML = 0;
96
  static final int OUTPUT_HTML = 1;
97
  static final int OUTPUT_TEXT = 2;
98
 
99
  final TransformerFactoryImpl factory;
100
  TransformerImpl transformer;
101
  Stylesheet parent;
102
  final XPathImpl xpath;
103
  final String systemId;
104
  final int precedence;
105
 
106
  final boolean debug;
107
 
108
  /**
109
   * Version of XSLT.
110
   */
111
  String version;
112
 
113
  Collection<String> extensionElementPrefixes;
114
  Collection<String> excludeResultPrefixes;
115
 
116
  /**
117
   * Set of element names for which we should strip whitespace.
118
   */
119
  Set<StrippingInstruction> stripSpace;
120
 
121
  /**
122
   * Set of element names for which we should preserve whitespace.
123
   */
124
  Set<StrippingInstruction> preserveSpace;
125
 
126
  /**
127
   * Output options.
128
   */
129
  Node output;
130
  int outputMethod;
131
  String outputVersion;
132
  String outputEncoding;
133
  boolean outputOmitXmlDeclaration;
134
  boolean outputStandalone;
135
  String outputPublicId;
136
  String outputSystemId;
137
  Collection<String> outputCdataSectionElements;
138
  boolean outputIndent;
139
  String outputMediaType;
140
 
141
  /**
142
   * Keys.
143
   */
144
  Collection<Key> keys;
145
 
146
  /**
147
   * Decimal formats.
148
   */
149
  Map<String,DecimalFormat> decimalFormats;
150
 
151
  /**
152
   * Namespace aliases.
153
   */
154
  Map<String,String> namespaceAliases;
155
 
156
  /**
157
   * Attribute-sets.
158
   */
159
  List<AttributeSet> attributeSets;
160
 
161
  /**
162
   * Variables.
163
   */
164
  List<ParameterNode> variables;
165
 
166
  /**
167
   * Variable and parameter bindings.
168
   */
169
  Bindings bindings;
170
 
171
  /**
172
   * Templates.
173
   */
174
  LinkedList<Template> templates;
175
 
176
  TemplateNode builtInNodeTemplate;
177
  TemplateNode builtInTextTemplate;
178
 
179
  /**
180
   * Holds the current node while parsing.
181
   * Necessary to associate the document function with its declaring node,
182
   * to resolve namespaces, and to maintain the current node for the
183
   * current() function.
184
   */
185
  Node current;
186
 
187
  /**
188
   * Set by a terminating message.
189
   */
190
  transient boolean terminated;
191
 
192
  /**
193
   * Current template in force.
194
   */
195
  transient Template currentTemplate;
196
 
197
  Stylesheet(TransformerFactoryImpl factory,
198
             Stylesheet parent,
199
             Document doc,
200
             String systemId,
201
             int precedence)
202
    throws TransformerConfigurationException
203
  {
204
    this.factory = factory;
205
    this.systemId = systemId;
206
    this.precedence = precedence;
207
    this.parent = parent;
208
    extensionElementPrefixes = new HashSet<String>();
209
    excludeResultPrefixes = new HashSet<String>();
210
    stripSpace = new LinkedHashSet<StrippingInstruction>();
211
    preserveSpace = new LinkedHashSet<StrippingInstruction>();
212
    outputCdataSectionElements = new LinkedHashSet<String>();
213
    xpath = (XPathImpl) factory.xpathFactory.newXPath();
214
    xpath.setNamespaceContext(this);
215
    if (parent == null)
216
      {
217
        bindings = new Bindings(this);
218
        attributeSets = new LinkedList<AttributeSet>();
219
        variables = new LinkedList<ParameterNode>();
220
        namespaceAliases = new LinkedHashMap<String,String>();
221
        templates = new LinkedList<Template>();
222
        keys = new LinkedList<Key>();
223
        decimalFormats = new LinkedHashMap<String,DecimalFormat>();
224
        initDefaultDecimalFormat();
225
        xpath.setXPathFunctionResolver(this);
226
      }
227
    else
228
      {
229
        /* Test for import circularity */
230
        for (Stylesheet ctx = this; ctx.parent != null; ctx = ctx.parent)
231
          {
232
            if (systemId != null && systemId.equals(ctx.parent.systemId))
233
              {
234
                String msg = "circularity importing " + systemId;
235
                throw new TransformerConfigurationException(msg);
236
              }
237
          }
238
        /* OK */
239
        Stylesheet root = getRootStylesheet();
240
        bindings = root.bindings;
241
        attributeSets = root.attributeSets;
242
        variables = root.variables;
243
        namespaceAliases = root.namespaceAliases;
244
        templates = root.templates;
245
        keys = root.keys;
246
        decimalFormats = root.decimalFormats;
247
        xpath.setXPathFunctionResolver(root);
248
      }
249
    xpath.setXPathVariableResolver(bindings);
250
 
251
    Test anyNode = new NodeTypeTest((short) 0);
252
    List<Test> tests = Collections.singletonList(anyNode);
253
    builtInNodeTemplate =
254
      new ApplyTemplatesNode(new Selector(Selector.CHILD, tests),
255
                             null, null, null, true);
256
    builtInTextTemplate =
257
      new ValueOfNode(new Selector(Selector.SELF, tests),
258
                      false);
259
 
260
    parse(doc.getDocumentElement(), true);
261
    current = doc; // Alow namespace resolution during processing
262
 
263
    debug = ("yes".equals(System.getProperty("xsl.debug")));
264
 
265
    if (debug)
266
      {
267
        System.err.println("Stylesheet: " + doc.getDocumentURI());
268
        for (Template t : templates)
269
          {
270
            t.list(System.err);
271
            System.err.println("--------------------");
272
          }
273
      }
274
  }
275
 
276
  Stylesheet getRootStylesheet()
277
  {
278
    Stylesheet stylesheet = this;
279
    while (stylesheet.parent != null)
280
      stylesheet = stylesheet.parent;
281
    return stylesheet;
282
  }
283
 
284
  void initDefaultDecimalFormat()
285
  {
286
    DecimalFormat defaultDecimalFormat = new DecimalFormat();
287
    DecimalFormatSymbols symbols = new DecimalFormatSymbols();
288
    symbols.setDecimalSeparator('.');
289
    symbols.setGroupingSeparator(',');
290
    symbols.setPercent('%');
291
    symbols.setPerMill('\u2030');
292
    symbols.setZeroDigit('0');
293
    symbols.setDigit('#');
294
    symbols.setPatternSeparator(';');
295
    symbols.setInfinity("Infinity");
296
    symbols.setNaN("NaN");
297
    symbols.setMinusSign('-');
298
    defaultDecimalFormat.setDecimalFormatSymbols(symbols);
299
    decimalFormats.put(null, defaultDecimalFormat);
300
  }
301
 
302
  // -- Cloneable --
303
 
304
  public Object clone()
305
  {
306
    try
307
      {
308
        Stylesheet clone = (Stylesheet) super.clone();
309
        clone.bindings = (Bindings) bindings.clone();
310
 
311
        LinkedList<Template> templates2 = new LinkedList<Template>();
312
        for (Template t : templates)
313
          {
314
            templates2.add(t.clone(clone));
315
          }
316
        clone.templates = templates2;
317
 
318
        LinkedList<AttributeSet> attributeSets2 = new LinkedList<AttributeSet>();
319
        for (AttributeSet as : attributeSets)
320
          {
321
            attributeSets2.add(as.clone(clone));
322
          }
323
        clone.attributeSets = attributeSets2;
324
 
325
        LinkedList<ParameterNode> variables2 = new LinkedList<ParameterNode>();
326
        for (ParameterNode var : variables)
327
          {
328
            variables2.add(var.clone(clone));
329
          }
330
        clone.variables = variables2;
331
 
332
        LinkedList<Key> keys2 = new LinkedList<Key>();
333
        for (Key k : keys)
334
          {
335
            keys2.add(k.clone(clone));
336
          }
337
        clone.keys = keys2;
338
 
339
        return clone;
340
      }
341
    catch (CloneNotSupportedException e)
342
      {
343
        throw new Error(e.getMessage());
344
      }
345
  }
346
 
347
  // -- Variable evaluation --
348
 
349
  void initTopLevelVariables(Node context)
350
    throws TransformerException
351
  {
352
    current = context;
353
    // Sort the variables into order
354
    // See XSLT 11.4: "If the template or expression specifying the value of
355
    // a global variable x references a global variable y, then the value
356
    // for y must be computed before the value of x."
357
    List<ParameterNode> topLevel = new ArrayList<ParameterNode>(variables);
358
    Collections.sort(topLevel);
359
    for (ParameterNode var : topLevel)
360
      {
361
        bindings.set(var.name,
362
                     var.getValue(this, null, context, 1, 1),
363
                     var.type);
364
      }
365
    current = null;
366
  }
367
 
368
  // -- NamespaceContext --
369
 
370
  public String getNamespaceURI(String prefix)
371
  {
372
    return (current == null) ? null : current.lookupNamespaceURI(prefix);
373
  }
374
 
375
  public String getPrefix(String namespaceURI)
376
  {
377
    return (current == null) ? null : current.lookupPrefix(namespaceURI);
378
  }
379
 
380
  public Iterator<String> getPrefixes(String namespaceURI)
381
  {
382
    // TODO
383
    return Collections.singleton(getPrefix(namespaceURI)).iterator();
384
  }
385
 
386
  final QName getQName(String name)
387
  {
388
    String localName = name, uri = null, prefix = null;
389
    int ci = name.indexOf(':');
390
    if (ci != -1)
391
      {
392
        prefix = name.substring(0, ci);
393
        localName = name.substring(ci + 1);
394
        uri = getNamespaceURI(prefix);
395
      }
396
    return new QName(uri, localName, prefix);
397
  }
398
 
399
  // -- Template selection --
400
 
401
  TemplateNode getTemplate(QName mode, Node context, boolean applyImports)
402
    throws TransformerException
403
  {
404
    if (debug)
405
      System.err.println("getTemplate: mode="+mode+" context="+context);
406
    Template selected = null;
407
    for (Template t : templates)
408
      {
409
        boolean isMatch = t.matches(mode, context);
410
        if (applyImports)
411
          {
412
            if (currentTemplate == null)
413
              {
414
                String msg = "current template may not be null " +
415
                  "during apply-imports";
416
                throw new TransformerException(msg);
417
              }
418
            if (!currentTemplate.imports(t))
419
              isMatch = false;
420
          }
421
        //System.err.println("\t"+context+" "+t+"="+isMatch);
422
        if (isMatch)
423
          {
424
            // Conflict resolution
425
            // @see http://www.w3.org/TR/xslt#conflict
426
            if (selected == null)
427
              selected = t;
428
            else
429
              {
430
                if (t.precedence < selected.precedence ||
431
                    t.priority < selected.priority)
432
                  continue;
433
                selected = t;
434
              }
435
          }
436
      }
437
    if (selected == null)
438
      {
439
        // Apply built-in template
440
        // Current template is unchanged
441
        if (debug)
442
          System.err.println("\tbuiltInTemplate context="+context);
443
        switch (context.getNodeType())
444
          {
445
          case Node.ELEMENT_NODE:
446
          case Node.DOCUMENT_NODE:
447
          case Node.DOCUMENT_FRAGMENT_NODE:
448
          case Node.PROCESSING_INSTRUCTION_NODE:
449
          case Node.COMMENT_NODE:
450
            return builtInNodeTemplate;
451
          case Node.TEXT_NODE:
452
          case Node.CDATA_SECTION_NODE:
453
          case Node.ATTRIBUTE_NODE:
454
            return builtInTextTemplate;
455
          default:
456
            return null;
457
          }
458
      }
459
    // Set current template
460
    currentTemplate = selected;
461
    if (debug)
462
      System.err.println("\ttemplate="+currentTemplate+" context="+context);
463
    return currentTemplate.node;
464
  }
465
 
466
  TemplateNode getTemplate(QName mode, QName name)
467
    throws TransformerException
468
  {
469
    Template selected = null;
470
    for (Template t : templates)
471
      {
472
        boolean isMatch = t.matches(name);
473
        if (isMatch)
474
          {
475
            // Conflict resolution
476
            // @see http://www.w3.org/TR/xslt#conflict
477
            if (selected == null)
478
              selected = t;
479
            else
480
              {
481
                if (t.precedence < selected.precedence ||
482
                    t.priority < selected.priority)
483
                  continue;
484
                selected = t;
485
              }
486
          }
487
      }
488
    if (selected == null)
489
      return null;
490
    return selected.node;
491
  }
492
 
493
  /**
494
   * template
495
   */
496
  final Template parseTemplate(Node node, NamedNodeMap attrs)
497
    throws TransformerConfigurationException, XPathExpressionException
498
  {
499
    String n = getAttribute(attrs, "name");
500
    QName name = (n == null) ? null : getQName(n);
501
    String m = getAttribute(attrs, "match");
502
    Pattern match = null;
503
    if (m != null)
504
      {
505
        try
506
          {
507
            match = (Pattern) xpath.compile(m);
508
          }
509
        catch (ClassCastException e)
510
          {
511
            String msg = "illegal pattern: " + m;
512
            throw new TransformerConfigurationException(msg);
513
          }
514
      }
515
    String p = getAttribute(attrs, "priority");
516
    String mm = getAttribute(attrs, "mode");
517
    QName mode = (mm == null) ? null : getQName(mm);
518
    Node children = node.getFirstChild();
519
    return new Template(this, name, match, parse(children),
520
                        precedence, p, mode);
521
  }
522
 
523
  /**
524
   * output
525
   */
526
  final void parseOutput(Node node, NamedNodeMap attrs)
527
    throws TransformerConfigurationException
528
  {
529
    output = node;
530
    String method = getAttribute(attrs, "method");
531
    if ("xml".equals(method) || method == null)
532
      outputMethod = OUTPUT_XML;
533
    else if ("html".equals(method))
534
      outputMethod = OUTPUT_HTML;
535
    else if ("text".equals(method))
536
      outputMethod = OUTPUT_TEXT;
537
    else
538
      {
539
        String msg = "unsupported output method: " + method;
540
        DOMSourceLocator l = new DOMSourceLocator(node);
541
        throw new TransformerConfigurationException(msg, l);
542
      }
543
    outputPublicId = getAttribute(attrs, "doctype-public");
544
    outputSystemId = getAttribute(attrs, "doctype-system");
545
    outputEncoding = getAttribute(attrs, "encoding");
546
    String indent = getAttribute(attrs, "indent");
547
    if (indent != null)
548
      outputIndent = "yes".equals(indent);
549
    outputVersion = getAttribute(attrs, "version");
550
    String omitXmlDecl = getAttribute(attrs, "omit-xml-declaration");
551
    if (omitXmlDecl != null)
552
      outputOmitXmlDeclaration = "yes".equals(omitXmlDecl);
553
    String standalone = getAttribute(attrs, "standalone");
554
    if (standalone != null)
555
      outputStandalone = "yes".equals(standalone);
556
    outputMediaType = getAttribute(attrs, "media-type");
557
    String cdataSectionElements =
558
      getAttribute(attrs, "cdata-section-elements");
559
    if (cdataSectionElements != null)
560
      {
561
        StringTokenizer st = new StringTokenizer(cdataSectionElements, " ");
562
        while (st.hasMoreTokens())
563
          outputCdataSectionElements.add(st.nextToken());
564
      }
565
  }
566
 
567
  /**
568
   * key
569
   */
570
  final void parseKey(Node node, NamedNodeMap attrs)
571
    throws TransformerConfigurationException, XPathExpressionException
572
  {
573
    String n = getRequiredAttribute(attrs, "name", node);
574
    String m = getRequiredAttribute(attrs, "match", node);
575
    String u = getRequiredAttribute(attrs, "use", node);
576
    QName name = getQName(n);
577
    Expr use = (Expr) xpath.compile(u);
578
    try
579
      {
580
        Pattern match = (Pattern) xpath.compile(m);
581
        Key key = new Key(name, match, use);
582
        keys.add(key);
583
      }
584
    catch (ClassCastException e)
585
      {
586
        throw new TransformerConfigurationException("invalid pattern: " + m);
587
      }
588
  }
589
 
590
  /**
591
   * decimal-format
592
   */
593
  final void parseDecimalFormat(Node node, NamedNodeMap attrs)
594
    throws TransformerConfigurationException
595
  {
596
    String dfName = getAttribute(attrs, "name");
597
    DecimalFormat df = new DecimalFormat();
598
    DecimalFormatSymbols symbols = new DecimalFormatSymbols();
599
    symbols.setDecimalSeparator(parseDFChar(attrs, "decimal-separator", '.'));
600
    symbols.setGroupingSeparator(parseDFChar(attrs, "grouping-separator", ','));
601
    symbols.setInfinity(parseDFString(attrs, "infinity", "Infinity"));
602
    symbols.setMinusSign(parseDFChar(attrs, "minus-sign", '-'));
603
    symbols.setNaN(parseDFString(attrs, "NaN", "NaN"));
604
    symbols.setPercent(parseDFChar(attrs, "percent", '%'));
605
    symbols.setPerMill(parseDFChar(attrs, "per-mille", '\u2030'));
606
    symbols.setZeroDigit(parseDFChar(attrs, "zero-digit", '0'));
607
    symbols.setDigit(parseDFChar(attrs, "digit", '#'));
608
    symbols.setPatternSeparator(parseDFChar(attrs, "pattern-separator", ';'));
609
    df.setDecimalFormatSymbols(symbols);
610
    decimalFormats.put(dfName, df);
611
  }
612
 
613
  private final char parseDFChar(NamedNodeMap attrs, String name, char def)
614
    throws TransformerConfigurationException
615
  {
616
    Node attr = attrs.getNamedItem(name);
617
    try
618
      {
619
        return (attr == null) ? def : attr.getNodeValue().charAt(0);
620
      }
621
    catch (StringIndexOutOfBoundsException e)
622
      {
623
        throw new TransformerConfigurationException("empty attribute '" +
624
                                                    name +
625
                                                    "' in decimal-format", e);
626
      }
627
  }
628
 
629
  private final String parseDFString(NamedNodeMap attrs, String name,
630
                                     String def)
631
  {
632
    Node attr = attrs.getNamedItem(name);
633
    return (attr == null) ? def : attr.getNodeValue();
634
  }
635
 
636
  /**
637
   * namespace-alias
638
   */
639
  final void parseNamespaceAlias(Node node, NamedNodeMap attrs)
640
    throws TransformerConfigurationException
641
  {
642
    String sp = getRequiredAttribute(attrs, "stylesheet-prefix", node);
643
    String rp = getRequiredAttribute(attrs, "result-prefix", node);
644
    namespaceAliases.put(sp, rp);
645
  }
646
 
647
  /**
648
   * attribute-set
649
   */
650
  final void parseAttributeSet(Node node, NamedNodeMap attrs)
651
    throws TransformerConfigurationException, XPathExpressionException
652
  {
653
    TemplateNode children = parse(node.getFirstChild());
654
    String name = getRequiredAttribute(attrs, "name", node);
655
    String uas = getAttribute(attrs, "use-attribute-sets");
656
    attributeSets.add(new AttributeSet(children, name, uas));
657
  }
658
 
659
  /**
660
   * Parse top-level elements.
661
   */
662
  void parse(Node node, boolean root)
663
    throws TransformerConfigurationException
664
  {
665
    while (node != null)
666
      {
667
        current = node;
668
        doParse(node, root);
669
        node = node.getNextSibling();
670
      }
671
  }
672
 
673
  void doParse(Node node, boolean root)
674
    throws TransformerConfigurationException
675
  {
676
    try
677
      {
678
        String namespaceUri = node.getNamespaceURI();
679
        if (XSL_NS.equals(namespaceUri) &&
680
            node.getNodeType() == Node.ELEMENT_NODE)
681
          {
682
            String name = node.getLocalName();
683
            NamedNodeMap attrs = node.getAttributes();
684
            if ("stylesheet".equals(name))
685
              {
686
                version = getAttribute(attrs, "version");
687
                String eep = getAttribute(attrs, "extension-element-prefixes");
688
                if (eep != null)
689
                  {
690
                    StringTokenizer st = new StringTokenizer(eep);
691
                    while (st.hasMoreTokens())
692
                      {
693
                        extensionElementPrefixes.add(st.nextToken());
694
                      }
695
                  }
696
                String erp = getAttribute(attrs, "exclude-result-prefixes");
697
                if (erp != null)
698
                  {
699
                    StringTokenizer st = new StringTokenizer(erp);
700
                    while (st.hasMoreTokens())
701
                      {
702
                        excludeResultPrefixes.add(st.nextToken());
703
                      }
704
                  }
705
                parse(node.getFirstChild(), false);
706
              }
707
            else if ("template".equals(name))
708
              templates.add(parseTemplate(node, attrs));
709
            else if ("param".equals(name) ||
710
                     "variable".equals(name))
711
              {
712
                int type = "variable".equals(name) ?
713
                  Bindings.VARIABLE : Bindings.PARAM;
714
                TemplateNode content = parse(node.getFirstChild());
715
                QName paramName =
716
                  getQName(getRequiredAttribute(attrs, "name", node));
717
                String select = getAttribute(attrs, "select");
718
                ParameterNode param;
719
                if (select != null && select.length() > 0)
720
                  {
721
                    if (content != null)
722
                      {
723
                        String msg = "parameter '" + paramName +
724
                          "' has both select and content";
725
                        DOMSourceLocator l = new DOMSourceLocator(node);
726
                        throw new TransformerConfigurationException(msg, l);
727
                      }
728
                    Expr expr = (Expr) xpath.compile(select);
729
                    param = new ParameterNode(paramName, expr, type);
730
                  }
731
                else
732
                  {
733
                    param = new ParameterNode(paramName, null, type);
734
                    param.children = content;
735
                  }
736
                variables.add(param);
737
              }
738
            else if ("include".equals(name) || "import".equals(name))
739
              {
740
                int delta = "import".equals(name) ? -1 : 0;
741
                String href = getRequiredAttribute(attrs, "href", node);
742
                Source source;
743
                synchronized (factory.resolver)
744
                  {
745
                    if (transformer != null)
746
                      {
747
                        factory.resolver
748
                          .setUserResolver(transformer.getURIResolver());
749
                        factory.resolver
750
                          .setUserListener(transformer.getErrorListener());
751
                      }
752
                    source = factory.resolver.resolve(systemId, href);
753
                  }
754
                factory.newStylesheet(source, precedence + delta, this);
755
              }
756
            else if ("output".equals(name))
757
              parseOutput(node, attrs);
758
            else if ("preserve-space".equals(name))
759
              {
760
                String elements =
761
                  getRequiredAttribute(attrs, "elements", node);
762
                StringTokenizer st = new StringTokenizer(elements,
763
                                                         " \t\n\r");
764
                while (st.hasMoreTokens())
765
                  {
766
                    NameTest element = parseNameTest(st.nextToken());
767
                    preserveSpace.add(new StrippingInstruction(element,
768
                                                               precedence));
769
                  }
770
              }
771
            else if ("strip-space".equals(name))
772
              {
773
                String elements =
774
                  getRequiredAttribute(attrs, "elements", node);
775
                StringTokenizer st = new StringTokenizer(elements,
776
                                                         " \t\n\r");
777
                while (st.hasMoreTokens())
778
                  {
779
                    NameTest element = parseNameTest(st.nextToken());
780
                    stripSpace.add(new StrippingInstruction(element,
781
                                                            precedence));
782
                  }
783
              }
784
            else if ("key".equals(name))
785
              parseKey(node, attrs);
786
            else if ("decimal-format".equals(name))
787
              parseDecimalFormat(node, attrs);
788
            else if ("namespace-alias".equals(name))
789
              parseNamespaceAlias(node, attrs);
790
            else if ("attribute-set".equals(name))
791
              parseAttributeSet(node, attrs);
792
          }
793
        else if (root)
794
          {
795
            // Literal document element
796
            Attr versionNode =
797
              ((Element)node).getAttributeNodeNS(XSL_NS, "version");
798
            if (versionNode == null)
799
              {
800
                String msg = "no xsl:version attribute on literal result node";
801
                DOMSourceLocator l = new DOMSourceLocator(node);
802
                throw new TransformerConfigurationException(msg, l);
803
              }
804
            version = versionNode.getValue();
805
            Node rootClone = node.cloneNode(true);
806
            NamedNodeMap attrs = rootClone.getAttributes();
807
            attrs.removeNamedItemNS(XSL_NS, "version");
808
            templates.add(new Template(this, null, new Root(),
809
                                       parse(rootClone),
810
                                       precedence,
811
                                       null,
812
                                       null));
813
          }
814
        else
815
          {
816
            // Skip unknown elements, text, comments, etc
817
          }
818
      }
819
    catch (TransformerException e)
820
      {
821
        DOMSourceLocator l = new DOMSourceLocator(node);
822
        throw new TransformerConfigurationException(e.getMessage(), l, e);
823
      }
824
    catch (DOMException e)
825
      {
826
        DOMSourceLocator l = new DOMSourceLocator(node);
827
        throw new TransformerConfigurationException(e.getMessage(), l, e);
828
      }
829
    catch (XPathExpressionException e)
830
      {
831
        DOMSourceLocator l = new DOMSourceLocator(node);
832
        throw new TransformerConfigurationException(e.getMessage(), l, e);
833
      }
834
  }
835
 
836
  final NameTest parseNameTest(String token)
837
  {
838
    if ("*".equals(token))
839
      return new NameTest(null, true, true);
840
    else if (token.endsWith(":*"))
841
      {
842
        QName qName = getQName(token);
843
        return new NameTest(qName, true, false);
844
      }
845
    else
846
      {
847
        QName qName = getQName(token);
848
        return new NameTest(qName, false, false);
849
      }
850
  }
851
 
852
  final TemplateNode parseAttributeValueTemplate(String value, Node source)
853
    throws TransformerConfigurationException, XPathExpressionException
854
  {
855
    current = source;
856
    // Tokenize
857
    int len = value.length();
858
    int off = 0;
859
    List<String> tokens = new ArrayList<String>(); // text tokens
860
    List<Boolean> types = new ArrayList<Boolean>(); // literal or expression
861
    int depth = 0;
862
    for (int i = 0; i < len; i++)
863
      {
864
        char c = value.charAt(i);
865
        if (c == '{')
866
          {
867
            if (i < (len - 1) && value.charAt(i + 1) == '{')
868
              {
869
                tokens.add(value.substring(off, i + 1));
870
                types.add(Boolean.FALSE);
871
                i++;
872
                off = i + 1;
873
                continue;
874
              }
875
            if (depth == 0)
876
              {
877
                if (i - off > 0)
878
                  {
879
                    tokens.add(value.substring(off, i));
880
                    types.add(Boolean.FALSE);
881
                  }
882
                off = i + 1;
883
              }
884
            depth++;
885
          }
886
        else if (c == '}')
887
          {
888
            if (i < (len - 1) && value.charAt(i + 1) == '}')
889
              {
890
                tokens.add(value.substring(off, i + 1));
891
                types.add(Boolean.FALSE);
892
                i++;
893
                off = i + 1;
894
                continue;
895
              }
896
            if (depth == 1)
897
              {
898
                if (i - off > 0)
899
                  {
900
                    tokens.add(value.substring(off, i));
901
                    types.add(Boolean.TRUE);
902
                  }
903
                else
904
                  {
905
                    String msg = "attribute value template " +
906
                      "must contain expression: " + value;
907
                    DOMSourceLocator l = new DOMSourceLocator(source);
908
                    throw new TransformerConfigurationException(msg, l);
909
                  }
910
                off = i + 1;
911
              }
912
            depth--;
913
          }
914
      }
915
    if (depth > 0)
916
      {
917
        String msg = "invalid attribute value template: " + value;
918
        throw new TransformerConfigurationException(msg);
919
      }
920
    if (len - off > 0)
921
      {
922
        // Trailing text
923
        tokens.add(value.substring(off));
924
        types.add(Boolean.FALSE);
925
      }
926
 
927
    // Construct template node tree
928
    TemplateNode ret = null;
929
    Document doc = source.getOwnerDocument();
930
    len = tokens.size();
931
    for (int i = len - 1; i >= 0; i--)
932
      {
933
        String token = tokens.get(i);
934
        Boolean type = types.get(i);
935
        if (type == Boolean.TRUE)
936
          {
937
            // Expression text
938
            Expr select = (Expr) xpath.compile(token);
939
            TemplateNode ret2 = new ValueOfNode(select, false);
940
            ret2.next = ret;
941
            ret = ret2;
942
          }
943
        else
944
          {
945
            // Verbatim text
946
            TemplateNode ret2 = new LiteralNode(doc.createTextNode(token));
947
            ret2.next = ret;
948
            ret = ret2;
949
          }
950
      }
951
    return ret;
952
  }
953
 
954
  /**
955
   * Whitespace stripping.
956
   * @param text the text node
957
   * @param source true if a source node, false if a stylesheet text node
958
   * @see http://www.w3.org/TR/xslt#strip
959
   */
960
  boolean isPreserved(Text text, boolean source)
961
    throws TransformerConfigurationException
962
  {
963
    // Check characters in text
964
    String value = text.getData();
965
    if (value != null)
966
      {
967
        int len = value.length();
968
        for (int i = 0; i < len; i++)
969
          {
970
            char c = value.charAt(i);
971
            if (c != 0x20 && c != 0x09 && c != 0x0a && c != 0x0d)
972
              return true;
973
          }
974
      }
975
    // Check parent node
976
    Node ctx = text.getParentNode();
977
    if (source)
978
      {
979
        // Source document text node
980
        boolean preserve = true;
981
        float psPriority = 0.0f, ssPriority = 0.0f;
982
        if (!stripSpace.isEmpty())
983
          {
984
            // Conflict resolution
985
            StrippingInstruction ssi = null, psi = null;
986
            for (StrippingInstruction si : stripSpace)
987
              {
988
                if (si.element.matches(ctx, 1, 1))
989
                  {
990
                    if (ssi != null)
991
                      {
992
                        if (si.precedence < ssi.precedence)
993
                          continue;
994
                        float p = si.getPriority();
995
                        if (p < ssPriority)
996
                          continue;
997
                      }
998
                    ssi = si;
999
                  }
1000
              }
1001
            for (StrippingInstruction si : preserveSpace)
1002
              {
1003
                if (si.element.matches(ctx, 1, 1))
1004
                  {
1005
                    if (psi != null)
1006
                      {
1007
                        if (si.precedence < psi.precedence)
1008
                          continue;
1009
                        float p = si.getPriority();
1010
                        if (p < psPriority)
1011
                          continue;
1012
                      }
1013
                    psi = si;
1014
                  }
1015
              }
1016
            if (ssi != null)
1017
              {
1018
                if (psi != null)
1019
                  {
1020
                    if (psi.precedence < ssi.precedence)
1021
                      preserve = false;
1022
                    else if (psPriority < ssPriority)
1023
                      preserve = false;
1024
                  }
1025
                else
1026
                  preserve = false;
1027
              }
1028
          }
1029
        if (preserve)
1030
          return true;
1031
      }
1032
    else
1033
      {
1034
        // Stylesheet text node
1035
        if (STYLESHEET_PRESERVE_TEXT.matches(ctx, 1, 1))
1036
          return true;
1037
      }
1038
    // Check whether any ancestor specified xml:space
1039
    while (ctx != null)
1040
      {
1041
        if (ctx.getNodeType() == Node.ELEMENT_NODE)
1042
          {
1043
            Element element = (Element) ctx;
1044
            String xmlSpace = element.getAttribute("xml:space");
1045
            if ("default".equals(xmlSpace))
1046
              break;
1047
            else if ("preserve".equals(xmlSpace))
1048
              return true;
1049
            else if (xmlSpace.length() > 0)
1050
              {
1051
                String msg = "Illegal value for xml:space: " + xmlSpace;
1052
                throw new TransformerConfigurationException(msg);
1053
              }
1054
          }
1055
        ctx = ctx.getParentNode();
1056
      }
1057
    return false;
1058
  }
1059
 
1060
  public XPathFunction resolveFunction(QName name, int arity)
1061
  {
1062
    String uri = name.getNamespaceURI();
1063
    if (XSL_NS.equals(uri) || uri == null || uri.length() == 0)
1064
      {
1065
        String localName = name.getLocalPart();
1066
        if ("document".equals(localName) && (arity == 1 || arity == 2))
1067
          {
1068
            if (current == null)
1069
                throw new RuntimeException("current is null");
1070
            return new DocumentFunction(getRootStylesheet(), current);
1071
          }
1072
        else if ("key".equals(localName) && (arity == 2))
1073
          return new KeyFunction(getRootStylesheet());
1074
        else if ("format-number".equals(localName) &&
1075
                 (arity == 2 || arity == 3))
1076
          return new FormatNumberFunction(getRootStylesheet());
1077
        else if ("current".equals(localName) && (arity == 0))
1078
          return new CurrentFunction(getRootStylesheet());
1079
        else if ("unparsed-entity-uri".equals(localName) && (arity == 1))
1080
          return new UnparsedEntityUriFunction();
1081
        else if ("generate-id".equals(localName) &&
1082
                 (arity == 1 || arity == 0))
1083
          return new GenerateIdFunction();
1084
        else if ("system-property".equals(localName) && (arity == 1))
1085
          return new SystemPropertyFunction();
1086
        else if ("element-available".equals(localName) && (arity == 1))
1087
          return new ElementAvailableFunction(new NamespaceProxy(current));
1088
        else if ("function-available".equals(localName) && (arity == 1))
1089
          return new FunctionAvailableFunction(new NamespaceProxy(current));
1090
      }
1091
    return null;
1092
  }
1093
 
1094
  // -- Parsing --
1095
 
1096
  /**
1097
   * apply-templates
1098
   */
1099
  final TemplateNode parseApplyTemplates(Node node)
1100
    throws TransformerConfigurationException, XPathExpressionException
1101
  {
1102
    NamedNodeMap attrs = node.getAttributes();
1103
    String m = getAttribute(attrs, "mode");
1104
    QName mode = (m == null) ? null : getQName(m);
1105
    String s = getAttribute(attrs, "select");
1106
    if (s == null)
1107
      s = "child::node()";
1108
    Node children = node.getFirstChild();
1109
    List<SortKey> sortKeys = parseSortKeys(children);
1110
    List<WithParam> withParams = parseWithParams(children);
1111
    Expr select = (Expr) xpath.compile(s);
1112
    return new ApplyTemplatesNode(select, mode,
1113
                                  sortKeys, withParams, false);
1114
  }
1115
 
1116
  /**
1117
   * call-template
1118
   */
1119
  final TemplateNode parseCallTemplate(Node node)
1120
    throws TransformerConfigurationException, XPathExpressionException
1121
  {
1122
    NamedNodeMap attrs = node.getAttributes();
1123
    String n = getRequiredAttribute(attrs, "name", node);
1124
    QName name = getQName(n);
1125
    Node children = node.getFirstChild();
1126
    List<WithParam> withParams = parseWithParams(children);
1127
    return new CallTemplateNode(name, withParams);
1128
  }
1129
 
1130
  /**
1131
   * value-of
1132
   */
1133
  final TemplateNode parseValueOf(Node node)
1134
    throws TransformerConfigurationException, XPathExpressionException
1135
  {
1136
    NamedNodeMap attrs = node.getAttributes();
1137
    String s = getRequiredAttribute(attrs, "select", node);
1138
    String doe = getAttribute(attrs, "disable-output-escaping");
1139
    boolean d = "yes".equals(doe);
1140
    Expr select = (Expr) xpath.compile(s);
1141
    return new ValueOfNode(select, d);
1142
  }
1143
 
1144
  /**
1145
   * for-each
1146
   */
1147
  final TemplateNode parseForEach(Node node)
1148
    throws TransformerConfigurationException, XPathExpressionException
1149
  {
1150
    NamedNodeMap attrs = node.getAttributes();
1151
    String s = getRequiredAttribute(attrs, "select", node);
1152
    Node children = node.getFirstChild();
1153
    List<SortKey> sortKeys = parseSortKeys(children);
1154
    Expr select = (Expr) xpath.compile(s);
1155
    ForEachNode ret = new ForEachNode(select, sortKeys);
1156
    ret.children = parse(children);
1157
    return ret;
1158
  }
1159
 
1160
  /**
1161
   * if
1162
   */
1163
  final TemplateNode parseIf(Node node)
1164
    throws TransformerConfigurationException, XPathExpressionException
1165
  {
1166
    NamedNodeMap attrs = node.getAttributes();
1167
    String t = getRequiredAttribute(attrs, "test", node);
1168
    Expr test = (Expr) xpath.compile(t);
1169
    Node children = node.getFirstChild();
1170
    IfNode ret = new IfNode(test);
1171
    ret.children = parse(children);
1172
    return ret;
1173
  }
1174
 
1175
  /**
1176
   * when
1177
   */
1178
  final TemplateNode parseWhen(Node node)
1179
    throws TransformerConfigurationException, XPathExpressionException
1180
  {
1181
    NamedNodeMap attrs = node.getAttributes();
1182
    String t = getRequiredAttribute(attrs, "test", node);
1183
    Expr test = (Expr) xpath.compile(t);
1184
    Node children = node.getFirstChild();
1185
    WhenNode ret = new WhenNode(test);
1186
    ret.children = parse(children);
1187
    return ret;
1188
  }
1189
 
1190
  /**
1191
   * element
1192
   */
1193
  final TemplateNode parseElement(Node node)
1194
    throws TransformerConfigurationException, XPathExpressionException
1195
  {
1196
    NamedNodeMap attrs = node.getAttributes();
1197
    String name = getRequiredAttribute(attrs, "name", node);
1198
    String namespace = getAttribute(attrs, "namespace");
1199
    String uas = getAttribute(attrs, "use-attribute-sets");
1200
    TemplateNode n = parseAttributeValueTemplate(name, node);
1201
    TemplateNode ns = (namespace == null) ? null :
1202
      parseAttributeValueTemplate(namespace, node);
1203
    Node children = node.getFirstChild();
1204
    ElementNode ret = new ElementNode(n, ns, uas, node);
1205
    ret.children = parse(children);
1206
    return ret;
1207
  }
1208
 
1209
  /**
1210
   * attribute
1211
   */
1212
  final TemplateNode parseAttribute(Node node)
1213
    throws TransformerConfigurationException, XPathExpressionException
1214
  {
1215
    NamedNodeMap attrs = node.getAttributes();
1216
    String name = getRequiredAttribute(attrs, "name", node);
1217
    String namespace = getAttribute(attrs, "namespace");
1218
    TemplateNode n = parseAttributeValueTemplate(name, node);
1219
    TemplateNode ns = (namespace == null) ? null :
1220
      parseAttributeValueTemplate(namespace, node);
1221
    Node children = node.getFirstChild();
1222
    AttributeNode ret = new AttributeNode(n, ns, node);
1223
    ret.children = parse(children);
1224
    return ret;
1225
  }
1226
 
1227
  /**
1228
   * text
1229
   */
1230
  final TemplateNode parseText(Node node)
1231
    throws TransformerConfigurationException, XPathExpressionException
1232
  {
1233
    NamedNodeMap attrs = node.getAttributes();
1234
    String doe = getAttribute(attrs, "disable-output-escaping");
1235
    boolean d = "yes".equals(doe);
1236
    Node children = node.getFirstChild();
1237
    TextNode ret = new TextNode(d);
1238
    ret.children = parse(children);
1239
    return ret;
1240
  }
1241
 
1242
  /**
1243
   * copy
1244
   */
1245
  final TemplateNode parseCopy(Node node)
1246
    throws TransformerConfigurationException, XPathExpressionException
1247
  {
1248
    NamedNodeMap attrs = node.getAttributes();
1249
    String uas = getAttribute(attrs, "use-attribute-sets");
1250
    Node children = node.getFirstChild();
1251
    CopyNode ret = new CopyNode(uas);
1252
    ret.children = parse(children);
1253
    return ret;
1254
  }
1255
 
1256
  /**
1257
   * processing-instruction
1258
   */
1259
  final TemplateNode parseProcessingInstruction(Node node)
1260
    throws TransformerConfigurationException, XPathExpressionException
1261
  {
1262
    NamedNodeMap attrs = node.getAttributes();
1263
    String name = getRequiredAttribute(attrs, "name", node);
1264
    Node children = node.getFirstChild();
1265
    ProcessingInstructionNode ret = new ProcessingInstructionNode(name);
1266
    ret.children = parse(children);
1267
    return ret;
1268
  }
1269
 
1270
  /**
1271
   * number
1272
   */
1273
  final TemplateNode parseNumber(Node node)
1274
    throws TransformerConfigurationException, XPathExpressionException
1275
  {
1276
    NamedNodeMap attrs = node.getAttributes();
1277
    String v = getAttribute(attrs, "value");
1278
    String ff = getAttribute(attrs, "format");
1279
    if (ff == null)
1280
      {
1281
        ff = "1";
1282
      }
1283
    TemplateNode format = parseAttributeValueTemplate(ff, node);
1284
    String lang = getAttribute(attrs, "lang");
1285
    String lv = getAttribute(attrs, "letter-value");
1286
    int letterValue = "traditional".equals(lv) ?
1287
      AbstractNumberNode.TRADITIONAL :
1288
      AbstractNumberNode.ALPHABETIC;
1289
    String gs = getAttribute(attrs, "grouping-separator");
1290
    String gz = getAttribute(attrs, "grouping-size");
1291
    int gz2 = (gz != null && gz.length() > 0) ?
1292
      Integer.parseInt(gz) : 1;
1293
    Node children = node.getFirstChild();
1294
    TemplateNode ret;
1295
    if (v != null && v.length() > 0)
1296
      {
1297
        Expr value = (Expr) xpath.compile(v);
1298
        ret = new NumberNode(value, format, lang,
1299
                             letterValue, gs, gz2);
1300
      }
1301
    else
1302
      {
1303
        String l = getAttribute(attrs, "level");
1304
        int level =
1305
          "multiple".equals(l) ? NodeNumberNode.MULTIPLE :
1306
                      "any".equals(l) ? NodeNumberNode.ANY :
1307
                      NodeNumberNode.SINGLE;
1308
        String c = getAttribute(attrs, "count");
1309
        String f = getAttribute(attrs, "from");
1310
        Pattern count = null;
1311
        Pattern from = null;
1312
        if (c != null)
1313
          {
1314
            try
1315
              {
1316
                count = (Pattern) xpath.compile(c);
1317
              }
1318
            catch (ClassCastException e)
1319
              {
1320
                String msg = "invalid pattern: " + c;
1321
                throw new TransformerConfigurationException(msg);
1322
              }
1323
          }
1324
        if (f != null)
1325
          {
1326
            try
1327
              {
1328
                from = (Pattern) xpath.compile(f);
1329
              }
1330
            catch (ClassCastException e)
1331
              {
1332
                String msg = "invalid pattern: " + f;
1333
                throw new TransformerConfigurationException(msg);
1334
              }
1335
          }
1336
        ret = new NodeNumberNode(level, count, from,
1337
                                 format, lang,
1338
                                 letterValue, gs, gz2);
1339
      }
1340
    ret.children = parse(children);
1341
    return ret;
1342
  }
1343
 
1344
  /**
1345
   * copy-of
1346
   */
1347
  final TemplateNode parseCopyOf(Node node)
1348
    throws TransformerConfigurationException, XPathExpressionException
1349
  {
1350
    NamedNodeMap attrs = node.getAttributes();
1351
    String s = getRequiredAttribute(attrs, "select", node);
1352
    Expr select = (Expr) xpath.compile(s);
1353
    Node children = node.getFirstChild();
1354
    CopyOfNode ret = new CopyOfNode(select);
1355
    ret.children = parse(children);
1356
    return ret;
1357
  }
1358
 
1359
  /**
1360
   * message
1361
   */
1362
  final TemplateNode parseMessage(Node node)
1363
    throws TransformerConfigurationException, XPathExpressionException
1364
  {
1365
    NamedNodeMap attrs = node.getAttributes();
1366
    String t = getAttribute(attrs, "terminate");
1367
    boolean terminate = "yes".equals(t);
1368
    Node children = node.getFirstChild();
1369
    MessageNode ret = new MessageNode(terminate);
1370
    ret.children = parse(children);
1371
    return ret;
1372
  }
1373
 
1374
  /**
1375
   * Parse template-level elements.
1376
   */
1377
  final TemplateNode parse(Node node)
1378
    throws TransformerConfigurationException
1379
  {
1380
    TemplateNode first = null;
1381
    TemplateNode previous = null;
1382
    while (node != null)
1383
      {
1384
        Node next = node.getNextSibling();
1385
        TemplateNode tnode = doParse(node);
1386
        if (tnode != null)
1387
          {
1388
            if (first == null)
1389
              first = tnode;
1390
            if (previous != null)
1391
              previous.next = tnode;
1392
            previous = tnode;
1393
          }
1394
        node = next;
1395
      }
1396
    return first;
1397
  }
1398
 
1399
  private final TemplateNode doParse(Node node)
1400
    throws TransformerConfigurationException
1401
  {
1402
    // Hack to associate the document function with its declaring node
1403
    current = node;
1404
    try
1405
      {
1406
        String namespaceUri = node.getNamespaceURI();
1407
        if (Stylesheet.XSL_NS.equals(namespaceUri) &&
1408
            Node.ELEMENT_NODE == node.getNodeType())
1409
          {
1410
            String name = node.getLocalName();
1411
            if ("apply-templates".equals(name))
1412
              return parseApplyTemplates(node);
1413
            else if ("call-template".equals(name))
1414
              return parseCallTemplate(node);
1415
            else if ("value-of".equals(name))
1416
              return parseValueOf(node);
1417
            else if ("for-each".equals(name))
1418
              return parseForEach(node);
1419
            else if ("if".equals(name))
1420
              return parseIf(node);
1421
            else if ("choose".equals(name))
1422
              {
1423
                Node children = node.getFirstChild();
1424
                ChooseNode ret = new ChooseNode();
1425
                ret.children = parse(children);
1426
                return ret;
1427
              }
1428
            else if ("when".equals(name))
1429
              return parseWhen(node);
1430
            else if ("otherwise".equals(name))
1431
              {
1432
                Node children = node.getFirstChild();
1433
                OtherwiseNode ret = new OtherwiseNode();
1434
                ret.children = parse(children);
1435
                return ret;
1436
              }
1437
            else if ("element".equals(name))
1438
              return parseElement(node);
1439
            else if ("attribute".equals(name))
1440
              return parseAttribute(node);
1441
            else if ("text".equals(name))
1442
              return parseText(node);
1443
            else if ("copy".equals(name))
1444
              return parseCopy(node);
1445
            else if ("processing-instruction".equals(name))
1446
              return parseProcessingInstruction(node);
1447
            else if ("comment".equals(name))
1448
              {
1449
                Node children = node.getFirstChild();
1450
                CommentNode ret = new CommentNode();
1451
                ret.children = parse(children);
1452
                return ret;
1453
              }
1454
            else if ("number".equals(name))
1455
              return parseNumber(node);
1456
            else if ("param".equals(name) ||
1457
                     "variable".equals(name))
1458
              {
1459
                int type = "variable".equals(name) ?
1460
                  Bindings.VARIABLE : Bindings.PARAM;
1461
                NamedNodeMap attrs = node.getAttributes();
1462
                Node children = node.getFirstChild();
1463
                TemplateNode content = parse(children);
1464
                QName paramName =
1465
                  getQName(getRequiredAttribute(attrs, "name", node));
1466
                String select = getAttribute(attrs, "select");
1467
                ParameterNode ret;
1468
                if (select != null)
1469
                  {
1470
                    if (content != null)
1471
                      {
1472
                        String msg = "parameter '" + paramName +
1473
                          "' has both select and content";
1474
                        DOMSourceLocator l = new DOMSourceLocator(node);
1475
                        throw new TransformerConfigurationException(msg, l);
1476
                      }
1477
                    Expr expr = (Expr) xpath.compile(select);
1478
                    ret = new ParameterNode(paramName, expr, type);
1479
                  }
1480
                else
1481
                  {
1482
                    ret = new ParameterNode(paramName, null, type);
1483
                    ret.children = content;
1484
                  }
1485
                return ret;
1486
              }
1487
            else if ("copy-of".equals(name))
1488
              return parseCopyOf(node);
1489
            else if ("message".equals(name))
1490
              return parseMessage(node);
1491
            else if ("apply-imports".equals(name))
1492
              {
1493
                Node children = node.getFirstChild();
1494
                ApplyImportsNode ret = new ApplyImportsNode();
1495
                ret.children = parse(children);
1496
                return ret;
1497
              }
1498
            else
1499
              {
1500
                // xsl:fallback
1501
                // Pass over any other XSLT nodes
1502
                return null;
1503
              }
1504
          }
1505
        String prefix = node.getPrefix();
1506
        if (extensionElementPrefixes.contains(prefix))
1507
          {
1508
            // Check for xsl:fallback
1509
            for (Node ctx = node.getFirstChild(); ctx != null;
1510
                 ctx = ctx.getNextSibling())
1511
              {
1512
                String ctxUri = ctx.getNamespaceURI();
1513
                if (XSL_NS.equals(ctxUri) &&
1514
                    "fallback".equals(ctx.getLocalName()))
1515
                  {
1516
                    ctx = ctx.getFirstChild();
1517
                    return (ctx == null) ? null : parse(ctx);
1518
                  }
1519
              }
1520
            // Otherwise pass over extension element
1521
            return null;
1522
          }
1523
        switch (node.getNodeType())
1524
          {
1525
          case Node.TEXT_NODE:
1526
          case Node.CDATA_SECTION_NODE:
1527
            // Determine whether to strip whitespace
1528
            Text text = (Text) node;
1529
            if (!isPreserved(text, false))
1530
              {
1531
                // Strip
1532
                text.getParentNode().removeChild(text);
1533
                return null;
1534
              }
1535
            break;
1536
          case Node.COMMENT_NODE:
1537
            // Ignore comments
1538
            return null;
1539
          case Node.ELEMENT_NODE:
1540
            // Check for attribute value templates and use-attribute-sets
1541
            NamedNodeMap attrs = node.getAttributes();
1542
            boolean convert = false;
1543
            String useAttributeSets = null;
1544
            int len = attrs.getLength();
1545
            for (int i = 0; i < len; i++)
1546
              {
1547
                Node attr = attrs.item(i);
1548
                String value = attr.getNodeValue();
1549
                if (Stylesheet.XSL_NS.equals(attr.getNamespaceURI()) &&
1550
                    "use-attribute-sets".equals(attr.getLocalName()))
1551
                  {
1552
                    useAttributeSets = value;
1553
                    convert = true;
1554
                    break;
1555
                  }
1556
                int start = value.indexOf('{');
1557
                int end = value.indexOf('}');
1558
                if (start != -1 || end != -1)
1559
                  {
1560
                    convert = true;
1561
                    break;
1562
                  }
1563
              }
1564
            if (convert)
1565
              {
1566
                // Create an element-producing template node instead
1567
                // with appropriate attribute-producing child template nodes
1568
                Node children = node.getFirstChild();
1569
                TemplateNode child = parse(children);
1570
                for (int i = 0; i < len; i++)
1571
                  {
1572
                    Node attr = attrs.item(i);
1573
                    String ans = attr.getNamespaceURI();
1574
                    String aname = attr.getNodeName();
1575
                    if (Stylesheet.XSL_NS.equals(ans) &&
1576
                        "use-attribute-sets".equals(attr.getLocalName()))
1577
                      continue;
1578
                    String value = attr.getNodeValue();
1579
                    TemplateNode grandchild =
1580
                      parseAttributeValueTemplate(value, node);
1581
                    TemplateNode n =
1582
                      parseAttributeValueTemplate(aname, node);
1583
                    TemplateNode ns = (ans == null) ? null :
1584
                      parseAttributeValueTemplate(ans, node);
1585
                    TemplateNode newChild = new AttributeNode(n, ns, attr);
1586
                    newChild.children = grandchild;
1587
                    newChild.next = child;
1588
                    child = newChild;
1589
                  }
1590
                String ename = node.getNodeName();
1591
                TemplateNode n = parseAttributeValueTemplate(ename, node);
1592
                //TemplateNode ns = (namespaceUri == null) ? null :
1593
                //  parseAttributeValueTemplate(namespaceUri, node);
1594
                TemplateNode ns = null;
1595
                ElementNode ret = new ElementNode(n, ns, useAttributeSets,
1596
                                                  node);
1597
                ret.children = child;
1598
                return ret;
1599
              }
1600
            // Otherwise fall through
1601
            break;
1602
          }
1603
      }
1604
    catch (XPathExpressionException e)
1605
      {
1606
        DOMSourceLocator l = new DOMSourceLocator(node);
1607
        throw new TransformerConfigurationException(e.getMessage(), l, e);
1608
      }
1609
    Node children = node.getFirstChild();
1610
    LiteralNode ret = new LiteralNode(node);
1611
    ret.children = parse(children);
1612
    return ret;
1613
  }
1614
 
1615
  final List<SortKey> parseSortKeys(Node node)
1616
    throws TransformerConfigurationException, XPathExpressionException
1617
  {
1618
    List<SortKey> ret = new LinkedList<SortKey>();
1619
    while (node != null)
1620
      {
1621
        String namespaceUri = node.getNamespaceURI();
1622
        if (Stylesheet.XSL_NS.equals(namespaceUri) &&
1623
            Node.ELEMENT_NODE == node.getNodeType() &&
1624
            "sort".equals(node.getLocalName()))
1625
          {
1626
            NamedNodeMap attrs = node.getAttributes();
1627
            String s = getAttribute(attrs, "select");
1628
            if (s == null)
1629
              s = ".";
1630
            Expr select = (Expr) xpath.compile(s);
1631
            String l = getAttribute(attrs, "lang");
1632
            TemplateNode lang = (l == null) ? null :
1633
              parseAttributeValueTemplate(l, node);
1634
            String dt = getAttribute(attrs, "data-type");
1635
            TemplateNode dataType = (dt == null) ? null :
1636
              parseAttributeValueTemplate(dt, node);
1637
            String o = getAttribute(attrs, "order");
1638
            TemplateNode order = (o == null) ? null :
1639
              parseAttributeValueTemplate(o, node);
1640
            String co = getAttribute(attrs, "case-order");
1641
            TemplateNode caseOrder = (co == null) ? null :
1642
              parseAttributeValueTemplate(co, node);
1643
            ret.add(new SortKey(select, lang, dataType, order, caseOrder));
1644
          }
1645
        node = node.getNextSibling();
1646
      }
1647
    return ret;
1648
  }
1649
 
1650
  final List<WithParam> parseWithParams(Node node)
1651
    throws TransformerConfigurationException, XPathExpressionException
1652
  {
1653
    List<WithParam> ret = new LinkedList<WithParam>();
1654
    while (node != null)
1655
      {
1656
        String namespaceUri = node.getNamespaceURI();
1657
        if (Stylesheet.XSL_NS.equals(namespaceUri) &&
1658
            Node.ELEMENT_NODE == node.getNodeType() &&
1659
            "with-param".equals(node.getLocalName()))
1660
          {
1661
            NamedNodeMap attrs = node.getAttributes();
1662
            TemplateNode content = parse(node.getFirstChild());
1663
            QName name =
1664
              getQName(getRequiredAttribute(attrs, "name", node));
1665
            String select = getAttribute(attrs, "select");
1666
            if (select != null)
1667
              {
1668
                if (content != null)
1669
                  {
1670
                    String msg = "parameter '" + name +
1671
                      "' has both select and content";
1672
                    DOMSourceLocator l = new DOMSourceLocator(node);
1673
                    throw new TransformerConfigurationException(msg, l);
1674
                  }
1675
                Expr expr = (Expr) xpath.compile(select);
1676
                ret.add(new WithParam(name, expr));
1677
              }
1678
            else
1679
              ret.add(new WithParam(name, content));
1680
          }
1681
        node = node.getNextSibling();
1682
      }
1683
    return ret;
1684
  }
1685
 
1686
  /**
1687
   * Created element nodes have a copy of the namespace nodes in the
1688
   * stylesheet, except the XSLT namespace, extension namespaces, and
1689
   * exclude-result-prefixes.
1690
   */
1691
  final void addNamespaceNodes(Node source, Node target, Document doc,
1692
                               Collection<String> elementExcludeResultPrefixes)
1693
  {
1694
    NamedNodeMap attrs = source.getAttributes();
1695
    if (attrs != null)
1696
      {
1697
        int len = attrs.getLength();
1698
        for (int i = 0; i < len; i++)
1699
          {
1700
            Node attr = attrs.item(i);
1701
            String uri = attr.getNamespaceURI();
1702
            if (uri == XMLConstants.XMLNS_ATTRIBUTE_NS_URI)
1703
              {
1704
                String prefix = attr.getLocalName();
1705
                if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
1706
                  prefix = "#default";
1707
                String ns = attr.getNodeValue();
1708
                // Should the namespace be excluded?
1709
                if (XSL_NS.equals(ns) ||
1710
                    extensionElementPrefixes.contains(prefix) ||
1711
                    elementExcludeResultPrefixes.contains(prefix) ||
1712
                    excludeResultPrefixes.contains(prefix))
1713
                  continue;
1714
                // Is the namespace already defined on the target?
1715
                if (prefix == "#default")
1716
                  prefix = null;
1717
                if (target.lookupNamespaceURI(prefix) != null)
1718
                  continue;
1719
                attr = attr.cloneNode(true);
1720
                attr = doc.adoptNode(attr);
1721
                target.getAttributes().setNamedItemNS(attr);
1722
              }
1723
          }
1724
      }
1725
    Node parent = source.getParentNode();
1726
    if (parent != null)
1727
      addNamespaceNodes(parent, target, doc, elementExcludeResultPrefixes);
1728
  }
1729
 
1730
  static final String getAttribute(NamedNodeMap attrs, String name)
1731
  {
1732
    Node attr = attrs.getNamedItem(name);
1733
    if (attr == null)
1734
      return null;
1735
    String ret = attr.getNodeValue();
1736
    if (ret.length() == 0)
1737
      return null;
1738
    return ret;
1739
  }
1740
 
1741
  static final String getRequiredAttribute(NamedNodeMap attrs, String name,
1742
                                           Node source)
1743
    throws TransformerConfigurationException
1744
  {
1745
    String value = getAttribute(attrs, name);
1746
    if (value == null || value.length() == 0)
1747
      {
1748
        String msg =
1749
          name + " attribute is required on " + source.getNodeName();
1750
        DOMSourceLocator l = new DOMSourceLocator(source);
1751
        throw new TransformerConfigurationException(msg, l);
1752
      }
1753
    return value;
1754
  }
1755
 
1756
  // Handle user data changes when nodes are cloned etc
1757
 
1758
  public void handle(short op, String key, Object data, Node src, Node dst)
1759
  {
1760
    dst.setUserData(key, data, this);
1761
  }
1762
 
1763
  public String toString()
1764
  {
1765
    CPStringBuilder b = new CPStringBuilder(getClass().getName());
1766
    b.append("[templates=");
1767
    b.append(templates);
1768
    b.append("]");
1769
    return b.toString();
1770
  }
1771
 
1772
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.