Browse Source

Added EvaluationListener support.

pull/57/head
Kalle Stenflo 10 years ago
parent
commit
3cb6d6b1de
  1. 3
      json-path-assert/src/test/java/com/jayway/jsonassert/JsonAssertTest.java
  2. 52
      json-path/src/main/java/com/jayway/jsonpath/Configuration.java
  3. 49
      json-path/src/main/java/com/jayway/jsonpath/EvaluationListener.java
  4. 1
      json-path/src/main/java/com/jayway/jsonpath/ReadContext.java
  5. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/CompiledPath.java
  6. 4
      json-path/src/main/java/com/jayway/jsonpath/internal/EvaluationAbortException.java
  7. 13
      json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java
  8. 40
      json-path/src/main/java/com/jayway/jsonpath/internal/token/EvaluationContextImpl.java
  9. 1
      json-path/src/test/java/com/jayway/jsonpath/BaseTest.java
  10. 100
      json-path/src/test/java/com/jayway/jsonpath/EvaluationListenerTest.java

3
json-path-assert/src/test/java/com/jayway/jsonassert/JsonAssertTest.java

@ -1,9 +1,6 @@
package com.jayway.jsonassert;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.PathCompiler;
import org.junit.Test;
import java.io.InputStream;

52
json-path/src/main/java/com/jayway/jsonpath/Configuration.java

@ -18,7 +18,12 @@ import com.jayway.jsonpath.internal.spi.json.JsonSmartJsonProvider;
import com.jayway.jsonpath.internal.spi.mapper.DefaultMappingProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
@ -31,6 +36,8 @@ import static java.util.Arrays.asList;
*/
public class Configuration {
private static final Logger logger = LoggerFactory.getLogger(Configuration.class);
private static Defaults DEFAULTS = new Defaults() {
private final MappingProvider mappingProvider = new DefaultMappingProvider();
@ -50,6 +57,8 @@ public class Configuration {
}
};
/**
* Set Default configuration
* @param defaults default configuration settings
@ -61,14 +70,34 @@ public class Configuration {
private final JsonProvider jsonProvider;
private final MappingProvider mappingProvider;
private final Set<Option> options;
private final Collection<EvaluationListener> evaluationListeners;
private Configuration(JsonProvider jsonProvider, MappingProvider mappingProvider, EnumSet<Option> options) {
private Configuration(JsonProvider jsonProvider, MappingProvider mappingProvider, EnumSet<Option> options, Collection<EvaluationListener> evaluationListeners) {
notNull(jsonProvider, "jsonProvider can not be null");
notNull(mappingProvider, "mappingProvider can not be null");
notNull(options, "setOptions can not be null");
notNull(evaluationListeners, "evaluationListener can not be null");
this.jsonProvider = jsonProvider;
this.mappingProvider = mappingProvider;
this.options = Collections.unmodifiableSet(options);
this.evaluationListeners = Collections.unmodifiableCollection(evaluationListeners);
}
/**
* Creates a new Configuration with the provided evaluation listeners
* @param evaluationListener listeners
* @return a new configuration
*/
public Configuration evaluationListener(EvaluationListener... evaluationListener){
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListener).build();
}
/**
* Returns the evaluation listeners registered in this configuration
* @return the evaluation listeners
*/
public Collection<EvaluationListener> evaluationListeners(){
return evaluationListeners;
}
/**
@ -77,7 +106,7 @@ public class Configuration {
* @return a new configuration
*/
public Configuration jsonProvider(JsonProvider newJsonProvider) {
return Configuration.builder().jsonProvider(newJsonProvider).mappingProvider(mappingProvider).options(options).build();
return Configuration.builder().jsonProvider(newJsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build();
}
/**
@ -94,7 +123,7 @@ public class Configuration {
* @return a new configuration
*/
public Configuration mappingProvider(MappingProvider newMappingProvider) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(newMappingProvider).options(options).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(newMappingProvider).options(options).evaluationListener(evaluationListeners).build();
}
/**
@ -114,7 +143,7 @@ public class Configuration {
EnumSet<Option> opts = EnumSet.noneOf(Option.class);
opts.addAll(this.options);
opts.addAll(asList(options));
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(opts).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(opts).evaluationListener(evaluationListeners).build();
}
/**
@ -123,7 +152,7 @@ public class Configuration {
* @return
*/
public Configuration setOptions(Option... options) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).build();
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build();
}
/**
@ -167,6 +196,7 @@ public class Configuration {
private JsonProvider jsonProvider;
private MappingProvider mappingProvider;
private EnumSet<Option> options = EnumSet.noneOf(Option.class);
private Collection<EvaluationListener> evaluationListener = new ArrayList<EvaluationListener>();
public ConfigurationBuilder jsonProvider(JsonProvider provider) {
this.jsonProvider = provider;
@ -190,6 +220,16 @@ public class Configuration {
return this;
}
public ConfigurationBuilder evaluationListener(EvaluationListener... listener){
this.evaluationListener = Arrays.asList(listener);
return this;
}
public ConfigurationBuilder evaluationListener(Collection<EvaluationListener> listeners){
this.evaluationListener = listeners == null ? Collections.<EvaluationListener>emptyList() : listeners;
return this;
}
public Configuration build() {
if (jsonProvider == null) {
jsonProvider = DEFAULTS.jsonProvider();
@ -197,7 +237,7 @@ public class Configuration {
if(mappingProvider == null){
mappingProvider = DEFAULTS.mappingProvider();
}
return new Configuration(jsonProvider, mappingProvider, options);
return new Configuration(jsonProvider, mappingProvider, options, evaluationListener);
}
}

49
json-path/src/main/java/com/jayway/jsonpath/EvaluationListener.java

@ -0,0 +1,49 @@
package com.jayway.jsonpath;
/**
* A listener that can be registered on a {@link com.jayway.jsonpath.Configuration} that is notified when a
* result is added to the result of this path evaluation.
*/
public interface EvaluationListener {
/**
* Callback invoked when result is found
* @param found the found result
* @return continuation instruction
*/
EvaluationContinuation resultFound(FoundResult found);
public static enum EvaluationContinuation {
/**
* Evaluation continues
*/
CONTINUE,
/**
* Current result is included but no further evaluation will be performed.
*/
ABORT
}
/**
*
*/
public interface FoundResult {
/**
* the index of this result. First result i 0
* @return index
*/
int index();
/**
* The path of this result
* @return path
*/
String path();
/**
* The result object
* @return the result object
*/
Object result();
}
}

