~mrooney/etherpad/ubuntu

« back to all changes in this revision

Viewing changes to trunk/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeString.java

  • Committer: Aaron Iba
  • Date: 2009-12-18 07:40:23 UTC
  • Revision ID: hg-v1:a9f8774a2e00cc15b35857471fecea17f649e3c9
initial code push

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * ***** BEGIN LICENSE BLOCK *****
 
4
 * Version: MPL 1.1/GPL 2.0
 
5
 *
 
6
 * The contents of this file are subject to the Mozilla Public License Version
 
7
 * 1.1 (the "License"); you may not use this file except in compliance with
 
8
 * the License. You may obtain a copy of the License at
 
9
 * http://www.mozilla.org/MPL/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Rhino code, released
 
17
 * May 6, 1999.
 
18
 *
 
19
 * The Initial Developer of the Original Code is
 
20
 * Netscape Communications Corporation.
 
21
 * Portions created by the Initial Developer are Copyright (C) 1997-1999
 
22
 * the Initial Developer. All Rights Reserved.
 
23
 *
 
24
 * Contributor(s):
 
25
 *   Tom Beauvais
 
26
 *   Norris Boyd
 
27
 *   Mike McCabe
 
28
 *
 
29
 * Alternatively, the contents of this file may be used under the terms of
 
30
 * the GNU General Public License Version 2 or later (the "GPL"), in which
 
31
 * case the provisions of the GPL are applicable instead of those above. If
 
32
 * you wish to allow use of your version of this file only under the terms of
 
33
 * the GPL and not to allow others to use your version of this file under the
 
34
 * MPL, indicate your decision by deleting the provisions above and replacing
 
35
 * them with the notice and other provisions required by the GPL. If you do
 
36
 * not delete the provisions above, a recipient may use your version of this
 
37
 * file under either the MPL or the GPL.
 
38
 *
 
39
 * ***** END LICENSE BLOCK ***** */
 
40
 
 
41
package org.mozilla.javascript;
 
42
 
 
43
import java.text.Collator;
 
44
 
 
45
/**
 
46
 * This class implements the String native object.
 
47
 *
 
48
 * See ECMA 15.5.
 
49
 *
 
50
 * String methods for dealing with regular expressions are
 
51
 * ported directly from C. Latest port is from version 1.40.12.19
 
52
 * in the JSFUN13_BRANCH.
 
53
 *
 
54
 * @author Mike McCabe
 
55
 * @author Norris Boyd
 
56
 */
 
57
final class NativeString extends IdScriptableObject
 
