KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > RubyString


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
15  * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
16  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
17  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
18  * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
19  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
20  * Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
21  * Copyright (C) 2005 Tim Azzopardi <tim@tigerfive.com>
22  * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
23  * Copyright (C) 2006 Ola Bini <ola@ologix.com>
24  * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the CPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the CPL, the GPL or the LGPL.
37  ***** END LICENSE BLOCK *****/

38 package org.jruby;
39
40 import java.nio.ByteBuffer JavaDoc;
41 import java.nio.charset.CharacterCodingException JavaDoc;
42 import java.nio.charset.Charset JavaDoc;
43 import java.nio.charset.CharsetDecoder JavaDoc;
44 import java.nio.charset.CodingErrorAction JavaDoc;
45 import java.util.ArrayList JavaDoc;
46 import java.util.List JavaDoc;
47 import java.util.Locale JavaDoc;
48 import java.util.regex.Matcher JavaDoc;
49 import java.util.regex.Pattern JavaDoc;
50
51 import org.jruby.javasupport.JavaUtil;
52 import org.jruby.runtime.Arity;
53 import org.jruby.runtime.Block;
54 import org.jruby.runtime.CallType;
55 import org.jruby.runtime.ClassIndex;
56 import org.jruby.runtime.ThreadContext;
57 import org.jruby.runtime.builtin.IRubyObject;
58 import org.jruby.runtime.marshal.UnmarshalStream;
59 import org.jruby.util.ByteList;
60 import org.jruby.util.KCode;
61 import org.jruby.util.Pack;
62 import org.jruby.util.PrintfFormat;
63
64 /**
65  *
66  * @author jpetersen
67  */

68 public class RubyString extends RubyObject {
69     // Default record seperator
70
private static final String JavaDoc DEFAULT_RS = "\n";
71
72     public static final byte OP_PLUS_SWITCHVALUE = 1;
73     public static final byte OP_LT_SWITCHVALUE = 2;
74     public static final byte AREF_SWITCHVALUE = 3;
75     public static final byte ASET_SWITCHVALUE = 4;
76     public static final byte NIL_P_SWITCHVALUE = 5;
77     public static final byte EQUALEQUAL_SWITCHVALUE = 6;
78     public static final byte OP_GE_SWITCHVALUE = 7;
79     public static final byte OP_LSHIFT_SWITCHVALUE = 8;
80     public static final byte EMPTY_P_SWITCHVALUE = 9;
81
82     private ByteList value;
83     private int hash;
84     private RubyFixnum r_hash;
85     private boolean validHash = false;
86     private String JavaDoc stringValue;
87
88     // @see IRuby.newString(...)
89
private RubyString(Ruby runtime, CharSequence JavaDoc value) {
90             this(runtime, runtime.getString(), value);
91     }
92
93     private RubyString(Ruby runtime, byte[] value) {
94             this(runtime, runtime.getString(), value);
95     }
96
97     private RubyString(Ruby runtime, ByteList value) {
98             this(runtime, runtime.getString(), value);
99     }
100
101     private RubyString(Ruby runtime, RubyClass rubyClass, CharSequence JavaDoc value) {
102         super(runtime, rubyClass);
103
104         assert value != null;
105
106         this.value = new ByteList(ByteList.plain(value),false);
107     }
108
109     private RubyString(Ruby runtime, RubyClass rubyClass, byte[] value) {
110         super(runtime, rubyClass);
111
112         assert value != null;
113
114         this.value = new ByteList(value);
115     }
116
117     private RubyString(Ruby runtime, RubyClass rubyClass, ByteList value) {
118         super(runtime, rubyClass);
119
120         assert value != null;
121
122         this.value = value;
123     }
124
125     public IRubyObject callMethod(ThreadContext context, RubyModule rubyclass, byte switchvalue, String JavaDoc name,
126             IRubyObject[] args, CallType callType, Block block) {
127         switch (switchvalue) {
128             case OP_PLUS_SWITCHVALUE:
129                 Arity.singleArgument().checkArity(context.getRuntime(), args);
130                 return op_plus(args[0]);
131             case OP_LT_SWITCHVALUE:
132                 Arity.singleArgument().checkArity(context.getRuntime(), args);
133                 return op_lt(args[0]);
134             case AREF_SWITCHVALUE:
135                 Arity.optional().checkArity(context.getRuntime(), args);
136                 return aref(args);
137             case ASET_SWITCHVALUE:
138                 Arity.optional().checkArity(context.getRuntime(), args);
139                 return aset(args);
140             case NIL_P_SWITCHVALUE:
141                 Arity.noArguments().checkArity(context.getRuntime(), args);
142                 return nil_p();
143             case EQUALEQUAL_SWITCHVALUE:
144                 Arity.singleArgument().checkArity(context.getRuntime(), args);
145                 return eql(args[0]);
146             case OP_GE_SWITCHVALUE:
147                 Arity.singleArgument().checkArity(context.getRuntime(), args);
148                 return op_ge(args[0]);
149             case OP_LSHIFT_SWITCHVALUE:
150                 Arity.singleArgument().checkArity(context.getRuntime(), args);
151                 return concat(args[0]);
152             case EMPTY_P_SWITCHVALUE:
153                 Arity.noArguments().checkArity(context.getRuntime(), args);
154                 return empty();
155             case 0:
156             default:
157                 return super.callMethod(context, rubyclass, name, args, callType, block);
158         }
159     }
160
161     public int getNativeTypeIndex() {
162         return ClassIndex.STRING;
163     }
164
165     public Class JavaDoc getJavaClass() {
166         return String JavaDoc.class;
167     }
168
169     /**
170      * Remembers toString value, which is expensive for StringBuffer.
171      */

172     public String JavaDoc toString() {
173         if (stringValue == null) {
174             stringValue = new String JavaDoc(ByteList.plain(value.bytes),0,value.realSize);
175         }
176         return stringValue;
177     }
178
179     /**
180      * This string has been changed, so invalidate stringValue.
181      */

182     private void stringMutated() {
183         stringValue = null;
184         validHash = false;
185         taint();
186     }
187
188     public static String JavaDoc bytesToString(byte[] bytes, int beg, int len) {
189         return new String JavaDoc(ByteList.plain(bytes), beg, len);
190     }
191
192     public static String JavaDoc byteListToString(ByteList bytes) {
193         return bytesToString(bytes.unsafeBytes(),0,bytes.length());
194     }
195
196     public static String JavaDoc bytesToString(byte[] bytes) {
197         return bytesToString(bytes,0,bytes.length);
198     }
199
200     public static byte[] stringToBytes(String JavaDoc string) {
201         return ByteList.plain(string);
202     }
203
204     public static boolean isDigit(int c) {
205         return c >= '0' && c <= '9';
206     }
207
208     public static boolean isUpper(int c) {
209         return c >= 'A' && c <= 'Z';
210     }
211
212     public static boolean isLower(int c) {
213         return c >= 'a' && c <= 'z';
214     }
215
216     public static boolean isLetter(int c) {
217         return isUpper(c) || isLower(c);
218     }
219
220     public static boolean isAlnum(int c) {
221         return isUpper(c) || isLower(c) || isDigit(c);
222     }
223
224     public static boolean isPrint(int c) {
225         return c >= 0x20 && c <= 0x7E;
226     }
227     
228     public IRubyObject checkStringType() {
229         return this;
230     }
231
232     public IRubyObject to_s() {
233         return this;
234     }
235
236     /* rb_str_cmp_m */
237     public IRubyObject op_cmp(IRubyObject other) {
238         if (other instanceof RubyString) {
239             return getRuntime().newFixnum(cmp((RubyString)other));
240         }
241
242         // FIXME: This code does not appear to be applicable for <=> according to ri.
243
// ri docs claim the other operand *must* be a String.
244
/*ThreadContext context = getRuntime().getCurrentContext();
245
246         if (other.respondsTo("to_str") && other.respondsTo("<=>")) {
247             IRubyObject tmp = other.callMethod(context, "<=>", this);
248
249             if (!tmp.isNil()) {
250                 return tmp instanceof RubyFixnum ? tmp.callMethod(context, "-") :
251                     getRuntime().newFixnum(0).callMethod(context, "-", tmp);
252             }
253         }*/

254
255         return getRuntime().getNil();
256     }
257
258     public IRubyObject eql(IRubyObject other) {
259         Ruby runtime = getRuntime();
260         if (other == this) {
261             return runtime.getTrue();
262         } else if (!(other instanceof RubyString)) {
263             if(other.respondsTo("to_str")) {
264                 return other.callMethod(runtime.getCurrentContext(), "==", this);
265             }
266             return runtime.getFalse();
267         }
268         /* use Java implementation if both different String instances */
269         return runtime.newBoolean(value.equals(((RubyString) other).value));
270     }
271
272     public IRubyObject op_plus(IRubyObject other) {
273         RubyString str = RubyString.stringValue(other);
274
275         ByteList newValue = new ByteList(value);
276         newValue.append(str.value);
277         return (RubyString) newString(getRuntime(), newValue).infectBy(str);
278     }
279
280     public IRubyObject op_mul(IRubyObject other) {
281         RubyInteger otherInteger =
282                 (RubyInteger) other.convertType(RubyInteger.class, "Integer", "to_i");
283         long len = otherInteger.getLongValue();
284
285         if (len < 0) {
286             throw getRuntime().newArgumentError("negative argument");
287         }
288
289         // we limit to int because ByteBuffer can only allocate int sizes
290
if (len > 0 && Integer.MAX_VALUE / len < value.length()) {
291             throw getRuntime().newArgumentError("argument too big");
292         }
293         ByteList newBytes = new ByteList(value.length() * (int)len);
294
295         for (int i = 0; i < len; i++) {
296             newBytes.append(value);
297         }
298
299         RubyString newString = newString(getRuntime(), newBytes);
300         newString.setTaint(isTaint());
301         return newString;
302     }
303
304     public IRubyObject format(IRubyObject arg) {
305         // FIXME: Should we make this work with platform's locale, or continue hardcoding US?
306
Object JavaDoc[] args;
307         IRubyObject[] rargs = null;
308         if (arg instanceof RubyArray) {
309             rargs = (IRubyObject[])((RubyArray)arg).getList().toArray(new IRubyObject[0]);
310             args = new Object JavaDoc[rargs.length];
311             for(int i=0;i<rargs.length;i++) {
312                 args[i] = JavaUtil.convertRubyToJava(rargs[i]);
313             }
314         } else {
315             args = new Object JavaDoc[]{JavaUtil.convertRubyToJava(arg)};
316             rargs = new IRubyObject[]{arg};
317         }
318         return getRuntime().newString(new PrintfFormat(Locale.US, toString()).sprintf(args,rargs));
319     }
320
321     public RubyFixnum hash() {
322         hashCode();
323         return r_hash;
324     }
325
326     public int hashCode() {
327         if(!validHash) {
328             hash = value.hashCode();
329             r_hash = getRuntime().newFixnum(hash);
330             validHash = true;
331         }
332         return hash;
333     }
334
335     public boolean equals(Object JavaDoc other) {
336         if (this == other) {
337             return true;
338         }
339
340         if (other instanceof RubyString) {
341             RubyString string = (RubyString)other;
342
343             if (string.value.equals(value)) {
344                 return true;
345             }
346         }
347
348         return false;
349     }
350
351     // Common enough check to make it a convenience method.
352
private boolean sameAs(RubyString other) {
353         return value.equals(other.value);
354     }
355
356     /** rb_obj_as_string
357      *
358      */

359     public static RubyString objAsString(IRubyObject obj) {
360         if (obj instanceof RubyString) return (RubyString) obj;
361
362         IRubyObject str = obj.callMethod(obj.getRuntime().getCurrentContext(), "to_s");
363
364         if (!(str instanceof RubyString)) return (RubyString) obj.anyToString();
365
366         if (obj.isTaint()) str.setTaint(true);
367
368         return (RubyString) str;
369     }
370
371     /** rb_str_cmp
372      *
373      */

374     public int cmp(RubyString other) {
375         return value.cmp(other.value);
376     }
377
378     /** rb_to_id
379      *
380      */

381     public String JavaDoc asSymbol() {
382         return toString();
383     }
384
385
386     /** Create a new String which uses the same Ruby runtime and the same
387      * class like this String.
388      *
389      * This method should be used to satisfy RCR #38.
390      *
391      */

392     public RubyString newString(CharSequence JavaDoc s) {
393         return new RubyString(getRuntime(), getType(), s);
394     }
395
396     /** Create a new String which uses the same Ruby runtime and the same
397      * class like this String.
398      *
399      * This method should be used to satisfy RCR #38.
400      *
401      */

402     public RubyString newString(ByteList s) {
403         return new RubyString(getRuntime(), getType(), s);
404     }
405
406     // Methods of the String class (rb_str_*):
407

408     /** rb_str_new2
409      *
410      */

411     public static RubyString newString(Ruby runtime, CharSequence JavaDoc str) {
412         return new RubyString(runtime, str);
413     }
414
415     public static RubyString newString(Ruby runtime, byte[] bytes) {
416         return new RubyString(runtime, bytes);
417     }
418
419     public static RubyString newString(Ruby runtime, ByteList bytes) {
420         return new RubyString(runtime, bytes);
421     }
422
423     public static RubyString newString(Ruby runtime, byte[] bytes, int start, int length) {
424         byte[] bytes2 = new byte[length];
425         System.arraycopy(bytes, start, bytes2, 0, length);
426         return new RubyString(runtime, bytes2);
427     }
428
429     public IRubyObject doClone(){
430         return newString(getRuntime(), (ByteList)value.clone());
431     }
432
433     public RubyString cat(byte[] str) {
434         value.append(str);
435         stringMutated();
436         return this;
437     }
438
439     public RubyString cat(ByteList str) {
440         value.append(str);
441         stringMutated();
442         return this;
443     }
444
445     public RubyString cat(byte ch) {
446         value.append(ch);
447         stringMutated();
448         return this;
449     }
450
451     public IRubyObject to_str() {
452         if (getMetaClass().getRealClass() != getRuntime().getString()) {
453             return newString(getRuntime(), value.bytes());
454         }
455         return this;
456     }
457
458     /** rb_str_replace_m
459      *
460      */

461     public RubyString replace(IRubyObject other) {
462         testFrozen("String");
463          
464         RubyString newValue = stringValue(other);
465         if (this == other || sameAs(newValue)) {
466             return this;
467         }
468         value.replace(newValue.value.bytes());
469         stringMutated();
470         return (RubyString) infectBy(newValue);
471     }
472
473     public RubyString reverse() {
474         return newString(getRuntime(), (ByteList)value.clone()).reverse_bang();
475     }
476
477     public RubyString reverse_bang() {
478         for (int i = 0; i < (value.length() / 2); i++) {
479             byte b = (byte)value.get(i);
480             value.set(i, value.get(value.length() - i - 1));
481             value.set(value.length() - i - 1, b);
482         }
483         stringMutated();
484         return this;
485     }
486
487     /** rb_str_s_new
488      *
489      */

490     public static RubyString newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
491         RubyString newString = recv.getRuntime().newString("");
492         newString.setMetaClass((RubyClass) recv);
493         newString.callInit(args, block);
494         return newString;
495     }
496
497     public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
498         if (checkArgumentCount(args, 0, 1) == 1) {
499             replace(args[0]);
500         }
501         return this;
502     }
503
504     public IRubyObject casecmp(IRubyObject other) {
505         int compare = toString().compareToIgnoreCase(stringValue(other).toString());
506         return RubyFixnum.newFixnum(getRuntime(), compare == 0 ? 0 : (compare < 0 ? -1 : 1));
507     }
508
509     /** rb_str_match
510      *
511      */

