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