Browse Source

Removing unnecessary object creation from streaming API and fixing tests

pull/93/head
hunterpayne 10 years ago
parent
commit
dd98bc1756
  1. 4
      json-path/src/main/java/com/jayway/jsonpath/EvaluationCallback.java
  2. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/token/ArrayToken.java
  3. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/token/FloatToken.java
  4. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/token/IntToken.java
  5. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/token/ObjectToken.java
  6. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/token/StringToken.java
  7. 177
      json-path/src/main/java/com/jayway/jsonpath/internal/token/TokenStack.java
  8. 36
      json-path/src/main/java/com/jayway/jsonpath/internal/token/TokenStackElement.java
  9. 4
      json-path/src/test/java/com/jayway/jsonpath/CallbackRecorder.java
  10. 8
      json-path/src/test/java/com/jayway/jsonpath/JacksonTest.java
  11. 35
      json-path/src/test/java/com/jayway/jsonpath/JacksonTest_Split.java

4
json-path/src/main/java/com/jayway/jsonpath/EvaluationCallback.java

@ -26,12 +26,12 @@ public interface EvaluationCallback {
* Callback invoked when result is found
* @param path -- the specific path that was triggered
*/
public void resultFound(Object source, Object obj, Path path) throws Exception;
public void resultFound(Path path) throws Exception;
/**
* Callback invoked when the parser leaves the region in which the match
* was found
* @param path -- the specific path that was untriggered
*/
public void resultFoundExit(Object source, Object obj, Path path) throws Exception;
public void resultFoundExit(Path path) throws Exception;
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/token/ArrayToken.java

@ -5,7 +5,7 @@ package com.jayway.jsonpath.internal.token;
*
* @author Hunter Payne
**/
public class ArrayToken extends TokenStackElement
public class ArrayToken implements TokenStackElement
{
int currentIndex;
TokenStackElement value; // can be an object, array, or property

2
json-path/src/main/java/com/jayway/jsonpath/internal/token/FloatToken.java

@ -5,7 +5,7 @@ package com.jayway.jsonpath.internal.token;
*
* @author Hunter Payne
**/
public class FloatToken extends TokenStackElement
public class FloatToken implements TokenStackElement
{
public float value;

2
json-path/src/main/java/com/jayway/jsonpath/internal/token/IntToken.java

@ -5,7 +5,7 @@ package com.jayway.jsonpath.internal.token;
*
* @author Hunter Payne
**/
public class IntToken extends TokenStackElement
public class IntToken implements TokenStackElement
{
public int value;

2
json-path/src/main/java/com/jayway/jsonpath/internal/token/ObjectToken.java

@ -5,7 +5,7 @@ package com.jayway.jsonpath.internal.token;
*
* @author Hunter Payne
**/
public class ObjectToken extends TokenStackElement
public class ObjectToken implements TokenStackElement
{
String key;
TokenStackElement value; // can be an array, object, or property

2
json-path/src/main/java/com/jayway/jsonpath/internal/token/StringToken.java

@ -7,7 +7,7 @@ import java.util.regex.Pattern;
*
* @author Hunter Payne
**/
public class StringToken extends TokenStackElement
public class StringToken implements TokenStackElement
{
public String value;
public Pattern pattern;

177
json-path/src/main/java/com/jayway/jsonpath/internal/token/TokenStack.java

@ -2,13 +2,14 @@
package com.jayway.jsonpath.internal.token;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.EvaluationCallback;
import com.jayway.jsonpath.internal.Path;
import com.fasterxml.jackson.core.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonToken;
/**
*
@ -16,18 +17,15 @@ import org.slf4j.LoggerFactory;
**/
public class TokenStack
{
private static final Logger log = LoggerFactory.getLogger(TokenStack.class);
protected static Logger log = Logger.getLogger("com.jayway.jsonpath");
protected Configuration conf;
protected Stack<TokenStackElement> elements;
protected Stack<Object> objStack;
protected List<Path> paths;
protected Map<TokenStackElement, Path> matchedPaths;
private TokenStackElement curr;
private Path rootMatch;
private Class jsonArrayType;
private Class jsonObjectType;
public TokenStack(Configuration conf)
{
@ -35,11 +33,7 @@ public class TokenStack
paths = new ArrayList<Path>();
matchedPaths = new HashMap<TokenStackElement, Path>();
elements = new Stack<TokenStackElement>();
objStack = new Stack<Object>();
rootMatch = null;
this.jsonArrayType = this.getNewJsonArray().getClass();
this.jsonObjectType = this.getNewJsonObject().getClass();
}
public Stack<TokenStackElement> getStack()
@ -55,26 +49,28 @@ public class TokenStack
paths.add(path);
}
public void read(JsonParser parser, EvaluationCallback callback)
throws Exception
{
read(parser,callback,true);
}
/**
* reads from stream and notifies the callback of matched registered paths, with results.
* Note: GSON not supported
* reads from stream and notifies the callback of matched registered paths
*/
public void read(JsonParser parser, EvaluationCallback callback, boolean getParents)
public void read(JsonParser parser, EvaluationCallback callback)
throws Exception
{
assert(callback != null);
Object obj = null;
boolean needsPathCheck = false;
/*
if (null == curr && elements.empty()) {
// check for $ patterns
for (Path path : paths) {
if (path.checkForMatch(this)) {
matchedPaths.put(curr, path);
callback.resultFound(path);
rootMatch = path;
}
}
}
*/
while (parser.nextToken() != null) {
//log.debug("type/name/val: " + parser.getCurrentToken() + " " + parser.getCurrentName() + " " + parser.getText());
boolean saveMatch = false;
switch (parser.getCurrentToken()) {
case START_ARRAY:
@ -89,42 +85,33 @@ public class TokenStack
saveMatch = true;
needsPathCheck = true;
elements.push(curr);
obj = stackPush(parser.getCurrentName(),getNewJsonArray());
break;
}
case END_ARRAY:
{
Path match = matchedPaths.remove(curr);
if (match != null) {
callback.resultFoundExit(parser.getCurrentToken(), obj, match);
callback.resultFoundExit(match);
}
elements.pop();
if (elements.empty()) curr = null;
else curr = elements.peek();
obj = stackPop(callback, curr, getJsonArrayType(), match);
break;
}
case VALUE_EMBEDDED_OBJECT:
case START_OBJECT:
{
obj = stackPush(parser.getCurrentName(), getNewJsonObject());
if (isArray(curr)) {
if (curr != null && curr.getType() == TokenType.ARRAY_TOKEN) {
if (((ArrayToken)curr).getValue() != null &&
matchedPaths.containsKey(curr))
{
Path match = matchedPaths.remove(curr);
if (getParents) {
callback.resultFoundExit(parser.getCurrentToken(), obj, match);
}
callback.resultFoundExit(match);
if (match.checkForMatch(this)) {
matchedPaths.put(curr, match);
//callback.resultFound(match);
curr.setMatched();
callback.resultFound(match);
}
}
} else if (null == curr && elements.empty()) {
@ -132,7 +119,7 @@ public class TokenStack
for (Path path : paths) {
if (path.checkForMatch(this)) {
matchedPaths.put(curr, path);
//callback.resultFound(path);
callback.resultFound(path);
rootMatch = path;
}
}
@ -152,24 +139,20 @@ public class TokenStack
}
case END_OBJECT:
{
if (getParents) {
if (!"$".equals(curr)) {
Path match = matchedPaths.remove(curr);
if (match != null) {
callback.resultFoundExit(parser.getCurrentToken(), obj, match);
callback.resultFoundExit(match);
}
} else {
Path match = matchedPaths.get("$");
if (match != null) {
callback.resultFoundExit(parser.getCurrentToken(), obj, match);
callback.resultFoundExit(match);
}
}
}
elements.pop();
if (elements.empty()) curr = null;
else curr = elements.peek();
obj = stackPop(callback, curr, this.getJsonObjectType(), null);
break;
}
case FIELD_NAME:
@ -183,7 +166,6 @@ public class TokenStack
StringToken newToken = new StringToken("FALSE");
curr.setValue(newToken);
needsPathCheck = true;
objPutVal(obj, curr, newToken.value);
break;
}
case VALUE_TRUE:
@ -191,7 +173,6 @@ public class TokenStack
StringToken newToken = new StringToken("TRUE");
curr.setValue(newToken);
needsPathCheck = true;
objPutVal(obj, curr, newToken.value);
break;
}
case VALUE_NUMBER_FLOAT:
@ -200,7 +181,6 @@ public class TokenStack
new FloatToken((float)parser.getValueAsDouble());
curr.setValue(newToken);
needsPathCheck = true;
objPutVal(obj, curr, newToken.value);
break;
}
case VALUE_NUMBER_INT:
@ -208,7 +188,6 @@ public class TokenStack
IntToken newToken = new IntToken(parser.getValueAsInt());
curr.setValue(newToken);
needsPathCheck = true;
objPutVal(obj, curr, newToken.value);
break;
}
case VALUE_STRING:
@ -216,14 +195,12 @@ public class TokenStack
StringToken newToken = new StringToken(parser.getText());
curr.setValue(newToken);
needsPathCheck = true;
objPutVal(obj, curr, parser.getText());
break;
}
case VALUE_NULL:
{
curr.setValue(null);
needsPathCheck = true;
objPutVal(obj, curr, null);
break;
}
default:
@ -234,117 +211,19 @@ public class TokenStack
for (Path path : paths) {
if (path.checkForMatch(this)) {
if (saveMatch) matchedPaths.put(curr, path);
curr.setMatched();
if (getParents) {
callback.resultFound(parser.getCurrentToken(), obj, path);
}
callback.resultFound(path);
}
}
needsPathCheck = false;
}
if (rootMatch != null && elements.empty() && getParents) {
if (isArray(curr)) {
obj = this.getNewJsonArray();
}
callback.resultFoundExit(parser.getCurrentToken(), obj, rootMatch);
if (rootMatch != null && elements.empty()) {
callback.resultFoundExit(rootMatch);
rootMatch = null;
}
}
}
private Object getNewJsonObject() {
return conf.jsonProvider().createMap();
}
private Object getJsonArrayType() {
return this.jsonArrayType;
}
private Object getNewJsonArray() {
return conf.jsonProvider().createArray();
}
private Class<? extends Object> getJsonObjectType() {
return this.jsonObjectType;
}
private Object stackPush(String key, Object jsObj) throws Exception {
if (jsObj == null) {
return null;
}
if (this.objStack.size() > 0) {
Object obj = this.objStack.peek();
//log.trace("PP : Push[" + this.objStack.size() + "] " + jsObj.getClass().getSimpleName() + " -> " + obj.getClass());
if (obj.getClass() == getJsonArrayType()) {
((List)obj).add(jsObj);
} else if (obj.getClass() == getJsonObjectType()) {
((HashMap<String,Object>)obj).put(key, jsObj);
} else {
throw new Exception("Unhandled type: " + obj.getClass());
}
} else {
//log.trace("PP : Push " + jsObj.getClass().getSimpleName() + " -> ROOT");
}
this.objStack.add(jsObj);
return jsObj;
}
private <T> Object stackPop(EvaluationCallback callback, TokenStackElement tse, T jsObj, Path path) throws Exception {
if (jsObj == null) {
return null;
}
Object obj = null;
Object popObj = null;
if (this.objStack.size() > 0) {
popObj = this.objStack.peek();
//log.trace("PP : Pop " +( popObj != null ? popObj.getClass() : " null"));
if (popObj.getClass() != jsObj) {
throw new Exception("Unexpected type : " + popObj.getClass());
} else {
popObj = this.objStack.pop();
if (this.objStack.size() > 0) {
obj = objStack.peek();
} else {
//log.debug("PP : Now ROOT");
obj = null;
}
}
}
if (tse != null && tse.getMatched()) {
callback.resultFound("Stack", popObj, path);
}
return obj;
}
private boolean isArray(TokenStackElement current) {
return current != null && current.getType() == TokenType.ARRAY_TOKEN;
}
private void objPutVal(Object objIn, TokenStackElement el, Object value) throws Exception {
if (objIn == null) {
return;
}
Class objInCls = objIn.getClass();
if (objInCls == this.getJsonObjectType()) {
HashMap obj = (HashMap)objIn;
obj.put(((ObjectToken)el).key, value);
} else if (objInCls == this.getJsonArrayType()) {
ArrayList obj = (ArrayList)objIn;
obj.add(value);
} else {
throw new Exception("Unhandled type: " + objInCls);
}
}
public String toString()
{
StringBuilder sb = new StringBuilder();

36
json-path/src/main/java/com/jayway/jsonpath/internal/token/TokenStackElement.java

@ -1,45 +1,17 @@
package com.jayway.jsonpath.internal.token;
import java.util.logging.Logger;
/**
*
* @author Hunter Payne
**/
public abstract class TokenStackElement
public interface TokenStackElement
{
private static Logger log = Logger.getLogger(TokenStackElement.class.getName());
private boolean matched = false;
private TokenStackElement parent;
public abstract TokenType getType(); // otherwise its an object
public abstract TokenStackElement getValue();
public abstract void setValue(TokenStackElement elem);
public TokenStackElement getParent() {
if (parent == null) {
return this;
}
//log.trace("parent: " + parent);
return parent;
}
public void setParent(TokenStackElement parent) {
this.parent = parent;
}
public TokenType getType(); // otherwise its an object
public void setMatched() {
this.matched = true;
}
public TokenStackElement getValue();
public boolean getMatched() {
return matched;
}
public void setValue(TokenStackElement elem);
}
// End TokenStackElement.java

4
json-path/src/test/java/com/jayway/jsonpath/CallbackRecorder.java

@ -44,12 +44,12 @@ public class CallbackRecorder implements EvaluationCallback {
results = new ArrayList<CallbackEvent>();
}
public void resultFound(Object src, Object val, Path path) {
public void resultFound(Path path) {
System.err.println("found result " + path);
results.add(new CallbackEvent(path, false));
}
public void resultFoundExit(Object src, Object val, Path path) {
public void resultFoundExit(Path path) {
System.err.println("exiting result " + path);
results.add(new CallbackEvent(path, true));
}

8
json-path/src/test/java/com/jayway/jsonpath/JacksonTest.java

@ -605,7 +605,7 @@ public class JacksonTest extends BaseTest implements EvaluationCallback {
equals(new CallbackRecorder.CallbackEvent(rootPath, true)));
}
public void resultFound(Object src, Object val, Path path) {
public void resultFound(Path path) {
if (path == idPath) {
switch (match++) {
case 0:
@ -1038,14 +1038,14 @@ public class JacksonTest extends BaseTest implements EvaluationCallback {
break;
}
}
recorder.resultFound(src, val, path);
recorder.resultFound(path);
}
public void resultFoundExit(Object src, Object val, Path path) {
public void resultFoundExit(Path path) {
assert(path != idPath);
assert(path != floatPath);
assert(path != intPath);
recorder.resultFoundExit(src, val, path);
recorder.resultFoundExit(path);
}
protected void checkResult(TokenStack stack, Object expected) {

35
json-path/src/test/java/com/jayway/jsonpath/JacksonTest_Split.java

@ -29,30 +29,33 @@ public class JacksonTest_Split extends BaseTest implements EvaluationCallback {
results.clear();
}
private void jsonSplit_Test(Configuration jsonProviderCfg) throws JsonParseException, IOException, Exception {
String res = "json_opsview1.json";
try (InputStream stream = getClass().getClassLoader().getResourceAsStream(res)) {
Path path = PathCompiler.compile("$.list[*]");
TokenStack stack = new TokenStack(jsonProviderCfg);
JsonFactory factory = new JsonFactory();
stack.registerPath(path);
stack.read(factory.createParser(stream), this, false);
private void jsonSplit_Test(Configuration jsonProviderCfg)
throws JsonParseException, IOException, Exception {
try {
String res = "json_opsview1.json";
InputStream stream = getClass().getClassLoader().getResourceAsStream(res);
Path path = PathCompiler.compile("$.list[*]");
TokenStack stack = new TokenStack(jsonProviderCfg);
JsonFactory factory = new JsonFactory();
stack.registerPath(path);
stack.read(factory.createParser(stream), this);
} finally {
log.debug("results: " + results.size());
//assertTrue(results.size() == 96);
}
log.debug("results: " + results.size());
assertTrue(results.size() == 96);
}
@Override
public void resultFound(Object source, Object obj, Path path) throws Exception {
public void resultFound(Path path) throws Exception {
//log.debug(source + ":" + String.valueOf(obj));
results.add(obj);
//results.add(obj);
}
@Override
public void resultFoundExit(Object source, Object obj, Path path) throws Exception {
public void resultFoundExit(Path path) throws Exception {
//log.debug(source + ":" + String.valueOf(obj));
}
}

Loading…
Cancel
Save