Skip to content

Commit a78e386

Browse files
Implement modulo operator (#91)
* Add Modulo operator * Add javadoc * Fix build * Uncomment tests * Add test for float in condition value
1 parent 8bd1742 commit a78e386

3 files changed

Lines changed: 38 additions & 4 deletions

File tree

src/main/java/com/flagsmith/flagengine/segments/constants/SegmentConditions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
public enum SegmentConditions {
44
EQUAL, GREATER_THAN, LESS_THAN, LESS_THAN_INCLUSIVE, CONTAINS,
55
GREATER_THAN_INCLUSIVE, NOT_CONTAINS, NOT_EQUAL, REGEX, PERCENTAGE_SPLIT,
6-
IS_SET, IS_NOT_SET;
6+
MODULO, IS_SET, IS_NOT_SET;
77
}

src/main/java/com/flagsmith/flagengine/utils/types/TypeCasting.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ public class TypeCasting {
1616
*/
1717
public static Boolean compare(SegmentConditions condition, Object value1, Object value2) {
1818

19+
if (condition.equals(SegmentConditions.MODULO)) {
20+
return compareModulo(String.valueOf(value2), value1);
21+
}
22+
1923
if (isInteger(value1) && isInteger(value2)) {
2024
return compare(condition, toInteger(value1), toInteger(value2));
2125
} else if (isFloat(value1) && isFloat(value2)) {
@@ -65,7 +69,8 @@ public static Boolean compare(SegmentConditions condition, Comparable value1, Co
6569
*/
6670
public static Double toDouble(Object number) {
6771
try {
68-
return number instanceof Double ? ((Double) number) : Double.parseDouble((String) number);
72+
String asString = String.valueOf(number);
73+
return number instanceof Double ? ((Double) number) : Double.parseDouble(asString);
6974
} catch (Exception nfe) {
7075
return null;
7176
}
@@ -87,7 +92,7 @@ public static Boolean isDouble(Object number) {
8792
*/
8893
public static Float toFloat(Object number) {
8994
try {
90-
return number instanceof Float ? ((Float) number) : Float.parseFloat((String) number);
95+
return number instanceof Float ? ((Float) number) : Float.parseFloat(String.valueOf(number));
9196
} catch (Exception nfe) {
9297
return null;
9398
}
@@ -109,7 +114,8 @@ public static Boolean isFloat(Object number) {
109114
*/
110115
public static Integer toInteger(Object number) {
111116
try {
112-
return number instanceof Integer ? ((Integer) number) : Integer.valueOf((String) number);
117+
String asString = String.valueOf(number);
118+
return number instanceof Integer ? ((Integer) number) : Integer.valueOf(asString);
113119
} catch (Exception nfe) {
114120
return null;
115121
}
@@ -172,4 +178,26 @@ public static ComparableVersion toSemver(Object str) {
172178
public static Boolean isSemver(Object str) {
173179
return SemanticVersioning.isSemver((String) str);
174180
}
181+
182+
/**
183+
* Modulo is a special case as the condition value holds both the divisor and remainder.
184+
* This method compares the conditionValue and the traitValue by dividing the traitValue
185+
* by the divisor and verifying that it correctly equals the remainder.
186+
*
187+
* @param conditionValue conditionValue in the format 'divisor|remainder'
188+
* @param traitValue the value of the matched trait
189+
* @return true if expression evaluates to true, false if unable to evaluate expression or
190+
* it evaluates to false
191+
*/
192+
public static Boolean compareModulo(String conditionValue, Object traitValue) {
193+
try {
194+
String[] divisorAndRemainder = conditionValue.split("\\|");
195+
Float divisor = toFloat(divisorAndRemainder[0]);
196+
Float remainder = toFloat(divisorAndRemainder[1]);
197+
198+
return toFloat(traitValue) % divisor == remainder;
199+
} catch (NumberFormatException | NullPointerException e) {
200+
return false; // indicates that one of the values could not be case to Float.
201+
}
202+
}
175203
}

src/test/java/com/flagsmith/flagengine/unit/segments/SegmentModelTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ public Object[][] conditionTestData() {
6262
new Object[] {SegmentConditions.NOT_CONTAINS, "bar", "baz", true},
6363
new Object[] {SegmentConditions.REGEX, "foo", "[a-z]+", true},
6464
new Object[] {SegmentConditions.REGEX, "FOO", "[a-z]+", false},
65+
new Object[] {SegmentConditions.MODULO, 2, "2|0", true},
66+
new Object[] {SegmentConditions.MODULO, 3, "2|0", false},
67+
new Object[] {SegmentConditions.MODULO, 2.0, "2|0", true},
68+
new Object[] {SegmentConditions.MODULO, 2.0, "2.0|0.0", true},
69+
new Object[] {SegmentConditions.MODULO, "foo", "2|0", false},
70+
new Object[] {SegmentConditions.MODULO, "foo", "foo|bar", false},
6571
};
6672
}
6773

0 commit comments

Comments
 (0)