1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.templates;
  58. import org.apache.xml.utils.res.XResourceBundle;
  59. //import org.w3c.dom.*;
  60. //import org.w3c.dom.traversal.NodeIterator;
  61. import org.apache.xml.dtm.DTM;
  62. import org.apache.xml.dtm.DTMIterator;
  63. import org.w3c.dom.NamedNodeMap;
  64. import org.w3c.dom.Node;
  65. //import org.w3c.dom.xpath.XPathResult;
  66. import org.xml.sax.*;
  67. import java.util.*;
  68. import java.text.DecimalFormatSymbols;
  69. import java.text.NumberFormat;
  70. import java.text.DecimalFormat;
  71. import org.apache.xpath.*;
  72. import org.apache.xpath.objects.XObject;
  73. import org.apache.xpath.compiler.XPathParser;
  74. import org.apache.xml.utils.PrefixResolver;
  75. import org.apache.xml.utils.PrefixResolverDefault;
  76. import org.apache.xml.utils.QName;
  77. import org.apache.xml.utils.StringBufferPool;
  78. import org.apache.xml.utils.FastStringBuffer;
  79. import org.apache.xalan.res.*;
  80. import org.apache.xalan.transformer.DecimalToRoman;
  81. import org.apache.xalan.transformer.CountersTable;
  82. import org.apache.xalan.transformer.ResultTreeHandler;
  83. import org.apache.xalan.transformer.TransformerImpl;
  84. import org.apache.xml.utils.NodeVector;
  85. import javax.xml.transform.TransformerException;
  86. // import org.apache.xalan.dtm.*;
  87. /**
  88. * <meta name="usage" content="advanced"/>
  89. * Implement xsl:number.
  90. * <pre>
  91. * <!ELEMENT xsl:number EMPTY>
  92. * <!ATTLIST xsl:number
  93. * level (single|multiple|any) "single"
  94. * count %pattern; #IMPLIED
  95. * from %pattern; #IMPLIED
  96. * value %expr; #IMPLIED
  97. * format %avt; '1'
  98. * lang %avt; #IMPLIED
  99. * letter-value %avt; #IMPLIED
  100. * grouping-separator %avt; #IMPLIED
  101. * grouping-size %avt; #IMPLIED
  102. * >
  103. * </pre>
  104. * @see <a href="http://www.w3.org/TR/xslt#number">number in XSLT Specification</a>
  105. */
  106. public class ElemNumber extends ElemTemplateElement
  107. {
  108. private class MyPrefixResolver implements PrefixResolver {
  109. DTM dtm;
  110. int handle;
  111. boolean handleNullPrefix;
  112. /**
  113. * Constructor for MyPrefixResolver.
  114. * @param xpathExpressionContext
  115. */
  116. public MyPrefixResolver(Node xpathExpressionContext, DTM dtm, int handle, boolean handleNullPrefix) {
  117. this.dtm = dtm;
  118. this.handle = handle;
  119. this.handleNullPrefix = handleNullPrefix;
  120. }
  121. /**
  122. * @see PrefixResolver#getNamespaceForPrefix(String, Node)
  123. */
  124. public String getNamespaceForPrefix(String prefix) {
  125. return dtm.getNamespaceURI(handle);
  126. }
  127. /**
  128. * @see PrefixResolver#getNamespaceForPrefix(String, Node)
  129. * this shouldn't get called.
  130. */
  131. public String getNamespaceForPrefix(String prefix, Node context) {
  132. return getNamespaceForPrefix(prefix);
  133. }
  134. /**
  135. * @see PrefixResolver#getBaseIdentifier()
  136. */
  137. public String getBaseIdentifier() {
  138. return ElemNumber.this.getBaseIdentifier();
  139. }
  140. /**
  141. * @see PrefixResolver#handlesNullPrefixes()
  142. */
  143. public boolean handlesNullPrefixes() {
  144. return handleNullPrefix;
  145. }
  146. }
  147. /**
  148. * Only nodes are counted that match this pattern.
  149. * @serial
  150. */
  151. private XPath m_countMatchPattern = null;
  152. /**
  153. * Set the "count" attribute.
  154. * The count attribute is a pattern that specifies what nodes
  155. * should be counted at those levels. If count attribute is not
  156. * specified, then it defaults to the pattern that matches any
  157. * node with the same node type as the current node and, if the
  158. * current node has an expanded-name, with the same expanded-name
  159. * as the current node.
  160. *
  161. * @param v Value to set for "count" attribute.
  162. */
  163. public void setCount(XPath v)
  164. {
  165. m_countMatchPattern = v;
  166. }
  167. /**
  168. * Get the "count" attribute.
  169. * The count attribute is a pattern that specifies what nodes
  170. * should be counted at those levels. If count attribute is not
  171. * specified, then it defaults to the pattern that matches any
  172. * node with the same node type as the current node and, if the
  173. * current node has an expanded-name, with the same expanded-name
  174. * as the current node.
  175. *
  176. * @return Value of "count" attribute.
  177. */
  178. public XPath getCount()
  179. {
  180. return m_countMatchPattern;
  181. }
  182. /**
  183. * Specifies where to count from.
  184. * For level="single" or level="multiple":
  185. * Only ancestors that are searched are
  186. * those that are descendants of the nearest ancestor that matches
  187. * the from pattern.
  188. * For level="any:
  189. * Only nodes after the first node before the
  190. * current node that match the from pattern are considered.
  191. * @serial
  192. */
  193. private XPath m_fromMatchPattern = null;
  194. /**
  195. * Set the "from" attribute. Specifies where to count from.
  196. * For level="single" or level="multiple":
  197. * Only ancestors that are searched are
  198. * those that are descendants of the nearest ancestor that matches
  199. * the from pattern.
  200. * For level="any:
  201. * Only nodes after the first node before the
  202. * current node that match the from pattern are considered.
  203. *
  204. * @param v Value to set for "from" attribute.
  205. */
  206. public void setFrom(XPath v)
  207. {
  208. m_fromMatchPattern = v;
  209. }
  210. /**
  211. * Get the "from" attribute.
  212. * For level="single" or level="multiple":
  213. * Only ancestors that are searched are
  214. * those that are descendants of the nearest ancestor that matches
  215. * the from pattern.
  216. * For level="any:
  217. * Only nodes after the first node before the
  218. * current node that match the from pattern are considered.
  219. *
  220. * @return Value of "from" attribute.
  221. */
  222. public XPath getFrom()
  223. {
  224. return m_fromMatchPattern;
  225. }
  226. /**
  227. * When level="single", it goes up to the first node in the ancestor-or-self axis
  228. * that matches the count pattern, and constructs a list of length one containing
  229. * one plus the number of preceding siblings of that ancestor that match the count
  230. * pattern. If there is no such ancestor, it constructs an empty list. If the from
  231. * attribute is specified, then the only ancestors that are searched are those
  232. * that are descendants of the nearest ancestor that matches the from pattern.
  233. * Preceding siblings has the same meaning here as with the preceding-sibling axis.
  234. *
  235. * When level="multiple", it constructs a list of all ancestors of the current node
  236. * in document order followed by the element itself; it then selects from the list
  237. * those nodes that match the count pattern; it then maps each node in the list to
  238. * one plus the number of preceding siblings of that node that match the count pattern.
  239. * If the from attribute is specified, then the only ancestors that are searched are
  240. * those that are descendants of the nearest ancestor that matches the from pattern.
  241. * Preceding siblings has the same meaning here as with the preceding-sibling axis.
  242. *
  243. * When level="any", it constructs a list of length one containing the number of
  244. * nodes that match the count pattern and belong to the set containing the current
  245. * node and all nodes at any level of the document that are before the current node
  246. * in document order, excluding any namespace and attribute nodes (in other words
  247. * the union of the members of the preceding and ancestor-or-self axes). If the
  248. * from attribute is specified, then only nodes after the first node before the
  249. * current node that match the from pattern are considered.
  250. * @serial
  251. */
  252. private int m_level = Constants.NUMBERLEVEL_SINGLE;
  253. /**
  254. * Set the "level" attribute.
  255. * The level attribute specifies what levels of the source tree should
  256. * be considered; it has the values single, multiple or any. The default
  257. * is single.
  258. *
  259. * @param v Value to set for "level" attribute.
  260. */
  261. public void setLevel(int v)
  262. {
  263. m_level = v;
  264. }
  265. /**
  266. * Get the "level" attribute.
  267. * The level attribute specifies what levels of the source tree should
  268. * be considered; it has the values single, multiple or any. The default
  269. * is single.
  270. *
  271. * @return Value of "level" attribute.
  272. */
  273. public int getLevel()
  274. {
  275. return m_level;
  276. }
  277. /**
  278. * The value attribute contains an expression. The expression is evaluated
  279. * and the resulting object is converted to a number as if by a call to the
  280. * number function.
  281. * @serial
  282. */
  283. private XPath m_valueExpr = null;
  284. /**
  285. * Set the "value" attribute.
  286. * The value attribute contains an expression. The expression is evaluated
  287. * and the resulting object is converted to a number as if by a call to the
  288. * number function.
  289. *
  290. * @param v Value to set for "value" attribute.
  291. */
  292. public void setValue(XPath v)
  293. {
  294. m_valueExpr = v;
  295. }
  296. /**
  297. * Get the "value" attribute.
  298. * The value attribute contains an expression. The expression is evaluated
  299. * and the resulting object is converted to a number as if by a call to the
  300. * number function.
  301. *
  302. * @return Value of "value" attribute.
  303. */
  304. public XPath getValue()
  305. {
  306. return m_valueExpr;
  307. }
  308. /**
  309. * The "format" attribute is used to control conversion of a list of
  310. * numbers into a string.
  311. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  312. * @serial
  313. */
  314. private AVT m_format_avt = null;
  315. /**
  316. * Set the "format" attribute.
  317. * The "format" attribute is used to control conversion of a list of
  318. * numbers into a string.
  319. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  320. *
  321. * @param v Value to set for "format" attribute.
  322. */
  323. public void setFormat(AVT v)
  324. {
  325. m_format_avt = v;
  326. }
  327. /**
  328. * Get the "format" attribute.
  329. * The "format" attribute is used to control conversion of a list of
  330. * numbers into a string.
  331. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  332. *
  333. * @return Value of "format" attribute.
  334. */
  335. public AVT getFormat()
  336. {
  337. return m_format_avt;
  338. }
  339. /**
  340. * When numbering with an alphabetic sequence, the lang attribute
  341. * specifies which language's alphabet is to be used.
  342. * @serial
  343. */
  344. private AVT m_lang_avt = null;
  345. /**
  346. * Set the "lang" attribute.
  347. * When numbering with an alphabetic sequence, the lang attribute
  348. * specifies which language's alphabet is to be used; it has the same
  349. * range of values as xml:lang [XML]; if no lang value is specified,
  350. * the language should be determined from the system environment.
  351. * Implementers should document for which languages they support numbering.
  352. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  353. *
  354. * @param v Value to set for "lang" attribute.
  355. */
  356. public void setLang(AVT v)
  357. {
  358. m_lang_avt = v;
  359. }
  360. /**
  361. * Get the "lang" attribute.
  362. * When numbering with an alphabetic sequence, the lang attribute
  363. * specifies which language's alphabet is to be used; it has the same
  364. * range of values as xml:lang [XML]; if no lang value is specified,
  365. * the language should be determined from the system environment.
  366. * Implementers should document for which languages they support numbering.
  367. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  368. *
  369. * @return Value ofr "lang" attribute.
  370. */
  371. public AVT getLang()
  372. {
  373. return m_lang_avt;
  374. }
  375. /**
  376. * The letter-value attribute disambiguates between numbering
  377. * sequences that use letters.
  378. * @serial
  379. */
  380. private AVT m_lettervalue_avt = null;
  381. /**
  382. * Set the "letter-value" attribute.
  383. * The letter-value attribute disambiguates between numbering sequences
  384. * that use letters.
  385. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  386. *
  387. * @param v Value to set for "letter-value" attribute.
  388. */
  389. public void setLetterValue(AVT v)
  390. {
  391. m_lettervalue_avt = v;
  392. }
  393. /**
  394. * Get the "letter-value" attribute.
  395. * The letter-value attribute disambiguates between numbering sequences
  396. * that use letters.
  397. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  398. *
  399. * @return Value to set for "letter-value" attribute.
  400. */
  401. public AVT getLetterValue()
  402. {
  403. return m_lettervalue_avt;
  404. }
  405. /**
  406. * The grouping-separator attribute gives the separator
  407. * used as a grouping (e.g. thousands) separator in decimal
  408. * numbering sequences.
  409. * @serial
  410. */
  411. private AVT m_groupingSeparator_avt = null;
  412. /**
  413. * Set the "grouping-separator" attribute.
  414. * The grouping-separator attribute gives the separator
  415. * used as a grouping (e.g. thousands) separator in decimal
  416. * numbering sequences.
  417. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  418. *
  419. * @param v Value to set for "grouping-separator" attribute.
  420. */
  421. public void setGroupingSeparator(AVT v)
  422. {
  423. m_groupingSeparator_avt = v;
  424. }
  425. /**
  426. * Get the "grouping-separator" attribute.
  427. * The grouping-separator attribute gives the separator
  428. * used as a grouping (e.g. thousands) separator in decimal
  429. * numbering sequences.
  430. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  431. *
  432. * @return Value of "grouping-separator" attribute.
  433. */
  434. public AVT getGroupingSeparator()
  435. {
  436. return m_groupingSeparator_avt;
  437. }
  438. /**
  439. * The optional grouping-size specifies the size (normally 3) of the grouping.
  440. * @serial
  441. */
  442. private AVT m_groupingSize_avt = null;
  443. /**
  444. * Set the "grouping-size" attribute.
  445. * The optional grouping-size specifies the size (normally 3) of the grouping.
  446. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  447. *
  448. * @param v Value to set for "grouping-size" attribute.
  449. */
  450. public void setGroupingSize(AVT v)
  451. {
  452. m_groupingSize_avt = v;
  453. }
  454. /**
  455. * Get the "grouping-size" attribute.
  456. * The optional grouping-size specifies the size (normally 3) of the grouping.
  457. * @see <a href="http://www.w3.org/TR/xslt#convert">convert in XSLT Specification</a>
  458. *
  459. * @return Value of "grouping-size" attribute.
  460. */
  461. public AVT getGroupingSize()
  462. {
  463. return m_groupingSize_avt;
  464. }
  465. /**
  466. * Shouldn't this be in the transformer? Big worries about threads...
  467. */
  468. // private XResourceBundle thisBundle;
  469. /**
  470. * Table to help in converting decimals to roman numerals.
  471. * @see org.apache.xalan.transformer.DecimalToRoman
  472. */
  473. private final static DecimalToRoman m_romanConvertTable[] = {
  474. new DecimalToRoman(1000, "M", 900, "CM"),
  475. new DecimalToRoman(500, "D", 400, "CD"),
  476. new DecimalToRoman(100L, "C", 90L, "XC"),
  477. new DecimalToRoman(50L, "L", 40L, "XL"),
  478. new DecimalToRoman(10L, "X", 9L, "IX"),
  479. new DecimalToRoman(5L, "V", 4L, "IV"),
  480. new DecimalToRoman(1L, "I", 1L, "I") };
  481. /**
  482. * Chars for converting integers into alpha counts.
  483. * @see TransformerImpl#int2alphaCount
  484. */
  485. private static char[] m_alphaCountTable = null;
  486. /**
  487. * This function is called after everything else has been
  488. * recomposed, and allows the template to set remaining
  489. * values that may be based on some other property that
  490. * depends on recomposition.
  491. */
  492. public void compose(StylesheetRoot sroot) throws TransformerException
  493. {
  494. super.compose(sroot);
  495. StylesheetRoot.ComposeState cstate = sroot.getComposeState();
  496. java.util.Vector vnames = cstate.getVariableNames();
  497. if(null != m_countMatchPattern)
  498. m_countMatchPattern.fixupVariables(vnames, cstate.getGlobalsSize());
  499. if(null != m_format_avt)
  500. m_format_avt.fixupVariables(vnames, cstate.getGlobalsSize());
  501. if(null != m_fromMatchPattern)
  502. m_fromMatchPattern.fixupVariables(vnames, cstate.getGlobalsSize());
  503. if(null != m_groupingSeparator_avt)
  504. m_groupingSeparator_avt.fixupVariables(vnames, cstate.getGlobalsSize());
  505. if(null != m_groupingSize_avt)
  506. m_groupingSize_avt.fixupVariables(vnames, cstate.getGlobalsSize());
  507. if(null != m_lang_avt)
  508. m_lang_avt.fixupVariables(vnames, cstate.getGlobalsSize());
  509. if(null != m_lettervalue_avt)
  510. m_lettervalue_avt.fixupVariables(vnames, cstate.getGlobalsSize());
  511. if(null != m_valueExpr)
  512. m_valueExpr.fixupVariables(vnames, cstate.getGlobalsSize());
  513. }
  514. /**
  515. * Get an int constant identifying the type of element.
  516. * @see org.apache.xalan.templates.Constants
  517. *
  518. * @return The token ID for this element
  519. */
  520. public int getXSLToken()
  521. {
  522. return Constants.ELEMNAME_NUMBER;
  523. }
  524. /**
  525. * Return the node name.
  526. *
  527. * @return The element's name
  528. */
  529. public String getNodeName()
  530. {
  531. return Constants.ELEMNAME_NUMBER_STRING;
  532. }
  533. /**
  534. * Execute an xsl:number instruction. The xsl:number element is
  535. * used to insert a formatted number into the result tree.
  536. *
  537. * @param transformer non-null reference to the the current transform-time state.
  538. * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
  539. * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
  540. *
  541. * @throws TransformerException
  542. */
  543. public void execute(
  544. TransformerImpl transformer)
  545. throws TransformerException
  546. {
  547. if (TransformerImpl.S_DEBUG)
  548. transformer.getTraceManager().fireTraceEvent(this);
  549. int sourceNode = transformer.getXPathContext().getCurrentNode();
  550. String countString = getCountString(transformer, sourceNode);
  551. try
  552. {
  553. transformer.getResultTreeHandler().characters(countString.toCharArray(),
  554. 0, countString.length());
  555. }
  556. catch(SAXException se)
  557. {
  558. throw new TransformerException(se);
  559. }
  560. finally
  561. {
  562. if (TransformerImpl.S_DEBUG)
  563. transformer.getTraceManager().fireTraceEndEvent(this);
  564. }
  565. }
  566. /**
  567. * Add a child to the child list.
  568. *
  569. * @param newChild Child to add to child list
  570. *
  571. * @return Child just added to child list
  572. *
  573. * @throws DOMException
  574. */
  575. public ElemTemplateElement appendChild(ElemTemplateElement newChild)
  576. {
  577. error(XSLTErrorResources.ER_CANNOT_ADD,
  578. new Object[]{ newChild.getNodeName(),
  579. this.getNodeName() }); //"Can not add " +((ElemTemplateElement)newChild).m_elemName +
  580. //" to " + this.m_elemName);
  581. return null;
  582. }
  583. /**
  584. * Given a 'from' pattern (ala xsl:number), a match pattern
  585. * and a context, find the first ancestor that matches the
  586. * pattern (including the context handed in).
  587. *
  588. * @param xctxt The XPath runtime state for this.
  589. * @param fromMatchPattern The ancestor must match this pattern.
  590. * @param countMatchPattern The ancestor must also match this pattern.
  591. * @param context The node that "." expresses.
  592. * @param namespaceContext The context in which namespaces in the
  593. * queries are supposed to be expanded.
  594. *
  595. * @return the first ancestor that matches the given pattern
  596. *
  597. * @throws javax.xml.transform.TransformerException
  598. */
  599. int findAncestor(
  600. XPathContext xctxt, XPath fromMatchPattern, XPath countMatchPattern,
  601. int context, ElemNumber namespaceContext)
  602. throws javax.xml.transform.TransformerException
  603. {
  604. DTM dtm = xctxt.getDTM(context);
  605. while (DTM.NULL != context)
  606. {
  607. if (null != fromMatchPattern)
  608. {
  609. if (fromMatchPattern.getMatchScore(xctxt, context)
  610. != XPath.MATCH_SCORE_NONE)
  611. {
  612. //context = null;
  613. break;
  614. }
  615. }
  616. if (null != countMatchPattern)
  617. {
  618. if (countMatchPattern.getMatchScore(xctxt, context)
  619. != XPath.MATCH_SCORE_NONE)
  620. {
  621. break;
  622. }
  623. }
  624. context = dtm.getParent(context);
  625. }
  626. return context;
  627. }
  628. /**
  629. * Given a 'from' pattern (ala xsl:number), a match pattern
  630. * and a context, find the first ancestor that matches the
  631. * pattern (including the context handed in).
  632. * @param xctxt The XPath runtime state for this.
  633. * @param fromMatchPattern The ancestor must match this pattern.
  634. * @param countMatchPattern The ancestor must also match this pattern.
  635. * @param context The node that "." expresses.
  636. * @param namespaceContext The context in which namespaces in the
  637. * queries are supposed to be expanded.
  638. *
  639. * @return the first preceding, ancestor or self node that
  640. * matches the given pattern
  641. *
  642. * @throws javax.xml.transform.TransformerException
  643. */
  644. private int findPrecedingOrAncestorOrSelf(
  645. XPathContext xctxt, XPath fromMatchPattern, XPath countMatchPattern,
  646. int context, ElemNumber namespaceContext)
  647. throws javax.xml.transform.TransformerException
  648. {
  649. DTM dtm = xctxt.getDTM(context);
  650. while (DTM.NULL != context)
  651. {
  652. if (null != fromMatchPattern)
  653. {
  654. if (fromMatchPattern.getMatchScore(xctxt, context)
  655. != XPath.MATCH_SCORE_NONE)
  656. {
  657. context = DTM.NULL;
  658. break;
  659. }
  660. }
  661. if (null != countMatchPattern)
  662. {
  663. if (countMatchPattern.getMatchScore(xctxt, context)
  664. != XPath.MATCH_SCORE_NONE)
  665. {
  666. break;
  667. }
  668. }
  669. int prevSibling = dtm.getPreviousSibling(context);
  670. if (DTM.NULL == prevSibling)
  671. {
  672. context = dtm.getParent(context);
  673. }
  674. else
  675. {
  676. // Now go down the chain of children of this sibling
  677. context = dtm.getLastChild(prevSibling);
  678. if (context == DTM.NULL)
  679. context = prevSibling;
  680. }
  681. }
  682. return context;
  683. }
  684. /**
  685. * Get the count match pattern, or a default value.
  686. *
  687. * @param support The XPath runtime state for this.
  688. * @param contextNode The node that "." expresses.
  689. *
  690. * @return the count match pattern, or a default value.
  691. *
  692. * @throws javax.xml.transform.TransformerException
  693. */
  694. XPath getCountMatchPattern(XPathContext support, int contextNode)
  695. throws javax.xml.transform.TransformerException
  696. {
  697. XPath countMatchPattern = m_countMatchPattern;
  698. DTM dtm = support.getDTM(contextNode);
  699. if (null == countMatchPattern)
  700. {
  701. switch (dtm.getNodeType(contextNode))
  702. {
  703. case DTM.ELEMENT_NODE :
  704. MyPrefixResolver resolver;
  705. if (dtm.getNamespaceURI(contextNode) == null) {
  706. resolver = new MyPrefixResolver(dtm.getNode(contextNode), dtm,contextNode, false);
  707. } else {
  708. resolver = new MyPrefixResolver(dtm.getNode(contextNode), dtm,contextNode, true);
  709. }
  710. countMatchPattern = new XPath(dtm.getNodeName(contextNode), this, resolver,
  711. XPath.MATCH, support.getErrorListener());
  712. break;
  713. case DTM.ATTRIBUTE_NODE :
  714. // countMatchPattern = m_stylesheet.createMatchPattern("@"+contextNode.getNodeName(), this);
  715. countMatchPattern = new XPath("@" + dtm.getNodeName(contextNode), this,
  716. this, XPath.MATCH, support.getErrorListener());
  717. break;
  718. case DTM.CDATA_SECTION_NODE :
  719. case DTM.TEXT_NODE :
  720. // countMatchPattern = m_stylesheet.createMatchPattern("text()", this);
  721. countMatchPattern = new XPath("text()", this, this, XPath.MATCH, support.getErrorListener());
  722. break;
  723. case DTM.COMMENT_NODE :
  724. // countMatchPattern = m_stylesheet.createMatchPattern("comment()", this);
  725. countMatchPattern = new XPath("comment()", this, this, XPath.MATCH, support.getErrorListener());
  726. break;
  727. case DTM.DOCUMENT_NODE :
  728. // countMatchPattern = m_stylesheet.createMatchPattern("/", this);
  729. countMatchPattern = new XPath("/", this, this, XPath.MATCH, support.getErrorListener());
  730. break;
  731. case DTM.PROCESSING_INSTRUCTION_NODE :
  732. // countMatchPattern = m_stylesheet.createMatchPattern("pi("+contextNode.getNodeName()+")", this);
  733. countMatchPattern = new XPath("pi(" + dtm.getNodeName(contextNode)
  734. + ")", this, this, XPath.MATCH, support.getErrorListener());
  735. break;
  736. default :
  737. countMatchPattern = null;
  738. }
  739. }
  740. return countMatchPattern;
  741. }
  742. /**
  743. * Given an XML source node, get the count according to the
  744. * parameters set up by the xsl:number attributes.
  745. * @param transformer non-null reference to the the current transform-time state.
  746. * @param sourceNode The source node being counted.
  747. *
  748. * @return The count of nodes
  749. *
  750. * @throws TransformerException
  751. */
  752. String getCountString(TransformerImpl transformer, int sourceNode)
  753. throws TransformerException
  754. {
  755. long[] list = null;
  756. XPathContext xctxt = transformer.getXPathContext();
  757. CountersTable ctable = transformer.getCountersTable();
  758. if (null != m_valueExpr)
  759. {
  760. XObject countObj = m_valueExpr.execute(xctxt, sourceNode, this);
  761. long count = (long)java.lang.Math.floor(countObj.num()+ 0.5);
  762. list = new long[1];
  763. list[0] = count;
  764. }
  765. else
  766. {
  767. if (Constants.NUMBERLEVEL_ANY == m_level)
  768. {
  769. list = new long[1];
  770. list[0] = ctable.countNode(xctxt, this, sourceNode);
  771. }
  772. else
  773. {
  774. NodeVector ancestors =
  775. getMatchingAncestors(xctxt, sourceNode,
  776. Constants.NUMBERLEVEL_SINGLE == m_level);
  777. int lastIndex = ancestors.size() - 1;
  778. if (lastIndex >= 0)
  779. {
  780. list = new long[lastIndex + 1];
  781. for (int i = lastIndex; i >= 0; i--)
  782. {
  783. int target = ancestors.elementAt(i);
  784. list[lastIndex - i] = ctable.countNode(xctxt, this, target);
  785. }
  786. }
  787. }
  788. }
  789. return (null != list)
  790. ? formatNumberList(transformer, list, sourceNode) : "";
  791. }
  792. /**
  793. * Get the previous node to be counted.
  794. *
  795. * @param xctxt The XPath runtime state for this.
  796. * @param pos The current node
  797. *
  798. * @return the previous node to be counted.
  799. *
  800. * @throws TransformerException
  801. */
  802. public int getPreviousNode(XPathContext xctxt, int pos)
  803. throws TransformerException
  804. {
  805. XPath countMatchPattern = getCountMatchPattern(xctxt, pos);
  806. DTM dtm = xctxt.getDTM(pos);
  807. if (Constants.NUMBERLEVEL_ANY == m_level)
  808. {
  809. XPath fromMatchPattern = m_fromMatchPattern;
  810. // Do a backwards document-order walk 'till a node is found that matches
  811. // the 'from' pattern, or a node is found that matches the 'count' pattern,
  812. // or the top of the tree is found.
  813. while (DTM.NULL != pos)
  814. {
  815. // Get the previous sibling, if there is no previous sibling,
  816. // then count the parent, but if there is a previous sibling,
  817. // dive down to the lowest right-hand (last) child of that sibling.
  818. int next = dtm.getPreviousSibling(pos);
  819. if (DTM.NULL == next)
  820. {
  821. next = dtm.getParent(pos);
  822. if ((DTM.NULL != next) && ((((null != fromMatchPattern) && (fromMatchPattern.getMatchScore(
  823. xctxt, next) != XPath.MATCH_SCORE_NONE)))
  824. || (dtm.getNodeType(next) == DTM.DOCUMENT_NODE)))
  825. {
  826. pos = DTM.NULL; // return null from function.
  827. break; // from while loop
  828. }
  829. }
  830. else
  831. {
  832. // dive down to the lowest right child.
  833. int child = next;
  834. while (DTM.NULL != child)
  835. {
  836. child = dtm.getLastChild(next);
  837. if (DTM.NULL != child)
  838. next = child;
  839. }
  840. }
  841. pos = next;
  842. if ((DTM.NULL != pos)
  843. && ((null == countMatchPattern)
  844. || (countMatchPattern.getMatchScore(xctxt, pos)
  845. != XPath.MATCH_SCORE_NONE)))
  846. {
  847. break;
  848. }
  849. }
  850. }
  851. else // NUMBERLEVEL_MULTI or NUMBERLEVEL_SINGLE
  852. {
  853. while (DTM.NULL != pos)
  854. {
  855. pos = dtm.getPreviousSibling(pos);
  856. if ((DTM.NULL != pos)
  857. && ((null == countMatchPattern)
  858. || (countMatchPattern.getMatchScore(xctxt, pos)
  859. != XPath.MATCH_SCORE_NONE)))
  860. {
  861. break;
  862. }
  863. }
  864. }
  865. return pos;
  866. }
  867. /**
  868. * Get the target node that will be counted..
  869. *
  870. * @param xctxt The XPath runtime state for this.
  871. * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
  872. *
  873. * @return the target node that will be counted
  874. *
  875. * @throws TransformerException
  876. */
  877. public int getTargetNode(XPathContext xctxt, int sourceNode)
  878. throws TransformerException
  879. {
  880. int target = DTM.NULL;
  881. XPath countMatchPattern = getCountMatchPattern(xctxt, sourceNode);
  882. if (Constants.NUMBERLEVEL_ANY == m_level)
  883. {
  884. target = findPrecedingOrAncestorOrSelf(xctxt, m_fromMatchPattern,
  885. countMatchPattern, sourceNode,
  886. this);
  887. }
  888. else
  889. {
  890. target = findAncestor(xctxt, m_fromMatchPattern, countMatchPattern,
  891. sourceNode, this);
  892. }
  893. return target;
  894. }
  895. /**
  896. * Get the ancestors, up to the root, that match the
  897. * pattern.
  898. *
  899. * @param patterns if non-null, count only nodes
  900. * that match this pattern, if null count all ancestors.
  901. * @param xctxt The XPath runtime state for this.
  902. * @param node Count this node and it's ancestors.
  903. * @param stopAtFirstFound Flag indicating to stop after the
  904. * first node is found (difference between level = single
  905. * or multiple)
  906. * @return The number of ancestors that match the pattern.
  907. *
  908. * @throws javax.xml.transform.TransformerException
  909. */
  910. NodeVector getMatchingAncestors(
  911. XPathContext xctxt, int node, boolean stopAtFirstFound)
  912. throws javax.xml.transform.TransformerException
  913. {
  914. NodeSetDTM ancestors = new NodeSetDTM(xctxt.getDTMManager());
  915. XPath countMatchPattern = getCountMatchPattern(xctxt, node);
  916. DTM dtm = xctxt.getDTM(node);
  917. while (DTM.NULL != node)
  918. {
  919. if ((null != m_fromMatchPattern)
  920. && (m_fromMatchPattern.getMatchScore(xctxt, node)
  921. != XPath.MATCH_SCORE_NONE))
  922. {
  923. // The following if statement gives level="single" different
  924. // behavior from level="multiple", which seems incorrect according
  925. // to the XSLT spec. For now we are leaving this in to replicate
  926. // the same behavior in XT, but, for all intents and purposes we
  927. // think this is a bug, or there is something about level="single"
  928. // that we still don't understand.
  929. if (!stopAtFirstFound)
  930. break;
  931. }
  932. if (null == countMatchPattern)
  933. System.out.println(
  934. "Programmers error! countMatchPattern should never be null!");
  935. if (countMatchPattern.getMatchScore(xctxt, node)
  936. != XPath.MATCH_SCORE_NONE)
  937. {
  938. ancestors.addElement(node);
  939. if (stopAtFirstFound)
  940. break;
  941. }
  942. node = dtm.getParent(node);
  943. }
  944. return ancestors;
  945. } // end getMatchingAncestors method
  946. /**
  947. * Get the locale we should be using.
  948. *
  949. * @param transformer non-null reference to the the current transform-time state.
  950. * @param contextNode The node that "." expresses.
  951. *
  952. * @return The locale to use. May be specified by "lang" attribute,
  953. * but if not, use default locale on the system.
  954. *
  955. * @throws TransformerException
  956. */
  957. Locale getLocale(TransformerImpl transformer, int contextNode)
  958. throws TransformerException
  959. {
  960. Locale locale = null;
  961. if (null != m_lang_avt)
  962. {
  963. XPathContext xctxt = transformer.getXPathContext();
  964. String langValue = m_lang_avt.evaluate(xctxt, contextNode, this);
  965. if (null != langValue)
  966. {
  967. // Not really sure what to do about the country code, so I use the
  968. // default from the system.
  969. // TODO: fix xml:lang handling.
  970. locale = new Locale(langValue.toUpperCase(), "");
  971. //Locale.getDefault().getDisplayCountry());
  972. if (null == locale)
  973. {
  974. transformer.getMsgMgr().warn(this, null, xctxt.getDTM(contextNode).getNode(contextNode),
  975. XSLTErrorResources.WG_LOCALE_NOT_FOUND,
  976. new Object[]{ langValue }); //"Warning: Could not find locale for xml:lang="+langValue);
  977. locale = Locale.getDefault();
  978. }
  979. }
  980. }
  981. else
  982. {
  983. locale = Locale.getDefault();
  984. }
  985. return locale;
  986. }
  987. /**
  988. * Get the number formatter to be used the format the numbers
  989. *
  990. * @param transformer non-null reference to the the current transform-time state.
  991. * @param contextNode The node that "." expresses.
  992. *
  993. * ($objectName$) @return The number formatter to be used
  994. *
  995. * @throws TransformerException
  996. */
  997. private DecimalFormat getNumberFormatter(
  998. TransformerImpl transformer, int contextNode) throws TransformerException
  999. {
  1000. // Patch from Steven Serocki
  1001. // Maybe we really want to do the clone in getLocale() and return
  1002. // a clone of the default Locale??
  1003. Locale locale = (Locale)getLocale(transformer, contextNode).clone();
  1004. // Helper to format local specific numbers to strings.
  1005. DecimalFormat formatter = null;
  1006. //synchronized (locale)
  1007. //{
  1008. // formatter = (DecimalFormat) NumberFormat.getNumberInstance(locale);
  1009. //}
  1010. String digitGroupSepValue =
  1011. (null != m_groupingSeparator_avt)
  1012. ? m_groupingSeparator_avt.evaluate(
  1013. transformer.getXPathContext(), contextNode, this) : null;
  1014. // Validate grouping separator if an AVT was used; otherwise this was
  1015. // validated statically in XSLTAttributeDef.java.
  1016. if ((digitGroupSepValue != null) && (!m_groupingSeparator_avt.isSimple()) &&
  1017. (digitGroupSepValue.length() != 1))
  1018. {
  1019. transformer.getMsgMgr().warn(
  1020. this, XSLTErrorResources.WG_ILLEGAL_ATTRIBUTE_VALUE,
  1021. new Object[]{ Constants.ATTRNAME_NAME, m_groupingSeparator_avt.getName()});
  1022. }
  1023. String nDigitsPerGroupValue =
  1024. (null != m_groupingSize_avt)
  1025. ? m_groupingSize_avt.evaluate(
  1026. transformer.getXPathContext(), contextNode, this) : null;
  1027. // TODO: Handle digit-group attributes
  1028. if ((null != digitGroupSepValue) && (null != nDigitsPerGroupValue) &&
  1029. // Ignore if separation value is empty string
  1030. (digitGroupSepValue.length() > 0))
  1031. {
  1032. try
  1033. {
  1034. formatter = (DecimalFormat) NumberFormat.getNumberInstance(locale);
  1035. formatter.setGroupingSize(
  1036. Integer.valueOf(nDigitsPerGroupValue).intValue());
  1037. DecimalFormatSymbols symbols = formatter.getDecimalFormatSymbols();
  1038. symbols.setGroupingSeparator(digitGroupSepValue.charAt(0));
  1039. formatter.setDecimalFormatSymbols(symbols);
  1040. formatter.setGroupingUsed(true);
  1041. }
  1042. catch (NumberFormatException ex)
  1043. {
  1044. formatter.setGroupingUsed(false);
  1045. }
  1046. }
  1047. return formatter;
  1048. }
  1049. /**
  1050. * Format a vector of numbers into a formatted string.
  1051. *
  1052. * @param xslNumberElement Element that takes %conversion-atts; attributes.
  1053. * @param transformer non-null reference to the the current transform-time state.
  1054. * @param list Array of one or more long integer numbers.
  1055. * @param contextNode The node that "." expresses.
  1056. * @return String that represents list according to
  1057. * %conversion-atts; attributes.
  1058. * TODO: Optimize formatNumberList so that it caches the last count and
  1059. * reuses that info for the next count.
  1060. *
  1061. * @throws TransformerException
  1062. */
  1063. String formatNumberList(
  1064. TransformerImpl transformer, long[] list, int contextNode)
  1065. throws TransformerException
  1066. {
  1067. String numStr;
  1068. FastStringBuffer formattedNumber = StringBufferPool.get();
  1069. try
  1070. {
  1071. int nNumbers = list.length, numberWidth = 1;
  1072. char numberType = '1';
  1073. String formatToken, lastSepString = null, formatTokenString = null;
  1074. // If a seperator hasn't been specified, then use "."
  1075. // as a default separator.
  1076. // For instance: [2][1][5] with a format value of "1 "
  1077. // should format to "2.1.5 " (I think).
  1078. // Otherwise, use the seperator specified in the format string.
  1079. // For instance: [2][1][5] with a format value of "01-001. "
  1080. // should format to "02-001-005 ".
  1081. String lastSep = ".";
  1082. boolean isFirstToken = true; // true if first token
  1083. String formatValue =
  1084. (null != m_format_avt)
  1085. ? m_format_avt.evaluate(
  1086. transformer.getXPathContext(), contextNode, this) : null;
  1087. if (null == formatValue)
  1088. formatValue = "1";
  1089. NumberFormatStringTokenizer formatTokenizer =
  1090. new NumberFormatStringTokenizer(formatValue);
  1091. // int sepCount = 0; // keep track of seperators
  1092. // Loop through all the numbers in the list.
  1093. for (int i = 0; i < nNumbers; i++)
  1094. {
  1095. // Loop to the next digit, letter, or separator.
  1096. if (formatTokenizer.hasMoreTokens())
  1097. {
  1098. formatToken = formatTokenizer.nextToken();
  1099. // If the first character of this token is a character or digit, then
  1100. // it is a number format directive.
  1101. if (Character.isLetterOrDigit(
  1102. formatToken.charAt(formatToken.length() - 1)))
  1103. {
  1104. numberWidth = formatToken.length();
  1105. numberType = formatToken.charAt(numberWidth - 1);
  1106. }
  1107. // If there is a number format directive ahead,
  1108. // then append the formatToken.
  1109. else if (formatTokenizer.isLetterOrDigitAhead())
  1110. {
  1111. formatTokenString = formatToken;
  1112. // Append the formatToken string...
  1113. // For instance [2][1][5] with a format value of "1--1. "
  1114. // should format to "2--1--5. " (I guess).
  1115. while (formatTokenizer.nextIsSep())
  1116. {
  1117. formatToken = formatTokenizer.nextToken();
  1118. formatTokenString += formatToken;
  1119. }
  1120. // Record this separator, so it can be used as the
  1121. // next separator, if the next is the last.
  1122. // For instance: [2][1][5] with a format value of "1-1 "
  1123. // should format to "2-1-5 ".
  1124. if (!isFirstToken)
  1125. lastSep = formatTokenString;
  1126. // Since we know the next is a number or digit, we get it now.
  1127. formatToken = formatTokenizer.nextToken();
  1128. numberWidth = formatToken.length();
  1129. numberType = formatToken.charAt(numberWidth - 1);
  1130. }
  1131. else // only separators left
  1132. {
  1133. // Set up the string for the trailing characters after
  1134. // the last number is formatted (i.e. after the loop).
  1135. lastSepString = formatToken;
  1136. // And append any remaining characters to the lastSepString.
  1137. while (formatTokenizer.hasMoreTokens())
  1138. {
  1139. formatToken = formatTokenizer.nextToken();
  1140. lastSepString += formatToken;
  1141. }
  1142. } // else
  1143. } // end if(formatTokenizer.hasMoreTokens())
  1144. // if this is the first token and there was a prefix
  1145. // append the prefix else, append the separator
  1146. // For instance, [2][1][5] with a format value of "(1-1.) "
  1147. // should format to "(2-1-5.) " (I guess).
  1148. if (null != formatTokenString && isFirstToken)
  1149. {
  1150. formattedNumber.append(formatTokenString);
  1151. }
  1152. else if (null != lastSep &&!isFirstToken)
  1153. formattedNumber.append(lastSep);
  1154. getFormattedNumber(transformer, contextNode, numberType, numberWidth,
  1155. list[i], formattedNumber);
  1156. isFirstToken = false; // After the first pass, this should be false
  1157. } // end for loop
  1158. // Check to see if we finished up the format string...
  1159. // Skip past all remaining letters or digits
  1160. while (formatTokenizer.isLetterOrDigitAhead())
  1161. {
  1162. formatTokenizer.nextToken();
  1163. }
  1164. if (lastSepString != null)
  1165. formattedNumber.append(lastSepString);
  1166. while (formatTokenizer.hasMoreTokens())
  1167. {
  1168. formatToken = formatTokenizer.nextToken();
  1169. formattedNumber.append(formatToken);
  1170. }
  1171. numStr = formattedNumber.toString();
  1172. }
  1173. finally
  1174. {
  1175. StringBufferPool.free(formattedNumber);
  1176. }
  1177. return numStr;
  1178. } // end formatNumberList method
  1179. /*
  1180. * Get Formatted number
  1181. */
  1182. /**
  1183. * Format the given number and store it in the given buffer
  1184. *
  1185. *
  1186. * @param transformer non-null reference to the the current transform-time state.
  1187. * @param contextNode The node that "." expresses.
  1188. * @param numberType Type to format to
  1189. * @param numberWidth Maximum length of formatted number
  1190. * @param listElement Number to format
  1191. * @param formattedNumber Buffer to store formatted number
  1192. *
  1193. * @throws javax.xml.transform.TransformerException
  1194. */
  1195. private void getFormattedNumber(
  1196. TransformerImpl transformer, int contextNode,
  1197. char numberType, int numberWidth, long listElement,
  1198. FastStringBuffer formattedNumber)
  1199. throws javax.xml.transform.TransformerException
  1200. {
  1201. String letterVal =
  1202. (m_lettervalue_avt != null)
  1203. ? m_lettervalue_avt.evaluate(
  1204. transformer.getXPathContext(), contextNode, this) : null;
  1205. switch (numberType)
  1206. {
  1207. case 'A' :
  1208. if (m_alphaCountTable == null)
  1209. {
  1210. XResourceBundle thisBundle;
  1211. thisBundle =
  1212. (XResourceBundle) XResourceBundle.loadResourceBundle(
  1213. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, getLocale(transformer, contextNode));
  1214. char[] alphabet;
  1215. alphabet = (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET);
  1216. m_alphaCountTable = alphabet;
  1217. }
  1218. int2alphaCount(listElement, m_alphaCountTable, formattedNumber);
  1219. break;
  1220. case 'a' :
  1221. if (m_alphaCountTable == null)
  1222. {
  1223. XResourceBundle thisBundle;
  1224. thisBundle =
  1225. (XResourceBundle) XResourceBundle.loadResourceBundle(
  1226. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, getLocale(transformer, contextNode));
  1227. char[] alphabet;
  1228. alphabet = (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET);
  1229. m_alphaCountTable = alphabet;
  1230. }
  1231. FastStringBuffer stringBuf = StringBufferPool.get();
  1232. try
  1233. {
  1234. int2alphaCount(listElement, m_alphaCountTable, stringBuf);
  1235. formattedNumber.append(
  1236. stringBuf.toString().toLowerCase(
  1237. getLocale(transformer, contextNode)));
  1238. }
  1239. finally
  1240. {
  1241. StringBufferPool.free(stringBuf);
  1242. }
  1243. break;
  1244. case 'I' :
  1245. formattedNumber.append(long2roman(listElement, true));
  1246. break;
  1247. case 'i' :
  1248. formattedNumber.append(
  1249. long2roman(listElement, true).toLowerCase(
  1250. getLocale(transformer, contextNode)));
  1251. break;
  1252. case 0x3042 :
  1253. {
  1254. XResourceBundle thisBundle;
  1255. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1256. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "HA"));
  1257. if (letterVal != null
  1258. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1259. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1260. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1261. formattedNumber.append(
  1262. int2singlealphaCount(
  1263. listElement,
  1264. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
  1265. break;
  1266. }
  1267. case 0x3044 :
  1268. {
  1269. XResourceBundle thisBundle;
  1270. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1271. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "HI"));
  1272. if ((letterVal != null)
  1273. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1274. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1275. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1276. formattedNumber.append(
  1277. int2singlealphaCount(
  1278. listElement,
  1279. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
  1280. break;
  1281. }
  1282. case 0x30A2 :
  1283. {
  1284. XResourceBundle thisBundle;
  1285. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1286. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "A"));
  1287. if (letterVal != null
  1288. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1289. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1290. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1291. formattedNumber.append(
  1292. int2singlealphaCount(
  1293. listElement,
  1294. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
  1295. break;
  1296. }
  1297. case 0x30A4 :
  1298. {
  1299. XResourceBundle thisBundle;
  1300. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1301. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ja", "JP", "I"));
  1302. if (letterVal != null
  1303. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1304. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1305. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1306. formattedNumber.append(
  1307. int2singlealphaCount(
  1308. listElement,
  1309. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET)));
  1310. break;
  1311. }
  1312. case 0x4E00 :
  1313. {
  1314. XResourceBundle thisBundle;
  1315. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1316. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("zh", "CN"));
  1317. if (letterVal != null
  1318. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1319. {
  1320. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1321. }
  1322. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1323. int2alphaCount(listElement,
  1324. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
  1325. formattedNumber);
  1326. break;
  1327. }
  1328. case 0x58F9 :
  1329. {
  1330. XResourceBundle thisBundle;
  1331. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1332. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("zh", "TW"));
  1333. if (letterVal != null
  1334. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1335. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1336. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1337. int2alphaCount(listElement,
  1338. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
  1339. formattedNumber);
  1340. break;
  1341. }
  1342. case 0x0E51 :
  1343. {
  1344. XResourceBundle thisBundle;
  1345. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1346. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("th", ""));
  1347. if (letterVal != null
  1348. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1349. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1350. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1351. int2alphaCount(listElement,
  1352. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
  1353. formattedNumber);
  1354. break;
  1355. }
  1356. case 0x05D0 :
  1357. {
  1358. XResourceBundle thisBundle;
  1359. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1360. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("he", ""));
  1361. if (letterVal != null
  1362. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1363. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1364. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1365. int2alphaCount(listElement,
  1366. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
  1367. formattedNumber);
  1368. break;
  1369. }
  1370. case 0x10D0 :
  1371. {
  1372. XResourceBundle thisBundle;
  1373. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1374. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("ka", ""));
  1375. if (letterVal != null
  1376. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1377. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1378. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1379. int2alphaCount(listElement,
  1380. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
  1381. formattedNumber);
  1382. break;
  1383. }
  1384. case 0x03B1 :
  1385. {
  1386. XResourceBundle thisBundle;
  1387. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1388. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("el", ""));
  1389. if (letterVal != null
  1390. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1391. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1392. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1393. int2alphaCount(listElement,
  1394. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
  1395. formattedNumber);
  1396. break;
  1397. }
  1398. case 0x0430 :
  1399. {
  1400. XResourceBundle thisBundle;
  1401. thisBundle = (XResourceBundle) XResourceBundle.loadResourceBundle(
  1402. org.apache.xml.utils.res.XResourceBundle.LANG_BUNDLE_NAME, new Locale("cy", ""));
  1403. if (letterVal != null
  1404. && letterVal.equals(Constants.ATTRVAL_TRADITIONAL))
  1405. formattedNumber.append(tradAlphaCount(listElement, thisBundle));
  1406. else //if (m_lettervalue_avt != null && m_lettervalue_avt.equals(Constants.ATTRVAL_ALPHABETIC))
  1407. int2alphaCount(listElement,
  1408. (char[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_ALPHABET),
  1409. formattedNumber);
  1410. break;
  1411. }
  1412. default : // "1"
  1413. DecimalFormat formatter = getNumberFormatter(transformer, contextNode);
  1414. String padString = formatter == null ? String.valueOf(0) : formatter.format(0);
  1415. String numString = formatter == null ? String.valueOf(listElement) : formatter.format(listElement);
  1416. int nPadding = numberWidth - numString.length();
  1417. for (int k = 0; k < nPadding; k++)
  1418. {
  1419. formattedNumber.append(padString);
  1420. }
  1421. formattedNumber.append(numString);
  1422. }
  1423. }
  1424. /**
  1425. * Get a string value for zero, which is not really defined by the 1.0 spec,
  1426. * thought I think it might be cleared up by the erreta.
  1427. */
  1428. String getZeroString()
  1429. {
  1430. return ""+0;
  1431. }
  1432. /**
  1433. * Convert a long integer into alphabetic counting, in other words
  1434. * count using the sequence A B C ... Z.
  1435. *
  1436. * @param val Value to convert -- must be greater than zero.
  1437. * @param table a table containing one character for each digit in the radix
  1438. * @return String representing alpha count of number.
  1439. * @see TransformerImpl#DecimalToRoman
  1440. *
  1441. * Note that the radix of the conversion is inferred from the size
  1442. * of the table.
  1443. */
  1444. protected String int2singlealphaCount(long val, char[] table)
  1445. {
  1446. int radix = table.length;
  1447. // TODO: throw error on out of range input
  1448. if (val > radix)
  1449. {
  1450. return getZeroString();
  1451. }
  1452. else
  1453. return (new Character(table[(int)val - 1])).toString(); // index into table is off one, starts at 0
  1454. }
  1455. /**
  1456. * Convert a long integer into alphabetic counting, in other words
  1457. * count using the sequence A B C ... Z AA AB AC.... etc.
  1458. *
  1459. * @param val Value to convert -- must be greater than zero.
  1460. * @param table a table containing one character for each digit in the radix
  1461. * @param aTable Array of alpha characters representing numbers
  1462. * @param stringBuf Buffer where to save the string representing alpha count of number.
  1463. *
  1464. * @see TransformerImpl#DecimalToRoman
  1465. *
  1466. * Note that the radix of the conversion is inferred from the size
  1467. * of the table.
  1468. */
  1469. protected void int2alphaCount(long val, char[] aTable,
  1470. FastStringBuffer stringBuf)
  1471. {
  1472. int radix = aTable.length;
  1473. char[] table = new char[aTable.length];
  1474. // start table at 1, add last char at index 0. Reason explained above and below.
  1475. int i;
  1476. for (i = 0; i < aTable.length - 1; i++)
  1477. {
  1478. table[i + 1] = aTable[i];
  1479. }
  1480. table[0] = aTable[i];
  1481. // Create a buffer to hold the result
  1482. // TODO: size of the table can be detereined by computing
  1483. // logs of the radix. For now, we fake it.
  1484. char buf[] = new char[100];
  1485. //some languages go left to right(ie. english), right to left (ie. Hebrew),
  1486. //top to bottom (ie.Japanese), etc... Handle them differently
  1487. //String orientation = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.LANG_ORIENTATION);
  1488. // next character to set in the buffer
  1489. int charPos;
  1490. charPos = buf.length - 1; // work backward through buf[]
  1491. // index in table of the last character that we stored
  1492. int lookupIndex = 1; // start off with anything other than zero to make correction work
  1493. // Correction number
  1494. //
  1495. // Correction can take on exactly two values:
  1496. //
  1497. // 0 if the next character is to be emitted is usual
  1498. //
  1499. // radix - 1
  1500. // if the next char to be emitted should be one less than
  1501. // you would expect
  1502. //
  1503. // For example, consider radix 10, where 1="A" and 10="J"
  1504. //
  1505. // In this scheme, we count: A, B, C ... H, I, J (not A0 and certainly
  1506. // not AJ), A1
  1507. //
  1508. // So, how do we keep from emitting AJ for 10? After correctly emitting the
  1509. // J, lookupIndex is zero. We now compute a correction number of 9 (radix-1).
  1510. // In the following line, we'll compute (val+correction) % radix, which is,
  1511. // (val+9)/10. By this time, val is 1, so we compute (1+9) % 10, which
  1512. // is 10 % 10 or zero. So, we'll prepare to emit "JJ", but then we'll
  1513. // later suppress the leading J as representing zero (in the mod system,
  1514. // it can represent either 10 or zero). In summary, the correction value of
  1515. // "radix-1" acts like "-1" when run through the mod operator, but with the
  1516. // desireable characteristic that it never produces a negative number.
  1517. long correction = 0;
  1518. // TODO: throw error on out of range input
  1519. do
  1520. {
  1521. // most of the correction calculation is explained above, the reason for the
  1522. // term after the "|| " is that it correctly propagates carries across
  1523. // multiple columns.
  1524. correction =
  1525. ((lookupIndex == 0) || (correction != 0 && lookupIndex == radix - 1))
  1526. ? (radix - 1) : 0;
  1527. // index in "table" of the next char to emit
  1528. lookupIndex = (int)(val + correction) % radix;
  1529. // shift input by one "column"
  1530. val = (val / radix);
  1531. // if the next value we'd put out would be a leading zero, we're done.
  1532. if (lookupIndex == 0 && val == 0)
  1533. break;
  1534. // put out the next character of output
  1535. buf[charPos--] = table[lookupIndex]; // left to right or top to bottom
  1536. }
  1537. while (val > 0);
  1538. stringBuf.append(buf, charPos + 1, (buf.length - charPos - 1));
  1539. }
  1540. /**
  1541. * Convert a long integer into traditional alphabetic counting, in other words
  1542. * count using the traditional numbering.
  1543. *
  1544. * @param val Value to convert -- must be greater than zero.
  1545. * @param table a table containing one character for each digit in the radix
  1546. * @param thisBundle Resource bundle to use
  1547. *
  1548. * @return String representing alpha count of number.
  1549. * @see XSLProcessor#DecimalToRoman
  1550. *
  1551. * Note that the radix of the conversion is inferred from the size
  1552. * of the table.
  1553. */
  1554. protected String tradAlphaCount(long val, XResourceBundle thisBundle)
  1555. {
  1556. // if this number is larger than the largest number we can represent, error!
  1557. if (val > Long.MAX_VALUE)
  1558. {
  1559. this.error(XSLTErrorResources.ER_NUMBER_TOO_BIG);
  1560. return XSLTErrorResources.ERROR_STRING;
  1561. }
  1562. char[] table = null;
  1563. // index in table of the last character that we stored
  1564. int lookupIndex = 1; // start off with anything other than zero to make correction work
  1565. // Create a buffer to hold the result
  1566. // TODO: size of the table can be detereined by computing
  1567. // logs of the radix. For now, we fake it.
  1568. char buf[] = new char[100];
  1569. //some languages go left to right(ie. english), right to left (ie. Hebrew),
  1570. //top to bottom (ie.Japanese), etc... Handle them differently
  1571. //String orientation = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.LANG_ORIENTATION);
  1572. // next character to set in the buffer
  1573. int charPos;
  1574. charPos = 0; //start at 0
  1575. // array of number groups: ie.1000, 100, 10, 1
  1576. int[] groups = (int[]) thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_NUMBERGROUPS);
  1577. // array of tables of hundreds, tens, digits...
  1578. String[] tables =
  1579. (String[]) (thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_NUM_TABLES));
  1580. //some languages have additive alphabetical notation,
  1581. //some multiplicative-additive, etc... Handle them differently.
  1582. String numbering = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.LANG_NUMBERING);
  1583. // do multiplicative part first
  1584. if (numbering.equals(org.apache.xml.utils.res.XResourceBundle.LANG_MULT_ADD))
  1585. {
  1586. String mult_order = thisBundle.getString(org.apache.xml.utils.res.XResourceBundle.MULT_ORDER);
  1587. long[] multiplier =
  1588. (long[]) (thisBundle.getObject(org.apache.xml.utils.res.XResourceBundle.LANG_MULTIPLIER));
  1589. char[] zeroChar = (char[]) thisBundle.getObject("zero");
  1590. int i = 0;
  1591. // skip to correct multiplier
  1592. while (i < multiplier.length && val < multiplier[i])
  1593. {
  1594. i++;
  1595. }
  1596. do
  1597. {
  1598. if (i >= multiplier.length)
  1599. break; //number is smaller than multipliers
  1600. // some languages (ie chinese) put a zero character (and only one) when
  1601. // the multiplier is multiplied by zero. (ie, 1001 is 1X1000 + 0X100 + 0X10 + 1)
  1602. // 0X100 is replaced by the zero character, we don't need one for 0X10
  1603. if (val < multiplier[i])
  1604. {
  1605. if (zeroChar.length == 0)
  1606. {
  1607. i++;
  1608. }
  1609. else
  1610. {
  1611. if (buf[charPos - 1] != zeroChar[0])
  1612. buf[charPos++] = zeroChar[0];
  1613. i++;
  1614. }
  1615. }
  1616. else if (val >= multiplier[i])
  1617. {
  1618. long mult = val / multiplier[i];
  1619. val = val % multiplier[i]; // save this.
  1620. int k = 0;
  1621. while (k < groups.length)
  1622. {
  1623. lookupIndex = 1; // initialize for each table
  1624. if (mult / groups[k] <= 0) // look for right table
  1625. k++;
  1626. else
  1627. {
  1628. // get the table
  1629. char[] THEletters = (char[]) thisBundle.getObject(tables[k]);
  1630. table = new char[THEletters.length + 1];
  1631. int j;
  1632. for (j = 0; j < THEletters.length; j++)
  1633. {
  1634. table[j + 1] = THEletters[j];
  1635. }
  1636. table[0] = THEletters[j - 1]; // don't need this
  1637. // index in "table" of the next char to emit
  1638. lookupIndex = (int)mult / groups[k];
  1639. //this should not happen
  1640. if (lookupIndex == 0 && mult == 0)
  1641. break;
  1642. char multiplierChar = ((char[]) (thisBundle.getObject(
  1643. org.apache.xml.utils.res.XResourceBundle.LANG_MULTIPLIER_CHAR)))[i];
  1644. // put out the next character of output
  1645. if (lookupIndex < table.length)
  1646. {
  1647. if (mult_order.equals(org.apache.xml.utils.res.XResourceBundle.MULT_PRECEDES))
  1648. {
  1649. buf[charPos++] = multiplierChar;
  1650. buf[charPos++] = table[lookupIndex];
  1651. }
  1652. else
  1653. {
  1654. // don't put out 1 (ie 1X10 is just 10)
  1655. if (lookupIndex == 1 && i == multiplier.length - 1){}
  1656. else
  1657. buf[charPos++] = table[lookupIndex];
  1658. buf[charPos++] = multiplierChar;
  1659. }
  1660. break; // all done!
  1661. }
  1662. else
  1663. return XSLTErrorResources.ERROR_STRING;
  1664. } //end else
  1665. } // end while
  1666. i++;
  1667. } // end else if
  1668. } // end do while
  1669. while (i < multiplier.length);
  1670. }
  1671. // Now do additive part...
  1672. int count = 0;
  1673. String tableName;
  1674. // do this for each table of hundreds, tens, digits...
  1675. while (count < groups.length)
  1676. {
  1677. if (val / groups[count] <= 0) // look for correct table
  1678. count++;
  1679. else
  1680. {
  1681. char[] theletters = (char[]) thisBundle.getObject(tables[count]);
  1682. table = new char[theletters.length + 1];
  1683. int j;
  1684. // need to start filling the table up at index 1
  1685. for (j = 0; j < theletters.length; j++)
  1686. {
  1687. table[j + 1] = theletters[j];
  1688. }
  1689. table[0] = theletters[j - 1]; // don't need this
  1690. // index in "table" of the next char to emit
  1691. lookupIndex = (int)val / groups[count];
  1692. // shift input by one "column"
  1693. val = val % groups[count];
  1694. // this should not happen
  1695. if (lookupIndex == 0 && val == 0)
  1696. break;
  1697. if (lookupIndex < table.length)
  1698. {
  1699. // put out the next character of output
  1700. buf[charPos++] = table[lookupIndex]; // left to right or top to bottom
  1701. }
  1702. else
  1703. return XSLTErrorResources.ERROR_STRING;
  1704. count++;
  1705. }
  1706. } // end while
  1707. // String s = new String(buf, 0, charPos);
  1708. return new String(buf, 0, charPos);
  1709. }
  1710. /**
  1711. * Convert a long integer into roman numerals.
  1712. * @param val Value to convert.
  1713. * @param prefixesAreOK true_ to enable prefix notation (e.g. 4 = "IV"),
  1714. * false_ to disable prefix notation (e.g. 4 = "IIII").
  1715. * @return Roman numeral string.
  1716. * @see DecimalToRoman
  1717. * @see m_romanConvertTable
  1718. */
  1719. protected String long2roman(long val, boolean prefixesAreOK)
  1720. {
  1721. if (val <= 0)
  1722. {
  1723. return getZeroString();
  1724. }
  1725. String roman = "";
  1726. int place = 0;
  1727. if (val <= 3999L)
  1728. {
  1729. do
  1730. {
  1731. while (val >= m_romanConvertTable[place].m_postValue)
  1732. {
  1733. roman += m_romanConvertTable[place].m_postLetter;
  1734. val -= m_romanConvertTable[place].m_postValue;
  1735. }
  1736. if (prefixesAreOK)
  1737. {
  1738. if (val >= m_romanConvertTable[place].m_preValue)
  1739. {
  1740. roman += m_romanConvertTable[place].m_preLetter;
  1741. val -= m_romanConvertTable[place].m_preValue;
  1742. }
  1743. }
  1744. place++;
  1745. }
  1746. while (val > 0);
  1747. }
  1748. else
  1749. {
  1750. roman = XSLTErrorResources.ERROR_STRING;
  1751. }
  1752. return roman;
  1753. } // end long2roman
  1754. /**
  1755. * Call the children visitors.
  1756. * @param visitor The visitor whose appropriate method will be called.
  1757. */
  1758. public void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
  1759. {
  1760. if(callAttrs)
  1761. {
  1762. if(null != m_countMatchPattern)
  1763. m_countMatchPattern.getExpression().callVisitors(m_countMatchPattern, visitor);
  1764. if(null != m_fromMatchPattern)
  1765. m_fromMatchPattern.getExpression().callVisitors(m_fromMatchPattern, visitor);
  1766. if(null != m_valueExpr)
  1767. m_valueExpr.getExpression().callVisitors(m_valueExpr, visitor);
  1768. if(null != m_format_avt)
  1769. m_format_avt.callVisitors(visitor);
  1770. if(null != m_groupingSeparator_avt)
  1771. m_groupingSeparator_avt.callVisitors(visitor);
  1772. if(null != m_groupingSize_avt)
  1773. m_groupingSize_avt.callVisitors(visitor);
  1774. if(null != m_lang_avt)
  1775. m_lang_avt.callVisitors(visitor);
  1776. if(null != m_lettervalue_avt)
  1777. m_lettervalue_avt.callVisitors(visitor);
  1778. }
  1779. super.callChildVisitors(visitor, callAttrs);
  1780. }
  1781. /**
  1782. * This class returns tokens using non-alphanumberic
  1783. * characters as delimiters.
  1784. */
  1785. class NumberFormatStringTokenizer
  1786. {
  1787. /** Current position in the format string */
  1788. private int currentPosition;
  1789. /** Index of last character in the format string */
  1790. private int maxPosition;
  1791. /** Format string to be tokenized */
  1792. private String str;
  1793. /**
  1794. * Construct a NumberFormatStringTokenizer.
  1795. *
  1796. * @param str Format string to be tokenized
  1797. */
  1798. public NumberFormatStringTokenizer(String str)
  1799. {
  1800. this.str = str;
  1801. maxPosition = str.length();
  1802. }
  1803. /**
  1804. * Reset tokenizer so that nextToken() starts from the beginning.
  1805. */
  1806. public void reset()
  1807. {
  1808. currentPosition = 0;
  1809. }
  1810. /**
  1811. * Returns the next token from this string tokenizer.
  1812. *
  1813. * @return the next token from this string tokenizer.
  1814. * @throws NoSuchElementException if there are no more tokens in this
  1815. * tokenizer's string.
  1816. */
  1817. public String nextToken()
  1818. {
  1819. if (currentPosition >= maxPosition)
  1820. {
  1821. throw new NoSuchElementException();
  1822. }
  1823. int start = currentPosition;
  1824. while ((currentPosition < maxPosition)
  1825. && Character.isLetterOrDigit(str.charAt(currentPosition)))
  1826. {
  1827. currentPosition++;
  1828. }
  1829. if ((start == currentPosition)
  1830. && (!Character.isLetterOrDigit(str.charAt(currentPosition))))
  1831. {
  1832. currentPosition++;
  1833. }
  1834. return str.substring(start, currentPosition);
  1835. }
  1836. /**
  1837. * Tells if there is a digit or a letter character ahead.
  1838. *
  1839. * @return true if there is a number or character ahead.
  1840. */
  1841. public boolean isLetterOrDigitAhead()
  1842. {
  1843. int pos = currentPosition;
  1844. while (pos < maxPosition)
  1845. {
  1846. if (Character.isLetterOrDigit(str.charAt(pos)))
  1847. return true;
  1848. pos++;
  1849. }
  1850. return false;
  1851. }
  1852. /**
  1853. * Tells if there is a digit or a letter character ahead.
  1854. *
  1855. * @return true if there is a number or character ahead.
  1856. */
  1857. public boolean nextIsSep()
  1858. {
  1859. if (Character.isLetterOrDigit(str.charAt(currentPosition)))
  1860. return false;
  1861. else
  1862. return true;
  1863. }
  1864. /**
  1865. * Tells if <code>nextToken</code> will throw an exception
  1866. * if it is called.
  1867. *
  1868. * @return true if <code>nextToken</code> can be called
  1869. * without throwing an exception.
  1870. */
  1871. public boolean hasMoreTokens()
  1872. {
  1873. return (currentPosition >= maxPosition) ? false : true;
  1874. }
  1875. /**
  1876. * Calculates the number of times that this tokenizer's
  1877. * <code>nextToken</code> method can be called before it generates an
  1878. * exception.
  1879. *
  1880. * @return the number of tokens remaining in the string using the current
  1881. * delimiter set.
  1882. * @see java.util.StringTokenizer#nextToken()
  1883. */
  1884. public int countTokens()
  1885. {
  1886. int count = 0;
  1887. int currpos = currentPosition;
  1888. while (currpos < maxPosition)
  1889. {
  1890. int start = currpos;
  1891. while ((currpos < maxPosition)
  1892. && Character.isLetterOrDigit(str.charAt(currpos)))
  1893. {
  1894. currpos++;
  1895. }
  1896. if ((start == currpos)
  1897. && (Character.isLetterOrDigit(str.charAt(currpos)) == false))
  1898. {
  1899. currpos++;
  1900. }
  1901. count++;
  1902. }
  1903. return count;
  1904. }
  1905. } // end NumberFormatStringTokenizer
  1906. }