1
json-path/src/main/java/com/jayway/jsonpath/ReadContext.java

@ -71,5 +71,6 @@ public interface ReadContext {
<T> T read(JsonPath path, Class<T> type);
ReadContext withListeners(EvaluationListener... listener);
}

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

@ -46,7 +46,9 @@ public class CompiledPath implements Path {
}
EvaluationContextImpl ctx = new EvaluationContextImpl(this, rootDocument, configuration);
try {
root.evaluate("", document, ctx);
} catch (EvaluationAbortException abort){};
return ctx;
}

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

@ -0,0 +1,4 @@
package com.jayway.jsonpath.internal;
public class EvaluationAbortException extends RuntimeException {
}

13
json-path/src/main/java/com/jayway/jsonpath/internal/JsonReader.java

@ -15,6 +15,7 @@
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.EvaluationListener;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.ParseContext;
import com.jayway.jsonpath.Predicate;
@ -44,6 +45,13 @@ public class JsonReader implements ParseContext, ReadContext {
this.configuration = configuration;
}
private JsonReader(Object json, Configuration configuration) {
notNull(json, "json can not be null");
notNull(configuration, "configuration can not be null");
this.configuration = configuration;
this.json = json;
}
//------------------------------------------------
//
// ParseContext impl
@ -137,6 +145,11 @@ public class JsonReader implements ParseContext, ReadContext {
return convert(read(path), type, configuration);
}
public ReadContext withListeners(EvaluationListener... listener){
return new JsonReader(json, configuration.evaluationListener(listener));
}
private <T> T convert(Object obj, Class<T> targetType, Configuration configuration){
return configuration.mappingProvider().map(obj, targetType, configuration);
}

40
json-path/src/main/java/com/jayway/jsonpath/internal/token/EvaluationContextImpl.java

