summaryrefslogtreecommitdiff
path: root/js/src/Ice/StringUtil.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/Ice/StringUtil.js')
-rw-r--r--js/src/Ice/StringUtil.js490
1 files changed, 490 insertions, 0 deletions
diff --git a/js/src/Ice/StringUtil.js b/js/src/Ice/StringUtil.js
new file mode 100644
index 00000000000..f7866d3d92a
--- /dev/null
+++ b/js/src/Ice/StringUtil.js
@@ -0,0 +1,490 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ require("Ice/Debug");
+
+ var Ice = global.Ice || {};
+
+ var Debug = Ice.Debug;
+
+ var StringUtil = {};
+
+ //
+ // Return the index of the first character in str to
+ // appear in match, starting from start. Returns -1 if none is
+ // found.
+ //
+ StringUtil.findFirstOf = function(str, match, start)
+ {
+ start = start === undefined ? 0 : start;
+
+ var len = str.length;
+ for(var i = start; i < len; i++)
+ {
+ var ch = str.charAt(i);
+ if(match.indexOf(ch) != -1)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+ //
+ // Return the index of the first character in str which does
+ // not appear in match, starting from start. Returns -1 if none is
+ // found.
+ //
+ StringUtil.findFirstNotOf = function(str, match, start)
+ {
+ start = start === undefined ? 0 : start;
+
+ var len = str.length;
+ for(var i = start; i < len; i++)
+ {
+ var ch = str.charAt(i);
+ if(match.indexOf(ch) == -1)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+ //
+ // Write the byte b as an escape sequence if it isn't a printable ASCII
+ // character and append the escape sequence to sb. Additional characters
+ // that should be escaped can be passed in special. If b is any of these
+ // characters, b is preceded by a backslash in sb.
+ //
+ function encodeChar(b, sb, special)
+ {
+ switch(b)
+ {
+ case 92: // '\\'
+ {
+ sb.push("\\\\");
+ break;
+ }
+ case 39: // '\''
+ {
+ sb.push("\\'");
+ break;
+ }
+ case 34: // '"'
+ {
+ sb.push("\\\"");
+ break;
+ }
+ case 8: // '\b'
+ {
+ sb.push("\\b");
+ break;
+ }
+ case 12: // '\f'
+ {
+ sb.push("\\f");
+ break;
+ }
+ case 10: // '\n'
+ {
+ sb.push("\\n");
+ break;
+ }
+ case 13: // '\r'
+ {
+ sb.push("\\r");
+ break;
+ }
+ case 9: // '\t'
+ {
+ sb.push("\\t");
+ break;
+ }
+ default:
+ {
+ if(!(b >= 32 && b <= 126))
+ {
+ sb.push('\\');
+ var octal = b.toString(8);
+ //
+ // Add leading zeroes so that we avoid problems during
+ // decoding. For example, consider the encoded string
+ // \0013 (i.e., a character with value 1 followed by
+ // the character '3'). If the leading zeroes were omitted,
+ // the result would be incorrectly interpreted by the
+ // decoder as a single character with value 11.
+ //
+ for(var j = octal.length; j < 3; j++)
+ {
+ sb.push('0');
+ }
+ sb.push(octal);
+ }
+ else
+ {
+ var c = String.fromCharCode(b);
+ if(special !== null && special.indexOf(c) !== -1)
+ {
+ sb.push('\\');
+ sb.push(c);
+ }
+ else
+ {
+ sb.push(c);
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Add escape sequences (such as "\n", or "\007") to make a string
+ // readable in ASCII. Any characters that appear in special are
+ // prefixed with a backlash in the returned string.
+ //
+ StringUtil.escapeString = function(s, special)
+ {
+ special = special === undefined ? null : special;
+
+ var i, length;
+ if(special !== null)
+ {
+ for(i = 0, length = special.length; i < length; ++i)
+ {
+ if(special.charCodeAt(i) < 32 || special.charCodeAt(i) > 126)
+ {
+ throw new Error("special characters must be in ASCII range 32-126");
+ }
+ }
+ }
+
+ var result = [], c;
+ for(i = 0, length = s.length; i < length; ++i)
+ {
+ c = s.charCodeAt(i);
+ if(c < 128)
+ {
+ encodeChar(c, result, special);
+ }
+ else if(c > 127 && c < 2048)
+ {
+ encodeChar((c >> 6) | 192, result, special);
+ encodeChar((c & 63) | 128, result, special);
+ }
+ else
+ {
+ encodeChar((c >> 12) | 224, result, special);
+ encodeChar(((c >> 6) & 63) | 128, result, special);
+ encodeChar((c & 63) | 128, result, special);
+ }
+ }
+
+ return result.join("");
+ };
+
+ function checkChar(s, pos)
+ {
+ var n = s.charCodeAt(pos);
+ if(!(n >= 32 && n <= 126))
+ {
+ var msg;
+ if(pos > 0)
+ {
+ msg = "character after `" + s.substring(0, pos) + "'";
+ }
+ else
+ {
+ msg = "first character";
+ }
+ msg += " is not a printable ASCII character (ordinal " + n + ")";
+ throw new Error(msg);
+ }
+ return n;
+ }
+
+ //
+ // Decode the character or escape sequence starting at start and return it.
+ // nextStart is set to the index of the first character following the decoded
+ // character or escape sequence.
+ //
+ function decodeChar(s, start, end, nextStart)
+ {
+ Debug.assert(start >= 0);
+ Debug.assert(end <= s.length);
+
+ if(start >= end)
+ {
+ throw new Error("EOF while decoding string");
+ }
+
+ var c;
+
+ if(s.charAt(start) != '\\')
+ {
+ c = checkChar(s, start++);
+ }
+ else
+ {
+ if(start + 1 == end)
+ {
+ throw new Error("trailing backslash");
+ }
+ switch(s.charAt(++start))
+ {
+ case '\\':
+ case '\'':
+ case '"':
+ {
+ c = s.charCodeAt(start++);
+ break;
+ }
+ case 'b':
+ {
+ ++start;
+ c = "\b".charCodeAt(0);
+ break;
+ }
+ case 'f':
+ {
+ ++start;
+ c = "\f".charCodeAt(0);
+ break;
+ }
+ case 'n':
+ {
+ ++start;
+ c = "\n".charCodeAt(0);
+ break;
+ }
+ case 'r':
+ {
+ ++start;
+ c = "\r".charCodeAt(0);
+ break;
+ }
+ case 't':
+ {
+ ++start;
+ c = "\t".charCodeAt(0);
+ break;
+ }
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ var octalChars = "01234567";
+ var val = 0;
+ for(var j = 0; j < 3 && start < end; ++j)
+ {
+ var ch = s.charAt(start++);
+ if(octalChars.indexOf(ch) == -1)
+ {
+ --start;
+ break;
+ }
+ val = val * 8 + parseInt(ch);
+ }
+ if(val > 255)
+ {
+ var msg = "octal value \\" + val.toString(8) + " (" + val + ") is out of range";
+ throw new Error(msg);
+ }
+ c = val;
+ break;
+ }
+ default:
+ {
+ c = checkChar(s, start++);
+ break;
+ }
+ }
+ }
+ nextStart.value = start;
+ return c;
+ }
+
+ //
+ // Remove escape sequences from s and append the result to sb.
+ // Return true if successful, false otherwise.
+ //
+ function decodeString(s, start, end, arr)
+ {
+ var nextStart = { 'value': 0 }, c, c2, c3;
+ while(start < end)
+ {
+ c = decodeChar(s, start, end, nextStart);
+ start = nextStart.value;
+
+ if(c < 128)
+ {
+ arr.push(String.fromCharCode(c));
+ }
+ else if(c > 191 && c < 224)
+ {
+ c2 = decodeChar(s, start, end, nextStart);
+ start = nextStart.value;
+ arr.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
+ }
+ else
+ {
+ c2 = decodeChar(s, start, end, nextStart);
+ start = nextStart.value;
+ c3 = decodeChar(s, start, end, nextStart);
+ start = nextStart.value;
+ arr.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
+ }
+ }
+ }
+
+ //
+ // Remove escape sequences added by escapeString. Throws Error
+ // for an invalid input string.
+ //
+ StringUtil.unescapeString = function(s, start, end)
+ {
+ start = start === undefined ? 0 : start;
+ end = end === undefined ? s.length : end;
+
+ Debug.assert(start >= 0 && start <= end && end <= s.length);
+
+ var arr = [];
+ decodeString(s, start, end, arr);
+
+ return arr.join("");
+ };
+
+ //
+ // Split string helper; returns null for unmatched quotes
+ //
+ StringUtil.splitString = function(str, delim)
+ {
+ var v = [];
+ var s = "";
+ var pos = 0;
+
+ var quoteChar = null;
+ while(pos < str.length)
+ {
+ if(quoteChar === null && (str.charAt(pos) === '"' || str.charAt(pos) === '\''))
+ {
+ quoteChar = str.charAt(pos++);
+ continue; // Skip the quote.
+ }
+ else if(quoteChar === null && str.charAt(pos) === '\\' && pos + 1 < str.length &&
+ (str.charAt(pos + 1) === '"' || str.charAt(pos + 1) === '\''))
+ {
+ ++pos; // Skip the backslash
+ }
+ else if(quoteChar !== null && str.charAt(pos) === '\\' && pos + 1 < str.length &&
+ str.charAt(pos + 1) === quoteChar)
+ {
+ ++pos; // Skip the backslash
+ }
+ else if(quoteChar !== null && str.charAt(pos) === quoteChar)
+ {
+ ++pos;
+ quoteChar = null;
+ continue; // Skip the quote.
+ }
+ else if(delim.indexOf(str.charAt(pos)) !== -1)
+ {
+ if(quoteChar === null)
+ {
+ ++pos;
+ if(s.length > 0)
+ {
+ v.push(s);
+ s = "";
+ }
+ continue;
+ }
+ }
+
+ if(pos < str.length)
+ {
+ s += str.charAt(pos++);
+ }
+ }
+
+ if(s.length > 0)
+ {
+ v.push(s);
+ }
+ if(quoteChar !== null)
+ {
+ return null; // Unmatched quote.
+ }
+
+ return v;
+ };
+
+ //
+ // If a single or double quotation mark is found at the start position,
+ // then the position of the matching closing quote is returned. If no
+ // quotation mark is found at the start position, then 0 is returned.
+ // If no matching closing quote is found, then -1 is returned.
+ //
+ StringUtil.checkQuote = function(s, start)
+ {
+ start = start === undefined ? 0 : start;
+
+ var quoteChar = s.charAt(start);
+ if(quoteChar == '"' || quoteChar == '\'')
+ {
+ start++;
+ var len = s.length;
+ var pos;
+ while(start < len && (pos = s.indexOf(quoteChar, start)) != -1)
+ {
+ if(s.charAt(pos - 1) != '\\')
+ {
+ return pos;
+ }
+ start = pos + 1;
+ }
+ return -1; // Unmatched quote
+ }
+ return 0; // Not quoted
+ };
+
+ StringUtil.hashCode = function(s)
+ {
+ var hash = 0;
+ var n = s.length;
+
+ for(var i = 0; i < n; i++)
+ {
+ hash = 31 * hash + s.charCodeAt(i);
+ }
+
+ return hash;
+ };
+
+ StringUtil.toInt = function(s)
+ {
+ var n = parseInt(s, 10);
+ if(isNaN(n))
+ {
+ throw new Error("conversion of `" + s + "' to int failed");
+ }
+ return n;
+ };
+
+ Ice.StringUtil = StringUtil;
+ global.Ice = Ice;
+}(typeof (global) === "undefined" ? window : global));