58
{
 
59
    static final long serialVersionUID = 920268368584188687L;
 
60
 
 
61
    private static final Object STRING_TAG = new Object();
 
62
 
 
63
    static void init(Scriptable scope, boolean sealed)
 
64
    {
 
65
        NativeString obj = new NativeString("");
 
66
        obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
 
67
    }
 
68
 
 
69
    private NativeString(String s) {
 
70
        string = s;
 
71
    }
 
72
 
 
73
    public String getClassName() {
 
74
        return "String";
 
75
    }
 
76
 
 
77
    private static final int
 
78
        Id_length                    =  1,
 
79
        MAX_INSTANCE_ID              =  1;
 
80
 
 
81
    protected int getMaxInstanceId()
 
82
    {
 
83
        return MAX_INSTANCE_ID;
 
84
    }
 
85
 
 
86
    protected int findInstanceIdInfo(String s)
 
87
    {
 
88
        if (s.equals("length")) {
 
89
            return instanceIdInfo(DONTENUM | READONLY | PERMANENT, Id_length);
 
90
        }
 
91
        return super.findInstanceIdInfo(s);
 
92
    }
 
93
 
 
94
    protected String getInstanceIdName(int id)
 
95
    {
 
96
        if (id == Id_length) { return "length"; }
 
97
        return super.getInstanceIdName(id);
 
98
    }
 
99
 
 
100
    protected Object getInstanceIdValue(int id)
 
101
    {
 
102
        if (id == Id_length) {
 
103
            return ScriptRuntime.wrapInt(string.length());
 
104
        }
 
105
        return super.getInstanceIdValue(id);
 
106
    }
 
107
 
 
108
    protected void fillConstructorProperties(IdFunctionObject ctor)
 
109
    {
 
110
        addIdFunctionProperty(ctor, STRING_TAG, ConstructorId_fromCharCode,
 
111
                "fromCharCode", 1);
 
112
        addIdFunctionProperty(ctor, STRING_TAG,
 
113
                ConstructorId_charAt, "charAt", 2);
 
114
        addIdFunctionProperty(ctor, STRING_TAG,
 
115
                ConstructorId_charCodeAt, "charCodeAt", 2);
 
116
        addIdFunctionProperty(ctor, STRING_TAG,
 
117
                ConstructorId_indexOf, "indexOf", 2);
 
118
        addIdFunctionProperty(ctor, STRING_TAG,
 
119
                ConstructorId_lastIndexOf, "lastIndexOf", 2);
 
120
        addIdFunctionProperty(ctor, STRING_TAG,
 
121
                ConstructorId_split, "split", 3);
 
122
        addIdFunctionProperty(ctor, STRING_TAG,
 
123
                ConstructorId_substring, "substring", 3);
 
124
        addIdFunctionProperty(ctor, STRING_TAG,
 
125
                ConstructorId_toLowerCase, "toLowerCase", 1);
 
126
        addIdFunctionProperty(ctor, STRING_TAG,
 
127
                ConstructorId_toUpperCase, "toUpperCase", 1);
 
128
        addIdFunctionProperty(ctor, STRING_TAG,
 
129
                ConstructorId_substr, "substr", 3);
 
130
        addIdFunctionProperty(ctor, STRING_TAG,
 
131
                ConstructorId_concat, "concat", 2);
 
132
        addIdFunctionProperty(ctor, STRING_TAG,
 
133
                ConstructorId_slice, "slice", 3);
 
134
        addIdFunctionProperty(ctor, STRING_TAG,
 
135
                ConstructorId_equalsIgnoreCase, "equalsIgnoreCase", 2);
 
136
        addIdFunctionProperty(ctor, STRING_TAG,
 
137
                ConstructorId_match, "match", 2);
 
138
        addIdFunctionProperty(ctor, STRING_TAG,
 
139
                ConstructorId_search, "search", 2);
 
140
        addIdFunctionProperty(ctor, STRING_TAG,
 
141
                ConstructorId_replace, "replace", 2);
 
142
        addIdFunctionProperty(ctor, STRING_TAG,
 
143
                ConstructorId_localeCompare, "localeCompare", 2);
 
144
        addIdFunctionProperty(ctor, STRING_TAG,
 
145
                ConstructorId_toLocaleLowerCase, "toLocaleLowerCase", 1);
 
146
        addIdFunctionProperty(ctor, STRING_TAG,
 
147
                ConstructorId_fromCharCode, "fromCharCode", 1);
 
148
        super.fillConstructorProperties(ctor);
 
149
    }
 
150
 
 
151
    protected void initPrototypeId(int id)
 
152
    {
 
153
        String s;
 
154
        int arity;
 
155
        switch (id) {
 
156
          case Id_constructor:       arity=1; s="constructor";       break;
 
157
          case Id_toString:          arity=0; s="toString";          break;
 
158
          case Id_toSource:          arity=0; s="toSource";          break;
 
159
          case Id_valueOf:           arity=0; s="valueOf";           break;
 
160
          case Id_charAt:            arity=1; s="charAt";            break;
 
161
          case Id_charCodeAt:        arity=1; s="charCodeAt";        break;
 
162
          case Id_indexOf:           arity=1; s="indexOf";           break;
 
163
          case Id_lastIndexOf:       arity=1; s="lastIndexOf";       break;
 
164
          case Id_split:             arity=2; s="split";             break;
 
165
          case Id_substring:         arity=2; s="substring";         break;
 
166
          case Id_toLowerCase:       arity=0; s="toLowerCase";       break;
 
167
          case Id_toUpperCase:       arity=0; s="toUpperCase";       break;
 
168
          case Id_substr:            arity=2; s="substr";            break;
 
169
          case Id_concat:            arity=1; s="concat";            break;
 
170
          case Id_slice:             arity=2; s="slice";             break;
 
171
          case Id_bold:              arity=0; s="bold";              break;
 
172
          case Id_italics:           arity=0; s="italics";           break;
 
173
          case Id_fixed:             arity=0; s="fixed";             break;
 
174
          case Id_strike:            arity=0; s="strike";            break;
 
175
          case Id_small:             arity=0; s="small";             break;
 
176
          case Id_big:               arity=0; s="big";               break;
 
177
          case Id_blink:             arity=0; s="blink";             break;
 
178
          case Id_sup:               arity=0; s="sup";               break;
 
179
          case Id_sub:               arity=0; s="sub";               break;
 
180
          case Id_fontsize:          arity=0; s="fontsize";          break;
 
181
          case Id_fontcolor:         arity=0; s="fontcolor";         break;
 
182
          case Id_link:              arity=0; s="link";              break;
 
183
          case Id_anchor:            arity=0; s="anchor";            break;
 
184
          case Id_equals:            arity=1; s="equals";            break;
 
185
          case Id_equalsIgnoreCase:  arity=1; s="equalsIgnoreCase";  break;
 
186
          case Id_match:             arity=1; s="match";             break;
 
187
          case Id_search:            arity=1; s="search";            break;
 
188
          case Id_replace:           arity=1; s="replace";           break;
 
189
          case Id_localeCompare:     arity=1; s="localeCompare";     break;
 
190
          case Id_toLocaleLowerCase: arity=0; s="toLocaleLowerCase"; break;
 
191
          case Id_toLocaleUpperCase: arity=0; s="toLocaleUpperCase"; break;
 
192
          default: throw new IllegalArgumentException(String.valueOf(id));
 
193
        }
 
194
        initPrototypeMethod(STRING_TAG, id, s, arity);
 
195
    }
 
196
 
 
197
    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
 
198
                             Scriptable thisObj, Object[] args)
 