512     public IRubyObject match(IRubyObject other) {
513         if (other instanceof RubyRegexp) {
514             return ((RubyRegexp) other).match(this);
515         } else if (other instanceof RubyString) {
516             throw getRuntime().newTypeError("type mismatch: String given");
517         }
518         return other.callMethod(getRuntime().getCurrentContext(), "=~", this);
519     }
520
521     /** rb_str_match2
522      *
523      */

524     public IRubyObject match2() {
525         return RubyRegexp.newRegexp(this, 0, null).match2();
526     }
527
528     /**
529      * String#match(pattern)
530      *
531      * @param pattern Regexp or String
532      */

533     public IRubyObject match3(IRubyObject pattern) {
534         if (pattern instanceof RubyRegexp) {
535             return ((RubyRegexp)pattern).search2(toString());
536         } else if (pattern instanceof RubyString) {
537             RubyRegexp regexp = RubyRegexp.newRegexp((RubyString) pattern, 0, null);
538             return regexp.search2(toString());
539         }
540
541         return getRuntime().getNil();
542     }
543
544     /** rb_str_capitalize
545      *
546      */

547     public IRubyObject capitalize() {
548         RubyString result = (RubyString) dup();
549         result.capitalize_bang();
550         return result;
551     }
552
553     /** rb_str_capitalize_bang
554      *
555      */

556     public IRubyObject capitalize_bang() {
557         if (isEmpty()) {
558             return getRuntime().getNil();
559         }
560         char capital = value.charAt(0);
561         boolean changed = false;
562         if (Character.isLetter(capital) && Character.isLowerCase(capital)) {
563             value.set(0, (byte)Character.toUpperCase(capital));
564             changed = true;
565         }
566
567         for (int i = 1; i < value.length(); i++) {
568             capital = value.charAt(i);
569             if (Character.isLetter(capital) && Character.isUpperCase(capital)) {
570                 value.set(i, (byte)Character.toLowerCase(capital));
571                 changed = true;
572             }
573         }
574
575         if (changed) {
576             stringMutated();
577             return this;
578         } else {
579             return getRuntime().getNil();
580         }
581     }
582
583     public IRubyObject op_ge(IRubyObject other) {
584         if (other instanceof RubyString) {
585             return getRuntime().newBoolean(cmp((RubyString) other) >= 0);
586         }
587
588         return RubyComparable.op_ge(this, other);
589     }
590
591     public IRubyObject op_gt(IRubyObject other) {
592         if (other instanceof RubyString) {
593             return getRuntime().newBoolean(cmp((RubyString) other) > 0);
594         }
595
596         return RubyComparable.op_gt(this, other);
597     }
598
599     public IRubyObject op_le(IRubyObject other) {
600         if (other instanceof RubyString) {
601             return getRuntime().newBoolean(cmp((RubyString) other) <= 0);
602         }
603
604         return RubyComparable.op_le(this, other);
605     }
606
607     public IRubyObject op_lt(IRubyObject other) {
608         if (other instanceof RubyString) {
609             return getRuntime().newBoolean(cmp((RubyString) other) < 0);
610         }
611
612         return RubyComparable.op_lt(this, other);
613     }
614
615     public IRubyObject op_eql(IRubyObject other) {
616         return equals(other) ? other.getRuntime().getTrue() : other.getRuntime().getFalse();
617     }
618
619     /** rb_str_upcase
620      *
621      */

622     public RubyString upcase() {
623         return newString(toString().toUpperCase());
624     }
625
626     /** rb_str_upcase_bang
627      *
628      */

629     public IRubyObject upcase_bang() {
630         boolean changed = false;
631         for (int i = 0; i < value.length(); i++) {
632             char c = value.charAt(i);
633             if (!Character.isLetter(c) || Character.isUpperCase(c)) continue;
634             value.set(i, (byte)Character.toUpperCase(c));
635             changed = true;
636         }
637         if (changed) {
638             stringMutated();
639             return this;
640         } else {
641             return getRuntime().getNil();
642         }
643     }
644
645     /** rb_str_downcase
646      *
647      */

648     public RubyString downcase() {
649         return newString(toString().toLowerCase());
650     }
651
652     /** rb_str_downcase_bang
653      *
654      */

655     public IRubyObject downcase_bang() {
656         boolean changed = false;
657         for (int i = 0; i < value.length(); i++) {
658             char c = value.charAt(i);
659             if (!Character.isLetter(c) || Character.isLowerCase(c)) continue;
660             value.set(i, (byte)Character.toLowerCase(c));
661             changed = true;
662         }
663         if (changed) {
664             stringMutated();
665             return this;
666         } else {
667             return getRuntime().getNil();
668         }
669     }
670
671     /** rb_str_swapcase
672      *
673      */

674     public RubyString swapcase() {
675         RubyString newString = newString(getRuntime(), (ByteList)value.clone());
676         IRubyObject swappedString = newString.swapcase_bang();
677
678         return (RubyString) (swappedString.isNil() ? newString : swappedString);
679     }
680
681     /** rb_str_swapcase_bang
682      *
683      */

684     public IRubyObject swapcase_bang() {
685         boolean changesMade = false;
686
687         for (int i = 0; i < value.length(); i++) {
688             char c = value.charAt(i);
689
690             if (!Character.isLetter(c)) {
691                 continue;
692             } else if (Character.isLowerCase(c)) {
693                 changesMade = true;
694                 value.set(i, (byte)Character.toUpperCase(c));
695             } else {
696                 changesMade = true;
697                 value.set(i, (byte)Character.toLowerCase(c));
698             }
699         }
700         if (changesMade) {
701             stringMutated();
702             return this;
703         }
704         return getRuntime().getNil();
705     }
706
707     /** rb_str_dump
708      *
709      */

710     public RubyString dump() {
711         return inspect(true);
712     }
713
714     public IRubyObject insert(IRubyObject indexArg, IRubyObject stringArg) {
715         int index = (int) indexArg.convertToInteger().getLongValue();
716         if (index < 0) {
717             index += value.length() + 1;
718         }
719
720         if (index < 0 || index > value.length()) {
721             throw getRuntime().newIndexError("index " + index + " out of range");
722         }
723
724         ByteList insert = ((RubyString)stringArg.convertToString()).value;
725         value.unsafeReplace(index, 0, insert);
726         stringMutated();
727         return this;
728     }
729
730     /** rb_str_inspect
731      *
732      */

733     public IRubyObject inspect() {
734         return inspect(false);
735     }
736
737     private RubyString inspect(boolean dump) {
738         final int length = value.length();
739         Ruby runtime = getRuntime();
740
741         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(length + 2 + length / 100);
742
743         sb.append('\"');
744
745         // FIXME: This may not be unicode-safe
746
for (int i = 0; i < length; i++) {
747             int c = value.get(i) & 0xFF;
748             if (isAlnum(c)) {
749                 sb.append((char)c);
750             } else if (runtime.getKCode() == KCode.UTF8 && c == 0xEF) {
751                 // don't escape encoded UTF8 characters, leave them as bytes
752
// append byte order mark plus two character bytes
753
sb.append((char)c);
754                 sb.append((char)(value.get(++i) & 0xFF));
755                 sb.append((char)(value.get(++i) & 0xFF));
756             } else if (c == '\"' || c == '\\') {
757                 sb.append('\\').append((char)c);
758             } else if (dump && c == '#') {
759                 sb.append('\\').append((char)c);
760             } else if (isPrint(c)) {
761                 sb.append((char)c);
762             } else if (c == '\n') {
763                 sb.append('\\').append('n');
764             } else if (c == '\r') {
765                 sb.append('\\').append('r');
766             } else if (c == '\t') {
767                 sb.append('\\').append('t');
768             } else if (c == '\f') {
769                 sb.append('\\').append('f');
770             } else if (c == '\u000B') {
771                 sb.append('\\').append('v');
772             } else if (c == '\u0007') {
773                 sb.append('\\').append('a');
774             } else if (c == '\u0008') {
775                 // FIXME: This doesn't appear to be right; ruby fails a test related to this at testString.rb line 440
776
sb.append('\\').append('b');
777             } else if (c == '\u001B') {
778                 sb.append('\\').append('e');
779             } else {
780                 sb.append(new PrintfFormat("\\%.3o").sprintf((char)c));
781             }
782         }
783
784         sb.append('\"');
785         return getRuntime().newString(sb.toString());
786     }
787
788     /** rb_str_length
789      *
790      */

791     public RubyFixnum length() {
792         return getRuntime().newFixnum(value.length());
793     }
794
795     /** rb_str_empty
796      *
797      */

798     public RubyBoolean empty() {
799         return getRuntime().newBoolean(isEmpty());
800     }
801
802     private boolean isEmpty() {
803         return value.length() == 0;
804     }
805
806     /** rb_str_append
807      *
808      */

809     public RubyString append(IRubyObject other) {
810         infectBy(other);
811         return cat(stringValue(other).value);
812     }
813
814     /** rb_str_concat
815      *
816      */

817     public RubyString concat(IRubyObject other) {
818         if ((other instanceof RubyFixnum) && ((RubyFixnum) other).getLongValue() < 256) {
819             return cat((byte) ((RubyFixnum) other).getLongValue());
820         }
821         return append(other);
822     }
823
824     /** rb_str_crypt
825      *
826      */

