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