Stylesheet.java 58 KB

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