001: /*
002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.es;
030:
031: import com.caucho.util.CharBuffer;
032: import com.caucho.util.IntMap;
033:
034: import java.text.CharacterIterator;
035:
036: /**
037: * Implementation class for JavaScript strings.
038: */
039: public class ESString extends ESBase {
040: static ESId NULL;
041: static ESId LENGTH;
042: private static ESId ints[];
043:
044: protected String string;
045: protected int hashCode;
046:
047: /**
048: * Create a new object based on a prototype
049: */
050: protected ESString(String string) {
051: if (ints == null) {
052: ints = new ESId[128];
053: NULL = ESId.intern("");
054: LENGTH = ESId.intern("length");
055:
056: for (int i = 0; i < ints.length; i++)
057: ints[i] = ESId.intern(String.valueOf(i));
058: }
059:
060: prototype = esNull;
061: this .string = string;
062: }
063:
064: public static ESString create(String string) {
065: return string == null ? NULL : new ESString(string);
066: }
067:
068: public static ESBase toStr(String string) {
069: return string == null ? esNull : new ESString(string);
070: }
071:
072: public static ESString create(int i) {
073: if (i >= 0 && i < ints.length)
074: return ints[i];
075:
076: return new ESString(String.valueOf(i));
077: }
078:
079: public static ESString createFromCharCode(char c) {
080: return new ESString(String.valueOf((char) c));
081: }
082:
083: public static ESString create(CharBuffer cb) {
084: return new ESString(cb.toString());
085: }
086:
087: /**
088: * Create a new string from a java object.
089: */
090: public static ESString create(Object obj) {
091: return new ESString(String.valueOf(obj));
092: }
093:
094: public ESBase typeof() throws ESException {
095: return ESString.create("string");
096: }
097:
098: public Class getJavaType() {
099: return String.class;
100: }
101:
102: public double toNum() {
103: return parseFloat(this , true);
104: }
105:
106: public ESString toStr() {
107: return this ;
108: }
109:
110: public boolean isString() {
111: return true;
112: }
113:
114: public ESString toSource(IntMap map, boolean isLoopPass) {
115: if (isLoopPass)
116: return null;
117:
118: return ESString.create("\"" + string + "\"");
119: }
120:
121: public ESObject toObject() {
122: ESObject obj = new ESWrapper("String",
123: Global.getGlobalProto().stringProto, this );
124:
125: obj.put(LENGTH, ESNumber.create(this .string.length()),
126: DONT_ENUM | DONT_DELETE | READ_ONLY);
127:
128: return obj;
129: }
130:
131: public Object toJavaObject() {
132: return string;
133: }
134:
135: public ESBase getProperty(ESString key) throws Throwable {
136: if (key.equals(LENGTH))
137: return ESNumber.create(this .string.length());
138: else
139: return Global.getGlobalProto().stringProto.getProperty(key);
140: }
141:
142: public boolean ecmaEquals(ESBase b) throws Throwable {
143: if (b == esNull)
144: return false;
145: else if (b instanceof ESString)
146: return string.equals(((ESString) b).string);
147: else if (b instanceof ESObject) {
148: ESBase pb = b.toPrimitive(NONE);
149: if (pb instanceof ESString)
150: return equals(pb);
151: else
152: return toNum() == pb.toNum();
153: } else {
154: return toNum() == b.toNum();
155: }
156: }
157:
158: public ESBase plus(ESBase b) throws Throwable {
159: ESBase prim = b.toPrimitive(NONE);
160:
161: return ESString.create(string + prim.toStr().toString());
162: }
163:
164: char charAt(int i) {
165: return string.charAt(i);
166: }
167:
168: public char carefulCharAt(int i) {
169: if (i >= string.length())
170: return CharacterIterator.DONE;
171: else
172: return string.charAt(i);
173: }
174:
175: int length() {
176: return string.length();
177: }
178:
179: boolean regionMatches(int i, String test, int j, int len) {
180: return string.regionMatches(i, test, j, len);
181: }
182:
183: public int hashCode() {
184: int hash = hashCode;
185:
186: if (hash != 0)
187: return hash;
188:
189: hash = 1021;
190: int len = string.length();
191:
192: for (int i = 0; i < len; i++)
193: hash = 65521 * hash + (string.charAt(i) + 1) * 251;
194:
195: hashCode = hash;
196:
197: return hash;
198: }
199:
200: public boolean equals(Object a) {
201: if (this == a)
202: return true;
203: else if (a instanceof ESString)
204: return string.equals(((ESString) a).string);
205: else
206: return false;
207: }
208:
209: int compareTo(ESString b) {
210: return string.compareTo(b.string);
211: }
212:
213: int indexOf(ESString a, int pos) {
214: return string.indexOf(a.string, pos);
215: }
216:
217: int lastIndexOf(ESString a, int pos) {
218: return string.lastIndexOf(a.string, pos);
219: }
220:
221: ESString substring(int begin, int end) {
222: return ESString.create(string.substring(begin, end));
223: }
224:
225: ESString substring(int begin) {
226: return substring(begin, string.length());
227: }
228:
229: ESString toLowerCase() {
230: return ESString.create(string.toLowerCase());
231: }
232:
233: ESString toUpperCase() {
234: return ESString.create(string.toUpperCase());
235: }
236:
237: ESBoolean contains(ESBase b) throws Throwable {
238: ESString sb = b.toStr();
239: if (sb.string.length() == 0)
240: return ESBoolean.TRUE;
241:
242: int len = string.length();
243: for (int i = 0; i <= len; i++) {
244: if (string.regionMatches(i, sb.string, 0, sb.length()))
245: return ESBoolean.TRUE;
246: }
247:
248: return ESBoolean.FALSE;
249: }
250:
251: ESBoolean startsWith(ESBase b) throws Throwable {
252: ESString sb = b.toStr();
253:
254: return ESBoolean.create(string.startsWith(sb.string));
255: }
256:
257: ESBoolean endsWith(ESBase b) throws Throwable {
258: ESString sb = b.toStr();
259:
260: return ESBoolean.create(string.endsWith(sb.string));
261: }
262:
263: public boolean toBoolean() {
264: return string.length() != 0;
265: }
266:
267: static boolean isWhitespace(int ch) {
268: switch (ch) {
269: case ' ':
270: case '\t':
271: case '\n':
272: case '\r':
273: case 0x0b:
274: case '\f':
275: return true;
276:
277: default:
278: return false;
279: }
280: }
281:
282: static double checkTail(double value, ESString string, int i) {
283: for (; i < string.length(); i++) {
284: if (!isWhitespace(string.charAt(i))) {
285: return 0.0 / 0.0;
286: }
287: }
288:
289: return value;
290: }
291:
292: static double parseFloat(ESString string, boolean hexOkay) {
293: int len = string.length();
294:
295: int i = 0;
296: int ch = 0;
297: for (; i < len && isWhitespace(string.charAt(i)); i++) {
298: }
299:
300: if (i >= len)
301: return hexOkay ? 0 : 0.0 / 0.0;
302:
303: int radix = 10;
304: if (hexOkay && i + 1 < len && (ch = string.charAt(i)) == '0'
305: && ((ch = string.charAt(i + 1)) == 'x' || ch == 'X')) {
306: i += 2;
307: radix = 16;
308: }
309:
310: int sign = 1;
311: if (radix == 10 && i < len && (ch = string.charAt(i)) == '+') {
312: i++;
313: } else if (radix == 10 && i < len && ch == '-') {
314: sign = -1;
315: i++;
316: }
317:
318: if (radix == 10 && string.regionMatches(i, "Infinity", 0, 8)) {
319: if (hexOkay)
320: return checkTail(sign * (1.0 / 0.0), string, i + 8);
321: else
322: return sign * (1.0 / 0.0);
323: }
324: double value = 0.0;
325: boolean hasDigit = false;
326: for (; i < len; i++) {
327: ch = string.charAt(i);
328:
329: if ('0' <= ch && ch <= '9') {
330: value = radix * value + string.charAt(i) - '0';
331: hasDigit = true;
332: } else if (radix > 10
333: && ('a' <= ch && ch <= 'a' + radix - 11)) {
334: value = radix * value + string.charAt(i) - 'a' + 10;
335: hasDigit = true;
336: } else if (radix > 10
337: && ('A' <= ch && ch <= 'A' + radix - 11)) {
338: value = radix * value + string.charAt(i) - 'A' + 10;
339: hasDigit = true;
340: } else
341: break;
342: }
343:
344: if (radix == 16 && !hasDigit)
345: return 0.0 / 0.0;
346: else if (radix == 16)
347: return checkTail(value, string, i);
348:
349: int expt = 0;
350: if (i < len && string.charAt(i) == '.') {
351: i++;
352:
353: int power = 1;
354: for (; i < len && (ch = string.charAt(i)) >= '0'
355: && ch <= '9'; i++) {
356: if (ch == '0')
357: power++;
358: else {
359: value = value * Math.pow(10, power) + ch - '0';
360: expt -= power;
361: power = 1;
362: }
363: hasDigit = true;
364: }
365: }
366:
367: if (!hasDigit)
368: return 0.0 / 0.0;
369:
370: if (i < len && ((ch = string.charAt(i)) == 'e' || ch == 'E')) {
371: i++;
372: int exptSign = 1;
373: if (i < len && (ch = string.charAt(i)) == '+')
374: i++;
375: else if (i < len && ch == '-') {
376: exptSign = -1;
377: i++;
378: }
379:
380: hasDigit = false;
381:
382: int newExpt = 0;
383: for (; i < len && (ch = string.charAt(i)) >= '0'
384: && ch <= '9'; i++) {
385: newExpt = 10 * newExpt + ch - '0';
386: hasDigit = true;
387: }
388:
389: if (!hasDigit)
390: return 0.0 / 0.0;
391:
392: expt += exptSign * newExpt;
393: }
394:
395: if (expt < 0)
396: value = sign * value / Math.pow(10.0, -expt);
397: else
398: value = sign * value * Math.pow(10.0, expt);
399:
400: if (hexOkay)
401: return checkTail(value, string, i);
402: else
403: return value;
404: }
405:
406: static double parseFloat(ESString string) {
407: return parseFloat(string, false);
408: }
409:
410: /**
411: * Returns this as a string.
412: */
413: public String toString() {
414: return string;
415: }
416: }
|