Skip to content

Commit

Permalink
Add XOR ArithmeticGroupFunction (#4386)
Browse files Browse the repository at this point in the history
* #4385 add XOR ArithmeticGroupFunction (1 of n)

Signed-off-by: Fabian Vollmann <[email protected]>
  • Loading branch information
fabianvo committed Sep 26, 2024
1 parent 437a885 commit a5c488d
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ ModelGroupItem:
;

enum ModelGroupFunction:
EQUALITY='EQUALITY' | AND='AND' | OR='OR' | NAND='NAND' | NOR='NOR' | AVG='AVG' | MEDIAN='MEDIAN' | SUM='SUM' | MAX='MAX' | MIN='MIN' | COUNT='COUNT' | LATEST='LATEST' | EARLIEST='EARLIEST'
EQUALITY='EQUALITY' | AND='AND' | OR='OR' | NAND='NAND' | NOR='NOR' | XOR='XOR' | AVG='AVG' | MEDIAN='MEDIAN' | SUM='SUM' | MAX='MAX' | MIN='MIN' | COUNT='COUNT' | LATEST='LATEST' | EARLIEST='EARLIEST'
;

ModelNormalItem:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ private GroupFunction createDefaultGroupFunction(GroupFunctionDTO function, @Nul
logger.error("Group function 'NOT OR' requires two arguments. Using Equality instead.");
}
break;
case "XOR":
args = parseStates(baseItem, function.params);
if (args.size() == 2) {
return new ArithmeticGroupFunction.Xor(args.get(0), args.get(1));
} else {
logger.error("Group function 'XOR' requires two arguments. Using Equality instead.");
}
break;
case "COUNT":
if (function.params != null && function.params.length == 1) {
State countParam = new StringType(function.params[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* @author Kai Kreuzer - Initial contribution
* @author Thomas Eichstädt-Engelen - Added "N" functions
* @author Gaël L'hopital - Added count function
* @author Fabian Vollmann - Added XOR function
*/
@NonNullByDefault
public interface ArithmeticGroupFunction extends GroupFunction {
Expand Down Expand Up @@ -212,6 +213,82 @@ public State calculate(@Nullable Set<Item> items) {
}
}

/**
* This does a logical 'xor' operation. If exactly one item is of 'activeState' this
* is returned, otherwise the 'passiveState' is returned.
*
* Through the getStateAs() method, it can be determined, how many
* items actually are in the 'activeState'.
*/
class Xor implements GroupFunction {

protected final State activeState;
protected final State passiveState;

public Xor(@Nullable State activeValue, @Nullable State passiveValue) {
if (activeValue == null || passiveValue == null) {
throw new IllegalArgumentException("Parameters must not be null!");
}
this.activeState = activeValue;
this.passiveState = passiveValue;
}

@Override
public State calculate(@Nullable Set<Item> items) {
if (items != null) {
boolean foundOne = false;

for (Item item : items) {
if (activeState.equals(item.getStateAs(activeState.getClass()))) {
if (foundOne) {
return passiveState;
} else {
foundOne = true;
}
}
}
if (foundOne) {
return activeState;
}
}

return passiveState;
}

@Override
public @Nullable <T extends State> T getStateAs(@Nullable Set<Item> items, Class<T> stateClass) {
State state = calculate(items);
if (stateClass.isInstance(state)) {
return stateClass.cast(state);
} else {
if (stateClass == DecimalType.class) {
if (items != null) {
return stateClass.cast(new DecimalType(count(items, activeState)));
} else {
return stateClass.cast(DecimalType.ZERO);
}
} else {
return null;
}
}
}

private int count(Set<Item> items, State state) {
int count = 0;
for (Item item : items) {
if (state.equals(item.getStateAs(state.getClass()))) {
count++;
}
}
return count;
}

@Override
public State[] getParameters() {
return new State[] { activeState, passiveState };
}
}

/**
* This calculates the numeric average over all item states of decimal type.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,58 @@ public void testNAndFunctionNegative() {
assertEquals(OpenClosedType.OPEN, state);
}

@Test
public void testXorFunction() {
Set<Item> items = new HashSet<>();
items.add(new TestItem("TestItem1", OpenClosedType.OPEN));
items.add(new TestItem("TestItem2", OpenClosedType.CLOSED));

GroupFunction function = new ArithmeticGroupFunction.Xor(OpenClosedType.OPEN, OpenClosedType.CLOSED);
State state = function.calculate(items);

assertEquals(OpenClosedType.OPEN, state);
}

@Test
public void testXorFunctionMultiple() {
Set<Item> items = new HashSet<>();
items.add(new TestItem("TestItem1", OpenClosedType.CLOSED));
items.add(new TestItem("TestItem2", OpenClosedType.CLOSED));
items.add(new TestItem("TestItem3", OpenClosedType.OPEN));
items.add(new TestItem("TestItem4", OpenClosedType.CLOSED));

GroupFunction function = new ArithmeticGroupFunction.Xor(OpenClosedType.OPEN, OpenClosedType.CLOSED);
State state = function.calculate(items);

assertEquals(OpenClosedType.OPEN, state);
}

@Test
public void testXorFunctionNegative() {
Set<Item> items = new HashSet<>();
items.add(new TestItem("TestItem1", OpenClosedType.OPEN));
items.add(new TestItem("TestItem2", OpenClosedType.OPEN));

GroupFunction function = new ArithmeticGroupFunction.Xor(OpenClosedType.OPEN, OpenClosedType.CLOSED);
State state = function.calculate(items);

assertEquals(OpenClosedType.CLOSED, state);
}

@Test
public void testXorFunctionNegativeMultiple() {
Set<Item> items = new HashSet<>();
items.add(new TestItem("TestItem1", OpenClosedType.CLOSED));
items.add(new TestItem("TestItem2", OpenClosedType.OPEN));
items.add(new TestItem("TestItem3", OpenClosedType.OPEN));
items.add(new TestItem("TestItem4", OpenClosedType.CLOSED));

GroupFunction function = new ArithmeticGroupFunction.Xor(OpenClosedType.OPEN, OpenClosedType.CLOSED);
State state = function.calculate(items);

assertEquals(OpenClosedType.CLOSED, state);
}

@Test
public void testAvgFunction() {
Set<Item> items = new HashSet<>();
Expand Down

0 comments on commit a5c488d

Please sign in to comment.