1 /*
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3 * @LastModified: Nov 2017
4 */
5 /*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements. See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 package com.sun.org.apache.xalan.internal.xsltc.compiler;
23
24 import com.sun.java_cup.internal.runtime.Symbol;
25 import com.sun.org.apache.xalan.internal.XalanConstants;
26 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
27 import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
28 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
33 import com.sun.org.apache.xml.internal.serializer.utils.SystemIDResolver;
34 import java.io.File;
35 import java.io.IOException;
36 import java.io.StringReader;
37 import java.util.ArrayList;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Properties;
43 import java.util.Stack;
44 import java.util.StringTokenizer;
45 import javax.xml.XMLConstants;
46 import javax.xml.catalog.CatalogFeatures;
47 import javax.xml.parsers.ParserConfigurationException;
48 import javax.xml.parsers.SAXParser;
49 import javax.xml.parsers.SAXParserFactory;
50 import jdk.xml.internal.JdkXmlFeatures;
51 import jdk.xml.internal.JdkXmlUtils;
52 import jdk.xml.internal.SecuritySupport;
53 import org.xml.sax.Attributes;
54 import org.xml.sax.ContentHandler;
55 import org.xml.sax.InputSource;
56 import org.xml.sax.Locator;
57 import org.xml.sax.SAXException;
58 import org.xml.sax.SAXNotRecognizedException;
59 import org.xml.sax.SAXNotSupportedException;
60 import org.xml.sax.SAXParseException;
61 import org.xml.sax.XMLReader;
62 import org.xml.sax.helpers.AttributesImpl;
63
64 /**
65 * @author Jacek Ambroziak
66 * @author Santiago Pericas-Geertsen
67 * @author G. Todd Miller
68 * @author Morten Jorgensen
69 * @author Erwin Bolwidt <ejb@klomp.org>
70 */
71 public class Parser implements Constants, ContentHandler {
72
73 private static final String XSL = "xsl"; // standard prefix
74 private static final String TRANSLET = "translet"; // extension prefix
75
76 private Locator _locator = null;
77
78 private XSLTC _xsltc; // Reference to the compiler object.
79 private XPathParser _xpathParser; // Reference to the XPath parser.
80 private ArrayList<ErrorMsg> _errors; // Contains all compilation errors
81 private ArrayList<ErrorMsg> _warnings; // Contains all compilation warnings
82
83 private Map<String, String> _instructionClasses; // Maps instructions to classes
84 private Map<String, String[]> _instructionAttrs; // reqd and opt attrs
85 private Map<String, QName> _qNames;
86 private Map<String, Map<String, QName>> _namespaces;
87 private QName _useAttributeSets;
88 private QName _excludeResultPrefixes;
89 private QName _extensionElementPrefixes;
90 private Map<String, Object> _variableScope;
91 private Stylesheet _currentStylesheet;
92 private SymbolTable _symbolTable; // Maps QNames to syntax-tree nodes
93 private Output _output;
94 private Template _template; // Reference to the template being parsed.
95
96 private boolean _rootNamespaceDef; // Used for validity check
97
98 private SyntaxTreeNode _root;
99
100 private String _target;
101
102 private int _currentImportPrecedence;
103
104 private boolean _useServicesMechanism = true;
105
106 public Parser(XSLTC xsltc, boolean useServicesMechanism) {
107 _xsltc = xsltc;
108 _useServicesMechanism = useServicesMechanism;
109 }
110
111 public void init() {
112 _qNames = new HashMap<>(512);
113 _namespaces = new HashMap<>();
114 _instructionClasses = new HashMap<>();
115 _instructionAttrs = new HashMap<>();
116 _variableScope = new HashMap<>();
117 _template = null;
118 _errors = new ArrayList<>();
119 _warnings = new ArrayList<>();
120 _symbolTable = new SymbolTable();
121 _xpathParser = new XPathParser(this);
122 _currentStylesheet = null;
123 _output = null;
124 _root = null;
125 _rootNamespaceDef = false;
126 _currentImportPrecedence = 1;
127
128 initStdClasses();
129 initInstructionAttrs();
130 initExtClasses();
131 initSymbolTable();
132
133 _useAttributeSets =
134 getQName(XSLT_URI, XSL, "use-attribute-sets");
135 _excludeResultPrefixes =
136 getQName(XSLT_URI, XSL, "exclude-result-prefixes");
137 _extensionElementPrefixes =
138 getQName(XSLT_URI, XSL, "extension-element-prefixes");
139 }
140
141 public void setOutput(Output output) {
142 if (_output != null) {
143 if (_output.getImportPrecedence() <= output.getImportPrecedence()) {
144 output.mergeOutput(_output);
145 _output.disable();
146 _output = output;
147 }
148 else {
149 output.disable();
150 }
151 }
152 else {
153 _output = output;
154 }
155 }
156
157 public Output getOutput() {
158 return _output;
159 }
160
161 public Properties getOutputProperties() {
162 return getTopLevelStylesheet().getOutputProperties();
163 }
164
165 public void addVariable(Variable var) {
166 addVariableOrParam(var);
167 }
168
169 public void addParameter(Param param) {
170 addVariableOrParam(param);
171 }
172
173 private void addVariableOrParam(VariableBase var) {
174 Object existing = _variableScope.get(var.getName().getStringRep());
175 if (existing != null) {
176 if (existing instanceof Stack) {
177 @SuppressWarnings("unchecked")
178 Stack<VariableBase> stack = (Stack<VariableBase>)existing;
179 stack.push(var);
180 }
181 else if (existing instanceof VariableBase) {
182 Stack<VariableBase> stack = new Stack<>();
183 stack.push((VariableBase)existing);
184 stack.push(var);
185 _variableScope.put(var.getName().getStringRep(), stack);
186 }
187 }
188 else {
189 _variableScope.put(var.getName().getStringRep(), var);
190 }
191 }
192
193 public void removeVariable(QName name) {
194 Object existing = _variableScope.get(name.getStringRep());
195 if (existing instanceof Stack) {
196 @SuppressWarnings("unchecked")
197 Stack<VariableBase> stack = (Stack<VariableBase>)existing;
198 if (!stack.isEmpty()) stack.pop();
199 if (!stack.isEmpty()) return;
200 }
201 _variableScope.remove(name.getStringRep());
202 }
203
204 public VariableBase lookupVariable(QName name) {
205 Object existing = _variableScope.get(name.getStringRep());
206 if (existing instanceof VariableBase) {
207 return (VariableBase)existing;
208 }
209 else if (existing instanceof Stack) {
210 @SuppressWarnings("unchecked")
211 Stack<VariableBase> stack = (Stack<VariableBase>)existing;
212 return stack.peek();
213 }
214 return null;
215 }
216
217 public void setXSLTC(XSLTC xsltc) {
218 _xsltc = xsltc;
219 }
220
221 public XSLTC getXSLTC() {
222 return _xsltc;
223 }
224
225 public int getCurrentImportPrecedence() {
226 return _currentImportPrecedence;
227 }
228
229 public int getNextImportPrecedence() {
230 return ++_currentImportPrecedence;
231 }
232
233 public void setCurrentStylesheet(Stylesheet stylesheet) {
234 _currentStylesheet = stylesheet;
235 }
236
237 public Stylesheet getCurrentStylesheet() {
238 return _currentStylesheet;
239 }
240
241 public Stylesheet getTopLevelStylesheet() {
242 return _xsltc.getStylesheet();
243 }
244
245 public QName getQNameSafe(final String stringRep) {
246 // parse and retrieve namespace
247 final int colon = stringRep.lastIndexOf(':');
248 if (colon != -1) {
249 final String prefix = stringRep.substring(0, colon);
250 final String localname = stringRep.substring(colon + 1);
251 String namespace = null;
252
253 // Get the namespace uri from the symbol table
254 if (prefix.equals(XMLNS_PREFIX) == false) {
255 namespace = _symbolTable.lookupNamespace(prefix);
256 if (namespace == null) namespace = EMPTYSTRING;
257 }
258 return getQName(namespace, prefix, localname);
259 }
260 else {
261 final String uri = stringRep.equals(XMLNS_PREFIX) ? null
262 : _symbolTable.lookupNamespace(EMPTYSTRING);
263 return getQName(uri, null, stringRep);
264 }
265 }
266
267 public QName getQName(final String stringRep) {
268 return getQName(stringRep, true, false);
269 }
270
271 public QName getQNameIgnoreDefaultNs(final String stringRep) {
272 return getQName(stringRep, true, true);
273 }
274
275 public QName getQName(final String stringRep, boolean reportError) {
276 return getQName(stringRep, reportError, false);
277 }
278
279 private QName getQName(final String stringRep, boolean reportError,
280 boolean ignoreDefaultNs)
281 {
282 // parse and retrieve namespace
283 final int colon = stringRep.lastIndexOf(':');
284 if (colon != -1) {
285 final String prefix = stringRep.substring(0, colon);
286 final String localname = stringRep.substring(colon + 1);
287 String namespace = null;
288
289 // Get the namespace uri from the symbol table
290 if (prefix.equals(XMLNS_PREFIX) == false) {
291 namespace = _symbolTable.lookupNamespace(prefix);
292 if (namespace == null && reportError) {
293 final int line = getLineNumber();
294 ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
295 line, prefix);
296 reportError(ERROR, err);
297 }
298 }
299 return getQName(namespace, prefix, localname);
300 }
301 else {
302 if (stringRep.equals(XMLNS_PREFIX)) {
303 ignoreDefaultNs = true;
304 }
305 final String defURI = ignoreDefaultNs ? null
306 : _symbolTable.lookupNamespace(EMPTYSTRING);
307 return getQName(defURI, null, stringRep);
308 }
309 }
310
311 public QName getQName(String namespace, String prefix, String localname) {
312 if (namespace == null || namespace.equals(EMPTYSTRING)) {
313 QName name = _qNames.get(localname);
314 if (name == null) {
315 name = new QName(null, prefix, localname);
316 _qNames.put(localname, name);
317 }
318 return name;
319 }
320 else {
321 Map<String, QName> space = _namespaces.get(namespace);
322 String lexicalQName =
323 (prefix == null || prefix.length() == 0)
324 ? localname
325 : (prefix + ':' + localname);
326
327 if (space == null) {
328 final QName name = new QName(namespace, prefix, localname);
329 _namespaces.put(namespace, space = new HashMap<>());
330 space.put(lexicalQName, name);
331 return name;
332 }
333 else {
334 QName name = space.get(lexicalQName);
335 if (name == null) {
336 name = new QName(namespace, prefix, localname);
337 space.put(lexicalQName, name);
338 }
339 return name;
340 }
341 }
342 }
343
344 public QName getQName(String scope, String name) {
345 return getQName(scope + name);
346 }
347
348 public QName getQName(QName scope, QName name) {
349 return getQName(scope.toString() + name.toString());
350 }
351
352 public QName getUseAttributeSets() {
353 return _useAttributeSets;
354 }
355
356 public QName getExtensionElementPrefixes() {
357 return _extensionElementPrefixes;
358 }
359
360 public QName getExcludeResultPrefixes() {
361 return _excludeResultPrefixes;
362 }
363
364 /**
365 * Create an instance of the <code>Stylesheet</code> class,
366 * and then parse, typecheck and compile the instance.
367 * Must be called after <code>parse()</code>.
368 */
369 public Stylesheet makeStylesheet(SyntaxTreeNode element)
370 throws CompilerException {
371 try {
372 Stylesheet stylesheet;
373
374 if (element instanceof Stylesheet) {
375 stylesheet = (Stylesheet)element;
376 }
377 else {
378 stylesheet = new Stylesheet();
379 stylesheet.setSimplified();
380 stylesheet.addElement(element);
381 stylesheet.setAttributes((AttributesImpl) element.getAttributes());
382
383 // Map the default NS if not already defined
384 if (element.lookupNamespace(EMPTYSTRING) == null) {
385 element.addPrefixMapping(EMPTYSTRING, EMPTYSTRING);
386 }
387 }
388 stylesheet.setParser(this);
389 return stylesheet;
390 }
391 catch (ClassCastException e) {
392 ErrorMsg err = new ErrorMsg(ErrorMsg.NOT_STYLESHEET_ERR, element);
393 throw new CompilerException(err.toString());
394 }
395 }
396
397 /**
398 * Instanciates a SAX2 parser and generate the AST from the input.
399 */
400 public void createAST(Stylesheet stylesheet) {
401 try {
402 if (stylesheet != null) {
403 stylesheet.parseContents(this);
404 final Iterator<SyntaxTreeNode> elements = stylesheet.elements();
405 while (elements.hasNext()) {
406 SyntaxTreeNode child = elements.next();
407 if (child instanceof Text) {
408 final int l = getLineNumber();
409 ErrorMsg err =
410 new ErrorMsg(ErrorMsg.ILLEGAL_TEXT_NODE_ERR,l,null);
411 reportError(ERROR, err);
412 }
413 }
414 if (!errorsFound()) {
415 stylesheet.typeCheck(_symbolTable);
416 }
417 }
418 }
419 catch (TypeCheckError e) {
420 reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
421 }
422 }
423
424 /**
425 * Parses a stylesheet and builds the internal abstract syntax tree
426 * @param reader A SAX2 SAXReader (parser)
427 * @param input A SAX2 InputSource can be passed to a SAX reader
428 * @return The root of the abstract syntax tree
429 */
430 public SyntaxTreeNode parse(XMLReader reader, InputSource input) {
431 try {
432 // Parse the input document and build the abstract syntax tree
433 reader.setContentHandler(this);
434 reader.parse(input);
435 // Find the start of the stylesheet within the tree
436 return getStylesheet(_root);
437 }
438 catch (IOException e) {
439 if (_xsltc.debug()) e.printStackTrace();
440 reportError(ERROR,new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
441 }
442 catch (SAXException e) {
443 Throwable ex = e.getException();
444 if (_xsltc.debug()) {
445 e.printStackTrace();
446 if (ex != null) ex.printStackTrace();
447 }
448 reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
449 }
450 catch (CompilerException e) {
451 if (_xsltc.debug()) e.printStackTrace();
452 reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
453 }
454 catch (Exception e) {
455 if (_xsltc.debug()) e.printStackTrace();
456 reportError(ERROR, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
457 }
458 return null;
459 }
460
461 /**
462 * Parses a stylesheet and builds the internal abstract syntax tree
463 * @param input A SAX2 InputSource can be passed to a SAX reader
464 * @return The root of the abstract syntax tree
465 */
466 public SyntaxTreeNode parse(InputSource input) {
467 try {
468 // Create a SAX parser and get the XMLReader object it uses
469 final SAXParserFactory factory = FactoryImpl.getSAXFactory(_useServicesMechanism);
470
471 if (_xsltc.isSecureProcessing()) {
472 try {
473 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
474 }
475 catch (SAXException e) {}
476 }
477
478 try {
479 factory.setFeature(Constants.NAMESPACE_FEATURE,true);
480 }
481 catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
482 factory.setNamespaceAware(true);
483 }
484
485 final SAXParser parser = factory.newSAXParser();
486 try {
487 parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD,
488 _xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_DTD));
489 } catch (SAXNotRecognizedException e) {
490 ErrorMsg err = new ErrorMsg(ErrorMsg.WARNING_MSG,
491 parser.getClass().getName() + ": " + e.getMessage());
492 reportError(WARNING, err);
493 }
494
495 boolean supportCatalog = true;
496 boolean useCatalog = _xsltc.getFeature(JdkXmlFeatures.XmlFeature.USE_CATALOG);
497 try {
498 factory.setFeature(JdkXmlUtils.USE_CATALOG,useCatalog);
499 }
500 catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
501 supportCatalog = false;
502 }
503
504 if (supportCatalog && useCatalog) {
505 try {
506 CatalogFeatures cf = (CatalogFeatures)_xsltc.getProperty(JdkXmlFeatures.CATALOG_FEATURES);
507 if (cf != null) {
508 for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
509 parser.setProperty(f.getPropertyName(), cf.get(f));
510 }
511 }
512 } catch (SAXNotRecognizedException e) {
513 //shall not happen for internal settings
514 }
515 }
516
517 final XMLReader reader = parser.getXMLReader();
518 String lastProperty = "";
519 try {
520 XMLSecurityManager securityManager =
521 (XMLSecurityManager)_xsltc.getProperty(XalanConstants.SECURITY_MANAGER);
522 for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) {
523 lastProperty = limit.apiProperty();
524 reader.setProperty(lastProperty, securityManager.getLimitValueAsString(limit));
525 }
526 if (securityManager.printEntityCountInfo()) {
527 lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO;
528 parser.setProperty(XalanConstants.JDK_ENTITY_COUNT_INFO, XalanConstants.JDK_YES);
529 }
530 } catch (SAXException se) {
531 XMLSecurityManager.printWarning(reader.getClass().getName(), lastProperty, se);
532 }
533
534 // try setting other JDK-impl properties, ignore if not supported
535 JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, JdkXmlUtils.CDATA_CHUNK_SIZE,
536 _xsltc.getProperty(JdkXmlUtils.CDATA_CHUNK_SIZE), false);
537
538 return(parse(reader, input));
539 }
540 catch (ParserConfigurationException e) {
541 ErrorMsg err = new ErrorMsg(ErrorMsg.SAX_PARSER_CONFIG_ERR);
542 reportError(ERROR, err);
543 }
544 catch (SAXParseException e){
545 reportError(ERROR, new ErrorMsg(e.getMessage(),e.getLineNumber()));
546 }
547 catch (SAXException e) {
548 reportError(ERROR, new ErrorMsg(e.getMessage()));
549 }
550 return null;
551 }
552
553 public SyntaxTreeNode getDocumentRoot() {
554 return _root;
555 }
556
557 private String _PImedia = null;
558 private String _PItitle = null;
559 private String _PIcharset = null;
560
561 /**
562 * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
563 * processing instruction in the case where the input document is an
564 * XML document with one or more references to a stylesheet.
565 * @param media The media attribute to be matched. May be null, in which
566 * case the prefered templates will be used (i.e. alternate = no).
567 * @param title The value of the title attribute to match. May be null.
568 * @param charset The value of the charset attribute to match. May be null.
569 */
570 protected void setPIParameters(String media, String title, String charset) {
571 _PImedia = media;
572 _PItitle = title;
573 _PIcharset = charset;
574 }
575
576 /**
577 * Extracts the DOM for the stylesheet. In the case of an embedded
578 * stylesheet, it extracts the DOM subtree corresponding to the
579 * embedded stylesheet that has an 'id' attribute whose value is the
580 * same as the value declared in the <?xml-stylesheet...?> processing
581 * instruction (P.I.). In the xml-stylesheet P.I. the value is labeled
582 * as the 'href' data of the P.I. The extracted DOM representing the
583 * stylesheet is returned as an Element object.
584 */
585 private SyntaxTreeNode getStylesheet(SyntaxTreeNode root)
586 throws CompilerException {
587
588 // Assume that this is a pure XSL stylesheet if there is not
589 // <?xml-stylesheet ....?> processing instruction
590 if (_target == null) {
591 if (!_rootNamespaceDef) {
592 ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_URI_ERR);
593 throw new CompilerException(msg.toString());
594 }
595 return(root);
596 }
597
598 // Find the xsl:stylesheet or xsl:transform with this reference
599 if (_target.charAt(0) == '#') {
600 SyntaxTreeNode element = findStylesheet(root, _target.substring(1));
601 if (element == null) {
602 ErrorMsg msg = new ErrorMsg(ErrorMsg.MISSING_XSLT_TARGET_ERR,
603 _target, root);
604 throw new CompilerException(msg.toString());
605 }
606 return(element);
607 }
608 else {
609 try {
610 String path = _target;
611 if (path.indexOf(":")==-1) {
612 path = "file:" + path;
613 }
614 path = SystemIDResolver.getAbsoluteURI(path);
615 String accessError = SecuritySupport.checkAccess(path,
616 (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET),
617 XalanConstants.ACCESS_EXTERNAL_ALL);
618 if (accessError != null) {
619 ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR,
620 SecuritySupport.sanitizePath(_target), accessError,
621 root);
622 throw new CompilerException(msg.toString());
623 }
624 } catch (IOException ex) {
625 throw new CompilerException(ex);
626 }
627
628 return(loadExternalStylesheet(_target));
629 }
630 }
631
632 /**
633 * Find a Stylesheet element with a specific ID attribute value.
634 * This method is used to find a Stylesheet node that is referred
635 * in a <?xml-stylesheet ... ?> processing instruction.
636 */
637 private SyntaxTreeNode findStylesheet(SyntaxTreeNode root, String href) {
638
639 if (root == null) return null;
640
641 if (root instanceof Stylesheet) {
642 String id = root.getAttribute("id");
643 if (id.equals(href)) return root;
644 }
645 List<SyntaxTreeNode> children = root.getContents();
646 if (children != null) {
647 final int count = children.size();
648 for (int i = 0; i < count; i++) {
649 SyntaxTreeNode child = children.get(i);
650 SyntaxTreeNode node = findStylesheet(child, href);
651 if (node != null) return node;
652 }
653 }
654 return null;
655 }
656
657 /**
658 * For embedded stylesheets: Load an external file with stylesheet
659 */
660 private SyntaxTreeNode loadExternalStylesheet(String location)
661 throws CompilerException {
662
663 InputSource source;
664
665 // Check if the location is URL or a local file
666 if ((new File(location)).exists())
667 source = new InputSource("file:"+location);
668 else
669 source = new InputSource(location);
670
671 SyntaxTreeNode external = parse(source);
672 return(external);
673 }
674
675 private void initAttrTable(String elementName, String[] attrs) {
676 _instructionAttrs.put(getQName(XSLT_URI, XSL, elementName).getStringRep(),
677 attrs);
678 }
679
680 private void initInstructionAttrs() {
681 initAttrTable("template",
682 new String[] {"match", "name", "priority", "mode"});
683 initAttrTable("stylesheet",
684 new String[] {"id", "version", "extension-element-prefixes",
685 "exclude-result-prefixes"});
686 initAttrTable("transform",
687 new String[] {"id", "version", "extension-element-prefixes",
688 "exclude-result-prefixes"});
689 initAttrTable("text", new String[] {"disable-output-escaping"});
690 initAttrTable("if", new String[] {"test"});
691 initAttrTable("choose", new String[] {});
692 initAttrTable("when", new String[] {"test"});
693 initAttrTable("otherwise", new String[] {});
694 initAttrTable("for-each", new String[] {"select"});
695 initAttrTable("message", new String[] {"terminate"});
696 initAttrTable("number",
697 new String[] {"level", "count", "from", "value", "format", "lang",
698 "letter-value", "grouping-separator", "grouping-size"});
699 initAttrTable("comment", new String[] {});
700 initAttrTable("copy", new String[] {"use-attribute-sets"});
701 initAttrTable("copy-of", new String[] {"select"});
702 initAttrTable("param", new String[] {"name", "select"});
703 initAttrTable("with-param", new String[] {"name", "select"});
704 initAttrTable("variable", new String[] {"name", "select"});
705 initAttrTable("output",
706 new String[] {"method", "version", "encoding",
707 "omit-xml-declaration", "standalone", "doctype-public",
708 "doctype-system", "cdata-section-elements", "indent",
709 "media-type"});
710 initAttrTable("sort",
711 new String[] {"select", "order", "case-order", "lang", "data-type"});
712 initAttrTable("key", new String[] {"name", "match", "use"});
713 initAttrTable("fallback", new String[] {});
714 initAttrTable("attribute", new String[] {"name", "namespace"});
715 initAttrTable("attribute-set",
716 new String[] {"name", "use-attribute-sets"});
717 initAttrTable("value-of",
718 new String[] {"select", "disable-output-escaping"});
719 initAttrTable("element",
720 new String[] {"name", "namespace", "use-attribute-sets"});
721 initAttrTable("call-template", new String[] {"name"});
722 initAttrTable("apply-templates", new String[] {"select", "mode"});
723 initAttrTable("apply-imports", new String[] {});
724 initAttrTable("decimal-format",
725 new String[] {"name", "decimal-separator", "grouping-separator",
726 "infinity", "minus-sign", "NaN", "percent", "per-mille",
727 "zero-digit", "digit", "pattern-separator"});
728 initAttrTable("import", new String[] {"href"});
729 initAttrTable("include", new String[] {"href"});
730 initAttrTable("strip-space", new String[] {"elements"});
731 initAttrTable("preserve-space", new String[] {"elements"});
732 initAttrTable("processing-instruction", new String[] {"name"});
733 initAttrTable("namespace-alias",
734 new String[] {"stylesheet-prefix", "result-prefix"});
735 }
736
737 /**
738 * Initialize the _instructionClasses map, which maps XSL element
739 * names to Java classes in this package.
740 */
741 private void initStdClasses() {
742 initStdClass("template", "Template");
743 initStdClass("stylesheet", "Stylesheet");
744 initStdClass("transform", "Stylesheet");
745 initStdClass("text", "Text");
746 initStdClass("if", "If");
747 initStdClass("choose", "Choose");
748 initStdClass("when", "When");
749 initStdClass("otherwise", "Otherwise");
750 initStdClass("for-each", "ForEach");
751 initStdClass("message", "Message");
752 initStdClass("number", "Number");
753 initStdClass("comment", "Comment");
754 initStdClass("copy", "Copy");
755 initStdClass("copy-of", "CopyOf");
756 initStdClass("param", "Param");
757 initStdClass("with-param", "WithParam");
758 initStdClass("variable", "Variable");
759 initStdClass("output", "Output");
760 initStdClass("sort", "Sort");
761 initStdClass("key", "Key");
762 initStdClass("fallback", "Fallback");
763 initStdClass("attribute", "XslAttribute");
764 initStdClass("attribute-set", "AttributeSet");
765 initStdClass("value-of", "ValueOf");
766 initStdClass("element", "XslElement");
767 initStdClass("call-template", "CallTemplate");
768 initStdClass("apply-templates", "ApplyTemplates");
769 initStdClass("apply-imports", "ApplyImports");
770 initStdClass("decimal-format", "DecimalFormatting");
771 initStdClass("import", "Import");
772 initStdClass("include", "Include");
773 initStdClass("strip-space", "Whitespace");
774 initStdClass("preserve-space", "Whitespace");
775 initStdClass("processing-instruction", "ProcessingInstruction");
776 initStdClass("namespace-alias", "NamespaceAlias");
777 }
778
779 private void initStdClass(String elementName, String className) {
780 _instructionClasses.put(getQName(XSLT_URI, XSL, elementName).getStringRep(),
781 COMPILER_PACKAGE + '.' + className);
782 }
783
784 public boolean elementSupported(String namespace, String localName) {
785 return(_instructionClasses.get(getQName(namespace, XSL, localName).getStringRep()) != null);
786 }
787
788 public boolean functionSupported(String fname) {
789 return(_symbolTable.lookupPrimop(fname) != null);
790 }
791
792 private void initExtClasses() {
793 initExtClass("output", "TransletOutput");
794 initExtClass(REDIRECT_URI, "write", "TransletOutput");
795 }
796
797 private void initExtClass(String elementName, String className) {
798 _instructionClasses.put(getQName(TRANSLET_URI, TRANSLET, elementName).getStringRep(),
799 COMPILER_PACKAGE + '.' + className);
800 }
801
802 private void initExtClass(String namespace, String elementName, String className) {
803 _instructionClasses.put(getQName(namespace, TRANSLET, elementName).getStringRep(),
804 COMPILER_PACKAGE + '.' + className);
805 }
806
807 /**
808 * Add primops and base functions to the symbol table.
809 */
810 @SuppressWarnings("unused")
811 private void initSymbolTable() {
812 MethodType I_V = new MethodType(Type.Int, Type.Void);
813 MethodType I_R = new MethodType(Type.Int, Type.Real);
814 MethodType I_S = new MethodType(Type.Int, Type.String);
815 MethodType I_D = new MethodType(Type.Int, Type.NodeSet);
816 MethodType R_I = new MethodType(Type.Real, Type.Int);
817 MethodType R_V = new MethodType(Type.Real, Type.Void);
818 MethodType R_R = new MethodType(Type.Real, Type.Real);
819 MethodType R_D = new MethodType(Type.Real, Type.NodeSet);
820 MethodType R_O = new MethodType(Type.Real, Type.Reference);
821 MethodType I_I = new MethodType(Type.Int, Type.Int);
822 MethodType D_O = new MethodType(Type.NodeSet, Type.Reference);
823 MethodType D_V = new MethodType(Type.NodeSet, Type.Void);
824 MethodType D_S = new MethodType(Type.NodeSet, Type.String);
825 MethodType D_D = new MethodType(Type.NodeSet, Type.NodeSet);
826 MethodType A_V = new MethodType(Type.Node, Type.Void);
827 MethodType S_V = new MethodType(Type.String, Type.Void);
828 MethodType S_S = new MethodType(Type.String, Type.String);
829 MethodType S_A = new MethodType(Type.String, Type.Node);
830 MethodType S_D = new MethodType(Type.String, Type.NodeSet);
831 MethodType S_O = new MethodType(Type.String, Type.Reference);
832 MethodType B_O = new MethodType(Type.Boolean, Type.Reference);
833 MethodType B_V = new MethodType(Type.Boolean, Type.Void);
834 MethodType B_B = new MethodType(Type.Boolean, Type.Boolean);
835 MethodType B_S = new MethodType(Type.Boolean, Type.String);
836 MethodType D_X = new MethodType(Type.NodeSet, Type.Object);
837 MethodType R_RR = new MethodType(Type.Real, Type.Real, Type.Real);
838 MethodType I_II = new MethodType(Type.Int, Type.Int, Type.Int);
839 MethodType B_RR = new MethodType(Type.Boolean, Type.Real, Type.Real);
840 MethodType B_II = new MethodType(Type.Boolean, Type.Int, Type.Int);
841 MethodType S_SS = new MethodType(Type.String, Type.String, Type.String);
842 MethodType S_DS = new MethodType(Type.String, Type.Real, Type.String);
843 MethodType S_SR = new MethodType(Type.String, Type.String, Type.Real);
844 MethodType O_SO = new MethodType(Type.Reference, Type.String, Type.Reference);
845
846 MethodType D_SS =
847 new MethodType(Type.NodeSet, Type.String, Type.String);
848 MethodType D_SD =
849 new MethodType(Type.NodeSet, Type.String, Type.NodeSet);
850 MethodType B_BB =
851 new MethodType(Type.Boolean, Type.Boolean, Type.Boolean);
852 MethodType B_SS =
853 new MethodType(Type.Boolean, Type.String, Type.String);
854 MethodType S_SD =
855 new MethodType(Type.String, Type.String, Type.NodeSet);
856 MethodType S_DSS =
857 new MethodType(Type.String, Type.Real, Type.String, Type.String);
858 MethodType S_SRR =
859 new MethodType(Type.String, Type.String, Type.Real, Type.Real);
860 MethodType S_SSS =
861 new MethodType(Type.String, Type.String, Type.String, Type.String);
862
863 /*
864 * Standard functions: implemented but not in this table concat().
865 * When adding a new function make sure to uncomment
866 * the corresponding line in <tt>FunctionAvailableCall</tt>.
867 */
868
869 // The following functions are inlined
870
871 _symbolTable.addPrimop("current", A_V);
872 _symbolTable.addPrimop("last", I_V);
873 _symbolTable.addPrimop("position", I_V);
874 _symbolTable.addPrimop("true", B_V);
875 _symbolTable.addPrimop("false", B_V);
876 _symbolTable.addPrimop("not", B_B);
877 _symbolTable.addPrimop("name", S_V);
878 _symbolTable.addPrimop("name", S_A);
879 _symbolTable.addPrimop("generate-id", S_V);
880 _symbolTable.addPrimop("generate-id", S_A);
881 _symbolTable.addPrimop("ceiling", R_R);
882 _symbolTable.addPrimop("floor", R_R);
883 _symbolTable.addPrimop("round", R_R);
884 _symbolTable.addPrimop("contains", B_SS);
885 _symbolTable.addPrimop("number", R_O);
886 _symbolTable.addPrimop("number", R_V);
887 _symbolTable.addPrimop("boolean", B_O);
888 _symbolTable.addPrimop("string", S_O);
889 _symbolTable.addPrimop("string", S_V);
890 _symbolTable.addPrimop("translate", S_SSS);
891 _symbolTable.addPrimop("string-length", I_V);
892 _symbolTable.addPrimop("string-length", I_S);
893 _symbolTable.addPrimop("starts-with", B_SS);
894 _symbolTable.addPrimop("format-number", S_DS);
895 _symbolTable.addPrimop("format-number", S_DSS);
896 _symbolTable.addPrimop("unparsed-entity-uri", S_S);
897 _symbolTable.addPrimop("key", D_SS);
898 _symbolTable.addPrimop("key", D_SD);
899 _symbolTable.addPrimop("id", D_S);
900 _symbolTable.addPrimop("id", D_D);
901 _symbolTable.addPrimop("namespace-uri", S_V);
902 _symbolTable.addPrimop("function-available", B_S);
903 _symbolTable.addPrimop("element-available", B_S);
904 _symbolTable.addPrimop("document", D_S);
905 _symbolTable.addPrimop("document", D_V);
906
907 // The following functions are implemented in the basis library
908 _symbolTable.addPrimop("count", I_D);
909 _symbolTable.addPrimop("sum", R_D);
910 _symbolTable.addPrimop("local-name", S_V);
911 _symbolTable.addPrimop("local-name", S_D);
912 _symbolTable.addPrimop("namespace-uri", S_V);
913 _symbolTable.addPrimop("namespace-uri", S_D);
914 _symbolTable.addPrimop("substring", S_SR);
915 _symbolTable.addPrimop("substring", S_SRR);
916 _symbolTable.addPrimop("substring-after", S_SS);
917 _symbolTable.addPrimop("substring-before", S_SS);
918 _symbolTable.addPrimop("normalize-space", S_V);
919 _symbolTable.addPrimop("normalize-space", S_S);
920 _symbolTable.addPrimop("system-property", S_S);
921
922 // Extensions
923 _symbolTable.addPrimop("nodeset", D_O);
924 _symbolTable.addPrimop("objectType", S_O);
925 _symbolTable.addPrimop("cast", O_SO);
926
927 // Operators +, -, *, /, % defined on real types.
928 _symbolTable.addPrimop("+", R_RR);
929 _symbolTable.addPrimop("-", R_RR);
930 _symbolTable.addPrimop("*", R_RR);
931 _symbolTable.addPrimop("/", R_RR);
932 _symbolTable.addPrimop("%", R_RR);
933
934 // Operators +, -, * defined on integer types.
935 // Operators / and % are not defined on integers (may cause exception)
936 _symbolTable.addPrimop("+", I_II);
937 _symbolTable.addPrimop("-", I_II);
938 _symbolTable.addPrimop("*", I_II);
939
940 // Operators <, <= >, >= defined on real types.
941 _symbolTable.addPrimop("<", B_RR);
942 _symbolTable.addPrimop("<=", B_RR);
943 _symbolTable.addPrimop(">", B_RR);
944 _symbolTable.addPrimop(">=", B_RR);
945
946 // Operators <, <= >, >= defined on int types.
947 _symbolTable.addPrimop("<", B_II);
948 _symbolTable.addPrimop("<=", B_II);
949 _symbolTable.addPrimop(">", B_II);
950 _symbolTable.addPrimop(">=", B_II);
951
952 // Operators <, <= >, >= defined on boolean types.
953 _symbolTable.addPrimop("<", B_BB);
954 _symbolTable.addPrimop("<=", B_BB);
955 _symbolTable.addPrimop(">", B_BB);
956 _symbolTable.addPrimop(">=", B_BB);
957
958 // Operators 'and' and 'or'.
959 _symbolTable.addPrimop("or", B_BB);
960 _symbolTable.addPrimop("and", B_BB);
961
962 // Unary minus.
963 _symbolTable.addPrimop("u-", R_R);
964 _symbolTable.addPrimop("u-", I_I);
965 }
966
967 public SymbolTable getSymbolTable() {
968 return _symbolTable;
969 }
970
971 public Template getTemplate() {
972 return _template;
973 }
974
975 public void setTemplate(Template template) {
976 _template = template;
977 }
978
979 private int _templateIndex = 0;
980
981 public int getTemplateIndex() {
982 return(_templateIndex++);
983 }
984
985 /**
986 * Creates a new node in the abstract syntax tree. This node can be
987 * o) a supported XSLT 1.0 element
988 * o) an unsupported XSLT element (post 1.0)
989 * o) a supported XSLT extension
990 * o) an unsupported XSLT extension
991 * o) a literal result element (not an XSLT element and not an extension)
992 * Unsupported elements do not directly generate an error. We have to wait
993 * until we have received all child elements of an unsupported element to
994 * see if any <xsl:fallback> elements exist.
995 */
996
997 private boolean versionIsOne = true;
998
999 public SyntaxTreeNode makeInstance(String uri, String prefix,
1000 String local, Attributes attributes)
1001 {
1002 SyntaxTreeNode node = null;
1003 QName qname = getQName(uri, prefix, local);
1004 String className = _instructionClasses.get(qname.getStringRep());
1005
1006 if (className != null) {
1007 try {
1008 final Class<?> clazz = ObjectFactory.findProviderClass(className, true);
1009 node = (SyntaxTreeNode)clazz.getDeclaredConstructor().newInstance();
1010 node.setQName(qname);
1011 node.setParser(this);
1012 if (_locator != null) {
1013 node.setLineNumber(getLineNumber());
1014 }
1015 if (node instanceof Stylesheet) {
1016 _xsltc.setStylesheet((Stylesheet)node);
1017 }
1018 checkForSuperfluousAttributes(node, attributes);
1019 }
1020 catch (ClassNotFoundException e) {
1021 ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, node);
1022 reportError(ERROR, err);
1023 }
1024 catch (Exception e) {
1025 ErrorMsg err = new ErrorMsg(ErrorMsg.INTERNAL_ERR,
1026 e.getMessage(), node);
1027 reportError(FATAL, err);
1028 }
1029 }
1030 else {
1031 if (uri != null) {
1032 // Check if the element belongs in our namespace
1033 if (uri.equals(XSLT_URI)) {
1034 node = new UnsupportedElement(uri, prefix, local, false);
1035 UnsupportedElement element = (UnsupportedElement)node;
1036 ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_XSL_ERR,
1037 getLineNumber(),local);
1038 element.setErrorMessage(msg);
1039 if (versionIsOne) {
1040 reportError(UNSUPPORTED,msg);
1041 }
1042 }
1043 // Check if this is an XSLTC extension element
1044 else if (uri.equals(TRANSLET_URI)) {
1045 node = new UnsupportedElement(uri, prefix, local, true);
1046 UnsupportedElement element = (UnsupportedElement)node;
1047 ErrorMsg msg = new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
1048 getLineNumber(),local);
1049 element.setErrorMessage(msg);
1050 }
1051 // Check if this is an extension of some other XSLT processor
1052 else {
1053 Stylesheet sheet = _xsltc.getStylesheet();
1054 if ((sheet != null) && (sheet.isExtension(uri))) {
1055 if (sheet != _parentStack.peek()) {
1056 node = new UnsupportedElement(uri, prefix, local, true);
1057 UnsupportedElement elem = (UnsupportedElement)node;
1058 ErrorMsg msg =
1059 new ErrorMsg(ErrorMsg.UNSUPPORTED_EXT_ERR,
1060 getLineNumber(),
1061 prefix+":"+local);
1062 elem.setErrorMessage(msg);
1063 }
1064 }
1065 }
1066 }
1067 if (node == null) {
1068 node = new LiteralElement();
1069 node.setLineNumber(getLineNumber());
1070 }
1071 }
1072 if ((node != null) && (node instanceof LiteralElement)) {
1073 ((LiteralElement)node).setQName(qname);
1074 }
1075 return(node);
1076 }
1077
1078 /**
1079 * checks the list of attributes against a list of allowed attributes
1080 * for a particular element node.
1081 */
1082 private void checkForSuperfluousAttributes(SyntaxTreeNode node,
1083 Attributes attrs)
1084 {
1085 QName qname = node.getQName();
1086 boolean isStylesheet = (node instanceof Stylesheet);
1087 String[] legal = _instructionAttrs.get(qname.getStringRep());
1088 if (versionIsOne && legal != null) {
1089 int j;
1090 final int n = attrs.getLength();
1091
1092 for (int i = 0; i < n; i++) {
1093 final String attrQName = attrs.getQName(i);
1094
1095 if (isStylesheet && attrQName.equals("version")) {
1096 versionIsOne = attrs.getValue(i).equals("1.0");
1097 }
1098
1099 // Ignore if special or if it has a prefix
1100 if (attrQName.startsWith("xml") ||
1101 attrQName.indexOf(':') > 0) continue;
1102
1103 for (j = 0; j < legal.length; j++) {
1104 if (attrQName.equalsIgnoreCase(legal[j])) {
1105 break;
1106 }
1107 }
1108 if (j == legal.length) {
1109 final ErrorMsg err =
1110 new ErrorMsg(ErrorMsg.ILLEGAL_ATTRIBUTE_ERR,
1111 attrQName, node);
1112 // Workaround for the TCK failure ErrorListener.errorTests.error001..
1113 err.setWarningError(true);
1114 reportError(WARNING, err);
1115 }
1116 }
1117 }
1118 }
1119
1120
1121 /**
1122 * Parse an XPath expression:
1123 * @param parent - XSL element where the expression occured
1124 * @param exp - textual representation of the expression
1125 */
1126 public Expression parseExpression(SyntaxTreeNode parent, String exp) {
1127 return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, null);
1128 }
1129
1130 /**
1131 * Parse an XPath expression:
1132 * @param parent - XSL element where the expression occured
1133 * @param attr - name of this element's attribute to get expression from
1134 * @param def - default expression (if the attribute was not found)
1135 */
1136 public Expression parseExpression(SyntaxTreeNode parent,
1137 String attr, String def) {
1138 // Get the textual representation of the expression (if any)
1139 String exp = parent.getAttribute(attr);
1140 // Use the default expression if none was found
1141 if ((exp.length() == 0) && (def != null)) exp = def;
1142 // Invoke the XPath parser
1143 return (Expression)parseTopLevel(parent, "<EXPRESSION>"+exp, exp);
1144 }
1145
1146 /**
1147 * Parse an XPath pattern:
1148 * @param parent - XSL element where the pattern occured
1149 * @param pattern - textual representation of the pattern
1150 */
1151 public Pattern parsePattern(SyntaxTreeNode parent, String pattern) {
1152 return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1153 }
1154
1155 /**
1156 * Parse an XPath pattern:
1157 * @param parent - XSL element where the pattern occured
1158 * @param attr - name of this element's attribute to get pattern from
1159 * @param def - default pattern (if the attribute was not found)
1160 */
1161 public Pattern parsePattern(SyntaxTreeNode parent,
1162 String attr, String def) {
1163 // Get the textual representation of the pattern (if any)
1164 String pattern = parent.getAttribute(attr);
1165 // Use the default pattern if none was found
1166 if ((pattern.length() == 0) && (def != null)) pattern = def;
1167 // Invoke the XPath parser
1168 return (Pattern)parseTopLevel(parent, "<PATTERN>"+pattern, pattern);
1169 }
1170
1171 /**
1172 * Parse an XPath expression or pattern using the generated XPathParser
1173 * The method will return a Dummy node if the XPath parser fails.
1174 */
1175 private SyntaxTreeNode parseTopLevel(SyntaxTreeNode parent, String text,
1176 String expression) {
1177 int line = getLineNumber();
1178
1179 try {
1180 _xpathParser.setScanner(new XPathLexer(new StringReader(text)));
1181 Symbol result = _xpathParser.parse(expression, line);
1182 if (result != null) {
1183 final SyntaxTreeNode node = (SyntaxTreeNode)result.value;
1184 if (node != null) {
1185 node.setParser(this);
1186 node.setParent(parent);
1187 node.setLineNumber(line);
1188 return node;
1189 }
1190 }
1191 reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR,
1192 expression, parent));
1193 }
1194 catch (Exception e) {
1195 if (_xsltc.debug()) e.printStackTrace();
1196 reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR,
1197 expression, parent));
1198 }
1199
1200 // Return a dummy pattern (which is an expression)
1201 SyntaxTreeNode.Dummy.setParser(this);
1202 return SyntaxTreeNode.Dummy;
1203 }
1204
1205 /************************ ERROR HANDLING SECTION ************************/
1206
1207 /**
1208 * Returns true if there were any errors during compilation
1209 */
1210 public boolean errorsFound() {
1211 return _errors.size() > 0;
1212 }
1213
1214 /**
1215 * Prints all compile-time errors
1216 */
1217 public void printErrors() {
1218 final int size = _errors.size();
1219 if (size > 0) {
1220 System.err.println(new ErrorMsg(ErrorMsg.COMPILER_ERROR_KEY));
1221 for (int i = 0; i < size; i++) {
1222 System.err.println(" " + _errors.get(i));
1223 }
1224 }
1225 }
1226
1227 /**
1228 * Prints all compile-time warnings
1229 */
1230 public void printWarnings() {
1231 final int size = _warnings.size();
1232 if (size > 0) {
1233 System.err.println(new ErrorMsg(ErrorMsg.COMPILER_WARNING_KEY));
1234 for (int i = 0; i < size; i++) {
1235 System.err.println(" " + _warnings.get(i));
1236 }
1237 }
1238 }
1239
1240 /**
1241 * Common error/warning message handler
1242 */
1243 public void reportError(final int category, final ErrorMsg error) {
1244 switch (category) {
1245 case Constants.INTERNAL:
1246 // Unexpected internal errors, such as null-ptr exceptions, etc.
1247 // Immediately terminates compilation, no translet produced
1248 _errors.add(error);
1249 break;
1250 case Constants.UNSUPPORTED:
1251 // XSLT elements that are not implemented and unsupported ext.
1252 // Immediately terminates compilation, no translet produced
1253 _errors.add(error);
1254 break;
1255 case Constants.FATAL:
1256 // Fatal error in the stylesheet input (parsing or content)
1257 // Immediately terminates compilation, no translet produced
1258 _errors.add(error);
1259 break;
1260 case Constants.ERROR:
1261 // Other error in the stylesheet input (parsing or content)
1262 // Does not terminate compilation, no translet produced
1263 _errors.add(error);
1264 break;
1265 case Constants.WARNING:
1266 // Other error in the stylesheet input (content errors only)
1267 // Does not terminate compilation, a translet is produced
1268 _warnings.add(error);
1269 break;
1270 }
1271 }
1272
1273 public ArrayList<ErrorMsg> getErrors() {
1274 return _errors;
1275 }
1276
1277 public ArrayList<ErrorMsg> getWarnings() {
1278 return _warnings;
1279 }
1280
1281 /************************ SAX2 ContentHandler INTERFACE *****************/
1282
1283 private Stack<SyntaxTreeNode> _parentStack = null;
1284 private Map<String, String> _prefixMapping = null;
1285
1286 /**
1287 * SAX2: Receive notification of the beginning of a document.
1288 */
1289 public void startDocument() {
1290 _root = null;
1291 _target = null;
1292 _prefixMapping = null;
1293 _parentStack = new Stack<>();
1294 }
1295
1296 /**
1297 * SAX2: Receive notification of the end of a document.
1298 */
1299 public void endDocument() { }
1300
1301
1302 /**
1303 * SAX2: Begin the scope of a prefix-URI Namespace mapping.
1304 * This has to be passed on to the symbol table!
1305 */
1306 public void startPrefixMapping(String prefix, String uri) {
1307 if (_prefixMapping == null) {
1308 _prefixMapping = new HashMap<>();
1309 }
1310 _prefixMapping.put(prefix, uri);
1311 }
1312
1313 /**
1314 * SAX2: End the scope of a prefix-URI Namespace mapping.
1315 * This has to be passed on to the symbol table!
1316 */
1317 public void endPrefixMapping(String prefix) { }
1318
1319 /**
1320 * SAX2: Receive notification of the beginning of an element.
1321 * The parser may re-use the attribute list that we're passed so
1322 * we clone the attributes in our own Attributes implementation
1323 */
1324 public void startElement(String uri, String localname,
1325 String qname, Attributes attributes)
1326 throws SAXException {
1327 final int col = qname.lastIndexOf(':');
1328 final String prefix = (col == -1) ? null : qname.substring(0, col);
1329
1330 SyntaxTreeNode element = makeInstance(uri, prefix,
1331 localname, attributes);
1332 if (element == null) {
1333 ErrorMsg err = new ErrorMsg(ErrorMsg.ELEMENT_PARSE_ERR,
1334 prefix+':'+localname);
1335 throw new SAXException(err.toString());
1336 }
1337
1338 // If this is the root element of the XML document we need to make sure
1339 // that it contains a definition of the XSL namespace URI
1340 if (_root == null) {
1341 if ((_prefixMapping == null) ||
1342 (_prefixMapping.containsValue(Constants.XSLT_URI) == false))
1343 _rootNamespaceDef = false;
1344 else
1345 _rootNamespaceDef = true;
1346 _root = element;
1347 }
1348 else {
1349 SyntaxTreeNode parent = _parentStack.peek();
1350
1351 if (element.getClass().isAssignableFrom(Import.class) &&
1352 parent.notTypeOf(Import.class)) {
1353 ErrorMsg err = new ErrorMsg(ErrorMsg.IMPORT_PRECEDE_OTHERS_ERR,
1354 prefix+':'+localname);
1355 throw new SAXException(err.toString());
1356 }
1357
1358 parent.addElement(element);
1359 element.setParent(parent);
1360 }
1361 element.setAttributes(new AttributesImpl(attributes));
1362 element.setPrefixMapping(_prefixMapping);
1363
1364 if (element instanceof Stylesheet) {
1365 // Extension elements and excluded elements have to be
1366 // handled at this point in order to correctly generate
1367 // Fallback elements from <xsl:fallback>s.
1368 getSymbolTable().setCurrentNode(element);
1369 ((Stylesheet)element).declareExtensionPrefixes(this);
1370 }
1371
1372 _prefixMapping = null;
1373 _parentStack.push(element);
1374 }
1375
1376 /**
1377 * SAX2: Receive notification of the end of an element.
1378 */
1379 public void endElement(String uri, String localname, String qname) {
1380 _parentStack.pop();
1381 }
1382
1383 /**
1384 * SAX2: Receive notification of character data.
1385 */
1386 public void characters(char[] ch, int start, int length) {
1387 String string = new String(ch, start, length);
1388 SyntaxTreeNode parent = _parentStack.peek();
1389
1390 if (string.length() == 0) return;
1391
1392 // If this text occurs within an <xsl:text> element we append it
1393 // as-is to the existing text element
1394 if (parent instanceof Text) {
1395 ((Text)parent).setText(string);
1396 return;
1397 }
1398
1399 // Ignore text nodes that occur directly under <xsl:stylesheet>
1400 if (parent instanceof Stylesheet) return;
1401
1402 SyntaxTreeNode bro = parent.lastChild();
1403 if ((bro != null) && (bro instanceof Text)) {
1404 Text text = (Text)bro;
1405 if (!text.isTextElement()) {
1406 if ((length > 1) || ( ((int)ch[0]) < 0x100)) {
1407 text.setText(string);
1408 return;
1409 }
1410 }
1411 }
1412
1413 // Add it as a regular text node otherwise
1414 parent.addElement(new Text(string));
1415 }
1416
1417 private String getTokenValue(String token) {
1418 final int start = token.indexOf('"');
1419 final int stop = token.lastIndexOf('"');
1420 return token.substring(start+1, stop);
1421 }
1422
1423 /**
1424 * SAX2: Receive notification of a processing instruction.
1425 * These require special handling for stylesheet PIs.
1426 */
1427 public void processingInstruction(String name, String value) {
1428 // We only handle the <?xml-stylesheet ...?> PI
1429 if ((_target == null) && (name.equals("xml-stylesheet"))) {
1430
1431 String href = null; // URI of stylesheet found
1432 String media = null; // Media of stylesheet found
1433 String title = null; // Title of stylesheet found
1434 String charset = null; // Charset of stylesheet found
1435
1436 // Get the attributes from the processing instruction
1437 StringTokenizer tokens = new StringTokenizer(value);
1438 while (tokens.hasMoreElements()) {
1439 String token = (String)tokens.nextElement();
1440 if (token.startsWith("href"))
1441 href = getTokenValue(token);
1442 else if (token.startsWith("media"))
1443 media = getTokenValue(token);
1444 else if (token.startsWith("title"))
1445 title = getTokenValue(token);
1446 else if (token.startsWith("charset"))
1447 charset = getTokenValue(token);
1448 }
1449
1450 // Set the target to this PI's href if the parameters are
1451 // null or match the corresponding attributes of this PI.
1452 if ( ((_PImedia == null) || (_PImedia.equals(media))) &&
1453 ((_PItitle == null) || (_PImedia.equals(title))) &&
1454 ((_PIcharset == null) || (_PImedia.equals(charset))) ) {
1455 _target = href;
1456 }
1457 }
1458 }
1459
1460 /**
1461 * IGNORED - all ignorable whitespace is ignored
1462 */
1463 public void ignorableWhitespace(char[] ch, int start, int length) { }
1464
1465 /**
1466 * IGNORED - we do not have to do anything with skipped entities
1467 */
1468 public void skippedEntity(String name) { }
1469
1470 /**
1471 * Store the document locator to later retrieve line numbers of all
1472 * elements from the stylesheet
1473 */
1474 public void setDocumentLocator(Locator locator) {
1475 _locator = locator;
1476 }
1477
1478 /**
1479 * Get the line number, or zero
1480 * if there is no _locator.
1481 */
1482 private int getLineNumber() {
1483 int line = 0;
1484 if (_locator != null)
1485 line = _locator.getLineNumber();
1486 return line;
1487 }
1488
1489 }
--- EOF ---