199
    {
 
200
        if (!f.hasTag(STRING_TAG)) {
 
201
            return super.execIdCall(f, cx, scope, thisObj, args);
 
202
        }
 
203
        int id = f.methodId();
 
204
      again:
 
205
        for(;;) {
 
206
            switch (id) {
 
207
              case ConstructorId_charAt:
 
208
              case ConstructorId_charCodeAt:
 
209
              case ConstructorId_indexOf:
 
210
              case ConstructorId_lastIndexOf:
 
211
              case ConstructorId_split:
 
212
              case ConstructorId_substring:
 
213
              case ConstructorId_toLowerCase:
 
214
              case ConstructorId_toUpperCase:
 
215
              case ConstructorId_substr:
 
216
              case ConstructorId_concat:
 
217
              case ConstructorId_slice:
 
218
              case ConstructorId_equalsIgnoreCase:
 
219
              case ConstructorId_match:
 
220
              case ConstructorId_search:
 
221
              case ConstructorId_replace:
 
222
              case ConstructorId_localeCompare:
 
223
              case ConstructorId_toLocaleLowerCase: {
 
224
                thisObj = ScriptRuntime.toObject(scope,
 
225
                        ScriptRuntime.toString(args[0]));
 
226
                Object[] newArgs = new Object[args.length-1];
 
227
                for (int i=0; i < newArgs.length; i++)
 
228
                    newArgs[i] = args[i+1];
 
229
                args = newArgs;
 
230
                id = -id;
 
231
                continue again;
 
232
              }
 
233
    
 
234
              case ConstructorId_fromCharCode: {
 
235
                int N = args.length;
 
236
                if (N < 1)
 
237
                    return "";
 
238
                StringBuffer sb = new StringBuffer(N);
 
239
                for (int i = 0; i != N; ++i) {
 
240
                    sb.append(ScriptRuntime.toUint16(args[i]));
 
241
                }
 
242
                return sb.toString();
 
243
              }
 
244
    
 
245
              case Id_constructor: {
 
246
                String s = (args.length >= 1)
 
247
                    ? ScriptRuntime.toString(args[0]) : "";
 
248
                if (thisObj == null) {
 
249
                    // new String(val) creates a new String object.
 
250
                    return new NativeString(s);
 
251
                }
 
252
                // String(val) converts val to a string value.
 
253
                return s;
 
254
              }
 
255
    
 
256
              case Id_toString:
 
257
              case Id_valueOf:
 
258
                // ECMA 15.5.4.2: 'the toString function is not generic.
 
259
                return realThis(thisObj, f).string;
 
260
    
 
261
              case Id_toSource: {
 
262
                String s = realThis(thisObj, f).string;
 
263
                return "(new String(\""+ScriptRuntime.escapeString(s)+"\"))";
 
264
              }
 
265
    
 
266
              case Id_charAt:
 
267
              case Id_charCodeAt: {
 
268
                 // See ECMA 15.5.4.[4,5]
 
269
                String target = ScriptRuntime.toString(thisObj);
 
270
                double pos = ScriptRuntime.toInteger(args, 0);
 
271
                if (pos < 0 || pos >= target.length()) {
 
272
                    if (id == Id_charAt) return "";
 
273
                    else return ScriptRuntime.NaNobj;
 
274
                }
 
275
                char c = target.charAt((int)pos);
 
276
                if (id == Id_charAt) return String.valueOf(c);
 
277
                else return ScriptRuntime.wrapInt(c);
 
278
              }
 
279
    
 
280
              case Id_indexOf:
 
281
                return ScriptRuntime.wrapInt(js_indexOf(
 
282
                    ScriptRuntime.toString(thisObj), args));
 
283
    
 
284
              case Id_lastIndexOf:
 
285
                return ScriptRuntime.wrapInt(js_lastIndexOf(
 
286
                    ScriptRuntime.toString(thisObj), args));
 
287
    
 
288
              case Id_split:
 
289
                return js_split(cx, scope, ScriptRuntime.toString(thisObj),
 
290
                        args);
 
291
    
 
292
              case Id_substring:
 
293
                return js_substring(cx, ScriptRuntime.toString(thisObj), args);
 
294
    
 
295
              case Id_toLowerCase:
 
296
                // See ECMA 15.5.4.11
 
297
                return ScriptRuntime.toString(thisObj).toLowerCase();
 
298
    
 
299
              case Id_toUpperCase:
 
300
                // See ECMA 15.5.4.12
 
301
                return ScriptRuntime.toString(thisObj).toUpperCase();
 
302
    
 
303
              case Id_substr:
 
304
                return js_substr(ScriptRuntime.toString(thisObj), args);
 
305
    
 
306
              case Id_concat:
 
307
                return js_concat(ScriptRuntime.toString(thisObj), args);
 
308
    
 
309
              case Id_slice:
 
310
                return js_slice(ScriptRuntime.toString(thisObj), args);
 
311
    
 
312
              case Id_bold:
 
313
                return tagify(thisObj, "b", null, null);
 
314
    
 
315
              case Id_italics:
 
316
                return tagify(thisObj, "i", null, null);
 
317
    
 
318
              case Id_fixed:
 
319
                return tagify(thisObj, "tt", null, null);
 
320
    
 
321
              case Id_strike:
 
322
                return tagify(thisObj, "strike", null, null);
 
323
    
 
324
              case Id_small:
 
325
                return tagify(thisObj, "small", null, null);
 
326
    
 
327
              case Id_big:
 
328
                return tagify(thisObj, "big", null, null);
 
329
    
 
330
              case Id_blink:
 
331
                return tagify(thisObj, "blink", null, null);
 
332
    
 
333
              case Id_sup:
 
334
                return tagify(thisObj, "sup", null, null);
 
335
    
 
336
              case Id_sub:
 
337
                return tagify(thisObj, "sub", null, null);
 
338
    
 
339
              case Id_fontsize:
 
340
                return tagify(thisObj, "font", "size", args);
 
341
    
 
342
              case Id_fontcolor:
 
343
                return tagify(thisObj, "font", "color", args);
 
344
    
 
345
              case Id_link:
 
346
                return tagify(thisObj, "a", "href", args);
 
347
    
 
348
              case Id_anchor:
 
349
                return tagify(thisObj, "a", "name", args);
 
350
    
 
351
              case Id_equals:
 
352
              case Id_equalsIgnoreCase: {
 
353
                String s1 = ScriptRuntime.toString(thisObj);
 
354
                String s2 = ScriptRuntime.toString(args, 0);
 
355
                return ScriptRuntime.wrapBoolean(
 
356
                    (id == Id_equals) ? s1.equals(s2) 
 
357
                                      : s1.equalsIgnoreCase(s2));
 
358
              }
 
359
                  
 
360
              case Id_match:
 
361
              case Id_search:
 
362
              case Id_replace:
 
363
                {
 
364
                    int actionType;
 
365
                    if (id == Id_match) {
 
366
                        actionType = RegExpProxy.RA_MATCH;
 
367
                    } else if (id == Id_search) {
 
368
                        actionType = RegExpProxy.RA_SEARCH;
 
369
                    } else {
 
370
                        actionType = RegExpProxy.RA_REPLACE;
 
371
                    }
 
372
                    return ScriptRuntime.checkRegExpProxy(cx).
 
373
                        action(cx, scope, thisObj, args, actionType);
 
374
                }
 
375
                // ECMA-262 1 5.5.4.9
 
376
              case Id_localeCompare:
 
377
                {
 
378
                    // For now, create and configure a collator instance. I can't
 
379
                    // actually imagine that this'd be slower than caching them
 
380
                    // a la ClassCache, so we aren't trying to outsmart ourselves
 
381
                    // with a caching mechanism for now.
 
382
                    Collator collator = Collator.getInstance(cx.getLocale());
 
383
                    collator.setStrength(Collator.IDENTICAL);
 
384
                    collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
 
385
                    return ScriptRuntime.wrapNumber(collator.compare(
 
386
                            ScriptRuntime.toString(thisObj), 
 
387
                            ScriptRuntime.toString(args, 0)));
 
388
                }
 
389
              case Id_toLocaleLowerCase:
 
390
                {
 
391
                    return ScriptRuntime.toString(thisObj)
 
392
                            .toLowerCase(cx.getLocale());
 
393
                }
 
394
              case Id_toLocaleUpperCase:
 
395
                {
 
396
                    return ScriptRuntime.toString(thisObj)
 
397
                            .toUpperCase(cx.getLocale());
 
398
                }
 
399
            }
 
400
            throw new IllegalArgumentException(String.valueOf(id));
 
401
        }
 
402
    }
 
