-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #114 from tkobayas/DROOLS-7635-error-incompatible-…
…type-02 [DROOLS-7635] ansible-rulebook : Raise an error when a condition compares incompatible types
- Loading branch information
Showing
8 changed files
with
623 additions
and
34 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
...ok-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/LogUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.drools.ansible.rulebook.integration.api; | ||
|
||
import java.util.Map; | ||
|
||
public class LogUtil { | ||
|
||
private LogUtil() { | ||
// utility class | ||
} | ||
|
||
// convert java class to python class | ||
private static Map<Class<?>, String> convertMap = Map.of( | ||
java.lang.Integer.class, "int", | ||
java.lang.Boolean.class, "bool", | ||
java.lang.String.class, "str", | ||
java.lang.Double.class, "float", | ||
java.util.List.class, "list", | ||
java.util.ArrayList.class, "list", | ||
java.util.Map.class, "dict", | ||
java.util.LinkedHashMap.class, "dict", | ||
java.util.HashMap.class, "dict" | ||
); | ||
|
||
public static String convertJavaClassToPythonClass(Class<?> javaClass) { | ||
return convertMap.getOrDefault(javaClass, javaClass.getSimpleName()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
188 changes: 188 additions & 0 deletions
188
...rools/ansible/rulebook/integration/api/domain/constraints/RulebookConstraintOperator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package org.drools.ansible.rulebook.integration.api.domain.constraints; | ||
|
||
import java.util.Map; | ||
import java.util.function.BiPredicate; | ||
|
||
import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext; | ||
import org.drools.model.Index; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import static org.drools.ansible.rulebook.integration.api.LogUtil.convertJavaClassToPythonClass; | ||
import static org.drools.model.util.OperatorUtils.areEqual; | ||
import static org.drools.model.util.OperatorUtils.compare; | ||
|
||
public class RulebookConstraintOperator implements RulebookOperator { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(RulebookConstraintOperator.class); | ||
|
||
private Index.ConstraintType type; | ||
private ConditionContext conditionContext; | ||
private boolean typeCheckLogged = false; | ||
|
||
public RulebookConstraintOperator(Index.ConstraintType type) { | ||
this.type = type; | ||
} | ||
|
||
public void setConditionContext(RuleGenerationContext ruleContext, Map<?, ?> expression) { | ||
this.conditionContext = new ConditionContext(ruleContext.getRuleSetName(), ruleContext.getRuleName(), expression.toString()); | ||
} | ||
|
||
@Override | ||
public boolean hasIndex() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public Index.ConstraintType getIndexType() { | ||
return type; | ||
} | ||
|
||
public RulebookConstraintOperator negate() { | ||
switch (this.type) { | ||
case FORALL_SELF_JOIN: | ||
case EQUAL: | ||
this.type = Index.ConstraintType.NOT_EQUAL; | ||
return this; | ||
case NOT_EQUAL: | ||
this.type = Index.ConstraintType.EQUAL; | ||
return this; | ||
case GREATER_THAN: | ||
this.type = Index.ConstraintType.LESS_OR_EQUAL; | ||
return this; | ||
case GREATER_OR_EQUAL: | ||
this.type = Index.ConstraintType.LESS_THAN; | ||
return this; | ||
case LESS_OR_EQUAL: | ||
this.type = Index.ConstraintType.GREATER_THAN; | ||
return this; | ||
case LESS_THAN: | ||
this.type = Index.ConstraintType.GREATER_OR_EQUAL; | ||
return this; | ||
} | ||
this.type = Index.ConstraintType.UNKNOWN; | ||
return this; | ||
} | ||
|
||
public boolean canInverse() { | ||
switch (this.type) { | ||
case EQUAL: | ||
case NOT_EQUAL: | ||
case GREATER_THAN: | ||
case GREATER_OR_EQUAL: | ||
case LESS_THAN: | ||
case LESS_OR_EQUAL: | ||
return true; | ||
default: | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
public <T, V> BiPredicate<T, V> asPredicate() { | ||
final BiPredicate<T, V> predicate; | ||
switch (this.type) { | ||
case EQUAL: | ||
predicate = (t, v) -> areEqual(t, v); | ||
break; | ||
case NOT_EQUAL: | ||
predicate = (t, v) -> !areEqual(t, v); | ||
break; | ||
case GREATER_THAN: | ||
predicate = (t,v) -> t != null && compare(t, v) > 0; | ||
break; | ||
case GREATER_OR_EQUAL: | ||
predicate = (t,v) -> t != null && compare(t, v) >= 0; | ||
break; | ||
case LESS_THAN: | ||
predicate = (t,v) -> t != null && compare(t, v) < 0; | ||
break; | ||
case LESS_OR_EQUAL: | ||
predicate = (t,v) -> t != null && compare(t, v) <= 0; | ||
break; | ||
default: | ||
throw new UnsupportedOperationException("Cannot convert " + this + " into a predicate"); | ||
} | ||
return (t, v) -> predicateWithTypeCheck(t, v, predicate); | ||
} | ||
|
||
private <T, V> boolean predicateWithTypeCheck(T t, V v, BiPredicate<T, V> predicate) { | ||
if (t == null | ||
|| v == null | ||
|| t instanceof Number && v instanceof Number | ||
|| t.getClass() == v.getClass()) { | ||
return predicate.test(t, v); | ||
} else { | ||
if (!typeCheckLogged) { | ||
LOG.error("Cannot compare values of different types: {} and {}. RuleSet: {}. RuleName: {}. Condition: {}", | ||
convertJavaClassToPythonClass(t.getClass()), | ||
convertJavaClassToPythonClass(v.getClass()), | ||
conditionContext.getRuleSet(), conditionContext.getRuleName(), conditionContext.getConditionExpression()); | ||
typeCheckLogged = true; // Log only once per constraint | ||
} | ||
return false; // Different types are never equal | ||
} | ||
} | ||
|
||
public RulebookConstraintOperator inverse() { | ||
switch (this.type) { | ||
case GREATER_THAN: | ||
this.type = Index.ConstraintType.LESS_THAN; | ||
return this; | ||
case GREATER_OR_EQUAL: | ||
this.type = Index.ConstraintType.LESS_OR_EQUAL; | ||
return this; | ||
case LESS_THAN: | ||
this.type = Index.ConstraintType.GREATER_THAN; | ||
return this; | ||
case LESS_OR_EQUAL: | ||
this.type = Index.ConstraintType.GREATER_OR_EQUAL; | ||
return this; | ||
default: | ||
return this; | ||
} | ||
} | ||
|
||
public boolean isComparison() { | ||
return isAscending() || isDescending(); | ||
} | ||
|
||
public boolean isAscending() { | ||
return this.type == Index.ConstraintType.GREATER_THAN || this.type == Index.ConstraintType.GREATER_OR_EQUAL; | ||
} | ||
|
||
public boolean isDescending() { | ||
return this.type == Index.ConstraintType.LESS_THAN || this.type == Index.ConstraintType.LESS_OR_EQUAL; | ||
} | ||
|
||
private class ConditionContext { | ||
|
||
private String ruleSet; | ||
private String ruleName; | ||
private String conditionExpression; | ||
|
||
public ConditionContext(String ruleSet, String ruleName, String conditionExpression) { | ||
this.ruleSet = ruleSet; | ||
this.ruleName = ruleName; | ||
this.conditionExpression = conditionExpression; | ||
} | ||
|
||
public String getRuleSet() { | ||
return ruleSet; | ||
} | ||
|
||
public String getRuleName() { | ||
return ruleName; | ||
} | ||
|
||
public String getConditionExpression() { | ||
return conditionExpression; | ||
} | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
// works for node sharing | ||
return type.toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
...tion-api/src/test/java/org/drools/ansible/rulebook/integration/api/StringPrintStream.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.drools.ansible.rulebook.integration.api; | ||
|
||
import java.io.PrintStream; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class StringPrintStream extends PrintStream { | ||
|
||
public static final String LINE_SEP = System.getProperty("line.separator"); | ||
PrintStream other; | ||
List<String> stringList = new ArrayList<>(); | ||
|
||
public StringPrintStream(PrintStream ps) { | ||
super(ps); | ||
other = ps; | ||
} | ||
|
||
public void print(String s) { | ||
other.print(s); | ||
stringList.add(s); | ||
} | ||
|
||
public void println(String s) { | ||
other.println(s); | ||
stringList.add(s); | ||
} | ||
|
||
public void println(Object o) { | ||
other.println(o); | ||
stringList.add(o.toString()); | ||
} | ||
|
||
public List<String> getStringList() { | ||
return stringList; | ||
} | ||
} |
Oops, something went wrong.