CB-9782 Implements PlatformApi contract for Android platform.

This closes #226
This commit is contained in:
Vladimir Kotikov
2015-10-20 10:30:31 +03:00
parent 789c505a88
commit 400282282f
131 changed files with 9482 additions and 3048 deletions
+51
View File
@@ -0,0 +1,51 @@
# node-properties-parser
A parser for [.properties](http://en.wikipedia.org/wiki/.properties) files written in javascript. Properties files store key-value pairs. They are typically used for configuration and internationalization in Java applications as well as in Actionscript projects. Here's an example of the format:
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website = http://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009
*(taken from [Wikipedia](http://en.wikipedia.org/wiki/.properties#Format))*
Currently works with any version of node.js.
## The API
- `parse(text)`: Parses `text` into key-value pairs. Returns an object containing the key-value pairs.
- `read(path[, callback])`: Opens the file specified by `path` and calls `parse` on its content. If the optional `callback` parameter is provided, the result is then passed to it as the second parameter. If an error occurs, the error object is passed to `callback` as the first parameter. If `callback` is not provided, the file specified by `path` is synchronously read and calls `parse` on its contents. The resulting object is immediately returned.
- `createEditor([path][, options][, callback]])`: If neither `path` or `callback` are provided an empty editor object is returned synchronously. If only `path` is provided, the file specified by `path` is synchronously read and parsed. An editor object with the results in then immediately returned. If both `path` and `callback` are provided, the file specified by `path` is read and parsed asynchronously. An editor object with the results are then passed to `callback` as the second parameters. If an error occurs, the error object is passed to `callback` as the first parameter. The following options are supported:
- `options.separator`: The character used to separate key/values. Defaults to "=".
- `options.path`: Treated the same way as the optional `path` argument. If both are provided the arguement wins.
- `options.callback`: Treated the same way as the optional `callback` parameter. If both are provided the arguement wins.
- `Editor`: The editor object is returned by `createEditor`. Has the following API:
- `get(key)`: Returns the value currently associated with `key`.
- `set(key, [value[, comment]])`: Associates `key` with `value`. An optional comment can be provided. If `value` is not specified or is `null`, then `key` is unset.
- `unset(key)`: Unsets the specified `key`.
- `save([path][, callback]])`: Writes the current contents of this editor object to a file specified by `path`. If `path` is not provided, then it'll be defaulted to the `path` value passed to `createEditor`. The `callback` parameter is called when the file has been written to disk.
- `addHeadComment`: Added a comment to the head of the file.
- `toString`: Returns the string representation of this properties editor object. This string will be written to a file if `save` is called.
## Getting node-properties-parser
The easiest way to get node-properties-parser is with [npm](http://npmjs.org/):
npm install properties-parser
Alternatively you can clone this git repository:
git://github.com/xavi-/node-properties-parser.git
## Developed by
* Xavi Ramirez
## License
This project is released under [The MIT License](http://www.opensource.org/licenses/mit-license.php).
+420
View File
@@ -0,0 +1,420 @@
var fs = require("fs");
function Iterator(text) {
var pos = 0, length = text.length;
this.peek = function(num) {
num = num || 0;
if(pos + num >= length) { return null; }
return text.charAt(pos + num);
};
this.next = function(inc) {
inc = inc || 1;
if(pos >= length) { return null; }
return text.charAt((pos += inc) - inc);
};
this.pos = function() {
return pos;
};
}
var rWhitespace = /\s/;
function isWhitespace(chr) {
return rWhitespace.test(chr);
}
function consumeWhiteSpace(iter) {
var start = iter.pos();
while(isWhitespace(iter.peek())) { iter.next(); }
return { type: "whitespace", start: start, end: iter.pos() };
}
function startsComment(chr) {
return chr === "!" || chr === "#";
}
function isEOL(chr) {
return chr == null || chr === "\n" || chr === "\r";
}
function consumeComment(iter) {
var start = iter.pos();
while(!isEOL(iter.peek())) { iter.next(); }
return { type: "comment", start: start, end: iter.pos() };
}
function startsKeyVal(chr) {
return !isWhitespace(chr) && !startsComment(chr);
}
function startsSeparator(chr) {
return chr === "=" || chr === ":" || isWhitespace(chr);
}
function startsEscapedVal(chr) {
return chr === "\\";
}
function consumeEscapedVal(iter) {
var start = iter.pos();
iter.next(); // move past "\"
var curChar = iter.next();
if(curChar === "u") { // encoded unicode char
iter.next(4); // Read in the 4 hex values
}
return { type: "escaped-value", start: start, end: iter.pos() };
}
function consumeKey(iter) {
var start = iter.pos(), children = [];
var curChar;
while((curChar = iter.peek()) !== null) {
if(startsSeparator(curChar)) { break; }
if(startsEscapedVal(curChar)) { children.push(consumeEscapedVal(iter)); continue; }
iter.next();
}
return { type: "key", start: start, end: iter.pos(), children: children };
}
function consumeKeyValSeparator(iter) {
var start = iter.pos();
var seenHardSep = false, curChar;
while((curChar = iter.peek()) !== null) {
if(isEOL(curChar)) { break; }
if(isWhitespace(curChar)) { iter.next(); continue; }
if(seenHardSep) { break; }
seenHardSep = (curChar === ":" || curChar === "=");
if(seenHardSep) { iter.next(); continue; }
break; // curChar is a non-separtor char
}
return { type: "key-value-separator", start: start, end: iter.pos() };
}
function startsLineBreak(iter) {
return iter.peek() === "\\" && isEOL(iter.peek(1));
}
function consumeLineBreak(iter) {
var start = iter.pos();
iter.next(); // consume \
if(iter.peek() === "\r") { iter.next(); }
iter.next(); // consume \n
var curChar;
while((curChar = iter.peek()) !== null) {
if(isEOL(curChar)) { break; }
if(!isWhitespace(curChar)) { break; }
iter.next();
}
return { type: "line-break", start: start, end: iter.pos() };
}
function consumeVal(iter) {
var start = iter.pos(), children = [];
var curChar;
while((curChar = iter.peek()) !== null) {
if(startsLineBreak(iter)) { children.push(consumeLineBreak(iter)); continue; }
if(startsEscapedVal(curChar)) { children.push(consumeEscapedVal(iter)); continue; }
if(isEOL(curChar)) { break; }
iter.next();
}
return { type: "value", start: start, end: iter.pos(), children: children };
}
function consumeKeyVal(iter) {
return {
type: "key-value",
start: iter.pos(),
children: [
consumeKey(iter),
consumeKeyValSeparator(iter),
consumeVal(iter)
],
end: iter.pos()
};
}
var renderChild = {
"escaped-value": function(child, text) {
var type = text.charAt(child.start + 1);
if(type === "t") { return "\t"; }
if(type === "r") { return "\r"; }
if(type === "n") { return "\n"; }
if(type === "f") { return "\f"; }
if(type !== "u") { return type; }
return String.fromCharCode(parseInt(text.substr(child.start + 2, 4), 16));
},
"line-break": function (child, text) {
return "";
}
};
function rangeToBuffer(range, text) {
var start = range.start, buffer = [];
for(var i = 0; i < range.children.length; i++) {
var child = range.children[i];
buffer.push(text.substring(start, child.start));
buffer.push(renderChild[child.type](child, text));
start = child.end;
}
buffer.push(text.substring(start, range.end));
return buffer;
}
function rangesToObject(ranges, text) {
var obj = Object.create(null); // Creates to a true hash map
for(var i = 0; i < ranges.length; i++) {
var range = ranges[i];
if(range.type !== "key-value") { continue; }
var key = rangeToBuffer(range.children[0], text).join("");
var val = rangeToBuffer(range.children[2], text).join("");
obj[key] = val;
}
return obj;
}
function stringToRanges(text) {
var iter = new Iterator(text), ranges = [];
var curChar;
while((curChar = iter.peek()) !== null) {
if(isWhitespace(curChar)) { ranges.push(consumeWhiteSpace(iter)); continue; }
if(startsComment(curChar)) { ranges.push(consumeComment(iter)); continue; }
if(startsKeyVal(curChar)) { ranges.push(consumeKeyVal(iter)); continue; }
throw Error("Something crazy happened. text: '" + text + "'; curChar: '" + curChar + "'");
}
return ranges;
}
function isNewLineRange(range) {
if(!range) { return false; }
if(range.type === "whitespace") { return true; }
if(range.type === "literal") {
return isWhitespace(range.text) && range.text.indexOf("\n") > -1;
}
return false;
}
function escapeMaker(escapes) {
return function escapeKey(key) {
var zeros = [ "", "0", "00", "000" ];
var buf = [];
for(var i = 0; i < key.length; i++) {
var chr = key.charAt(i);
if(escapes[chr]) { buf.push(escapes[chr]); continue; }
var code = chr.codePointAt(0);
if(code <= 0x7F) { buf.push(chr); continue; }
var hex = code.toString(16);
buf.push("\\u");
buf.push(zeros[4 - hex.length]);
buf.push(hex);
}
return buf.join("");
};
}
var escapeKey = escapeMaker({ " ": "\\ ", "\n": "\\n", ":": "\\:", "=": "\\=" });
var escapeVal = escapeMaker({ "\n": "\\n" });
function Editor(text, options) {
if (typeof text === 'object') {
options = text;
text = null;
}
text = text || "";
var path = options.path;
var separator = options.separator || '=';
var ranges = stringToRanges(text);
var obj = rangesToObject(ranges, text);
var keyRange = Object.create(null); // Creates to a true hash map
for(var i = 0; i < ranges.length; i++) {
var range = ranges[i];
if(range.type !== "key-value") { continue; }
var key = rangeToBuffer(range.children[0], text).join("");
keyRange[key] = range;
}
this.addHeadComment = function(comment) {
if(comment == null) { return; }
ranges.unshift({ type: "literal", text: "# " + comment.replace(/\n/g, "\n# ") + "\n" });
};
this.get = function(key) { return obj[key]; };
this.set = function(key, val, comment) {
if(val == null) { this.unset(key); return; }
obj[key] = val;
var escapedKey = escapeKey(key);
var escapedVal = escapeVal(val);
var range = keyRange[key];
if(!range) {
keyRange[key] = range = {
type: "literal",
text: escapedKey + separator + escapedVal
};
var prevRange = ranges[ranges.length - 1];
if(prevRange != null && !isNewLineRange(prevRange)) {
ranges.push({ type: "literal", text: "\n" });
}
ranges.push(range);
}
// comment === null deletes comment. if comment === undefined, it's left alone
if(comment !== undefined) {
range.comment = comment && "# " + comment.replace(/\n/g, "\n# ") + "\n";
}
if(range.type === "literal") {
range.text = escapedKey + separator + escapedVal;
if(range.comment != null) { range.text = range.comment + range.text; }
} else if(range.type === "key-value") {
range.children[2] = { type: "literal", text: escapedVal };
} else {
throw "Unknown node type: " + range.type;
}
};
this.unset = function(key) {
if(!(key in obj)) { return; }
var range = keyRange[key];
var idx = ranges.indexOf(range);
ranges.splice(idx, (isNewLineRange(ranges[idx + 1]) ? 2 : 1));
delete keyRange[key];
delete obj[key];
};
this.valueOf = this.toString = function() {
var buffer = [], stack = [].concat(ranges);
var node;
while((node = stack.shift()) != null) {
switch(node.type) {
case "literal":
buffer.push(node.text);
break;
case "key":
case "value":
case "comment":
case "whitespace":
case "key-value-separator":
case "escaped-value":
case "line-break":
buffer.push(text.substring(node.start, node.end));
break;
case "key-value":
Array.prototype.unshift.apply(stack, node.children);
if(node.comment) { stack.unshift({ type: "literal", text: node.comment }); }
break;
}
}
return buffer.join("");
};
this.save = function(newPath, callback) {
if(typeof newPath === 'function') {
callback = newPath;
newPath = path;
}
newPath = newPath || path;
if(!newPath) {
if (callback) {
return callback("Unknown path");
}
throw new Error("Unknown path");
}
if (callback) {
fs.writeFile(newPath, this.toString(), callback);
} else {
fs.writeFileSync(newPath, this.toString());
}
};
}
function createEditor(/*path, options, callback*/) {
var path, options, callback;
var args = Array.prototype.slice.call(arguments);
for (var i = 0; i < args.length; i ++) {
var arg = args[i];
if (!path && typeof arg === 'string') {
path = arg;
} else if (!options && typeof arg === 'object') {
options = arg;
} else if (!callback && typeof arg === 'function') {
callback = arg;
}
}
options = options || {};
path = path || options.path;
callback = callback || options.callback;
options.path = path;
if(!path) { return new Editor(options); }
if(!callback) { return new Editor(fs.readFileSync(path).toString(), options); }
return fs.readFile(path, function(err, text) {
if(err) { return callback(err, null); }
text = text.toString();
return callback(null, new Editor(text, options));
});
}
function parse(text) {
text = text.toString();
var ranges = stringToRanges(text);
return rangesToObject(ranges, text);
}
function read(path, callback) {
if(!callback) { return parse(fs.readFileSync(path)); }
return fs.readFile(path, function(err, data) {
if(err) { return callback(err, null); }
return callback(null, parse(data));
});
}
module.exports = { parse: parse, read: read, createEditor: createEditor };
+50
View File
@@ -0,0 +1,50 @@
{
"name": "properties-parser",
"version": "0.3.0",
"description": "A parser for .properties files written in javascript",
"keywords": [
"parser",
".properties",
"properties",
"java",
"file parser",
"actionscript"
],
"maintainers": [
{
"name": "xavi",
"email": "xavi.rmz@gmail.com"
}
],
"main": "./index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/xavi-/node-properties-parser.git"
},
"license": "MIT",
"engines": {
"node": ">= 0.3.1"
},
"gitHead": "d9f75e462c3da0e6eb33261e578e040994ff50c9",
"bugs": {
"url": "https://github.com/xavi-/node-properties-parser/issues"
},
"homepage": "https://github.com/xavi-/node-properties-parser",
"_id": "properties-parser@0.3.0",
"scripts": {},
"_shasum": "6ba6dc6ac40cf53b1ee2c2045f86623e70213caa",
"_from": "properties-parser@>=0.3.0 <0.4.0",
"_npmVersion": "2.5.1",
"_nodeVersion": "1.2.0",
"_npmUser": {
"name": "xavi",
"email": "xavi.rmz@gmail.com"
},
"dist": {
"shasum": "6ba6dc6ac40cf53b1ee2c2045f86623e70213caa",
"tarball": "http://registry.npmjs.org/properties-parser/-/properties-parser-0.3.0.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/properties-parser/-/properties-parser-0.3.0.tgz",
"readme": "ERROR: No README data found!"
}