2014-06-05 11:31:04 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2014 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project 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.
|
|
|
|
*/
|
2015-04-01 01:23:52 +02:00
|
|
|
package io.netty.util;
|
2014-06-05 11:31:04 +02:00
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
|
|
|
import static io.netty.util.internal.StringUtil.UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET;
|
|
|
|
import io.netty.util.ByteProcessor.IndexOfProcessor;
|
2014-06-05 11:31:04 +02:00
|
|
|
import io.netty.util.internal.EmptyArrays;
|
2015-04-01 01:23:52 +02:00
|
|
|
import io.netty.util.internal.PlatformDependent;
|
2014-06-05 11:31:04 +02:00
|
|
|
|
|
|
|
import java.nio.ByteBuffer;
|
2014-09-19 01:04:35 +02:00
|
|
|
import java.nio.charset.Charset;
|
2014-06-05 11:31:04 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
2014-09-19 01:04:35 +02:00
|
|
|
import java.util.Comparator;
|
2014-06-05 11:31:04 +02:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
import java.util.regex.PatternSyntaxException;
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* A string which has been encoded into a character encoding whose character always takes a single byte, similarly to
|
|
|
|
* ASCII. It internally keeps its content in a byte array unlike {@link String}, which uses a character array, for
|
|
|
|
* reduced memory footprint and faster data transfer from/to byte-based data structures such as a byte array and
|
2015-04-01 01:23:52 +02:00
|
|
|
* {@link ByteBuffer}. It is often used in conjunction with {@link TextHeaders}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
2015-04-01 01:23:52 +02:00
|
|
|
public final class AsciiString extends ByteString implements CharSequence, Comparable<CharSequence> {
|
2014-06-05 11:31:04 +02:00
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
private static final char MAX_CHAR_VALUE = 255;
|
2014-06-05 11:31:04 +02:00
|
|
|
public static final AsciiString EMPTY_STRING = new AsciiString("");
|
2015-04-01 01:23:52 +02:00
|
|
|
|
2014-09-19 01:04:35 +02:00
|
|
|
public static final Comparator<AsciiString> CASE_INSENSITIVE_ORDER = new Comparator<AsciiString>() {
|
|
|
|
@Override
|
|
|
|
public int compare(AsciiString o1, AsciiString o2) {
|
|
|
|
return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(o1, o2);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
public static final Comparator<AsciiString> CASE_SENSITIVE_ORDER = new Comparator<AsciiString>() {
|
|
|
|
@Override
|
|
|
|
public int compare(AsciiString o1, AsciiString o2) {
|
|
|
|
return CHARSEQUENCE_CASE_SENSITIVE_ORDER.compare(o1, o2);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
public static final Comparator<CharSequence> CHARSEQUENCE_CASE_INSENSITIVE_ORDER = new Comparator<CharSequence>() {
|
|
|
|
@Override
|
|
|
|
public int compare(CharSequence o1, CharSequence o2) {
|
|
|
|
if (o1 == o2) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AsciiString a1 = o1 instanceof AsciiString ? (AsciiString) o1 : null;
|
|
|
|
AsciiString a2 = o2 instanceof AsciiString ? (AsciiString) o2 : null;
|
|
|
|
|
|
|
|
int result;
|
|
|
|
int length1 = o1.length();
|
|
|
|
int length2 = o2.length();
|
|
|
|
int minLength = Math.min(length1, length2);
|
|
|
|
if (a1 != null && a2 != null) {
|
|
|
|
byte[] thisValue = a1.value;
|
|
|
|
byte[] thatValue = a2.value;
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
|
|
|
byte v1 = thisValue[i];
|
|
|
|
byte v2 = thatValue[i];
|
|
|
|
if (v1 == v2) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-04-01 01:23:52 +02:00
|
|
|
int c1 = toLowerCase(v1);
|
|
|
|
int c2 = toLowerCase(v2);
|
2014-09-19 01:04:35 +02:00
|
|
|
result = c1 - c2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (a1 != null) {
|
|
|
|
byte[] thisValue = a1.value;
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
2015-04-01 01:23:52 +02:00
|
|
|
int c1 = toLowerCase(thisValue[i]);
|
2014-09-19 01:04:35 +02:00
|
|
|
int c2 = toLowerCase(o2.charAt(i));
|
|
|
|
result = c1 - c2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (a2 != null) {
|
|
|
|
byte[] thatValue = a2.value;
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
|
|
|
int c1 = toLowerCase(o1.charAt(i));
|
2015-04-01 01:23:52 +02:00
|
|
|
int c2 = toLowerCase(thatValue[i]);
|
2014-09-19 01:04:35 +02:00
|
|
|
result = c1 - c2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
|
|
|
int c1 = toLowerCase(o1.charAt(i));
|
|
|
|
int c2 = toLowerCase(o2.charAt(i));
|
|
|
|
result = c1 - c2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return length1 - length2;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
public static final Comparator<CharSequence> CHARSEQUENCE_CASE_SENSITIVE_ORDER = new Comparator<CharSequence>() {
|
|
|
|
@Override
|
|
|
|
public int compare(CharSequence o1, CharSequence o2) {
|
|
|
|
if (o1 == o2) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AsciiString a1 = o1 instanceof AsciiString ? (AsciiString) o1 : null;
|
|
|
|
AsciiString a2 = o2 instanceof AsciiString ? (AsciiString) o2 : null;
|
|
|
|
|
|
|
|
int result;
|
|
|
|
int length1 = o1.length();
|
|
|
|
int length2 = o2.length();
|
|
|
|
int minLength = Math.min(length1, length2);
|
|
|
|
if (a1 != null && a2 != null) {
|
|
|
|
byte[] thisValue = a1.value;
|
|
|
|
byte[] thatValue = a2.value;
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
|
|
|
byte v1 = thisValue[i];
|
|
|
|
byte v2 = thatValue[i];
|
|
|
|
result = v1 - v2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (a1 != null) {
|
|
|
|
byte[] thisValue = a1.value;
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
|
|
|
int c1 = thisValue[i];
|
|
|
|
int c2 = o2.charAt(i);
|
|
|
|
result = c1 - c2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (a2 != null) {
|
|
|
|
byte[] thatValue = a2.value;
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
|
|
|
int c1 = o1.charAt(i);
|
|
|
|
int c2 = thatValue[i];
|
|
|
|
result = c1 - c2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < minLength; i++) {
|
|
|
|
int c1 = o1.charAt(i);
|
|
|
|
int c2 = o2.charAt(i);
|
|
|
|
result = c1 - c2;
|
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return length1 - length2;
|
|
|
|
}
|
|
|
|
};
|
2014-06-05 11:31:04 +02:00
|
|
|
|
2014-06-14 10:33:34 +02:00
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Returns the case-insensitive hash code of the specified string. Note that this method uses the same hashing
|
2014-06-14 10:33:34 +02:00
|
|
|
* algorithm with {@link #hashCode()} so that you can put both {@link AsciiString}s and arbitrary
|
|
|
|
* {@link CharSequence}s into the same {@link TextHeaders}.
|
|
|
|
*/
|
|
|
|
public static int caseInsensitiveHashCode(CharSequence value) {
|
2014-06-05 11:31:04 +02:00
|
|
|
if (value instanceof AsciiString) {
|
|
|
|
return value.hashCode();
|
|
|
|
}
|
|
|
|
|
|
|
|
int hash = 0;
|
|
|
|
final int end = value.length();
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = 0; i < end; i++) {
|
2015-04-01 01:23:52 +02:00
|
|
|
hash = hash * HASH_CODE_PRIME ^ value.charAt(i) & HASH_CODE_PRIME;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2014-06-14 10:33:34 +02:00
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. This only supports 8-bit
|
|
|
|
* ASCII.
|
2014-06-14 10:33:34 +02:00
|
|
|
*/
|
|
|
|
public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) {
|
|
|
|
if (a == b) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a instanceof AsciiString) {
|
|
|
|
AsciiString aa = (AsciiString) a;
|
|
|
|
return aa.equalsIgnoreCase(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b instanceof AsciiString) {
|
|
|
|
AsciiString ab = (AsciiString) b;
|
|
|
|
return ab.equalsIgnoreCase(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a == null || b == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.toString().equalsIgnoreCase(b.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns {@code true} if both {@link CharSequence}'s are equals. This only supports 8-bit ASCII.
|
|
|
|
*/
|
|
|
|
public static boolean equals(CharSequence a, CharSequence b) {
|
|
|
|
if (a == b) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a instanceof AsciiString) {
|
|
|
|
AsciiString aa = (AsciiString) a;
|
|
|
|
return aa.equals(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b instanceof AsciiString) {
|
|
|
|
AsciiString ab = (AsciiString) b;
|
|
|
|
return ab.equals(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a == null || b == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.equals(b);
|
|
|
|
}
|
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
private String string;
|
2014-09-19 01:04:35 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an {@link AsciiString} containing the given character sequence. If the given string is already a
|
|
|
|
* {@link AsciiString}, just returns the same instance.
|
|
|
|
*/
|
|
|
|
public static AsciiString of(CharSequence string) {
|
|
|
|
return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string);
|
|
|
|
}
|
|
|
|
|
2014-06-05 11:31:04 +02:00
|
|
|
public AsciiString(byte[] value) {
|
2015-04-01 01:23:52 +02:00
|
|
|
super(value);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(byte[] value, boolean copy) {
|
2015-04-01 01:23:52 +02:00
|
|
|
super(value, copy);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(byte[] value, int start, int length) {
|
2015-04-01 01:23:52 +02:00
|
|
|
super(value, start, length);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(byte[] value, int start, int length, boolean copy) {
|
2015-04-01 01:23:52 +02:00
|
|
|
super(value, start, length, copy);
|
|
|
|
}
|
2014-06-05 11:31:04 +02:00
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
public AsciiString(ByteBuffer value) {
|
|
|
|
super(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(ByteBuffer value, int start, int length) {
|
|
|
|
super(value, start, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(ByteBuffer value, int start, int length, boolean copy) {
|
|
|
|
super(value, start, length, copy);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(char[] value) {
|
2015-04-01 01:23:52 +02:00
|
|
|
this(checkNotNull(value, "value"), 0, value.length);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(char[] value, int start, int length) {
|
2015-04-01 01:23:52 +02:00
|
|
|
super(length);
|
|
|
|
if (start < 0 || start > checkNotNull(value, "value").length - length) {
|
2014-09-19 01:04:35 +02:00
|
|
|
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
|
|
|
|
+ ") <= " + "value.length(" + value.length + ')');
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = 0, j = start; i < length; i++, j++) {
|
2014-06-05 11:31:04 +02:00
|
|
|
this.value[i] = c2b(value[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(CharSequence value) {
|
2015-04-01 01:23:52 +02:00
|
|
|
this(checkNotNull(value, "value"), 0, value.length());
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public AsciiString(CharSequence value, int start, int length) {
|
2015-04-01 01:23:52 +02:00
|
|
|
super(length);
|
|
|
|
if (start < 0 || length < 0 || length > checkNotNull(value, "value").length() - start) {
|
2014-09-19 01:04:35 +02:00
|
|
|
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
|
|
|
|
+ ") <= " + "value.length(" + value.length() + ')');
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
this.value[i] = c2b(value.charAt(start + i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public char charAt(int index) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return b2c(byteAt(index));
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static byte c2b(char c) {
|
2015-04-01 01:23:52 +02:00
|
|
|
if (c > MAX_CHAR_VALUE) {
|
2014-06-05 11:31:04 +02:00
|
|
|
return '?';
|
|
|
|
}
|
|
|
|
return (byte) c;
|
|
|
|
}
|
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
private static char b2c(byte b) {
|
|
|
|
return (char) (b & 0xFF);
|
|
|
|
}
|
|
|
|
|
2014-06-05 11:31:04 +02:00
|
|
|
private static byte toLowerCase(byte b) {
|
|
|
|
if ('A' <= b && b <= 'Z') {
|
2015-04-01 01:23:52 +02:00
|
|
|
return (byte) (b + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static char toLowerCase(char c) {
|
|
|
|
if ('A' <= c && c <= 'Z') {
|
2015-04-01 01:23:52 +02:00
|
|
|
return (char) (c + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static byte toUpperCase(byte b) {
|
|
|
|
if ('a' <= b && b <= 'z') {
|
2015-04-01 01:23:52 +02:00
|
|
|
return (byte) (b - UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-04-01 01:23:52 +02:00
|
|
|
public String toString(Charset charset, int start, int end) {
|
|
|
|
if (start == 0 && end == length()) {
|
|
|
|
if (string == null) {
|
|
|
|
string = super.toString(charset, start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
return super.toString(charset, start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string using the ASCII values of the characters. Returns 0 if the strings
|
|
|
|
* contain the same characters in the same order. Returns a negative integer if the first non-equal character in
|
|
|
|
* this string has an ASCII value which is less than the ASCII value of the character at the same position in the
|
|
|
|
* specified string, or if this string is a prefix of the specified string. Returns a positive integer if the first
|
|
|
|
* non-equal character in this string has a ASCII value which is greater than the ASCII value of the character at
|
|
|
|
* the same position in the specified string, or if the specified string is a prefix of this string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param string the string to compare.
|
|
|
|
* @return 0 if the strings are equal, a negative integer if this string is before the specified string, or a
|
|
|
|
* positive integer if this string is after the specified string.
|
|
|
|
* @throws NullPointerException if {@code string} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public int compareTo(CharSequence string) {
|
|
|
|
if (this == string) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int result;
|
|
|
|
int length1 = length();
|
|
|
|
int length2 = string.length();
|
|
|
|
int minLength = Math.min(length1, length2);
|
|
|
|
byte[] value = this.value;
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = 0, j = 0; j < minLength; i++, j++) {
|
2015-04-01 01:23:52 +02:00
|
|
|
result = b2c(value[i]) - string.charAt(j);
|
2014-06-05 11:31:04 +02:00
|
|
|
if (result != 0) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return length1 - length2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string using the ASCII values of the characters, ignoring case differences.
|
|
|
|
* Returns 0 if the strings contain the same characters in the same order. Returns a negative integer if the first
|
|
|
|
* non-equal character in this string has an ASCII value which is less than the ASCII value of the character at the
|
|
|
|
* same position in the specified string, or if this string is a prefix of the specified string. Returns a positive
|
|
|
|
* integer if the first non-equal character in this string has an ASCII value which is greater than the ASCII value
|
|
|
|
* of the character at the same position in the specified string, or if the specified string is a prefix of this
|
|
|
|
* string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param string the string to compare.
|
|
|
|
* @return 0 if the strings are equal, a negative integer if this string is before the specified string, or a
|
|
|
|
* positive integer if this string is after the specified string.
|
|
|
|
* @throws NullPointerException if {@code string} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public int compareToIgnoreCase(CharSequence string) {
|
2014-09-19 01:04:35 +02:00
|
|
|
return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(this, string);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Concatenates this string and the specified string.
|
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param string the string to concatenate
|
|
|
|
* @return a new string which is the concatenation of this string and the specified string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public AsciiString concat(CharSequence string) {
|
|
|
|
int thisLen = length();
|
|
|
|
int thatLen = string.length();
|
|
|
|
if (thatLen == 0) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (string instanceof AsciiString) {
|
|
|
|
AsciiString that = (AsciiString) string;
|
|
|
|
if (isEmpty()) {
|
|
|
|
return that;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] newValue = Arrays.copyOf(value, thisLen + thatLen);
|
|
|
|
System.arraycopy(that.value, 0, newValue, thisLen, thatLen);
|
|
|
|
|
|
|
|
return new AsciiString(newValue, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isEmpty()) {
|
|
|
|
return new AsciiString(string);
|
|
|
|
}
|
|
|
|
|
|
|
|
int newLen = thisLen + thatLen;
|
|
|
|
byte[] newValue = Arrays.copyOf(value, newLen);
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = thisLen, j = 0; i < newLen; i++, j++) {
|
2014-06-05 11:31:04 +02:00
|
|
|
newValue[i] = c2b(string.charAt(j));
|
|
|
|
}
|
|
|
|
|
|
|
|
return new AsciiString(newValue, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string to determine if the specified string is a suffix.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param suffix the suffix to look for.
|
|
|
|
* @return {@code true} if the specified string is a suffix of this string, {@code false} otherwise.
|
|
|
|
* @throws NullPointerException if {@code suffix} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public boolean endsWith(CharSequence suffix) {
|
|
|
|
int suffixLen = suffix.length();
|
|
|
|
return regionMatches(length() - suffixLen, suffix, 0, suffixLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string ignoring the case of the characters and returns true if they are
|
|
|
|
* equal.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param string the string to compare.
|
|
|
|
* @return {@code true} if the specified string is equal to this string, {@code false} otherwise.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public boolean equalsIgnoreCase(CharSequence string) {
|
|
|
|
if (string == this) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (string == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int thisLen = value.length;
|
|
|
|
final int thatLen = string.length();
|
|
|
|
if (thisLen != thatLen) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = 0; i < thisLen; i++) {
|
2015-04-01 01:23:52 +02:00
|
|
|
char c1 = b2c(value[i]);
|
2014-06-05 11:31:04 +02:00
|
|
|
char c2 = string.charAt(i);
|
|
|
|
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies the characters in this string to a character array.
|
|
|
|
*
|
|
|
|
* @return a character array containing the characters of this string.
|
|
|
|
*/
|
|
|
|
public char[] toCharArray() {
|
|
|
|
return toCharArray(0, length());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies the characters in this string to a character array.
|
|
|
|
*
|
|
|
|
* @return a character array containing the characters of this string.
|
|
|
|
*/
|
|
|
|
public char[] toCharArray(int start, int end) {
|
|
|
|
int length = end - start;
|
|
|
|
if (length == 0) {
|
|
|
|
return EmptyArrays.EMPTY_CHARS;
|
|
|
|
}
|
|
|
|
|
|
|
|
final char[] buffer = new char[length];
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = 0, j = start; i < length; i++, j++) {
|
2015-04-01 01:23:52 +02:00
|
|
|
buffer[i] = b2c(value[j]);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copied the content of this string to a character array.
|
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param srcIdx the starting offset of characters to copy.
|
|
|
|
* @param dst the destination character array.
|
|
|
|
* @param dstIdx the starting offset in the destination byte array.
|
|
|
|
* @param length the number of characters to copy.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public void copy(int srcIdx, char[] dst, int dstIdx, int length) {
|
|
|
|
if (dst == null) {
|
|
|
|
throw new NullPointerException("dst");
|
|
|
|
}
|
|
|
|
|
|
|
|
final int thisLen = value.length;
|
|
|
|
|
|
|
|
if (srcIdx < 0 || length > thisLen - srcIdx) {
|
2014-09-19 01:04:35 +02:00
|
|
|
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
|
|
|
|
+ length + ") <= srcLen(" + thisLen + ')');
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
final int dstEnd = dstIdx + length;
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = srcIdx, j = dstIdx; j < dstEnd; i++, j++) {
|
2015-04-01 01:23:52 +02:00
|
|
|
dst[j] = b2c(value[i]);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
@Override
|
|
|
|
public AsciiString subSequence(int start, int end) {
|
|
|
|
return (AsciiString) super.subSequence(start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Searches in this string for the first index of the specified string. The search for the string starts at the
|
|
|
|
* beginning and moves towards the end of this string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param string the string to find.
|
|
|
|
* @return the index of the first character of the specified string in this string, -1 if the specified string is
|
|
|
|
* not a substring.
|
|
|
|
* @throws NullPointerException if {@code string} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public int indexOf(CharSequence string) {
|
|
|
|
return indexOf(string, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Searches in this string for the index of the specified string. The search for the string starts at the specified
|
|
|
|
* offset and moves towards the end of this string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param subString the string to find.
|
|
|
|
* @param start the starting offset.
|
|
|
|
* @return the index of the first character of the specified string in this string, -1 if the specified string is
|
|
|
|
* not a substring.
|
|
|
|
* @throws NullPointerException if {@code subString} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public int indexOf(CharSequence subString, int start) {
|
|
|
|
if (start < 0) {
|
|
|
|
start = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int thisLen = value.length;
|
|
|
|
|
|
|
|
int subCount = subString.length();
|
|
|
|
if (subCount <= 0) {
|
|
|
|
return start < thisLen ? start : thisLen;
|
|
|
|
}
|
|
|
|
if (subCount > thisLen - start) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
final char firstChar = subString.charAt(0);
|
|
|
|
ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar);
|
|
|
|
try {
|
|
|
|
for (;;) {
|
|
|
|
int i = forEachByte(start, thisLen - start, IndexOfVisitor);
|
|
|
|
if (i == -1 || subCount + i > thisLen) {
|
|
|
|
return -1; // handles subCount > count || start >= count
|
|
|
|
}
|
|
|
|
int o1 = i, o2 = 0;
|
|
|
|
while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
|
|
|
|
// Intentionally empty
|
|
|
|
}
|
|
|
|
if (o2 == subCount) {
|
2014-06-05 11:31:04 +02:00
|
|
|
return i;
|
|
|
|
}
|
2015-04-01 01:23:52 +02:00
|
|
|
start = i + 1;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
2015-04-01 01:23:52 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
PlatformDependent.throwException(e);
|
|
|
|
return -1;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Searches in this string for the last index of the specified string. The search for the string starts at the end
|
|
|
|
* and moves towards the beginning of this string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param string the string to find.
|
|
|
|
* @return the index of the first character of the specified string in this string, -1 if the specified string is
|
|
|
|
* not a substring.
|
|
|
|
* @throws NullPointerException if {@code string} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public int lastIndexOf(CharSequence string) {
|
|
|
|
// Use count instead of count - 1 so lastIndexOf("") answers count
|
|
|
|
return lastIndexOf(string, length());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Searches in this string for the index of the specified string. The search for the string starts at the specified
|
|
|
|
* offset and moves towards the beginning of this string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param subString the string to find.
|
|
|
|
* @param start the starting offset.
|
|
|
|
* @return the index of the first character of the specified string in this string , -1 if the specified string is
|
|
|
|
* not a substring.
|
|
|
|
* @throws NullPointerException if {@code subString} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public int lastIndexOf(CharSequence subString, int start) {
|
|
|
|
final int thisLen = value.length;
|
|
|
|
final int subCount = subString.length();
|
|
|
|
|
|
|
|
if (subCount > thisLen || start < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (subCount <= 0) {
|
|
|
|
return start < thisLen ? start : thisLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
start = Math.min(start, thisLen - subCount);
|
|
|
|
|
|
|
|
// count and subCount are both >= 1
|
2015-04-01 01:23:52 +02:00
|
|
|
final char firstChar = subString.charAt(0);
|
|
|
|
ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar);
|
|
|
|
try {
|
|
|
|
for (;;) {
|
|
|
|
int i = forEachByteDesc(start, thisLen - start, IndexOfVisitor);
|
|
|
|
if (i == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
int o1 = i, o2 = 0;
|
|
|
|
while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
|
|
|
|
// Intentionally empty
|
|
|
|
}
|
|
|
|
if (o2 == subCount) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
start = i - 1;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
2015-04-01 01:23:52 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
PlatformDependent.throwException(e);
|
|
|
|
return -1;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string and compares the specified range of characters to determine if they
|
|
|
|
* are the same.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param thisStart the starting offset in this string.
|
|
|
|
* @param string the string to compare.
|
|
|
|
* @param start the starting offset in the specified string.
|
|
|
|
* @param length the number of characters to compare.
|
|
|
|
* @return {@code true} if the ranges of characters are equal, {@code false} otherwise
|
|
|
|
* @throws NullPointerException if {@code string} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public boolean regionMatches(int thisStart, CharSequence string, int start, int length) {
|
|
|
|
if (string == null) {
|
|
|
|
throw new NullPointerException("string");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start < 0 || string.length() - start < length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int thisLen = value.length;
|
|
|
|
if (thisStart < 0 || thisLen - thisStart < length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length <= 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int thisEnd = thisStart + length;
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = thisStart, j = start; i < thisEnd; i++, j++) {
|
2015-04-01 01:23:52 +02:00
|
|
|
if (b2c(value[i]) != string.charAt(j)) {
|
2014-06-05 11:31:04 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string and compares the specified range of characters to determine if they
|
|
|
|
* are the same. When ignoreCase is true, the case of the characters is ignored during the comparison.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param ignoreCase specifies if case should be ignored.
|
|
|
|
* @param thisStart the starting offset in this string.
|
|
|
|
* @param string the string to compare.
|
|
|
|
* @param start the starting offset in the specified string.
|
|
|
|
* @param length the number of characters to compare.
|
|
|
|
* @return {@code true} if the ranges of characters are equal, {@code false} otherwise.
|
|
|
|
* @throws NullPointerException if {@code string} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
2014-09-19 01:04:35 +02:00
|
|
|
public boolean regionMatches(boolean ignoreCase, int thisStart, CharSequence string, int start, int length) {
|
2014-06-05 11:31:04 +02:00
|
|
|
if (!ignoreCase) {
|
|
|
|
return regionMatches(thisStart, string, start, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (string == null) {
|
|
|
|
throw new NullPointerException("string");
|
|
|
|
}
|
|
|
|
|
|
|
|
final int thisLen = value.length;
|
|
|
|
if (thisStart < 0 || length > thisLen - thisStart) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (start < 0 || length > string.length() - start) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int thisEnd = thisStart + length;
|
|
|
|
while (thisStart < thisEnd) {
|
2015-04-01 01:23:52 +02:00
|
|
|
char c1 = b2c(value[thisStart++]);
|
2014-06-05 11:31:04 +02:00
|
|
|
char c2 = string.charAt(start++);
|
|
|
|
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Copies this string replacing occurrences of the specified character with another character.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param oldChar the character to replace.
|
|
|
|
* @param newChar the replacement character.
|
2014-06-05 11:31:04 +02:00
|
|
|
* @return a new string with occurrences of oldChar replaced by newChar.
|
|
|
|
*/
|
|
|
|
public AsciiString replace(char oldChar, char newChar) {
|
2015-04-01 01:23:52 +02:00
|
|
|
if (oldChar > MAX_CHAR_VALUE) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int index;
|
|
|
|
final byte oldCharByte = c2b(oldChar);
|
|
|
|
try {
|
|
|
|
index = forEachByte(new IndexOfProcessor(oldCharByte));
|
|
|
|
} catch (Exception e) {
|
|
|
|
PlatformDependent.throwException(e);
|
|
|
|
return this;
|
|
|
|
}
|
2014-06-05 11:31:04 +02:00
|
|
|
if (index == -1) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int count = value.length;
|
2015-04-01 01:23:52 +02:00
|
|
|
final byte newCharByte = c2b(newChar);
|
2014-06-05 11:31:04 +02:00
|
|
|
byte[] buffer = new byte[count];
|
2015-04-01 01:23:52 +02:00
|
|
|
for (int i = 0, j = 0; i < count; i++, j++) {
|
2014-06-05 11:31:04 +02:00
|
|
|
byte b = value[i];
|
2015-04-01 01:23:52 +02:00
|
|
|
if (b == oldCharByte) {
|
|
|
|
b = newCharByte;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
buffer[j] = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new AsciiString(buffer, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string to determine if the specified string is a prefix.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param prefix the string to look for.
|
|
|
|
* @return {@code true} if the specified string is a prefix of this string, {@code false} otherwise
|
|
|
|
* @throws NullPointerException if {@code prefix} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public boolean startsWith(CharSequence prefix) {
|
|
|
|
return startsWith(prefix, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares the specified string to this string, starting at the specified offset, to determine if the specified
|
|
|
|
* string is a prefix.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param prefix the string to look for.
|
|
|
|
* @param start the starting offset.
|
|
|
|
* @return {@code true} if the specified string occurs in this string at the specified offset, {@code false}
|
|
|
|
* otherwise.
|
|
|
|
* @throws NullPointerException if {@code prefix} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public boolean startsWith(CharSequence prefix, int start) {
|
|
|
|
return regionMatches(start, prefix, 0, prefix.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Converts the characters in this string to lowercase, using the default Locale.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @return a new string containing the lowercase characters equivalent to the characters in this string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public AsciiString toLowerCase() {
|
|
|
|
boolean lowercased = true;
|
2014-09-19 01:04:35 +02:00
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < value.length; ++i) {
|
|
|
|
byte b = value[i];
|
2014-06-05 11:31:04 +02:00
|
|
|
if (b >= 'A' && b <= 'Z') {
|
|
|
|
lowercased = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this string does not contain any uppercase characters.
|
|
|
|
if (lowercased) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int length = value.length;
|
|
|
|
final byte[] newValue = new byte[length];
|
2014-09-19 01:04:35 +02:00
|
|
|
for (i = 0, j = 0; i < length; ++i, ++j) {
|
2014-06-05 11:31:04 +02:00
|
|
|
newValue[i] = toLowerCase(value[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new AsciiString(newValue, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Converts the characters in this string to uppercase, using the default Locale.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @return a new string containing the uppercase characters equivalent to the characters in this string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public AsciiString toUpperCase() {
|
|
|
|
boolean uppercased = true;
|
2014-09-19 01:04:35 +02:00
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < value.length; ++i) {
|
|
|
|
byte b = value[i];
|
2014-06-05 11:31:04 +02:00
|
|
|
if (b >= 'a' && b <= 'z') {
|
|
|
|
uppercased = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this string does not contain any lowercase characters.
|
|
|
|
if (uppercased) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
final int length = value.length;
|
|
|
|
final byte[] newValue = new byte[length];
|
2014-09-19 01:04:35 +02:00
|
|
|
for (i = 0, j = 0; i < length; ++i, ++j) {
|
2014-06-05 11:31:04 +02:00
|
|
|
newValue[i] = toUpperCase(value[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new AsciiString(newValue, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Copies this string removing white space characters from the beginning and end of the string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @return a new string with characters {@code <= \\u0020} removed from the beginning and the end.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public AsciiString trim() {
|
|
|
|
int start = 0, last = value.length;
|
|
|
|
int end = last;
|
|
|
|
while (start <= end && value[start] <= ' ') {
|
2014-09-19 01:04:35 +02:00
|
|
|
start++;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
while (end >= start && value[end] <= ' ') {
|
2014-09-19 01:04:35 +02:00
|
|
|
end--;
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
if (start == 0 && end == last) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
return new AsciiString(value, start, end - start + 1, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Compares a {@code CharSequence} to this {@code String} to determine if their contents are equal.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param cs the character sequence to compare to.
|
2014-06-05 11:31:04 +02:00
|
|
|
* @return {@code true} if equal, otherwise {@code false}
|
|
|
|
*/
|
|
|
|
public boolean contentEquals(CharSequence cs) {
|
|
|
|
if (cs == null) {
|
|
|
|
throw new NullPointerException();
|
|
|
|
}
|
|
|
|
|
|
|
|
int length1 = length();
|
|
|
|
int length2 = cs.length();
|
|
|
|
if (length1 != length2) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length1 == 0 && length2 == 0) {
|
|
|
|
return true; // since both are empty strings
|
|
|
|
}
|
|
|
|
|
|
|
|
return regionMatches(0, cs, 0, length2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines whether this string matches a given regular expression.
|
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param expr the regular expression to be matched.
|
2014-06-05 11:31:04 +02:00
|
|
|
* @return {@code true} if the expression matches, otherwise {@code false}.
|
2014-09-19 01:04:35 +02:00
|
|
|
* @throws PatternSyntaxException if the syntax of the supplied regular expression is not valid.
|
|
|
|
* @throws NullPointerException if {@code expr} is {@code null}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public boolean matches(String expr) {
|
|
|
|
return Pattern.matches(expr, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Splits this string using the supplied regular expression {@code expr}. The parameter {@code max} controls the
|
|
|
|
* behavior how many times the pattern is applied to the string.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param expr the regular expression used to divide the string.
|
|
|
|
* @param max the number of entries in the resulting array.
|
|
|
|
* @return an array of Strings created by separating the string along matches of the regular expression.
|
|
|
|
* @throws NullPointerException if {@code expr} is {@code null}.
|
|
|
|
* @throws PatternSyntaxException if the syntax of the supplied regular expression is not valid.
|
2014-06-05 11:31:04 +02:00
|
|
|
* @see Pattern#split(CharSequence, int)
|
|
|
|
*/
|
|
|
|
public AsciiString[] split(String expr, int max) {
|
|
|
|
return toAsciiStringArray(Pattern.compile(expr).split(this, max));
|
|
|
|
}
|
|
|
|
|
|
|
|
private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
|
|
|
|
AsciiString[] res = new AsciiString[jdkResult.length];
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = 0; i < jdkResult.length; i++) {
|
2014-06-05 11:31:04 +02:00
|
|
|
res[i] = new AsciiString(jdkResult[i]);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Splits the specified {@link String} with the specified delimiter..
|
|
|
|
*/
|
|
|
|
public AsciiString[] split(char delim) {
|
|
|
|
final List<AsciiString> res = new ArrayList<AsciiString>();
|
|
|
|
|
|
|
|
int start = 0;
|
|
|
|
final int length = value.length;
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = start; i < length; i++) {
|
2014-06-05 11:31:04 +02:00
|
|
|
if (charAt(i) == delim) {
|
|
|
|
if (start == i) {
|
|
|
|
res.add(EMPTY_STRING);
|
|
|
|
} else {
|
|
|
|
res.add(new AsciiString(value, start, i - start, false));
|
|
|
|
}
|
|
|
|
start = i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start == 0) { // If no delimiter was found in the value
|
|
|
|
res.add(this);
|
|
|
|
} else {
|
|
|
|
if (start != length) {
|
|
|
|
// Add the last element if it's not empty.
|
|
|
|
res.add(new AsciiString(value, start, length - start, false));
|
|
|
|
} else {
|
|
|
|
// Truncate trailing empty elements.
|
2014-09-19 01:04:35 +02:00
|
|
|
for (int i = res.size() - 1; i >= 0; i--) {
|
2014-06-05 11:31:04 +02:00
|
|
|
if (res.get(i).isEmpty()) {
|
|
|
|
res.remove(i);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res.toArray(new AsciiString[res.size()]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-09-19 01:04:35 +02:00
|
|
|
* Determines if this {@code String} contains the sequence of characters in the {@code CharSequence} passed.
|
2014-06-05 11:31:04 +02:00
|
|
|
*
|
2014-09-19 01:04:35 +02:00
|
|
|
* @param cs the character sequence to search for.
|
|
|
|
* @return {@code true} if the sequence of characters are contained in this string, otherwise {@code false}.
|
2014-06-05 11:31:04 +02:00
|
|
|
*/
|
|
|
|
public boolean contains(CharSequence cs) {
|
|
|
|
if (cs == null) {
|
|
|
|
throw new NullPointerException();
|
|
|
|
}
|
|
|
|
return indexOf(cs) >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int parseInt() {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiInt();
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public int parseInt(int radix) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiInt(radix);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public int parseInt(int start, int end) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiInt(start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public int parseInt(int start, int end, int radix) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiInt(start, end, radix);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public long parseLong() {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiLong();
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public long parseLong(int radix) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiLong(radix);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public long parseLong(int start, int end) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiLong(start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public long parseLong(int start, int end, int radix) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiLong(start, end, radix);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
2015-04-01 01:23:52 +02:00
|
|
|
public char parseChar(int start) {
|
|
|
|
return charAt(start);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public short parseShort() {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiShort();
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public short parseShort(int radix) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiShort(radix);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public short parseShort(int start, int end) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiShort(start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public short parseShort(int start, int end, int radix) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiShort(start, end, radix);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public float parseFloat() {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiFloat();
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public float parseFloat(int start, int end) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiFloat(start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public double parseDouble() {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiDouble();
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public double parseDouble(int start, int end) {
|
2015-04-01 01:23:52 +02:00
|
|
|
return parseAsciiDouble(start, end);
|
2014-06-05 11:31:04 +02:00
|
|
|
}
|
|
|
|
}
|