/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.es;
import com.caucho.util.CharBuffer;
import com.caucho.util.IntMap;
import java.text.CharacterIterator;
import java.util.Locale;
/**
* Implementation class for JavaScript strings.
*/
public class ESString extends ESBase {
static ESId NULL;
static ESId LENGTH;
private static ESId ints[];
protected String string;
protected int hashCode;
/**
* Create a new object based on a prototype
*/
protected ESString(String string)
{
if (ints == null) {
ints = new ESId[128];
NULL = ESId.intern("");
LENGTH = ESId.intern("length");
for (int i = 0; i < ints.length; i++)
ints[i] = ESId.intern(String.valueOf(i));
}
prototype = esNull;
this.string = string;
}
public static ESString create(String string)
{
return string == null ? NULL : new ESString(string);
}
public static ESBase toStr(String string)
{
return string == null ? esNull : new ESString(string);
}
public static ESString create(int i)
{
if (i >= 0 && i < ints.length)
return ints[i];
return new ESString(String.valueOf(i));
}
public static ESString createFromCharCode(char c)
{
return new ESString(String.valueOf((char) c));
}
public static ESString create(CharBuffer cb)
{
return new ESString(cb.toString());
}
/**
* Create a new string from a java object.
*/
public static ESString create(Object obj)
{
return new ESString(String.valueOf(obj));
}
public ESBase typeof() throws ESException
{
return ESString.create("string");
}
public Class getJavaType()
{
return String.class;
}
public double toNum()
{
return parseFloat(this, true);
}
public ESString toStr()
{
return this;
}
public boolean isString()
{
return true;
}
public ESString toSource(IntMap map, boolean isLoopPass)
{
if (isLoopPass)
return null;
return ESString.create("\"" + string + "\"");
}
public ESObject toObject()
{
ESObject obj = new ESWrapper("String", Global.getGlobalProto().stringProto, this);
obj.put(LENGTH, ESNumber.create(this.string.length()),
DONT_ENUM|DONT_DELETE|READ_ONLY);
return obj;
}
public Object toJavaObject()
{
return string;
}
public ESBase getProperty(ESString key) throws Throwable
{
if (key.equals(LENGTH))
return ESNumber.create(this.string.length());
else
return Global.getGlobalProto().stringProto.getProperty(key);
}
public boolean ecmaEquals(ESBase b) throws Throwable
{
if (b == esNull)
return false;
else if (b instanceof ESString)
return string.equals(((ESString) b).string);
else if (b instanceof ESObject) {
ESBase pb = b.toPrimitive(NONE);
if (pb instanceof ESString)
return equals(pb);
else
return toNum() == pb.toNum();
} else {
return toNum() == b.toNum();
}
}
public ESBase plus(ESBase b) throws Throwable
{
ESBase prim = b.toPrimitive(NONE);
return ESString.create(string + prim.toStr().toString());
}
char charAt(int i) { return string.charAt(i); }
public char carefulCharAt(int i)
{
if (i >= string.length())
return CharacterIterator.DONE;
else
return string.charAt(i);
}
int length() { return string.length(); }
boolean regionMatches(int i, String test, int j, int len)
{
return string.regionMatches(i, test, j, len);
}
public int hashCode()
{
int hash = hashCode;
if (hash != 0)
return hash;
hash = 1021;
int len = string.length();
for (int i = 0; i < len; i++)
hash = 65521 * hash + (string.charAt(i) + 1) * 251;
hashCode = hash;
return hash;
}
public boolean equals(Object a)
{
if (this == a)
return true;
else if (a instanceof ESString)
return string.equals(((ESString) a).string);
else
return false;
}
int compareTo(ESString b)
{
return string.compareTo(b.string);
}
int indexOf(ESString a, int pos) { return string.indexOf(a.string, pos); }
int lastIndexOf(ESString a, int pos)
{
return string.lastIndexOf(a.string, pos);
}
ESString substring(int begin, int end)
{
return ESString.create(string.substring(begin, end));
}
ESString substring(int begin)
{
return substring(begin, string.length());
}
ESString toLowerCase()
{
return ESString.create(string.toLowerCase(Locale.ENGLISH));
}
ESString toUpperCase()
{
return ESString.create(string.toUpperCase(Locale.ENGLISH));
}
ESBoolean contains(ESBase b) throws Throwable
{
ESString sb = b.toStr();
if (sb.string.length() == 0)
return ESBoolean.TRUE;
int len = string.length();
for (int i = 0; i <= len; i++) {
if (string.regionMatches(i, sb.string, 0, sb.length()))
return ESBoolean.TRUE;
}
return ESBoolean.FALSE;
}
ESBoolean startsWith(ESBase b) throws Throwable
{
ESString sb = b.toStr();
return ESBoolean.create(string.startsWith(sb.string));
}
ESBoolean endsWith(ESBase b) throws Throwable
{
ESString sb = b.toStr();
return ESBoolean.create(string.endsWith(sb.string));
}
public boolean toBoolean()
{
return string.length() != 0;
}
static boolean isWhitespace(int ch)
{
switch (ch) {
case ' ': case '\t': case '\n': case '\r': case 0x0b: case '\f':
return true;
default:
return false;
}
}
static double checkTail(double value, ESString string, int i)
{
for (; i < string.length(); i++) {
if (! isWhitespace(string.charAt(i))) {
return 0.0/0.0;
}
}
return value;
}
static double parseFloat(ESString string, boolean hexOkay)
{
int len = string.length();
int i = 0;
int ch = 0;
for (; i < len && isWhitespace(string.charAt(i)); i++) {
}
if (i >= len)
return hexOkay ? 0 : 0.0/0.0;
int radix = 10;
if (hexOkay && i + 1 < len && (ch = string.charAt(i)) == '0' &&
((ch = string.charAt(i + 1)) == 'x' || ch == 'X')) {
i += 2;
radix = 16;
}
int sign = 1;
if (radix == 10 && i < len && (ch = string.charAt(i)) == '+') {
i++;
} else if (radix == 10 && i < len && ch == '-') {
sign = -1;
i++;
}
if (radix == 10 && string.regionMatches(i, "Infinity", 0, 8)) {
if (hexOkay)
return checkTail(sign * (1.0/0.0), string, i + 8);
else
return sign * (1.0/0.0);
}
double value = 0.0;
boolean hasDigit = false;
for (; i < len; i++) {
ch = string.charAt(i);
if ('0' <= ch && ch <= '9') {
value = radix * value + string.charAt(i) - '0';
hasDigit = true;
} else if (radix > 10 && ('a' <= ch && ch <= 'a' + radix - 11)) {
value = radix * value + string.charAt(i) - 'a' + 10;
hasDigit = true;
} else if (radix > 10 && ('A' <= ch && ch <= 'A' + radix - 11)) {
value = radix * value + string.charAt(i) - 'A' + 10;
hasDigit = true;
} else
break;
}
if (radix == 16 && ! hasDigit)
return 0.0/0.0;
else if (radix == 16)
return checkTail(value, string, i);
int expt = 0;
if (i < len && string.charAt(i) == '.') {
i++;
int power = 1;
for (; i < len && (ch = string.charAt(i)) >= '0' && ch <= '9'; i++) {
if (ch == '0')
power++;
else {
value = value * Math.pow(10, power) + ch - '0';
expt -= power;
power = 1;
}
hasDigit = true;
}
}
if (! hasDigit)
return 0.0/0.0;
if (i < len && ((ch = string.charAt(i)) == 'e' || ch == 'E')) {
i++;
int exptSign = 1;
if (i < len && (ch = string.charAt(i)) == '+')
i++;
else if (i < len && ch == '-') {
exptSign = -1;
i++;
}
hasDigit = false;
int newExpt = 0;
for (; i < len && (ch = string.charAt(i)) >= '0' && ch <= '9'; i++) {
newExpt = 10 * newExpt + ch - '0';
hasDigit = true;
}
if (! hasDigit)
return 0.0/0.0;
expt += exptSign * newExpt;
}
if (expt < 0)
value = sign * value / Math.pow(10.0, -expt);
else
value = sign * value * Math.pow(10.0, expt);
if (hexOkay)
return checkTail(value, string, i);
else
return value;
}
static double parseFloat(ESString string)
{
return parseFloat(string, false);
}
/**
* Returns this as a string.
*/
public String toString()
{
return string;
}
}