Kalle Stenflo
13 years ago
4 changed files with 672 additions and 0 deletions
@ -0,0 +1,434 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import javax.lang.model.type.NullType; |
||||
import java.util.*; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static java.util.Arrays.asList; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/5/12 |
||||
* Time: 12:08 PM |
||||
*/ |
||||
public class Criteria { |
||||
|
||||
|
||||
/** |
||||
* Custom "not-null" object as we have to be able to work with {@literal null} values as well. |
||||
*/ |
||||
private static final Object NOT_SET = new Object(); |
||||
|
||||
private String key; |
||||
|
||||
private List<Criteria> criteriaChain; |
||||
|
||||
private LinkedHashMap<String, Object> criteria = new LinkedHashMap<String, Object>(); |
||||
|
||||
private Object isValue = NOT_SET; |
||||
|
||||
|
||||
public Criteria(String key) { |
||||
this.criteriaChain = new ArrayList<Criteria>(); |
||||
this.criteriaChain.add(this); |
||||
this.key = key; |
||||
} |
||||
|
||||
protected Criteria(List<Criteria> criteriaChain, String key) { |
||||
this.criteriaChain = criteriaChain; |
||||
this.criteriaChain.add(this); |
||||
this.key = key; |
||||
} |
||||
|
||||
public String getKey() { |
||||
return this.key; |
||||
} |
||||
|
||||
public boolean apply(Map<String, Object> map) { |
||||
|
||||
if (this.criteriaChain.size() == 1) { |
||||
return criteriaChain.get(0).singleObjectApply(map); |
||||
} else { |
||||
|
||||
for (Criteria c : this.criteriaChain) { |
||||
|
||||
if (!c.singleObjectApply(map)) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
protected boolean singleObjectApply(Map<String, Object> map) { |
||||
|
||||
boolean not = false; |
||||
for (String key : this.criteria.keySet()) { |
||||
Object expectedVal = null; |
||||
Object actualVal = map.get(this.key); |
||||
if (not) { |
||||
expectedVal = this.criteria.get(key); |
||||
not = false; |
||||
} else { |
||||
if ("$not".equals(key)) { |
||||
not = true; |
||||
} else { |
||||
expectedVal = this.criteria.get(key); |
||||
if ("$gt".equals(key)) { |
||||
|
||||
Number expectedNumber = (Number) expectedVal; |
||||
Number actualNumber = (Number) actualVal; |
||||
|
||||
return (actualNumber.doubleValue() > expectedNumber.doubleValue()); |
||||
|
||||
} else if ("$gte".equals(key)) { |
||||
|
||||
Number expectedNumber = (Number) expectedVal; |
||||
Number actualNumber = (Number) actualVal; |
||||
|
||||
return (actualNumber.doubleValue() >= expectedNumber.doubleValue()); |
||||
|
||||
} else if ("$lt".equals(key)) { |
||||
|
||||
Number expectedNumber = (Number) expectedVal; |
||||
Number actualNumber = (Number) actualVal; |
||||
|
||||
return (actualNumber.doubleValue() < expectedNumber.doubleValue()); |
||||
|
||||
} else if ("$lte".equals(key)) { |
||||
|
||||
Number expectedNumber = (Number) expectedVal; |
||||
Number actualNumber = (Number) actualVal; |
||||
|
||||
return (actualNumber.doubleValue() <= expectedNumber.doubleValue()); |
||||
|
||||
} else if ("$ne".equals(key)) { |
||||
|
||||
return !expectedVal.equals(actualVal); |
||||
|
||||
} else if ("$in".equals(key)) { |
||||
|
||||
Collection exp = (Collection) expectedVal; |
||||
|
||||
return exp.contains(actualVal); |
||||
|
||||
} else if ("$nin".equals(key)) { |
||||
|
||||
Collection exp = (Collection) expectedVal; |
||||
|
||||
return !exp.contains(actualVal); |
||||
} else if ("$all".equals(key)) { |
||||
|
||||
Collection exp = (Collection) expectedVal; |
||||
Collection act = (Collection) actualVal; |
||||
|
||||
return act.containsAll(exp); |
||||
|
||||
} else if ("$size".equals(key)) { |
||||
|
||||
int exp = (Integer) expectedVal; |
||||
List act = (List) actualVal; |
||||
|
||||
return (act.size() == exp); |
||||
|
||||
} else if ("$exists".equals(key)) { |
||||
|
||||
boolean exp = (Boolean) expectedVal; |
||||
boolean act = map.containsKey(this.key); |
||||
|
||||
return act == exp; |
||||
|
||||
} else if ("$type".equals(key)) { |
||||
|
||||
Class<?> exp = (Class<?>) expectedVal; |
||||
Class<?> act = map.containsKey(this.key) ? map.get(this.key).getClass() : NullType.class; |
||||
|
||||
return act.equals(exp); |
||||
|
||||
} else if ("$regex".equals(key)) { |
||||
|
||||
|
||||
Pattern exp = (Pattern) expectedVal; |
||||
String act = (String) actualVal; |
||||
|
||||
return exp.matcher(act).matches(); |
||||
|
||||
} else if ("$or".equals(key)) { |
||||
|
||||
|
||||
System.out.println("auch"); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
} |
||||
if (isValue != NOT_SET) { |
||||
return isValue.equals(map.get(key)); |
||||
} else { |
||||
|
||||
} |
||||
return true; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Static factory method to create a Criteria using the provided key |
||||
* |
||||
* @param key |
||||
* @return |
||||
*/ |
||||
|
||||
public static Criteria where(String key) { |
||||
return new Criteria(key); |
||||
} |
||||
|
||||
/** |
||||
* Static factory method to create a Criteria using the provided key |
||||
* |
||||
* @return |
||||
*/ |
||||
public Criteria and(String key) { |
||||
return new Criteria(this.criteriaChain, key); |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using equality |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria is(Object o) { |
||||
if (isValue != NOT_SET) { |
||||
throw new InvalidCriteriaException( |
||||
"Multiple 'is' values declared. You need to use 'and' with multiple criteria"); |
||||
} |
||||
if (this.criteria.size() > 0 && "$not".equals(this.criteria.keySet().toArray()[this.criteria.size() - 1])) { |
||||
throw new InvalidCriteriaException("Invalid query: 'not' can't be used with 'is' - use 'ne' instead."); |
||||
} |
||||
this.isValue = o; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $ne operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria ne(Object o) { |
||||
criteria.put("$ne", o); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $lt operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria lt(Object o) { |
||||
criteria.put("$lt", o); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $lte operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria lte(Object o) { |
||||
criteria.put("$lte", o); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $gt operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria gt(Object o) { |
||||
criteria.put("$gt", o); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $gte operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria gte(Object o) { |
||||
criteria.put("$gte", o); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $in operator |
||||
* |
||||
* @param o the values to match against |
||||
* @return |
||||
*/ |
||||
public Criteria in(Object... o) { |
||||
if (o.length > 1 && o[1] instanceof Collection) { |
||||
throw new InvalidCriteriaException("You can only pass in one argument of type " |
||||
+ o[1].getClass().getName()); |
||||
} |
||||
return in(Arrays.asList(o)); |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $in operator |
||||
* |
||||
* @param c the collection containing the values to match against |
||||
* @return |
||||
*/ |
||||
public Criteria in(Collection<?> c) { |
||||
criteria.put("$in", c); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $nin operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria nin(Object... o) { |
||||
return nin(Arrays.asList(o)); |
||||
} |
||||
|
||||
public Criteria nin(Collection<?> o) { |
||||
criteria.put("$nin", o); |
||||
return this; |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* Creates a criterion using the $all operator |
||||
* |
||||
* @param o |
||||
* @return |
||||
*/ |
||||
public Criteria all(Object... o) { |
||||
return all(Arrays.asList(o)); |
||||
} |
||||
|
||||
public Criteria all(Collection<?> o) { |
||||
criteria.put("$all", o); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $size operator |
||||
* |
||||
* @param s |
||||
* @return |
||||
*/ |
||||
public Criteria size(int s) { |
||||
criteria.put("$size", s); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $exists operator |
||||
* |
||||
* @param b |
||||
* @return |
||||
*/ |
||||
public Criteria exists(boolean b) { |
||||
criteria.put("$exists", b); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $type operator |
||||
* |
||||
* @param t |
||||
* @return |
||||
*/ |
||||
public Criteria type(Class<?> t) { |
||||
criteria.put("$type", t); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $not meta operator which affects the clause directly following |
||||
* |
||||
* @return |
||||
*/ |
||||
public Criteria not() { |
||||
criteria.put("$not", null); |
||||
return this; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Creates a criterion using a $regex and $options |
||||
* |
||||
* @param pattern |
||||
* @return |
||||
*/ |
||||
public Criteria regex(Pattern pattern) { |
||||
criteria.put("$regex", pattern); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a criterion using the $mod operator |
||||
* |
||||
* @param value |
||||
* @param remainder |
||||
* @return |
||||
*/ |
||||
/* |
||||
public Criteria mod(Number value, Number remainder) { |
||||
List<Object> l = new ArrayList<Object>(); |
||||
l.add(value); |
||||
l.add(remainder); |
||||
criteria.put("$mod", l); |
||||
return this; |
||||
} |
||||
*/ |
||||
|
||||
/** |
||||
* Creates an 'or' criteria using the $or operator for all of the provided criteria |
||||
* |
||||
* @param criteria |
||||
*/ |
||||
public Criteria orOperator(Criteria... criteria) { |
||||
criteriaChain.add(new Criteria("$or").is(asList(criteria))); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Creates a 'nor' criteria using the $nor operator for all of the provided criteria |
||||
* |
||||
* @param criteria |
||||
*/ |
||||
/* |
||||
public Criteria norOperator(Criteria... criteria) { |
||||
criteriaChain.add(new Criteria("$nor").is(asList(criteria))); |
||||
return this; |
||||
}*/ |
||||
|
||||
/** |
||||
* Creates an 'and' criteria using the $and operator for all of the provided criteria |
||||
* |
||||
* @param criteria |
||||
*/ |
||||
public Criteria andOperator(Criteria... criteria) { |
||||
criteriaChain.add(new Criteria("$and").is(asList(criteria))); |
||||
return this; |
||||
} |
||||
|
||||
|
||||
|
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import java.util.*; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/5/12 |
||||
* Time: 12:05 PM |
||||
*/ |
||||
public class Filter { |
||||
|
||||
private HashMap<String, Criteria> criteria = new LinkedHashMap<String, Criteria>(); |
||||
|
||||
public Filter(Criteria criteria) { |
||||
addCriteria(criteria); |
||||
} |
||||
|
||||
|
||||
public static Filter filter(Criteria criteria) { |
||||
return new Filter(criteria); |
||||
} |
||||
|
||||
|
||||
public Filter addCriteria(Criteria criteria) { |
||||
Criteria existing = this.criteria.get(criteria.getKey()); |
||||
String key = criteria.getKey(); |
||||
if (existing == null) { |
||||
this.criteria.put(key, criteria); |
||||
} else { |
||||
existing.andOperator(criteria); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
protected List<Criteria> getCriteria() { |
||||
return new ArrayList<Criteria>(this.criteria.values()); |
||||
} |
||||
|
||||
public boolean apply(Map<String, Object> map) { |
||||
|
||||
for (Criteria criterion : getCriteria()) { |
||||
if(!criterion.apply(map)){ |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,24 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/5/12 |
||||
* Time: 12:20 PM |
||||
*/ |
||||
public class InvalidCriteriaException extends RuntimeException{ |
||||
public InvalidCriteriaException() { |
||||
} |
||||
|
||||
public InvalidCriteriaException(String message) { |
||||
super(message); |
||||
} |
||||
|
||||
public InvalidCriteriaException(String message, Throwable cause) { |
||||
super(message, cause); |
||||
} |
||||
|
||||
public InvalidCriteriaException(Throwable cause) { |
||||
super(cause); |
||||
} |
||||
} |
@ -0,0 +1,162 @@
|
||||
package com.jayway.jsonpath; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import static com.jayway.jsonpath.Criteria.where; |
||||
import static com.jayway.jsonpath.Filter.filter; |
||||
import static java.util.Arrays.asList; |
||||
import static junit.framework.Assert.assertFalse; |
||||
import static junit.framework.Assert.assertTrue; |
||||
|
||||
/** |
||||
* Created by IntelliJ IDEA. |
||||
* User: kallestenflo |
||||
* Date: 3/5/12 |
||||
* Time: 12:27 PM |
||||
*/ |
||||
public class FilterTest { |
||||
|
||||
public final static String DOCUMENT = |
||||
"{ \"store\": {\n" + |
||||
" \"book\": [ \n" + |
||||
" { \"category\": \"reference\",\n" + |
||||
" \"author\": \"Nigel Rees\",\n" + |
||||
" \"title\": \"Sayings of the Century\",\n" + |
||||
" \"price\": 8.95\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Evelyn Waugh\",\n" + |
||||
" \"title\": \"Sword of Honour\",\n" + |
||||
" \"price\": 12.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"Herman Melville\",\n" + |
||||
" \"title\": \"Moby Dick\",\n" + |
||||
" \"isbn\": \"0-553-21311-3\",\n" + |
||||
" \"price\": 8.99\n" + |
||||
" },\n" + |
||||
" { \"category\": \"fiction\",\n" + |
||||
" \"author\": \"J. R. R. Tolkien\",\n" + |
||||
" \"title\": \"The Lord of the Rings\",\n" + |
||||
" \"isbn\": \"0-395-19395-8\",\n" + |
||||
" \"price\": 22.99\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"bicycle\": {\n" + |
||||
" \"color\": \"red\",\n" + |
||||
" \"price\": 19.95,\n" + |
||||
" \"foo:bar\": \"fooBar\",\n" + |
||||
" \"dot.notation\": \"new\"\n" + |
||||
" }\n" + |
||||
" }\n" + |
||||
"}"; |
||||
|
||||
|
||||
@Test |
||||
public void a_single_is_filter_can_be_built_and_applied() throws Exception { |
||||
Map<String, Object> check = JsonPath.read(DOCUMENT, "store.book[0]"); |
||||
Filter filter = Filter.filter(Criteria.where("category").is("reference")); |
||||
assertTrue(filter.apply(check)); |
||||
} |
||||
|
||||
@Test |
||||
public void multiple_filters_can_be_built_and_applied() throws Exception { |
||||
Map<String, Object> check = JsonPath.read(DOCUMENT, "store.book[0]"); |
||||
|
||||
Filter filter = Filter.filter(Criteria |
||||
.where("category").is("reference") |
||||
.and("author").is("Nigel Rees") |
||||
.and("price").gt(8) |
||||
.and("price").gte(8) |
||||
.and("price").lt(10) |
||||
.and("price").lte(10) |
||||
.and("title").ne("is not") |
||||
.and("title").in("is not", "Sayings of the Century") |
||||
.and("title").nin("is not this", "is not that") |
||||
); |
||||
assertTrue(filter.apply(check)); |
||||
} |
||||
|
||||
@Test |
||||
public void all_filters_evaluates() throws Exception { |
||||
Map<String, Object> check = new HashMap<String, Object>(); |
||||
check.put("items", asList(1, 2, 3)); |
||||
|
||||
|
||||
assertTrue(filter(where("items").all(1, 2, 3)).apply(check)); |
||||
assertFalse(filter(where("items").all(1, 2, 3, 4)).apply(check)); |
||||
} |
||||
|
||||
@Test |
||||
public void size_filters_evaluates() throws Exception { |
||||
Map<String, Object> check = new HashMap<String, Object>(); |
||||
check.put("items", asList(1, 2, 3)); |
||||
|
||||
|
||||
assertTrue(filter(where("items").size(3)).apply(check)); |
||||
assertFalse(filter(where("items").size(2)).apply(check)); |
||||
} |
||||
|
||||
@Test |
||||
public void exists_filters_evaluates() throws Exception { |
||||
Map<String, Object> check = new HashMap<String, Object>(); |
||||
check.put("foo", "foo"); |
||||
|
||||
|
||||
assertTrue(filter(where("foo").exists(true)).apply(check)); |
||||
assertFalse(filter(where("foo").exists(false)).apply(check)); |
||||
|
||||
assertTrue(filter(where("bar").exists(false)).apply(check)); |
||||
assertFalse(filter(where("bar").exists(true)).apply(check)); |
||||
} |
||||
|
||||
@Test |
||||
public void type_filters_evaluates() throws Exception { |
||||
Map<String, Object> check = new HashMap<String, Object>(); |
||||
check.put("string", "foo"); |
||||
check.put("int", 1); |
||||
check.put("long", 1L); |
||||
check.put("double", 1.12D); |
||||
|
||||
|
||||
assertTrue(filter(where("string").type(String.class)).apply(check)); |
||||
assertFalse(filter(where("string").type(Number.class)).apply(check)); |
||||
|
||||
assertTrue(filter(where("int").type(Integer.class)).apply(check)); |
||||
assertFalse(filter(where("int").type(Long.class)).apply(check)); |
||||
|
||||
assertTrue(filter(where("long").type(Long.class)).apply(check)); |
||||
assertFalse(filter(where("long").type(Integer.class)).apply(check)); |
||||
|
||||
assertTrue(filter(where("double").type(Double.class)).apply(check)); |
||||
assertFalse(filter(where("double").type(Integer.class)).apply(check)); |
||||
} |
||||
|
||||
@Test |
||||
public void pattern_filters_evaluates() throws Exception { |
||||
Map<String, Object> check = new HashMap<String, Object>(); |
||||
check.put("string", "kalle"); |
||||
|
||||
|
||||
assertTrue(filter(where("string").regex(Pattern.compile(".alle"))).apply(check)); |
||||
assertFalse(filter(where("string").regex(Pattern.compile("KALLE"))).apply(check)); |
||||
assertTrue(filter(where("string").regex(Pattern.compile("KALLE", Pattern.CASE_INSENSITIVE))).apply(check)); |
||||
|
||||
} |
||||
|
||||
/* |
||||
@Test |
||||
public void or_filters_evaluates() throws Exception { |
||||
Map<String, Object> check = new HashMap<String, Object>(); |
||||
check.put("string", "kalle"); |
||||
|
||||
|
||||
assertTrue(filter(where("string").is("x").orOperator(where("string").is("kalle"))).apply(check)); |
||||
}*/ |
||||
|
||||
|
||||
} |
Loading…
Reference in new issue