2014-09-24 23:52:39 +04:00
|
|
|
/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
|
'use strict';
|
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
/**
|
2014-09-30 21:00:20 +04:00
|
|
|
* string.js: Native implementations of String and StringBuffer.
|
2014-09-26 21:57:15 +04:00
|
|
|
*
|
2014-09-30 21:00:20 +04:00
|
|
|
* Methods are defined in the same order as the Java source.
|
|
|
|
* Any missing methods have been noted in comments.
|
2014-09-26 21:57:15 +04:00
|
|
|
*/
|
|
|
|
|
2014-09-30 21:00:20 +04:00
|
|
|
//################################################################
|
|
|
|
// java.lang.String (manipulated via the 'str' property)
|
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
function isString(obj) {
|
|
|
|
return obj && obj.str !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
//****************************************************************
|
|
|
|
// Constructors
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.<init>.()V", function() {
|
|
|
|
this.str = "";
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.<init>.(Ljava/lang/String;)V", function(jStr) {
|
|
|
|
if (!jStr) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
2014-09-24 23:52:39 +04:00
|
|
|
}
|
2014-09-26 21:57:15 +04:00
|
|
|
this.str = jStr.str;
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.<init>.([C)V", function(chars) {
|
|
|
|
if (!chars) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
|
|
|
}
|
|
|
|
this.str = util.fromJavaChars(chars);
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.<init>.([CII)V", function(value, offset, count) {
|
|
|
|
if (offset < 0 || count < 0 || offset > value.length - count) {
|
|
|
|
throw new JavaException("java/lang/IndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
this.str = util.fromJavaChars(value, offset, count);
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
// Several constructors below share this implementation:
|
|
|
|
function constructFromByteArray(bytes, off, len, enc) {
|
|
|
|
enc = normalizeEncoding(enc);
|
|
|
|
bytes = bytes.subarray(off, off + len);
|
|
|
|
try {
|
|
|
|
this.str = new TextDecoder(enc).decode(bytes);
|
|
|
|
} catch(e) {
|
|
|
|
throw new JavaException("java/io/UnsupportedEncodingException");
|
2014-09-24 23:52:39 +04:00
|
|
|
}
|
2014-09-26 21:57:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Override.simple(
|
|
|
|
"java/lang/String.<init>.([BIILjava/lang/String;)V",
|
|
|
|
function(bytes, off, len, enc) {
|
|
|
|
constructFromByteArray.call(this, bytes, off, len, enc.str);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple(
|
|
|
|
"java/lang/String.<init>.([BLjava/lang/String;)V",
|
|
|
|
function(bytes, enc) {
|
|
|
|
constructFromByteArray.call(this, bytes, 0, bytes.length, enc.str);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple(
|
|
|
|
"java/lang/String.<init>.([BII)V",
|
|
|
|
function(bytes, offset, len) {
|
|
|
|
constructFromByteArray.call(this, bytes, offset, len, "UTF-8");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple(
|
|
|
|
"java/lang/String.<init>.([B)V",
|
|
|
|
function(bytes) {
|
|
|
|
constructFromByteArray.call(this, bytes, 0, bytes.length, "UTF-8");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple(
|
|
|
|
"java/lang/String.<init>.(Ljava/lang/StringBuffer;)V",
|
2014-09-30 21:00:20 +04:00
|
|
|
function(jBuffer) {
|
|
|
|
this.str = util.fromJavaChars(jBuffer.buf, 0, jBuffer.count);
|
2014-09-26 21:57:15 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple(
|
|
|
|
"java/lang/String.<init>.(II[C)V",
|
|
|
|
function(offset, count, value) {
|
|
|
|
this.str = util.fromJavaChars(value, offset, count);
|
|
|
|
});
|
|
|
|
|
|
|
|
//****************************************************************
|
|
|
|
// Methods
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.length.()I", function() {
|
2014-09-29 21:04:09 +04:00
|
|
|
return this.str.length;
|
|
|
|
});
|
2014-09-26 21:57:15 +04:00
|
|
|
|
|
|
|
Override.simple("java/lang/String.charAt.(I)C", function(index) {
|
2014-09-29 21:04:09 +04:00
|
|
|
if (index < 0 || index >= this.str.length) {
|
|
|
|
throw new JavaException("java/lang/IndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
return this.str.charCodeAt(index);
|
|
|
|
});
|
2014-09-26 21:57:15 +04:00
|
|
|
|
|
|
|
Override.simple("java/lang/String.getChars.(II[CI)V", function(srcBegin, srcEnd, dst, dstBegin) {
|
|
|
|
if (srcBegin < 0 || srcEnd > this.str.length || srcBegin > srcEnd ||
|
|
|
|
dstBegin + (srcEnd - srcBegin) > dst.length || dstBegin < 0) {
|
|
|
|
throw new JavaException("java/lang/IndexOutOfBoundsException");
|
|
|
|
}
|
2014-09-30 21:00:20 +04:00
|
|
|
dst.set(util.stringToCharArray(this.str.substring(srcBegin, srcEnd)), dstBegin);
|
2014-09-26 21:57:15 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
// Java returns encodings like "UTF_16"; TextEncoder and friends only
|
|
|
|
// like hyphens, not underscores.
|
|
|
|
function normalizeEncoding(enc) {
|
|
|
|
var encoding = enc.toLowerCase().replace(/_/g, '-');
|
|
|
|
if (encoding == "utf-16") {
|
|
|
|
encoding = "utf-16be"; // Java defaults to big-endian, JS to little-endian.
|
|
|
|
}
|
|
|
|
return encoding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.getBytes.(Ljava/lang/String;)[B", function(jEnc) {
|
|
|
|
try {
|
|
|
|
var encoding = normalizeEncoding(jEnc.str);
|
|
|
|
return new Int8Array(new TextEncoder(encoding).encode(this.str));
|
|
|
|
} catch (e) {
|
|
|
|
throw new JavaException("java/io/UnsupportedEncodingException");
|
2014-09-24 23:52:39 +04:00
|
|
|
}
|
2014-09-26 21:57:15 +04:00
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.getBytes.()[B", function() {
|
|
|
|
return new Int8Array(new TextEncoder("utf-8").encode(this.str));
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.equals.(Ljava/lang/Object;)Z", function(anObject) {
|
|
|
|
return (isString(anObject) && anObject.str === this.str) ? 1 : 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.equalsIgnoreCase.(Ljava/lang/String;)Z", function(anotherString) {
|
|
|
|
return (isString(anotherString) && (anotherString.str.toLowerCase() === this.str.toLowerCase())) ? 1 : 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.compareTo.(Ljava/lang/String;)I", function(anotherString) {
|
|
|
|
// Sadly, JS String doesn't have a compareTo() method, so we must
|
|
|
|
// replicate the Java algorithm. (There is String.localeCompare, but
|
|
|
|
// that only returns {-1, 0, 1}, not a distance measure, which this
|
|
|
|
// requires.
|
|
|
|
var len1 = this.str.length;
|
|
|
|
var len2 = anotherString.str.length;
|
|
|
|
var n = Math.min(len1, len2);
|
|
|
|
var v1 = this.str;
|
|
|
|
var v2 = anotherString.str;
|
|
|
|
for (var k = 0; k < n; k++) {
|
|
|
|
var c1 = v1.charCodeAt(k);
|
|
|
|
var c2 = v2.charCodeAt(k);
|
|
|
|
if (c1 != c2) {
|
|
|
|
return c1 - c2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return len1 - len2;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.regionMatches.(ZILjava/lang/String;II)Z", function(ignoreCase, toffset, other, ooffset, len) {
|
|
|
|
var a = (ignoreCase ? this.str.toLowerCase() : this.str);
|
|
|
|
var b = (ignoreCase ? other.str.toLowerCase() : other.str);
|
|
|
|
return a.substr(toffset, len) === b.substr(ooffset, len);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.startsWith.(Ljava/lang/String;I)Z", function(prefix, toffset) {
|
|
|
|
return this.str.substr(toffset, prefix.str.length) === prefix.str;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.startsWith.(Ljava/lang/String;)Z", function(prefix) {
|
|
|
|
return this.str.substr(0, prefix.str.length) === prefix.str;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.endsWith.(Ljava/lang/String;)Z", function(suffix) {
|
|
|
|
return this.str.indexOf(suffix.str, this.str.length - suffix.str.length) !== -1;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.hashCode.()I", function() {
|
2014-09-24 23:52:39 +04:00
|
|
|
var hash = 0;
|
2014-09-26 21:57:15 +04:00
|
|
|
for (var i = 0; i < this.str.length; i++) {
|
|
|
|
hash = Math.imul(31, hash) + this.str.charCodeAt(i) | 0;
|
2014-09-24 23:52:39 +04:00
|
|
|
}
|
2014-09-26 21:57:15 +04:00
|
|
|
return hash;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.indexOf.(I)I", function(ch) {
|
|
|
|
return this.str.indexOf(String.fromCharCode(ch));
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.indexOf.(II)I", function(ch, fromIndex) {
|
|
|
|
return this.str.indexOf(String.fromCharCode(ch), fromIndex);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.lastIndexOf.(I)I", function(ch) {
|
|
|
|
return this.str.lastIndexOf(String.fromCharCode(ch));
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.lastIndexOf.(II)I", function(ch, fromIndex) {
|
|
|
|
return this.str.lastIndexOf(String.fromCharCode(ch), fromIndex);
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.indexOf.(Ljava/lang/String;)I", function(s) {
|
|
|
|
return this.str.indexOf(s.str);
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.indexOf.(Ljava/lang/String;I)I", function(s, fromIndex) {
|
|
|
|
return this.str.indexOf(s.str, fromIndex);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.substring.(I)Ljava/lang/String;", function(beginIndex) {
|
|
|
|
if (beginIndex < 0 || beginIndex > this.str.length) {
|
|
|
|
throw new JavaException("java/lang/IndexOutOfBoundsException");
|
2014-09-24 23:52:39 +04:00
|
|
|
}
|
2014-09-26 21:57:15 +04:00
|
|
|
return this.str.substring(beginIndex);
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.substring.(II)Ljava/lang/String;", function(beginIndex, endIndex) {
|
|
|
|
if (beginIndex < 0 || endIndex > this.str.length || beginIndex > endIndex) {
|
|
|
|
throw new JavaException("java/lang/IndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
return this.str.substring(beginIndex, endIndex);
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.concat.(Ljava/lang/String;)Ljava/lang/String;", function(s) {
|
|
|
|
return this.str + s.str;
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
// via MDN:
|
|
|
|
function escapeRegExp(str) {
|
|
|
|
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
|
|
}
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.replace.(CC)Ljava/lang/String;", function(oldChar, newChar) {
|
|
|
|
// Using a RegExp here to replace all matches of oldChar, rather than just the first.
|
|
|
|
return this.str.replace(
|
|
|
|
new RegExp(escapeRegExp(String.fromCharCode(oldChar)), "g"),
|
|
|
|
String.fromCharCode(newChar));
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.toLowerCase.()Ljava/lang/String;", function() {
|
|
|
|
return this.str.toLowerCase();
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.toUpperCase.()Ljava/lang/String;", function() {
|
|
|
|
return this.str.toUpperCase();
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.trim.()Ljava/lang/String;", function() {
|
|
|
|
// Java's String.trim() removes any character <= ASCII 32;
|
|
|
|
// JavaScript's only removes a few whitespacey chars.
|
2014-09-24 23:52:39 +04:00
|
|
|
var start = 0;
|
2014-09-26 21:57:15 +04:00
|
|
|
var end = this.str.length;
|
|
|
|
while (start < end && this.str.charCodeAt(start) <= 32) {
|
2014-09-24 23:52:39 +04:00
|
|
|
start++;
|
|
|
|
}
|
2014-09-26 21:57:15 +04:00
|
|
|
while (start < end && this.str.charCodeAt(end - 1) <= 32) {
|
2014-09-24 23:52:39 +04:00
|
|
|
end--;
|
|
|
|
}
|
2014-09-29 21:04:09 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
return this.str.substring(start, end);
|
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.toString.()Ljava/lang/String;", function() {
|
|
|
|
return this; // Note: returning "this" so that we keep the same object.
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.toCharArray.()[C", function() {
|
2014-09-30 21:00:20 +04:00
|
|
|
return util.stringToCharArray(this.str);
|
2014-09-26 21:57:15 +04:00
|
|
|
});
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
//****************************************************************
|
|
|
|
// String.valueOf() for various types
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
// NOTE: String.valueOf(Object) left in Java to avoid having to call
|
|
|
|
// back into Java for Object.toString().
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.valueOf.([C)Ljava/lang/String;", function(chars) {
|
|
|
|
if (!chars) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
2014-09-24 23:52:39 +04:00
|
|
|
}
|
2014-09-26 21:57:15 +04:00
|
|
|
return util.fromJavaChars(chars);
|
|
|
|
}, { static: true });
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.valueOf.([CII)Ljava/lang/String;", function(chars, offset, count) {
|
|
|
|
if (!chars) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
|
|
|
}
|
|
|
|
return util.fromJavaChars(chars, offset, count);
|
|
|
|
}, { static: true });
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.valueOf.(Z)Ljava/lang/String;", function(bool) {
|
|
|
|
return bool ? "true" : "false";
|
|
|
|
}, { static: true });
|
2014-09-24 23:52:39 +04:00
|
|
|
|
2014-09-26 21:57:15 +04:00
|
|
|
Override.simple("java/lang/String.valueOf.(C)Ljava/lang/String;", function(ch) {
|
|
|
|
return String.fromCharCode(ch);
|
|
|
|
}, { static: true });
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.valueOf.(I)Ljava/lang/String;", function(n) {
|
|
|
|
return n.toString();
|
|
|
|
}, { static: true });
|
|
|
|
|
|
|
|
Override.simple("java/lang/String.valueOf.(J)Ljava/lang/String;", function(n, _) {
|
|
|
|
// This function takes a dummy second argument, since we're taking a
|
|
|
|
// Long and need to pop two items off the stack.
|
|
|
|
return n.toString();
|
|
|
|
}, { static: true });
|
|
|
|
|
|
|
|
|
|
|
|
// String.valueOf(float) and String.valueOf(double) have been left in
|
2014-09-30 21:00:20 +04:00
|
|
|
// Java for now, as they require support for complex formatting rules.
|
|
|
|
// Additionally, their tests check for coverage of nuanced things like
|
|
|
|
// positive zero vs. negative zero, which we don't currently support.
|
2014-09-26 21:57:15 +04:00
|
|
|
|
2014-09-30 21:00:20 +04:00
|
|
|
var internedStrings = new Map();
|
2014-09-26 21:57:15 +04:00
|
|
|
|
2014-09-30 21:00:20 +04:00
|
|
|
Native["java/lang/String.intern.()Ljava/lang/String;"] = function(ctx, stack) {
|
|
|
|
var javaString = stack.pop();
|
|
|
|
var string = util.fromJavaString(javaString);
|
2014-09-26 21:57:15 +04:00
|
|
|
|
2014-09-30 21:00:20 +04:00
|
|
|
var internedString = internedStrings.get(string);
|
|
|
|
|
|
|
|
if (internedString) {
|
|
|
|
stack.push(internedString);
|
|
|
|
} else {
|
|
|
|
internedStrings.set(string, javaString);
|
|
|
|
stack.push(javaString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//################################################################
|
|
|
|
// java.lang.StringBuffer (manipulated via the 'buf' property)
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.<init>.()V", function() {
|
|
|
|
this.buf = new Uint16Array(16); // Initial buffer size: 16, per the Java implementation.
|
|
|
|
this.count = 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.<init>.(I)V", function(length) {
|
|
|
|
if (length < 0) {
|
|
|
|
throw new JavaException("java/lang/NegativeArraySizeException");
|
|
|
|
}
|
|
|
|
this.buf = new Uint16Array(length);
|
|
|
|
this.count = 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.<init>.(Ljava/lang/String;)V", function(jStr) {
|
|
|
|
var stringBuf = util.stringToCharArray(jStr.str);
|
2014-09-30 23:08:48 +04:00
|
|
|
this.buf = new Uint16Array(stringBuf.length + 16); // Add 16, per the Java implementation.
|
2014-09-30 21:00:20 +04:00
|
|
|
this.buf.set(stringBuf, 0);
|
|
|
|
this.count = stringBuf.length;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.length.()I", function() {
|
|
|
|
return this.count;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.capacity.()I", function() {
|
|
|
|
return this.buf.length;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.copy.()V", function() {
|
|
|
|
// We don't support copying (there's no need unless we also support shared buffers).
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expand capacity to max(minCapacity, (capacity + 1) * 2).
|
|
|
|
*
|
|
|
|
* @this StringBuffer
|
|
|
|
* @param {number} minCapacity
|
|
|
|
*/
|
|
|
|
function expandCapacity(minCapacity) {
|
2014-09-30 23:08:48 +04:00
|
|
|
var newCapacity = (this.buf.length + 1) << 1;
|
|
|
|
if (minCapacity > newCapacity) {
|
2014-09-30 21:00:20 +04:00
|
|
|
newCapacity = minCapacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
var oldBuf = this.buf;
|
|
|
|
this.buf = new Uint16Array(newCapacity);
|
|
|
|
this.buf.set(oldBuf, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.ensureCapacity.(I)V", function(minCapacity) {
|
|
|
|
if (this.buf.length < minCapacity) {
|
|
|
|
expandCapacity.call(this, minCapacity);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-09-30 23:08:48 +04:00
|
|
|
// StringBuffer.expandCapacity is private and not needed with these overrides.
|
2014-09-30 21:00:20 +04:00
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.setLength.(I)V", function(newLength) {
|
|
|
|
if (newLength < 0) {
|
|
|
|
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newLength > this.buf.length) {
|
|
|
|
expandCapacity.call(this, newLength);
|
|
|
|
}
|
2014-10-01 01:49:06 +04:00
|
|
|
for (; this.count < newLength; this.count++) {
|
|
|
|
this.buf[this.count] = '\0';
|
|
|
|
}
|
2014-09-30 21:00:20 +04:00
|
|
|
this.count = newLength;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.charAt.(I)C", function(index) {
|
|
|
|
if (index < 0 || index >= this.count) {
|
|
|
|
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
return this.buf[index];
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.getChars.(II[CI)V", function(srcBegin, srcEnd, dst, dstBegin) {
|
2014-10-01 01:49:06 +04:00
|
|
|
if (srcBegin < 0 || srcEnd < 0 || srcEnd > this.count || srcBegin > srcEnd) {
|
2014-09-30 21:00:20 +04:00
|
|
|
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
|
|
|
|
}
|
2014-10-01 01:49:06 +04:00
|
|
|
if (dstBegin + (srcEnd - srcBegin) > dst.length || dstBegin < 0) {
|
|
|
|
throw new JavaException("java/lang/ArrayIndexOutOfBoundsException");
|
|
|
|
}
|
2014-09-30 21:00:20 +04:00
|
|
|
dst.set(this.buf.subarray(srcBegin, srcEnd), dstBegin);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.setCharAt.(IC)V", function(index, ch) {
|
|
|
|
if (index < 0 || index >= this.count) {
|
|
|
|
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
this.buf[index] = ch;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Append `data`, which should be either a JS String or a Uint16Array.
|
|
|
|
* Data must not be null.
|
|
|
|
*
|
|
|
|
* @this StringBuffer
|
|
|
|
* @param {Uint16Array|string} data
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
function stringBufferAppend(data) {
|
|
|
|
if (data == null) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
|
|
|
}
|
|
|
|
if (!(data instanceof Uint16Array)) {
|
|
|
|
data = util.stringToCharArray(data);
|
|
|
|
}
|
|
|
|
if (this.buf.length < this.count + data.length) {
|
|
|
|
expandCapacity.call(this, this.count + data.length);
|
|
|
|
}
|
|
|
|
this.buf.set(data, this.count);
|
|
|
|
this.count += data.length;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringBuffer.append(java.lang.Object) left in Java to avoid Object.toString().
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.append.(Ljava/lang/String;)Ljava/lang/StringBuffer;", function(jStr) {
|
|
|
|
return stringBufferAppend.call(this, jStr ? jStr.str : "null");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.append.([C)Ljava/lang/StringBuffer;", function(chars) {
|
2014-10-01 01:49:06 +04:00
|
|
|
if (chars == null) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
|
|
|
}
|
2014-09-30 21:00:20 +04:00
|
|
|
return stringBufferAppend.call(this, chars);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.append.([CII)Ljava/lang/StringBuffer;", function(chars, offset, length) {
|
2014-10-01 01:49:06 +04:00
|
|
|
if (chars == null) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
|
|
|
}
|
|
|
|
if (offset < 0 || offset + length > chars.length) {
|
|
|
|
throw new JavaException("java/lang/ArrayIndexOutOfBoundsException");
|
2014-09-30 21:00:20 +04:00
|
|
|
}
|
|
|
|
return stringBufferAppend.call(this, chars.subarray(offset, offset + length));
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.append.(Z)Ljava/lang/StringBuffer;", function(bool) {
|
|
|
|
return stringBufferAppend.call(this, bool ? "true" : "false");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.append.(C)Ljava/lang/StringBuffer;", function(ch) {
|
|
|
|
if (this.buf.length < this.count + 1) {
|
|
|
|
expandCapacity.call(this, this.count + 1);
|
|
|
|
}
|
|
|
|
this.buf[this.count++] = ch;
|
|
|
|
return this;
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.append.(I)Ljava/lang/StringBuffer;", function(n) {
|
|
|
|
return stringBufferAppend.call(this, n + "");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.append.(J)Ljava/lang/StringBuffer;", function(n, _) {
|
|
|
|
return stringBufferAppend.call(this, n + "");
|
|
|
|
});
|
|
|
|
|
|
|
|
// StringBuffer.append(float) left in Java (see String.valueOf(float) above).
|
|
|
|
|
|
|
|
// StringBuffer.append(double) left in Java (see String.valueOf(double) above).
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete characters between [start, end).
|
|
|
|
*
|
|
|
|
* @this StringBuffer
|
|
|
|
* @param {number} start
|
|
|
|
* @param {number} end
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
function stringBufferDelete(start, end) {
|
|
|
|
if (start < 0) {
|
|
|
|
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
if (end > this.count) {
|
|
|
|
end = this.count;
|
|
|
|
}
|
|
|
|
if (start > end) {
|
|
|
|
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
|
|
|
|
var len = end - start;
|
|
|
|
if (len > 0) {
|
|
|
|
// When Gecko 34 is released, we can use TypedArray.copyWithin() instead.
|
2014-10-01 01:49:06 +04:00
|
|
|
this.buf.set(this.buf.subarray(end, this.count), start);
|
2014-09-30 21:00:20 +04:00
|
|
|
this.count -= len;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.delete.(II)Ljava/lang/StringBuffer;",
|
|
|
|
stringBufferDelete);
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.deleteCharAt.(I)Ljava/lang/StringBuffer;", function(index) {
|
|
|
|
if (index < 0 || index >= this.count) {
|
|
|
|
throw new JavaException("java/lang/StringIndexOutOfBoundsException");
|
|
|
|
}
|
|
|
|
return stringBufferDelete.call(this, index, index + 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Insert `data` at the given `offset`.
|
|
|
|
*
|
|
|
|
* @this StringBuffer
|
|
|
|
* @param {number} offset
|
|
|
|
* @param {Uint16Array|string} data
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
function stringBufferInsert(offset, data) {
|
|
|
|
if (data == null) {
|
|
|
|
throw new JavaException("java/lang/NullPointerException");
|
|
|
|
}
|
|
|
|
if (offset < 0 || offset > this.count) {
|
2014-10-01 01:49:06 +04:00
|
|
|
throw new JavaException("java/lang/ArrayIndexOutOfBoundsException");
|
2014-09-30 21:00:20 +04:00
|
|
|
}
|
|
|
|
if (!(data instanceof Uint16Array)) {
|
|
|
|
data = util.stringToCharArray(data);
|
|
|
|
}
|
|
|
|
if (this.buf.length < this.count + data.length) {
|
|
|
|
expandCapacity.call(this, this.count + data.length);
|
|
|
|
}
|
|
|
|
// When Gecko 34 is released, we can use TypedArray.copyWithin() instead.
|
|
|
|
this.buf.set(this.buf.subarray(offset, this.count), offset + data.length);
|
|
|
|
this.buf.set(data, offset);
|
|
|
|
this.count += data.length;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringBuffer.insert(Object) left in Java (for String.valueOf()).
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.insert.(ILjava/lang/String;)Ljava/lang/StringBuffer;", function(offset, jStr) {
|
|
|
|
return stringBufferInsert.call(this, offset, jStr ? jStr.str : "null");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.insert.(I[C)Ljava/lang/StringBuffer;", function(offset, chars) {
|
|
|
|
return stringBufferInsert.call(this, offset, chars);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.insert.(IZ)Ljava/lang/StringBuffer;", function(offset, bool) {
|
|
|
|
return stringBufferInsert.call(this, offset, bool ? "true" : "false");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.insert.(IC)Ljava/lang/StringBuffer;", function(offset, ch) {
|
|
|
|
return stringBufferInsert.call(this, offset, String.fromCharCode(ch));
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.insert.(II)Ljava/lang/StringBuffer;", function(offset, n) {
|
|
|
|
return stringBufferInsert.call(this, offset, n + "");
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.insert.(IJ)Ljava/lang/StringBuffer;", function(offset, n, _) {
|
|
|
|
return stringBufferInsert.call(this, offset, n + "");
|
|
|
|
});
|
|
|
|
|
|
|
|
// StringBuffer.insert(float) left in Java.
|
|
|
|
|
|
|
|
// StringBuffer.insert(double) left in Java.
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.reverse.()Ljava/lang/StringBuffer;", function() {
|
|
|
|
var buf = this.buf;
|
|
|
|
for (var i = 0, j = this.count - 1; i < j; i++, j--) {
|
|
|
|
var tmp = buf[i];
|
|
|
|
buf[i] = buf[j];
|
|
|
|
buf[j] = tmp;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
});
|
2014-09-26 21:57:15 +04:00
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.toString.()Ljava/lang/String;", function() {
|
2014-09-30 21:00:20 +04:00
|
|
|
return util.fromJavaChars(this.buf, 0, this.count);
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.setShared.()V", function() {
|
|
|
|
// Our StringBuffers are never shared. Everyone gets their very own!
|
|
|
|
});
|
|
|
|
|
|
|
|
Override.simple("java/lang/StringBuffer.getValue.()[C", function() {
|
|
|
|
// In theory, this method should only be called by String (which
|
|
|
|
// we've overridden to not do), so it should never be called. In any
|
|
|
|
// case, mutating this buf would have the same effect here as it
|
|
|
|
// would in Java.
|
|
|
|
return this.buf;
|
2014-09-26 21:57:15 +04:00
|
|
|
});
|