827     public RubyString crypt(IRubyObject other) {
828         String JavaDoc salt = stringValue(other).getValue().toString();
829         if(salt.length()<2) {
830             throw getRuntime().newArgumentError("salt too short(need >=2 bytes)");
831         }
832
833         salt = salt.substring(0,2);
834         return getRuntime().newString(JavaCrypt.crypt(salt, this.toString()));
835     }
836
837
838     public static class JavaCrypt {
839         private static java.util.Random JavaDoc r_gen = new java.util.Random JavaDoc();
840
841         private static final char theBaseSalts[] = {
842             'a','b','c','d','e','f','g','h','i','j','k','l','m',
843             'n','o','p','q','r','s','t','u','v','w','x','y','z',
844             'A','B','C','D','E','F','G','H','I','J','K','L','M',
845             'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
846             '0','1','2','3','4','5','6','7','8','9','/','.'};
847
848         private static final int ITERATIONS = 16;
849
850         private static final int con_salt[] = {
851             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
852             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
853             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
854             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
855             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
856             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
857             0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
858             0x0A, 0x0B, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
859             0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
860             0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
861             0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
862             0x23, 0x24, 0x25, 0x20, 0x21, 0x22, 0x23, 0x24,
863             0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
864             0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
865             0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C,
866             0x3D, 0x3E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00,
867         };
868
869         private static final boolean shifts2[] = {
870             false, false, true, true, true, true, true, true,
871             false, true, true, true, true, true, true, false };
872
873         private static final int skb[][] = {
874             {
875                 /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
876                 0x00000000, 0x00000010, 0x20000000, 0x20000010,
877                 0x00010000, 0x00010010, 0x20010000, 0x20010010,
878                 0x00000800, 0x00000810, 0x20000800, 0x20000810,
879                 0x00010800, 0x00010810, 0x20010800, 0x20010810,
880                 0x00000020, 0x00000030, 0x20000020, 0x20000030,
881                 0x00010020, 0x00010030, 0x20010020, 0x20010030,
882                 0x00000820, 0x00000830, 0x20000820, 0x20000830,
883                 0x00010820, 0x00010830, 0x20010820, 0x20010830,
884                 0x00080000, 0x00080010, 0x20080000, 0x20080010,
885                 0x00090000, 0x00090010, 0x20090000, 0x20090010,
886                 0x00080800, 0x00080810, 0x20080800, 0x20080810,
887                 0x00090800, 0x00090810, 0x20090800, 0x20090810,
888                 0x00080020, 0x00080030, 0x20080020, 0x20080030,
889                 0x00090020, 0x00090030, 0x20090020, 0x20090030,
890                 0x00080820, 0x00080830, 0x20080820, 0x20080830,
891                 0x00090820, 0x00090830, 0x20090820, 0x20090830,
892             },{
893                 /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
894                 0x00000000, 0x02000000, 0x00002000, 0x02002000,
895                 0x00200000, 0x02200000, 0x00202000, 0x02202000,
896                 0x00000004, 0x02000004, 0x00002004, 0x02002004,
897                 0x00200004, 0x02200004, 0x00202004, 0x02202004,
898                 0x00000400, 0x02000400, 0x00002400, 0x02002400,
899                 0x00200400, 0x02200400, 0x00202400, 0x02202400,
900                 0x00000404, 0x02000404, 0x00002404, 0x02002404,
901                 0x00200404, 0x02200404, 0x00202404, 0x02202404,
902                 0x10000000, 0x12000000, 0x10002000, 0x12002000,
903                 0x10200000, 0x12200000, 0x10202000, 0x12202000,
904                 0x10000004, 0x12000004, 0x10002004, 0x12002004,
905                 0x10200004, 0x12200004, 0x10202004, 0x12202004,
906                 0x10000400, 0x12000400, 0x10002400, 0x12002400,
907                 0x10200400, 0x12200400, 0x10202400, 0x12202400,
908                 0x10000404, 0x12000404, 0x10002404, 0x12002404,
909                 0x10200404, 0x12200404, 0x10202404, 0x12202404,
910             },{
911                 /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
912                 0x00000000, 0x00000001, 0x00040000, 0x00040001,
913                 0x01000000, 0x01000001, 0x01040000, 0x01040001,
914                 0x00000002, 0x00000003, 0x00040002, 0x00040003,
915                 0x01000002, 0x01000003, 0x01040002, 0x01040003,
916                 0x00000200, 0x00000201, 0x00040200, 0x00040201,
917                 0x01000200, 0x01000201, 0x01040200, 0x01040201,
918                 0x00000202, 0x00000203, 0x00040202, 0x00040203,
919                 0x01000202, 0x01000203, 0x01040202, 0x01040203,
920                 0x08000000, 0x08000001, 0x08040000, 0x08040001,
921                 0x09000000, 0x09000001, 0x09040000, 0x09040001,
922                 0x08000002, 0x08000003, 0x08040002, 0x08040003,
923                 0x09000002, 0x09000003, 0x09040002, 0x09040003,
924                 0x08000200, 0x08000201, 0x08040200, 0x08040201,
925                 0x09000200, 0x09000201, 0x09040200, 0x09040201,
926                 0x08000202, 0x08000203, 0x08040202, 0x08040203,
927                 0x09000202, 0x09000203, 0x09040202, 0x09040203,
928             },{
929                 /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
930                 0x00000000, 0x00100000, 0x00000100, 0x00100100,
931                 0x00000008, 0x00100008, 0x00000108, 0x00100108,
932                 0x00001000, 0x00101000, 0x00001100, 0x00101100,
933                 0x00001008, 0x00101008, 0x00001108, 0x00101108,
934                 0x04000000, 0x04100000, 0x04000100, 0x04100100,
935                 0x04000008, 0x04100008, 0x04000108, 0x04100108,
936                 0x04001000, 0x04101000, 0x04001100, 0x04101100,
937                 0x04001008, 0x04101008, 0x04001108, 0x04101108,
938                 0x00020000, 0x00120000, 0x00020100, 0x00120100,
939                 0x00020008, 0x00120008, 0x00020108, 0x00120108,
940                 0x00021000, 0x00121000, 0x00021100, 0x00121100,
941                 0x00021008, 0x00121008, 0x00021108, 0x00121108,
942                 0x04020000, 0x04120000, 0x04020100, 0x04120100,
943                 0x04020008, 0x04120008, 0x04020108, 0x04120108,
944                 0x04021000, 0x04121000, 0x04021100, 0x04121100,
945                 0x04021008, 0x04121008, 0x04021108, 0x04121108,
946             },{
947                 /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
948                 0x00000000, 0x10000000, 0x00010000, 0x10010000,
949                 0x00000004, 0x10000004, 0x00010004, 0x10010004,
950                 0x20000000, 0x30000000, 0x20010000, 0x30010000,
951                 0x20000004, 0x30000004, 0x20010004, 0x30010004,
952                 0x00100000, 0x10100000, 0x00110000, 0x10110000,
953                 0x00100004, 0x10100004, 0x00110004, 0x10110004,
954                 0x20100000, 0x30100000, 0x20110000, 0x30110000,
955                 0x20100004, 0x30100004, 0x20110004, 0x30110004,
956                 0x00001000, 0x10001000, 0x00011000, 0x10011000,
957                 0x00001004, 0x10001004, 0x00011004, 0x10011004,
958                 0x20001000, 0x30001000, 0x20011000, 0x30011000,
959                 0x20001004, 0x30001004, 0x20011004, 0x30011004,
960                 0x00101000, 0x10101000, 0x00111000, 0x10111000,
961                 0x00101004, 0x10101004, 0x00111004, 0x10111004,
962                 0x20101000, 0x30101000, 0x20111000, 0x30111000,
963                 0x20101004, 0x30101004, 0x20111004, 0x30111004,
964             },{
965                 /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
966                 0x00000000, 0x08000000, 0x00000008, 0x08000008,
967                 0x00000400, 0x08000400, 0x00000408, 0x08000408,
968                 0x00020000, 0x08020000, 0x00020008, 0x08020008,
969                 0x00020400, 0x08020400, 0x00020408, 0x08020408,
970                 0x00000001, 0x08000001, 0x00000009, 0x08000009,
971                 0x00000401, 0x08000401, 0x00000409, 0x08000409,
972                 0x00020001, 0x08020001, 0x00020009, 0x08020009,
973                 0x00020401, 0x08020401, 0x00020409, 0x08020409,
974                 0x02000000, 0x0A000000, 0x02000008, 0x0A000008,
975                 0x02000400, 0x0A000400, 0x02000408, 0x0A000408,
976                 0x02020000, 0x0A020000, 0x02020008, 0x0A020008,
977                 0x02020400, 0x0A020400, 0x02020408, 0x0A020408,
978                 0x02000001, 0x0A000001, 0x02000009, 0x0A000009,
979                 0x02000401, 0x0A000401, 0x02000409, 0x0A000409,
980                 0x02020001, 0x0A020001, 0x02020009, 0x0A020009,
981                 0x02020401, 0x0A020401, 0x02020409, 0x0A020409,
982             },{
983                 /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
984                 0x00000000, 0x00000100, 0x00080000, 0x00080100,
985                 0x01000000, 0x01000100, 0x01080000, 0x01080100,
986                 0x00000010, 0x00000110, 0x00080010, 0x00080110,
987                 0x01000010, 0x01000110, 0x01080010, 0x01080110,
988                 0x00200000, 0x00200100, 0x00280000, 0x00280100,
989                 0x01200000, 0x01200100, 0x01280000, 0x01280100,
990                 0x00200010, 0x00200110, 0x00280010, 0x00280110,
991                 0x01200010, 0x01200110, 0x01280010, 0x01280110,
992                 0x00000200, 0x00000300, 0x00080200, 0x00080300,
993                 0x01000200, 0x01000300, 0x01080200, 0x01080300,
994                 0x00000210, 0x00000310, 0x00080210, 0x00080310,
995                 0x01000210, 0x01000310, 0x01080210, 0x01080310,
996                 0x00200200, 0x00200300, 0x00280200, 0x00280300,
997                 0x01200200, 0x01200300, 0x01280200, 0x01280300,
998                 0x00200210, 0x00200310, 0x00280210, 0x00280310,
999                 0x01200210, 0x01200310, 0x01280210, 0x01280310,
1000            },{
1001                /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
1002                0x00000000, 0x04000000, 0x00040000, 0x04040000,
1003                0x00000002, 0x04000002, 0x00040002, 0x04040002,
1004                0x00002000, 0x04002000, 0x00042000, 0x04042000,
1005                0x00002002, 0x04002002, 0x00042002, 0x04042002,
1006                0x00000020, 0x04000020, 0x00040020, 0x04040020,
1007                0x00000022, 0x04000022, 0x00040022, 0x04040022,
1008                0x00002020, 0x04002020, 0x00042020, 0x04042020,
1009                0x00002022, 0x04002022, 0x00042022, 0x04042022,
1010                0x00000800, 0x04000800, 0x00040800, 0x04040800,
1011                0x00000802, 0x04000802, 0x00040802, 0x04040802,
1012                0x00002800, 0x04002800, 0x00042800, 0x04042800,
1013                0x00002802, 0x04002802, 0x00042802, 0x04042802,
1014                0x00000820, 0x04000820, 0x00040820, 0x04040820,
1015                0x00000822, 0x04000822, 0x00040822, 0x04040822,
1016                0x00002820, 0x04002820, 0x00042820, 0x04042820,
1017                0x00002822, 0x04002822, 0x00042822, 0x04042822,
1018            }
1019        };
1020
1021        private static final int SPtrans[][] = {
1022            {
1023                /* nibble 0 */
1024                0x00820200, 0x00020000, 0x80800000, 0x80820200,
1025                0x00800000, 0x80020200, 0x80020000, 0x80800000,
1026                0x80020200, 0x00820200, 0x00820000, 0x80000200,
1027                0x80800200, 0x00800000, 0x00000000, 0x80020000,
1028                0x00020000, 0x80000000, 0x00800200, 0x00020200,
1029                0x80820200, 0x00820000, 0x80000200, 0x00800200,
1030                0x80000000, 0x00000200, 0x00020200, 0x80820000,
1031                0x00000200, 0x80800200, 0x80820000, 0x00000000,
1032                0x00000000, 0x80820200, 0x00800200, 0x80020000,
1033                0x00820200, 0x00020000, 0x80000200, 0x00800200,
1034                0x80820000, 0x00000200, 0x00020200, 0x80800000,
1035                0x80020200, 0x80000000, 0x80800000, 0x00820000,
1036                0x80820200, 0x00020200, 0x00820000, 0x80800200,
1037                0x00800000, 0x80000200, 0x80020000, 0x00000000,
1038                0x00020000, 0x00800000, 0x80800200, 0x00820200,
1039                0x80000000, 0x80820000, 0x00000200, 0x80020200,
1040            },{
1041                /* nibble 1 */
1042                0x10042004, 0x00000000, 0x00042000, 0x10040000,
1043                0x10000004, 0x00002004, 0x10002000, 0x00042000,
1044                0x00002000, 0x10040004, 0x00000004, 0x10002000,
1045                0x00040004, 0x10042000, 0x10040000, 0x00000004,
1046                0x00040000, 0x10002004, 0x10040004, 0x00002000,
1047                0x00042004, 0x10000000, 0x00000000, 0x00040004,
1048                0x10002004, 0x00042004, 0x10042000, 0x10000004,
1049                0x10000000, 0x00040000, 0x00002004, 0x10042004,
1050                0x00040004, 0x10042000, 0x10002000, 0x00042004,
1051                0x10042004, 0x00040004, 0x10000004, 0x00000000,
1052                0x10000000, 0x00002004, 0x00040000, 0x10040004,
1053                0x00002000, 0x10000000, 0x00042004, 0x10002004,
1054                0x10042000, 0x00002000, 0x00000000, 0x10000004,
1055                0x00000004, 0x10042004, 0x00042000, 0x10040000,
1056                0x10040004, 0x00040000, 0x00002004, 0x10002000,
1057                0x10002004, 0x00000004, 0x10040000, 0x00042000,
1058            },{
1059                /* nibble 2 */
1060                0x41000000, 0x01010040, 0x00000040, 0x41000040,
1061                0x40010000, 0x01000000, 0x41000040, 0x00010040,
1062                0x01000040, 0x00010000, 0x01010000, 0x40000000,
1063                0x41010040, 0x40000040, 0x40000000, 0x41010000,
1064                0x00000000, 0x40010000, 0x01010040, 0x00000040,
1065                0x40000040, 0x41010040, 0x00010000, 0x41000000,
1066                0x41010000, 0x01000040, 0x40010040, 0x01010000,
1067                0x00010040, 0x00000000, 0x01000000, 0x40010040,
1068                0x01010040, 0x00000040, 0x40000000, 0x00010000,
1069                0x40000040, 0x40010000, 0x01010000, 0x41000040,
1070                0x00000000, 0x01010040, 0x00010040, 0x41010000,
1071                0x40010000, 0x01000000, 0x41010040, 0x40000000,
1072                0x40010040, 0x41000000, 0x01000000, 0x41010040,
1073                0x00010000, 0x01000040, 0x41000040, 0x00010040,
1074                0x01000040, 0x00000000, 0x41010000, 0x40000040,
1075                0x41000000, 0x40010040, 0x00000040, 0x01010000,
1076            },{
1077                /* nibble 3 */
1078                0x00100402, 0x04000400, 0x00000002, 0x04100402,
1079                0x00000000, 0x04100000, 0x04000402, 0x00100002,
1080                0x04100400, 0x04000002, 0x04000000, 0x00000402,
1081                0x04000002, 0x00100402, 0x00100000, 0x04000000,
1082                0x04100002, 0x00100400, 0x00000400, 0x00000002,
1083                0x00100400, 0x04000402, 0x04100000, 0x00000400,
1084                0x00000402, 0x00000000, 0x00100002, 0x04100400,
1085                0x04000400, 0x04100002, 0x04100402, 0x00100000,
1086                0x04100002, 0x00000402, 0x00100000, 0x04000002,
1087                0x00100400, 0x04000400, 0x00000002, 0x04100000,
1088                0x04000402, 0x00000000, 0x00000400, 0x00100002,
1089                0x00000000, 0x04100002, 0x04100400, 0x00000400,
1090                0x04000000, 0x04100402, 0x00100402, 0x00100000,
1091                0x04100402, 0x00000002, 0x04000400, 0x00100402,
1092                0x00100002, 0x00100400, 0x04100000, 0x04000402,
1093                0x00000402, 0x04000000, 0x04000002, 0x04100400,
1094            },{
1095                /* nibble 4 */
1096                0x02000000, 0x00004000, 0x00000100, 0x02004108,
1097                0x02004008, 0x02000100, 0x00004108, 0x02004000,
1098                0x00004000, 0x00000008, 0x02000008, 0x00004100,
1099                0x02000108, 0x02004008, 0x02004100, 0x00000000,
1100                0x00004100, 0x02000000, 0x00004008, 0x00000108,
1101                0x02000100, 0x00004108, 0x00000000, 0x02000008,
1102                0x00000008, 0x02000108, 0x02004108, 0x00004008,
1103                0x02004000, 0x00000100, 0x00000108, 0x02004100,
1104                0x02004100, 0x02000108, 0x00004008, 0x02004000,
1105                0x00004000, 0x00000008, 0x02000008, 0x02000100,
1106                0x02000000, 0x00004100, 0x02004108, 0x00000000,
1107                0x00004108, 0x02000000, 0x00000100, 0x00004008,
1108                0x02000108, 0x00000100, 0x00000000, 0x02004108,
1109                0x02004008, 0x02004100, 0x00000108, 0x00004000,
1110                0x00004100, 0x02004008, 0x02000100, 0x00000108,
1111                0x00000008, 0x00004108, 0x02004000, 0x02000008,
1112            },{
1113                /* nibble 5 */
1114                0x20000010, 0x00080010, 0x00000000, 0x20080800,
1115                0x00080010, 0x00000800, 0x20000810, 0x00080000,
1116                0x00000810, 0x20080810, 0x00080800, 0x20000000,
1117                0x20000800, 0x20000010, 0x20080000, 0x00080810,
1118                0x00080000, 0x20000810, 0x20080010, 0x00000000,
1119                0x00000800, 0x00000010, 0x20080800, 0x20080010,
1120                0x20080810, 0x20080000, 0x20000000, 0x00000810,
1121                0x00000010, 0x00080800, 0x00080810, 0x20000800,
1122                0x00000810, 0x20000000, 0x20000800, 0x00080810,
1123                0x20080800, 0x00080010, 0x00000000, 0x20000800,
1124                0x20000000, 0x00000800, 0x20080010, 0x00080000,
1125                0x00080010, 0x20080810, 0x00080800, 0x00000010,
1126                0x20080810, 0x00080800, 0x00080000, 0x20000810,
1127                0x20000010, 0x20080000, 0x00080810, 0x00000000,
1128                0x00000800, 0x20000010, 0x20000810, 0x20080800,
1129                0x20080000, 0x00000810, 0x00000010, 0x20080010,
1130            },{
1131                /* nibble 6 */
1132                0x00001000, 0x00000080, 0x00400080, 0x00400001,
1133                0x00401081, 0x00001001, 0x00001080, 0x00000000,
1134                0x00400000, 0x00400081, 0x00000081, 0x00401000,
1135                0x00000001, 0x00401080, 0x00401000, 0x00000081,
1136                0x00400081, 0x00001000, 0x00001001, 0x00401081,
1137                0x00000000, 0x00400080, 0x00400001, 0x00001080,
1138                0x00401001, 0x00001081, 0x00401080, 0x00000001,
1139                0x00001081, 0x00401001, 0x00000080, 0x00400000,
1140                0x00001081, 0x00401000, 0x00401001, 0x00000081,
1141                0x00001000, 0x00000080, 0x00400000, 0x00401001,
1142                0x00400081, 0x00001081, 0x00001080, 0x00000000,
1143                0x00000080, 0x00400001, 0x00000001, 0x00400080,
1144                0x00000000, 0x00400081, 0x00400080, 0x00001080,
1145                0x00000081, 0x00001000, 0x00401081, 0x00400000,
1146                0x00401080, 0x00000001, 0x00001001, 0x00401081,
1147                0x00400001, 0x00401080, 0x00401000, 0x00001001,
1148            },{
1149                /* nibble 7 */
1150                0x08200020, 0x08208000, 0x00008020, 0x00000000,
1151                0x08008000, 0x00200020, 0x08200000, 0x08208020,
1152                0x00000020, 0x08000000, 0x00208000, 0x00008020,
1153                0x00208020, 0x08008020, 0x08000020, 0x08200000,
1154                0x00008000, 0x00208020, 0x00200020, 0x08008000,
1155                0x08208020, 0x08000020, 0x00000000, 0x00208000,
1156                0x08000000, 0x00200000, 0x08008020, 0x08200020,
1157                0x00200000, 0x00008000, 0x08208000, 0x00000020,
1158                0x00200000, 0x00008000, 0x08000020, 0x08208020,
1159                0x00008020, 0x08000000, 0x00000000, 0x00208000,
1160                0x08200020, 0x08008020, 0x08008000, 0x00200020,
1161                0x08208000, 0x00000020, 0x00200020, 0x08008000,
1162                0x08208020, 0x00200000, 0x08200000, 0x08000020,
1163                0x00208000, 0x00008020, 0x08008020, 0x08200000,
1164                0x00000020, 0x08208000, 0x00208020, 0x00000000,
1165                0x08000000, 0x08200020, 0x00008000, 0x00208020
1166            }
1167        };
1168
1169        private static final int cov_2char[] = {
1170            0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
1171            0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44,
1172            0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C,
1173            0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
1174            0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62,
1175            0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
1176            0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
1177            0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
1178        };
1179
1180        private static final int byteToUnsigned(byte b) {
1181            return b & 0xFF;
1182        }
1183
1184        private static int fourBytesToInt(byte b[], int offset) {
1185            int value;
1186            value = byteToUnsigned(b[offset++]);
1187            value |= (byteToUnsigned(b[offset++]) << 8);
1188            value |= (byteToUnsigned(b[offset++]) << 16);
1189            value |= (byteToUnsigned(b[offset++]) << 24);
1190            return(value);
1191        }
1192
1193        private static final void intToFourBytes(int iValue, byte b[], int offset) {
1194            b[offset++] = (byte)((iValue) & 0xff);
1195            b[offset++] = (byte)((iValue >>> 8 ) & 0xff);
1196            b[offset++] = (byte)((iValue >>> 16) & 0xff);
1197            b[offset++] = (byte)((iValue >>> 24) & 0xff);
1198        }
1199
1200        private static final void PERM_OP(int a, int b, int n, int m, int results[]) {
1201            int t;
1202
1203            t = ((a >>> n) ^ b) & m;
1204            a ^= t << n;
1205            b ^= t;
1206
1207            results[0] = a;
1208            results[1] = b;
1209        }
1210
1211        private static final int HPERM_OP(int a, int n, int m) {
1212            int t;
1213
1214            t = ((a << (16 - n)) ^ a) & m;
1215            a = a ^ t ^ (t >>> (16 - n));
1216
1217            return(a);
1218        }
1219
1220        private static int [] des_set_key(byte key[]) {
1221            int schedule[] = new int[ITERATIONS * 2];
1222
1223            int c = fourBytesToInt(key, 0);
1224            int d = fourBytesToInt(key, 4);
1225
1226            int results[] = new int[2];
1227
1228            PERM_OP(d, c, 4, 0x0f0f0f0f, results);
1229            d = results[0]; c = results[1];
1230
1231            c = HPERM_OP(c, -2, 0xcccc0000);
1232            d = HPERM_OP(d, -2, 0xcccc0000);
1233
1234            PERM_OP(d, c, 1, 0x55555555, results);
1235            d = results[0]; c = results[1];
1236
1237            PERM_OP(c, d, 8, 0x00ff00ff, results);
1238            c = results[0]; d = results[1];
1239
1240            PERM_OP(d, c, 1, 0x55555555, results);
1241            d = results[0]; c = results[1];
1242
1243            d = (((d & 0x000000ff) << 16) | (d & 0x0000ff00) |
1244                 ((d & 0x00ff0000) >>> 16) | ((c & 0xf0000000) >>> 4));
1245            c &= 0x0fffffff;
1246
1247            int s, t;
1248            int j = 0;
1249
1250            for(int i = 0; i < ITERATIONS; i ++) {
1251                if(shifts2[i]) {
1252                    c = (c >>> 2) | (c << 26);
1253                    d = (d >>> 2) | (d << 26);
1254                } else {
1255                    c = (c >>> 1) | (c << 27);
1256                    d = (d >>> 1) | (d << 27);
1257                }
1258
1259                c &= 0x0fffffff;
1260                d &= 0x0fffffff;
1261
1262                s = skb[0][ (c ) & 0x3f ]|
1263                    skb[1][((c >>> 6) & 0x03) | ((c >>> 7) & 0x3c)]|
1264                    skb[2][((c >>> 13) & 0x0f) | ((c >>> 14) & 0x30)]|
1265                    skb[3][((c >>> 20) & 0x01) | ((c >>> 21) & 0x06) |
1266                           ((c >>> 22) & 0x38)];
1267
1268                t = skb[4][ (d ) & 0x3f ]|
1269                    skb[5][((d >>> 7) & 0x03) | ((d >>> 8) & 0x3c)]|
1270                    skb[6][ (d >>>15) & 0x3f ]|
1271                    skb[7][((d >>>21) & 0x0f) | ((d >>> 22) & 0x30)];
1272
1273                schedule[j++] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff;
1274                s = ((s >>> 16) | (t & 0xffff0000));
1275
1276                s = (s << 4) | (s >>> 28);
1277                schedule[j++] = s & 0xffffffff;
1278            }
1279            return(schedule);
1280        }
1281
1282        private static final int D_ENCRYPT(int L, int R, int S, int E0, int E1, int s[]) {
1283            int t, u, v;
1284
1285            v = R ^ (R >>> 16);
1286            u = v & E0;
1287            v = v & E1;
1288            u = (u ^ (u << 16)) ^ R ^ s[S];
1289            t = (v ^ (v << 16)) ^ R ^ s[S + 1];
1290            t = (t >>> 4) | (t << 28);
1291
1292            L ^= SPtrans[1][(t ) & 0x3f] |
1293                SPtrans[3][(t >>> 8) & 0x3f] |
1294                SPtrans[5][(t >>> 16) & 0x3f] |
1295                SPtrans[7][(t >>> 24) & 0x3f] |
1296                SPtrans[0][(u ) & 0x3f] |
1297                SPtrans[2][(u >>> 8) & 0x3f] |
1298                SPtrans[4][(u >>> 16) & 0x3f] |
1299                SPtrans[6][(u >>> 24) & 0x3f];
1300
1301            return(L);
1302        }
1303
1304        private static final int [] body(int schedule[], int Eswap0, int Eswap1) {
1305            int left = 0;
1306            int right = 0;
1307            int t = 0;
1308
1309            for(int j = 0; j < 25; j ++) {
1310                for(int i = 0; i < ITERATIONS * 2; i += 4) {
1311                    left = D_ENCRYPT(left, right, i, Eswap0, Eswap1, schedule);
1312                    right = D_ENCRYPT(right, left, i + 2, Eswap0, Eswap1, schedule);
1313                }
1314                t = left;
1315                left = right;
1316                right = t;
1317            }
1318
1319            t = right;
1320
1321            right = (left >>> 1) | (left << 31);
1322            left = (t >>> 1) | (t << 31);
1323
1324            left &= 0xffffffff;
1325            right &= 0xffffffff;
1326
1327            int results[] = new int[2];
1328
1329            PERM_OP(right, left, 1, 0x55555555, results);
1330            right = results[0]; left = results[1];
1331
1332            PERM_OP(left, right, 8, 0x00ff00ff, results);
1333            left = results[0]; right = results[1];
1334
1335            PERM_OP(right, left, 2, 0x33333333, results);
1336            right = results[0]; left = results[1];
1337
1338            PERM_OP(left, right, 16, 0x0000ffff, results);
1339            left = results[0]; right = results[1];
1340
1341            PERM_OP(right, left, 4, 0x0f0f0f0f, results);
1342            right = results[0]; left = results[1];
1343
1344            int out[] = new int[2];
1345
1346            out[0] = left; out[1] = right;
1347
1348            return(out);
1349        }
1350
1351        public static final String JavaDoc crypt(String JavaDoc salt, String JavaDoc original) {
1352            while(salt.length() < 2)
1353                salt += getSaltChar();
1354
1355            StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(" ");
1356
1357            char charZero = salt.charAt(0);
1358            char charOne = salt.charAt(1);
1359
1360            buffer.setCharAt(0, charZero);
1361            buffer.setCharAt(1, charOne);
1362
1363            int Eswap0 = con_salt[(int)charZero];
1364            int Eswap1 = con_salt[(int)charOne] << 4;
1365
1366            byte key[] = new byte[8];
1367
1368            for(int i = 0; i < key.length; i ++) {
1369                key[i] = (byte)0;
1370            }
1371
1372            for(int i = 0; i < key.length && i < original.length(); i ++) {
1373                int iChar = (int)original.charAt(i);
1374
1375                key[i] = (byte)(iChar << 1);
1376            }
1377
1378            int schedule[] = des_set_key(key);
1379            int out[] = body(schedule, Eswap0, Eswap1);
1380
1381            byte b[] = new byte[9];
1382
1383            intToFourBytes(out[0], b, 0);
1384            intToFourBytes(out[1], b, 4);
1385            b[8] = 0;
1386
1387            for(int i = 2, y = 0, u = 0x80; i < 13; i ++) {
1388                for(int j = 0, c = 0; j < 6; j ++) {
1389                    c <<= 1;
1390
1391                    if(((int)b[y] & u) != 0)
1392                        c |= 1;
1393
1394                    u >>>= 1;
1395
1396                    if(u == 0) {
1397                        y++;
1398                        u = 0x80;
1399                    }
1400                    buffer.setCharAt(i, (char)cov_2char[c]);
1401                }
1402            }
1403            return(buffer.toString());
1404        }
1405
1406        private static String JavaDoc getSaltChar() {
1407            return JavaCrypt.getSaltChar(1);
1408        }
1409
1410        private static String JavaDoc getSaltChar(int amount) {
1411            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1412            for(int i=amount;i>0;i--) {
1413                sb.append(theBaseSalts[(Math.abs(r_gen.nextInt())%64)]);
1414            }
1415            return sb.toString();
1416        }
1417
1418        public static boolean check(String JavaDoc theClear,String JavaDoc theCrypt) {
1419            String JavaDoc theTest = JavaCrypt.crypt(theCrypt.substring(0,2),theClear);
1420            return theTest.equals(theCrypt);
1421        }
1422
1423        public static String JavaDoc crypt(String JavaDoc theClear) {
1424            return JavaCrypt.crypt(getSaltChar(2),theClear);
1425        }
1426    }
1427
1428    /* RubyString aka rb_string_value */
1429    public static RubyString stringValue(IRubyObject object) {
1430        return (RubyString) (object instanceof RubyString ? object :
1431            object.convertType(RubyString.class, "String", "to_str"));
1432    }
1433
1434    /** rb_str_sub
1435     *
1436     */

