org.htmlunit.xpath.objects.XString Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of htmlunit-xpath Show documentation
Show all versions of htmlunit-xpath Show documentation
The XPath engine used by HtmlUnit.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.htmlunit.xpath.objects;
import org.htmlunit.xpath.XPathVisitor;
import org.htmlunit.xpath.xml.utils.XMLCharacterRecognizer;
/**
* This class represents an XPath string object, and is capable of converting the string to other
* types, such as a number.
*/
public class XString extends XObject {
/** Empty string XString object */
public static final XString EMPTYSTRING = new XString("");
/**
* Construct a XNodeSet object.
*
* @param val String object this will wrap.
*/
public XString(final String val) {
super(val);
}
/** {@inheritDoc} */
@Override
public int getType() {
return CLASS_STRING;
}
/** {@inheritDoc} */
@Override
public String getTypeString() {
return "#STRING";
}
/**
* Tell if this object contains a java String object.
*
* @return true if this XMLString can return a string without creating one.
*/
public boolean hasString() {
return true;
}
/** {@inheritDoc} */
@Override
public double num() {
return toDouble();
}
/**
* Convert a string to a double -- Allowed input is in fixed notation ddd.fff.
*
* @return A double value representation of the string, or return Double.NaN if the string can not
* be converted.
*/
public double toDouble() {
/*
* XMLCharacterRecognizer.isWhiteSpace(char c) methods treats the following
* characters as white space characters. ht - horizontal tab, nl - newline , cr
* - carriage return and sp - space trim() methods by default also takes care of
* these white space characters So trim() method is used to remove leading and
* trailing white spaces.
*/
final XString s = trim();
double result = Double.NaN;
for (int i = 0; i < s.length(); i++) {
final char c = s.charAt(i);
if (c != '-' && c != '.' && (c < 0X30 || c > 0x39)) {
// The character is not a '-' or a '.' or a digit
// then return NaN because something is wrong.
return result;
}
}
try {
result = Double.parseDouble(s.toString());
}
catch (final NumberFormatException e) {
// ignore
}
return result;
}
/** {@inheritDoc} */
@Override
public boolean bool() {
return str().length() > 0;
}
/** {@inheritDoc} */
@Override
public XString xstr() {
return this;
}
/** {@inheritDoc} */
@Override
public String str() {
return (null != m_obj) ? ((String) m_obj) : "";
}
/**
* Returns the length of this string.
*
* @return the length of the sequence of characters represented by this object.
*/
public int length() {
return str().length();
}
/**
* Returns the character at the specified index. An index ranges from 0
to
* length() - 1
. The first character of the sequence is at index 0
, the next
* at index 1
, and so on, as for array indexing.
*
* @param index the index of the character.
* @return the character at the specified index of this string. The first character is at index
* 0
.
* @exception IndexOutOfBoundsException if the index
argument is negative or not less
* than the length of this string.
*/
public char charAt(final int index) {
return str().charAt(index);
}
/**
* Copies characters from this string into the destination character array.
*
* @param srcBegin index of the first character in the string to copy.
* @param srcEnd index after the last character in the string to copy.
* @param dst the destination array.
* @param dstBegin the start offset in the destination array.
* @exception IndexOutOfBoundsException If any of the following is true:
*
* srcBegin
is negative.
* srcBegin
is greater than srcEnd
* srcEnd
is greater than the length of this string
* dstBegin
is negative
* dstBegin+(srcEnd-srcBegin)
is larger than dst.length
*
*
* @exception NullPointerException if dst
is null
*/
public void getChars(final int srcBegin, final int srcEnd, final char[] dst, final int dstBegin) {
str().getChars(srcBegin, srcEnd, dst, dstBegin);
}
/** {@inheritDoc} */
@Override
public boolean equals(final XObject obj2) {
// In order to handle the 'all' semantics of
// nodeset comparisons, we always call the
// nodeset function.
final int t = obj2.getType();
try {
if (XObject.CLASS_NODESET == t) {
return obj2.equals(this);
}
// If at least one object to be compared is a boolean, then each object
// to be compared is converted to a boolean as if by applying the
// boolean function.
else if (XObject.CLASS_BOOLEAN == t) {
return obj2.bool() == bool();
}
// Otherwise, if at least one object to be compared is a number, then each
// object
// to be compared is converted to a number as if by applying the number
// function.
else if (XObject.CLASS_NUMBER == t) {
return obj2.num() == num();
}
}
catch (final javax.xml.transform.TransformerException te) {
throw new org.htmlunit.xpath.xml.utils.WrappedRuntimeException(te);
}
// Otherwise, both objects to be compared are converted to strings as
// if by applying the string function.
return xstr().equals(obj2.xstr());
}
/**
* Compares this string to the specified String
. The result is true
if
* and only if the argument is not null
and is a String
object that
* represents the same sequence of characters as this object.
*
* @param obj2 the object to compare this String
against.
* @return true
if the String
s are equal; false
otherwise.
* @see java.lang.String#compareTo(java.lang.String)
* @see java.lang.String#equalsIgnoreCase(java.lang.String)
*/
public boolean equals(final String obj2) {
return str().equals(obj2);
}
/**
* Compares this string to the specified object. The result is true
if and only if
* the argument is not null
and is a String
object that represents the
* same sequence of characters as this object.
*
* @param obj2 the object to compare this String
against.
* @return true
if the String
are equal; false
otherwise.
* @see java.lang.String#compareTo(java.lang.String)
* @see java.lang.String#equalsIgnoreCase(java.lang.String)
*/
public boolean equals(final XString obj2) {
if (obj2 != null) {
if (!obj2.hasString()) {
return obj2.equals(str());
}
return str().equals(obj2.toString());
}
return false;
}
/** {@inheritDoc} */
@Override
public boolean equals(final Object obj2) {
if (null == obj2) {
return false;
}
// In order to handle the 'all' semantics of
// nodeset comparisons, we always call the
// nodeset function.
else if (obj2 instanceof XNodeSet) {
return obj2.equals(this);
}
else if (obj2 instanceof XNumber) {
return obj2.equals(this);
}
else {
return str().equals(obj2.toString());
}
}
/**
* Tests if this string starts with the specified prefix beginning a specified index.
*
* @param prefix the prefix.
* @param toffset where to begin looking in the string.
* @return true
if the character sequence represented by the argument is a prefix of
* the substring of this object starting at index toffset
; false
*
otherwise. The result is false
if toffset
is negative or
* greater than the length of this String
object; otherwise the result is the
* same as the result of the expression
*
* this.subString(toffset).startsWith(prefix)
*
*
* @exception java.lang.NullPointerException if prefix
is null
.
*/
public boolean startsWith(final XString prefix, final int toffset) {
int to = toffset;
final int tlim = this.length();
int po = 0;
int pc = prefix.length();
// Note: toffset might be near -1>>>1.
if ((toffset < 0) || (toffset > tlim - pc)) {
return false;
}
while (--pc >= 0) {
if (this.charAt(to) != prefix.charAt(po)) {
return false;
}
to++;
po++;
}
return true;
}
/**
* Tests if this string starts with the specified prefix.
*
* @param prefix the prefix.
* @return true
if the character sequence represented by the argument is a prefix of
* the character sequence represented by this string; false
otherwise. Note also
* that true
will be returned if the argument is an empty string or is equal to
* this String
object as determined by the {@link #equals(Object)} method.
* @exception java.lang.NullPointerException if prefix
is null
.
*/
public boolean startsWith(final XString prefix) {
return startsWith(prefix, 0);
}
/**
* Returns the index within this string of the first occurrence of the specified substring. The
* integer returned is the smallest value k such that:
*
*
*
*
* this.startsWith(str, k)
*
*
*
*
* is true
.
*
* @param str any string.
* @return if the string argument occurs as a substring within this object, then the index of the
* first character of the first such substring is returned; if it does not occur as a
* substring, -1
is returned.
* @exception java.lang.NullPointerException if str
is null
.
*/
public int indexOf(final XString str) {
return str().indexOf(str.toString());
}
/**
* Returns a new string that is a substring of this string. The substring begins with the
* character at the specified index and extends to the end of this string.
*
* Examples:
*
*
*
*
* "unhappy".substring(2) returns "happy"
* "Harbison".substring(3) returns "bison"
* "emptiness".substring(9) returns "" (an empty string)
*
*
*
*
* @param beginIndex the beginning index, inclusive.
* @return the specified substring.
* @exception IndexOutOfBoundsException if beginIndex
is negative or larger than the
* length of this String
object.
*/
public XString substring(final int beginIndex) {
return new XString(str().substring(beginIndex));
}
/**
* Returns a new string that is a substring of this string. The substring begins at the specified
* beginIndex
and extends to the character at index endIndex - 1
*
. Thus the length of the substring is endIndex-beginIndex
.
*
* @param beginIndex the beginning index, inclusive.
* @param endIndex the ending index, exclusive.
* @return the specified substring.
* @exception IndexOutOfBoundsException if the beginIndex
is negative, or
* endIndex
is larger than the length of this String
object, or
* beginIndex
is larger than endIndex
.
*/
public XString substring(final int beginIndex, final int endIndex) {
return new XString(str().substring(beginIndex, endIndex));
}
/**
* Removes white space from both ends of this string.
*
* @return this string, with white space removed from the front and end.
*/
public XString trim() {
return new XString(str().trim());
}
/**
* Returns whether the specified ch conforms to the XML 1.0 definition of whitespace.
* Refer to the definition of S
*
for details.
*
* @param ch Character to check as XML whitespace.
* @return =true if ch is XML whitespace; otherwise =false.
*/
private static boolean isSpace(final char ch) {
return XMLCharacterRecognizer.isWhiteSpace(ch); // Take the easy way out for now.
}
/**
* Conditionally trim all leading and trailing whitespace in the specified String. All strings of
* white space are replaced by a single space character (#x20), except spaces after punctuation
* which receive double spaces if doublePunctuationSpaces is true. This function may be useful to
* a formatter, but to get first class results, the formatter should probably do it's own white
* space handling based on the semantics of the formatting object.
*
* @param trimHead Trim leading whitespace?
* @param trimTail Trim trailing whitespace?
* @param doublePunctuationSpaces Use double spaces for punctuation?
* @return The trimmed string.
*/
public XString fixWhiteSpace(
final boolean trimHead, final boolean trimTail, final boolean doublePunctuationSpaces) {
// %OPT% !!!!!!!
final int len = this.length();
final char[] buf = new char[len];
this.getChars(0, len, buf, 0);
boolean edit = false;
int s;
for (s = 0; s < len; s++) {
if (isSpace(buf[s])) {
break;
}
}
/* replace S to ' '. and ' '+ -> single ' '. */
int d = s;
boolean pres = false;
for ( ; s < len; s++) {
final char c = buf[s];
if (isSpace(c)) {
if (!pres) {
if (' ' != c) {
edit = true;
}
buf[d++] = ' ';
if (doublePunctuationSpaces && (s != 0)) {
final char prevChar = buf[s - 1];
if (!((prevChar == '.') || (prevChar == '!') || (prevChar == '?'))) {
pres = true;
}
}
else {
pres = true;
}
}
else {
edit = true;
pres = true;
}
}
else {
buf[d++] = c;
pres = false;
}
}
if (trimTail && 1 <= d && ' ' == buf[d - 1]) {
edit = true;
d--;
}
int start = 0;
if (trimHead && 0 < d && ' ' == buf[0]) {
edit = true;
start++;
}
return edit ? new XString(new String(buf, start, d - start)) : this;
}
/** {@inheritDoc} */
@Override
public void callVisitors(final XPathVisitor visitor) {
visitor.visitStringLiteral();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy