blob: 963b8325e1a0dac0f6b48f4b26738e76bd44bab9 [file] [log] [blame]
The Android Open Source Project0eec4642012-04-01 00:00:00 -07001/*
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 */
21package org.apache.xpath.objects;
22
23import java.util.Locale;
24
25import org.apache.xml.dtm.DTM;
26import org.apache.xml.utils.XMLCharacterRecognizer;
27import org.apache.xml.utils.XMLString;
28import org.apache.xml.utils.XMLStringFactory;
29import org.apache.xpath.ExpressionOwner;
30import org.apache.xpath.XPathContext;
31import 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 */
38public 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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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&uuml;gen".toUpperCase() returns "FAHRVERGN&Uuml;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 */
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 */
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 */
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 */
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 */
1117 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
1118 {
1119 visitor.visitStringLiteral(owner, this);
1120 }
1121
1122}