1437    public IRubyObject sub(IRubyObject[] args, Block block) {
1438        return sub(args, false, block);
1439    }
1440
1441    /** rb_str_sub_bang
1442     *
1443     */

1444    public IRubyObject sub_bang(IRubyObject[] args, Block block) {
1445        return sub(args, true, block);
1446    }
1447
1448    private IRubyObject sub(IRubyObject[] args, boolean bang, Block block) {
1449        IRubyObject repl = getRuntime().getNil();
1450        boolean iter = false;
1451        ThreadContext tc = getRuntime().getCurrentContext();
1452
1453        if (args.length == 1 && block.isGiven()) {
1454            iter = true;
1455        } else if (args.length == 2) {
1456            repl = args[1];
1457        } else {
1458            throw getRuntime().newArgumentError("wrong number of arguments");
1459        }
1460        RubyRegexp pat = RubyRegexp.regexpValue(args[0]);
1461
1462        String JavaDoc intern = toString();
1463        boolean utf8 = pat.getCode() == KCode.UTF8;
1464
1465        if(utf8) {
1466            try {
1467                intern = new String JavaDoc(ByteList.plain(intern),"UTF8");
1468            } catch(Exception JavaDoc e) {
1469            }
1470        }
1471
1472        if (pat.search(intern, 0) >= 0) {
1473            RubyMatchData match = (RubyMatchData) tc.getBackref();
1474            RubyString newStr = match.pre_match();
1475            newStr.append(iter ? tc.yield(match.group(0), block) : pat.regsub(repl, match));
1476            newStr.append(match.post_match());
1477            newStr.setTaint(isTaint() || repl.isTaint());
1478            if(utf8) {
1479                try {
1480                    newStr.setValue(new String JavaDoc(ByteList.plain(newStr.toString().getBytes("UTF8"))));
1481                } catch(Exception JavaDoc e) {
1482                }
1483            }
1484            if (bang) {
1485                value = newStr.value;
1486                stringMutated();
1487                return this;
1488            }
1489
1490            return newStr;
1491        }
1492
1493        return bang ? getRuntime().getNil() : this;
1494    }
1495
1496    /** rb_str_gsub
1497     *
1498     */

