0001: /*
0002: * Copyright 1999-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: /*
0017: * $Id: XString.java,v 1.20 2005/07/25 18:19:33 johng Exp $
0018: */
0019: package org.apache.xpath.objects;
0020:
0021: import java.util.Locale;
0022:
0023: import org.apache.xml.dtm.DTM;
0024: import org.apache.xml.utils.XMLCharacterRecognizer;
0025: import org.apache.xml.utils.XMLString;
0026: import org.apache.xml.utils.XMLStringFactory;
0027: import org.apache.xpath.ExpressionOwner;
0028: import org.apache.xpath.XPathContext;
0029: import org.apache.xpath.XPathVisitor;
0030:
0031: /**
0032: * This class represents an XPath string object, and is capable of
0033: * converting the string to other types, such as a number.
0034: * @xsl.usage general
0035: */
0036: public class XString extends XObject implements XMLString {
0037: static final long serialVersionUID = 2020470518395094525L;
0038:
0039: /** Empty string XString object */
0040: public static final XString EMPTYSTRING = new XString("");
0041:
0042: /**
0043: * Construct a XString object. This constructor exists for derived classes.
0044: *
0045: * @param val String object this will wrap.
0046: */
0047: protected XString(Object val) {
0048: super (val);
0049: }
0050:
0051: /**
0052: * Construct a XNodeSet object.
0053: *
0054: * @param val String object this will wrap.
0055: */
0056: public XString(String val) {
0057: super (val);
0058: }
0059:
0060: /**
0061: * Tell that this is a CLASS_STRING.
0062: *
0063: * @return type CLASS_STRING
0064: */
0065: public int getType() {
0066: return CLASS_STRING;
0067: }
0068:
0069: /**
0070: * Given a request type, return the equivalent string.
0071: * For diagnostic purposes.
0072: *
0073: * @return type string "#STRING"
0074: */
0075: public String getTypeString() {
0076: return "#STRING";
0077: }
0078:
0079: /**
0080: * Tell if this object contains a java String object.
0081: *
0082: * @return true if this XMLString can return a string without creating one.
0083: */
0084: public boolean hasString() {
0085: return true;
0086: }
0087:
0088: /**
0089: * Cast result object to a number.
0090: *
0091: * @return 0.0 if this string is null, numeric value of this string
0092: * or NaN
0093: */
0094: public double num() {
0095: return toDouble();
0096: }
0097:
0098: /**
0099: * Convert a string to a double -- Allowed input is in fixed
0100: * notation ddd.fff.
0101: *
0102: * @return A double value representation of the string, or return Double.NaN
0103: * if the string can not be converted.
0104: */
0105: public double toDouble() {
0106: /* XMLCharacterRecognizer.isWhiteSpace(char c) methods treats the following
0107: * characters as white space characters.
0108: * ht - horizontal tab, nl - newline , cr - carriage return and sp - space
0109: * trim() methods by default also takes care of these white space characters
0110: * So trim() method is used to remove leading and trailing white spaces.
0111: */
0112: XMLString s = trim();
0113: double result = Double.NaN;
0114: for (int i = 0; i < s.length(); i++) {
0115: char c = s.charAt(i);
0116: if (c != '-' && c != '.' && (c < 0X30 || c > 0x39)) {
0117: // The character is not a '-' or a '.' or a digit
0118: // then return NaN because something is wrong.
0119: return result;
0120: }
0121: }
0122: try {
0123: result = Double.parseDouble(s.toString());
0124: } catch (NumberFormatException e) {
0125: }
0126:
0127: return result;
0128: }
0129:
0130: /**
0131: * Cast result object to a boolean.
0132: *
0133: * @return True if the length of this string object is greater
0134: * than 0.
0135: */
0136: public boolean bool() {
0137: return str().length() > 0;
0138: }
0139:
0140: /**
0141: * Cast result object to a string.
0142: *
0143: * @return The string this wraps or the empty string if null
0144: */
0145: public XMLString xstr() {
0146: return this ;
0147: }
0148:
0149: /**
0150: * Cast result object to a string.
0151: *
0152: * @return The string this wraps or the empty string if null
0153: */
0154: public String str() {
0155: return (null != m_obj) ? ((String) m_obj) : "";
0156: }
0157:
0158: /**
0159: * Cast result object to a result tree fragment.
0160: *
0161: * @param support Xpath context to use for the conversion
0162: *
0163: * @return A document fragment with this string as a child node
0164: */
0165: public int rtf(XPathContext support) {
0166:
0167: DTM frag = support.createDocumentFragment();
0168:
0169: frag.appendTextChild(str());
0170:
0171: return frag.getDocument();
0172: }
0173:
0174: /**
0175: * Directly call the
0176: * characters method on the passed ContentHandler for the
0177: * string-value. Multiple calls to the
0178: * ContentHandler's characters methods may well occur for a single call to
0179: * this method.
0180: *
0181: * @param ch A non-null reference to a ContentHandler.
0182: *
0183: * @throws org.xml.sax.SAXException
0184: */
0185: public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch)
0186: throws org.xml.sax.SAXException {
0187:
0188: String str = str();
0189:
0190: ch.characters(str.toCharArray(), 0, str.length());
0191: }
0192:
0193: /**
0194: * Directly call the
0195: * comment method on the passed LexicalHandler for the
0196: * string-value.
0197: *
0198: * @param lh A non-null reference to a LexicalHandler.
0199: *
0200: * @throws org.xml.sax.SAXException
0201: */
0202: public void dispatchAsComment(org.xml.sax.ext.LexicalHandler lh)
0203: throws org.xml.sax.SAXException {
0204:
0205: String str = str();
0206:
0207: lh.comment(str.toCharArray(), 0, str.length());
0208: }
0209:
0210: /**
0211: * Returns the length of this string.
0212: *
0213: * @return the length of the sequence of characters represented by this
0214: * object.
0215: */
0216: public int length() {
0217: return str().length();
0218: }
0219:
0220: /**
0221: * Returns the character at the specified index. An index ranges
0222: * from <code>0</code> to <code>length() - 1</code>. The first character
0223: * of the sequence is at index <code>0</code>, the next at index
0224: * <code>1</code>, and so on, as for array indexing.
0225: *
0226: * @param index the index of the character.
0227: * @return the character at the specified index of this string.
0228: * The first character is at index <code>0</code>.
0229: * @exception IndexOutOfBoundsException if the <code>index</code>
0230: * argument is negative or not less than the length of this
0231: * string.
0232: */
0233: public char charAt(int index) {
0234: return str().charAt(index);
0235: }
0236:
0237: /**
0238: * Copies characters from this string into the destination character
0239: * array.
0240: *
0241: * @param srcBegin index of the first character in the string
0242: * to copy.
0243: * @param srcEnd index after the last character in the string
0244: * to copy.
0245: * @param dst the destination array.
0246: * @param dstBegin the start offset in the destination array.
0247: * @exception IndexOutOfBoundsException If any of the following
0248: * is true:
0249: * <ul><li><code>srcBegin</code> is negative.
0250: * <li><code>srcBegin</code> is greater than <code>srcEnd</code>
0251: * <li><code>srcEnd</code> is greater than the length of this
0252: * string
0253: * <li><code>dstBegin</code> is negative
0254: * <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
0255: * <code>dst.length</code></ul>
0256: * @exception NullPointerException if <code>dst</code> is <code>null</code>
0257: */
0258: public void getChars(int srcBegin, int srcEnd, char dst[],
0259: int dstBegin) {
0260: str().getChars(srcBegin, srcEnd, dst, dstBegin);
0261: }
0262:
0263: /**
0264: * Tell if two objects are functionally equal.
0265: *
0266: * @param obj2 Object to compare this to
0267: *
0268: * @return true if the two objects are equal
0269: *
0270: * @throws javax.xml.transform.TransformerException
0271: */
0272: public boolean equals(XObject obj2) {
0273:
0274: // In order to handle the 'all' semantics of
0275: // nodeset comparisons, we always call the
0276: // nodeset function.
0277: int t = obj2.getType();
0278: try {
0279: if (XObject.CLASS_NODESET == t)
0280: return obj2.equals(this );
0281: // If at least one object to be compared is a boolean, then each object
0282: // to be compared is converted to a boolean as if by applying the
0283: // boolean function.
0284: else if (XObject.CLASS_BOOLEAN == t)
0285: return obj2.bool() == bool();
0286: // Otherwise, if at least one object to be compared is a number, then each object
0287: // to be compared is converted to a number as if by applying the number function.
0288: else if (XObject.CLASS_NUMBER == t)
0289: return obj2.num() == num();
0290: } catch (javax.xml.transform.TransformerException te) {
0291: throw new org.apache.xml.utils.WrappedRuntimeException(te);
0292: }
0293:
0294: // Otherwise, both objects to be compared are converted to strings as
0295: // if by applying the string function.
0296: return xstr().equals(obj2.xstr());
0297: }
0298:
0299: /**
0300: * Compares this string to the specified object.
0301: * The result is <code>true</code> if and only if the argument is not
0302: * <code>null</code> and is a <code>String</code> object that represents
0303: * the same sequence of characters as this object.
0304: *
0305: * @param obj2 the object to compare this <code>String</code>
0306: * against.
0307: * @return <code>true</code> if the <code>String </code>are equal;
0308: * <code>false</code> otherwise.
0309: * @see java.lang.String#compareTo(java.lang.String)
0310: * @see java.lang.String#equalsIgnoreCase(java.lang.String)
0311: */
0312: public boolean equals(XMLString obj2) {
0313:
0314: if (!obj2.hasString())
0315: return obj2.equals(this );
0316: else
0317: return str().equals(obj2.toString());
0318: }
0319:
0320: /**
0321: * Compares this string to the specified object.
0322: * The result is <code>true</code> if and only if the argument is not
0323: * <code>null</code> and is a <code>String</code> object that represents
0324: * the same sequence of characters as this object.
0325: *
0326: * @param obj2 the object to compare this <code>String</code>
0327: * against.
0328: * @return <code>true</code> if the <code>String </code>are equal;
0329: * <code>false</code> otherwise.
0330: * @see java.lang.String#compareTo(java.lang.String)
0331: * @see java.lang.String#equalsIgnoreCase(java.lang.String)
0332: */
0333: public boolean equals(Object obj2) {
0334:
0335: if (null == obj2)
0336: return false;
0337:
0338: // In order to handle the 'all' semantics of
0339: // nodeset comparisons, we always call the
0340: // nodeset function.
0341: else if (obj2 instanceof XNodeSet)
0342: return obj2.equals(this );
0343: else if (obj2 instanceof XNumber)
0344: return obj2.equals(this );
0345: else
0346: return str().equals(obj2.toString());
0347: }
0348:
0349: /**
0350: * Compares this <code>String</code> to another <code>String</code>,
0351: * ignoring case considerations. Two strings are considered equal
0352: * ignoring case if they are of the same length, and corresponding
0353: * characters in the two strings are equal ignoring case.
0354: *
0355: * @param anotherString the <code>String</code> to compare this
0356: * <code>String</code> against.
0357: * @return <code>true</code> if the argument is not <code>null</code>
0358: * and the <code>String</code>s are equal,
0359: * ignoring case; <code>false</code> otherwise.
0360: * @see #equals(Object)
0361: * @see java.lang.Character#toLowerCase(char)
0362: * @see java.lang.Character#toUpperCase(char)
0363: */
0364: public boolean equalsIgnoreCase(String anotherString) {
0365: return str().equalsIgnoreCase(anotherString);
0366: }
0367:
0368: /**
0369: * Compares two strings lexicographically.
0370: *
0371: * @param xstr the <code>String</code> to be compared.
0372: *
0373: * @return the value <code>0</code> if the argument string is equal to
0374: * this string; a value less than <code>0</code> if this string
0375: * is lexicographically less than the string argument; and a
0376: * value greater than <code>0</code> if this string is
0377: * lexicographically greater than the string argument.
0378: * @exception java.lang.NullPointerException if <code>anotherString</code>
0379: * is <code>null</code>.
0380: */
0381: public int compareTo(XMLString xstr) {
0382:
0383: int len1 = this .length();
0384: int len2 = xstr.length();
0385: int n = Math.min(len1, len2);
0386: int i = 0;
0387: int j = 0;
0388:
0389: while (n-- != 0) {
0390: char c1 = this .charAt(i);
0391: char c2 = xstr.charAt(j);
0392:
0393: if (c1 != c2) {
0394: return c1 - c2;
0395: }
0396:
0397: i++;
0398: j++;
0399: }
0400:
0401: return len1 - len2;
0402: }
0403:
0404: /**
0405: * Compares two strings lexicographically, ignoring case considerations.
0406: * This method returns an integer whose sign is that of
0407: * <code>this.toUpperCase().toLowerCase().compareTo(
0408: * str.toUpperCase().toLowerCase())</code>.
0409: * <p>
0410: * Note that this method does <em>not</em> take locale into account,
0411: * and will result in an unsatisfactory ordering for certain locales.
0412: * The java.text package provides <em>collators</em> to allow
0413: * locale-sensitive ordering.
0414: *
0415: * @param str the <code>String</code> to be compared.
0416: * @return a negative integer, zero, or a positive integer as the
0417: * the specified String is greater than, equal to, or less
0418: * than this String, ignoring case considerations.
0419: * @see java.text.Collator#compare(String, String)
0420: * @since 1.2
0421: */
0422: public int compareToIgnoreCase(XMLString str) {
0423: // %REVIEW% Like it says, @since 1.2. Doesn't exist in earlier
0424: // versions of Java, hence we can't yet shell out to it. We can implement
0425: // it as character-by-character compare, but doing so efficiently
0426: // is likely to be (ahem) interesting.
0427: //
0428: // However, since nobody is actually _using_ this method yet:
0429: // return str().compareToIgnoreCase(str.toString());
0430:
0431: throw new org.apache.xml.utils.WrappedRuntimeException(
0432: new java.lang.NoSuchMethodException(
0433: "Java 1.2 method, not yet implemented"));
0434: }
0435:
0436: /**
0437: * Tests if this string starts with the specified prefix beginning
0438: * a specified index.
0439: *
0440: * @param prefix the prefix.
0441: * @param toffset where to begin looking in the string.
0442: * @return <code>true</code> if the character sequence represented by the
0443: * argument is a prefix of the substring of this object starting
0444: * at index <code>toffset</code>; <code>false</code> otherwise.
0445: * The result is <code>false</code> if <code>toffset</code> is
0446: * negative or greater than the length of this
0447: * <code>String</code> object; otherwise the result is the same
0448: * as the result of the expression
0449: * <pre>
0450: * this.subString(toffset).startsWith(prefix)
0451: * </pre>
0452: * @exception java.lang.NullPointerException if <code>prefix</code> is
0453: * <code>null</code>.
0454: */
0455: public boolean startsWith(String prefix, int toffset) {
0456: return str().startsWith(prefix, toffset);
0457: }
0458:
0459: /**
0460: * Tests if this string starts with the specified prefix.
0461: *
0462: * @param prefix the prefix.
0463: * @return <code>true</code> if the character sequence represented by the
0464: * argument is a prefix of the character sequence represented by
0465: * this string; <code>false</code> otherwise.
0466: * Note also that <code>true</code> will be returned if the
0467: * argument is an empty string or is equal to this
0468: * <code>String</code> object as determined by the
0469: * {@link #equals(Object)} method.
0470: * @exception java.lang.NullPointerException if <code>prefix</code> is
0471: * <code>null</code>.
0472: */
0473: public boolean startsWith(String prefix) {
0474: return startsWith(prefix, 0);
0475: }
0476:
0477: /**
0478: * Tests if this string starts with the specified prefix beginning
0479: * a specified index.
0480: *
0481: * @param prefix the prefix.
0482: * @param toffset where to begin looking in the string.
0483: * @return <code>true</code> if the character sequence represented by the
0484: * argument is a prefix of the substring of this object starting
0485: * at index <code>toffset</code>; <code>false</code> otherwise.
0486: * The result is <code>false</code> if <code>toffset</code> is
0487: * negative or greater than the length of this
0488: * <code>String</code> object; otherwise the result is the same
0489: * as the result of the expression
0490: * <pre>
0491: * this.subString(toffset).startsWith(prefix)
0492: * </pre>
0493: * @exception java.lang.NullPointerException if <code>prefix</code> is
0494: * <code>null</code>.
0495: */
0496: public boolean startsWith(XMLString prefix, int toffset) {
0497:
0498: int to = toffset;
0499: int tlim = this .length();
0500: int po = 0;
0501: int pc = prefix.length();
0502:
0503: // Note: toffset might be near -1>>>1.
0504: if ((toffset < 0) || (toffset > tlim - pc)) {
0505: return false;
0506: }
0507:
0508: while (--pc >= 0) {
0509: if (this .charAt(to) != prefix.charAt(po)) {
0510: return false;
0511: }
0512:
0513: to++;
0514: po++;
0515: }
0516:
0517: return true;
0518: }
0519:
0520: /**
0521: * Tests if this string starts with the specified prefix.
0522: *
0523: * @param prefix the prefix.
0524: * @return <code>true</code> if the character sequence represented by the
0525: * argument is a prefix of the character sequence represented by
0526: * this string; <code>false</code> otherwise.
0527: * Note also that <code>true</code> will be returned if the
0528: * argument is an empty string or is equal to this
0529: * <code>String</code> object as determined by the
0530: * {@link #equals(Object)} method.
0531: * @exception java.lang.NullPointerException if <code>prefix</code> is
0532: * <code>null</code>.
0533: */
0534: public boolean startsWith(XMLString prefix) {
0535: return startsWith(prefix, 0);
0536: }
0537:
0538: /**
0539: * Tests if this string ends with the specified suffix.
0540: *
0541: * @param suffix the suffix.
0542: * @return <code>true</code> if the character sequence represented by the
0543: * argument is a suffix of the character sequence represented by
0544: * this object; <code>false</code> otherwise. Note that the
0545: * result will be <code>true</code> if the argument is the
0546: * empty string or is equal to this <code>String</code> object
0547: * as determined by the {@link #equals(Object)} method.
0548: * @exception java.lang.NullPointerException if <code>suffix</code> is
0549: * <code>null</code>.
0550: */
0551: public boolean endsWith(String suffix) {
0552: return str().endsWith(suffix);
0553: }
0554:
0555: /**
0556: * Returns a hashcode for this string. The hashcode for a
0557: * <code>String</code> object is computed as
0558: * <blockquote><pre>
0559: * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
0560: * </pre></blockquote>
0561: * using <code>int</code> arithmetic, where <code>s[i]</code> is the
0562: * <i>i</i>th character of the string, <code>n</code> is the length of
0563: * the string, and <code>^</code> indicates exponentiation.
0564: * (The hash value of the empty string is zero.)
0565: *
0566: * @return a hash code value for this object.
0567: */
0568: public int hashCode() {
0569: return str().hashCode();
0570: }
0571:
0572: /**
0573: * Returns the index within this string of the first occurrence of the
0574: * specified character. If a character with value <code>ch</code> occurs
0575: * in the character sequence represented by this <code>String</code>
0576: * object, then the index of the first such occurrence is returned --
0577: * that is, the smallest value <i>k</i> such that:
0578: * <blockquote><pre>
0579: * this.charAt(<i>k</i>) == ch
0580: * </pre></blockquote>
0581: * is <code>true</code>. If no such character occurs in this string,
0582: * then <code>-1</code> is returned.
0583: *
0584: * @param ch a character.
0585: * @return the index of the first occurrence of the character in the
0586: * character sequence represented by this object, or
0587: * <code>-1</code> if the character does not occur.
0588: */
0589: public int indexOf(int ch) {
0590: return str().indexOf(ch);
0591: }
0592:
0593: /**
0594: * Returns the index within this string of the first occurrence of the
0595: * specified character, starting the search at the specified index.
0596: * <p>
0597: * If a character with value <code>ch</code> occurs in the character
0598: * sequence represented by this <code>String</code> object at an index
0599: * no smaller than <code>fromIndex</code>, then the index of the first
0600: * such occurrence is returned--that is, the smallest value <i>k</i>
0601: * such that:
0602: * <blockquote><pre>
0603: * (this.charAt(<i>k</i>) == ch) && (<i>k</i> >= fromIndex)
0604: * </pre></blockquote>
0605: * is true. If no such character occurs in this string at or after
0606: * position <code>fromIndex</code>, then <code>-1</code> is returned.
0607: * <p>
0608: * There is no restriction on the value of <code>fromIndex</code>. If it
0609: * is negative, it has the same effect as if it were zero: this entire
0610: * string may be searched. If it is greater than the length of this
0611: * string, it has the same effect as if it were equal to the length of
0612: * this string: <code>-1</code> is returned.
0613: *
0614: * @param ch a character.
0615: * @param fromIndex the index to start the search from.
0616: * @return the index of the first occurrence of the character in the
0617: * character sequence represented by this object that is greater
0618: * than or equal to <code>fromIndex</code>, or <code>-1</code>
0619: * if the character does not occur.
0620: */
0621: public int indexOf(int ch, int fromIndex) {
0622: return str().indexOf(ch, fromIndex);
0623: }
0624:
0625: /**
0626: * Returns the index within this string of the last occurrence of the
0627: * specified character. That is, the index returned is the largest
0628: * value <i>k</i> such that:
0629: * <blockquote><pre>
0630: * this.charAt(<i>k</i>) == ch
0631: * </pre></blockquote>
0632: * is true.
0633: * The String is searched backwards starting at the last character.
0634: *
0635: * @param ch a character.
0636: * @return the index of the last occurrence of the character in the
0637: * character sequence represented by this object, or
0638: * <code>-1</code> if the character does not occur.
0639: */
0640: public int lastIndexOf(int ch) {
0641: return str().lastIndexOf(ch);
0642: }
0643:
0644: /**
0645: * Returns the index within this string of the last occurrence of the
0646: * specified character, searching backward starting at the specified
0647: * index. That is, the index returned is the largest value <i>k</i>
0648: * such that:
0649: * <blockquote><pre>
0650: * this.charAt(k) == ch) && (k <= fromIndex)
0651: * </pre></blockquote>
0652: * is true.
0653: *
0654: * @param ch a character.
0655: * @param fromIndex the index to start the search from. There is no
0656: * restriction on the value of <code>fromIndex</code>. If it is
0657: * greater than or equal to the length of this string, it has
0658: * the same effect as if it were equal to one less than the
0659: * length of this string: this entire string may be searched.
0660: * If it is negative, it has the same effect as if it were -1:
0661: * -1 is returned.
0662: * @return the index of the last occurrence of the character in the
0663: * character sequence represented by this object that is less
0664: * than or equal to <code>fromIndex</code>, or <code>-1</code>
0665: * if the character does not occur before that point.
0666: */
0667: public int lastIndexOf(int ch, int fromIndex) {
0668: return str().lastIndexOf(ch, fromIndex);
0669: }
0670:
0671: /**
0672: * Returns the index within this string of the first occurrence of the
0673: * specified substring. The integer returned is the smallest value
0674: * <i>k</i> such that:
0675: * <blockquote><pre>
0676: * this.startsWith(str, <i>k</i>)
0677: * </pre></blockquote>
0678: * is <code>true</code>.
0679: *
0680: * @param str any string.
0681: * @return if the string argument occurs as a substring within this
0682: * object, then the index of the first character of the first
0683: * such substring is returned; if it does not occur as a
0684: * substring, <code>-1</code> is returned.
0685: * @exception java.lang.NullPointerException if <code>str</code> is
0686: * <code>null</code>.
0687: */
0688: public int indexOf(String str) {
0689: return str().indexOf(str);
0690: }
0691:
0692: /**
0693: * Returns the index within this string of the first occurrence of the
0694: * specified substring. The integer returned is the smallest value
0695: * <i>k</i> such that:
0696: * <blockquote><pre>
0697: * this.startsWith(str, <i>k</i>)
0698: * </pre></blockquote>
0699: * is <code>true</code>.
0700: *
0701: * @param str any string.
0702: * @return if the string argument occurs as a substring within this
0703: * object, then the index of the first character of the first
0704: * such substring is returned; if it does not occur as a
0705: * substring, <code>-1</code> is returned.
0706: * @exception java.lang.NullPointerException if <code>str</code> is
0707: * <code>null</code>.
0708: */
0709: public int indexOf(XMLString str) {
0710: return str().indexOf(str.toString());
0711: }
0712:
0713: /**
0714: * Returns the index within this string of the first occurrence of the
0715: * specified substring, starting at the specified index. The integer
0716: * returned is the smallest value <i>k</i> such that:
0717: * <blockquote><pre>
0718: * this.startsWith(str, <i>k</i>) && (<i>k</i> >= fromIndex)
0719: * </pre></blockquote>
0720: * is <code>true</code>.
0721: * <p>
0722: * There is no restriction on the value of <code>fromIndex</code>. If
0723: * it is negative, it has the same effect as if it were zero: this entire
0724: * string may be searched. If it is greater than the length of this
0725: * string, it has the same effect as if it were equal to the length of
0726: * this string: <code>-1</code> is returned.
0727: *
0728: * @param str the substring to search for.
0729: * @param fromIndex the index to start the search from.
0730: * @return If the string argument occurs as a substring within this
0731: * object at a starting index no smaller than
0732: * <code>fromIndex</code>, then the index of the first character
0733: * of the first such substring is returned. If it does not occur
0734: * as a substring starting at <code>fromIndex</code> or beyond,
0735: * <code>-1</code> is returned.
0736: * @exception java.lang.NullPointerException if <code>str</code> is
0737: * <code>null</code>
0738: */
0739: public int indexOf(String str, int fromIndex) {
0740: return str().indexOf(str, fromIndex);
0741: }
0742:
0743: /**
0744: * Returns the index within this string of the rightmost occurrence
0745: * of the specified substring. The rightmost empty string "" is
0746: * considered to occur at the index value <code>this.length()</code>.
0747: * The returned index is the largest value <i>k</i> such that
0748: * <blockquote><pre>
0749: * this.startsWith(str, k)
0750: * </pre></blockquote>
0751: * is true.
0752: *
0753: * @param str the substring to search for.
0754: * @return if the string argument occurs one or more times as a substring
0755: * within this object, then the index of the first character of
0756: * the last such substring is returned. If it does not occur as
0757: * a substring, <code>-1</code> is returned.
0758: * @exception java.lang.NullPointerException if <code>str</code> is
0759: * <code>null</code>.
0760: */
0761: public int lastIndexOf(String str) {
0762: return str().lastIndexOf(str);
0763: }
0764:
0765: /**
0766: * Returns the index within this string of the last occurrence of
0767: * the specified substring.
0768: *
0769: * @param str the substring to search for.
0770: * @param fromIndex the index to start the search from. There is no
0771: * restriction on the value of fromIndex. If it is greater than
0772: * the length of this string, it has the same effect as if it
0773: * were equal to the length of this string: this entire string
0774: * may be searched. If it is negative, it has the same effect
0775: * as if it were -1: -1 is returned.
0776: * @return If the string argument occurs one or more times as a substring
0777: * within this object at a starting index no greater than
0778: * <code>fromIndex</code>, then the index of the first character of
0779: * the last such substring is returned. If it does not occur as a
0780: * substring starting at <code>fromIndex</code> or earlier,
0781: * <code>-1</code> is returned.
0782: * @exception java.lang.NullPointerException if <code>str</code> is
0783: * <code>null</code>.
0784: */
0785: public int lastIndexOf(String str, int fromIndex) {
0786: return str().lastIndexOf(str, fromIndex);
0787: }
0788:
0789: /**
0790: * Returns a new string that is a substring of this string. The
0791: * substring begins with the character at the specified index and
0792: * extends to the end of this string. <p>
0793: * Examples:
0794: * <blockquote><pre>
0795: * "unhappy".substring(2) returns "happy"
0796: * "Harbison".substring(3) returns "bison"
0797: * "emptiness".substring(9) returns "" (an empty string)
0798: * </pre></blockquote>
0799: *
0800: * @param beginIndex the beginning index, inclusive.
0801: * @return the specified substring.
0802: * @exception IndexOutOfBoundsException if
0803: * <code>beginIndex</code> is negative or larger than the
0804: * length of this <code>String</code> object.
0805: */
0806: public XMLString substring(int beginIndex) {
0807: return new XString(str().substring(beginIndex));
0808: }
0809:
0810: /**
0811: * Returns a new string that is a substring of this string. The
0812: * substring begins at the specified <code>beginIndex</code> and
0813: * extends to the character at index <code>endIndex - 1</code>.
0814: * Thus the length of the substring is <code>endIndex-beginIndex</code>.
0815: *
0816: * @param beginIndex the beginning index, inclusive.
0817: * @param endIndex the ending index, exclusive.
0818: * @return the specified substring.
0819: * @exception IndexOutOfBoundsException if the
0820: * <code>beginIndex</code> is negative, or
0821: * <code>endIndex</code> is larger than the length of
0822: * this <code>String</code> object, or
0823: * <code>beginIndex</code> is larger than
0824: * <code>endIndex</code>.
0825: */
0826: public XMLString substring(int beginIndex, int endIndex) {
0827: return new XString(str().substring(beginIndex, endIndex));
0828: }
0829:
0830: /**
0831: * Concatenates the specified string to the end of this string.
0832: *
0833: * @param str the <code>String</code> that is concatenated to the end
0834: * of this <code>String</code>.
0835: * @return a string that represents the concatenation of this object's
0836: * characters followed by the string argument's characters.
0837: * @exception java.lang.NullPointerException if <code>str</code> is
0838: * <code>null</code>.
0839: */
0840: public XMLString concat(String str) {
0841:
0842: // %REVIEW% Make an FSB here?
0843: return new XString(str().concat(str));
0844: }
0845:
0846: /**
0847: * Converts all of the characters in this <code>String</code> to lower
0848: * case using the rules of the given <code>Locale</code>.
0849: *
0850: * @param locale use the case transformation rules for this locale
0851: * @return the String, converted to lowercase.
0852: * @see java.lang.Character#toLowerCase(char)
0853: * @see java.lang.String#toUpperCase(Locale)
0854: */
0855: public XMLString toLowerCase(Locale locale) {
0856: return new XString(str().toLowerCase(locale));
0857: }
0858:
0859: /**
0860: * Converts all of the characters in this <code>String</code> to lower
0861: * case using the rules of the default locale, which is returned
0862: * by <code>Locale.getDefault</code>.
0863: * <p>
0864: *
0865: * @return the string, converted to lowercase.
0866: * @see java.lang.Character#toLowerCase(char)
0867: * @see java.lang.String#toLowerCase(Locale)
0868: */
0869: public XMLString toLowerCase() {
0870: return new XString(str().toLowerCase());
0871: }
0872:
0873: /**
0874: * Converts all of the characters in this <code>String</code> to upper
0875: * case using the rules of the given locale.
0876: * @param locale use the case transformation rules for this locale
0877: * @return the String, converted to uppercase.
0878: * @see java.lang.Character#toUpperCase(char)
0879: * @see java.lang.String#toLowerCase(Locale)
0880: */
0881: public XMLString toUpperCase(Locale locale) {
0882: return new XString(str().toUpperCase(locale));
0883: }
0884:
0885: /**
0886: * Converts all of the characters in this <code>String</code> to upper
0887: * case using the rules of the default locale, which is returned
0888: * by <code>Locale.getDefault</code>.
0889: *
0890: * <p>
0891: * If no character in this string has a different uppercase version,
0892: * based on calling the <code>toUpperCase</code> method defined by
0893: * <code>Character</code>, then the original string is returned.
0894: * <p>
0895: * Otherwise, this method creates a new <code>String</code> object
0896: * representing a character sequence identical in length to the
0897: * character sequence represented by this <code>String</code> object and
0898: * with every character equal to the result of applying the method
0899: * <code>Character.toUpperCase</code> to the corresponding character of
0900: * this <code>String</code> object. <p>
0901: * Examples:
0902: * <blockquote><pre>
0903: * "Fahrvergnügen".toUpperCase() returns "FAHRVERGNÜGEN"
0904: * "Visit Ljubinje!".toUpperCase() returns "VISIT LJUBINJE!"
0905: * </pre></blockquote>
0906: *
0907: * @return the string, converted to uppercase.
0908: * @see java.lang.Character#toUpperCase(char)
0909: * @see java.lang.String#toUpperCase(Locale)
0910: */
0911: public XMLString toUpperCase() {
0912: return new XString(str().toUpperCase());
0913: }
0914:
0915: /**
0916: * Removes white space from both ends of this string.
0917: *
0918: * @return this string, with white space removed from the front and end.
0919: */
0920: public XMLString trim() {
0921: return new XString(str().trim());
0922: }
0923:
0924: /**
0925: * Returns whether the specified <var>ch</var> conforms to the XML 1.0 definition
0926: * of whitespace. Refer to <A href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S">
0927: * the definition of <CODE>S</CODE></A> for details.
0928: * @param ch Character to check as XML whitespace.
0929: * @return =true if <var>ch</var> is XML whitespace; otherwise =false.
0930: */
0931: private static boolean isSpace(char ch) {
0932: return XMLCharacterRecognizer.isWhiteSpace(ch); // Take the easy way out for now.
0933: }
0934:
0935: /**
0936: * Conditionally trim all leading and trailing whitespace in the specified String.
0937: * All strings of white space are
0938: * replaced by a single space character (#x20), except spaces after punctuation which
0939: * receive double spaces if doublePunctuationSpaces is true.
0940: * This function may be useful to a formatter, but to get first class
0941: * results, the formatter should probably do it's own white space handling
0942: * based on the semantics of the formatting object.
0943: *
0944: * @param trimHead Trim leading whitespace?
0945: * @param trimTail Trim trailing whitespace?
0946: * @param doublePunctuationSpaces Use double spaces for punctuation?
0947: * @return The trimmed string.
0948: */
0949: public XMLString fixWhiteSpace(boolean trimHead, boolean trimTail,
0950: boolean doublePunctuationSpaces) {
0951:
0952: // %OPT% !!!!!!!
0953: int len = this .length();
0954: char[] buf = new char[len];
0955:
0956: this .getChars(0, len, buf, 0);
0957:
0958: boolean edit = false;
0959: int s;
0960:
0961: for (s = 0; s < len; s++) {
0962: if (isSpace(buf[s])) {
0963: break;
0964: }
0965: }
0966:
0967: /* replace S to ' '. and ' '+ -> single ' '. */
0968: int d = s;
0969: boolean pres = false;
0970:
0971: for (; s < len; s++) {
0972: char c = buf[s];
0973:
0974: if (isSpace(c)) {
0975: if (!pres) {
0976: if (' ' != c) {
0977: edit = true;
0978: }
0979:
0980: buf[d++] = ' ';
0981:
0982: if (doublePunctuationSpaces && (s != 0)) {
0983: char prevChar = buf[s - 1];
0984:
0985: if (!((prevChar == '.') || (prevChar == '!') || (prevChar == '?'))) {
0986: pres = true;
0987: }
0988: } else {
0989: pres = true;
0990: }
0991: } else {
0992: edit = true;
0993: pres = true;
0994: }
0995: } else {
0996: buf[d++] = c;
0997: pres = false;
0998: }
0999: }
1000:
1001: if (trimTail && 1 <= d && ' ' == buf[d - 1]) {
1002: edit = true;
1003:
1004: d--;
1005: }
1006:
1007: int start = 0;
1008:
1009: if (trimHead && 0 < d && ' ' == buf[0]) {
1010: edit = true;
1011:
1012: start++;
1013: }
1014:
1015: XMLStringFactory xsf = XMLStringFactoryImpl.getFactory();
1016:
1017: return edit ? xsf.newstr(new String(buf, start, d - start))
1018: : this ;
1019: }
1020:
1021: /**
1022: * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
1023: */
1024: public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) {
1025: visitor.visitStringLiteral(owner, this);
1026: }
1027:
1028: }
|