1818
1919import  static  com .google .common .base .Preconditions .checkArgument ;
2020import  static  com .google .common .base .Preconditions .checkNotNull ;
21+ import  static  com .google .common .collect .ImmutableMap .toImmutableMap ;
2122
2223import  com .google .common .collect .ImmutableList ;
24+ import  com .google .common .collect .ImmutableMap ;
2325import  com .google .firebase .internal .NonNull ;
2426import  com .google .firebase .internal .Nullable ;
25- 
2627import  java .math .BigInteger ;
2728import  java .nio .charset .StandardCharsets ;
2829import  java .security .MessageDigest ;
3536import  java .util .regex .Pattern ;
3637import  java .util .regex .PatternSyntaxException ;
3738import  java .util .stream .Collectors ;
38- 
3939import  org .slf4j .Logger ;
4040import  org .slf4j .LoggerFactory ;
41-   
41+ 
4242final  class  ConditionEvaluator  {
4343  private  static  final  int  MAX_CONDITION_RECURSION_DEPTH  = 10 ;
4444  private  static  final  Logger  logger  = LoggerFactory .getLogger (ConditionEvaluator .class );
4545  private  static  final  BigInteger  MICRO_PERCENT_MODULO  = BigInteger .valueOf (100_000_000L );
4646
4747  /** 
4848   * Evaluates server conditions and assigns a boolean value to each condition. 
49-    *   
49+    * 
5050   * @param conditions List of conditions which are to be evaluated. 
51-    * @param context A map with additional metadata used during evaluation.   
51+    * @param context A map with additional metadata used during evaluation. 
5252   * @return A map of condition to evaluated value. 
5353   */ 
5454  @ NonNull 
5555  Map <String , Boolean > evaluateConditions (
56-       @ NonNull  List <ServerCondition > conditions ,
57-       @ Nullable  KeysAndValues  context ) {
56+       @ NonNull  List <ServerCondition > conditions , @ Nullable  KeysAndValues  context ) {
5857    checkNotNull (conditions , "List of conditions must not be null." );
5958    checkArgument (!conditions .isEmpty (), "List of conditions must not be empty." );
60-     KeysAndValues  evaluationContext  = context  != null  
61-         ? context  
62-         : new  KeysAndValues .Builder ().build ();
63- 
64-     Map <String , Boolean > evaluatedConditions  = conditions .stream ()
65-         .collect (Collectors .toMap (
66-             ServerCondition ::getName ,
67-             condition  -> 
68-                 evaluateCondition (condition .getCondition (), evaluationContext , /* nestingLevel= */ 0 )
69-         ));
59+     if  (context  == null  || conditions .isEmpty ()) {
60+       return  ImmutableMap .of ();
61+     }
62+     KeysAndValues  evaluationContext  =
63+         context  != null  ? context  : new  KeysAndValues .Builder ().build ();
64+ 
65+     Map <String , Boolean > evaluatedConditions  =
66+         conditions .stream ()
67+             .collect (
68+                 toImmutableMap (
69+                     ServerCondition ::getName ,
70+                     condition  ->
71+                         evaluateCondition (
72+                             condition .getCondition (), evaluationContext , /* nestingLevel= */  0 )));
7073
7174    return  evaluatedConditions ;
7275  }
7376
74-   private  boolean  evaluateCondition (OneOfCondition   condition ,  KeysAndValues   context , 
75-       int  nestingLevel ) {
77+   private  boolean  evaluateCondition (
78+       OneOfCondition   condition ,  KeysAndValues   context ,  int  nestingLevel ) {
7679    if  (nestingLevel  > MAX_CONDITION_RECURSION_DEPTH ) {
7780      logger .warn ("Maximum condition recursion depth exceeded." );
7881      return  false ;
@@ -95,30 +98,30 @@ private boolean evaluateCondition(OneOfCondition condition, KeysAndValues contex
9598    return  false ;
9699  }
97100
98- 
99-   private  boolean  evaluateOrCondition (OrCondition  condition , KeysAndValues  context ,
100-       int  nestingLevel ) {
101+   private  boolean  evaluateOrCondition (
102+       OrCondition  condition , KeysAndValues  context , int  nestingLevel ) {
101103    return  condition .getConditions ().stream ()
102104        .anyMatch (subCondition  -> evaluateCondition (subCondition , context , nestingLevel  + 1 ));
103105  }
104106
105-   private  boolean  evaluateAndCondition (AndCondition   condition ,  KeysAndValues   context , 
106-       int  nestingLevel ) {
107+   private  boolean  evaluateAndCondition (
108+       AndCondition   condition ,  KeysAndValues   context ,  int  nestingLevel ) {
107109    return  condition .getConditions ().stream ()
108110        .allMatch (subCondition  -> evaluateCondition (subCondition , context , nestingLevel  + 1 ));
109111  }
110112
111-   private  boolean  evaluateCustomSignalCondition (CustomSignalCondition   condition , 
112-       KeysAndValues  context ) {
113+   private  boolean  evaluateCustomSignalCondition (
114+       CustomSignalCondition   condition ,  KeysAndValues  context ) {
113115    CustomSignalOperator  customSignalOperator  = condition .getCustomSignalOperator ();
114116    String  customSignalKey  = condition .getCustomSignalKey ();
115-     ImmutableList <String > targetCustomSignalValues  =  ImmutableList . copyOf ( 
116-         condition .getTargetCustomSignalValues ());
117+     ImmutableList <String > targetCustomSignalValues  =
118+         ImmutableList . copyOf ( condition .getTargetCustomSignalValues ());
117119
118120    if  (targetCustomSignalValues .isEmpty ()) {
119-       logger .warn (String .format (
120-           "Values must be assigned to all custom signal fields. Operator:%s, Key:%s, Values:%s" ,
121-           customSignalOperator , customSignalKey , targetCustomSignalValues ));
121+       logger .warn (
122+           String .format (
123+               "Values must be assigned to all custom signal fields. Operator:%s, Key:%s, Values:%s" ,
124+               customSignalOperator , customSignalKey , targetCustomSignalValues ));
122125      return  false ;
123126    }
124127
@@ -130,64 +133,65 @@ private boolean evaluateCustomSignalCondition(CustomSignalCondition condition,
130133    switch  (customSignalOperator ) {
131134      // String operations. 
132135      case  STRING_CONTAINS :
133-         return  compareStrings (targetCustomSignalValues , customSignalValue ,
136+         return  compareStrings (
137+             targetCustomSignalValues ,
138+             customSignalValue ,
134139            (customSignal , targetSignal ) -> customSignal .contains (targetSignal ));
135140      case  STRING_DOES_NOT_CONTAIN :
136-         return  !compareStrings (targetCustomSignalValues , customSignalValue ,
141+         return  !compareStrings (
142+             targetCustomSignalValues ,
143+             customSignalValue ,
137144            (customSignal , targetSignal ) -> customSignal .contains (targetSignal ));
138145      case  STRING_EXACTLY_MATCHES :
139-         return  compareStrings (targetCustomSignalValues , customSignalValue ,
146+         return  compareStrings (
147+             targetCustomSignalValues ,
148+             customSignalValue ,
140149            (customSignal , targetSignal ) -> customSignal .equals (targetSignal ));
141150      case  STRING_CONTAINS_REGEX :
142-         return  compareStrings (targetCustomSignalValues , customSignalValue ,
151+         return  compareStrings (
152+             targetCustomSignalValues ,
153+             customSignalValue ,
143154            (customSignal , targetSignal ) -> compareStringRegex (customSignal , targetSignal ));
144155
145156      // Numeric operations. 
146157      case  NUMERIC_LESS_THAN :
147-         return  compareNumbers (targetCustomSignalValues , customSignalValue ,
148-             (result ) -> result  < 0 );
158+         return  compareNumbers (targetCustomSignalValues , customSignalValue , (result ) -> result  < 0 );
149159      case  NUMERIC_LESS_EQUAL :
150-         return  compareNumbers (targetCustomSignalValues , customSignalValue ,
151-             (result ) -> result  <= 0 );
160+         return  compareNumbers (targetCustomSignalValues , customSignalValue , (result ) -> result  <= 0 );
152161      case  NUMERIC_EQUAL :
153-         return  compareNumbers (targetCustomSignalValues , customSignalValue ,
154-             (result ) -> result  == 0 );
162+         return  compareNumbers (targetCustomSignalValues , customSignalValue , (result ) -> result  == 0 );
155163      case  NUMERIC_NOT_EQUAL :
156-         return  compareNumbers (targetCustomSignalValues , customSignalValue ,
157-             (result ) -> result  != 0 );
164+         return  compareNumbers (targetCustomSignalValues , customSignalValue , (result ) -> result  != 0 );
158165      case  NUMERIC_GREATER_THAN :
159-         return  compareNumbers (targetCustomSignalValues , customSignalValue ,
160-             (result ) -> result  > 0 );
166+         return  compareNumbers (targetCustomSignalValues , customSignalValue , (result ) -> result  > 0 );
161167      case  NUMERIC_GREATER_EQUAL :
162-         return  compareNumbers (targetCustomSignalValues , customSignalValue ,
163-             (result ) -> result  >= 0 );
168+         return  compareNumbers (targetCustomSignalValues , customSignalValue , (result ) -> result  >= 0 );
164169
165170      // Semantic operations. 
166171      case  SEMANTIC_VERSION_EQUAL :
167-         return  compareSemanticVersions (targetCustomSignalValues ,  customSignalValue , 
168-             (result ) -> result  == 0 );
172+         return  compareSemanticVersions (
173+             targetCustomSignalValues ,  customSignalValue ,  (result ) -> result  == 0 );
169174      case  SEMANTIC_VERSION_GREATER_EQUAL :
170-         return  compareSemanticVersions (targetCustomSignalValues ,  customSignalValue , 
171-             (result ) -> result  >= 0 );
175+         return  compareSemanticVersions (
176+             targetCustomSignalValues ,  customSignalValue ,  (result ) -> result  >= 0 );
172177      case  SEMANTIC_VERSION_GREATER_THAN :
173-         return  compareSemanticVersions (targetCustomSignalValues ,  customSignalValue , 
174-             (result ) -> result  > 0 );
178+         return  compareSemanticVersions (
179+             targetCustomSignalValues ,  customSignalValue ,  (result ) -> result  > 0 );
175180      case  SEMANTIC_VERSION_LESS_EQUAL :
176-         return  compareSemanticVersions (targetCustomSignalValues ,  customSignalValue , 
177-             (result ) -> result  <= 0 );
181+         return  compareSemanticVersions (
182+             targetCustomSignalValues ,  customSignalValue ,  (result ) -> result  <= 0 );
178183      case  SEMANTIC_VERSION_LESS_THAN :
179-         return  compareSemanticVersions (targetCustomSignalValues ,  customSignalValue , 
180-             (result ) -> result  < 0 );
184+         return  compareSemanticVersions (
185+             targetCustomSignalValues ,  customSignalValue ,  (result ) -> result  < 0 );
181186      case  SEMANTIC_VERSION_NOT_EQUAL :
182-         return  compareSemanticVersions (targetCustomSignalValues ,  customSignalValue , 
183-             (result ) -> result  != 0 );
187+         return  compareSemanticVersions (
188+             targetCustomSignalValues ,  customSignalValue ,  (result ) -> result  != 0 );
184189      default :
185190        return  false ;
186191    }
187192  }
188193
189-   private  boolean  evaluatePercentCondition (PercentCondition  condition ,
190-       KeysAndValues  context ) {
194+   private  boolean  evaluatePercentCondition (PercentCondition  condition , KeysAndValues  context ) {
191195    if  (!context .containsKey ("randomizationId" )) {
192196      logger .warn ("Percentage operation must not be performed without randomizationId" );
193197      return  false ;
@@ -197,18 +201,16 @@ private boolean evaluatePercentCondition(PercentCondition condition,
197201
198202    // The micro-percent interval to be used with the BETWEEN operator. 
199203    MicroPercentRange  microPercentRange  = condition .getMicroPercentRange ();
200-     int  microPercentUpperBound  = microPercentRange  != null 
201-         ? microPercentRange .getMicroPercentUpperBound ()
202-         : 0 ;
203-     int  microPercentLowerBound  = microPercentRange  != null 
204-         ? microPercentRange .getMicroPercentLowerBound ()
205-         : 0 ;
204+     int  microPercentUpperBound  =
205+         microPercentRange  != null  ? microPercentRange .getMicroPercentUpperBound () : 0 ;
206+     int  microPercentLowerBound  =
207+         microPercentRange  != null  ? microPercentRange .getMicroPercentLowerBound () : 0 ;
206208    // The limit of percentiles to target in micro-percents when using the 
207209    // LESS_OR_EQUAL and GREATER_THAN operators. The value must be in the range [0 
208210    // and 100000000]. 
209211    int  microPercent  = condition .getMicroPercent ();
210-     BigInteger  microPercentile  =  getMicroPercentile ( condition . getSeed (), 
211-         context .get ("randomizationId" ));
212+     BigInteger  microPercentile  =
213+         getMicroPercentile ( condition . getSeed (),  context .get ("randomizationId" ));
212214    switch  (operator ) {
213215      case  LESS_OR_EQUAL :
214216        return  microPercentile .compareTo (BigInteger .valueOf (microPercent )) <= 0 ;
@@ -246,10 +248,12 @@ private BigInteger hashSeededRandomizationId(String seededRandomizationId) {
246248    }
247249  }
248250
249-   private  boolean  compareStrings (ImmutableList <String > targetValues , String  customSignal ,
250-                               BiPredicate <String , String > compareFunction ) {
251-     return  targetValues .stream ().anyMatch (targetValue  -> 
252-               compareFunction .test (customSignal , targetValue ));
251+   private  boolean  compareStrings (
252+       ImmutableList <String > targetValues ,
253+       String  customSignal ,
254+       BiPredicate <String , String > compareFunction ) {
255+     return  targetValues .stream ()
256+         .anyMatch (targetValue  -> compareFunction .test (customSignal , targetValue ));
253257  }
254258
255259  private  boolean  compareStringRegex (String  customSignal , String  targetSignal ) {
@@ -260,12 +264,13 @@ private boolean compareStringRegex(String customSignal, String targetSignal) {
260264    }
261265  }
262266
263-   private  boolean  compareNumbers (ImmutableList < String >  targetValues ,  String   customSignal , 
264-                              IntPredicate  compareFunction ) {
267+   private  boolean  compareNumbers (
268+       ImmutableList < String >  targetValues ,  String   customSignal ,  IntPredicate  compareFunction ) {
265269    if  (targetValues .size () != 1 ) {
266-       logger .warn (String .format (
267-           "Target values must contain 1 element for numeric operations. Target Value: %s" ,
268-           targetValues ));
270+       logger .warn (
271+           String .format (
272+               "Target values must contain 1 element for numeric operations. Target Value: %s" ,
273+               targetValues ));
269274      return  false ;
270275    }
271276
@@ -275,23 +280,22 @@ private boolean compareNumbers(ImmutableList<String> targetValues, String custom
275280      int  comparisonResult  = Double .compare (customSignalDouble , targetValue );
276281      return  compareFunction .test (comparisonResult );
277282    } catch  (NumberFormatException  e ) {
278-       logger .warn ("Error parsing numeric values: customSignal=%s, targetValue=%s" ,
283+       logger .warn (
284+           "Error parsing numeric values: customSignal=%s, targetValue=%s" ,
279285          customSignal , targetValues .get (0 ), e );
280286      return  false ;
281287    }
282288  }
283289
284-   private  boolean  compareSemanticVersions (ImmutableList <String > targetValues ,
285-                                       String  customSignal ,
286-                                       IntPredicate  compareFunction ) {
290+   private  boolean  compareSemanticVersions (
291+       ImmutableList <String > targetValues , String  customSignal , IntPredicate  compareFunction ) {
287292    if  (targetValues .size () != 1 ) {
288293      logger .warn (String .format ("Target values must contain 1 element for semantic operation." ));
289294      return  false ;
290295    }
291296
292297    String  targetValueString  = targetValues .get (0 );
293-     if  (!validateSemanticVersion (targetValueString )
294-         || !validateSemanticVersion (customSignal )) {
298+     if  (!validateSemanticVersion (targetValueString ) || !validateSemanticVersion (customSignal )) {
295299      return  false ;
296300    }
297301
@@ -300,7 +304,8 @@ private boolean compareSemanticVersions(ImmutableList<String> targetValues,
300304
301305    int  maxLength  = 5 ;
302306    if  (targetVersion .size () > maxLength  || customSignalVersion .size () > maxLength ) {
303-       logger .warn ("Semantic version max length(%s) exceeded. Target: %s, Custom Signal: %s" ,
307+       logger .warn (
308+           "Semantic version max length(%s) exceeded. Target: %s, Custom Signal: %s" ,
304309          maxLength , targetValueString , customSignal );
305310      return  false ;
306311    }
@@ -316,8 +321,8 @@ private int compareSemanticVersions(List<Integer> version1, List<Integer> versio
316321
317322    for  (int  i  = 0 ; i  < maxLength ; i ++) {
318323      // Default to 0 if segment is missing 
319-       int  v1  =   i  < version1Size  ? version1 .get (i ) : 0 ;
320-       int  v2  =   i  < version2Size  ? version2 .get (i ) : 0 ;
324+       int  v1  = i  < version1Size  ? version1 .get (i ) : 0 ;
325+       int  v2  = i  < version2Size  ? version2 .get (i ) : 0 ;
321326
322327      int  comparison  = Integer .compare (v1 , v2 );
323328      if  (comparison  != 0 ) {
@@ -330,8 +335,8 @@ private int compareSemanticVersions(List<Integer> version1, List<Integer> versio
330335
331336  private  List <Integer > parseSemanticVersion (String  versionString ) {
332337    return  Arrays .stream (versionString .split ("\\ ." ))
333-            .map (Integer ::parseInt )
334-            .collect (Collectors .toList ());
338+         .map (Integer ::parseInt )
339+         .collect (Collectors .toList ());
335340  }
336341
337342  private  boolean  validateSemanticVersion (String  version ) {
0 commit comments