0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Tom Beauvais
0026: * Norris Boyd
0027: * Mike McCabe
0028: *
0029: * Alternatively, the contents of this file may be used under the terms of
0030: * the GNU General Public License Version 2 or later (the "GPL"), in which
0031: * case the provisions of the GPL are applicable instead of those above. If
0032: * you wish to allow use of your version of this file only under the terms of
0033: * the GPL and not to allow others to use your version of this file under the
0034: * MPL, indicate your decision by deleting the provisions above and replacing
0035: * them with the notice and other provisions required by the GPL. If you do
0036: * not delete the provisions above, a recipient may use your version of this
0037: * file under either the MPL or the GPL.
0038: *
0039: * ***** END LICENSE BLOCK ***** */
0040:
0041: package org.mozilla.javascript;
0042:
0043: import java.text.Collator;
0044:
0045: /**
0046: * This class implements the String native object.
0047: *
0048: * See ECMA 15.5.
0049: *
0050: * String methods for dealing with regular expressions are
0051: * ported directly from C. Latest port is from version 1.40.12.19
0052: * in the JSFUN13_BRANCH.
0053: *
0054: * @author Mike McCabe
0055: * @author Norris Boyd
0056: */
0057: final class NativeString extends IdScriptableObject {
0058: static final long serialVersionUID = 920268368584188687L;
0059:
0060: private static final Object STRING_TAG = new Object();
0061:
0062: static void init(Scriptable scope, boolean sealed) {
0063: NativeString obj = new NativeString("");
0064: obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
0065: }
0066:
0067: private NativeString(String s) {
0068: string = s;
0069: }
0070:
0071: public String getClassName() {
0072: return "String";
0073: }
0074:
0075: private static final int Id_length = 1, MAX_INSTANCE_ID = 1;
0076:
0077: protected int getMaxInstanceId() {
0078: return MAX_INSTANCE_ID;
0079: }
0080:
0081: protected int findInstanceIdInfo(String s) {
0082: if (s.equals("length")) {
0083: return instanceIdInfo(DONTENUM | READONLY | PERMANENT,
0084: Id_length);
0085: }
0086: return super .findInstanceIdInfo(s);
0087: }
0088:
0089: protected String getInstanceIdName(int id) {
0090: if (id == Id_length) {
0091: return "length";
0092: }
0093: return super .getInstanceIdName(id);
0094: }
0095:
0096: protected Object getInstanceIdValue(int id) {
0097: if (id == Id_length) {
0098: return ScriptRuntime.wrapInt(string.length());
0099: }
0100: return super .getInstanceIdValue(id);
0101: }
0102:
0103: protected void fillConstructorProperties(IdFunctionObject ctor) {
0104: addIdFunctionProperty(ctor, STRING_TAG,
0105: ConstructorId_fromCharCode, "fromCharCode", 1);
0106: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_charAt,
0107: "charAt", 2);
0108: addIdFunctionProperty(ctor, STRING_TAG,
0109: ConstructorId_charCodeAt, "charCodeAt", 2);
0110: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_indexOf,
0111: "indexOf", 2);
0112: addIdFunctionProperty(ctor, STRING_TAG,
0113: ConstructorId_lastIndexOf, "lastIndexOf", 2);
0114: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_split,
0115: "split", 3);
0116: addIdFunctionProperty(ctor, STRING_TAG,
0117: ConstructorId_substring, "substring", 3);
0118: addIdFunctionProperty(ctor, STRING_TAG,
0119: ConstructorId_toLowerCase, "toLowerCase", 1);
0120: addIdFunctionProperty(ctor, STRING_TAG,
0121: ConstructorId_toUpperCase, "toUpperCase", 1);
0122: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_substr,
0123: "substr", 3);
0124: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_concat,
0125: "concat", 2);
0126: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_slice,
0127: "slice", 3);
0128: addIdFunctionProperty(ctor, STRING_TAG,
0129: ConstructorId_equalsIgnoreCase, "equalsIgnoreCase", 2);
0130: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_match,
0131: "match", 2);
0132: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_search,
0133: "search", 2);
0134: addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_replace,
0135: "replace", 2);
0136: addIdFunctionProperty(ctor, STRING_TAG,
0137: ConstructorId_localeCompare, "localeCompare", 2);
0138: addIdFunctionProperty(ctor, STRING_TAG,
0139: ConstructorId_toLocaleLowerCase, "toLocaleLowerCase", 1);
0140: addIdFunctionProperty(ctor, STRING_TAG,
0141: ConstructorId_fromCharCode, "fromCharCode", 1);
0142: super .fillConstructorProperties(ctor);
0143: }
0144:
0145: protected void initPrototypeId(int id) {
0146: String s;
0147: int arity;
0148: switch (id) {
0149: case Id_constructor:
0150: arity = 1;
0151: s = "constructor";
0152: break;
0153: case Id_toString:
0154: arity = 0;
0155: s = "toString";
0156: break;
0157: case Id_toSource:
0158: arity = 0;
0159: s = "toSource";
0160: break;
0161: case Id_valueOf:
0162: arity = 0;
0163: s = "valueOf";
0164: break;
0165: case Id_charAt:
0166: arity = 1;
0167: s = "charAt";
0168: break;
0169: case Id_charCodeAt:
0170: arity = 1;
0171: s = "charCodeAt";
0172: break;
0173: case Id_indexOf:
0174: arity = 1;
0175: s = "indexOf";
0176: break;
0177: case Id_lastIndexOf:
0178: arity = 1;
0179: s = "lastIndexOf";
0180: break;
0181: case Id_split:
0182: arity = 2;
0183: s = "split";
0184: break;
0185: case Id_substring:
0186: arity = 2;
0187: s = "substring";
0188: break;
0189: case Id_toLowerCase:
0190: arity = 0;
0191: s = "toLowerCase";
0192: break;
0193: case Id_toUpperCase:
0194: arity = 0;
0195: s = "toUpperCase";
0196: break;
0197: case Id_substr:
0198: arity = 2;
0199: s = "substr";
0200: break;
0201: case Id_concat:
0202: arity = 1;
0203: s = "concat";
0204: break;
0205: case Id_slice:
0206: arity = 2;
0207: s = "slice";
0208: break;
0209: case Id_bold:
0210: arity = 0;
0211: s = "bold";
0212: break;
0213: case Id_italics:
0214: arity = 0;
0215: s = "italics";
0216: break;
0217: case Id_fixed:
0218: arity = 0;
0219: s = "fixed";
0220: break;
0221: case Id_strike:
0222: arity = 0;
0223: s = "strike";
0224: break;
0225: case Id_small:
0226: arity = 0;
0227: s = "small";
0228: break;
0229: case Id_big:
0230: arity = 0;
0231: s = "big";
0232: break;
0233: case Id_blink:
0234: arity = 0;
0235: s = "blink";
0236: break;
0237: case Id_sup:
0238: arity = 0;
0239: s = "sup";
0240: break;
0241: case Id_sub:
0242: arity = 0;
0243: s = "sub";
0244: break;
0245: case Id_fontsize:
0246: arity = 0;
0247: s = "fontsize";
0248: break;
0249: case Id_fontcolor:
0250: arity = 0;
0251: s = "fontcolor";
0252: break;
0253: case Id_link:
0254: arity = 0;
0255: s = "link";
0256: break;
0257: case Id_anchor:
0258: arity = 0;
0259: s = "anchor";
0260: break;
0261: case Id_equals:
0262: arity = 1;
0263: s = "equals";
0264: break;
0265: case Id_equalsIgnoreCase:
0266: arity = 1;
0267: s = "equalsIgnoreCase";
0268: break;
0269: case Id_match:
0270: arity = 1;
0271: s = "match";
0272: break;
0273: case Id_search:
0274: arity = 1;
0275: s = "search";
0276: break;
0277: case Id_replace:
0278: arity = 1;
0279: s = "replace";
0280: break;
0281: case Id_localeCompare:
0282: arity = 1;
0283: s = "localeCompare";
0284: break;
0285: case Id_toLocaleLowerCase:
0286: arity = 0;
0287: s = "toLocaleLowerCase";
0288: break;
0289: case Id_toLocaleUpperCase:
0290: arity = 0;
0291: s = "toLocaleUpperCase";
0292: break;
0293: default:
0294: throw new IllegalArgumentException(String.valueOf(id));
0295: }
0296: initPrototypeMethod(STRING_TAG, id, s, arity);
0297: }
0298:
0299: public Object execIdCall(IdFunctionObject f, Context cx,
0300: Scriptable scope, Scriptable this Obj, Object[] args) {
0301: if (!f.hasTag(STRING_TAG)) {
0302: return super .execIdCall(f, cx, scope, this Obj, args);
0303: }
0304: int id = f.methodId();
0305: again: for (;;) {
0306: switch (id) {
0307: case ConstructorId_charAt:
0308: case ConstructorId_charCodeAt:
0309: case ConstructorId_indexOf:
0310: case ConstructorId_lastIndexOf:
0311: case ConstructorId_split:
0312: case ConstructorId_substring:
0313: case ConstructorId_toLowerCase:
0314: case ConstructorId_toUpperCase:
0315: case ConstructorId_substr:
0316: case ConstructorId_concat:
0317: case ConstructorId_slice:
0318: case ConstructorId_equalsIgnoreCase:
0319: case ConstructorId_match:
0320: case ConstructorId_search:
0321: case ConstructorId_replace:
0322: case ConstructorId_localeCompare:
0323: case ConstructorId_toLocaleLowerCase: {
0324: this Obj = ScriptRuntime.toObject(scope, ScriptRuntime
0325: .toString(args[0]));
0326: Object[] newArgs = new Object[args.length - 1];
0327: for (int i = 0; i < newArgs.length; i++)
0328: newArgs[i] = args[i + 1];
0329: args = newArgs;
0330: id = -id;
0331: continue again;
0332: }
0333:
0334: case ConstructorId_fromCharCode: {
0335: int N = args.length;
0336: if (N < 1)
0337: return "";
0338: StringBuffer sb = new StringBuffer(N);
0339: for (int i = 0; i != N; ++i) {
0340: sb.append(ScriptRuntime.toUint16(args[i]));
0341: }
0342: return sb.toString();
0343: }
0344:
0345: case Id_constructor: {
0346: String s = (args.length >= 1) ? ScriptRuntime
0347: .toString(args[0]) : "";
0348: if (this Obj == null) {
0349: // new String(val) creates a new String object.
0350: return new NativeString(s);
0351: }
0352: // String(val) converts val to a string value.
0353: return s;
0354: }
0355:
0356: case Id_toString:
0357: case Id_valueOf:
0358: // ECMA 15.5.4.2: 'the toString function is not generic.
0359: return realThis(this Obj, f).string;
0360:
0361: case Id_toSource: {
0362: String s = realThis(this Obj, f).string;
0363: return "(new String(\"" + ScriptRuntime.escapeString(s)
0364: + "\"))";
0365: }
0366:
0367: case Id_charAt:
0368: case Id_charCodeAt: {
0369: // See ECMA 15.5.4.[4,5]
0370: String target = ScriptRuntime.toString(this Obj);
0371: double pos = ScriptRuntime.toInteger(args, 0);
0372: if (pos < 0 || pos >= target.length()) {
0373: if (id == Id_charAt)
0374: return "";
0375: else
0376: return ScriptRuntime.NaNobj;
0377: }
0378: char c = target.charAt((int) pos);
0379: if (id == Id_charAt)
0380: return String.valueOf(c);
0381: else
0382: return ScriptRuntime.wrapInt(c);
0383: }
0384:
0385: case Id_indexOf:
0386: return ScriptRuntime.wrapInt(js_indexOf(ScriptRuntime
0387: .toString(this Obj), args));
0388:
0389: case Id_lastIndexOf:
0390: return ScriptRuntime.wrapInt(js_lastIndexOf(
0391: ScriptRuntime.toString(this Obj), args));
0392:
0393: case Id_split:
0394: return js_split(cx, scope, ScriptRuntime
0395: .toString(this Obj), args);
0396:
0397: case Id_substring:
0398: return js_substring(cx,
0399: ScriptRuntime.toString(this Obj), args);
0400:
0401: case Id_toLowerCase:
0402: // See ECMA 15.5.4.11
0403: return ScriptRuntime.toString(this Obj).toLowerCase();
0404:
0405: case Id_toUpperCase:
0406: // See ECMA 15.5.4.12
0407: return ScriptRuntime.toString(this Obj).toUpperCase();
0408:
0409: case Id_substr:
0410: return js_substr(ScriptRuntime.toString(this Obj), args);
0411:
0412: case Id_concat:
0413: return js_concat(ScriptRuntime.toString(this Obj), args);
0414:
0415: case Id_slice:
0416: return js_slice(ScriptRuntime.toString(this Obj), args);
0417:
0418: case Id_bold:
0419: return tagify(this Obj, "b", null, null);
0420:
0421: case Id_italics:
0422: return tagify(this Obj, "i", null, null);
0423:
0424: case Id_fixed:
0425: return tagify(this Obj, "tt", null, null);
0426:
0427: case Id_strike:
0428: return tagify(this Obj, "strike", null, null);
0429:
0430: case Id_small:
0431: return tagify(this Obj, "small", null, null);
0432:
0433: case Id_big:
0434: return tagify(this Obj, "big", null, null);
0435:
0436: case Id_blink:
0437: return tagify(this Obj, "blink", null, null);
0438:
0439: case Id_sup:
0440: return tagify(this Obj, "sup", null, null);
0441:
0442: case Id_sub:
0443: return tagify(this Obj, "sub", null, null);
0444:
0445: case Id_fontsize:
0446: return tagify(this Obj, "font", "size", args);
0447:
0448: case Id_fontcolor:
0449: return tagify(this Obj, "font", "color", args);
0450:
0451: case Id_link:
0452: return tagify(this Obj, "a", "href", args);
0453:
0454: case Id_anchor:
0455: return tagify(this Obj, "a", "name", args);
0456:
0457: case Id_equals:
0458: case Id_equalsIgnoreCase: {
0459: String s1 = ScriptRuntime.toString(this Obj);
0460: String s2 = ScriptRuntime.toString(args, 0);
0461: return ScriptRuntime.wrapBoolean((id == Id_equals) ? s1
0462: .equals(s2) : s1.equalsIgnoreCase(s2));
0463: }
0464:
0465: case Id_match:
0466: case Id_search:
0467: case Id_replace: {
0468: int actionType;
0469: if (id == Id_match) {
0470: actionType = RegExpProxy.RA_MATCH;
0471: } else if (id == Id_search) {
0472: actionType = RegExpProxy.RA_SEARCH;
0473: } else {
0474: actionType = RegExpProxy.RA_REPLACE;
0475: }
0476: return ScriptRuntime.checkRegExpProxy(cx).action(cx,
0477: scope, this Obj, args, actionType);
0478: }
0479: // ECMA-262 1 5.5.4.9
0480: case Id_localeCompare: {
0481: // For now, create and configure a collator instance. I can't
0482: // actually imagine that this'd be slower than caching them
0483: // a la ClassCache, so we aren't trying to outsmart ourselves
0484: // with a caching mechanism for now.
0485: Collator collator = Collator
0486: .getInstance(cx.getLocale());
0487: collator.setStrength(Collator.IDENTICAL);
0488: collator
0489: .setDecomposition(Collator.CANONICAL_DECOMPOSITION);
0490: return ScriptRuntime.wrapNumber(collator.compare(
0491: ScriptRuntime.toString(this Obj), ScriptRuntime
0492: .toString(args, 0)));
0493: }
0494: case Id_toLocaleLowerCase: {
0495: return ScriptRuntime.toString(this Obj).toLowerCase(
0496: cx.getLocale());
0497: }
0498: case Id_toLocaleUpperCase: {
0499: return ScriptRuntime.toString(this Obj).toUpperCase(
0500: cx.getLocale());
0501: }
0502: }
0503: throw new IllegalArgumentException(String.valueOf(id));
0504: }
0505: }
0506:
0507: private static NativeString realThis(Scriptable this Obj,
0508: IdFunctionObject f) {
0509: if (!(this Obj instanceof NativeString))
0510: throw incompatibleCallError(f);
0511: return (NativeString) this Obj;
0512: }
0513:
0514: /*
0515: * HTML composition aids.
0516: */
0517: private static String tagify(Object this Obj, String tag,
0518: String attribute, Object[] args) {
0519: String str = ScriptRuntime.toString(this Obj);
0520: StringBuffer result = new StringBuffer();
0521: result.append('<');
0522: result.append(tag);
0523: if (attribute != null) {
0524: result.append(' ');
0525: result.append(attribute);
0526: result.append("=\"");
0527: result.append(ScriptRuntime.toString(args, 0));
0528: result.append('"');
0529: }
0530: result.append('>');
0531: result.append(str);
0532: result.append("</");
0533: result.append(tag);
0534: result.append('>');
0535: return result.toString();
0536: }
0537:
0538: public String toString() {
0539: return string;
0540: }
0541:
0542: /* Make array-style property lookup work for strings.
0543: * XXX is this ECMA? A version check is probably needed. In js too.
0544: */
0545: public Object get(int index, Scriptable start) {
0546: if (0 <= index && index < string.length()) {
0547: return string.substring(index, index + 1);
0548: }
0549: return super .get(index, start);
0550: }
0551:
0552: public void put(int index, Scriptable start, Object value) {
0553: if (0 <= index && index < string.length()) {
0554: return;
0555: }
0556: super .put(index, start, value);
0557: }
0558:
0559: /*
0560: *
0561: * See ECMA 15.5.4.6. Uses Java String.indexOf()
0562: * OPT to add - BMH searching from jsstr.c.
0563: */
0564: private static int js_indexOf(String target, Object[] args) {
0565: String search = ScriptRuntime.toString(args, 0);
0566: double begin = ScriptRuntime.toInteger(args, 1);
0567:
0568: if (begin > target.length()) {
0569: return -1;
0570: } else {
0571: if (begin < 0)
0572: begin = 0;
0573: return target.indexOf(search, (int) begin);
0574: }
0575: }
0576:
0577: /*
0578: *
0579: * See ECMA 15.5.4.7
0580: *
0581: */
0582: private static int js_lastIndexOf(String target, Object[] args) {
0583: String search = ScriptRuntime.toString(args, 0);
0584: double end = ScriptRuntime.toNumber(args, 1);
0585:
0586: if (end != end || end > target.length())
0587: end = target.length();
0588: else if (end < 0)
0589: end = 0;
0590:
0591: return target.lastIndexOf(search, (int) end);
0592: }
0593:
0594: /*
0595: * Used by js_split to find the next split point in target,
0596: * starting at offset ip and looking either for the given
0597: * separator substring, or for the next re match. ip and
0598: * matchlen must be reference variables (assumed to be arrays of
0599: * length 1) so they can be updated in the leading whitespace or
0600: * re case.
0601: *
0602: * Return -1 on end of string, >= 0 for a valid index of the next
0603: * separator occurrence if found, or the string length if no
0604: * separator is found.
0605: */
0606: private static int find_split(Context cx, Scriptable scope,
0607: String target, String separator, int version,
0608: RegExpProxy reProxy, Scriptable re, int[] ip,
0609: int[] matchlen, boolean[] matched, String[][] parensp) {
0610: int i = ip[0];
0611: int length = target.length();
0612:
0613: /*
0614: * Perl4 special case for str.split(' '), only if the user has selected
0615: * JavaScript1.2 explicitly. Split on whitespace, and skip leading w/s.
0616: * Strange but true, apparently modeled after awk.
0617: */
0618: if (version == Context.VERSION_1_2 && re == null
0619: && separator.length() == 1
0620: && separator.charAt(0) == ' ') {
0621: /* Skip leading whitespace if at front of str. */
0622: if (i == 0) {
0623: while (i < length
0624: && Character.isWhitespace(target.charAt(i)))
0625: i++;
0626: ip[0] = i;
0627: }
0628:
0629: /* Don't delimit whitespace at end of string. */
0630: if (i == length)
0631: return -1;
0632:
0633: /* Skip over the non-whitespace chars. */
0634: while (i < length
0635: && !Character.isWhitespace(target.charAt(i)))
0636: i++;
0637:
0638: /* Now skip the next run of whitespace. */
0639: int j = i;
0640: while (j < length
0641: && Character.isWhitespace(target.charAt(j)))
0642: j++;
0643:
0644: /* Update matchlen to count delimiter chars. */
0645: matchlen[0] = j - i;
0646: return i;
0647: }
0648:
0649: /*
0650: * Stop if past end of string. If at end of string, we will
0651: * return target length, so that
0652: *
0653: * "ab,".split(',') => new Array("ab", "")
0654: *
0655: * and the resulting array converts back to the string "ab,"
0656: * for symmetry. NB: This differs from perl, which drops the
0657: * trailing empty substring if the LIMIT argument is omitted.
0658: */
0659: if (i > length)
0660: return -1;
0661:
0662: /*
0663: * Match a regular expression against the separator at or
0664: * above index i. Return -1 at end of string instead of
0665: * trying for a match, so we don't get stuck in a loop.
0666: */
0667: if (re != null) {
0668: return reProxy.find_split(cx, scope, target, separator, re,
0669: ip, matchlen, matched, parensp);
0670: }
0671:
0672: /*
0673: * Deviate from ECMA by never splitting an empty string by any separator
0674: * string into a non-empty array (an array of length 1 that contains the
0675: * empty string).
0676: */
0677: if (version != Context.VERSION_DEFAULT
0678: && version < Context.VERSION_1_3 && length == 0)
0679: return -1;
0680:
0681: /*
0682: * Special case: if sep is the empty string, split str into
0683: * one character substrings. Let our caller worry about
0684: * whether to split once at end of string into an empty
0685: * substring.
0686: *
0687: * For 1.2 compatibility, at the end of the string, we return the length as
0688: * the result, and set the separator length to 1 -- this allows the caller
0689: * to include an additional null string at the end of the substring list.
0690: */
0691: if (separator.length() == 0) {
0692: if (version == Context.VERSION_1_2) {
0693: if (i == length) {
0694: matchlen[0] = 1;
0695: return i;
0696: }
0697: return i + 1;
0698: }
0699: return (i == length) ? -1 : i + 1;
0700: }
0701:
0702: /* Punt to j.l.s.indexOf; return target length if separator is
0703: * not found.
0704: */
0705: if (ip[0] >= length)
0706: return length;
0707:
0708: i = target.indexOf(separator, ip[0]);
0709:
0710: return (i != -1) ? i : length;
0711: }
0712:
0713: /*
0714: * See ECMA 15.5.4.8. Modified to match JS 1.2 - optionally takes
0715: * a limit argument and accepts a regular expression as the split
0716: * argument.
0717: */
0718: private static Object js_split(Context cx, Scriptable scope,
0719: String target, Object[] args) {
0720: // create an empty Array to return;
0721: Scriptable top = getTopLevelScope(scope);
0722: Scriptable result = ScriptRuntime.newObject(cx, top, "Array",
0723: null);
0724:
0725: // return an array consisting of the target if no separator given
0726: // don't check against undefined, because we want
0727: // 'fooundefinedbar'.split(void 0) to split to ['foo', 'bar']
0728: if (args.length < 1) {
0729: result.put(0, result, target);
0730: return result;
0731: }
0732:
0733: // Use the second argument as the split limit, if given.
0734: boolean limited = (args.length > 1)
0735: && (args[1] != Undefined.instance);
0736: long limit = 0; // Initialize to avoid warning.
0737: if (limited) {
0738: /* Clamp limit between 0 and 1 + string length. */
0739: limit = ScriptRuntime.toUint32(args[1]);
0740: if (limit > target.length())
0741: limit = 1 + target.length();
0742: }
0743:
0744: String separator = null;
0745: int[] matchlen = new int[1];
0746: Scriptable re = null;
0747: RegExpProxy reProxy = null;
0748: if (args[0] instanceof Scriptable) {
0749: reProxy = ScriptRuntime.getRegExpProxy(cx);
0750: if (reProxy != null) {
0751: Scriptable test = (Scriptable) args[0];
0752: if (reProxy.isRegExp(test)) {
0753: re = test;
0754: }
0755: }
0756: }
0757: if (re == null) {
0758: separator = ScriptRuntime.toString(args[0]);
0759: matchlen[0] = separator.length();
0760: }
0761:
0762: // split target with separator or re
0763: int[] ip = { 0 };
0764: int match;
0765: int len = 0;
0766: boolean[] matched = { false };
0767: String[][] parens = { null };
0768: int version = cx.getLanguageVersion();
0769: while ((match = find_split(cx, scope, target, separator,
0770: version, reProxy, re, ip, matchlen, matched, parens)) >= 0) {
0771: if ((limited && len >= limit) || (match > target.length()))
0772: break;
0773:
0774: String substr;
0775: if (target.length() == 0)
0776: substr = target;
0777: else
0778: substr = target.substring(ip[0], match);
0779:
0780: result.put(len, result, substr);
0781: len++;
0782: /*
0783: * Imitate perl's feature of including parenthesized substrings
0784: * that matched part of the delimiter in the new array, after the
0785: * split substring that was delimited.
0786: */
0787: if (re != null && matched[0] == true) {
0788: int size = parens[0].length;
0789: for (int num = 0; num < size; num++) {
0790: if (limited && len >= limit)
0791: break;
0792: result.put(len, result, parens[0][num]);
0793: len++;
0794: }
0795: matched[0] = false;
0796: }
0797: ip[0] = match + matchlen[0];
0798:
0799: if (version < Context.VERSION_1_3
0800: && version != Context.VERSION_DEFAULT) {
0801: /*
0802: * Deviate from ECMA to imitate Perl, which omits a final
0803: * split unless a limit argument is given and big enough.
0804: */
0805: if (!limited && ip[0] == target.length())
0806: break;
0807: }
0808: }
0809: return result;
0810: }
0811:
0812: /*
0813: * See ECMA 15.5.4.15
0814: */
0815: private static String js_substring(Context cx, String target,
0816: Object[] args) {
0817: int length = target.length();
0818: double start = ScriptRuntime.toInteger(args, 0);
0819: double end;
0820:
0821: if (start < 0)
0822: start = 0;
0823: else if (start > length)
0824: start = length;
0825:
0826: if (args.length <= 1 || args[1] == Undefined.instance) {
0827: end = length;
0828: } else {
0829: end = ScriptRuntime.toInteger(args[1]);
0830: if (end < 0)
0831: end = 0;
0832: else if (end > length)
0833: end = length;
0834:
0835: // swap if end < start
0836: if (end < start) {
0837: if (cx.getLanguageVersion() != Context.VERSION_1_2) {
0838: double temp = start;
0839: start = end;
0840: end = temp;
0841: } else {
0842: // Emulate old JDK1.0 java.lang.String.substring()
0843: end = start;
0844: }
0845: }
0846: }
0847: return target.substring((int) start, (int) end);
0848: }
0849:
0850: int getLength() {
0851: return string.length();
0852: }
0853:
0854: /*
0855: * Non-ECMA methods.
0856: */
0857: private static String js_substr(String target, Object[] args) {
0858: if (args.length < 1)
0859: return target;
0860:
0861: double begin = ScriptRuntime.toInteger(args[0]);
0862: double end;
0863: int length = target.length();
0864:
0865: if (begin < 0) {
0866: begin += length;
0867: if (begin < 0)
0868: begin = 0;
0869: } else if (begin > length) {
0870: begin = length;
0871: }
0872:
0873: if (args.length == 1) {
0874: end = length;
0875: } else {
0876: end = ScriptRuntime.toInteger(args[1]);
0877: if (end < 0)
0878: end = 0;
0879: end += begin;
0880: if (end > length)
0881: end = length;
0882: }
0883:
0884: return target.substring((int) begin, (int) end);
0885: }
0886:
0887: /*
0888: * Python-esque sequence operations.
0889: */
0890: private static String js_concat(String target, Object[] args) {
0891: int N = args.length;
0892: if (N == 0) {
0893: return target;
0894: } else if (N == 1) {
0895: String arg = ScriptRuntime.toString(args[0]);
0896: return target.concat(arg);
0897: }
0898:
0899: // Find total capacity for the final string to avoid unnecessary
0900: // re-allocations in StringBuffer
0901: int size = target.length();
0902: String[] argsAsStrings = new String[N];
0903: for (int i = 0; i != N; ++i) {
0904: String s = ScriptRuntime.toString(args[i]);
0905: argsAsStrings[i] = s;
0906: size += s.length();
0907: }
0908:
0909: StringBuffer result = new StringBuffer(size);
0910: result.append(target);
0911: for (int i = 0; i != N; ++i) {
0912: result.append(argsAsStrings[i]);
0913: }
0914: return result.toString();
0915: }
0916:
0917: private static String js_slice(String target, Object[] args) {
0918: if (args.length != 0) {
0919: double begin = ScriptRuntime.toInteger(args[0]);
0920: double end;
0921: int length = target.length();
0922: if (begin < 0) {
0923: begin += length;
0924: if (begin < 0)
0925: begin = 0;
0926: } else if (begin > length) {
0927: begin = length;
0928: }
0929:
0930: if (args.length == 1) {
0931: end = length;
0932: } else {
0933: end = ScriptRuntime.toInteger(args[1]);
0934: if (end < 0) {
0935: end += length;
0936: if (end < 0)
0937: end = 0;
0938: } else if (end > length) {
0939: end = length;
0940: }
0941: if (end < begin)
0942: end = begin;
0943: }
0944: return target.substring((int) begin, (int) end);
0945: }
0946: return target;
0947: }
0948:
0949: // #string_id_map#
0950:
0951: protected int findPrototypeId(String s) {
0952: int id;
0953: // #generated# Last update: 2007-05-01 22:11:49 EDT
0954: L0: {
0955: id = 0;
0956: String X = null;
0957: int c;
0958: L: switch (s.length()) {
0959: case 3:
0960: c = s.charAt(2);
0961: if (c == 'b') {
0962: if (s.charAt(0) == 's' && s.charAt(1) == 'u') {
0963: id = Id_sub;
0964: break L0;
0965: }
0966: } else if (c == 'g') {
0967: if (s.charAt(0) == 'b' && s.charAt(1) == 'i') {
0968: id = Id_big;
0969: break L0;
0970: }
0971: } else if (c == 'p') {
0972: if (s.charAt(0) == 's' && s.charAt(1) == 'u') {
0973: id = Id_sup;
0974: break L0;
0975: }
0976: }
0977: break L;
0978: case 4:
0979: c = s.charAt(0);
0980: if (c == 'b') {
0981: X = "bold";
0982: id = Id_bold;
0983: } else if (c == 'l') {
0984: X = "link";
0985: id = Id_link;
0986: }
0987: break L;
0988: case 5:
0989: switch (s.charAt(4)) {
0990: case 'd':
0991: X = "fixed";
0992: id = Id_fixed;
0993: break L;
0994: case 'e':
0995: X = "slice";
0996: id = Id_slice;
0997: break L;
0998: case 'h':
0999: X = "match";
1000: id = Id_match;
1001: break L;
1002: case 'k':
1003: X = "blink";
1004: id = Id_blink;
1005: break L;
1006: case 'l':
1007: X = "small";
1008: id = Id_small;
1009: break L;
1010: case 't':
1011: X = "split";
1012: id = Id_split;
1013: break L;
1014: }
1015: break L;
1016: case 6:
1017: switch (s.charAt(1)) {
1018: case 'e':
1019: X = "search";
1020: id = Id_search;
1021: break L;
1022: case 'h':
1023: X = "charAt";
1024: id = Id_charAt;
1025: break L;
1026: case 'n':
1027: X = "anchor";
1028: id = Id_anchor;
1029: break L;
1030: case 'o':
1031: X = "concat";
1032: id = Id_concat;
1033: break L;
1034: case 'q':
1035: X = "equals";
1036: id = Id_equals;
1037: break L;
1038: case 't':
1039: X = "strike";
1040: id = Id_strike;
1041: break L;
1042: case 'u':
1043: X = "substr";
1044: id = Id_substr;
1045: break L;
1046: }
1047: break L;
1048: case 7:
1049: switch (s.charAt(1)) {
1050: case 'a':
1051: X = "valueOf";
1052: id = Id_valueOf;
1053: break L;
1054: case 'e':
1055: X = "replace";
1056: id = Id_replace;
1057: break L;
1058: case 'n':
1059: X = "indexOf";
1060: id = Id_indexOf;
1061: break L;
1062: case 't':
1063: X = "italics";
1064: id = Id_italics;
1065: break L;
1066: }
1067: break L;
1068: case 8:
1069: c = s.charAt(4);
1070: if (c == 'r') {
1071: X = "toString";
1072: id = Id_toString;
1073: } else if (c == 's') {
1074: X = "fontsize";
1075: id = Id_fontsize;
1076: } else if (c == 'u') {
1077: X = "toSource";
1078: id = Id_toSource;
1079: }
1080: break L;
1081: case 9:
1082: c = s.charAt(0);
1083: if (c == 'f') {
1084: X = "fontcolor";
1085: id = Id_fontcolor;
1086: } else if (c == 's') {
1087: X = "substring";
1088: id = Id_substring;
1089: }
1090: break L;
1091: case 10:
1092: X = "charCodeAt";
1093: id = Id_charCodeAt;
1094: break L;
1095: case 11:
1096: switch (s.charAt(2)) {
1097: case 'L':
1098: X = "toLowerCase";
1099: id = Id_toLowerCase;
1100: break L;
1101: case 'U':
1102: X = "toUpperCase";
1103: id = Id_toUpperCase;
1104: break L;
1105: case 'n':
1106: X = "constructor";
1107: id = Id_constructor;
1108: break L;
1109: case 's':
1110: X = "lastIndexOf";
1111: id = Id_lastIndexOf;
1112: break L;
1113: }
1114: break L;
1115: case 13:
1116: X = "localeCompare";
1117: id = Id_localeCompare;
1118: break L;
1119: case 16:
1120: X = "equalsIgnoreCase";
1121: id = Id_equalsIgnoreCase;
1122: break L;
1123: case 17:
1124: c = s.charAt(8);
1125: if (c == 'L') {
1126: X = "toLocaleLowerCase";
1127: id = Id_toLocaleLowerCase;
1128: } else if (c == 'U') {
1129: X = "toLocaleUpperCase";
1130: id = Id_toLocaleUpperCase;
1131: }
1132: break L;
1133: }
1134: if (X != null && X != s && !X.equals(s))
1135: id = 0;
1136: break L0;
1137: }
1138: // #/generated#
1139: return id;
1140: }
1141:
1142: private static final int ConstructorId_fromCharCode = -1,
1143:
1144: Id_constructor = 1, Id_toString = 2, Id_toSource = 3,
1145: Id_valueOf = 4, Id_charAt = 5, Id_charCodeAt = 6,
1146: Id_indexOf = 7, Id_lastIndexOf = 8, Id_split = 9,
1147: Id_substring = 10, Id_toLowerCase = 11,
1148: Id_toUpperCase = 12, Id_substr = 13, Id_concat = 14,
1149: Id_slice = 15, Id_bold = 16, Id_italics = 17,
1150: Id_fixed = 18, Id_strike = 19, Id_small = 20, Id_big = 21,
1151: Id_blink = 22, Id_sup = 23, Id_sub = 24, Id_fontsize = 25,
1152: Id_fontcolor = 26, Id_link = 27, Id_anchor = 28,
1153: Id_equals = 29, Id_equalsIgnoreCase = 30, Id_match = 31,
1154: Id_search = 32, Id_replace = 33, Id_localeCompare = 34,
1155: Id_toLocaleLowerCase = 35, Id_toLocaleUpperCase = 36,
1156: MAX_PROTOTYPE_ID = 36;
1157:
1158: // #/string_id_map#
1159:
1160: private static final int ConstructorId_charAt = -Id_charAt,
1161: ConstructorId_charCodeAt = -Id_charCodeAt,
1162: ConstructorId_indexOf = -Id_indexOf,
1163: ConstructorId_lastIndexOf = -Id_lastIndexOf,
1164: ConstructorId_split = -Id_split,
1165: ConstructorId_substring = -Id_substring,
1166: ConstructorId_toLowerCase = -Id_toLowerCase,
1167: ConstructorId_toUpperCase = -Id_toUpperCase,
1168: ConstructorId_substr = -Id_substr,
1169: ConstructorId_concat = -Id_concat,
1170: ConstructorId_slice = -Id_slice,
1171: ConstructorId_equalsIgnoreCase = -Id_equalsIgnoreCase,
1172: ConstructorId_match = -Id_match,
1173: ConstructorId_search = -Id_search,
1174: ConstructorId_replace = -Id_replace,
1175: ConstructorId_localeCompare = -Id_localeCompare,
1176: ConstructorId_toLocaleLowerCase = -Id_toLocaleLowerCase;
1177:
1178: private String string;
1179: }
|