Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XOR ArithmeticGroupFunction #4386

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 ONE 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. Only if items are of opposing 'activeState' and
* 'passiveState', 'activeState' is returned. For more than 2 states, this is '1 of n'.
*
* 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