-
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.
[1.0.x][DROOLS-7635] ansible-rulebook : Raise an error when a conditi…
…on compares incompatible types (#120) * 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 * [DROOLS-7635] ansible-rulebook : Raise an error when a condition comp… (#117) * [DROOLS-7635] ansible-rulebook : Raise an error when a condition compares incompatible types - alpha index test, beta index test beta index test fix add negate test and any test * refactor * minor comment fix
- Loading branch information
Showing
8 changed files
with
939 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
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
202 changes: 202 additions & 0 deletions
202
...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,202 @@ | ||
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; | ||
} | ||
|
||
@Override | ||
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; | ||
} | ||
|
||
@Override | ||
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 (isCompatibleType(t, v)) { | ||
return predicate.test(t, v); | ||
} else { | ||
logTypeCheck(t, v); | ||
return false; // Different types evaluation always return false even if the operator is NOT_EQUAL | ||
} | ||
} | ||
|
||
/* | ||
* Log a type check error once per constraint | ||
*/ | ||
<T, V> void logTypeCheck(T t, V v) { | ||
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 | ||
} | ||
} | ||
|
||
public static boolean isCompatibleType(Object t, Object v) { | ||
return t == null | ||
|| v == null | ||
|| t instanceof Number && v instanceof Number | ||
|| t.getClass() == v.getClass(); | ||
} | ||
|
||
@Override | ||
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
Oops, something went wrong.