403
 
 
404
    private static NativeString realThis(Scriptable thisObj, IdFunctionObject f)
 
405
    {
 
406
        if (!(thisObj instanceof NativeString))
 
407
            throw incompatibleCallError(f);
 
408
        return (NativeString)thisObj;
 
409
    }
 
410
 
 
411
    /*
 
412
     * HTML composition aids.
 
413
     */
 
414
    private static String tagify(Object thisObj, String tag,
 
415
                                 String attribute, Object[] args)
 
416
    {
 
417
        String str = ScriptRuntime.toString(thisObj);
 
418
        StringBuffer result = new StringBuffer();
 
419
        result.append('<');
 
420
        result.append(tag);
 
421
        if (attribute != null) {
 
422
            result.append(' ');
 
423
            result.append(attribute);
 
424
            result.append("=\"");
 
425
            result.append(ScriptRuntime.toString(args, 0));
 
426
            result.append('"');
 
427
        }
 
428
        result.append('>');
 
429
        result.append(str);
 
430
        result.append("</");
 
431
        result.append(tag);
 
432
        result.append('>');
 
433
        return result.toString();
 
434
    }
 
435
 
 
436
    public String toString() {
 
437
        return string;
 
438
    }
 
439
 
 
440
    /* Make array-style property lookup work for strings.
 
441
     * XXX is this ECMA?  A version check is probably needed. In js too.
 
442
     */
 
443
    public Object get(int index, Scriptable start) {
 
444
        if (0 <= index && index < string.length()) {
 
445
            return string.substring(index, index + 1);
 
446
        }
 
447
        return super.get(index, start);
 
448
    }
 
449
 
 
450
    public void put(int index, Scriptable start, Object value) {
 
451
        if (0 <= index && index < string.length()) {
 
452
            return;
 
453
        }
 
454
        super.put(index, start, value);
 
455
    }
 
456
 
 
457
    /*
 
458
     *
 
459
     * See ECMA 15.5.4.6.  Uses Java String.indexOf()
 
460
     * OPT to add - BMH searching from jsstr.c.
 
461
     */
 
462
    private static int js_indexOf(String target, Object[] args) {
 
463
        String search = ScriptRuntime.toString(args, 0);
 
464
        double begin = ScriptRuntime.toInteger(args, 1);
 
465
 
 
466
        if (begin > target.length()) {
 
467
            return -1;
 
468
        } else {
 
469
            if (begin < 0)
 
470
                begin = 0;
 
471
            return target.indexOf(search, (int)begin);
 
472
        }
 
473
    }
 
