29995
+ − 1
/*
+ − 2
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ − 3
*
+ − 4
* Redistribution and use in source and binary forms, with or without
+ − 5
* modification, are permitted provided that the following conditions
+ − 6
* are met:
+ − 7
*
+ − 8
* - Redistributions of source code must retain the above copyright
+ − 9
* notice, this list of conditions and the following disclaimer.
+ − 10
*
+ − 11
* - Redistributions in binary form must reproduce the above copyright
+ − 12
* notice, this list of conditions and the following disclaimer in the
+ − 13
* documentation and/or other materials provided with the distribution.
+ − 14
*
+ − 15
* - Neither the name of Oracle nor the names of its
+ − 16
* contributors may be used to endorse or promote products derived
+ − 17
* from this software without specific prior written permission.
+ − 18
*
+ − 19
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ − 20
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ − 21
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ − 22
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ − 23
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ − 24
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ − 25
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ − 26
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ − 27
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ − 28
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ − 29
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ − 30
*/
+ − 31
+ − 32
/*
+ − 33
* Hjson - "the Human JSON - A configuration file format that
+ − 34
* caters to humans and helps reduce the errors they make"
+ − 35
* See also: http://hjson.org/
+ − 36
*
+ − 37
* I wanted to see if we can use Nashorn Parser API (jdk9) to support
+ − 38
* similar flexible JSON extension with #nashorn. In this FlexiJSON.parse
+ − 39
* implementation, Nashorn Parser API is used to validate that the
+ − 40
* extendable flexi JSON is "data only" (i.e., no executable code) and
+ − 41
* then 'eval'ed to make an object out of it.
+ − 42
*
+ − 43
* FlexiJSON allows the following:
+ − 44
*
+ − 45
* * single and mutliple line comments anywhere
+ − 46
* * non-quoted property names and values
+ − 47
* * regexp literal values
+ − 48
* * omitting trailing comma
+ − 49
*
+ − 50
* When nashorn -scripting mode is enabled, FlexiJSON supports these
+ − 51
* as well:
+ − 52
*
+ − 53
* * shell style # comments
+ − 54
* * multiple line (Unix heredoc style) string values
+ − 55
*/
+ − 56
+ − 57
"use strict";
+ − 58
+ − 59
function FlexiJSON() {}
+ − 60
+ − 61
// helper to locate Nashorn Parser API classes
+ − 62
FlexiJSON.treeType = function(name) {
+ − 63
return Java.type("jdk.nashorn.api.tree." + name);
+ − 64
}
+ − 65
+ − 66
// Nashorn Parser API classes used
+ − 67
FlexiJSON.ArrayLiteral = FlexiJSON.treeType("ArrayLiteralTree");
+ − 68
FlexiJSON.ExpressionStatement = FlexiJSON.treeType("ExpressionStatementTree");
+ − 69
FlexiJSON.ObjectLiteral = FlexiJSON.treeType("ObjectLiteralTree");
+ − 70
FlexiJSON.RegExpLiteral = FlexiJSON.treeType("RegExpLiteralTree");
+ − 71
FlexiJSON.Literal = FlexiJSON.treeType("LiteralTree");
+ − 72
FlexiJSON.Parser = FlexiJSON.treeType("Parser");
+ − 73
FlexiJSON.SimpleTreeVisitor = FlexiJSON.treeType("SimpleTreeVisitorES5_1");
+ − 74
+ − 75
// FlexiJSON.parse API
+ − 76
+ − 77
FlexiJSON.parse = function(str) {
+ − 78
var parser = (typeof $OPTIONS == "undefined")?
+ − 79
FlexiJSON.Parser.create() :
+ − 80
FlexiJSON.Parser.create("-scripting");
+ − 81
+ − 82
// force the string to be an expression by putting it inside (, )
+ − 83
str = "(" + str + ")";
+ − 84
var ast = parser.parse("<flexijsondoc>", str, null);
+ − 85
// Should not happen. parse would have thrown syntax error
+ − 86
if (!ast) {
+ − 87
return undefined;
+ − 88
}
+ − 89
+ − 90
// allowed 'literal' values in flexi JSON
+ − 91
function isLiteral(node) {
+ − 92
return node instanceof FlexiJSON.ArrayLiteral ||
+ − 93
node instanceof FlexiJSON.Literal ||
+ − 94
node instanceof FlexiJSON.ObjectLiteral ||
+ − 95
node instanceof FlexiJSON.RegExpLiteral;
+ − 96
}
+ − 97
+ − 98
var visitor;
+ − 99
ast.accept(visitor = new (Java.extend(FlexiJSON.SimpleTreeVisitor)) {
+ − 100
lineMap: null,
+ − 101
+ − 102
throwError: function(msg, node) {
+ − 103
if (this.lineMap) {
+ − 104
var pos = node.startPosition;
+ − 105
var line = this.lineMap.getLineNumber(pos);
+ − 106
var column = this.lineMap.getColumnNumber(pos);
+ − 107
// we introduced extra '(' at start. So, adjust column number
+ − 108
msg = msg + " @ " + line + ":" + (column - 1);
+ − 109
}
+ − 110
throw new TypeError(msg);
+ − 111
},
+ − 112
+ − 113
visitLiteral: function(node, extra) {
+ − 114
print(node.value);
+ − 115
},
+ − 116
+ − 117
visitExpressionStatement: function(node, extra) {
+ − 118
var expr = node.expression;
+ − 119
if (isLiteral(expr)) {
+ − 120
expr.accept(visitor, extra);
+ − 121
} else {
+ − 122
this.throwError("only literals can occur", expr);
+ − 123
}
+ − 124
},
+ − 125
+ − 126
visitArrayLiteral: function(node, extra) {
+ − 127
for each (var elem in node.elements) {
+ − 128
if (isLiteral(elem)) {
+ − 129
elem.accept(visitor, extra);
+ − 130
} else {
+ − 131
this.throwError("only literal array element value allowed", elem);
+ − 132
}
+ − 133
}
+ − 134
},
+ − 135
+ − 136
visitObjectLiteral: function(node, extra) {
+ − 137
for each (var prop in node.properties) {
+ − 138
if (prop.getter != null || prop.setter != null) {
+ − 139
this.throwError("getter/setter property not allowed", node);
+ − 140
}
+ − 141
+ − 142
var value = prop.value;
+ − 143
if (isLiteral(value)) {
+ − 144
value.accept(visitor, extra);
+ − 145
} else {
+ − 146
this.throwError("only literal property value allowed", value);
+ − 147
}
+ − 148
}
+ − 149
},
+ − 150
+ − 151
visitCompilationUnit: function(node, extra) {
+ − 152
this.lineMap = node.lineMap;
+ − 153
var elements = node.sourceElements;
+ − 154
if (elements.length > 1) {
+ − 155
this.throwError("more than one top level expression", node.sourceElements[1]);
+ − 156
}
+ − 157
var stat = node.sourceElements[0];
+ − 158
if (! (stat instanceof FlexiJSON.ExpressionStatement)) {
+ − 159
this.throwError("only one top level expresion allowed", stat);
+ − 160
}
+ − 161
stat.accept(visitor, extra);
+ − 162
},
+ − 163
}, null);
+ − 164
+ − 165
// safe to eval given string as flexi JSON!
+ − 166
return eval(str);
+ − 167
}