1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: XStringForFSB.java,v 1.20 2004/02/17 04:34:38 minchau Exp $
  18. */
  19. package com.sun.org.apache.xpath.internal.objects;
  20. import com.sun.org.apache.xalan.internal.res.XSLMessages;
  21. import com.sun.org.apache.xml.internal.utils.FastStringBuffer;
  22. import com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer;
  23. import com.sun.org.apache.xml.internal.utils.XMLString;
  24. import com.sun.org.apache.xml.internal.utils.XMLStringFactory;
  25. import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  26. /**
  27. * This class will wrap a FastStringBuffer and allow for
  28. */
  29. public class XStringForFSB extends XString
  30. {
  31. /** The start position in the fsb. */
  32. int m_start;
  33. /** The length of the string. */
  34. int m_length;
  35. /** If the str() function is called, the string will be cached here. */
  36. protected String m_strCache = null;
  37. /** cached hash code */
  38. protected int m_hash = 0;
  39. /**
  40. * Construct a XNodeSet object.
  41. *
  42. * @param val FastStringBuffer object this will wrap, must be non-null.
  43. * @param start The start position in the array.
  44. * @param length The number of characters to read from the array.
  45. */
  46. public XStringForFSB(FastStringBuffer val, int start, int length)
  47. {
  48. super(val);
  49. m_start = start;
  50. m_length = length;
  51. if (null == val)
  52. throw new IllegalArgumentException(
  53. XSLMessages.createXPATHMessage(XPATHErrorResources.ER_FASTSTRINGBUFFER_CANNOT_BE_NULL, null));
  54. }
  55. /**
  56. * Construct a XNodeSet object.
  57. *
  58. * @param val String object this will wrap.
  59. */
  60. private XStringForFSB(String val)
  61. {
  62. super(val);
  63. throw new IllegalArgumentException(
  64. XSLMessages.createXPATHMessage(XPATHErrorResources.ER_FSB_CANNOT_TAKE_STRING, null)); // "XStringForFSB can not take a string for an argument!");
  65. }
  66. /**
  67. * Cast result object to a string.
  68. *
  69. * @return The string this wraps or the empty string if null
  70. */
  71. public FastStringBuffer fsb()
  72. {
  73. return ((FastStringBuffer) m_obj);
  74. }
  75. /**
  76. * Cast result object to a string.
  77. *
  78. * @return The string this wraps or the empty string if null
  79. */
  80. public void appendToFsb(com.sun.org.apache.xml.internal.utils.FastStringBuffer fsb)
  81. {
  82. // %OPT% !!! FSB has to be updated to take partial fsb's for append.
  83. fsb.append(str());
  84. }
  85. /**
  86. * Tell if this object contains a java String object.
  87. *
  88. * @return true if this XMLString can return a string without creating one.
  89. */
  90. public boolean hasString()
  91. {
  92. return (null != m_strCache);
  93. }
  94. // /** NEEDSDOC Field strCount */
  95. // public static int strCount = 0;
  96. //
  97. // /** NEEDSDOC Field xtable */
  98. // static java.util.Hashtable xtable = new java.util.Hashtable();
  99. /**
  100. * Since this object is incomplete without the length and the offset, we
  101. * have to convert to a string when this function is called.
  102. *
  103. * @return The java String representation of this object.
  104. */
  105. public Object object()
  106. {
  107. return str();
  108. }
  109. /**
  110. * Cast result object to a string.
  111. *
  112. * @return The string this wraps or the empty string if null
  113. */
  114. public String str()
  115. {
  116. if (null == m_strCache)
  117. {
  118. m_strCache = fsb().getString(m_start, m_length);
  119. // strCount++;
  120. //
  121. // RuntimeException e = new RuntimeException("Bad! Bad!");
  122. // java.io.CharArrayWriter writer = new java.io.CharArrayWriter();
  123. // java.io.PrintWriter pw = new java.io.PrintWriter(writer);
  124. //
  125. // e.printStackTrace(pw);
  126. //
  127. // String str = writer.toString();
  128. //
  129. // str = str.substring(0, 600);
  130. //
  131. // if (null == xtable.get(str))
  132. // {
  133. // xtable.put(str, str);
  134. // System.out.println(str);
  135. // }
  136. // System.out.println("strCount: " + strCount);
  137. // throw e;
  138. // e.printStackTrace();
  139. // System.exit(-1);
  140. }
  141. return m_strCache;
  142. }
  143. /**
  144. * Directly call the
  145. * characters method on the passed ContentHandler for the
  146. * string-value. Multiple calls to the
  147. * ContentHandler's characters methods may well occur for a single call to
  148. * this method.
  149. *
  150. * @param ch A non-null reference to a ContentHandler.
  151. *
  152. * @throws org.xml.sax.SAXException
  153. */
  154. public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
  155. throws org.xml.sax.SAXException
  156. {
  157. fsb().sendSAXcharacters(ch, m_start, m_length);
  158. }
  159. /**
  160. * Directly call the
  161. * comment method on the passed LexicalHandler for the
  162. * string-value.
  163. *
  164. * @param lh A non-null reference to a LexicalHandler.
  165. *
  166. * @throws org.xml.sax.SAXException
  167. */
  168. public void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh)
  169. throws org.xml.sax.SAXException
  170. {
  171. fsb().sendSAXComment(lh, m_start, m_length);
  172. }
  173. /**
  174. * Returns the length of this string.
  175. *
  176. * @return the length of the sequence of characters represented by this
  177. * object.
  178. */
  179. public int length()
  180. {
  181. return m_length;
  182. }
  183. /**
  184. * Returns the character at the specified index. An index ranges
  185. * from <code>0</code> to <code>length() - 1</code>. The first character
  186. * of the sequence is at index <code>0</code>, the next at index
  187. * <code>1</code>, and so on, as for array indexing.
  188. *
  189. * @param index the index of the character.
  190. * @return the character at the specified index of this string.
  191. * The first character is at index <code>0</code>.
  192. * @exception IndexOutOfBoundsException if the <code>index</code>
  193. * argument is negative or not less than the length of this
  194. * string.
  195. */
  196. public char charAt(int index)
  197. {
  198. return fsb().charAt(m_start + index);
  199. }
  200. /**
  201. * Copies characters from this string into the destination character
  202. * array.
  203. *
  204. * @param srcBegin index of the first character in the string
  205. * to copy.
  206. * @param srcEnd index after the last character in the string
  207. * to copy.
  208. * @param dst the destination array.
  209. * @param dstBegin the start offset in the destination array.
  210. * @exception IndexOutOfBoundsException If any of the following
  211. * is true:
  212. * <ul><li><code>srcBegin</code> is negative.
  213. * <li><code>srcBegin</code> is greater than <code>srcEnd</code>
  214. * <li><code>srcEnd</code> is greater than the length of this
  215. * string
  216. * <li><code>dstBegin</code> is negative
  217. * <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
  218. * <code>dst.length</code></ul>
  219. * @exception NullPointerException if <code>dst</code> is <code>null</code>
  220. */
  221. public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
  222. {
  223. // %OPT% Need to call this on FSB when it is implemented.
  224. // %UNTESTED% (I don't think anyone calls this yet?)
  225. int n = srcEnd - srcBegin;
  226. if (n > m_length)
  227. n = m_length;
  228. if (n > (dst.length - dstBegin))
  229. n = (dst.length - dstBegin);
  230. int end = srcBegin + m_start + n;
  231. int d = dstBegin;
  232. FastStringBuffer fsb = fsb();
  233. for (int i = srcBegin + m_start; i < end; i++)
  234. {
  235. dst[d++] = fsb.charAt(i);
  236. }
  237. }
  238. /**
  239. * Compares this string to the specified object.
  240. * The result is <code>true</code> if and only if the argument is not
  241. * <code>null</code> and is a <code>String</code> object that represents
  242. * the same sequence of characters as this object.
  243. *
  244. * @param anObject the object to compare this <code>String</code>
  245. * against.
  246. *
  247. * NEEDSDOC @param obj2
  248. * @return <code>true</code> if the <code>String </code>are equal;
  249. * <code>false</code> otherwise.
  250. * @see java.lang.String#compareTo(java.lang.String)
  251. * @see java.lang.String#equalsIgnoreCase(java.lang.String)
  252. */
  253. public boolean equals(XMLString obj2)
  254. {
  255. if (this == obj2)
  256. {
  257. return true;
  258. }
  259. int n = m_length;
  260. if (n == obj2.length())
  261. {
  262. FastStringBuffer fsb = fsb();
  263. int i = m_start;
  264. int j = 0;
  265. while (n-- != 0)
  266. {
  267. if (fsb.charAt(i) != obj2.charAt(j))
  268. {
  269. return false;
  270. }
  271. i++;
  272. j++;
  273. }
  274. return true;
  275. }
  276. return false;
  277. }
  278. /**
  279. * Tell if two objects are functionally equal.
  280. *
  281. * @param obj2 Object to compare this to
  282. *
  283. * @return true if the two objects are equal
  284. *
  285. * @throws javax.xml.transform.TransformerException
  286. */
  287. public boolean equals(XObject obj2)
  288. {
  289. if (this == obj2)
  290. {
  291. return true;
  292. }
  293. if(obj2.getType() == XObject.CLASS_NUMBER)
  294. return obj2.equals(this);
  295. String str = obj2.str();
  296. int n = m_length;
  297. if (n == str.length())
  298. {
  299. FastStringBuffer fsb = fsb();
  300. int i = m_start;
  301. int j = 0;
  302. while (n-- != 0)
  303. {
  304. if (fsb.charAt(i) != str.charAt(j))
  305. {
  306. return false;
  307. }
  308. i++;
  309. j++;
  310. }
  311. return true;
  312. }
  313. return false;
  314. }
  315. /**
  316. * Tell if two objects are functionally equal.
  317. *
  318. * @param obj2 Object to compare this to
  319. *
  320. * NEEDSDOC @param anotherString
  321. *
  322. * @return true if the two objects are equal
  323. *
  324. * @throws javax.xml.transform.TransformerException
  325. */
  326. public boolean equals(String anotherString)
  327. {
  328. int n = m_length;
  329. if (n == anotherString.length())
  330. {
  331. FastStringBuffer fsb = fsb();
  332. int i = m_start;
  333. int j = 0;
  334. while (n-- != 0)
  335. {
  336. if (fsb.charAt(i) != anotherString.charAt(j))
  337. {
  338. return false;
  339. }
  340. i++;
  341. j++;
  342. }
  343. return true;
  344. }
  345. return false;
  346. }
  347. /**
  348. * Compares this string to the specified object.
  349. * The result is <code>true</code> if and only if the argument is not
  350. * <code>null</code> and is a <code>String</code> object that represents
  351. * the same sequence of characters as this object.
  352. *
  353. * @param anObject the object to compare this <code>String</code>
  354. * against.
  355. *
  356. * NEEDSDOC @param obj2
  357. * @return <code>true</code> if the <code>String </code>are equal;
  358. * <code>false</code> otherwise.
  359. * @see java.lang.String#compareTo(java.lang.String)
  360. * @see java.lang.String#equalsIgnoreCase(java.lang.String)
  361. */
  362. public boolean equals(Object obj2)
  363. {
  364. if (null == obj2)
  365. return false;
  366. if(obj2 instanceof XNumber)
  367. return obj2.equals(this);
  368. // In order to handle the 'all' semantics of
  369. // nodeset comparisons, we always call the
  370. // nodeset function.
  371. else if (obj2 instanceof XNodeSet)
  372. return obj2.equals(this);
  373. else if (obj2 instanceof XStringForFSB)
  374. return equals((XMLString) this);
  375. else
  376. return equals(obj2.toString());
  377. }
  378. /**
  379. * Compares this <code>String</code> to another <code>String</code>,
  380. * ignoring case considerations. Two strings are considered equal
  381. * ignoring case if they are of the same length, and corresponding
  382. * characters in the two strings are equal ignoring case.
  383. *
  384. * @param anotherString the <code>String</code> to compare this
  385. * <code>String</code> against.
  386. * @return <code>true</code> if the argument is not <code>null</code>
  387. * and the <code>String</code>s are equal,
  388. * ignoring case; <code>false</code> otherwise.
  389. * @see #equals(Object)
  390. * @see java.lang.Character#toLowerCase(char)
  391. * @see java.lang.Character#toUpperCase(char)
  392. */
  393. public boolean equalsIgnoreCase(String anotherString)
  394. {
  395. return (m_length == anotherString.length())
  396. ? str().equalsIgnoreCase(anotherString) : false;
  397. }
  398. /**
  399. * Compares two strings lexicographically.
  400. *
  401. * @param anotherString the <code>String</code> to be compared.
  402. *
  403. * NEEDSDOC @param xstr
  404. * @return the value <code>0</code> if the argument string is equal to
  405. * this string; a value less than <code>0</code> if this string
  406. * is lexicographically less than the string argument; and a
  407. * value greater than <code>0</code> if this string is
  408. * lexicographically greater than the string argument.
  409. * @exception java.lang.NullPointerException if <code>anotherString</code>
  410. * is <code>null</code>.
  411. */
  412. public int compareTo(XMLString xstr)
  413. {
  414. int len1 = m_length;
  415. int len2 = xstr.length();
  416. int n = Math.min(len1, len2);
  417. FastStringBuffer fsb = fsb();
  418. int i = m_start;
  419. int j = 0;
  420. while (n-- != 0)
  421. {
  422. char c1 = fsb.charAt(i);
  423. char c2 = xstr.charAt(j);
  424. if (c1 != c2)
  425. {
  426. return c1 - c2;
  427. }
  428. i++;
  429. j++;
  430. }
  431. return len1 - len2;
  432. }
  433. /**
  434. * Compares two strings lexicographically, ignoring case considerations.
  435. * This method returns an integer whose sign is that of
  436. * <code>this.toUpperCase().toLowerCase().compareTo(
  437. * str.toUpperCase().toLowerCase())</code>.
  438. * <p>
  439. * Note that this method does <em>not</em> take locale into account,
  440. * and will result in an unsatisfactory ordering for certain locales.
  441. * The java.text package provides <em>collators</em> to allow
  442. * locale-sensitive ordering.
  443. *
  444. * @param str the <code>String</code> to be compared.
  445. *
  446. * NEEDSDOC @param xstr
  447. * @return a negative integer, zero, or a positive integer as the
  448. * the specified String is greater than, equal to, or less
  449. * than this String, ignoring case considerations.
  450. * @see java.text.Collator#compare(String, String)
  451. * @since 1.2
  452. */
  453. public int compareToIgnoreCase(XMLString xstr)
  454. {
  455. int len1 = m_length;
  456. int len2 = xstr.length();
  457. int n = Math.min(len1, len2);
  458. FastStringBuffer fsb = fsb();
  459. int i = m_start;
  460. int j = 0;
  461. while (n-- != 0)
  462. {
  463. char c1 = Character.toLowerCase(fsb.charAt(i));
  464. char c2 = Character.toLowerCase(xstr.charAt(j));
  465. if (c1 != c2)
  466. {
  467. return c1 - c2;
  468. }
  469. i++;
  470. j++;
  471. }
  472. return len1 - len2;
  473. }
  474. /**
  475. * Returns a hashcode for this string. The hashcode for a
  476. * <code>String</code> object is computed as
  477. * <blockquote><pre>
  478. * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
  479. * </pre></blockquote>
  480. * using <code>int</code> arithmetic, where <code>s[i]</code> is the
  481. * <i>i</i>th character of the string, <code>n</code> is the length of
  482. * the string, and <code>^</code> indicates exponentiation.
  483. * (The hash value of the empty string is zero.)
  484. *
  485. * @return a hash code value for this object.
  486. */
  487. public int hashCode()
  488. {
  489. // Commenting this out because in JDK1.1.8 and VJ++
  490. // we don't match XMLStrings. Defaulting to the super
  491. // causes us to create a string, but at this point
  492. // this only seems to get called in key processing.
  493. // Maybe we can live with it?
  494. /*
  495. int h = m_hash;
  496. if (h == 0)
  497. {
  498. int off = m_start;
  499. int len = m_length;
  500. FastStringBuffer fsb = fsb();
  501. for (int i = 0; i < len; i++)
  502. {
  503. h = 31 * h + fsb.charAt(off);
  504. off++;
  505. }
  506. m_hash = h;
  507. }
  508. */
  509. return super.hashCode(); // h;
  510. }
  511. /**
  512. * Tests if this string starts with the specified prefix beginning
  513. * a specified index.
  514. *
  515. * @param prefix the prefix.
  516. * @param toffset where to begin looking in the string.
  517. * @return <code>true</code> if the character sequence represented by the
  518. * argument is a prefix of the substring of this object starting
  519. * at index <code>toffset</code> <code>false</code> otherwise.
  520. * The result is <code>false</code> if <code>toffset</code> is
  521. * negative or greater than the length of this
  522. * <code>String</code> object; otherwise the result is the same
  523. * as the result of the expression
  524. * <pre>
  525. * this.subString(toffset).startsWith(prefix)
  526. * </pre>
  527. * @exception java.lang.NullPointerException if <code>prefix</code> is
  528. * <code>null</code>.
  529. */
  530. public boolean startsWith(XMLString prefix, int toffset)
  531. {
  532. FastStringBuffer fsb = fsb();
  533. int to = m_start + toffset;
  534. int tlim = m_start + m_length;
  535. int po = 0;
  536. int pc = prefix.length();
  537. // Note: toffset might be near -1>>>1.
  538. if ((toffset < 0) || (toffset > m_length - pc))
  539. {
  540. return false;
  541. }
  542. while (--pc >= 0)
  543. {
  544. if (fsb.charAt(to) != prefix.charAt(po))
  545. {
  546. return false;
  547. }
  548. to++;
  549. po++;
  550. }
  551. return true;
  552. }
  553. /**
  554. * Tests if this string starts with the specified prefix.
  555. *
  556. * @param prefix the prefix.
  557. * @return <code>true</code> if the character sequence represented by the
  558. * argument is a prefix of the character sequence represented by
  559. * this string; <code>false</code> otherwise.
  560. * Note also that <code>true</code> will be returned if the
  561. * argument is an empty string or is equal to this
  562. * <code>String</code> object as determined by the
  563. * {@link #equals(Object)} method.
  564. * @exception java.lang.NullPointerException if <code>prefix</code> is
  565. * <code>null</code>.
  566. * @since JDK1. 0
  567. */
  568. public boolean startsWith(XMLString prefix)
  569. {
  570. return startsWith(prefix, 0);
  571. }
  572. /**
  573. * Returns the index within this string of the first occurrence of the
  574. * specified character. If a character with value <code>ch</code> occurs
  575. * in the character sequence represented by this <code>String</code>
  576. * object, then the index of the first such occurrence is returned --
  577. * that is, the smallest value <i>k</i> such that:
  578. * <blockquote><pre>
  579. * this.charAt(<i>k</i>) == ch
  580. * </pre></blockquote>
  581. * is <code>true</code>. If no such character occurs in this string,
  582. * then <code>-1</code> is returned.
  583. *
  584. * @param ch a character.
  585. * @return the index of the first occurrence of the character in the
  586. * character sequence represented by this object, or
  587. * <code>-1</code> if the character does not occur.
  588. */
  589. public int indexOf(int ch)
  590. {
  591. return indexOf(ch, 0);
  592. }
  593. /**
  594. * Returns the index within this string of the first occurrence of the
  595. * specified character, starting the search at the specified index.
  596. * <p>
  597. * If a character with value <code>ch</code> occurs in the character
  598. * sequence represented by this <code>String</code> object at an index
  599. * no smaller than <code>fromIndex</code>, then the index of the first
  600. * such occurrence is returned--that is, the smallest value <i>k</i>
  601. * such that:
  602. * <blockquote><pre>
  603. * (this.charAt(<i>k</i>) == ch) && (<i>k</i> >= fromIndex)
  604. * </pre></blockquote>
  605. * is true. If no such character occurs in this string at or after
  606. * position <code>fromIndex</code>, then <code>-1</code> is returned.
  607. * <p>
  608. * There is no restriction on the value of <code>fromIndex</code>. If it
  609. * is negative, it has the same effect as if it were zero: this entire
  610. * string may be searched. If it is greater than the length of this
  611. * string, it has the same effect as if it were equal to the length of
  612. * this string: <code>-1</code> is returned.
  613. *
  614. * @param ch a character.
  615. * @param fromIndex the index to start the search from.
  616. * @return the index of the first occurrence of the character in the
  617. * character sequence represented by this object that is greater
  618. * than or equal to <code>fromIndex</code>, or <code>-1</code>
  619. * if the character does not occur.
  620. */
  621. public int indexOf(int ch, int fromIndex)
  622. {
  623. int max = m_start + m_length;
  624. FastStringBuffer fsb = fsb();
  625. if (fromIndex < 0)
  626. {
  627. fromIndex = 0;
  628. }
  629. else if (fromIndex >= m_length)
  630. {
  631. // Note: fromIndex might be near -1>>>1.
  632. return -1;
  633. }
  634. for (int i = m_start + fromIndex; i < max; i++)
  635. {
  636. if (fsb.charAt(i) == ch)
  637. {
  638. return i - m_start;
  639. }
  640. }
  641. return -1;
  642. }
  643. /**
  644. * Returns a new string that is a substring of this string. The
  645. * substring begins with the character at the specified index and
  646. * extends to the end of this string. <p>
  647. * Examples:
  648. * <blockquote><pre>
  649. * "unhappy".substring(2) returns "happy"
  650. * "Harbison".substring(3) returns "bison"
  651. * "emptiness".substring(9) returns "" (an empty string)
  652. * </pre></blockquote>
  653. *
  654. * @param beginIndex the beginning index, inclusive.
  655. * @return the specified substring.
  656. * @exception IndexOutOfBoundsException if
  657. * <code>beginIndex</code> is negative or larger than the
  658. * length of this <code>String</code> object.
  659. */
  660. public XMLString substring(int beginIndex)
  661. {
  662. int len = m_length - beginIndex;
  663. if (len <= 0)
  664. return XString.EMPTYSTRING;
  665. else
  666. {
  667. int start = m_start + beginIndex;
  668. return new XStringForFSB(fsb(), start, len);
  669. }
  670. }
  671. /**
  672. * Returns a new string that is a substring of this string. The
  673. * substring begins at the specified <code>beginIndex</code> and
  674. * extends to the character at index <code>endIndex - 1</code>.
  675. * Thus the length of the substring is <code>endIndex-beginIndex</code>.
  676. *
  677. * @param beginIndex the beginning index, inclusive.
  678. * @param endIndex the ending index, exclusive.
  679. * @return the specified substring.
  680. * @exception IndexOutOfBoundsException if the
  681. * <code>beginIndex</code> is negative, or
  682. * <code>endIndex</code> is larger than the length of
  683. * this <code>String</code> object, or
  684. * <code>beginIndex</code> is larger than
  685. * <code>endIndex</code>.
  686. */
  687. public XMLString substring(int beginIndex, int endIndex)
  688. {
  689. int len = endIndex - beginIndex;
  690. if (len > m_length)
  691. len = m_length;
  692. if (len <= 0)
  693. return XString.EMPTYSTRING;
  694. else
  695. {
  696. int start = m_start + beginIndex;
  697. return new XStringForFSB(fsb(), start, len);
  698. }
  699. }
  700. /**
  701. * Concatenates the specified string to the end of this string.
  702. *
  703. * @param str the <code>String</code> that is concatenated to the end
  704. * of this <code>String</code>.
  705. * @return a string that represents the concatenation of this object's
  706. * characters followed by the string argument's characters.
  707. * @exception java.lang.NullPointerException if <code>str</code> is
  708. * <code>null</code>.
  709. */
  710. public XMLString concat(String str)
  711. {
  712. // %OPT% Make an FSB here?
  713. return new XString(str().concat(str));
  714. }
  715. /**
  716. * Removes white space from both ends of this string.
  717. *
  718. * @return this string, with white space removed from the front and end.
  719. */
  720. public XMLString trim()
  721. {
  722. return fixWhiteSpace(true, true, false);
  723. }
  724. /**
  725. * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition
  726. * of whitespace. Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
  727. * the definition of <CODE>S</CODE></A> for details.
  728. * @param ch Character to check as XML whitespace.
  729. * @return =true if <var>ch</var> is XML whitespace; otherwise =false.
  730. */
  731. private static boolean isSpace(char ch)
  732. {
  733. return XMLCharacterRecognizer.isWhiteSpace(ch); // Take the easy way out for now.
  734. }
  735. /**
  736. * Conditionally trim all leading and trailing whitespace in the specified String.
  737. * All strings of white space are
  738. * replaced by a single space character (#x20), except spaces after punctuation which
  739. * receive double spaces if doublePunctuationSpaces is true.
  740. * This function may be useful to a formatter, but to get first class
  741. * results, the formatter should probably do it's own white space handling
  742. * based on the semantics of the formatting object.
  743. *
  744. * @param trimHead Trim leading whitespace?
  745. * @param trimTail Trim trailing whitespace?
  746. * @param doublePunctuationSpaces Use double spaces for punctuation?
  747. * @return The trimmed string.
  748. */
  749. public XMLString fixWhiteSpace(boolean trimHead, boolean trimTail,
  750. boolean doublePunctuationSpaces)
  751. {
  752. int end = m_length + m_start;
  753. char[] buf = new char[m_length];
  754. FastStringBuffer fsb = fsb();
  755. boolean edit = false;
  756. /* replace S to ' '. and ' '+ -> single ' '. */
  757. int d = 0;
  758. boolean pres = false;
  759. for (int s = m_start; s < end; s++)
  760. {
  761. char c = fsb.charAt(s);
  762. if (isSpace(c))
  763. {
  764. if (!pres)
  765. {
  766. if (' ' != c)
  767. {
  768. edit = true;
  769. }
  770. buf[d++] = ' ';
  771. if (doublePunctuationSpaces && (d != 0))
  772. {
  773. char prevChar = buf[d - 1];
  774. if (!((prevChar == '.') || (prevChar == '!')
  775. || (prevChar == '?')))
  776. {
  777. pres = true;
  778. }
  779. }
  780. else
  781. {
  782. pres = true;
  783. }
  784. }
  785. else
  786. {
  787. edit = true;
  788. pres = true;
  789. }
  790. }
  791. else
  792. {
  793. buf[d++] = c;
  794. pres = false;
  795. }
  796. }
  797. if (trimTail && 1 <= d && ' ' == buf[d - 1])
  798. {
  799. edit = true;
  800. d--;
  801. }
  802. int start = 0;
  803. if (trimHead && 0 < d && ' ' == buf[0])
  804. {
  805. edit = true;
  806. start++;
  807. }
  808. XMLStringFactory xsf = XMLStringFactoryImpl.getFactory();
  809. return edit ? xsf.newstr(buf, start, d - start) : this;
  810. }
  811. /**
  812. * Convert a string to a double -- Allowed input is in fixed
  813. * notation ddd.fff.
  814. *
  815. * %OPT% CHECK PERFORMANCE against generating a Java String and
  816. * converting it to double. The advantage of running in native
  817. * machine code -- perhaps even microcode, on some systems -- may
  818. * more than make up for the cost of allocating and discarding the
  819. * additional object. We need to benchmark this.
  820. *
  821. * %OPT% More importantly, we need to decide whether we _care_ about
  822. * the performance of this operation. Does XString.toDouble constitute
  823. * any measurable percentage of our typical runtime? I suspect not!
  824. *
  825. * @return A double value representation of the string, or return Double.NaN
  826. * if the string can not be converted. */
  827. public double toDouble()
  828. {
  829. if(m_length == 0)
  830. return Double.NaN;
  831. int i;
  832. char c;
  833. String valueString = fsb().getString(m_start,m_length);
  834. // The following are permitted in the Double.valueOf, but not by the XPath spec:
  835. // - a plus sign
  836. // - The use of e or E to indicate exponents
  837. // - trailing f, F, d, or D
  838. // See function comments; not sure if this is slower than actually doing the
  839. // conversion ourselves (as was before).
  840. for (i=0;i<m_length;i++)
  841. if (!XMLCharacterRecognizer.isWhiteSpace(valueString.charAt(i)))
  842. break;
  843. if (valueString.charAt(i) == '-')
  844. i++;
  845. for (;i<m_length;i++) {
  846. c = valueString.charAt(i);
  847. if (c != '.' && (c < '0' || c > '9'))
  848. break;
  849. }
  850. for (;i<m_length;i++)
  851. if (!XMLCharacterRecognizer.isWhiteSpace(valueString.charAt(i)))
  852. break;
  853. if (i != m_length)
  854. return Double.NaN;
  855. try {
  856. return new Double(valueString).doubleValue();
  857. } catch (NumberFormatException nfe) {
  858. // This should catch double periods, empty strings.
  859. return Double.NaN;
  860. }
  861. }
  862. }