blob: f91a190b27bd429ff4e8f77b9b5c4738f61abae9 [file] [log] [blame]
/*
* 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.
*/
#include "String.h"
#include <decaf/lang/ArrayPointer.h>
#include <decaf/lang/System.h>
#include <decaf/lang/exceptions/NullPointerException.h>
#include <decaf/lang/exceptions/IndexOutOfBoundsException.h>
#include <decaf/lang/exceptions/StringIndexOutOfBoundsException.h>
#include <decaf/lang/Short.h>
#include <decaf/lang/Integer.h>
#include <decaf/lang/Long.h>
#include <decaf/lang/Character.h>
#include <decaf/lang/Float.h>
#include <decaf/lang/Double.h>
#include <decaf/internal/util/StringUtils.h>
using namespace std;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
using namespace decaf::internal;
using namespace decaf::internal::util;
////////////////////////////////////////////////////////////////////////////////
namespace decaf {
namespace lang {
class Contents {
public:
ArrayPointer<char> value;
int length;
int offset;
int hashCode;
public:
/**
* Contents as empty string.
*/
Contents() : value(), length(0), offset(0), hashCode(0) {}
/**
* Contents created with the given length, the array is length + 1 to add the
* null terminating character.
*/
Contents(int length) : value(length + 1), length(length), offset(0), hashCode(0) {
value[length] = 0; // Null terminated
}
/**
* Contents is a view of some other String which can either be all or a
* window allowing for substring methods to not need to copy the contents.
*/
Contents(int offset, int length, ArrayPointer<char> value) :
value(value), length(length), offset(offset), hashCode(0) {}
};
}}
////////////////////////////////////////////////////////////////////////////////
String::String(Contents* content) :
contents(new Contents(0, content->length, content->value)) {
}
////////////////////////////////////////////////////////////////////////////////
String::String(int offset, int length, const ArrayPointer<char> content) :
contents(new Contents(offset, length, content)) {
}
////////////////////////////////////////////////////////////////////////////////
String::String() : contents(new Contents()) {
}
////////////////////////////////////////////////////////////////////////////////
String::String(const char value, int count) : contents() {
if (count < 0) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "count parameter out of Bounds: %d.", count);
}
contents = new Contents(count);
for (int i = 0; i < count; ++i) {
contents->value[i] = value;
}
}
////////////////////////////////////////////////////////////////////////////////
String::String(const String& source) : contents(new Contents(*source.contents)) {
}
////////////////////////////////////////////////////////////////////////////////
String::String(const std::string& source) : contents(new Contents((int)source.length())) {
// load the passed string into the contents value.
System::arraycopy(source.c_str(), 0, contents->value.get(), 0, source.length());
}
////////////////////////////////////////////////////////////////////////////////
String::String(const char* array) : contents() {
if (array == NULL) {
throw NullPointerException(
__FILE__, __LINE__, "Buffer pointer passed was NULL.");
}
int size = StringUtils::stringLength(array);
if (size > 0) {
this->contents = new Contents(size);
System::arraycopy(array, 0, contents->value.get(), 0, size);
} else {
this->contents = new Contents();
}
}
////////////////////////////////////////////////////////////////////////////////
String::String(const char* array, int size) : contents() {
if (size < 0) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "size parameter out of Bounds: %d.", size);
}
if (array == NULL) {
throw NullPointerException(
__FILE__, __LINE__, "Buffer pointer passed was NULL.");
}
if (size > 0) {
this->contents = new Contents(size);
System::arraycopy(array, 0, contents->value.get(), 0, size);
} else {
this->contents = new Contents();
}
}
////////////////////////////////////////////////////////////////////////////////
String::String(const char* array, int offset, int length) : contents() {
int size = StringUtils::stringLength(array);
if (offset > size || offset < 0) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "offset parameter out of Bounds: %d.", offset);
}
if (length < 0 || length > size - offset) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "length parameter out of Bounds: %d.", length);
}
if (array == NULL) {
throw NullPointerException(
__FILE__, __LINE__, "Buffer pointer passed was NULL.");
}
if (size > 0 && length > 0) {
this->contents = new Contents(length);
System::arraycopy(array, offset, contents->value.get(), 0, length);
} else {
this->contents = new Contents();
}
}
////////////////////////////////////////////////////////////////////////////////
String::String(const char* array, int size, int offset, int length) : contents() {
if (size < 0) {
throw IndexOutOfBoundsException(
__FILE__, __LINE__, "size parameter out of Bounds: %d.", size);
}
if (offset > size || offset < 0) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "offset parameter out of Bounds: %d.", offset);
}
if (length < 0 || length > size - offset) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "length parameter out of Bounds: %d.", length);
}
if (array == NULL) {
throw NullPointerException(
__FILE__, __LINE__, "Buffer pointer passed was NULL.");
}
if (size > 0 && length > 0) {
this->contents = new Contents(length);
System::arraycopy(array, offset, contents->value.get(), 0, length);
} else {
this->contents = new Contents();
}
}
////////////////////////////////////////////////////////////////////////////////
String::~String() {
try {
delete this->contents;
}
DECAF_CATCH_NOTHROW(Exception)
DECAF_CATCHALL_NOTHROW()
}
////////////////////////////////////////////////////////////////////////////////
String& String::operator= (const String& other) {
contents->value = other.contents->value;
contents->length = other.contents->length;
contents->offset = other.contents->offset;
contents->hashCode = other.contents->hashCode;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
String& String::operator= (const std::string& other) {
delete contents;
if (!other.empty()) {
int length = (int) other.length();
contents = new Contents(length);
System::arraycopy(other.c_str(), 0, contents->value.get(), 0, length);
} else {
contents = new Contents();
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
String& String::operator= (const char* other) {
if (other == NULL) {
throw NullPointerException(__FILE__, __LINE__, "Assignment from NULL not supported.");
}
int length = StringUtils::stringLength(other);
delete contents;
if (length > 0) {
contents = new Contents(length);
System::arraycopy(other, 0, contents->value.get(), 0, length);
} else {
contents = new Contents();
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator==(const String& other) const {
return this->equals(other);
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator==(const std::string& other) const {
return this->equals(other);
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator==(const char* other) const {
return this->equals(other);
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator!=(const String& other) const {
return !this->equals(other);
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator!=(const std::string& other) const {
return !this->equals(other);
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator!=(const char* other) const {
return !this->equals(other);
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator< (const String& other) const {
return this->compareTo(other) < 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator< (const std::string& other) const {
return this->compareTo(other) < 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator< (const char* other) const {
return this->compareTo(other) < 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator<=(const String& other) const {
return this->compareTo(other) <= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator<=(const std::string& other) const {
return this->compareTo(other) <= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator<=(const char* other) const {
return this->compareTo(other) <= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator> (const String& other) const {
return this->compareTo(other) > 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator> (const std::string& other) const {
return this->compareTo(other) > 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator> (const char* other) const {
return this->compareTo(other) > 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator>=(const String& other) const {
return this->compareTo(other) >= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator>=(const std::string& other) const {
return this->compareTo(other) >= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::operator>=(const char* other) const {
return this->compareTo(other) >= 0;
}
////////////////////////////////////////////////////////////////////////////////
String String::operator+ (const String& other) const {
return this->concat(other);
}
////////////////////////////////////////////////////////////////////////////////
String String::operator+ (const std::string& other) const {
return this->concat(other);
}
////////////////////////////////////////////////////////////////////////////////
String String::operator+ (const char* other) const {
return this->concat(other);
}
////////////////////////////////////////////////////////////////////////////////
const char* String::c_str() const {
if (contents->length == 0) {
return "";
}
if (contents->length == contents->value.length() - 1) {
return (const char*) (contents->value.get() + contents->offset);
}
Contents* newContents = new Contents(contents->length);
System::arraycopy(contents->value.get(), contents->offset,
newContents->value.get(), 0, contents->length);
Contents* oldContents = this->contents;
this->contents = newContents;
delete oldContents;
return contents->value.get();
}
////////////////////////////////////////////////////////////////////////////////
char String::charAt(int index) const {
try {
if (index < 0 || index >= this->length()) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "Index given is out of bounds: %d.", index);
}
return this->contents->value[this->contents->offset + index];
}
DECAF_CATCH_RETHROW(StringIndexOutOfBoundsException)
DECAF_CATCHALL_THROW(StringIndexOutOfBoundsException)
}
////////////////////////////////////////////////////////////////////////////////
String String::compact() const {
// Empty String.
if (contents->value.length() == 0) {
return *this;
}
// Don't do anything if the string is already compact.
if (contents->value.length() > this->contents->length + 1) {
return String(contents->value.get(), contents->offset, contents->length);
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
int String::compareTo(const String& string) const {
int o1 = contents->offset;
int o2 = string.contents->offset;
int result;
int end = contents->offset +
(contents->length < string.contents->length ? contents->length : string.contents->length);
while (o1 < end) {
if ((result = contents->value[o1++] - string.contents->value[o2++]) != 0) {
return result;
}
}
return contents->length - string.contents->length;
}
////////////////////////////////////////////////////////////////////////////////
int String::compareTo(const std::string& string) const {
int o1 = contents->offset;
int o2 = 0;
int result;
int end = contents->offset +
(contents->length < (int) string.length() ? contents->length : (int) string.length());
while (o1 < end) {
if ((result = contents->value[o1++] - string.at(o2++)) != 0) {
return result;
}
}
return contents->length - (int)string.length();
}
////////////////////////////////////////////////////////////////////////////////
int String::compareTo(const char* string) const {
if (string == NULL) {
throw NullPointerException(__FILE__, __LINE__, "Comparison C String cannot be NULL");
}
int length = StringUtils::stringLength(string);
int o1 = contents->offset;
int o2 = 0;
int result;
int end = contents->offset +
(contents->length < length ? contents->length : length);
while (o1 < end) {
if ((result = contents->value[o1++] - string[o2++]) != 0) {
return result;
}
}
return contents->length - length;
}
////////////////////////////////////////////////////////////////////////////////
int String::compareToIgnoreCase(const String& string) const {
int o1 = contents->offset;
int o2 = string.contents->offset;
int result;
int end = contents->offset +
(contents->length < string.contents->length ? contents->length : string.contents->length);
char c1, c2;
while (o1 < end) {
if ((c1 = contents->value[o1++]) == (c2 = string.contents->value[o2++])) {
continue;
}
c1 = Character::toLowerCase(c1);
c2 = Character::toLowerCase(c2);
if ((result = c1 - c2) != 0) {
return result;
}
}
return contents->length - string.contents->length;
}
////////////////////////////////////////////////////////////////////////////////
int String::compareToIgnoreCase(const std::string& string) const {
int o1 = contents->offset;
int o2 = 0;
int result;
int end = contents->offset +
(contents->length < (int) string.length() ? contents->length : (int) string.length());
char c1, c2;
while (o1 < end) {
if ((c1 = contents->value[o1++]) == (c2 = string.at(o2++))) {
continue;
}
c1 = Character::toLowerCase(c1);
c2 = Character::toLowerCase(c2);
if ((result = c1 - c2) != 0) {
return result;
}
}
return contents->length - (int) string.length();
}
////////////////////////////////////////////////////////////////////////////////
int String::compareToIgnoreCase(const char* string) const {
if (string == NULL) {
throw NullPointerException(__FILE__, __LINE__, "Comparison C String cannot be NULL");
}
int length = StringUtils::stringLength(string);
int o1 = contents->offset;
int o2 = 0;
int result;
int end = contents->offset + (contents->length < length ? contents->length : length);
char c1, c2;
while (o1 < end) {
if ((c1 = contents->value[o1++]) == (c2 = string[o2++])) {
continue;
}
c1 = Character::toLowerCase(c1);
c2 = Character::toLowerCase(c2);
if ((result = c1 - c2) != 0) {
return result;
}
}
return contents->length - length;
}
////////////////////////////////////////////////////////////////////////////////
String String::copyValueOf(const char* data) {
if (data == NULL) {
throw NullPointerException(__FILE__, __LINE__, "C String to be copied cannot be NULL");
}
return String(data, 0, StringUtils::stringLength(data));
}
////////////////////////////////////////////////////////////////////////////////
String String::copyValueOf(char* data, int start, int length) {
if (data == NULL) {
throw NullPointerException(__FILE__, __LINE__, "C String to be copied cannot be NULL");
}
return String(data, start, length);
}
////////////////////////////////////////////////////////////////////////////////
String String::concat(const String& string) const {
if (string.contents->length == 0) {
return *this;
}
Contents buffer(contents->length + string.contents->length);
if (contents->length > 0) {
System::arraycopy(contents->value.get(), contents->offset,
buffer.value.get(), 0, contents->length);
}
System::arraycopy(string.contents->value.get(), string.contents->offset,
buffer.value.get(), contents->length, string.contents->length);
return String(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
String String::concat(const std::string& string) const {
if (string.length() == 0) {
return *this;
}
Contents buffer(contents->length + (int) string.length());
if (contents->length > 0) {
System::arraycopy(contents->value.get(), contents->offset,
buffer.value.get(), 0, contents->length);
}
System::arraycopy(string.c_str(), 0, buffer.value.get(), contents->length, string.length());
return String(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
String String::concat(const char* string) const {
if (string == NULL) {
return *this;
}
int length = StringUtils::stringLength(string);
if (length == 0) {
return *this;
}
Contents buffer(contents->length + length);
if (contents->length > 0) {
System::arraycopy(contents->value.get(), contents->offset,
buffer.value.get(), 0, contents->length);
}
System::arraycopy(string, 0, buffer.value.get(), contents->length, length);
return String(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
bool String::contains(const String& string) const {
return indexOf(string) >= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::contains(const std::string& string) const {
return indexOf(string) >= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::contains(const char* string) const {
return indexOf(string) >= 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::endsWith(const String& suffix) const {
return regionMatches(contents->length - suffix.contents->length, suffix, 0, suffix.contents->length);
}
////////////////////////////////////////////////////////////////////////////////
bool String::equals(const String& other) const {
if (this == &other) {
return true;
}
if (other.isEmpty() && this->isEmpty()) {
return true;
}
if (other.length() != this->length()) {
return false;
}
// Don't force compute hash code on this instance, if not already done
// we will just do a straight compare.
int hashCode = contents->hashCode;
int otherHashCode = other.contents->hashCode;
if (hashCode != otherHashCode && hashCode != 0 && otherHashCode != 0) {
return false;
}
for (int i = 0; i < contents->length; ++i) {
if (contents->value[contents->offset + i] != other.contents->value[other.contents->offset + i]) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool String::equals(const std::string& other) const {
if (other.empty() && this->isEmpty()) {
return true;
}
if (other.length() != (std::size_t) this->length()) {
return false;
}
for (int i = 0; i < contents->length; ++i) {
if (contents->value[contents->offset + i] != (unsigned char) other.at(i)) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool String::equals(const char* other) const {
if (other == NULL) {
return false;
}
if (StringUtils::stringLength(other) != this->length()) {
return false;
}
for (int i = 0; i < contents->length; ++i) {
if (contents->value[contents->offset + i] != (unsigned char) other[i]) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool String::equalsIgnoreCase(const String& string) const {
if (this == &string) {
return true;
}
if (contents->length != string.contents->length) {
return false;
}
int offsetThis = contents->offset;
int offsetOther = string.contents->offset;
int end = contents->offset + contents->length;
char c1, c2;
ArrayPointer<char> target = string.contents->value;
while (offsetThis < end) {
if ((c1 = contents->value[offsetThis++]) != (c2 = target[offsetOther++])) {
// If we add support for multibyte strings we need to check both cases
// toUpperCase and toLowerCase because of Unicode.
if (Character::toUpperCase(c1) != Character::toUpperCase(c2)) {
return false;
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool String::equalsIgnoreCase(const std::string& string) const {
if (contents->length != (int) string.length()) {
return false;
}
int offsetThis = contents->offset;
int end = contents->offset + contents->length;
int indexOther = 0;
char c1, c2;
while (offsetThis < end) {
if ((c1 = contents->value[offsetThis++]) != (c2 = string.at(indexOther++))) {
// If we add support for multibyte strings we need to check both cases
// toUpperCase and toLowerCase because of Unicode.
if (Character::toUpperCase(c1) != Character::toUpperCase(c2)) {
return false;
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool String::equalsIgnoreCase(const char* string) const {
if (string == NULL) {
return false;
}
int stringLen = StringUtils::stringLength(string);
if (contents->length != stringLen) {
return false;
}
int indexOther = 0;
int offsetThis = contents->offset;
int end = contents->offset + contents->length;
char c1, c2;
while (offsetThis < end) {
if ((c1 = contents->value[offsetThis++]) != (c2 = string[indexOther++])) {
// If we add support for multibyte strings we need to check both cases
// toUpperCase and toLowerCase because of Unicode.
if (Character::toUpperCase(c1) != Character::toUpperCase(c2)) {
return false;
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
int String::findFirstOf(const String& chars) const {
return findFirstOf(chars, 0);
}
////////////////////////////////////////////////////////////////////////////////
int String::findFirstOf(const String& chars, int start) const {
if (start < contents->length) {
if (start < 0) {
start = 0;
}
for (int i = contents->offset + start; i < contents->offset + contents->length; i++) {
char c = contents->value[i];
if (chars.indexOf(c) != -1) {
return i;
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
int String::findFirstNotOf(const String& chars) const {
return findFirstNotOf(chars, 0);
}
////////////////////////////////////////////////////////////////////////////////
int String::findFirstNotOf(const String& chars, int start) const {
if (start < contents->length) {
if (start < 0) {
start = 0;
}
for (int i = contents->offset + start; i < contents->offset + contents->length; i++) {
char c = contents->value[i];
if (chars.indexOf(c) == -1) {
return i;
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
void String::getChars(int srcBegin, int srcEnd, char* dest, int destSize, int destBegin) const {
if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > contents->length) {
throw StringIndexOutOfBoundsException(__FILE__, __LINE__,
"Invalid start or end parameters: %d, %d", srcBegin, srcEnd);
}
if (destSize < 0 || destBegin < 0 || (destBegin + (srcEnd - srcBegin)) > destSize) {
throw StringIndexOutOfBoundsException(__FILE__, __LINE__,
"Invalid destination size or offset parameters: %d, %d", destSize, destBegin);
}
if (dest == NULL) {
throw NullPointerException(__FILE__, __LINE__, "Destination pointer was Null");
}
// Note: last character not copied!
System::arraycopy(contents->value.get(), srcBegin + contents->offset,
dest, destBegin, srcEnd - srcBegin);
}
////////////////////////////////////////////////////////////////////////////////
void String::getChars(int start, int end, char* buffer, int index) const {
// NOTE last character not copied!
System::arraycopy(contents->value.get(), start + contents->offset, buffer, index, end - start);
}
////////////////////////////////////////////////////////////////////////////////
int String::hashCode() const {
if (contents->hashCode == 0) {
if (contents->length == 0) {
return 0;
}
int hash = 0;
for (int i = contents->offset; i < contents->length + contents->offset; i++) {
hash = contents->value[i] + ((hash << 5) - hash);
}
contents->hashCode = hash;
}
return contents->hashCode;
}
////////////////////////////////////////////////////////////////////////////////
bool String::isEmpty() const {
return this->contents->length == 0;
}
////////////////////////////////////////////////////////////////////////////////
bool String::isNullOrEmpty(const char* string) {
return string == NULL || StringUtils::stringLength(string) == 0;
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(char value) const {
return indexOf(value, 0);
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(char value, int start) const {
if (start < contents->length) {
if (start < 0) {
start = 0;
}
for (int i = contents->offset + start; i < contents->offset + contents->length; i++) {
if (contents->value[i] == value) {
return i - contents->offset;
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(const String& string) const {
return indexOf(string, 0);
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(const String& subString, int start) const {
if (start < 0) {
start = 0;
}
int subCount = subString.contents->length;
if (subCount > 0) {
if (subCount + start > contents->length) {
return -1;
}
char* target = subString.contents->value.get();
int subOffset = subString.contents->offset;
char firstChar = target[subOffset];
int end = subOffset + subCount;
while (true) {
int i = indexOf(firstChar, start);
if (i == -1 || subCount + i > contents->length) {
return -1; // handles subCount > length() || start >= length()
}
int o1 = contents->offset + i;
int o2 = subOffset;
while (++o2 < end && contents->value[++o1] == target[o2]) {
// Intentionally empty
}
if (o2 == end) {
return i;
}
start = i + 1;
}
}
return start < contents->length ? start : contents->length;
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(const std::string& string) const {
return indexOf(string, 0);
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(const std::string& subString, int start) const {
if (start < 0) {
start = 0;
}
int subCount = (int) subString.length();
if (subCount > 0) {
if (subCount + start > contents->length) {
return -1;
}
const char* target = subString.c_str();
char firstChar = target[0];
int end = subCount;
while (true) {
int i = indexOf(firstChar, start);
if (i == -1 || subCount + i > contents->length) {
return -1; // handles subCount > length() || start >= length()
}
int o1 = contents->offset + i;
int o2 = 0;
while (++o2 < end && contents->value[++o1] == target[o2]) {
// Intentionally empty
}
if (o2 == end) {
return i;
}
start = i + 1;
}
}
return start < contents->length ? start : contents->length;
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(const char* string) const {
return indexOf(string, 0);
}
////////////////////////////////////////////////////////////////////////////////
int String::indexOf(const char* subString, int start) const {
if (start < 0) {
start = 0;
}
if (subString == NULL) {
return -1;
}
int subCount = StringUtils::stringLength(subString);
if (subCount > 0) {
if (subCount + start > contents->length) {
return -1;
}
char firstChar = subString[0];
int end = subCount;
while (true) {
int i = indexOf(firstChar, start);
if (i == -1 || subCount + i > contents->length) {
return -1; // handles subCount > length() || start >= length()
}
int o1 = contents->offset + i;
int o2 = 0;
while (++o2 < end && contents->value[++o1] == subString[o2]) {
// Intentionally empty
}
if (o2 == end) {
return i;
}
start = i + 1;
}
}
return start < contents->length ? start : contents->length;
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(char value) const {
return lastIndexOf(value, contents->length - 1);
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(char value, int start) const {
if (start >= 0) {
if (start >= contents->length) {
start = contents->length - 1;
}
for (int i = contents->offset + start; i >= contents->offset; --i) {
if (contents->value[i] == value) {
return i - contents->offset;
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(const String& string) const {
// Use length instead of length - 1 so lastIndexOf("") answers length
return lastIndexOf(string, contents->length);
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(const String& subString, int start) const {
int subCount = subString.contents->length;
if (subCount <= contents->length && start >= 0) {
if (subCount > 0) {
if (start > contents->length - subCount) {
start = contents->length - subCount;
}
// count and subCount are both >= 1
char* target = subString.contents->value.get();
int subOffset = subString.contents->offset;
char firstChar = target[subOffset];
int end = subOffset + subCount;
while (true) {
int i = lastIndexOf(firstChar, start);
if (i == -1) {
return -1;
}
int o1 = contents->offset + i;
int o2 = subOffset;
while (++o2 < end && contents->value[++o1] == target[o2]) {
// Intentionally empty
}
if (o2 == end) {
return i;
}
start = i - 1;
}
}
return start < contents->length ? start : contents->length;
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(const std::string& string) const {
// Use length instead of length - 1 so lastIndexOf("") answers length
return lastIndexOf(string, contents->length);
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(const std::string& subString, int start) const {
int subCount = (int) subString.length();
if (subCount <= contents->length && start >= 0) {
if (subCount > 0) {
if (start > contents->length - subCount) {
start = contents->length - subCount;
}
// count and subCount are both >= 1
const char* target = subString.c_str();
char firstChar = target[0];
int end = subCount;
while (true) {
int i = lastIndexOf(firstChar, start);
if (i == -1) {
return -1;
}
int o1 = contents->offset + i;
int o2 = 0;
while (++o2 < end && contents->value[++o1] == target[o2]) {
// Intentionally empty
}
if (o2 == end) {
return i;
}
start = i - 1;
}
}
return start < contents->length ? start : contents->length;
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(const char* string) const {
// Use length instead of length - 1 so lastIndexOf("") answers length
return lastIndexOf(string, contents->length);
}
////////////////////////////////////////////////////////////////////////////////
int String::lastIndexOf(const char* subString, int start) const {
if (subString == NULL) {
return -1;
}
int subCount = StringUtils::stringLength(subString);
if (subCount <= contents->length && start >= 0) {
if (subCount > 0) {
if (start > contents->length - subCount) {
start = contents->length - subCount;
}
// count and subCount are both >= 1
char firstChar = subString[0];
int end = subCount;
while (true) {
int i = lastIndexOf(firstChar, start);
if (i == -1) {
return -1;
}
int o1 = contents->offset + i;
int o2 = 0;
while (++o2 < end && contents->value[++o1] == subString[o2]) {
// Intentionally empty
}
if (o2 == end) {
return i;
}
start = i - 1;
}
}
return start < contents->length ? start : contents->length;
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
int String::length() const {
return this->contents->length;
}
////////////////////////////////////////////////////////////////////////////////
bool String::regionMatches(int thisStart, const String& string, int start, int length) const {
if (string.contents->length - start < length || start < 0) {
return false;
}
if (thisStart < 0 || contents->length - thisStart < length) {
return false;
}
if (length <= 0) {
return true;
}
int o1 = contents->offset + thisStart;
int o2 = string.contents->offset + start;
for (int i = 0; i < length; ++i) {
if (contents->value[o1 + i] != string.contents->value[o2 + i]) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool String::regionMatches(bool ignoreCase, int thisStart, const String& string, int start, int length) const {
if (!ignoreCase) {
return regionMatches(thisStart, string, start, length);
}
if (thisStart < 0 || length > contents->length - thisStart) {
return false;
}
if (start < 0 || length > string.contents->length - start) {
return false;
}
thisStart += contents->offset;
start += string.contents->offset;
int end = thisStart + length;
char c1, c2;
while (thisStart < end) {
if ((c1 = contents->value[thisStart++]) != (c2 = string.contents->value[start++])) {
// If we add support for multibyte strings we need to check both cases
// toUpperCase and toLowerCase because of Unicode.
if (Character::toUpperCase(c1) != Character::toUpperCase(c2)) {
return false;
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
String String::replace(char oldChar, char newChar) const {
int index = indexOf(oldChar, 0);
if (index == -1) {
return *this;
}
Contents buffer(contents->length);
System::arraycopy(contents->value.get(), contents->offset, buffer.value.get(), 0, contents->length);
do {
buffer.value[index++] = newChar;
} while ((index = indexOf(oldChar, index)) != -1);
return String(&buffer);
}
////////////////////////////////////////////////////////////////////////////////
bool String::startsWith(const String& prefix) const {
return regionMatches(0, prefix, 0, prefix.contents->length);
}
////////////////////////////////////////////////////////////////////////////////
bool String::startsWith(const String& prefix, int start) const {
return regionMatches(start, prefix, 0, prefix.contents->length);
}
////////////////////////////////////////////////////////////////////////////////
String String::substring(int start) const {
if (start == 0) {
return *this;
}
if (0 <= start && start <= contents->length) {
return String(contents->offset + start, contents->length - start, contents->value);
}
throw StringIndexOutOfBoundsException(__FILE__, __LINE__, start);
}
////////////////////////////////////////////////////////////////////////////////
String String::substring(int start, int end) const {
if (start == 0 && end == contents->length) {
return *this;
}
if (start < 0) {
throw StringIndexOutOfBoundsException(__FILE__, __LINE__, start);
} else if (start > end) {
throw StringIndexOutOfBoundsException(__FILE__, __LINE__, end - start);
} else if (end > contents->length) {
throw StringIndexOutOfBoundsException(__FILE__, __LINE__, end);
}
// NOTE last character not copied!
return String(contents->offset + start, end - start, contents->value);
}
////////////////////////////////////////////////////////////////////////////////
CharSequence* String::subSequence(int start DECAF_UNUSED, int end DECAF_UNUSED) const {
try {
if (start > end) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "Start index is greater than end index.");
}
if (end - start > this->length()) {
throw StringIndexOutOfBoundsException(
__FILE__, __LINE__, "Requested Range is greater than the String length.");
}
return NULL;
}
DECAF_CATCH_RETHROW(IndexOutOfBoundsException)
DECAF_CATCHALL_THROW(IndexOutOfBoundsException)
}
////////////////////////////////////////////////////////////////////////////////
char* String::toCharArray() const {
char* buffer = new char[contents->length];
System::arraycopy((const char*)contents->value.get(), contents->offset, buffer, 0, contents->length);
return buffer;
}
////////////////////////////////////////////////////////////////////////////////
String String::toLowerCase() const {
Contents newContents(contents->length);
int offset = contents->offset;
for (int i = 0; i < contents->length; ++i) {
newContents.value[i] = Character::toLowerCase(contents->value[offset + i]);
}
return String(&newContents);
}
////////////////////////////////////////////////////////////////////////////////
String String::toUpperCase() const {
Contents newContents(contents->length);
int offset = contents->offset;
for (int i = 0; i < contents->length; ++i) {
newContents.value[i] = Character::toUpperCase(contents->value[offset + i]);
}
return String(&newContents);
}
////////////////////////////////////////////////////////////////////////////////
std::string String::toString() const {
if (this->contents == NULL) {
return "null";
}
if (this->contents->length == 0) {
return "";
}
return std::string((const char*) contents->value.get() + contents->offset, this->length());
}
////////////////////////////////////////////////////////////////////////////////
String String::trim() const {
int start = contents->offset;
int last = contents->offset + contents->length - 1;
int end = last;
while ((start <= end) && (contents->value[start] <= 0x20)) {
start++;
}
while ((end >= start) && (contents->value[end] <= 0x20)) {
end--;
}
if (start == contents->offset && end == last) {
return *this;
}
return String(start, end - start + 1, contents->value);
}
////////////////////////////////////////////////////////////////////////////////
String String::valueOf(bool value) {
if (value) {
return String("true");
}
return String("false");
}
////////////////////////////////////////////////////////////////////////////////
String String::valueOf(char value) {
return String(&value, 1);
}
////////////////////////////////////////////////////////////////////////////////
String String::valueOf(float value) {
return String(Float::toString(value));
}
////////////////////////////////////////////////////////////////////////////////
String String::valueOf(double value) {
return String(Double::toString(value));
}
////////////////////////////////////////////////////////////////////////////////
String String::valueOf(short value) {
return String(Short::toString(value));
}
////////////////////////////////////////////////////////////////////////////////
String String::valueOf(int value) {
return String(Integer::toString(value));
}
////////////////////////////////////////////////////////////////////////////////
String String::valueOf(long long value) {
return String(Long::toString(value));
}
////////////////////////////////////////////////////////////////////////////////
namespace decaf {
namespace lang {
std::ostream& operator<<(std::ostream &out, const String& target) {
if (target.isEmpty()) {
out << "";
}
for (int i = 0; i < target.length(); ++i) {
out << target.charAt(i);
}
return out;
}
bool operator==(const std::string& left, const String& right) {
return right.equals(left);
}
bool operator==(const char* left, const String& right) {
return right.equals(left);
}
bool operator!=(const std::string& left, const String& right) {
return !right.equals(left);
}
bool operator!=(const char* left, const String& right) {
return !right.equals(left);
}
bool operator< (const std::string& left, const String& right) {
return right.compareTo(left) > 0;
}
bool operator< (const char* left, const String& right) {
return right.compareTo(left) > 0;
}
bool operator<=(const std::string& left, const String& right) {
return right.compareTo(left) >= 0;
}
bool operator<=(const char* left, const String& right) {
return right.compareTo(left) >= 0;
}
bool operator> (const std::string& left, const String& right) {
return right.compareTo(left) < 0;
}
bool operator> (const char* left, const String& right) {
return right.compareTo(left) < 0;
}
bool operator>=(const std::string& left, const String& right) {
return right.compareTo(left) <= 0;
}
bool operator>=(const char* left, const String& right) {
return right.compareTo(left) <= 0;
}
}}