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 */
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}
1123