You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2011/10/29 02:18:08 UTC
svn commit: r1190707 [1/2] - in
/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src:
main/java/org/apache/jackrabbit/spi2microkernel/json/
test/java/org/apache/jackrabbit/spi2microkernel/json/ test/resources/
Author: mduerig
Date: Sat Oct 29 00:18:05 2011
New Revision: 1190707
URL: http://svn.apache.org/viewvc?rev=1190707&view=rev
Log:
Microkernel based Jackrabbit prototype (WIP)
Add Json parser
Added:
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizer.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/FullJsonParser.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonHandler.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonParser.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonTokenizer.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonValue.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/LevelOrderJsonParser.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/ParseException.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/Token.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizer.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizerTest.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/JsonParserTest.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizerTest.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/resources/test1.json
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/resources/test2.json
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizer.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizer.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizer.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.Token.Type;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DefaultJsonTokenizer extends JsonTokenizer {
+ private final String json;
+
+ private int pos;
+
+ public DefaultJsonTokenizer(String json) {
+ this.json = json;
+ }
+
+ protected DefaultJsonTokenizer(DefaultJsonTokenizer tokenizer) {
+ super(tokenizer);
+ json = tokenizer.json;
+ pos = tokenizer.pos;
+ }
+
+ @Override
+ protected Token nextToken() {
+ skipWhiteSpace();
+ if (pos < json.length()) {
+ switch (json.charAt(pos)) {
+ case '{': return createToken(Type.BEGIN_OBJECT, "{", pos++);
+ case '}': return createToken(Type.END_OBJECT, "}", pos++);
+ case '[': return createToken(Type.BEGIN_ARRAY, "[", pos++);
+ case ']': return createToken(Type.END_ARRAY, "]", pos++);
+ case ':': return createToken(Type.COLON, ":", pos++);
+ case ',': return createToken(Type.COMMA, ",", pos++);
+ case 't': return readLiteral(Type.TRUE, "true");
+ case 'f': return readLiteral(Type.FALSE, "false");
+ case 'n': return readLiteral(Type.NULL, "null");
+ case '"': return readString();
+ default: return readNumber();
+ }
+ }
+ else {
+ return createToken(Type.EOF, "", pos);
+ }
+ }
+
+ @Override
+ public int pos() {
+ return peek().pos();
+ }
+
+ @Override
+ public void setPos(int pos) {
+ currentToken = null;
+ this.pos = pos;
+ }
+
+ @Override
+ public String toString() {
+ return (currentToken == null ? "" : currentToken) + " " + json.substring(pos);
+ }
+
+ @Override
+ public JsonTokenizer copy() {
+ return new DefaultJsonTokenizer(this);
+ }
+
+ //------------------------------------------< protected >---
+
+ protected void skipWhiteSpace() {
+ while (pos < json.length() && Character.isWhitespace(json.charAt(pos))) {
+ pos++;
+ }
+ }
+
+ protected Token createToken(Type type, String text, int pos) {
+ return new Token(type, text, pos);
+ }
+
+ protected Token readLiteral(Type type, String text) {
+ if (json.substring(pos).startsWith(text)) {
+ Token token = createToken(type, text, pos);
+ pos += text.length();
+ return token;
+ }
+ else {
+ throw new ParseException(pos, "Expected '" + text + ",' found: " + excerpt(json, pos, 40));
+ }
+ }
+
+ protected Token readString() {
+ int i;
+ boolean found = false;
+ boolean even = true;
+
+ // starting at pos + 1, find index i of the first quote character in json which
+ // is preceded by an even number of backslash characters
+ for (i = pos + 1; i < json.length() && !(found = json.charAt(i) == '"' && even); i++) {
+ even = json.charAt(i) != '\\' || !even;
+ }
+
+ if (found) {
+ String text = json.substring(pos + 1, i);
+ Token token = createToken(Type.STRING, text, pos);
+ pos = i + 1;
+ return token;
+ }
+ else {
+ throw new ParseException(pos, "Expected string, found. " + excerpt(json, pos, 40));
+ }
+ }
+
+ private static final Pattern NUMBER_PATTERN = Pattern.compile(
+ "(\\+|-)?(\\d+)((\\.)(\\d+))?(((e|E)(\\+|-)?)(\\d+))?");
+
+ protected Token readNumber() {
+ Matcher matcher = NUMBER_PATTERN.matcher(json.substring(pos));
+ if (matcher.lookingAt()) {
+ Token token = createToken(Type.NUMBER, matcher.group(), pos);
+ pos += matcher.end();
+ return token;
+ }
+ else {
+ throw new ParseException(pos, "Expected number, found: " + excerpt(json, pos, 40));
+ }
+ }
+
+ //------------------------------------------< private >---
+
+ private static String excerpt(String string, int pos, int len) {
+ return string.substring(pos, Math.min(string.length(), pos + len)) + "...";
+ }
+
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/FullJsonParser.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/FullJsonParser.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/FullJsonParser.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/FullJsonParser.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonArray;
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonObject;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+
+public class FullJsonParser {
+ private FullJsonParser() { }
+
+ public static JsonObject parseObject(JsonTokenizer tokenizer) {
+ ObjectHandler objectHandler = new ObjectHandler();
+ new JsonParser(objectHandler).parseObject(tokenizer);
+ return objectHandler.getObject();
+ }
+
+ public static JsonArray parseArray(JsonTokenizer tokenizer) {
+ ArrayHandler arrayHandler = new ArrayHandler();
+ new JsonParser(arrayHandler).parseArray(tokenizer);
+ return arrayHandler.getArray();
+ }
+
+ public static class ObjectHandler extends JsonHandler {
+ private final JsonObject object = new JsonObject(new LinkedHashMap<String, JsonValue>());
+
+ @Override
+ public void atom(Token key, Token value) {
+ object.put(key.text(), new JsonAtom(value));
+ }
+
+ @Override
+ public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ object.put(key.text(), parseObject(tokenizer));
+ }
+
+ @Override
+ public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ object.put(key.text(), parseArray(tokenizer));
+ }
+
+ public JsonObject getObject() {
+ return object;
+ }
+
+ }
+
+ public static class ArrayHandler extends JsonHandler {
+ private final JsonArray array = new JsonArray(new ArrayList<JsonValue>());
+
+ @Override
+ public void atom(Token key, Token value) {
+ array.add(new JsonAtom(value));
+ }
+
+ @Override
+ public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ array.add(parseObject(tokenizer));
+ }
+
+ @Override
+ public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ array.add(parseArray(tokenizer));
+ }
+
+ public JsonArray getArray() {
+ return array;
+ }
+ }
+
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonHandler.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonHandler.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonHandler.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+public class JsonHandler {
+ public static final JsonHandler INSTANCE = new JsonHandler();
+
+ public void atom(Token key, Token value) { }
+ public void comma(Token token) { }
+
+ public void pair(JsonParser parser, JsonTokenizer tokenizer) {
+ parser.parsePair(tokenizer);
+ }
+
+ public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ parser.parseObject(tokenizer);
+ }
+
+ public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ parser.parseArray(tokenizer);
+ }
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonParser.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonParser.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonParser.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonParser.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.Token.Type;
+
+/**
+ * OBJECT ::= { (PAIR (, PAIR)*)? }
+ * PAIR ::= STRING : VALUE
+ * VALUE ::= OBJECT | ARRAY | STRING | NUMBER | true | false | null
+ * ARRAY ::= [ (VALUE (, VALUE)*)? ]
+ */
+public final class JsonParser {
+ private final JsonHandler jsonHandler;
+
+ public JsonParser(JsonHandler jsonHandler) {
+ this.jsonHandler = jsonHandler;
+ }
+
+ /* OBJECT ::= { (PAIR (, PAIR)*)? } */
+ public void parseObject(JsonTokenizer tokenizer) {
+ tokenizer.read(Type.BEGIN_OBJECT);
+
+ if (tryParsePair(tokenizer)) {
+ while (tokenizer.peek(Type.COMMA)) {
+ jsonHandler.comma(tokenizer.read());
+ if (!tryParsePair(tokenizer)) {
+ throw new ParseException(tokenizer.pos(), "Expected pair, found: " + tokenizer.peek());
+ }
+ }
+ }
+
+ tokenizer.read(Type.END_OBJECT);
+ }
+
+ /* PAIR ::= STRING: VALUE */
+ public void parsePair(JsonTokenizer tokenizer) {
+ if (!tokenizer.peek(Type.STRING)) {
+ throw new ParseException(tokenizer.pos(), "Expected string, found: " + tokenizer.peek());
+ }
+
+ Token key = tokenizer.read();
+ tokenizer.read(Type.COLON);
+ parseValue(key, tokenizer);
+ }
+
+ /* VALUE ::= OBJECT | ARRAY | STRING | NUMBER | true | false | null */
+ public void parseValue(Token key, JsonTokenizer tokenizer) {
+ switch (tokenizer.peek().type()) {
+ case BEGIN_OBJECT:
+ jsonHandler.object(this, key, tokenizer);
+ break;
+ case BEGIN_ARRAY:
+ jsonHandler.array(this, key, tokenizer);
+ break;
+ case STRING:
+ case NUMBER:
+ case TRUE:
+ case FALSE:
+ case NULL:
+ jsonHandler.atom(key, tokenizer.read());
+ break;
+ default:
+ throw new ParseException(tokenizer.pos(), "Expected value, found: " + tokenizer.peek());
+ }
+ }
+
+ /* ARRAY ::= [ (VALUE (, VALUE)*)? ] */
+ public void parseArray(JsonTokenizer tokenizer) {
+ tokenizer.read(Type.BEGIN_ARRAY);
+
+ if (tryParseValue(tokenizer)) {
+ while (tokenizer.peek(Type.COMMA)) {
+ jsonHandler.comma(tokenizer.read());
+ if (!tryParseValue(tokenizer)) {
+ throw new ParseException(tokenizer.pos(), "Expected value, found: " + tokenizer.peek());
+ }
+ }
+ }
+
+ tokenizer.read(Type.END_ARRAY);
+ }
+
+ //------------------------------------------< private >---
+
+ private boolean tryParsePair(JsonTokenizer tokenizer) {
+ if (tokenizer.peek(Type.STRING)) {
+ jsonHandler.pair(this, tokenizer);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ private boolean tryParseValue(JsonTokenizer tokenizer) {
+ if (tokenizer.peek(Type.END_ARRAY)) {
+ return false;
+ } else {
+ parseValue(null, tokenizer);
+ return true;
+ }
+ }
+
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonTokenizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonTokenizer.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonTokenizer.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonTokenizer.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.Token.Type;
+
+public abstract class JsonTokenizer {
+ protected Token currentToken;
+
+ protected JsonTokenizer(JsonTokenizer tokenizer) {
+ currentToken = tokenizer.currentToken;
+ }
+
+ protected JsonTokenizer() { }
+
+ public Token peek() {
+ if (currentToken == null) {
+ currentToken = nextToken();
+ }
+
+ return currentToken;
+ }
+
+ public boolean peek(Type type) {
+ return peek().type() == type;
+ }
+
+ public Token read() {
+ if (currentToken == null) {
+ return nextToken();
+ }
+ else {
+ Token token = currentToken;
+ currentToken = null;
+ return token;
+ }
+ }
+
+ public Token read(Type type) {
+ Token token = peek();
+ if (token.type() == type) {
+ return read();
+ }
+ else {
+ throw new ParseException(token.pos(), "Expected token type " + type + ", found: " + token);
+ }
+ }
+
+ public boolean skip(Type type) {
+ if (peek(type)) {
+ read();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public abstract int pos();
+ public abstract void setPos(int pos);
+ public abstract JsonTokenizer copy();
+ protected abstract Token nextToken();
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonValue.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonValue.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonValue.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/JsonValue.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public abstract class JsonValue {
+ public enum Type {
+ STRING(false),
+ NUMBER(false),
+ BOOLEAN(false),
+ NULL(false),
+ OBJECT(true),
+ ARRAY(true);
+
+ private final boolean compound;
+
+ Type(boolean compound) {
+ this.compound = compound;
+ }
+
+ public boolean compound() {
+ return compound;
+ }
+ }
+
+ public abstract static class Visitor {
+ public void visit(JsonAtom atom) { }
+ public void visit(JsonArray array) { }
+ public void visit(JsonObject object) { }
+ }
+
+ public static String toJson(JsonValue jsonValue) {
+ final StringBuilder sb = new StringBuilder();
+ jsonValue.accept(new Visitor() {
+ @Override
+ public void visit(JsonAtom atom) {
+ sb.append(toJson(atom));
+ }
+
+ @Override
+ public void visit(JsonArray array) {
+ sb.append('[');
+ String comma = "";
+ for (JsonValue value : array.value()) {
+ sb.append(comma);
+ comma = ",";
+ value.accept(this);
+ }
+ sb.append(']');
+ }
+
+ @Override
+ public void visit(JsonObject object) {
+ sb.append('{');
+ String comma = "";
+ for (Entry<String, JsonValue> entry : object.value().entrySet()) {
+ sb.append(comma);
+ comma = ",";
+ sb.append(quote(entry.getKey())).append(':');
+ entry.getValue().accept(this);
+ }
+ sb.append('}');
+ }
+ });
+ return sb.toString();
+ }
+
+ /**
+ * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F).
+ */
+ public static String escape(String string) {
+ if (string == null) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < string.length(); i++) {
+ char ch = string.charAt(i);
+ switch (ch) {
+ case '"':
+ sb.append("\\\"");
+ break;
+ case '\\':
+ sb.append("\\\\");
+ break;
+ case '\b':
+ sb.append("\\b");
+ break;
+ case '\f':
+ sb.append("\\f");
+ break;
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ default:
+ //Reference: http://www.unicode.org/versions/Unicode5.1.0/
+ if (ch >= '\u0000' && ch <= '\u001F' ||
+ ch >= '\u007F' && ch <= '\u009F' ||
+ ch >= '\u2000' && ch <= '\u20FF') {
+
+ String ss = Integer.toHexString(ch);
+ sb.append("\\u");
+ for (int k = 0; k < 4 - ss.length(); k++) {
+ sb.append('0');
+ }
+ sb.append(ss.toUpperCase());
+ } else {
+ sb.append(ch);
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * @throws StringIndexOutOfBoundsException
+ * @throws NumberFormatException
+ */
+ public static String unescape(String text) {
+ if (text.length() == 0) {
+ return text;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int k = 0; k < text.length(); k++) {
+ char c = text.charAt(k);
+ if (c == '\\') {
+ c = text.charAt(++k);
+ switch (c) {
+ case 'b':
+ sb.append('\b');
+ break;
+ case 't':
+ sb.append('\t');
+ break;
+ case 'n':
+ sb.append('\n');
+ break;
+ case 'f':
+ sb.append('\f');
+ break;
+ case 'r':
+ sb.append('\r');
+ break;
+ case 'u':
+ String u = text.substring(++k, k += 4);
+ sb.append((char) Integer.parseInt(u, 16));
+ break;
+ case 'x':
+ String x = text.substring(++k, k += 2);
+ sb.append((char) Integer.parseInt(x, 16));
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+ else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ public abstract Object value();
+ public abstract Type type();
+ public abstract void accept(Visitor visitor);
+
+ public String toJson() {
+ return toJson(this);
+ }
+
+ public static class JsonAtom extends JsonValue {
+ private final String value;
+ private final Type type;
+
+ public JsonAtom(String value, Type type) {
+ this.value = value;
+ this.type = type;
+ }
+
+ public JsonAtom(Token token) {
+ this(token.text(), valueType(token.type()));
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public Type type() {
+ return type;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ return value + ": " + type;
+ }
+
+ @Override
+ public int hashCode() {
+ return 37 * (37 * (17 + value().hashCode()) + type().hashCode());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof JsonAtom) {
+ JsonAtom that = (JsonAtom) other;
+ return that.value().equals(value()) && that.type() == type();
+ }
+ else {
+ return false;
+ }
+ }
+
+ //------------------------------------------< private >---
+
+ private static JsonValue.Type valueType(Token.Type type) {
+ switch (type) {
+ case TRUE:
+ case FALSE:
+ return JsonValue.Type.BOOLEAN;
+ case NULL:
+ return JsonValue.Type.NULL;
+ case STRING:
+ return JsonValue.Type.STRING;
+ case NUMBER:
+ return JsonValue.Type.NUMBER;
+ default:
+ throw new IllegalArgumentException("Cannot map token type " + type + " to value type");
+ }
+ }
+ }
+
+ public static class JsonArray extends JsonValue {
+ private final List<JsonValue> values;
+
+ public JsonArray(List<JsonValue> values) {
+ this.values = values;
+ }
+
+ public void add(JsonValue value) {
+ values.add(value);
+ }
+
+ public JsonValue get(int index) {
+ return values.get(index);
+ }
+
+ @Override
+ public List<JsonValue> value() {
+ return values;
+ }
+
+ @Override
+ public Type type() {
+ return Type.ARRAY;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ return values.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return value().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof JsonArray) {
+ JsonArray that = (JsonArray) other;
+ return that.value().equals(value());
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ public static class JsonObject extends JsonValue {
+ private final Map<String, JsonValue> values;
+
+ public JsonObject(Map<String, JsonValue> values) {
+ this.values = values;
+ }
+
+ public void put(String key, JsonValue value) {
+ values.put(key, value);
+ }
+
+ public JsonValue get(String key) {
+ return values.get(key);
+ }
+
+ @Override
+ public Map<String, JsonValue> value() {
+ return values;
+ }
+
+ @Override
+ public Type type() {
+ return Type.OBJECT;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ return values.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return value().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof JsonObject) {
+ JsonObject that = (JsonObject) other;
+ return that.value().equals(value());
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ //------------------------------------------< private >---
+
+ private static String toJson(JsonAtom atom) {
+ return atom.type() == Type.STRING
+ ? quote(escape(atom.value()))
+ : atom.value();
+ }
+
+ private static String quote(String text) {
+ return '\"' + text + '\"';
+ }
+
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/LevelOrderJsonParser.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/LevelOrderJsonParser.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/LevelOrderJsonParser.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/LevelOrderJsonParser.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonArray;
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonObject;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public final class LevelOrderJsonParser {
+ private LevelOrderJsonParser() { }
+
+ public static JsonObject parseObject(JsonTokenizer tokenizer) {
+ ObjectHandler objectHandler = new ObjectHandler();
+ new JsonParser(objectHandler).parseObject(tokenizer);
+ return objectHandler.getObject();
+ }
+
+ public static JsonArray parseArray(JsonTokenizer tokenizer) {
+ ArrayHandler arrayHandler = new ArrayHandler();
+ new JsonParser(arrayHandler).parseArray(tokenizer);
+ return arrayHandler.getArray();
+ }
+
+ public static class ObjectHandler extends JsonHandler {
+ private final JsonObject object = new JsonObject(new LinkedHashMap<String, JsonValue>());
+
+ @Override
+ public void atom(Token key, Token value) {
+ object.put(key.text(), new JsonAtom(value));
+ }
+
+ @Override
+ public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ object.put(key.text(), new DeferredObjectValue(tokenizer.copy()));
+ tokenizer.setPos(getNextPairPos(tokenizer.copy()));
+ }
+
+ @Override
+ public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ object.put(key.text(), parseArray(tokenizer));
+ }
+
+ public JsonObject getObject() {
+ return object;
+ }
+
+ }
+
+ public static class ArrayHandler extends JsonHandler {
+ private final JsonArray array = new JsonArray(new ArrayList<JsonValue>());
+
+ @Override
+ public void atom(Token key, Token value) {
+ array.add(new JsonAtom(value));
+ }
+
+ @Override
+ public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ array.add(new DeferredObjectValue(tokenizer.copy()));
+ tokenizer.setPos(getNextPairPos(tokenizer.copy()));
+ }
+
+ @Override
+ public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ array.add(parseArray(tokenizer));
+ }
+
+ public JsonArray getArray() {
+ return array;
+ }
+ }
+
+ //------------------------------------------< private >---
+
+ private static int getNextPairPos(JsonTokenizer tokenizer) {
+ SkipObjectHandler skipObjectHandler = new SkipObjectHandler(tokenizer.pos());
+ try {
+ new JsonParser(skipObjectHandler).parseObject(tokenizer);
+ }
+ catch (ParseException e) {
+ return skipObjectHandler.newPos;
+ }
+ return tokenizer.pos();
+ }
+
+ private static class DeferredObjectValue extends JsonObject {
+ private final JsonTokenizer tokenizer;
+
+ public DeferredObjectValue(JsonTokenizer tokenizer) {
+ super(null);
+ this.tokenizer = tokenizer;
+ }
+
+ @Override
+ public void put(String key, JsonValue value) {
+ throw new IllegalStateException("Cannot add value");
+ }
+
+ @Override
+ public JsonValue get(String key) {
+ throw new IllegalStateException("Cannot get single value");
+ }
+
+ @Override
+ public Map<String, JsonValue> value() {
+ return parseObject(tokenizer.copy()).value();
+ }
+
+ @Override
+ public String toString() {
+ return "<deferred>";
+ }
+
+ }
+
+ private static class SkipObjectHandler extends JsonHandler {
+ private final int startPos;
+ private int newPos;
+
+ public SkipObjectHandler(int startPos) {
+ this.startPos = startPos;
+ }
+
+ @Override
+ public void atom(Token key, Token value) {
+ if (key != null && ":size".equals(key.text()) && Token.Type.NUMBER == value.type()) {
+ newPos = startPos + Integer.parseInt(value.text());
+ throw new ParseException(-1, null);
+ }
+ }
+ }
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/ParseException.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/ParseException.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/ParseException.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/ParseException.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+public class ParseException extends RuntimeException {
+ public ParseException(int pos, String message) {
+ super(pos + ": " + message);
+ }
+
+ public ParseException(int pos, String message, Throwable cause) {
+ super(pos + ": " + message, cause);
+ }
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/Token.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/Token.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/Token.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/Token.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+public final class Token {
+ private final Type type;
+ private final String text;
+ private final int pos;
+
+ public enum Type {BEGIN_OBJECT, END_OBJECT, BEGIN_ARRAY, END_ARRAY, COLON, COMMA, EOF, TRUE,
+ FALSE, NULL, STRING, NUMBER}
+
+ public Token(Type type, String text, int pos) {
+ this.type = type;
+ this.text = text;
+ this.pos = pos;
+ }
+
+ public Type type() {
+ return type;
+ }
+
+ public String text() {
+ return text;
+ }
+
+ public int pos() {
+ return pos;
+ }
+
+ @Override
+ public String toString() {
+ return "Token[" + type + ", " + text + ", " + pos + ']';
+ }
+
+ @Override
+ public int hashCode() {
+ return 37 * (37 * (17 + type().hashCode()) + text().hashCode());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Token) {
+ Token that = (Token) other;
+ return that.type == type && that.text.equals(text);
+ }
+ else {
+ return false;
+ }
+ }
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizer.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizer.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/main/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizer.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.Token.Type;
+
+public class UnescapingJsonTokenizer extends DefaultJsonTokenizer {
+ public UnescapingJsonTokenizer(String json) {
+ super(json);
+ }
+
+ protected UnescapingJsonTokenizer(UnescapingJsonTokenizer tokenizer) {
+ super(tokenizer);
+ }
+
+ @Override
+ public JsonTokenizer copy() {
+ return new UnescapingJsonTokenizer(this);
+ }
+
+ //------------------------------------------< protected >---
+
+ @Override
+ protected Token createToken(Type type, String text, int pos) {
+ return super.createToken(type, type == Type.STRING ? unescape(text) : text, pos);
+ }
+
+ //------------------------------------------< private >---
+
+ private String unescape(String text) {
+ try {
+ return JsonValue.unescape(text);
+ }
+ catch (IndexOutOfBoundsException e) {
+ throw new ParseException(pos(), "Invalid character escaping in string", e);
+ }
+ catch (NumberFormatException e) {
+ throw new ParseException(pos(), "Invalid character escaping in string", e);
+ }
+ }
+
+
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizerTest.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizerTest.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/DefaultJsonTokenizerTest.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.Token.Type;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+public class DefaultJsonTokenizerTest {
+
+ private static final Token[] TOKENS = new Token[] {
+ new Token(Type.BEGIN_OBJECT, "{", 0),
+ new Token(Type.END_OBJECT, "}", 2),
+ new Token(Type.BEGIN_ARRAY, "[", 4),
+ new Token(Type.END_ARRAY, "]", 6),
+ new Token(Type.COLON, ":", 8),
+ new Token(Type.COMMA, ",", 10),
+ new Token(Type.TRUE, "true", 12),
+ new Token(Type.FALSE, "false", 17),
+ new Token(Type.NULL, "null", 23),
+ new Token(Type.STRING, "string", 28),
+ new Token(Type.NUMBER, "-12.34e-56", 37),
+ new Token(Type.STRING, "ab\\\"", 48),
+ new Token(Type.STRING, "ab\\\\", 55),
+ new Token(Type.STRING, "", 62),
+ new Token(Type.STRING, "\\\"", 65),
+ new Token(Type.STRING, "\\\\", 70),
+};
+
+ private static final String TOKEN_STRING = join(TOKENS);
+
+ private static final Token EOF_TOKEN = new Token(Type.EOF, "", TOKEN_STRING.length());
+
+ @Test
+ public void testRead() {
+ JsonTokenizer tokenizer = new DefaultJsonTokenizer(TOKEN_STRING);
+
+ for (Token token : TOKENS) {
+ int pos = tokenizer.pos();
+ assertEquals(token, tokenizer.read());
+ assertEquals(pos, token.pos());
+ }
+ assertEquals(EOF_TOKEN, tokenizer.read());
+ }
+
+ @Test
+ public void testPeek() {
+ JsonTokenizer tokenizer = new DefaultJsonTokenizer(TOKEN_STRING);
+
+ for (Token token : TOKENS) {
+ Token peeked = tokenizer.peek();
+ assertEquals(token, peeked);
+ assertEquals(token.pos(), peeked.pos());
+
+ for (Token t : TOKENS) {
+ if (t.type() != token.type()) {
+ assertFalse(tokenizer.peek(t.type()));
+ }
+ }
+ assertEquals(peeked, tokenizer.read(peeked.type()));
+ }
+ assertEquals(EOF_TOKEN, tokenizer.peek());
+ }
+
+ @Test
+ public void testSkip() {
+ JsonTokenizer tokenizer = new DefaultJsonTokenizer(TOKEN_STRING);
+
+ for (Token token : TOKENS) {
+ Token peeked = tokenizer.peek();
+ assertEquals(token, peeked);
+
+ for (Token t : TOKENS) {
+ if (t.type() != token.type()) {
+ assertFalse(tokenizer.skip(t.type()));
+ }
+ }
+ assertTrue(tokenizer.skip(token.type()));
+ }
+ assertEquals(EOF_TOKEN, tokenizer.peek());
+ }
+
+ //------------------------------------------< private >---
+
+ private static String join(Token[] tokens) {
+ StringBuilder json = new StringBuilder();
+ for (Token token : tokens) {
+ json.append(getString(token)).append(' ');
+ }
+ return json.toString();
+ }
+
+ private static String getString(Token token) {
+ return token.type() == Type.STRING
+ ? '\"' + token.text() + '\"'
+ : token.text();
+ }
+
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/JsonParserTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/JsonParserTest.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/JsonParserTest.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/JsonParserTest.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonArray;
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.JsonObject;
+import org.apache.jackrabbit.spi2microkernel.json.JsonValue.Visitor;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Stack;
+
+import static junit.framework.Assert.assertEquals;
+
+public class JsonParserTest {
+ private static final String JSON_IN = readFile("/test1.json");
+
+ @Test
+ public void testParser() {
+ final StringBuilder jsonOut = new StringBuilder("{");
+ new JsonParser(new JsonHandler() {
+ @Override
+ public void atom(Token key, Token value) {
+ jsonOut.append(createKey(key));
+ if (value.type() == Token.Type.STRING) {
+ jsonOut.append(quoteAndEscape(value.text()));
+ }
+ else {
+ jsonOut.append(value.text());
+ }
+ }
+
+ @Override
+ public void comma(Token token) {
+ jsonOut.append(',');
+ }
+
+ @Override
+ public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ jsonOut.append(createKey(key)).append('{');
+ super.object(parser, key, tokenizer);
+ jsonOut.append('}');
+ }
+
+ @Override
+ public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ jsonOut.append(createKey(key)).append('[');
+ super.array(parser, key, tokenizer);
+ jsonOut.append(']');
+ }
+
+ }).parseObject(new UnescapingJsonTokenizer(JSON_IN));
+ jsonOut.append('}');
+
+ assertEquals(JSON_IN, jsonOut.toString());
+ }
+
+ @Test
+ public void testParserWithBuilder() {
+ final Stack<JsonValue> objects = new Stack<JsonValue>();
+ objects.push(new JsonObject(new LinkedHashMap<String, JsonValue>()));
+
+ new JsonParser(new JsonHandler() {
+ @Override
+ public void atom(Token key, Token value) {
+ put(key, new JsonAtom(value));
+ }
+
+ @Override
+ public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ JsonObject object = new JsonObject(new LinkedHashMap<String, JsonValue>());
+ put(key, object);
+ objects.push(object);
+ super.object(parser, key, tokenizer);
+ objects.pop();
+ }
+
+ @Override
+ public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+ JsonArray array = new JsonArray(new ArrayList<JsonValue>());
+ put(key, array);
+ objects.push(array);
+ super.array(parser, key, tokenizer);
+ objects.pop();
+ }
+
+ private void put(final Token key, final JsonValue value) {
+ objects.peek().accept(new Visitor(){
+ @Override
+ public void visit(JsonArray array) {
+ array.add(value);
+ }
+ @Override
+ public void visit(JsonObject object) {
+ object.put(key.text(), value);
+ }
+ });
+ }
+
+ }).parseObject(new UnescapingJsonTokenizer(JSON_IN));
+
+ assertEquals(1, objects.size());
+ assertEquals(JSON_IN, objects.peek().toJson());
+ }
+
+
+ @Test(expected = ParseException.class)
+ public void testParseExceptionValue1() {
+ JsonParser parser = new JsonParser(new JsonHandler());
+ parser.parseObject(new UnescapingJsonTokenizer("{\"key\":}"));
+ }
+
+ @Test(expected = ParseException.class)
+ public void testParseExceptionValue2() {
+ JsonParser parser = new JsonParser(new JsonHandler());
+ parser.parseObject(new UnescapingJsonTokenizer("{\"key\":[1,]}"));
+ }
+
+ @Test(expected = ParseException.class)
+ public void testParseExceptionPair1() {
+ JsonParser parser = new JsonParser(new JsonHandler());
+ parser.parseObject(new UnescapingJsonTokenizer("{\"key\":1,}"));
+ }
+
+ @Test(expected = ParseException.class)
+ public void testParseExceptionPair2() {
+ JsonParser parser = new JsonParser(new JsonHandler());
+ parser.parsePair(new UnescapingJsonTokenizer(""));
+ }
+
+ @Test
+ public void testJson1() {
+ JsonObject object1 = FullJsonParser.parseObject(new UnescapingJsonTokenizer(JSON_IN));
+ assertEquals(JSON_IN, object1.toJson());
+
+ JsonObject object2 = LevelOrderJsonParser.parseObject(new UnescapingJsonTokenizer(JSON_IN));
+ assertEquals(JSON_IN, object2.toJson());
+
+ assertEquals(object1, object2);
+ assertEquals(object1.toJson(), object2.toJson());
+ }
+
+ @Test
+ public void testJson2() {
+ String json = readFile("/test2.json");
+ JsonObject object1 = FullJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+ JsonObject object2 = LevelOrderJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+ assertEquals(object1, object2);
+ assertEquals(object1.toJson(), object2.toJson());
+ }
+
+ //------------------------------------------< private >---
+
+ private static String readFile(String fileName) {
+ InputStream is = JsonParserTest.class.getResourceAsStream(fileName);
+ if (is == null) {
+ throw new RuntimeException("Resource not found: " + fileName);
+ }
+
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+ String s;
+ StringBuilder sb = new StringBuilder();
+ while((s = reader.readLine()) != null) {
+ sb.append(s);
+ }
+
+ reader.close();
+ return sb.toString();
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String createKey(Token key) {
+ return key == null ? "" : quoteAndEscape(key.text()) + ':';
+ }
+
+ private static String quoteAndEscape(String text) {
+ return '\"' + JsonValue.escape(text) + '\"';
+ }
+
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizerTest.java?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizerTest.java (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/java/org/apache/jackrabbit/spi2microkernel/json/UnescapingJsonTokenizerTest.java Sat Oct 29 00:18:05 2011
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.spi2microkernel.json;
+
+import org.apache.jackrabbit.spi2microkernel.json.Token.Type;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+
+public class UnescapingJsonTokenizerTest {
+
+ private static final Token[] TOKENS = new Token[] {
+ new Token(Type.STRING, "foobar", 0),
+ new Token(Type.STRING, "foo\\bar", 9),
+ new Token(Type.STRING, "foobar", 20),
+ new Token(Type.STRING, "foobar", 33),
+ new Token(Type.STRING, "foo\bbar", 48),
+ new Token(Type.STRING, "foo\tbar", 59),
+ new Token(Type.STRING, "foo\nbar", 70),
+ new Token(Type.STRING, "foo\fbar", 81),
+ new Token(Type.STRING, "foo\rbar", 92),
+ new Token(Type.STRING, "foo\"bar", 103),
+ };
+
+ private static final Token EOF_TOKEN = new Token(Type.EOF, "", 113);
+
+ @Test
+ public void test() {
+ JsonTokenizer tokenizer = new UnescapingJsonTokenizer("\"foobar\" \"foo\\\\bar\" " +
+ "\"foo\\x62bar\" \"foo\\u0062bar\" \"foo\\bbar\" \"foo\\tbar\" \"foo\\nbar\" " +
+ "\"foo\\fbar\" \"foo\\rbar\" \"foo\\\"bar\"");
+
+ for (Token token : TOKENS) {
+ Token t = tokenizer.read();
+ assertEquals(token, t);
+ assertEquals(token.pos(), t.pos());
+ }
+ Token t = tokenizer.read();
+ assertEquals(EOF_TOKEN, t);
+ assertEquals(EOF_TOKEN.pos(), t.pos());
+ }
+}
Added: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/resources/test1.json
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/resources/test1.json?rev=1190707&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/resources/test1.json (added)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi2microkernel/src/test/resources/test1.json Sat Oct 29 00:18:05 2011
@@ -0,0 +1 @@
+{"string":"va\"lue","empty":"","number":1.42,"null":null,"false":false,"true":true,"emptyArray":[],"nestedArray":[[],[[],[]]],"array":[1,2,3],"array2":["aa","bb","cc"],"emptyObject":{},"almostEmptyObject":{":size":12},"object":{":size":212,"string":"value","empty":"","number":1.42,"null":null,"false":false,"true":true,"emptyArray":[],"array1":[1,2,3,{}],"array2":[1,2,3,{"number":1.42,"array99":[1,2,3,{}]}],"emptyObject":{},"object":{}}}
\ No newline at end of file