1499    public IRubyObject gsub(IRubyObject[] args, Block block) {
1500        return gsub(args, false, block);
1501    }
1502
1503    /** rb_str_gsub_bang
1504     *
1505     */

1506    public IRubyObject gsub_bang(IRubyObject[] args, Block block) {
1507        return gsub(args, true, block);
1508    }
1509
1510    private IRubyObject gsub(IRubyObject[] args, boolean bang, Block block) {
1511        // TODO: improve implementation. this is _really_ slow
1512
IRubyObject repl = getRuntime().getNil();
1513        RubyMatchData match;
1514        boolean iter = false;
1515        if (args.length == 1 && block.isGiven()) {
1516            iter = true;
1517        } else if (args.length == 2) {
1518            repl = args[1];
1519        } else {
1520            throw getRuntime().newArgumentError("wrong number of arguments");
1521        }
1522        boolean taint = repl.isTaint();
1523        RubyRegexp pat = RubyRegexp.regexpValue(args[0]);
1524
1525        String JavaDoc str = toString();
1526        int beg = pat.search(str, 0);
1527        if (beg < 0) {
1528            return bang ? getRuntime().getNil() : dup();
1529        }
1530        ByteList sbuf = new ByteList(this.value.length());
1531        IRubyObject newStr;
1532        int offset = 0;
1533
1534        // Fix for JRUBY-97: Temporary fix pending
1535
// decision on UTF8-based string implementation.
1536
ThreadContext tc = getRuntime().getCurrentContext();
1537
1538        if(iter) {
1539            while (beg >= 0) {
1540                match = (RubyMatchData) tc.getBackref();
1541                sbuf.append(this.value,offset,beg-offset);
1542                newStr = tc.yield(match.group(0), block);
1543                taint |= newStr.isTaint();
1544                sbuf.append(newStr.objAsString().getByteList());
1545                offset = match.matchEndPosition();
1546                beg = pat.search(str, offset == beg ? beg + 1 : offset);
1547            }
1548        } else {
1549            RubyString r = stringValue(repl);
1550            while (beg >= 0) {
1551                match = (RubyMatchData) tc.getBackref();
1552                sbuf.append(this.value,offset,beg-offset);
1553                pat.regsub(r, match, sbuf);
1554                offset = match.matchEndPosition();
1555                beg = pat.search(str, offset == beg ? beg + 1 : offset);
1556            }
1557        }
1558
1559        sbuf.append(this.value,offset,this.value.length()-offset);
1560
1561        if (bang) {
1562            setTaint(isTaint() || taint);
1563            setValue(sbuf);
1564            return this;
1565        }
1566        RubyString result = newString(sbuf);
1567        result.setTaint(isTaint() || taint);
1568        return result;
1569    }
1570
1571    /** rb_str_index_m
1572     *
1573     */

1574    public IRubyObject index(IRubyObject[] args) {
1575        return index(args, false);
1576    }
1577
1578    /** rb_str_rindex_m
1579     *
1580     */

1581    public IRubyObject rindex(IRubyObject[] args) {
1582        return index(args, true);
1583    }
1584
1585    /**
1586     * @fixme may be a problem with pos when doing reverse searches
1587     */

1588    private IRubyObject index(IRubyObject[] args, boolean reverse) {
1589        //FIXME may be a problem with pos when doing reverse searches
1590
int pos = 0;
1591        if (reverse) {
1592            pos = value.length();
1593        }
1594        if (checkArgumentCount(args, 1, 2) == 2) {
1595            pos = RubyNumeric.fix2int(args[1]);
1596        }
1597        if (pos < 0) {
1598            pos += value.length();
1599            if (pos < 0) {
1600                return getRuntime().getNil();
1601            }
1602        }
1603        if (args[0] instanceof RubyRegexp) {
1604            int doNotLookPastIfReverse = pos;
1605
1606            // RubyRegexp doesn't (yet?) support reverse searches, so we
1607
// find all matches and use the last one--very inefficient.
1608
// XXX - find a better way
1609
pos = ((RubyRegexp) args[0]).search(toString(), reverse ? 0 : pos);
1610
1611            int dummy = pos;
1612            while (reverse && dummy > -1 && dummy <= doNotLookPastIfReverse) {
1613                pos = dummy;
1614                dummy = ((RubyRegexp) args[0]).search(toString(), pos + 1);
1615            }
1616        } else if (args[0] instanceof RubyString) {
1617            String JavaDoc sub = ((RubyString) args[0]).toString();
1618            StringBuffer JavaDoc sb = new StringBuffer JavaDoc(toString());
1619            pos = reverse ? sb.lastIndexOf(sub, pos) : sb.indexOf(sub, pos);
1620        } else if (args[0] instanceof RubyFixnum) {
1621            char c = (char) ((RubyFixnum) args[0]).getLongValue();
1622            pos = reverse ? toString().lastIndexOf(c, pos) : toString().indexOf(c, pos);
1623        } else {
1624            throw getRuntime().newArgumentError("wrong type of argument");
1625        }
1626
1627        return pos == -1 ? getRuntime().getNil() : getRuntime().newFixnum(pos);
1628    }
1629
1630    /* rb_str_substr */
1631    public IRubyObject substr(int beg, int len) {
1632        int length = value.length();
1633        if (len < 0 || beg > length) {
1634            return getRuntime().getNil();
1635        }
1636        if (beg < 0) {
1637            beg += length;
1638            if (beg < 0) {
1639                return getRuntime().getNil();
1640            }
1641        }
1642        int end = Math.min(length, beg + len);
1643        ByteList newValue = new ByteList(value, beg, end - beg);
1644        return newString(getRuntime(), newValue).infectBy(this);
1645    }
1646
1647    /* rb_str_replace */
1648    public IRubyObject replace(int beg, int len, RubyString replaceWith) {
1649        if (beg + len >= value.length()) {
1650            len = value.length() - beg;
1651        }
1652
1653        value.unsafeReplace(beg,len,replaceWith.value);
1654        stringMutated();
1655        return infectBy(replaceWith);
1656    }
1657
1658    /** rb_str_aref, rb_str_aref_m
1659     *
1660     */

1661    public IRubyObject aref(IRubyObject[] args) {
1662        if (checkArgumentCount(args, 1, 2) == 2) {
1663            if (args[0] instanceof RubyRegexp) {
1664                IRubyObject match = RubyRegexp.regexpValue(args[0]).match(toString(), 0);
1665                long idx = args[1].convertToInteger().getLongValue();
1666                getRuntime().getCurrentContext().setBackref(match);
1667                return RubyRegexp.nth_match((int) idx, match);
1668            }
1669            return substr(RubyNumeric.fix2int(args[0]), RubyNumeric.fix2int(args[1]));
1670        }
1671
1672        if (args[0] instanceof RubyRegexp) {
1673            return RubyRegexp.regexpValue(args[0]).search(toString(), 0) >= 0 ?
1674                RubyRegexp.last_match(getRuntime().getCurrentContext().getBackref()) :
1675                getRuntime().getNil();
1676        } else if (args[0] instanceof RubyString) {
1677            return toString().indexOf(stringValue(args[0]).toString()) != -1 ?
1678                args[0] : getRuntime().getNil();
1679        } else if (args[0] instanceof RubyRange) {
1680            long[] begLen = ((RubyRange) args[0]).getBeginLength(value.length(), true, false);
1681            return begLen == null ? getRuntime().getNil() :
1682                substr((int) begLen[0], (int) begLen[1]);
1683        }
1684        int idx = (int) args[0].convertToInteger().getLongValue();
1685        if (idx < 0) {
1686            idx += value.length();
1687        }
1688        if (idx < 0 || idx >= value.length()) {
1689            return getRuntime().getNil();
1690        } else {
1691            RubyFixnum result = getRuntime().newFixnum(value.get(idx) & 0xFF);
1692            return result;
1693        }
1694    }
1695
1696    /**
1697     * rb_str_subpat_set
1698     *
1699     */