474
 
 
475
    /*
 
476
     *
 
477
     * See ECMA 15.5.4.7
 
478
     *
 
479
     */
 
480
    private static int js_lastIndexOf(String target, Object[] args) {
 
481
        String search = ScriptRuntime.toString(args, 0);
 
482
        double end = ScriptRuntime.toNumber(args, 1);
 
483
 
 
484
        if (end != end || end > target.length())
 
485
            end = target.length();
 
486
        else if (end < 0)
 
487
            end = 0;
 
488
 
 
489
        return target.lastIndexOf(search, (int)end);
 
490
    }
 
491
 
 
492
    /*
 
493
     * Used by js_split to find the next split point in target,
 
494
     * starting at offset ip and looking either for the given
 
495
     * separator substring, or for the next re match.  ip and
 
496
     * matchlen must be reference variables (assumed to be arrays of
 
497
     * length 1) so they can be updated in the leading whitespace or
 
498
     * re case.
 
499
     *
 
500
     * Return -1 on end of string, >= 0 for a valid index of the next
 
501
     * separator occurrence if found, or the string length if no
 
502
     * separator is found.
 
503
     */
 
504
    private static int find_split(Context cx, Scriptable scope, String target,
 
505
                                  String separator, int version,
 
506
                                  RegExpProxy reProxy, Scriptable re,
 
507
                                  int[] ip, int[] matchlen, boolean[] matched,
 
508
                                  String[][] parensp)
 
509
    {
 
510
        int i = ip[0];
 
511
        int length = target.length();
 
512
 
 
513
        /*
 
514
         * Perl4 special case for str.split(' '), only if the user has selected
 
515
         * JavaScript1.2 explicitly.  Split on whitespace, and skip leading w/s.
 
516
         * Strange but true, apparently modeled after awk.
 
517
         */
 
518
        if (version == Context.VERSION_1_2 &&
 
519
            re == null && separator.length() == 1 && separator.charAt(0) == ' ')
 
520
        {
 
521
            /* Skip leading whitespace if at front of str. */
 
522
            if (i == 0) {
 
523
                while (i < length && Character.isWhitespace(target.charAt(i)))
 
524
                    i++;
 
525
                ip[0] = i;
 
526
            }
 
527
 
 
528
            /* Don't delimit whitespace at end of string. */
 
529
            if (i == length)
 
530
                return -1;
 
531
 
 
532
            /* Skip over the non-whitespace chars. */
 
533
            while (i < length
 
534
                   && !Character.isWhitespace(target.charAt(i)))
 
535
                i++;
 
536
 
 
537
            /* Now skip the next run of whitespace. */
 
538
            int j = i;
 
539
            while (j < length && Character.isWhitespace(target.charAt(j)))
 
540
                j++;
 
541
 
 
542
            /* Update matchlen to count delimiter chars. */
 
543
            matchlen[0] = j - i;
 
544
            return i;
 
545
        }
 
546
 
 
547
        /*
 
548
         * Stop if past end of string.  If at end of string, we will
 
549
         * return target length, so that
 
550
         *
 
551
         *  "ab,".split(',') => new Array("ab", "")
 
552
         *
 
553
         * and the resulting array converts back to the string "ab,"
 
554
         * for symmetry.  NB: This differs from perl, which drops the
 
555
         * trailing empty substring if the LIMIT argument is omitted.
 
556
         */
 
557
        if (i > length)
 
558
            return -1;
 
559
 
 
560
        /*
 
561
         * Match a regular expression against the separator at or
 
562
         * above index i.  Return -1 at end of string instead of
 
563
         * trying for a match, so we don't get stuck in a loop.
 
564
         */
 
565
        if (re != null) {
 
566
            return reProxy.find_split(cx, scope, target, separator, re,
 
567
                                      ip, matchlen, matched, parensp);
 
568
        }
 
569
 
 
570
        /*
 
571
         * Deviate from ECMA by never splitting an empty string by any separator
 
572
         * string into a non-empty array (an array of length 1 that contains the
 
573
         * empty string).
 
574
         */
 
575
        if (version != Context.VERSION_DEFAULT && version < Context.VERSION_1_3
 
576
            && length == 0)
 
577
            return -1;
 
578
 
 
579
        /*
 
580
         * Special case: if sep is the empty string, split str into
 
581
         * one character substrings.  Let our caller worry about
 
582
         * whether to split once at end of string into an empty
 
583
         * substring.
 
584
         *
 
585
         * For 1.2 compatibility, at the end of the string, we return the length as
 
586
         * the result, and set the separator length to 1 -- this allows the caller
 
587
         * to include an additional null string at the end of the substring list.
 
588
         */
 
589
        if (separator.length() == 0) {
 
590
            if (version == Context.VERSION_1_2) {
 
591
                if (i == length) {
 
592
                    matchlen[0] = 1;
 
593
                    return i;
 
594
                }
 
595
                return i + 1;
 
596
            }
 
597
            return (i == length) ? -1 : i + 1;
 
598
        }
 
599
 
 
600
        /* Punt to j.l.s.indexOf; return target length if separator is
 
601
         * not found.
 
602
         */
 
603
        if (ip[0] >= length)
 
604
            return length;
 
605
 
 
606
        i = target.indexOf(separator, ip[0]);
 
607
 
 
608
        return (i != -1) ? i : length;
 
609
    }
 
