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