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 java.util.Locale; 25 26import com.sun.org.apache.xml.internal.dtm.DTM; 27import com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer; 28import com.sun.org.apache.xml.internal.utils.XMLString; 29import com.sun.org.apache.xml.internal.utils.XMLStringFactory; 30import com.sun.org.apache.xpath.internal.ExpressionOwner; 31import com.sun.org.apache.xpath.internal.XPathContext; 32import com.sun.org.apache.xpath.internal.XPathVisitor; 33 34/** 35 * This class represents an XPath string object, and is capable of 36 * converting the string to other types, such as a number. 37 * @xsl.usage general 38 */ 39public class XString extends XObject implements XMLString 40{ 41 static final long serialVersionUID = 2020470518395094525L; 42 43 /** Empty string XString object */ 44 public static final XString EMPTYSTRING = new XString(""); 45 46 /** 47 * Construct a XString object. This constructor exists for derived classes. 48 * 49 * @param val String object this will wrap. 50 */ 51 protected XString(Object val) 52 { 53 super(val); 54 } 55 56 /** 57 * Construct a XNodeSet object. 58 * 59 * @param val String object this will wrap. 60 */ 61 public XString(String val) 62 { 63 super(val); 64 } 65 66 /** 67 * Tell that this is a CLASS_STRING. 68 * 69 * @return type CLASS_STRING 70 */ 71 public int getType() 72 { 73 return CLASS_STRING; 74 } 75 76 /** 77 * Given a request type, return the equivalent string. 78 * For diagnostic purposes. 79 * 80 * @return type string "#STRING" 81 */ 82 public String getTypeString() 83 { 84 return "#STRING"; 85 } 86 87 /** 88 * Tell if this object contains a java String object. 89 * 90 * @return true if this XMLString can return a string without creating one. 91 */ 92 public boolean hasString() 93 { 94 return true; 95 } 96 97 /** 98 * Cast result object to a number. 99 * 100 * @return 0.0 if this string is null, numeric value of this string 101 * or NaN 102 */ 103 public double num() 104 { 105 return toDouble(); 106 } 107 108 /** 109 * Convert a string to a double -- Allowed input is in fixed 110 * notation ddd.fff. 111 * 112 * @return A double value representation of the string, or return Double.NaN 113 * if the string can not be converted. 114 */ 115 public double toDouble() 116 { 117 /* XMLCharacterRecognizer.isWhiteSpace(char c) methods treats the following 118 * characters as white space characters. 119 * ht - horizontal tab, nl - newline , cr - carriage return and sp - space 120 * trim() methods by default also takes care of these white space characters 121 * So trim() method is used to remove leading and trailing white spaces. 122 */ 123 XMLString s = trim(); 124 double result = Double.NaN; 125 for (int i = 0; i < s.length(); i++) 126 { 127 char c = s.charAt(i); 128 if (c != '-' && c != '.' && ( c < 0X30 || c > 0x39)) { 129 // The character is not a '-' or a '.' or a digit 130 // then return NaN because something is wrong. 131 return result; 132 } 133 } 134 try 135 { 136 result = Double.parseDouble(s.toString()); 137 } catch (NumberFormatException e){} 138 139 return result; 140} 141 142 /** 143 * Cast result object to a boolean. 144 * 145 * @return True if the length of this string object is greater 146 * than 0. 147 */ 148 public boolean bool() 149 { 150 return str().length() > 0; 151 } 152 153 /** 154 * Cast result object to a string. 155 * 156 * @return The string this wraps or the empty string if null 157 */ 158 public XMLString xstr() 159 { 160 return this; 161 } 162 163 /** 164 * Cast result object to a string. 165 * 166 * @return The string this wraps or the empty string if null 167 */ 168 public String str() 169 { 170 return (null != m_obj) ? ((String) m_obj) : ""; 171 } 172 173 /** 174 * Cast result object to a result tree fragment. 175 * 176 * @param support Xpath context to use for the conversion 177 * 178 * @return A document fragment with this string as a child node 179 */ 180 public int rtf(XPathContext support) 181 { 182 183 DTM frag = support.createDocumentFragment(); 184 185 frag.appendTextChild(str()); 186 187 return frag.getDocument(); 188 } 189 190 /** 191 * Directly call the 192 * characters method on the passed ContentHandler for the 193 * string-value. Multiple calls to the 194 * ContentHandler's characters methods may well occur for a single call to 195 * this method. 196 * 197 * @param ch A non-null reference to a ContentHandler. 198 * 199 * @throws org.xml.sax.SAXException 200 */ 201 public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch) 202 throws org.xml.sax.SAXException 203 { 204 205 String str = str(); 206 207 ch.characters(str.toCharArray(), 0, str.length()); 208 } 209 210 /** 211 * Directly call the 212 * comment method on the passed LexicalHandler for the 213 * string-value. 214 * 215 * @param lh A non-null reference to a LexicalHandler. 216 * 217 * @throws org.xml.sax.SAXException 218 */ 219 public void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh) 220 throws org.xml.sax.SAXException 221 { 222 223 String str = str(); 224 225 lh.comment(str.toCharArray(), 0, str.length()); 226 } 227 228 /** 229 * Returns the length of this string. 230 * 231 * @return the length of the sequence of characters represented by this 232 * object. 233 */ 234 public int length() 235 { 236 return str().length(); 237 } 238 239 /** 240 * Returns the character at the specified index. An index ranges 241 * from <code>0</code> to <code>length() - 1</code>. The first character 242 * of the sequence is at index <code>0</code>, the next at index 243 * <code>1</code>, and so on, as for array indexing. 244 * 245 * @param index the index of the character. 246 * @return the character at the specified index of this string. 247 * The first character is at index <code>0</code>. 248 * @exception IndexOutOfBoundsException if the <code>index</code> 249 * argument is negative or not less than the length of this 250 * string. 251 */ 252 public char charAt(int index) 253 { 254 return str().charAt(index); 255 } 256 257 /** 258 * Copies characters from this string into the destination character 259 * array. 260 * 261 * @param srcBegin index of the first character in the string 262 * to copy. 263 * @param srcEnd index after the last character in the string 264 * to copy. 265 * @param dst the destination array. 266 * @param dstBegin the start offset in the destination array. 267 * @exception IndexOutOfBoundsException If any of the following 268 * is true: 269 * <ul><li><code>srcBegin</code> is negative. 270 * <li><code>srcBegin</code> is greater than <code>srcEnd</code> 271 * <li><code>srcEnd</code> is greater than the length of this 272 * string 273 * <li><code>dstBegin</code> is negative 274 * <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than 275 * <code>dst.length</code></ul> 276 * @exception NullPointerException if <code>dst</code> is <code>null</code> 277 */ 278 public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) 279 { 280 str().getChars(srcBegin, srcEnd, dst, dstBegin); 281 } 282 283 /** 284 * Tell if two objects are functionally equal. 285 * 286 * @param obj2 Object to compare this to 287 * 288 * @return true if the two objects are equal 289 * 290 * @throws javax.xml.transform.TransformerException 291 */ 292 public boolean equals(XObject obj2) 293 { 294 295 // In order to handle the 'all' semantics of 296 // nodeset comparisons, we always call the 297 // nodeset function. 298 int t = obj2.getType(); 299 try 300 { 301 if (XObject.CLASS_NODESET == t) 302 return obj2.equals(this); 303 // If at least one object to be compared is a boolean, then each object 304 // to be compared is converted to a boolean as if by applying the 305 // boolean function. 306 else if(XObject.CLASS_BOOLEAN == t) 307 return obj2.bool() == bool(); 308 // Otherwise, if at least one object to be compared is a number, then each object 309 // to be compared is converted to a number as if by applying the number function. 310 else if(XObject.CLASS_NUMBER == t) 311 return obj2.num() == num(); 312 } 313 catch(javax.xml.transform.TransformerException te) 314 { 315 throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(te); 316 } 317 318 // Otherwise, both objects to be compared are converted to strings as 319 // if by applying the string function. 320 return xstr().equals(obj2.xstr()); 321 } 322 323 /** 324 * Compares this string to the specified <code>String</code>. 325 * The result is <code>true</code> if and only if the argument is not 326 * <code>null</code> and is a <code>String</code> object that represents 327 * the same sequence of characters as this object. 328 * 329 * @param obj2 the object to compare this <code>String</code> against. 330 * @return <code>true</code> if the <code>String</code>s are equal; 331 * <code>false</code> otherwise. 332 * @see java.lang.String#compareTo(java.lang.String) 333 * @see java.lang.String#equalsIgnoreCase(java.lang.String) 334 */ 335 public boolean equals(String obj2) { 336 return str().equals(obj2); 337 } 338 339 /** 340 * Compares this string to the specified object. 341 * The result is <code>true</code> if and only if the argument is not 342 * <code>null</code> and is a <code>String</code> object that represents 343 * the same sequence of characters as this object. 344 * 345 * @param obj2 the object to compare this <code>String</code> 346 * against. 347 * @return <code>true</code> if the <code>String </code>are equal; 348 * <code>false</code> otherwise. 349 * @see java.lang.String#compareTo(java.lang.String) 350 * @see java.lang.String#equalsIgnoreCase(java.lang.String) 351 */ 352 public boolean equals(XMLString obj2) 353 { 354 if (obj2 != null) { 355 if (!obj2.hasString()) { 356 return obj2.equals(str()); 357 } else { 358 return str().equals(obj2.toString()); 359 } 360 } 361 return false; 362 } 363 364 /** 365 * Compares this string to the specified object. 366 * The result is <code>true</code> if and only if the argument is not 367 * <code>null</code> and is a <code>String</code> object that represents 368 * the same sequence of characters as this object. 369 * 370 * @param obj2 the object to compare this <code>String</code> 371 * against. 372 * @return <code>true</code> if the <code>String </code>are equal; 373 * <code>false</code> otherwise. 374 * @see java.lang.String#compareTo(java.lang.String) 375 * @see java.lang.String#equalsIgnoreCase(java.lang.String) 376 */ 377 public boolean equals(Object obj2) 378 { 379 380 if (null == obj2) 381 return false; 382 383 // In order to handle the 'all' semantics of 384 // nodeset comparisons, we always call the 385 // nodeset function. 386 else if (obj2 instanceof XNodeSet) 387 return obj2.equals(this); 388 else if(obj2 instanceof XNumber) 389 return obj2.equals(this); 390 else 391 return str().equals(obj2.toString()); 392 } 393 394 /** 395 * Compares this <code>String</code> to another <code>String</code>, 396 * ignoring case considerations. Two strings are considered equal 397 * ignoring case if they are of the same length, and corresponding 398 * characters in the two strings are equal ignoring case. 399 * 400 * @param anotherString the <code>String</code> to compare this 401 * <code>String</code> against. 402 * @return <code>true</code> if the argument is not <code>null</code> 403 * and the <code>String</code>s are equal, 404 * ignoring case; <code>false</code> otherwise. 405 * @see #equals(Object) 406 * @see java.lang.Character#toLowerCase(char) 407 * @see java.lang.Character#toUpperCase(char) 408 */ 409 public boolean equalsIgnoreCase(String anotherString) 410 { 411 return str().equalsIgnoreCase(anotherString); 412 } 413 414 /** 415 * Compares two strings lexicographically. 416 * 417 * @param xstr the <code>String</code> to be compared. 418 * 419 * @return the value <code>0</code> if the argument string is equal to 420 * this string; a value less than <code>0</code> if this string 421 * is lexicographically less than the string argument; and a 422 * value greater than <code>0</code> if this string is 423 * lexicographically greater than the string argument. 424 * @exception java.lang.NullPointerException if <code>anotherString</code> 425 * is <code>null</code>. 426 */ 427 public int compareTo(XMLString xstr) 428 { 429 430 int len1 = this.length(); 431 int len2 = xstr.length(); 432 int n = Math.min(len1, len2); 433 int i = 0; 434 int j = 0; 435 436 while (n-- != 0) 437 { 438 char c1 = this.charAt(i); 439 char c2 = xstr.charAt(j); 440 441 if (c1 != c2) 442 { 443 return c1 - c2; 444 } 445 446 i++; 447 j++; 448 } 449 450 return len1 - len2; 451 } 452 453 /** 454 * Compares two strings lexicographically, ignoring case considerations. 455 * This method returns an integer whose sign is that of 456 * <code>this.toUpperCase().toLowerCase().compareTo( 457 * str.toUpperCase().toLowerCase())</code>. 458 * <p> 459 * Note that this method does <em>not</em> take locale into account, 460 * and will result in an unsatisfactory ordering for certain locales. 461 * The java.text package provides <em>collators</em> to allow 462 * locale-sensitive ordering. 463 * 464 * @param str the <code>String</code> to be compared. 465 * @return a negative integer, zero, or a positive integer as the 466 * the specified String is greater than, equal to, or less 467 * than this String, ignoring case considerations. 468 * @see java.text.Collator#compare(String, String) 469 * @since 1.2 470 */ 471 public int compareToIgnoreCase(XMLString str) 472 { 473 // %REVIEW% Like it says, @since 1.2. Doesn't exist in earlier 474 // versions of Java, hence we can't yet shell out to it. We can implement 475 // it as character-by-character compare, but doing so efficiently 476 // is likely to be (ahem) interesting. 477 // 478 // However, since nobody is actually _using_ this method yet: 479 // return str().compareToIgnoreCase(str.toString()); 480 481 throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException( 482 new java.lang.NoSuchMethodException( 483 "Java 1.2 method, not yet implemented")); 484 } 485 486 /** 487 * Tests if this string starts with the specified prefix beginning 488 * a specified index. 489 * 490 * @param prefix the prefix. 491 * @param toffset where to begin looking in the string. 492 * @return <code>true</code> if the character sequence represented by the 493 * argument is a prefix of the substring of this object starting 494 * at index <code>toffset</code>; <code>false</code> otherwise. 495 * The result is <code>false</code> if <code>toffset</code> is 496 * negative or greater than the length of this 497 * <code>String</code> object; otherwise the result is the same 498 * as the result of the expression 499 * <pre> 500 * this.subString(toffset).startsWith(prefix) 501 * </pre> 502 * @exception java.lang.NullPointerException if <code>prefix</code> is 503 * <code>null</code>. 504 */ 505 public boolean startsWith(String prefix, int toffset) 506 { 507 return str().startsWith(prefix, toffset); 508 } 509 510 /** 511 * Tests if this string starts with the specified prefix. 512 * 513 * @param prefix the prefix. 514 * @return <code>true</code> if the character sequence represented by the 515 * argument is a prefix of the character sequence represented by 516 * this string; <code>false</code> otherwise. 517 * Note also that <code>true</code> will be returned if the 518 * argument is an empty string or is equal to this 519 * <code>String</code> object as determined by the 520 * {@link #equals(Object)} method. 521 * @exception java.lang.NullPointerException if <code>prefix</code> is 522 * <code>null</code>. 523 */ 524 public boolean startsWith(String prefix) 525 { 526 return startsWith(prefix, 0); 527 } 528 529 /** 530 * Tests if this string starts with the specified prefix beginning 531 * a specified index. 532 * 533 * @param prefix the prefix. 534 * @param toffset where to begin looking in the string. 535 * @return <code>true</code> if the character sequence represented by the 536 * argument is a prefix of the substring of this object starting 537 * at index <code>toffset</code>; <code>false</code> otherwise. 538 * The result is <code>false</code> if <code>toffset</code> is 539 * negative or greater than the length of this 540 * <code>String</code> object; otherwise the result is the same 541 * as the result of the expression 542 * <pre> 543 * this.subString(toffset).startsWith(prefix) 544 * </pre> 545 * @exception java.lang.NullPointerException if <code>prefix</code> is 546 * <code>null</code>. 547 */ 548 public boolean startsWith(XMLString prefix, int toffset) 549 { 550 551 int to = toffset; 552 int tlim = this.length(); 553 int po = 0; 554 int pc = prefix.length(); 555 556 // Note: toffset might be near -1>>>1. 557 if ((toffset < 0) || (toffset > tlim - pc)) 558 { 559 return false; 560 } 561 562 while (--pc >= 0) 563 { 564 if (this.charAt(to) != prefix.charAt(po)) 565 { 566 return false; 567 } 568 569 to++; 570 po++; 571 } 572 573 return true; 574 } 575 576 /** 577 * Tests if this string starts with the specified prefix. 578 * 579 * @param prefix the prefix. 580 * @return <code>true</code> if the character sequence represented by the 581 * argument is a prefix of the character sequence represented by 582 * this string; <code>false</code> otherwise. 583 * Note also that <code>true</code> will be returned if the 584 * argument is an empty string or is equal to this 585 * <code>String</code> object as determined by the 586 * {@link #equals(Object)} method. 587 * @exception java.lang.NullPointerException if <code>prefix</code> is 588 * <code>null</code>. 589 */ 590 public boolean startsWith(XMLString prefix) 591 { 592 return startsWith(prefix, 0); 593 } 594 595 /** 596 * Tests if this string ends with the specified suffix. 597 * 598 * @param suffix the suffix. 599 * @return <code>true</code> if the character sequence represented by the 600 * argument is a suffix of the character sequence represented by 601 * this object; <code>false</code> otherwise. Note that the 602 * result will be <code>true</code> if the argument is the 603 * empty string or is equal to this <code>String</code> object 604 * as determined by the {@link #equals(Object)} method. 605 * @exception java.lang.NullPointerException if <code>suffix</code> is 606 * <code>null</code>. 607 */ 608 public boolean endsWith(String suffix) 609 { 610 return str().endsWith(suffix); 611 } 612 613 /** 614 * Returns a hashcode for this string. The hashcode for a 615 * <code>String</code> object is computed as 616 * <blockquote><pre> 617 * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 618 * </pre></blockquote> 619 * using <code>int</code> arithmetic, where <code>s[i]</code> is the 620 * <i>i</i>th character of the string, <code>n</code> is the length of 621 * the string, and <code>^</code> indicates exponentiation. 622 * (The hash value of the empty string is zero.) 623 * 624 * @return a hash code value for this object. 625 */ 626 public int hashCode() 627 { 628 return str().hashCode(); 629 } 630 631 /** 632 * Returns the index within this string of the first occurrence of the 633 * specified character. If a character with value <code>ch</code> occurs 634 * in the character sequence represented by this <code>String</code> 635 * object, then the index of the first such occurrence is returned -- 636 * that is, the smallest value <i>k</i> such that: 637 * <blockquote><pre> 638 * this.charAt(<i>k</i>) == ch 639 * </pre></blockquote> 640 * is <code>true</code>. If no such character occurs in this string, 641 * then <code>-1</code> is returned. 642 * 643 * @param ch a character. 644 * @return the index of the first occurrence of the character in the 645 * character sequence represented by this object, or 646 * <code>-1</code> if the character does not occur. 647 */ 648 public int indexOf(int ch) 649 { 650 return str().indexOf(ch); 651 } 652 653 /** 654 * Returns the index within this string of the first occurrence of the 655 * specified character, starting the search at the specified index. 656 * <p> 657 * If a character with value <code>ch</code> occurs in the character 658 * sequence represented by this <code>String</code> object at an index 659 * no smaller than <code>fromIndex</code>, then the index of the first 660 * such occurrence is returned--that is, the smallest value <i>k</i> 661 * such that: 662 * <blockquote><pre> 663 * (this.charAt(<i>k</i>) == ch) && (<i>k</i> >= fromIndex) 664 * </pre></blockquote> 665 * is true. If no such character occurs in this string at or after 666 * position <code>fromIndex</code>, then <code>-1</code> is returned. 667 * <p> 668 * There is no restriction on the value of <code>fromIndex</code>. If it 669 * is negative, it has the same effect as if it were zero: this entire 670 * string may be searched. If it is greater than the length of this 671 * string, it has the same effect as if it were equal to the length of 672 * this string: <code>-1</code> is returned. 673 * 674 * @param ch a character. 675 * @param fromIndex the index to start the search from. 676 * @return the index of the first occurrence of the character in the 677 * character sequence represented by this object that is greater 678 * than or equal to <code>fromIndex</code>, or <code>-1</code> 679 * if the character does not occur. 680 */ 681 public int indexOf(int ch, int fromIndex) 682 { 683 return str().indexOf(ch, fromIndex); 684 } 685 686 /** 687 * Returns the index within this string of the last occurrence of the 688 * specified character. That is, the index returned is the largest 689 * value <i>k</i> such that: 690 * <blockquote><pre> 691 * this.charAt(<i>k</i>) == ch 692 * </pre></blockquote> 693 * is true. 694 * The String is searched backwards starting at the last character. 695 * 696 * @param ch a character. 697 * @return the index of the last occurrence of the character in the 698 * character sequence represented by this object, or 699 * <code>-1</code> if the character does not occur. 700 */ 701 public int lastIndexOf(int ch) 702 { 703 return str().lastIndexOf(ch); 704 } 705 706 /** 707 * Returns the index within this string of the last occurrence of the 708 * specified character, searching backward starting at the specified 709 * index. That is, the index returned is the largest value <i>k</i> 710 * such that: 711 * <blockquote><pre> 712 * this.charAt(k) == ch) && (k <= fromIndex) 713 * </pre></blockquote> 714 * is true. 715 * 716 * @param ch a character. 717 * @param fromIndex the index to start the search from. There is no 718 * restriction on the value of <code>fromIndex</code>. If it is 719 * greater than or equal to the length of this string, it has 720 * the same effect as if it were equal to one less than the 721 * length of this string: this entire string may be searched. 722 * If it is negative, it has the same effect as if it were -1: 723 * -1 is returned. 724 * @return the index of the last occurrence of the character in the 725 * character sequence represented by this object that is less 726 * than or equal to <code>fromIndex</code>, or <code>-1</code> 727 * if the character does not occur before that point. 728 */ 729 public int lastIndexOf(int ch, int fromIndex) 730 { 731 return str().lastIndexOf(ch, fromIndex); 732 } 733 734 /** 735 * Returns the index within this string of the first occurrence of the 736 * specified substring. The integer returned is the smallest value 737 * <i>k</i> such that: 738 * <blockquote><pre> 739 * this.startsWith(str, <i>k</i>) 740 * </pre></blockquote> 741 * is <code>true</code>. 742 * 743 * @param str any string. 744 * @return if the string argument occurs as a substring within this 745 * object, then the index of the first character of the first 746 * such substring is returned; if it does not occur as a 747 * substring, <code>-1</code> is returned. 748 * @exception java.lang.NullPointerException if <code>str</code> is 749 * <code>null</code>. 750 */ 751 public int indexOf(String str) 752 { 753 return str().indexOf(str); 754 } 755 756 /** 757 * Returns the index within this string of the first occurrence of the 758 * specified substring. The integer returned is the smallest value 759 * <i>k</i> such that: 760 * <blockquote><pre> 761 * this.startsWith(str, <i>k</i>) 762 * </pre></blockquote> 763 * is <code>true</code>. 764 * 765 * @param str any string. 766 * @return if the string argument occurs as a substring within this 767 * object, then the index of the first character of the first 768 * such substring is returned; if it does not occur as a 769 * substring, <code>-1</code> is returned. 770 * @exception java.lang.NullPointerException if <code>str</code> is 771 * <code>null</code>. 772 */ 773 public int indexOf(XMLString str) 774 { 775 return str().indexOf(str.toString()); 776 } 777 778 /** 779 * Returns the index within this string of the first occurrence of the 780 * specified substring, starting at the specified index. The integer 781 * returned is the smallest value <i>k</i> such that: 782 * <blockquote><pre> 783 * this.startsWith(str, <i>k</i>) && (<i>k</i> >= fromIndex) 784 * </pre></blockquote> 785 * is <code>true</code>. 786 * <p> 787 * There is no restriction on the value of <code>fromIndex</code>. If 788 * it is negative, it has the same effect as if it were zero: this entire 789 * string may be searched. If it is greater than the length of this 790 * string, it has the same effect as if it were equal to the length of 791 * this string: <code>-1</code> is returned. 792 * 793 * @param str the substring to search for. 794 * @param fromIndex the index to start the search from. 795 * @return If the string argument occurs as a substring within this 796 * object at a starting index no smaller than 797 * <code>fromIndex</code>, then the index of the first character 798 * of the first such substring is returned. If it does not occur 799 * as a substring starting at <code>fromIndex</code> or beyond, 800 * <code>-1</code> is returned. 801 * @exception java.lang.NullPointerException if <code>str</code> is 802 * <code>null</code> 803 */ 804 public int indexOf(String str, int fromIndex) 805 { 806 return str().indexOf(str, fromIndex); 807 } 808 809 /** 810 * Returns the index within this string of the rightmost occurrence 811 * of the specified substring. The rightmost empty string "" is 812 * considered to occur at the index value <code>this.length()</code>. 813 * The returned index is the largest value <i>k</i> such that 814 * <blockquote><pre> 815 * this.startsWith(str, k) 816 * </pre></blockquote> 817 * is true. 818 * 819 * @param str the substring to search for. 820 * @return if the string argument occurs one or more times as a substring 821 * within this object, then the index of the first character of 822 * the last such substring is returned. If it does not occur as 823 * a substring, <code>-1</code> is returned. 824 * @exception java.lang.NullPointerException if <code>str</code> is 825 * <code>null</code>. 826 */ 827 public int lastIndexOf(String str) 828 { 829 return str().lastIndexOf(str); 830 } 831 832 /** 833 * Returns the index within this string of the last occurrence of 834 * the specified substring. 835 * 836 * @param str the substring to search for. 837 * @param fromIndex the index to start the search from. There is no 838 * restriction on the value of fromIndex. If it is greater than 839 * the length of this string, it has the same effect as if it 840 * were equal to the length of this string: this entire string 841 * may be searched. If it is negative, it has the same effect 842 * as if it were -1: -1 is returned. 843 * @return If the string argument occurs one or more times as a substring 844 * within this object at a starting index no greater than 845 * <code>fromIndex</code>, then the index of the first character of 846 * the last such substring is returned. If it does not occur as a 847 * substring starting at <code>fromIndex</code> or earlier, 848 * <code>-1</code> is returned. 849 * @exception java.lang.NullPointerException if <code>str</code> is 850 * <code>null</code>. 851 */ 852 public int lastIndexOf(String str, int fromIndex) 853 { 854 return str().lastIndexOf(str, fromIndex); 855 } 856 857 /** 858 * Returns a new string that is a substring of this string. The 859 * substring begins with the character at the specified index and 860 * extends to the end of this string. <p> 861 * Examples: 862 * <blockquote><pre> 863 * "unhappy".substring(2) returns "happy" 864 * "Harbison".substring(3) returns "bison" 865 * "emptiness".substring(9) returns "" (an empty string) 866 * </pre></blockquote> 867 * 868 * @param beginIndex the beginning index, inclusive. 869 * @return the specified substring. 870 * @exception IndexOutOfBoundsException if 871 * <code>beginIndex</code> is negative or larger than the 872 * length of this <code>String</code> object. 873 */ 874 public XMLString substring(int beginIndex) 875 { 876 return new XString(str().substring(beginIndex)); 877 } 878 879 /** 880 * Returns a new string that is a substring of this string. The 881 * substring begins at the specified <code>beginIndex</code> and 882 * extends to the character at index <code>endIndex - 1</code>. 883 * Thus the length of the substring is <code>endIndex-beginIndex</code>. 884 * 885 * @param beginIndex the beginning index, inclusive. 886 * @param endIndex the ending index, exclusive. 887 * @return the specified substring. 888 * @exception IndexOutOfBoundsException if the 889 * <code>beginIndex</code> is negative, or 890 * <code>endIndex</code> is larger than the length of 891 * this <code>String</code> object, or 892 * <code>beginIndex</code> is larger than 893 * <code>endIndex</code>. 894 */ 895 public XMLString substring(int beginIndex, int endIndex) 896 { 897 return new XString(str().substring(beginIndex, endIndex)); 898 } 899 900 /** 901 * Concatenates the specified string to the end of this string. 902 * 903 * @param str the <code>String</code> that is concatenated to the end 904 * of this <code>String</code>. 905 * @return a string that represents the concatenation of this object's 906 * characters followed by the string argument's characters. 907 * @exception java.lang.NullPointerException if <code>str</code> is 908 * <code>null</code>. 909 */ 910 public XMLString concat(String str) 911 { 912 913 // %REVIEW% Make an FSB here? 914 return new XString(str().concat(str)); 915 } 916 917 /** 918 * Converts all of the characters in this <code>String</code> to lower 919 * case using the rules of the given <code>Locale</code>. 920 * 921 * @param locale use the case transformation rules for this locale 922 * @return the String, converted to lowercase. 923 * @see java.lang.Character#toLowerCase(char) 924 * @see java.lang.String#toUpperCase(Locale) 925 */ 926 public XMLString toLowerCase(Locale locale) 927 { 928 return new XString(str().toLowerCase(locale)); 929 } 930 931 /** 932 * Converts all of the characters in this <code>String</code> to lower 933 * case using the rules of the default locale, which is returned 934 * by <code>Locale.getDefault</code>. 935 * <p> 936 * 937 * @return the string, converted to lowercase. 938 * @see java.lang.Character#toLowerCase(char) 939 * @see java.lang.String#toLowerCase(Locale) 940 */ 941 public XMLString toLowerCase() 942 { 943 return new XString(str().toLowerCase()); 944 } 945 946 /** 947 * Converts all of the characters in this <code>String</code> to upper 948 * case using the rules of the given locale. 949 * @param locale use the case transformation rules for this locale 950 * @return the String, converted to uppercase. 951 * @see java.lang.Character#toUpperCase(char) 952 * @see java.lang.String#toLowerCase(Locale) 953 */ 954 public XMLString toUpperCase(Locale locale) 955 { 956 return new XString(str().toUpperCase(locale)); 957 } 958 959 /** 960 * Converts all of the characters in this <code>String</code> to upper 961 * case using the rules of the default locale, which is returned 962 * by <code>Locale.getDefault</code>. 963 * 964 * <p> 965 * If no character in this string has a different uppercase version, 966 * based on calling the <code>toUpperCase</code> method defined by 967 * <code>Character</code>, then the original string is returned. 968 * <p> 969 * Otherwise, this method creates a new <code>String</code> object 970 * representing a character sequence identical in length to the 971 * character sequence represented by this <code>String</code> object and 972 * with every character equal to the result of applying the method 973 * <code>Character.toUpperCase</code> to the corresponding character of 974 * this <code>String</code> object. <p> 975 * Examples: 976 * <blockquote><pre> 977 * "Fahrvergnügen".toUpperCase() returns "FAHRVERGNÜGEN" 978 * "Visit Ljubinje!".toUpperCase() returns "VISIT LJUBINJE!" 979 * </pre></blockquote> 980 * 981 * @return the string, converted to uppercase. 982 * @see java.lang.Character#toUpperCase(char) 983 * @see java.lang.String#toUpperCase(Locale) 984 */ 985 public XMLString toUpperCase() 986 { 987 return new XString(str().toUpperCase()); 988 } 989 990 /** 991 * Removes white space from both ends of this string. 992 * 993 * @return this string, with white space removed from the front and end. 994 */ 995 public XMLString trim() 996 { 997 return new XString(str().trim()); 998 } 999 1000 /** 1001 * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition 1002 * of whitespace. Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S"> 1003 * the definition of <CODE>S</CODE></A> for details. 1004 * @param ch Character to check as XML whitespace. 1005 * @return =true if <var>ch</var> is XML whitespace; otherwise =false. 1006 */ 1007 private static boolean isSpace(char ch) 1008 { 1009 return XMLCharacterRecognizer.isWhiteSpace(ch); // Take the easy way out for now. 1010 } 1011 1012 /** 1013 * Conditionally trim all leading and trailing whitespace in the specified String. 1014 * All strings of white space are 1015 * replaced by a single space character (#x20), except spaces after punctuation which 1016 * receive double spaces if doublePunctuationSpaces is true. 1017 * This function may be useful to a formatter, but to get first class 1018 * results, the formatter should probably do it's own white space handling 1019 * based on the semantics of the formatting object. 1020 * 1021 * @param trimHead Trim leading whitespace? 1022 * @param trimTail Trim trailing whitespace? 1023 * @param doublePunctuationSpaces Use double spaces for punctuation? 1024 * @return The trimmed string. 1025 */ 1026 public XMLString fixWhiteSpace(boolean trimHead, boolean trimTail, 1027 boolean doublePunctuationSpaces) 1028 { 1029 1030 // %OPT% !!!!!!! 1031 int len = this.length(); 1032 char[] buf = new char[len]; 1033 1034 this.getChars(0, len, buf, 0); 1035 1036 boolean edit = false; 1037 int s; 1038 1039 for (s = 0; s < len; s++) 1040 { 1041 if (isSpace(buf[s])) 1042 { 1043 break; 1044 } 1045 } 1046 1047 /* replace S to ' '. and ' '+ -> single ' '. */ 1048 int d = s; 1049 boolean pres = false; 1050 1051 for (; s < len; s++) 1052 { 1053 char c = buf[s]; 1054 1055 if (isSpace(c)) 1056 { 1057 if (!pres) 1058 { 1059 if (' ' != c) 1060 { 1061 edit = true; 1062 } 1063 1064 buf[d++] = ' '; 1065 1066 if (doublePunctuationSpaces && (s != 0)) 1067 { 1068 char prevChar = buf[s - 1]; 1069 1070 if (!((prevChar == '.') || (prevChar == '!') 1071 || (prevChar == '?'))) 1072 { 1073 pres = true; 1074 } 1075 } 1076 else 1077 { 1078 pres = true; 1079 } 1080 } 1081 else 1082 { 1083 edit = true; 1084 pres = true; 1085 } 1086 } 1087 else 1088 { 1089 buf[d++] = c; 1090 pres = false; 1091 } 1092 } 1093 1094 if (trimTail && 1 <= d && ' ' == buf[d - 1]) 1095 { 1096 edit = true; 1097 1098 d--; 1099 } 1100 1101 int start = 0; 1102 1103 if (trimHead && 0 < d && ' ' == buf[0]) 1104 { 1105 edit = true; 1106 1107 start++; 1108 } 1109 1110 XMLStringFactory xsf = XMLStringFactoryImpl.getFactory(); 1111 1112 return edit ? xsf.newstr(new String(buf, start, d - start)) : this; 1113 } 1114 1115 /** 1116 * @see com.sun.org.apache.xpath.internal.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor) 1117 */ 1118 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) 1119 { 1120 visitor.visitStringLiteral(owner, this); 1121 } 1122 1123} 1124