1700    private void subpatSet(RubyRegexp regexp, int nth, IRubyObject repl) {
1701        int found = regexp.search(this.toString(), 0);
1702        if (found == -1) {
1703            throw getRuntime().newIndexError("regexp not matched");
1704        }
1705
1706        RubyMatchData match = (RubyMatchData) getRuntime().getCurrentContext()
1707                .getBackref();
1708
1709        if (nth >= match.getSize()) {
1710            throw getRuntime().newIndexError("index " + nth + " out of regexp");
1711        }
1712        if (nth < 0) {
1713            if (-nth >= match.getSize()) {
1714                throw getRuntime().newIndexError("index " + nth + " out of regexp");
1715            }
1716            nth += match.getSize();
1717        }
1718
1719        IRubyObject group = match.group(nth);
1720        if (getRuntime().getNil().equals(group)) {
1721            throw getRuntime().newIndexError(
1722                    "regexp group " + nth + " not matched");
1723        }
1724
1725        int beg = (int) match.begin(nth);
1726        int len = (int) (match.end(nth) - beg);
1727
1728        replace(beg, len, stringValue(repl));
1729
1730    }
1731
1732    /** rb_str_aset, rb_str_aset_m
1733     *
1734     */

1735    public IRubyObject aset(IRubyObject[] args) {
1736        testFrozen("class");
1737        int strLen = value.length();
1738        if (checkArgumentCount(args, 2, 3) == 3) {
1739            if (args[0] instanceof RubyFixnum) {
1740                RubyString repl = stringValue(args[2]);
1741                int beg = RubyNumeric.fix2int(args[0]);
1742                int len = RubyNumeric.fix2int(args[1]);
1743                if (len < 0) {
1744                    throw getRuntime().newIndexError("negative length");
1745                }
1746                if (beg < 0) {
1747                    beg += strLen;
1748                }
1749                if (beg < 0 || (beg > 0 && beg >= strLen)) {
1750                    throw getRuntime().newIndexError(
1751                            "string index out of bounds");
1752                }
1753                if (beg + len > strLen) {
1754                    len = strLen - beg;
1755                }
1756                replace(beg, len, repl);
1757                return repl;
1758            }
1759            if (args[0] instanceof RubyRegexp) {
1760                RubyString repl = stringValue(args[2]);
1761                int nth = RubyNumeric.fix2int(args[1]);
1762                subpatSet((RubyRegexp) args[0], nth, repl);
1763                return repl;
1764            }
1765        }
1766        if (args[0] instanceof RubyFixnum) { // RubyNumeric?
1767
int idx = RubyNumeric.fix2int(args[0]); // num2int?
1768
if (idx < 0) {
1769                idx += value.length();
1770            }
1771            if (idx < 0 || idx >= value.length()) {
1772                throw getRuntime().newIndexError("string index out of bounds");
1773            }
1774            if (args[1] instanceof RubyFixnum) {
1775                value.set(idx, (byte) RubyNumeric.fix2int(args[1]));
1776                stringMutated();
1777            } else {
1778                replace(idx, 1, stringValue(args[1]));
1779            }
1780            return args[1];
1781        }
1782        if (args[0] instanceof RubyRegexp) {
1783            sub_bang(args, null);
1784            return args[1];
1785        }
1786        if (args[0] instanceof RubyString) {
1787            RubyString orig = stringValue(args[0]);
1788            int beg = toString().indexOf(orig.toString());
1789            if (beg != -1) {
1790                replace(beg, orig.value.length(), stringValue(args[1]));
1791            }
1792            return args[1];
1793        }
1794        if (args[0] instanceof RubyRange) {
1795            long[] idxs = ((RubyRange) args[0]).getBeginLength(value.length(), true, true);
1796            replace((int) idxs[0], (int) idxs[1], stringValue(args[1]));
1797            return args[1];
1798        }
1799        throw getRuntime().newTypeError("wrong argument type");
1800    }
1801
1802    /** rb_str_slice_bang
1803     *
1804     */

1805    public IRubyObject slice_bang(IRubyObject[] args) {
1806        int argc = checkArgumentCount(args, 1, 2);
1807        IRubyObject[] newArgs = new IRubyObject[argc + 1];
1808        newArgs[0] = args[0];
1809        if (argc > 1) {
1810            newArgs[1] = args[1];
1811        }
1812        newArgs[argc] = newString("");
1813        IRubyObject result = aref(args);
1814        if (result.isNil()) {
1815            return result;
1816        }
1817        aset(newArgs);
1818        return result;
1819    }
1820
1821    public IRubyObject succ() {
1822        return ((RubyString) dup()).succ_bang();
1823    }
1824
1825    public IRubyObject succ_bang() {
1826        if (value.length() == 0) {
1827            return this;
1828        }
1829
1830        boolean alnumSeen = false;
1831        int pos = -1;
1832        int c = 0;
1833        int n = 0;
1834        for (int i = value.length() - 1; i >= 0; i--) {
1835            c = value.get(i) & 0xFF;
1836            if (isAlnum(c)) {
1837                alnumSeen = true;
1838                if ((isDigit(c) && c < '9') || (isLower(c) && c < 'z') || (isUpper(c) && c < 'Z')) {
1839                    value.set(i, (byte)(c + 1));
1840                    pos = -1;
1841                    break;
1842                }
1843                pos = i;
1844                n = isDigit(c) ? '0' : (isLower(c) ? 'a' : 'A');
1845                value.set(i, (byte)n);
1846            }
1847        }
1848        if (!alnumSeen) {
1849            for (int i = value.length() - 1; i >= 0; i--) {
1850                c = value.get(i);
1851                if (c < 0xff) {
1852                    value.set(i, (byte)(c + 1));
1853                    pos = -1;
1854                    break;
1855                }
1856                pos = i;
1857                n = '\u0001';
1858                value.set(i, 0);
1859            }
1860        }
1861        if (pos > -1) {
1862            // This represents left most digit in a set of incremented
1863
// values? Therefore leftmost numeric must be '1' and not '0'
1864
// 999 -> 1000, not 999 -> 0000. whereas chars should be
1865
// zzz -> aaaa
1866
value.prepend((byte)(isDigit(c) ? '1' : (isLower(c) ? 'a' : 'A')));
1867        }
1868        stringMutated();
1869        return this;
1870    }
1871
1872    /** rb_str_upto_m
1873     *
1874     */

1875    public IRubyObject upto(IRubyObject str, Block block) {
1876        return upto(str, false, block);
1877    }
1878
1879    /* rb_str_upto */
1880    public IRubyObject upto(IRubyObject str, boolean excl, Block block) {
1881        // alias 'this' to 'beg' for ease of comparison with MRI
1882
RubyString beg = this;
1883        RubyString end = stringValue(str);
1884
1885        int n = beg.cmp(end);
1886        if (n > 0 || (excl && n == 0)) {
1887            return beg;
1888        }
1889
1890        RubyString afterEnd = stringValue(end.succ());
1891        RubyString current = beg;
1892
1893        ThreadContext context = getRuntime().getCurrentContext();
1894        while (!current.equals(afterEnd)) {
1895            context.yield(current, block);
1896            if (!excl && current.equals(end)) {
1897                break;
1898            }
1899
1900            current = (RubyString) current.succ();
1901            if (excl && current.equals(end)) {
1902                break;
1903            }
1904            if (current.length().getLongValue() > end.length().getLongValue()) {
1905                break;
1906            }
1907        }
1908
1909        return beg;
1910
1911    }
1912
1913
1914    /** rb_str_include
1915     *
1916     */

1917    public RubyBoolean include(IRubyObject obj) {
1918        if (obj instanceof RubyFixnum) {
1919            int c = RubyNumeric.fix2int(obj);
1920            for (int i = 0; i < value.length(); i++) {
1921                if (value.get(i) == (byte)c) {
1922                    return getRuntime().getTrue();
1923                }
1924            }
1925            return getRuntime().getFalse();
1926        }
1927        String JavaDoc str = stringValue(obj).toString();
1928        return getRuntime().newBoolean(new StringBuffer JavaDoc(toString()).indexOf(str) != -1);
1929    }
1930
1931    /** rb_str_to_i
1932     *
1933     */

1934    public IRubyObject to_i(IRubyObject[] args) {
1935        long base = checkArgumentCount(args, 0, 1) == 0 ? 10 : ((RubyInteger) args[0].convertType(RubyInteger.class,
1936                "Integer", "to_i")).getLongValue();
1937        return RubyNumeric.str2inum(getRuntime(), this, (int) base);
1938    }
1939
1940    /** rb_str_oct
1941     *
1942     */

1943    public IRubyObject oct() {
1944        if (isEmpty()) {
1945            return getRuntime().newFixnum(0);
1946        }
1947
1948        int base = 8;
1949        String JavaDoc str = toString().trim();
1950        int pos = (str.charAt(0) == '-' || str.charAt(0) == '+') ? 1 : 0;
1951        if (str.indexOf("0x") == pos || str.indexOf("0X") == pos) {
1952            base = 16;
1953        } else if (str.indexOf("0b") == pos || str.indexOf("0B") == pos) {
1954            base = 2;
1955        }
1956        return RubyNumeric.str2inum(getRuntime(), this, base);
1957    }
1958
1959    /** rb_str_hex
1960     *
1961     */

1962    public IRubyObject hex() {
1963        return RubyNumeric.str2inum(getRuntime(), this, 16);
1964    }
1965
1966    /** rb_str_to_f
1967     *
1968     */

1969    public IRubyObject to_f() {
1970        return RubyNumeric.str2fnum(getRuntime(), this);
1971    }
1972
1973    /** rb_str_split
1974     *
1975     */

1976    public RubyArray split(IRubyObject[] args) {
1977        RubyRegexp pattern;
1978        boolean isWhitespace = false;
1979
1980        // get the pattern based on args
1981
if (args.length == 0) {
1982            isWhitespace = true;
1983            pattern = RubyRegexp.newRegexp(getRuntime(), "\\s+", 0, null);
1984        } else if (args[0] instanceof RubyRegexp) {
1985            // Even if we have whitespace-only explicit regexp we do not
1986
// mark it as whitespace. Apparently, this is so ruby can
1987
// still get the do not ignore the front match behavior.
1988
pattern = RubyRegexp.regexpValue(args[0]);
1989        } else {
1990            String JavaDoc stringPattern = RubyString.stringValue(args[0]).toString();
1991
1992            if (stringPattern.equals(" ")) {
1993                isWhitespace = true;
1994                pattern = RubyRegexp.newRegexp(getRuntime(), "\\s+", 0, null);
1995            } else {
1996                pattern = RubyRegexp.newRegexp(getRuntime(), RubyRegexp.escapeSpecialChars(stringPattern), 0, null);
1997            }
1998        }
1999
2000        int limit = getLimit(args);
2001        String JavaDoc[] result = null;
2002        // attempt to convert to Unicode when appropriate
2003
String JavaDoc splitee = toString();
2004        boolean unicodeSuccess = false;
2005        if (getRuntime().getKCode() == KCode.UTF8) {
2006            // We're in UTF8 mode; try to convert the string to UTF8, but fall back on raw bytes if we can't decode
2007
// TODO: all this decoder and charset stuff could be centralized...in KCode perhaps?
2008
CharsetDecoder JavaDoc decoder = Charset.forName("UTF-8").newDecoder();
2009            decoder.onMalformedInput(CodingErrorAction.REPORT);
2010            decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
2011
2012            try {
2013                splitee = decoder.decode(ByteBuffer.wrap(value.bytes())).toString();
2014                unicodeSuccess = true;
2015            } catch (CharacterCodingException JavaDoc cce) {
2016                // ignore, just use the unencoded string
2017
}
2018        }
2019
2020
2021        if (limit == 1) {
2022            result = new String JavaDoc[] {splitee};
2023        } else {
2024            List JavaDoc list = new ArrayList JavaDoc();
2025            int numberOfHits = 0;
2026            int stringLength = splitee.length();
2027
2028            Pattern JavaDoc pat = pattern.getPattern();
2029            Matcher JavaDoc matt = pat.matcher(splitee);
2030
2031            int startOfCurrentHit = 0;
2032            int endOfCurrentHit = 0;
2033            String JavaDoc group = null;
2034
2035            // TODO: There's a fast path in here somewhere that could just use Pattern.split
2036

2037            if (matt.find()) {
2038                // we have matches, proceed
2039

2040                // end of current hit is start of first match
2041
endOfCurrentHit = matt.start();
2042
2043                // filter out starting whitespace matches for non-regex whitespace splits
2044
if (endOfCurrentHit != 0 || !isWhitespace) {
2045                    // not a non-regex whitespace split, proceed
2046

2047                    numberOfHits++;
2048
2049                    // skip first positive lookahead match
2050
if (matt.end() != 0) {
2051
2052                        // add the first hit
2053
list.add(splitee.substring(startOfCurrentHit, endOfCurrentHit));
2054
2055                        // add any matched groups found while splitting the first hit
2056
for (int groupIndex = 1; groupIndex <= matt.groupCount(); groupIndex++) {
2057                            group = matt.group(groupIndex);
2058                            list.add(group);
2059                        }
2060                    }
2061                }
2062
2063                // advance start to the end of the current hit
2064
startOfCurrentHit = matt.end();
2065
2066                // ensure we haven't exceeded the hit limit
2067
if (numberOfHits + 1 != limit) {
2068                    // loop over the remaining matches
2069
while (matt.find()) {
2070                        // end of current hit is start of the next match
2071
endOfCurrentHit = matt.start();
2072                        numberOfHits++;
2073
2074                        // add the current hit
2075
list.add(splitee.substring(startOfCurrentHit, endOfCurrentHit));
2076
2077                        // add any matched groups found while splitting the current hit
2078
for (int groupIndex = 1; groupIndex <= matt.groupCount(); groupIndex++) {
2079                            group = matt.group(groupIndex);
2080                            list.add(group);
2081                        }
2082
2083                        // advance start to the end of the current hit
2084
startOfCurrentHit = matt.end();
2085                    }
2086                }
2087            }
2088
2089            if (numberOfHits == 0) {
2090                // we found no hits, use the entire string
2091
list.add(splitee);
2092            } else if (startOfCurrentHit <= stringLength) {
2093                // our last match ended before the end of the string, add remainder
2094
list.add(splitee.substring(startOfCurrentHit, stringLength));
2095            }
2096
2097            // Remove trailing whitespace when limit 0 is specified
2098
if (limit == 0 && list.size() > 0) {
2099                for (int size = list.size() - 1;
2100                size >= 0 && ((String JavaDoc) list.get(size)).length() == 0; size--) {
2101                    list.remove(size);
2102                }
2103            }
2104
2105            result = (String JavaDoc[])list.toArray(new String JavaDoc[list.size()]);
2106        }
2107
2108        // convert arraylist of strings to RubyArray of RubyStrings
2109
RubyArray resultArray = getRuntime().newArray(result.length);
2110
2111        for (int i = 0; i < result.length; i++) {
2112            RubyString string = getRuntime().newString(result[i]);
2113
2114            // if we're in unicode mode and successfully converted to a unicode string before,
2115
// make sure to keep unicode in the split values
2116
if (unicodeSuccess && getRuntime().getKCode() == KCode.UTF8) {
2117                string.setUnicodeValue(result[i]);
2118            }
2119
2120            resultArray.append(string);
2121        }
2122
2123        return resultArray;
2124    }
2125
2126    private static int getLimit(IRubyObject[] args) {
2127        if (args.length == 2) {
2128            return RubyNumeric.fix2int(args[1]);
2129        }
2130        return 0;
2131    }
2132
2133    /** rb_str_scan
2134     *
2135     */