610
 
 
611
    /*
 
612
     * See ECMA 15.5.4.8.  Modified to match JS 1.2 - optionally takes
 
613
     * a limit argument and accepts a regular expression as the split
 
614
     * argument.
 
615
     */
 
616
    private static Object js_split(Context cx, Scriptable scope,
 
617
                                   String target, Object[] args)
 
618
    {
 
619
        // create an empty Array to return;
 
620
        Scriptable top = getTopLevelScope(scope);
 
621
        Scriptable result = ScriptRuntime.newObject(cx, top, "Array", null);
 
622
 
 
623
        // return an array consisting of the target if no separator given
 
624
        // don't check against undefined, because we want
 
625
        // 'fooundefinedbar'.split(void 0) to split to ['foo', 'bar']
 
626
        if (args.length < 1) {
 
627
            result.put(0, result, target);
 
628
            return result;
 
629
        }
 
630
 
 
631
        // Use the second argument as the split limit, if given.
 
632
        boolean limited = (args.length > 1) && (args[1] != Undefined.instance);
 
633
        long limit = 0;  // Initialize to avoid warning.
 
634
        if (limited) {
 
635
            /* Clamp limit between 0 and 1 + string length. */
 
636
            limit = ScriptRuntime.toUint32(args[1]);
 
637
            if (limit > target.length())
 
638
                limit = 1 + target.length();
 
639
        }
 
640
 
 
641
        String separator = null;
 
642
        int[] matchlen = new int[1];
 
643
        Scriptable re = null;
 
644
        RegExpProxy reProxy = null;
 
645
        if (args[0] instanceof Scriptable) {
 
646
            reProxy = ScriptRuntime.getRegExpProxy(cx);
 
647
            if (reProxy != null) {
 
648
                Scriptable test = (Scriptable)args[0];
 
649
                if (reProxy.isRegExp(test)) {
 
650
                    re = test;
 
651
                }
 
652
            }
 
653
        }
 
654
        if (re == null) {
 
655
            separator = ScriptRuntime.toString(args[0]);
 
656
            matchlen[0] = separator.length();
 
657
        }
 
658
 
 
659
        // split target with separator or re
 
660
        int[] ip = { 0 };
 
661
        int match;
 
662
        int len = 0;
 
663
        boolean[] matched = { false };
 
664
        String[][] parens = { null };
 
665
        int version = cx.getLanguageVersion();
 
666
        while ((match = find_split(cx, scope, target, separator, version,
 
667
                                   reProxy, re, ip, matchlen, matched, parens))
 
668
               >= 0)
 
669
        {
 
670
            if ((limited && len >= limit) || (match > target.length()))
 
671
                break;
 
672
 
 
673
            String substr;
 
674
            if (target.length() == 0)
 
675
                substr = target;
 
676
            else
 
677
                substr = target.substring(ip[0], match);
 
678
 
 
679
            result.put(len, result, substr);
 
680
            len++;
 
681
        /*
 
682
         * Imitate perl's feature of including parenthesized substrings
 
683
         * that matched part of the delimiter in the new array, after the
 
684
         * split substring that was delimited.
 
685
         */
 
686
            if (re != null && matched[0] == true) {
 
687
                int size = parens[0].length;
 
688
                for (int num = 0; num < size; num++) {
 
689
                    if (limited && len >= limit)
 
690
                        break;
 
691
                    result.put(len, result, parens[0][num]);
 
692
                    len++;
 
693
                }
 
694
                matched[0] = false;
 
695
            }
 
696
            ip[0] = match + matchlen[0];
 
697
 
 
698
            if (version < Context.VERSION_1_3
 
699
                && version != Context.VERSION_DEFAULT)
 
700
            {
 
701
        /*
 
702
         * Deviate from ECMA to imitate Perl, which omits a final
 
703
         * split unless a limit argument is given and big enough.
 
704
         */
 
705
                if (!limited && ip[0] == target.length())
 
706
                    break;
 
707
            }
 
708
        }
 
709
        return result;
 
710
    }
 
711
 
 
712
    /*
 
713
     * See ECMA 15.5.4.15
 
714
     */
 
715
    private static String js_substring(Context cx, String target,
 
716
                                       Object[] args)
 
717
    {
 
718
        int length = target.length();
 
719
        double start = ScriptRuntime.toInteger(args, 0);
 
720
        double end;
 
721
 
 
722
        if (start < 0)
 
723
            start = 0;
 
724
        else if (start > length)
 
725
            start = length;
 
726
 
 
727
        if (args.length <= 1 || args[1] == Undefined.instance) {
 
728
            end = length;
 
729
        } else {
 
730
            end = ScriptRuntime.toInteger(args[1]);
 
731
            if (end < 0)
 
732
                end = 0;
 
733
            else if (end > length)
 
734
                end = length;
 
735
 
 
736
            // swap if end < start
 
737
            if (end < start) {
 
738
                if (cx.getLanguageVersion() != Context.VERSION_1_2) {
 
739
                    double temp = start;
 
740
                    start = end;
 
741
                    end = temp;
 
742
                } else {
 
743
                    // Emulate old JDK1.0 java.lang.String.substring()
 
744
                    end = start;
 
745
                }
 
746
            }
 
747
        }
 
748
        return target.substring((int)start, (int)end);
 
749
    }
 
