PageRenderTime 59ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/java-1.7.0-openjdk/openjdk/jaxp/src/com/sun/org/apache/xpath/internal/objects/XStringForFSB.java

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