2136    public IRubyObject scan(IRubyObject arg, Block block) {
2137        RubyRegexp pattern = RubyRegexp.regexpValue(arg);
2138        int start = 0;
2139        ThreadContext tc = getRuntime().getCurrentContext();
2140
2141        // Fix for JRUBY-97: Temporary fix pending
2142
// decision on UTF8-based string implementation.
2143
// Move toString() call outside loop.
2144
String JavaDoc toString = toString();
2145
2146        if (!block.isGiven()) {
2147            RubyArray ary = getRuntime().newArray();
2148            while (pattern.search(toString, start) != -1) {
2149                RubyMatchData md = (RubyMatchData) tc.getBackref();
2150                ary.append(md.getSize() == 1 ? md.group(0) : md.subseq(1, md.getSize()));
2151
2152                if (md.matchEndPosition() == md.matchStartPosition()) {
2153                    start++;
2154                } else {
2155                    start = md.matchEndPosition();
2156                }
2157            }
2158            return ary;
2159        }
2160
2161        while (pattern.search(toString, start) != -1) {
2162            RubyMatchData md = (RubyMatchData) tc.getBackref();
2163            tc.yield(md.getSize() == 1 ? md.group(0) : md.subseq(1, md.getSize()), block);
2164
2165            if (md.matchEndPosition() == md.matchStartPosition()) {
2166                start++;
2167            } else {
2168                start = md.matchEndPosition();
2169            }
2170        }
2171        return this;
2172    }
2173
2174    private IRubyObject justify(IRubyObject [] args, boolean leftJustify) {
2175        checkArgumentCount(args, 1, 2);
2176        int length = RubyNumeric.fix2int(args[0]);
2177        if (length <= value.length()) {
2178            return dup();
2179        }
2180
2181        String JavaDoc paddingArg;
2182
2183        if (args.length == 2) {
2184            paddingArg = args[1].convertToString().toString();
2185            if (paddingArg.length() == 0) {
2186                throw getRuntime().newArgumentError("zero width padding");
2187            }
2188        } else {
2189            paddingArg = " ";
2190        }
2191
2192        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(length);
2193        String JavaDoc thisStr = toString();
2194
2195        if (leftJustify) {
2196            sbuf.append(thisStr);
2197        }
2198
2199        // Add n whole paddings
2200
int whole = (length - thisStr.length()) / paddingArg.length();
2201        for (int w = 0; w < whole; w++ ) {
2202            sbuf.append(paddingArg);
2203        }
2204
2205        // Add fractional amount of padding to make up difference
2206
int fractionalLength = (length - thisStr.length()) % paddingArg.length();
2207        if (fractionalLength > 0) {
2208            sbuf.append(paddingArg.substring(0, fractionalLength));
2209        }
2210
2211        if (!leftJustify) {
2212            sbuf.append(thisStr);
2213        }
2214
2215        RubyString ret = newString(sbuf.toString());
2216
2217        if (args.length == 2) {
2218            ret.infectBy(args[1]);
2219        }
2220
2221        return ret;
2222    }
2223
2224    /** rb_str_ljust
2225     *
2226     */

2227    public IRubyObject ljust(IRubyObject [] args) {
2228        return justify(args, true);
2229    }
2230
2231    /** rb_str_rjust
2232     *
2233     */

2234    public IRubyObject rjust(IRubyObject [] args) {
2235        return justify(args, false);
2236    }
2237
2238    public IRubyObject center(IRubyObject[] args) {
2239        checkArgumentCount(args, 1, 2);
2240        int len = RubyNumeric.fix2int(args[0]);
2241        String JavaDoc pad = args.length == 2 ? args[1].convertToString().toString() : " ";
2242        int strLen = value.length();
2243        int padLen = pad.length();
2244
2245        if (padLen == 0) {
2246            throw getRuntime().newArgumentError("zero width padding");
2247        }
2248        if (len <= strLen) {
2249            return dup();
2250        }
2251        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(len);
2252        int lead = (len - strLen) / 2;
2253        for (int i = 0; i < lead; i++) {
2254            sbuf.append(pad.charAt(i % padLen));
2255        }
2256        sbuf.append(getValue());
2257        int remaining = len - (lead + strLen);
2258        for (int i = 0; i < remaining; i++) {
2259            sbuf.append(pad.charAt(i % padLen));
2260        }
2261        return newString(sbuf.toString());
2262    }
2263
2264    public IRubyObject chop() {
2265        RubyString newString = (RubyString) dup();
2266
2267        newString.chop_bang();
2268
2269        return newString;
2270    }
2271
2272    public IRubyObject chop_bang() {
2273        int end = value.length() - 1;
2274
2275        if (end < 0) {
2276            return getRuntime().getNil();
2277        }
2278
2279        if ((value.get(end) & 0xFF) == '\n') {
2280            if (end > 0 && (value.get(end-1) & 0xFF) == '\r') {
2281                end--;
2282            }
2283        }
2284
2285        value.length(end);
2286        stringMutated();
2287        return this;
2288    }
2289
2290    public RubyString chomp(IRubyObject[] args) {
2291        RubyString result = (RubyString) dup();
2292
2293        result.chomp_bang(args);
2294
2295        return result;
2296    }
2297
2298    /**
2299     * rb_str_chomp_bang
2300     *
2301     * In the common case, removes CR and LF characters in various ways depending on the value of
2302     * the optional args[0].
2303     * If args.length==0 removes one instance of CR, CRLF or LF from the end of the string.
2304     * If args.length>0 and args[0] is "\n" then same behaviour as args.length==0 .
2305     * If args.length>0 and args[0] is "" then removes trailing multiple LF or CRLF (but no CRs at
2306     * all(!)).
2307     * @param args See method description.
2308     */

2309    public IRubyObject chomp_bang(IRubyObject[] args) {
2310        if (isEmpty()) {
2311            return getRuntime().getNil();
2312        }
2313
2314        // Separator (a.k.a. $/) can be overriden by the -0 (zero) command line option
2315
String JavaDoc separator = (args.length == 0) ?
2316            getRuntime().getGlobalVariables().get("$/").asSymbol() : args[0].asSymbol();
2317
2318        if (separator.equals(DEFAULT_RS)) {
2319            int end = value.length() - 1;
2320            int removeCount = 0;
2321
2322            if (end < 0) {
2323                return getRuntime().getNil();
2324            }
2325
2326            if ((value.get(end) & 0xFF) == '\n') {
2327                removeCount++;
2328                if (end > 0 && (value.get(end-1) & 0xFF) == '\r') {
2329                    removeCount++;
2330                }
2331            } else if ((value.get(end) & 0xFF) == '\r') {
2332                removeCount++;
2333            }
2334
2335            if (removeCount == 0) {
2336                return getRuntime().getNil();
2337            }
2338
2339            value.length(end - removeCount + 1);
2340            stringMutated();
2341            return this;
2342        }
2343
2344        if (separator.length() == 0) {
2345            int end = value.length() - 1;
2346            int removeCount = 0;
2347            while(end - removeCount >= 0 && (value.get(end - removeCount) & 0xFF) == '\n') {
2348                removeCount++;
2349                if (end - removeCount >= 0 && (value.get(end - removeCount) & 0xFF) == '\r') {
2350                    removeCount++;
2351                }
2352            }
2353            if (removeCount == 0) {
2354                return getRuntime().getNil();
2355            }
2356
2357            value.length(end - removeCount + 1);
2358            stringMutated();
2359            return this;
2360        }
2361
2362        // Uncommon case of str.chomp!("xxx")
2363
if (toString().endsWith(separator)) {
2364            value.length(value.length() - separator.length());
2365            stringMutated();
2366            return this;
2367        }
2368        return getRuntime().getNil();
2369    }
2370
2371    public IRubyObject lstrip() {
2372        return newString(getRuntime(), lstripInternal());
2373    }
2374
2375    public ByteList lstripInternal() {
2376        int length = value.length();
2377        int i = 0;
2378
2379        for (; i < length; i++) {
2380            if (!Character.isWhitespace(value.charAt(i))) {
2381                break;
2382            }
2383        }
2384
2385        return new ByteList(value, i, value.length() - i);
2386    }
2387
2388    public IRubyObject lstrip_bang() {
2389        ByteList newBytes = lstripInternal();
2390        if (value.equals(newBytes)) {
2391            return getRuntime().getNil();
2392        }
2393        value = newBytes;
2394        stringMutated();
2395
2396        return this;
2397    }
2398
2399    public IRubyObject rstrip() {
2400        return newString(getRuntime(), rstripInternal());
2401    }
2402
2403    public ByteList rstripInternal() {
2404        int i = value.length() - 1;
2405
2406        for (; i >= 0; i--) {
2407            if (!Character.isWhitespace(value.charAt(i))) {
2408                break;
2409            }
2410        }
2411
2412        return new ByteList(value, 0, i + 1);
2413    }
2414
2415    public IRubyObject rstrip_bang() {
2416        ByteList newBytes = rstripInternal();
2417        if (value.equals(newBytes)) {
2418            return getRuntime().getNil();
2419        }
2420        value = newBytes;
2421        stringMutated();
2422
2423        return this;
2424    }
2425
2426    /** rb_str_strip
2427     *
2428     */

2429    public IRubyObject strip() {
2430        if (isEmpty()) {
2431            return dup();
2432        }
2433        ByteList bytes = stripInternal();
2434        if (bytes == null) {
2435            return newString(getRuntime(), (ByteList)value.clone());
2436        }
2437
2438        return newString(getRuntime(), bytes);
2439    }
2440
2441    /** rb_str_strip_bang
2442     *
2443     */