@ -15,8 +15,10 @@
package com.jayway.jsonpath.internal.token;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.EvaluationListener;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.EvaluationAbortException;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.spi.json.JsonProvider;
@ -24,7 +26,6 @@ import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static com.jayway.jsonpath.internal.Utils.notNull;
@ -62,6 +63,15 @@ public class EvaluationContextImpl implements EvaluationContext {
configuration.jsonProvider().setProperty(valueResult, resultIndex, model);
configuration.jsonProvider().setProperty(pathResult, resultIndex, path);
resultIndex++;
if(!configuration().evaluationListeners().isEmpty()){
int idx = resultIndex - 1;
for (EvaluationListener listener : configuration().evaluationListeners()) {
EvaluationListener.EvaluationContinuation continuation = listener.resultFound(new FoundResultImpl(idx, path, model));
if(EvaluationListener.EvaluationContinuation.ABORT == continuation){
throw new EvaluationAbortException();
}
}
}
}
public JsonProvider jsonProvider() {
@ -116,4 +126,32 @@ public class EvaluationContextImpl implements EvaluationContext {
return res;
}
private class FoundResultImpl implements EvaluationListener.FoundResult {
private final int index;
private final String path;
private final Object result;
private FoundResultImpl(int index, String path, Object result) {
this.index = index;
this.path = path;
this.result = result;
}
@Override
public int index() {
return index;
}
@Override
public String path() {
return path;
}
@Override
public Object result() {
return result;
}
}
}

1
json-path/src/test/java/com/jayway/jsonpath/BaseTest.java

@ -5,7 +5,6 @@ import com.jayway.jsonpath.internal.spi.json.GsonJsonProvider;
import com.jayway.jsonpath.internal.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.internal.spi.mapper.GsonMappingProvider;
import com.jayway.jsonpath.internal.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.internal.token.EvaluationContextImpl;
import com.jayway.jsonpath.internal.token.PredicateContextImpl;
import java.util.HashMap;

100
json-path/src/test/java/com/jayway/jsonpath/EvaluationListenerTest.java

@ -0,0 +1,100 @@
package com.jayway.jsonpath;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class EvaluationListenerTest extends BaseTest {
@Test
public void an_evaluation_listener_can_abort_after_one_result_using_fluent_api() {
EvaluationListener firstResultListener = new EvaluationListener() {
@Override
public EvaluationContinuation resultFound(FoundResult found) {
return EvaluationContinuation.ABORT;
}
};
List<String> title = JsonPath.parse(JSON_DOCUMENT).withListeners(firstResultListener).read("$..title", List.class);
assertThat(title).containsExactly("Sayings of the Century");
}
@Test
public void an_evaluation_listener_can_abort_after_one_result_using_configuration() {
EvaluationListener firstResultListener = new EvaluationListener() {
@Override
public EvaluationContinuation resultFound(FoundResult found) {
return EvaluationContinuation.ABORT;
}
};
Configuration configuration = Configuration.builder().evaluationListener(firstResultListener).build();
List<String> title = JsonPath.using(configuration).parse(JSON_DOCUMENT).read("$..title", List.class);
assertThat(title).containsExactly("Sayings of the Century");
}
@Test
public void an_evaluation_lister_can_continue() {
final List<Integer> idxs = new ArrayList<Integer>();
EvaluationListener firstResultListener = new EvaluationListener() {
@Override
public EvaluationContinuation resultFound(FoundResult found) {
idxs.add(found.index());
return EvaluationContinuation.CONTINUE;
}
};
List<String> title = JsonPath.parse(JSON_DOCUMENT).withListeners(firstResultListener).read("$..title", List.class);
assertThat(title).containsExactly("Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings");
assertThat(idxs).containsExactly(0, 1, 2, 3);
}
@Test
public void multiple_evaluation_listeners_can_be_added() {
final List<Integer> idxs1 = new ArrayList<Integer>();
final List<Integer> idxs2 = new ArrayList<Integer>();
EvaluationListener listener1 = new EvaluationListener() {
@Override
public EvaluationContinuation resultFound(FoundResult found) {
idxs1.add(found.index());
return EvaluationContinuation.CONTINUE;
}
};
EvaluationListener listener2 = new EvaluationListener() {
@Override
public EvaluationContinuation resultFound(FoundResult found) {
idxs2.add(found.index());
return EvaluationContinuation.CONTINUE;
}
};
List<String> title = JsonPath.parse(JSON_DOCUMENT).withListeners(listener1, listener2).read("$..title", List.class);
assertThat(title).containsExactly("Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings");
assertThat(idxs1).containsExactly(0, 1, 2, 3);
assertThat(idxs2).containsExactly(0, 1, 2, 3);
}
@Test
public void evaluation_listeners_can_be_cleared() {
EvaluationListener listener = new EvaluationListener() {
@Override
public EvaluationContinuation resultFound(FoundResult found) {
return EvaluationContinuation.CONTINUE;
}
};
Configuration configuration1 = Configuration.builder().evaluationListener(listener).build();
Configuration configuration2 = configuration1.evaluationListener();
assertThat(configuration1.evaluationListeners()).hasSize(1);
assertThat(configuration2.evaluationListeners()).hasSize(0);
}
}
Loading…
Cancel
Save