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