/* * Copyright 2011 the original author or authors. * Licensed 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 com.jayway.jsonpath; import com.jayway.jsonpath.internal.spi.compiler.PathCompiler; import com.jayway.jsonpath.spi.compiler.Path; import com.jayway.jsonpath.internal.Utils; import java.util.*; import java.util.regex.Pattern; import static com.jayway.jsonpath.internal.Utils.isNumeric; import static com.jayway.jsonpath.internal.Utils.notNull; import static java.util.Arrays.asList; /** * @author Kalle Stenflo */ public class Criteria { private enum CriteriaType { GT, GTE, LT, LTE, NE, IN, NIN, ALL, SIZE, EXISTS, TYPE, REGEX, NOT_EMPTY, OR } /** * 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 final JsonPath key; //private final Path key; private final Path key; private final List criteriaChain; private final LinkedHashMap criteria = new LinkedHashMap(); private Object isValue = NOT_SET; private Criteria(String key) { Utils.notEmpty(key, "key can not be null or empty"); this.criteriaChain = new ArrayList(); this.criteriaChain.add(this); this.key = PathCompiler.tokenize(key); } private Criteria(Path path) { Utils.notNull(path, "path can not be null"); this.criteriaChain = new ArrayList(); this.criteriaChain.add(this); this.key = path; } private Criteria(List criteriaChain, String key) { Utils.notEmpty(key, "key can not be null or empty"); this.criteriaChain = criteriaChain; //this.key = JsonPath.compile(key); this.criteriaChain.add(this); //this.key = PathCompiler.compile(key); this.key = PathCompiler.tokenize(key); } //JsonPath getKey() { //Path getKey() { Path getKey() { return this.key; } /** * Checks if this criteria matches the given map * * @param map map to check * @return true if criteria is a match */ boolean matches(Object map, Configuration configuration) { for (Criteria c : this.criteriaChain) { if (!c.singleObjectApply(map, configuration)) { return false; } } return true; } //private static Object readSafely(JsonPath path, Map map) { //private static Object readSafely(Path path, Object map) { private static Object readSafely(Path path, Object map) { try { return path.evaluate(map, Configuration.defaultConfiguration()).get(); //return PathEvaluator.evaluate(path, map, JsonProviderFactory.createProvider(), Collections.EMPTY_SET).getResult(); //return path. read(map); } catch (InvalidPathException e) { return null; } } private static boolean objectOrAnyCollectionItemMatches(final Object singleObjectOrCollection, final Predicate predicate) { if (singleObjectOrCollection instanceof Collection) { Iterator it = ((Collection) singleObjectOrCollection).iterator(); while (it.hasNext()) { if (predicate.accept((T) it.next())) { return true; } } return false; } return predicate.accept((T) singleObjectOrCollection); } private boolean singleObjectApply(Object map, final Configuration configuration) { for (CriteriaType key : this.criteria.keySet()) { final Object actualVal = readSafely(this.key, map); final Object expectedVal = this.criteria.get(key); if (CriteriaType.GT.equals(key)) { if (expectedVal == null || actualVal == null) { return false; } final Number expectedNumber = (Number) expectedVal; return objectOrAnyCollectionItemMatches(actualVal, new Predicate() { @Override public boolean accept(Number value) { return (value.doubleValue() > expectedNumber.doubleValue()); } }); } else if (CriteriaType.GTE.equals(key)) { if (expectedVal == null || actualVal == null) { return false; } final Number expectedNumber = (Number) expectedVal; return objectOrAnyCollectionItemMatches(actualVal, new Predicate() { @Override public boolean accept(Number value) { return (value.doubleValue() >= expectedNumber.doubleValue()); } }); } else if (CriteriaType.LT.equals(key)) { if (expectedVal == null || actualVal == null) { return false; } final Number expectedNumber = (Number) expectedVal; return objectOrAnyCollectionItemMatches(actualVal, new Predicate() { @Override public boolean accept(Number value) { return (value.doubleValue() < expectedNumber.doubleValue()); } }); } else if (CriteriaType.LTE.equals(key)) { if (expectedVal == null || actualVal == null) { return false; } final Number expectedNumber = (Number) expectedVal; return objectOrAnyCollectionItemMatches(actualVal, new Predicate() { @Override public boolean accept(Number value) { return (value.doubleValue() <= expectedNumber.doubleValue()); } }); } else if (CriteriaType.NE.equals(key)) { return objectOrAnyCollectionItemMatches(actualVal, new Predicate() { @Override public boolean accept(Object value) { if (expectedVal == null && value == null) { return false; } if (expectedVal == null) { return true; } else { return !expectedVal.equals(value); } } }); } else if (CriteriaType.IN.equals(key)) { Collection exp = (Collection) expectedVal; return exp.contains(actualVal); } else if (CriteriaType.NIN.equals(key)) { Collection exp = (Collection) expectedVal; return !exp.contains(actualVal); } else if (CriteriaType.ALL.equals(key)) { Collection exp = (Collection) expectedVal; Collection act = (Collection) actualVal; return act.containsAll(exp); } else if (CriteriaType.SIZE.equals(key)) { int exp = (Integer) expectedVal; List act = (List) actualVal; return (act.size() == exp); } else if (CriteriaType.NOT_EMPTY.equals(key)) { if (actualVal == null) { return false; } boolean empty = false; if (actualVal instanceof Collection) { empty = ((List) actualVal).isEmpty(); } else if (actualVal instanceof String) { empty = ((String) actualVal).isEmpty(); } return !empty; } else if (CriteriaType.EXISTS.equals(key)) { if (configuration.getProvider().isArray(map)) { return false; } final boolean exp = (Boolean) expectedVal; return objectOrAnyCollectionItemMatches(map, new Predicate() { @Override public boolean accept(final Object value) { boolean act = true; Object res; try { Set