@ -58,9 +58,9 @@ public class Criteria implements Predicate {
private static enum CriteriaType {
EQ {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
boolean res = ( 0 = = safeCompare ( left , right ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , left , res ) ;
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
boolean res = ( 0 = = safeCompare ( expected , model ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , expected , res ) ;
return res ;
}
@ -71,9 +71,9 @@ public class Criteria implements Predicate {
} ,
NE {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
boolean res = ( 0 ! = safeCompare ( left , right ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , left , res ) ;
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
boolean res = ( 0 ! = safeCompare ( expected , model ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , expected , res ) ;
return res ;
}
@ -84,14 +84,15 @@ public class Criteria implements Predicate {
} ,
GT {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
if ( ( left = = null ) ^ ( right = = null ) ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
if ( ( expected = = null ) ^ ( model = = null ) ) {
return false ;
}
boolean res = ( 0 > safeCompare ( left , right ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , left , res ) ;
boolean res = ( 0 > safeCompare ( expected , model ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , expected , res ) ;
return res ;
}
@Override
public String toString ( ) {
return ">" ;
@ -99,14 +100,15 @@ public class Criteria implements Predicate {
} ,
GTE {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
if ( ( left = = null ) ^ ( right = = null ) ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
if ( ( expected = = null ) ^ ( model = = null ) ) {
return false ;
}
boolean res = ( 0 > = safeCompare ( left , right ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , left , res ) ;
boolean res = ( 0 > = safeCompare ( expected , model ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , expected , res ) ;
return res ;
}
@Override
public String toString ( ) {
return ">=" ;
@ -114,14 +116,15 @@ public class Criteria implements Predicate {
} ,
LT {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
if ( ( left = = null ) ^ ( right = = null ) ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
if ( ( expected = = null ) ^ ( model = = null ) ) {
return false ;
}
boolean res = ( 0 < safeCompare ( left , right ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , left , res ) ;
boolean res = ( 0 < safeCompare ( expected , model ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , expected , res ) ;
return res ;
}
@Override
public String toString ( ) {
return "<" ;
@ -129,14 +132,15 @@ public class Criteria implements Predicate {
} ,
LTE {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
if ( ( left = = null ) ^ ( right = = null ) ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
if ( ( expected = = null ) ^ ( model = = null ) ) {
return false ;
}
boolean res = ( 0 < = safeCompare ( left , right ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , left , res ) ;
boolean res = ( 0 < = safeCompare ( expected , model ) ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , expected , res ) ;
return res ;
}
@Override
public String toString ( ) {
return "<=" ;
@ -144,37 +148,59 @@ public class Criteria implements Predicate {
} ,
IN {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
boolean res = false ;
Collection exps = ( Collection ) left ;
Collection exps = ( Collection ) expected ;
for ( Object exp : exps ) {
if ( 0 = = safeCompare ( exp , right ) ) {
if ( 0 = = safeCompare ( exp , model ) ) {
res = true ;
break ;
}
}
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , join ( ", " , exps ) , res ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , join ( ", " , exps ) , res ) ;
return res ;
}
} ,
NIN {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
Collection nexps = ( Collection ) left ;
boolean res = ! nexps . contains ( right ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right , name ( ) , join ( ", " , nexps ) , res ) ;
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
Collection nexps = ( Collection ) expected ;
boolean res = ! nexps . contains ( model ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , join ( ", " , nexps ) , res ) ;
return res ;
}
} ,
CONTAINS {
@Override
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
boolean res = false ;
if ( ctx . configuration ( ) . jsonProvider ( ) . isArray ( model ) ) {
for ( Object o : ctx . configuration ( ) . jsonProvider ( ) . toIterable ( model ) ) {
if ( expected . equals ( o ) ) {
res = true ;
break ;
}
}
} else if ( model instanceof String ) {
if ( isNullish ( expected ) | | ! ( expected instanceof String ) ) {
res = false ;
} else {
res = ( ( String ) model ) . contains ( ( String ) expected ) ;
}
}
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , model , name ( ) , expected , res ) ;
return res ;
}
} ,
ALL {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
boolean res = true ;
Collection exps = ( Collection ) left ;
if ( ctx . configuration ( ) . jsonProvider ( ) . isArray ( right ) ) {
Collection exps = ( Collection ) expected ;
if ( ctx . configuration ( ) . jsonProvider ( ) . isArray ( model ) ) {
for ( Object exp : exps ) {
boolean found = false ;
for ( Object check : ctx . configuration ( ) . jsonProvider ( ) . toIterable ( right ) ) {
for ( Object check : ctx . configuration ( ) . jsonProvider ( ) . toIterable ( model ) ) {
if ( 0 = = safeCompare ( exp , check ) ) {
found = true ;
break ;
@ -185,71 +211,75 @@ public class Criteria implements Predicate {
break ;
}
}
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , join ( ", " , ctx . configuration ( ) . jsonProvider ( ) . toIterable ( right ) ) , name ( ) , join ( ", " , exps ) , res ) ;
if ( logger . isDebugEnabled ( ) )
logger . debug ( "[{}] {} [{}] => {}" , join ( ", " , ctx . configuration ( ) . jsonProvider ( ) . toIterable ( model ) ) , name ( ) , join ( ", " , exps ) , res ) ;
} else {
res = false ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , "<NOT AN ARRAY>" , name ( ) , join ( ", " , exps ) , res ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , "<NOT AN ARRAY>" , name ( ) , join ( ", " , exps ) , res ) ;
}
return res ;
}
} ,
SIZE {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
int size = ( Integer ) left ;
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
int size = ( Integer ) expected ;
boolean res ;
if ( ctx . configuration ( ) . jsonProvider ( ) . isArray ( right ) ) {
int length = ctx . configuration ( ) . jsonProvider ( ) . length ( right ) ;
if ( ctx . configuration ( ) . jsonProvider ( ) . isArray ( model ) ) {
int length = ctx . configuration ( ) . jsonProvider ( ) . length ( model ) ;
res = ( length = = size ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "Array with size {} {} {} => {}" , length , name ( ) , size , res ) ;
} else if ( right instanceof String ) {
int length = ( ( String ) right ) . length ( ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "Array with size {} {} {} => {}" , length , name ( ) , size , res ) ;
} else if ( model instanceof String ) {
int length = ( ( String ) model ) . length ( ) ;
res = length = = size ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "String with length {} {} {} => {}" , length , name ( ) , size , res ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "String with length {} {} {} => {}" , length , name ( ) , size , res ) ;
} else {
res = false ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "{} {} {} => {}" , right = = null ? "null" : right . getClass ( ) . getName ( ) , name ( ) , size , res ) ;
if ( logger . isDebugEnabled ( ) )
logger . debug ( "{} {} {} => {}" , model = = null ? "null" : model . getClass ( ) . getName ( ) , name ( ) , size , res ) ;
}
return res ;
}
} ,
EXISTS {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
//This must be handled outside
throw new UnsupportedOperationException ( ) ;
}
} ,
TYPE {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
final Class < ? > expType = ( Class < ? > ) left ;
final Class < ? > actType = right = = null ? null : right . getClass ( ) ;
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
final Class < ? > expType = ( Class < ? > ) expected ;
final Class < ? > actType = model = = null ? null : model . getClass ( ) ;
return actType ! = null & & expType . isAssignableFrom ( actType ) ;
}
} ,
REGEX {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
boolean res = false ;
Pattern pattern ;
Object target ;
Object target ;
if ( right instanceof Pattern ) {
pattern = ( Pattern ) right ;
target = left ;
if ( model instanceof Pattern ) {
pattern = ( Pattern ) model ;
target = expected ;
} else {
pattern = ( Pattern ) left ;
target = right ;
pattern = ( Pattern ) expected ;
target = model ;
}
if ( target ! = null ) {
if ( target ! = null ) {
res = pattern . matcher ( target . toString ( ) ) . matches ( ) ;
}
if ( logger . isDebugEnabled ( ) ) logger . debug ( "[{}] {} [{}] => {}" , right = = null ? "null" : right . toString ( ) , name ( ) , left = = null ? "null" : left . toString ( ) , res ) ;
if ( logger . isDebugEnabled ( ) )
logger . debug ( "[{}] {} [{}] => {}" , model = = null ? "null" : model . toString ( ) , name ( ) , expected = = null ? "null" : expected . toString ( ) , res ) ;
return res ;
}
@Override
public String toString ( ) {
return "=~" ;
@ -257,32 +287,32 @@ public class Criteria implements Predicate {
} ,
MATCHES {
@Override
boolean eval ( Object left , final Object right , final PredicateContext ctx ) {
boolean eval ( Object expected , final Object model , final PredicateContext ctx ) {
PredicateContextImpl pci = ( PredicateContextImpl ) ctx ;
Predicate exp = ( Predicate ) left ;
return exp . apply ( new PredicateContextImpl ( right , ctx . root ( ) , ctx . configuration ( ) , pci . documentPathCache ( ) ) ) ;
Predicate exp = ( Predicate ) expected ;
return exp . apply ( new PredicateContextImpl ( model , ctx . root ( ) , ctx . configuration ( ) , pci . documentPathCache ( ) ) ) ;
}
} ,
NOT_EMPTY {
@Override
boolean eval ( Object left , Object right , PredicateContext ctx ) {
boolean eval ( Object expected , Object model , PredicateContext ctx ) {
boolean res = false ;
if ( right ! = null ) {
if ( ctx . configuration ( ) . jsonProvider ( ) . isArray ( right ) ) {
int len = ctx . configuration ( ) . jsonProvider ( ) . length ( right ) ;
if ( model ! = null ) {
if ( ctx . configuration ( ) . jsonProvider ( ) . isArray ( model ) ) {
int len = ctx . configuration ( ) . jsonProvider ( ) . length ( model ) ;
res = ( 0 ! = len ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "array length = {} {} => {}" , len , name ( ) , res ) ;
} else if ( right instanceof String ) {
int len = ( ( String ) right ) . length ( ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "array length = {} {} => {}" , len , name ( ) , res ) ;
} else if ( model instanceof String ) {
int len = ( ( String ) model ) . length ( ) ;
res = ( 0 ! = len ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "string length = {} {} => {}" , len , name ( ) , res ) ;
if ( logger . isDebugEnabled ( ) ) logger . debug ( "string length = {} {} => {}" , len , name ( ) , res ) ;
}
}
return res ;
}
} ;
abstract boolean eval ( Object left , Object right , PredicateContext ctx ) ;
abstract boolean eval ( Object expected , Object model , PredicateContext ctx ) ;
public static CriteriaType parse ( String str ) {
if ( "==" . equals ( str ) ) {
@ -306,12 +336,12 @@ public class Criteria implements Predicate {
}
private Criteria ( List < Criteria > criteriaChain , Object left ) {
/ *
if ( left instanceof Path ) {
if ( ! ( ( Path ) left ) . isDefinite ( ) ) {
throw new InvalidCriteriaException ( "A criteria path must be definite. The path " + left . toString ( ) + " is not!" ) ;
}
}
} * /
this . left = left ;
this . criteriaChain = criteriaChain ;
this . criteriaChain . add ( this ) ;
@ -338,17 +368,17 @@ public class Criteria implements Predicate {
return true ;
}
private Object evaluateIfPath ( Object target , PredicateContext ctx ) {
private Object evaluateIfPath ( Object target , PredicateContext ctx ) {
Object res = target ;
if ( res instanceof Path ) {
if ( res instanceof Path ) {
Path leftPath = ( Path ) target ;
if ( ctx instanceof PredicateContextImpl ) {
if ( ctx instanceof PredicateContextImpl ) {
//This will use cache for document ($) queries
PredicateContextImpl ctxi = ( PredicateContextImpl ) ctx ;
res = ctxi . evaluate ( leftPath ) ;
} else {
Object doc = leftPath . isRootPath ( ) ? ctx . root ( ) : ctx . item ( ) ;
Object doc = leftPath . isRootPath ( ) ? ctx . root ( ) : ctx . item ( ) ;
res = leftPath . evaluate ( doc , ctx . root ( ) , ctx . configuration ( ) ) . getValue ( ) ;
}
}
@ -361,8 +391,8 @@ public class Criteria implements Predicate {
try {
Configuration c = Configuration . builder ( ) . jsonProvider ( ctx . configuration ( ) . jsonProvider ( ) ) . options ( Option . REQUIRE_PROPERTIES ) . build ( ) ;
Object value = ( ( Path ) left ) . evaluate ( ctx . item ( ) , ctx . root ( ) , c ) . getValue ( ) ;
if ( exists ) {
return ( value ! = null ) ;
if ( exists ) {
return ( value ! = null ) ;
} else {
return ( value = = null ) ;
}
@ -403,7 +433,7 @@ public class Criteria implements Predicate {
* /
public static Criteria where ( String key ) {
if ( ! key . startsWith ( "$" ) & & ! key . startsWith ( "@" ) ) {
if ( ! key . startsWith ( "$" ) & & ! key . startsWith ( "@" ) ) {
key = "@." + key ;
}
return where ( PathCompiler . compile ( key ) ) ;
@ -416,7 +446,7 @@ public class Criteria implements Predicate {
* @return the criteria builder
* /
public Criteria and ( String key ) {
if ( ! key . startsWith ( "$" ) & & ! key . startsWith ( "@" ) ) {
if ( ! key . startsWith ( "$" ) & & ! key . startsWith ( "@" ) ) {
key = "@." + key ;
}
return new Criteria ( this . criteriaChain , PathCompiler . compile ( key ) ) ;
@ -542,6 +572,19 @@ public class Criteria implements Predicate {
return this ;
}
/ * *
* The < code > contains < / code > operator asserts that the provided object is contained
* in the result . The object that should contain the input can be either an object or a String .
*
* @param o that should exists in given collection or
* @return the criteria
* /
public Criteria contains ( Object o ) {
this . criteriaType = CriteriaType . CONTAINS ;
this . right = o ;
return this ;
}
/ * *
* The < code > nin < / code > operator is similar to $in except that it selects objects for
* which the specified field does not have any value in the specified array .
@ -658,15 +701,16 @@ public class Criteria implements Predicate {
return this ;
}
private static boolean isPath ( String string ) {
return ( string ! = null
& & ( string . startsWith ( "$" ) | | string . startsWith ( "@" ) | | string . startsWith ( "!@" ) ) ) ;
private static boolean isPath ( String string ) {
return ( string ! = null
& & ( string . startsWith ( "$" ) | | string . startsWith ( "@" ) | | string . startsWith ( "!@" ) ) ) ;
}
private static boolean isString ( String string ) {
private static boolean isString ( String string ) {
return ( string ! = null & & ! string . isEmpty ( ) & & string . charAt ( 0 ) = = '\'' & & string . charAt ( string . length ( ) - 1 ) = = '\'' ) ;
}
private static boolean isPattern ( String string ) {
private static boolean isPattern ( String string ) {
return ( string ! = null
& & ! string . isEmpty ( )
& & string . charAt ( 0 ) = = '/'
@ -684,13 +728,13 @@ public class Criteria implements Predicate {
}
/ * *
* Parse the provided criteria
*
* @param criteria
* @return a criteria
* /
public static Criteria parse ( String criteria ) {
public static Criteria parse ( String criteria ) {
int operatorIndex = - 1 ;
String left = "" ;
String operator = "" ;
@ -713,9 +757,10 @@ public class Criteria implements Predicate {
/ * *
* Creates a new criteria
* @param left path to evaluate in criteria
*
* @param left path to evaluate in criteria
* @param operator operator
* @param right expected value
* @param right expected value
* @return a new Criteria
* /
public static Criteria create ( String left , String operator , String right ) {
@ -725,38 +770,38 @@ public class Criteria implements Predicate {
Path rightPath = null ;
boolean existsCheck = true ;
if ( isPath ( left ) ) {
if ( left . charAt ( 0 ) = = '!' ) {
if ( isPath ( left ) ) {
if ( left . charAt ( 0 ) = = '!' ) {
existsCheck = false ;
left = left . substring ( 1 ) ;
}
leftPath = PathCompiler . compile ( left ) ;
if ( ! leftPath . isDefinite ( ) ) {
if ( ! leftPath . isDefinite ( ) ) {
throw new InvalidPathException ( "the predicate path: " + left + " is not definite" ) ;
}
leftPrepared = leftPath ;
} else if ( isString ( left ) ) {
} else if ( isString ( left ) ) {
leftPrepared = left . substring ( 1 , left . length ( ) - 1 ) ;
} else if ( isPattern ( left ) ) {
} else if ( isPattern ( left ) ) {
leftPrepared = compilePattern ( left ) ;
}
if ( isPath ( right ) ) {
if ( right . charAt ( 0 ) = = '!' ) {
if ( isPath ( right ) ) {
if ( right . charAt ( 0 ) = = '!' ) {
throw new InvalidPathException ( "Invalid negation! Can only be used for existence check e.g [?(!@.foo)]" ) ;
}
rightPath = PathCompiler . compile ( right ) ;
if ( ! rightPath . isDefinite ( ) ) {
if ( ! rightPath . isDefinite ( ) ) {
throw new InvalidPathException ( "the predicate path: " + right + " is not definite" ) ;
}
rightPrepared = rightPath ;
} else if ( isString ( right ) ) {
} else if ( isString ( right ) ) {
rightPrepared = right . substring ( 1 , right . length ( ) - 1 ) ;
} else if ( isPattern ( right ) ) {
} else if ( isPattern ( right ) ) {
rightPrepared = compilePattern ( right ) ;
}
if ( leftPath ! = null & & operator . isEmpty ( ) ) {
if ( leftPath ! = null & & operator . isEmpty ( ) ) {
return Criteria . where ( leftPath ) . exists ( existsCheck ) ;
} else {
return new Criteria ( leftPrepared , CriteriaType . parse ( operator ) , rightPrepared ) ;
@ -765,7 +810,7 @@ public class Criteria implements Predicate {
private static int safeCompare ( Object left , Object right ) throws ValueCompareException {
if ( left = = right ) {
if ( left = = right ) {
return 0 ;
}
@ -779,10 +824,10 @@ public class Criteria implements Predicate {
} else if ( leftNullish & & rightNullish ) {
return 0 ;
} else if ( left instanceof String & & right instanceof String ) {
String exp = ( String ) left ;
if ( exp . contains ( "\'" ) ) {
exp = exp . replace ( "\\'" , "'" ) ;
}
String exp = ( String ) left ;
if ( exp . contains ( "\'" ) ) {
exp = exp . replace ( "\\'" , "'" ) ;
}
return exp . compareTo ( ( String ) right ) ;
} else if ( left instanceof Number & & right instanceof Number ) {
return new BigDecimal ( left . toString ( ) ) . compareTo ( new BigDecimal ( right . toString ( ) ) ) ;
@ -807,24 +852,20 @@ public class Criteria implements Predicate {
}
@Override
public String toString ( ) {
StringBuilder sb = new StringBuilder ( ) ;
sb . append ( left . toString ( ) )
//.append("|")
. append ( criteriaType . toString ( ) )
//.append("|")
. append ( wrapString ( right ) ) ;
//.append("|");
return sb . toString ( ) ;
}
private static String wrapString ( Object o ) {
if ( o = = null ) {
private static String wrapString ( Object o ) {
if ( o = = null ) {
return "null" ;
}
if ( o instanceof String ) {
if ( o instanceof String ) {
return "'" + o . toString ( ) + "'" ;
} else {
return o . toString ( ) ;
@ -832,6 +873,4 @@ public class Criteria implements Predicate {
}
}