750
 
 
751
    int getLength() {
 
752
        return string.length();
 
753
    }
 
754
 
 
755
    /*
 
756
     * Non-ECMA methods.
 
757
     */
 
758
    private static String js_substr(String target, Object[] args) {
 
759
        if (args.length < 1)
 
760
            return target;
 
761
 
 
762
        double begin = ScriptRuntime.toInteger(args[0]);
 
763
        double end;
 
764
        int length = target.length();
 
765
 
 
766
        if (begin < 0) {
 
767
            begin += length;
 
768
            if (begin < 0)
 
769
                begin = 0;
 
770
        } else if (begin > length) {
 
771
            begin = length;
 
772
        }
 
773
 
 
774
        if (args.length == 1) {
 
775
            end = length;
 
776
        } else {
 
777
            end = ScriptRuntime.toInteger(args[1]);
 
778
            if (end < 0)
 
779
                end = 0;
 
780
            end += begin;
 
781
            if (end > length)
 
782
                end = length;
 
783
        }
 
784
 
 
785
        return target.substring((int)begin, (int)end);
 
786
    }
 
787
 
 
788
    /*
 
789
     * Python-esque sequence operations.
 
790
     */
 
791
    private static String js_concat(String target, Object[] args) {
 
792
        int N = args.length;
 
793
        if (N == 0) { return target; }
 
794
        else if (N == 1) {
 
795
            String arg = ScriptRuntime.toString(args[0]);
 
796
            return target.concat(arg);
 
797
        }
 
798
 
 
799
        // Find total capacity for the final string to avoid unnecessary
 
800
        // re-allocations in StringBuffer
 
801
        int size = target.length();
 
802
        String[] argsAsStrings = new String[N];
 
803
        for (int i = 0; i != N; ++i) {
 
804
            String s = ScriptRuntime.toString(args[i]);
 
805
            argsAsStrings[i] = s;
 
806
            size += s.length();
 
807
        }
 
808
 
 
809
        StringBuffer result = new StringBuffer(size);
 
810
        result.append(target);
 
811
        for (int i = 0; i != N; ++i) {
 
812
            result.append(argsAsStrings[i]);
 
813
        }
 
814
        return result.toString();
 
815
    }
 
816
 
 
817
    private static String js_slice(String target, Object[] args) {
 
818
        if (args.length != 0) {
 
819
            double begin = ScriptRuntime.toInteger(args[0]);
 
820
            double end;
 
821
            int length = target.length();
 
822
            if (begin < 0) {
 
823
                begin += length;
 
824
                if (begin < 0)
 
825
                    begin = 0;
 
826
            } else if (begin > length) {
 
827
                begin = length;
 
828
            }
 
829
 
 
830
            if (args.length == 1) {
 
831
                end = length;
 
832
            } else {
 
833
                end = ScriptRuntime.toInteger(args[1]);
 
834
                if (end < 0) {
 
835
                    end += length;
 
836
                    if (end < 0)
 
837
                        end = 0;
 
838
                } else if (end > length) {
 
839
                    end = length;
 
840
                }
 
841
                if (end < begin)
 
842
                    end = begin;
 
843
            }
 
844
            return target.substring((int)begin, (int)end);
 
845
        }
 
846
        return target;
 
847
    }
 
848
 
 
849
// #string_id_map#
 
850
 
 
851
    protected int findPrototypeId(String s)
 