2444    public IRubyObject strip_bang() {
2445        if (isEmpty()) {
2446            return getRuntime().getNil();
2447        }
2448        ByteList bytes = stripInternal();
2449        if (bytes == null) {
2450            return getRuntime().getNil();
2451        }
2452        value = bytes;
2453        stringMutated();
2454        return this;
2455    }
2456
2457    public ByteList stripInternal() {
2458        int head = 0;
2459        while (head < value.length() && Character.isWhitespace(value.charAt(head))) head++;
2460        int tail = value.length() - 1;
2461        while (tail > head && Character.isWhitespace(value.charAt(tail))) tail--;
2462
2463        if (head == 0 && tail == value.length() - 1) {
2464            return null;
2465        }
2466
2467        if (head <= tail) {
2468            return new ByteList(value, head, tail - head + 1);
2469        }
2470        
2471        if (head > tail) {
2472            return new ByteList();
2473        }
2474
2475        return null;
2476    }
2477
2478    private static String JavaDoc expandTemplate(String JavaDoc spec, boolean invertOK) {
2479        int len = spec.length();
2480        if (len <= 1) {
2481            return spec;
2482        }
2483        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
2484        int pos = (invertOK && spec.charAt(0) == '^') ? 1 : 0;
2485        while (pos < len) {
2486            char c1 = spec.charAt(pos), c2;
2487            if (pos + 2 < len && spec.charAt(pos + 1) == '-') {
2488                if ((c2 = spec.charAt(pos + 2)) > c1) {
2489                    for (int i = c1; i <= c2; i++) {
2490                        sbuf.append((char) i);
2491                    }
2492                }
2493                pos += 3;
2494                continue;
2495            }
2496            sbuf.append(c1);
2497            pos++;
2498        }
2499        return sbuf.toString();
2500    }
2501
2502    private String JavaDoc setupTable(String JavaDoc[] specs) {
2503        int[] table = new int[256];
2504        int numSets = 0;
2505        for (int i = 0; i < specs.length; i++) {
2506            String JavaDoc template = expandTemplate(specs[i], true);
2507            boolean invert = specs[i].length() > 1 && specs[i].charAt(0) == '^';
2508            for (int j = 0; j < 256; j++) {
2509                if (template.indexOf(j) != -1) {
2510                    table[j] += invert ? -1 : 1;
2511                }
2512            }
2513            numSets += invert ? 0 : 1;
2514        }
2515        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
2516        for (int k = 0; k < 256; k++) {
2517            if (table[k] == numSets) {
2518                sbuf.append((char) k);
2519            }
2520        }
2521        return sbuf.toString();
2522    }
2523
2524    /** rb_str_count
2525     *
2526     */

2527    public IRubyObject count(IRubyObject[] args) {
2528        int argc = checkArgumentCount(args, 1, -1);
2529        String JavaDoc[] specs = new String JavaDoc[argc];
2530        for (int i = 0; i < argc; i++) {
2531            specs[i] = stringValue(args[i]).toString();
2532        }
2533        String JavaDoc table = setupTable(specs);
2534
2535        int count = 0;
2536        for (int j = 0; j < value.length(); j++) {
2537            if (table.indexOf(value.get(j) & 0xFF) != -1) {
2538                count++;
2539            }
2540        }
2541        return getRuntime().newFixnum(count);
2542    }
2543
2544    private ByteList getDelete(IRubyObject[] args) {
2545        int argc = checkArgumentCount(args, 1, -1);
2546        String JavaDoc[] specs = new String JavaDoc[argc];
2547        for (int i = 0; i < argc; i++) {
2548            specs[i] = stringValue(args[i]).toString();
2549        }
2550        String JavaDoc table = setupTable(specs);
2551
2552        int strLen = value.length();
2553        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(strLen);
2554        int c;
2555        for (int j = 0; j < strLen; j++) {
2556            c = value.get(j) & 0xFF;
2557            if (table.indexOf(c) == -1) {
2558                sbuf.append((char)c);
2559            }
2560        }
2561        return new ByteList(stringToBytes(sbuf.toString()));
2562    }
2563
2564    /** rb_str_delete
2565     *
2566     */

2567    public IRubyObject delete(IRubyObject[] args) {
2568        return newString(getRuntime(), getDelete(args)).infectBy(this);
2569    }
2570
2571    /** rb_str_delete_bang
2572     *
2573     */

2574    public IRubyObject delete_bang(IRubyObject[] args) {
2575        ByteList newStr = getDelete(args);
2576        if (value.equals(newStr)) {
2577            return getRuntime().getNil();
2578        }
2579        value = newStr;
2580        stringMutated();
2581        return this;
2582    }
2583
2584    private ByteList getSqueeze(IRubyObject[] args) {
2585        int argc = args.length;
2586        String JavaDoc[] specs = null;
2587        if (argc > 0) {
2588            specs = new String JavaDoc[argc];
2589            for (int i = 0; i < argc; i++) {
2590                specs[i] = stringValue(args[i]).toString();
2591            }
2592        }
2593        String JavaDoc table = specs == null ? null : setupTable(specs);
2594
2595        int strLen = value.length();
2596        if (strLen <= 1) {
2597            return value;
2598        }
2599        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(strLen);
2600        int c1 = value.get(0) & 0xFF;
2601        sbuf.append((char)c1);
2602        int c2;
2603        for (int j = 1; j < strLen; j++) {
2604            c2 = value.get(j) & 0xFF;
2605            if (c2 == c1 && (table == null || table.indexOf(c2) != -1)) {
2606                continue;
2607            }
2608            sbuf.append((char)c2);
2609            c1 = c2;
2610        }
2611        return new ByteList(stringToBytes(sbuf.toString()));
2612    }
2613
2614    /** rb_str_squeeze
2615     *
2616     */

2617    public IRubyObject squeeze(IRubyObject[] args) {
2618        return newString(getRuntime(), getSqueeze(args)).infectBy(this);
2619    }
2620
2621    /** rb_str_squeeze_bang
2622     *
2623     */

2624    public IRubyObject squeeze_bang(IRubyObject[] args) {
2625        ByteList newStr = getSqueeze(args);
2626        if (value.equals(newStr)) {
2627            return getRuntime().getNil();
2628        }
2629        value = newStr;
2630        stringMutated();
2631        return this;
2632    }
2633
2634    private ByteList tr(IRubyObject search, IRubyObject replace, boolean squeeze) {
2635        String JavaDoc srchSpec = search.convertToString().toString();
2636        String JavaDoc srch = expandTemplate(srchSpec, true);
2637        if (srchSpec.startsWith("^")) {
2638            StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(256);
2639            for (int i = 0; i < 256; i++) {
2640                char c = (char) i;
2641                if (srch.indexOf(c) == -1) {
2642                    sbuf.append(c);
2643                }
2644            }
2645            srch = sbuf.toString();
2646        }
2647        String JavaDoc repl = expandTemplate(replace.convertToString().toString(), false);
2648
2649        int strLen = value.length();
2650        if (strLen == 0 || srch.length() == 0) {
2651            return value;
2652        }
2653        int repLen = repl.length();
2654        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(strLen);
2655        int last = -1;
2656        for (int i = 0; i < strLen; i++) {
2657            int cs = value.get(i) & 0xFF;
2658            int pos = srch.indexOf(cs);
2659            if (pos == -1) {
2660                sbuf.append((char)cs);
2661                last = -1;
2662            } else if (repLen > 0) {
2663                char cr = repl.charAt(Math.min(pos, repLen - 1));
2664                if (squeeze && cr == last) {
2665                    continue;
2666                }
2667                sbuf.append((char)cr);
2668                last = cr;
2669            }
2670        }
2671        return new ByteList(stringToBytes(sbuf.toString()));
2672    }
2673
2674    /** rb_str_tr
2675     *
2676     */

2677    public IRubyObject tr(IRubyObject search, IRubyObject replace) {
2678        return newString(getRuntime(), tr(search, replace, false)).infectBy(this);
2679    }
2680
2681    /** rb_str_tr_bang
2682     *
2683     */

2684    public IRubyObject tr_bang(IRubyObject search, IRubyObject replace) {
2685        ByteList newStr = tr(search, replace, false);
2686        if (value.equals(newStr)) {
2687            return getRuntime().getNil();
2688        }
2689        value = newStr;
2690        stringMutated();
2691        return this;
2692    }
2693
2694    /** rb_str_tr_s
2695     *
2696     */

2697    public IRubyObject tr_s(IRubyObject search, IRubyObject replace) {
2698        return newString(getRuntime(), tr(search, replace, true)).infectBy(this);
2699    }
2700
2701    /** rb_str_tr_s_bang
2702     *
2703     */

2704    public IRubyObject tr_s_bang(IRubyObject search, IRubyObject replace) {
2705        ByteList newStr = tr(search, replace, true);
2706        if (value.equals(newStr)) {
2707            return getRuntime().getNil();
2708        }
2709        value = newStr;
2710        stringMutated();
2711        return this;
2712    }
2713
2714    /** rb_str_each_line
2715     *
2716     */

2717    public IRubyObject each_line(IRubyObject[] args, Block block) {
2718        int strLen = value.length();
2719        if (strLen == 0) {
2720            return this;
2721        }
2722        String JavaDoc sep;
2723        if (checkArgumentCount(args, 0, 1) == 1) {
2724            sep = RubyRegexp.escapeSpecialChars(stringValue(args[0]).toString());
2725        } else {
2726            sep = RubyRegexp.escapeSpecialChars(getRuntime().getGlobalVariables().get("$/").asSymbol());
2727        }
2728        if (sep == null) {
2729            sep = "(?:\\n|\\r\\n?)";
2730        } else if (sep.length() == 0) {
2731            sep = "(?:\\n|\\r\\n?){2,}";
2732        }
2733        RubyRegexp pat = RubyRegexp.newRegexp(getRuntime(), ".*?" + sep, RubyRegexp.RE_OPTION_MULTILINE, null);
2734        int start = 0;
2735        ThreadContext tc = getRuntime().getCurrentContext();
2736
2737        // Fix for JRUBY-97: Temporary fix pending
2738
// decision on UTF8-based string implementation.
2739
// Move toString() call outside loop.
2740
String JavaDoc toString = toString();
2741
2742        while (pat.search(toString, start) != -1) {
2743            RubyMatchData md = (RubyMatchData) tc.getBackref();
2744            tc.yield(md.group(0), block);
2745            start = md.matchEndPosition();
2746        }
2747        if (start < strLen) {
2748            tc.yield(substr(start, strLen - start), block);
2749        }
2750        return this;
2751    }
2752
2753    /**
2754     * rb_str_each_byte
2755     */

2756    public RubyString each_byte(Block block) {
2757        int lLength = value.length();
2758        ThreadContext context = getRuntime().getCurrentContext();
2759        for (int i = 0; i < lLength; i++) {
2760            context.yield(getRuntime().newFixnum(value.get(i) & 0xFF), block);
2761        }
2762        return this;
2763    }
2764
2765    /** rb_str_intern
2766     *
2767     */

2768    public RubySymbol intern() {
2769        String JavaDoc s = toString();
2770        if (s.equals("")) {
2771            throw getRuntime().newArgumentError("interning empty string");
2772        }
2773        if (s.indexOf('\0') >= 0) {
2774            throw getRuntime().newArgumentError("symbol string may not contain '\\0'");
2775        }
2776        return RubySymbol.newSymbol(getRuntime(), toString());
2777    }
2778
2779    public RubySymbol to_sym() {
2780        return intern();
2781    }
2782
2783    public RubyInteger sum(IRubyObject[] args) {
2784        long bitSize = 16;
2785        if (args.length > 0) {
2786            bitSize = ((RubyInteger) args[0].convertType(RubyInteger.class,
2787                    "Integer", "to_i")).getLongValue();
2788        }
2789
2790        long result = 0;
2791        for (int i = 0; i < value.length(); i++) {
2792            result += value.get(i) & 0xFF;
2793        }
2794        return getRuntime().newFixnum(bitSize == 0 ? result : result % (long) Math.pow(2, bitSize));
2795    }
2796
2797    public static RubyString unmarshalFrom(UnmarshalStream input) throws java.io.IOException JavaDoc {
2798        RubyString result = newString(input.getRuntime(), input.unmarshalString());
2799        input.registerLinkTarget(result);
2800        return result;
2801    }
2802
2803    /**
2804     * @see org.jruby.util.Pack#unpack
2805     */

2806    public RubyArray unpack(IRubyObject obj) {
2807        return Pack.unpack(getRuntime(), this.value, stringValue(obj).value);
2808    }
2809
2810    /**
2811     * Mutator for internal string representation.
2812     *
2813     * @param value The new java.lang.String this RubyString should encapsulate
2814     */

2815    public void setValue(CharSequence JavaDoc value) {
2816        this.value.replace(ByteList.plain(value));
2817        stringMutated();
2818    }
2819
2820    public void setValue(ByteList value) {
2821        this.value = value;
2822        stringMutated();
2823    }
2824
2825    public CharSequence JavaDoc getValue() {
2826        return toString();
2827    }
2828
2829    public String JavaDoc getUnicodeValue() {
2830        try {
2831            return new String JavaDoc(value.bytes,0,value.realSize, "UTF8");
2832        } catch (Exception JavaDoc e) {
2833            throw new RuntimeException JavaDoc("Something's seriously broken with encodings", e);
2834        }
2835    }
2836
2837    public void setUnicodeValue(String JavaDoc newValue) {
2838        try {
2839            value.replace(newValue.getBytes("UTF8"));
2840            stringMutated();
2841        } catch (Exception JavaDoc e) {
2842            throw new RuntimeException JavaDoc("Something's seriously broken with encodings", e);
2843        }
2844    }
2845
2846    public byte[] getBytes() {
2847        return value.bytes();
2848    }
2849
2850    public ByteList getByteList() {
2851        return value;
2852    }
2853}
2854
Popular Tags