852
    {
 
853
        int id;
 
854
// #generated# Last update: 2007-05-01 22:11:49 EDT
 
855
        L0: { id = 0; String X = null; int c;
 
856
            L: switch (s.length()) {
 
857
            case 3: c=s.charAt(2);
 
858
                if (c=='b') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sub; break L0;} }
 
859
                else if (c=='g') { if (s.charAt(0)=='b' && s.charAt(1)=='i') {id=Id_big; break L0;} }
 
860
                else if (c=='p') { if (s.charAt(0)=='s' && s.charAt(1)=='u') {id=Id_sup; break L0;} }
 
861
                break L;
 
862
            case 4: c=s.charAt(0);
 
863
                if (c=='b') { X="bold";id=Id_bold; }
 
864
                else if (c=='l') { X="link";id=Id_link; }
 
865
                break L;
 
866
            case 5: switch (s.charAt(4)) {
 
867
                case 'd': X="fixed";id=Id_fixed; break L;
 
868
                case 'e': X="slice";id=Id_slice; break L;
 
869
                case 'h': X="match";id=Id_match; break L;
 
870
                case 'k': X="blink";id=Id_blink; break L;
 
871
                case 'l': X="small";id=Id_small; break L;
 
872
                case 't': X="split";id=Id_split; break L;
 
873
                } break L;
 
874
            case 6: switch (s.charAt(1)) {
 
875
                case 'e': X="search";id=Id_search; break L;
 
876
                case 'h': X="charAt";id=Id_charAt; break L;
 
877
                case 'n': X="anchor";id=Id_anchor; break L;
 
878
                case 'o': X="concat";id=Id_concat; break L;
 
879
                case 'q': X="equals";id=Id_equals; break L;
 
880
                case 't': X="strike";id=Id_strike; break L;
 
881
                case 'u': X="substr";id=Id_substr; break L;
 
882
                } break L;
 
883
            case 7: switch (s.charAt(1)) {
 
884
                case 'a': X="valueOf";id=Id_valueOf; break L;
 
885
                case 'e': X="replace";id=Id_replace; break L;
 
886
                case 'n': X="indexOf";id=Id_indexOf; break L;
 
887
                case 't': X="italics";id=Id_italics; break L;
 
888
                } break L;
 
889
            case 8: c=s.charAt(4);
 
890
                if (c=='r') { X="toString";id=Id_toString; }
 
891
                else if (c=='s') { X="fontsize";id=Id_fontsize; }
 
892
                else if (c=='u') { X="toSource";id=Id_toSource; }
 
893
                break L;
 
894
            case 9: c=s.charAt(0);
 
895
                if (c=='f') { X="fontcolor";id=Id_fontcolor; }
 
896
                else if (c=='s') { X="substring";id=Id_substring; }
 
897
                break L;
 
898
            case 10: X="charCodeAt";id=Id_charCodeAt; break L;
 
899
            case 11: switch (s.charAt(2)) {
 
900
                case 'L': X="toLowerCase";id=Id_toLowerCase; break L;
 
901
                case 'U': X="toUpperCase";id=Id_toUpperCase; break L;
 
902
                case 'n': X="constructor";id=Id_constructor; break L;
 
903
                case 's': X="lastIndexOf";id=Id_lastIndexOf; break L;
 
904
                } break L;
 
905
            case 13: X="localeCompare";id=Id_localeCompare; break L;
 
906
            case 16: X="equalsIgnoreCase";id=Id_equalsIgnoreCase; break L;
 
907
            case 17: c=s.charAt(8);
 
908
                if (c=='L') { X="toLocaleLowerCase";id=Id_toLocaleLowerCase; }
 
909
                else if (c=='U') { X="toLocaleUpperCase";id=Id_toLocaleUpperCase; }
 
910
                break L;
 
911
            }
 
912
            if (X!=null && X!=s && !X.equals(s)) id = 0;
 
913
            break L0;
 
914
        }
 
915
// #/generated#
 
916
        return id;
 
917
    }
 
918
 
 
919
    private static final int
 
920
        ConstructorId_fromCharCode   = -1,
 
921
 
 
922
        Id_constructor               = 1,
 
923
        Id_toString                  = 2,
 
924
        Id_toSource                  = 3,
 
925
        Id_valueOf                   = 4,
 
926
        Id_charAt                    = 5,
 
927
        Id_charCodeAt                = 6,
 
928
        Id_indexOf                   = 7,
 
929
        Id_lastIndexOf               = 8,
 
930
        Id_split                     = 9,
 
931
        Id_substring                 = 10,
 
932
        Id_toLowerCase               = 11,
 
933
        Id_toUpperCase               = 12,
 
934
        Id_substr                    = 13,
 
935
        Id_concat                    = 14,
 
936
        Id_slice                     = 15,
 
937
        Id_bold                      = 16,
 
938
        Id_italics                   = 17,
 
939
        Id_fixed                     = 18,
 
940
        Id_strike                    = 19,
 
941
        Id_small                     = 20,
 
942
        Id_big                       = 21,
 
943
        Id_blink                     = 22,
 
944
        Id_sup                       = 23,
 
945
        Id_sub                       = 24,
 
946
        Id_fontsize                  = 25,
 
947
        Id_fontcolor                 = 26,
 
948
        Id_link                      = 27,
 
949
        Id_anchor                    = 28,
 
950
        Id_equals                    = 29,
 
951
        Id_equalsIgnoreCase          = 30,
 
952
        Id_match                     = 31,
 
953
        Id_search                    = 32,
 
954
        Id_replace                   = 33,
 
955
        Id_localeCompare             = 34,
 
956
        Id_toLocaleLowerCase         = 35,
 
957
        Id_toLocaleUpperCase         = 36,
 
958
        MAX_PROTOTYPE_ID             = 36;
 
959
 
 
960
// #/string_id_map#
 
961
 
 
962
    private static final int 
 
963
        ConstructorId_charAt         = -Id_charAt,
 
964
        ConstructorId_charCodeAt     = -Id_charCodeAt,
 
965
        ConstructorId_indexOf        = -Id_indexOf,
 
966
        ConstructorId_lastIndexOf    = -Id_lastIndexOf,
 
967
        ConstructorId_split          = -Id_split,
 
968
        ConstructorId_substring      = -Id_substring,
 
969
        ConstructorId_toLowerCase    = -Id_toLowerCase,
 
970
        ConstructorId_toUpperCase    = -Id_toUpperCase,
 
971
        ConstructorId_substr         = -Id_substr,
 
972
        ConstructorId_concat         = -Id_concat,
 
973
        ConstructorId_slice          = -Id_slice,
 
974
        ConstructorId_equalsIgnoreCase = -Id_equalsIgnoreCase,
 
975
        ConstructorId_match          = -Id_match,
 
976
        ConstructorId_search         = -Id_search,
 
977
        ConstructorId_replace        = -Id_replace,
 
978
        ConstructorId_localeCompare  = -Id_localeCompare,
 
979
        ConstructorId_toLocaleLowerCase = -Id_toLocaleLowerCase;
 
980
 
 
981
    private String string;
 
982
}
 
983