diff --git a/.gitignore b/.gitignore
index 372e03f0..f0f81b6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,5 @@ dependency-reduced-pom.xml
#CI
!.ci
+
+.antlr
diff --git a/drools-ansible-rulebook-integration-api/pom.xml b/drools-ansible-rulebook-integration-api/pom.xml
index 6c8da9b1..f3f21e95 100644
--- a/drools-ansible-rulebook-integration-api/pom.xml
+++ b/drools-ansible-rulebook-integration-api/pom.xml
@@ -14,6 +14,11 @@
Drools :: Ansible Rulebook Integration :: API
+
+ org.drools
+ drools-ansible-rulebook-integration-protoextractor
+ ${project.version}
+
org.slf4j
slf4j-simple
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java
index fc4f8034..f7590baa 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java
@@ -4,6 +4,7 @@
import org.drools.ansible.rulebook.integration.api.rulesengine.RulesExecutorSession;
import org.drools.ansible.rulebook.integration.api.rulesengine.SessionStats;
import org.drools.base.facttemplates.Fact;
+import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.Match;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -95,4 +96,8 @@ public String getAllFactsAsJson() {
public CompletableFuture> advanceTime(long amount, TimeUnit unit ) {
return rulesEvaluator.advanceTime(amount, unit );
}
+
+ public KieSession asKieSession() {
+ return rulesEvaluator.asKieSession();
+ }
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java
index 6be1258d..bd6a302a 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java
@@ -6,6 +6,8 @@
import java.util.Map;
import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpression;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils;
import org.drools.model.PrototypeDSL;
import org.drools.model.PrototypeExpression;
import org.drools.model.PrototypeVariable;
@@ -37,8 +39,8 @@ public ConditionExpression(PrototypeExpression prototypeExpression, boolean fiel
public ConditionExpression composeWith(PrototypeExpression.BinaryOperation.Operator decodeBinaryOperator, ConditionExpression rhs) {
if (isBeta() && rhs.isBeta() && !prototypeName.equals(rhs.getPrototypeName())) {
- PrototypeExpression composed = prototypeField(betaVariable, getFieldName())
- .composeWith(decodeBinaryOperator, prototypeField(rhs.getBetaVariable(), rhs.getFieldName()));
+ PrototypeExpression composed = ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(betaVariable, getFieldName())
+ .composeWith(decodeBinaryOperator, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(rhs.getBetaVariable(), rhs.getFieldName()));
return new ConditionExpression(composed);
}
@@ -54,7 +56,7 @@ public boolean isBeta() {
}
public String getFieldName() {
- return ((PrototypeExpression.PrototypeFieldValue) prototypeExpression).getFieldName();
+ return prototypeExpression.getIndexingKey().orElseThrow(IllegalStateException::new);
}
public PrototypeExpression getPrototypeExpression() {
@@ -142,7 +144,7 @@ private static ConditionExpression createFieldExpression(RuleGenerationContext r
betaVariable = (PrototypeVariable) boundPattern.getFirstVariable();
}
}
- return new ConditionExpression(fieldName2PrototypeExpression(fieldName), true, prototypeName, betaVariable);
+ return new ConditionExpression(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(fieldName), true, prototypeName, betaVariable);
}
private static PrototypeExpression.BinaryOperation.Operator decodeBinaryOperator(String operator) {
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java
index db3a48af..4a539d09 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java
@@ -6,10 +6,9 @@
import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext;
import org.drools.ansible.rulebook.integration.api.rulesmodel.ParsedCondition;
import org.drools.model.ConstraintOperator;
-import org.drools.model.PrototypeFact;
+import org.drools.model.Prototype;
import static org.drools.ansible.rulebook.integration.api.domain.conditions.ConditionExpression.map2Expr;
-import static org.drools.model.PrototypeExpression.fixedValue;
import static org.drools.model.PrototypeExpression.thisPrototype;
public enum ExistsField implements ConstraintOperator, ConditionFactory {
@@ -21,7 +20,7 @@ public enum ExistsField implements ConstraintOperator, ConditionFactory {
@Override
public BiPredicate asPredicate() {
- return (t,v) -> ((PrototypeFact) t).has((String) v);
+ return (t, v) -> v != Prototype.UNDEFINED_VALUE; // actually, always true from caller: https://github.com/kiegroup/drools/blob/9de8d0b54b364bda1b1d76d81923c8bfc060c2f8/drools-model/drools-canonical-model/src/main/java/org/drools/model/PrototypeDSL.java#L273
}
@Override
@@ -31,6 +30,6 @@ public String toString() {
@Override
public ParsedCondition createParsedCondition(RuleGenerationContext ruleContext, String expressionName, Map, ?> expression) {
- return new ParsedCondition(thisPrototype(), this, fixedValue(map2Expr(ruleContext, expression).getFieldName())).withNotPattern(expressionName.equals(NEGATED_EXPRESSION_NAME));
+ return new ParsedCondition(thisPrototype(), this, map2Expr(ruleContext, expression).getPrototypeExpression()).withNotPattern(expressionName.equals(NEGATED_EXPRESSION_NAME));
}
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java
index c678c860..f920a4e7 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java
@@ -5,13 +5,14 @@
import org.drools.model.ConstraintOperator;
import org.drools.model.PrototypeFact;
+@Deprecated() // TODO seems no longer in-use?
public enum NotExistsField implements ConstraintOperator {
INSTANCE;
@Override
public BiPredicate asPredicate() {
- return (t,v) -> !((PrototypeFact) t).has((String) v);
+ throw new UnsupportedOperationException("deprecated and should be no longer in use");
}
@Override
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java
index 9d38c747..46b6512b 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java
@@ -8,6 +8,7 @@
import org.drools.ansible.rulebook.integration.api.domain.conditions.ConditionExpression;
import org.drools.ansible.rulebook.integration.api.rulesmodel.BetaParsedCondition;
import org.drools.ansible.rulebook.integration.api.rulesmodel.ParsedCondition;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils;
import org.drools.model.ConstraintOperator;
import org.drools.model.Prototype;
import org.drools.model.PrototypeDSL;
@@ -63,8 +64,8 @@ private static ParsedCondition createSelectConditionWithLeftAndRightFields(RuleG
PrototypeDSL.PrototypePatternDef rightPattern = ruleContext.getBoundPattern(rightPatternBindName);
return rightPattern == null ?
- new ParsedCondition(prototypeField(leftField), operator, prototypeField(rightField)) :
- new BetaParsedCondition(prototypeField(leftField), operator, (PrototypeVariable) rightPattern.getFirstVariable(), prototypeField(rightField.substring(dotPos+1)));
+ new ParsedCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftField), operator, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(rightField)) :
+ new BetaParsedCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftField), operator, (PrototypeVariable) rightPattern.getFirstVariable(), ExtractorPrototypeExpressionUtils.prototypeFieldExtractorSkippingFirst(rightField));
}
static ParsedCondition createSelectConditionWithFixedRight(ConditionExpression left, BiPredicate opPred, Object rhsValue, boolean positive) {
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java
index 097cafab..d372769a 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java
@@ -1,7 +1,15 @@
package org.drools.ansible.rulebook.integration.api.domain.temporal;
+import org.drools.ansible.rulebook.integration.protoextractor.ExtractorParser;
+import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpression;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils;
+import org.drools.base.facttemplates.Fact;
import org.drools.model.Index;
import org.drools.model.PrototypeDSL;
+import org.drools.model.PrototypeExpression;
+import org.drools.model.PrototypeFact;
import org.drools.model.PrototypeVariable;
import java.util.List;
@@ -18,13 +26,13 @@ public abstract class OnceAbstractTimeConstraint implements TimeConstraint {
protected String ruleName;
protected final TimeAmount timeAmount;
- protected final List groupByAttributes;
+ protected final List groupByAttributes;
protected PrototypeDSL.PrototypePatternDef guardedPattern;
private PrototypeVariable controlVariable;
- public OnceAbstractTimeConstraint(TimeAmount timeAmount, List groupByAttributes) {
+ public OnceAbstractTimeConstraint(TimeAmount timeAmount, List groupByAttributes) {
this.timeAmount = timeAmount;
this.groupByAttributes = groupByAttributes;
}
@@ -39,10 +47,13 @@ protected PrototypeVariable getControlVariable() {
protected PrototypeDSL.PrototypePatternDef createControlPattern() {
PrototypeDSL.PrototypePatternDef controlPattern = protoPattern(variable(getPrototype(SYNTHETIC_PROTOTYPE_NAME)));
- for (String unique : groupByAttributes) {
- controlPattern.expr( prototypeField(unique), Index.ConstraintType.EQUAL, getPatternVariable(), prototypeField(unique) );
+ for (GroupByAttribute unique : groupByAttributes) {
+ controlPattern.expr( prototypeField(unique.getKey()), // intentional, the control fact has the "group by" key string as-is (not structured), so we reference it for the left part
+ Index.ConstraintType.EQUAL,
+ getPatternVariable(),
+ unique.asPrototypeExpression() ); // on the right, we need extractor to check the real fact/event attribute value
}
- controlPattern.expr( prototypeField("drools_rule_name"), Index.ConstraintType.EQUAL, fixedValue(ruleName) );
+ controlPattern.expr( ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("drools_rule_name"), Index.ConstraintType.EQUAL, fixedValue(ruleName) );
this.controlVariable = (PrototypeVariable) controlPattern.getFirstVariable();
return controlPattern;
}
@@ -62,4 +73,39 @@ static String sanitizeAttributeName(String name) {
}
return name;
}
+
+ public static class GroupByAttribute {
+ private final String key;
+ private final ExtractorNode extractor;
+
+ private GroupByAttribute(String key, ExtractorNode extractor) {
+ this.key = key;
+ this.extractor = extractor;
+ }
+
+ public static GroupByAttribute from(String expr) {
+ return new GroupByAttribute(expr, ExtractorParser.parse(expr));
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public PrototypeExpression asPrototypeExpression() {
+ return new ExtractorPrototypeExpression(extractor);
+ }
+
+ public Object evalExtractorOnFact(PrototypeFact fact) {
+ return evalExtractorOnFact((Fact) fact);
+ }
+
+ public Object evalExtractorOnFact(Fact fact) {
+ return ExtractorUtils.getValueFrom(extractor, fact.asMap());
+ }
+
+ @Override
+ public String toString() {
+ return "GroupByAttribute [key=" + key + ", extractor=" + extractor + "]";
+ }
+ }
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java
index a72d7b28..ec2fe7a5 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java
@@ -140,7 +140,7 @@ private static Object controlFact2Event(Fact fact) {
return writeMetaDataOnEvent((Fact) fact.get("event"), ruleEngineMeta);
}
- public OnceAfterDefinition(TimeAmount timeAmount, List groupByAttributes) {
+ public OnceAfterDefinition(TimeAmount timeAmount, List groupByAttributes) {
super(timeAmount, groupByAttributes);
}
@@ -194,8 +194,8 @@ public List getControlRules(RuleGenerationContext ruleContext) {
not( createControlPattern() ),
on(getPatternVariable()).execute((drools, event) -> {
Event controlEvent = createMapBasedEvent( controlPrototype );
- for (String unique : groupByAttributes) {
- controlEvent.set(unique, event.get(unique));
+ for (GroupByAttribute unique : groupByAttributes) {
+ controlEvent.set(unique.getKey(), unique.evalExtractorOnFact(event));
}
controlEvent.set("drools_rule_name", ruleName);
controlEvent.set( "event", event );
@@ -250,7 +250,10 @@ public String toString() {
}
public static OnceAfterDefinition parseOnceAfter(String onceWithin, List groupByAttributes) {
- List sanitizedAttributes = groupByAttributes.stream().map(OnceAbstractTimeConstraint::sanitizeAttributeName).collect(toList());
+ List sanitizedAttributes = groupByAttributes.stream()
+ .map(OnceAbstractTimeConstraint::sanitizeAttributeName)
+ .map(GroupByAttribute::from)
+ .collect(toList());
return new OnceAfterDefinition(parseTimeAmount(onceWithin), sanitizedAttributes);
}
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java
index 349cb101..aff4bb26 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java
@@ -70,7 +70,7 @@ public class OnceWithinDefinition extends OnceAbstractTimeConstraint {
public static final String KEYWORD = "once_within";
- public OnceWithinDefinition(TimeAmount timeAmount, List groupByAttributes) {
+ public OnceWithinDefinition(TimeAmount timeAmount, List groupByAttributes) {
super(timeAmount, groupByAttributes);
}
@@ -89,8 +89,8 @@ public void executeTimeConstraintConsequence(Drools drools, Object... facts) {
Event controlEvent = createMapBasedEvent( getPrototype(SYNTHETIC_PROTOTYPE_NAME) )
.withExpiration(timeAmount.getAmount(), timeAmount.getTimeUnit());
Fact fact = (Fact) facts[0];
- for (String unique : groupByAttributes) {
- controlEvent.set(unique, fact.get(unique));
+ for (GroupByAttribute unique : groupByAttributes) {
+ controlEvent.set(unique.getKey(), unique.evalExtractorOnFact(fact));
}
controlEvent.set("drools_rule_name", ruleName);
drools.insert(controlEvent);
@@ -127,7 +127,10 @@ public String toString() {
}
public static OnceWithinDefinition parseOnceWithin(String onceWithin, List groupByAttributes) {
- List sanitizedAttributes = groupByAttributes.stream().map(OnceAbstractTimeConstraint::sanitizeAttributeName).collect(toList());
+ List sanitizedAttributes = groupByAttributes.stream()
+ .map(OnceAbstractTimeConstraint::sanitizeAttributeName)
+ .map(GroupByAttribute::from)
+ .collect(toList());
return new OnceWithinDefinition(parseTimeAmount(onceWithin), sanitizedAttributes);
}
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java
index ee8b4542..fc24cd4e 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java
@@ -19,6 +19,7 @@
import org.drools.ansible.rulebook.integration.api.io.RuleExecutorChannel;
import org.drools.base.facttemplates.Fact;
import org.drools.core.common.InternalFactHandle;
+import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.Match;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -247,4 +248,9 @@ private List atomicRuleEvaluation(boolean processEventInsertion, Supplier
return matches;
}
+
+ @Override
+ public KieSession asKieSession() {
+ return rulesExecutorSession.asKieSession();
+ }
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java
index 4d71ae88..a37cbc3a 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java
@@ -1,6 +1,7 @@
package org.drools.ansible.rulebook.integration.api.rulesengine;
import org.drools.ansible.rulebook.integration.api.RulesExecutorContainer;
+import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.Match;
import java.util.Collection;
@@ -41,4 +42,6 @@ public interface RulesEvaluator {
static RulesEvaluator createRulesEvaluator( RulesExecutorSession rulesExecutorSession, boolean async ) {
return async ? new AsyncRulesEvaluator(rulesExecutorSession) : new SyncRulesEvaluator(rulesExecutorSession);
}
+
+ KieSession asKieSession();
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java
index d7f37078..db0c9ebc 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java
@@ -16,7 +16,6 @@
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
-import static org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil.ORIGINAL_MAP_FIELD;
import static org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil.mapToFact;
@@ -107,7 +106,7 @@ private static boolean isKeyToBeIgnored(String wmFactKey, String... keysToExclud
}
}
}
- return wmFactKey.equals(ORIGINAL_MAP_FIELD);
+ return false;
}
int fireAllRules() {
@@ -165,4 +164,8 @@ void setExecuteActions(boolean executeActions) {
public boolean isMatchMultipleRules() {
return rulesSet.isMatchMultipleRules();
}
+
+ public KieSession asKieSession() {
+ return kieSession;
+ }
}
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/ParsedCondition.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/ParsedCondition.java
index be59d3ac..104fbdbe 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/ParsedCondition.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/ParsedCondition.java
@@ -2,6 +2,7 @@
import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext;
import org.drools.ansible.rulebook.integration.api.domain.constraints.NegationOperator;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils;
import org.drools.model.ConstraintOperator;
import org.drools.model.PrototypeDSL;
import org.drools.model.PrototypeExpression;
@@ -22,7 +23,7 @@ public class ParsedCondition {
private boolean negated = false;
public ParsedCondition(String left, ConstraintOperator operator, Object right) {
- this(prototypeField(left), operator, fixedValue(right));
+ this(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(left), operator, fixedValue(right));
}
public ParsedCondition(PrototypeExpression left, ConstraintOperator operator, PrototypeExpression right) {
diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java
index 070388db..58730683 100644
--- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java
+++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java
@@ -12,51 +12,29 @@
import static org.drools.modelcompiler.facttemplate.FactFactory.createMapBasedFact;
public class RulesModelUtil {
-
- public static final String ORIGINAL_MAP_FIELD = "$_ORIGINAL_$_MAP_$_FIELD_$";
public static final String META_FIELD = "meta";
public static final String RULE_ENGINE_META_FIELD = "rule_engine";
private RulesModelUtil() { }
public static Fact mapToFact(Map factMap, boolean event) {
- Fact fact = event ? createMapBasedEvent( getPrototype(DEFAULT_PROTOTYPE_NAME) ) : createMapBasedFact( getPrototype(DEFAULT_PROTOTYPE_NAME) );
- populateFact(fact, factMap, "");
- fact.set(ORIGINAL_MAP_FIELD, factMap);
+ Fact fact = event ? createMapBasedEvent( getPrototype(DEFAULT_PROTOTYPE_NAME), factMap ) : createMapBasedFact( getPrototype(DEFAULT_PROTOTYPE_NAME), factMap );
return fact;
}
- private static void populateFact(Fact fact, Map, ?> value, String fieldName) {
- for (Map.Entry, ?> entry : value.entrySet()) {
- String key = fieldName + entry.getKey();
- fact.set(key, entry.getValue());
- if (entry.getValue() instanceof Map) {
- populateFact(fact, (Map, ?>) entry.getValue(), key + ".");
- }
- }
- }
-
public static Object factToMap(Object fact) {
if (fact instanceof Fact) {
- return factToMap(((Fact) fact).asMap());
- }
- if (fact instanceof Map) {
- return factToMap(((Map) fact));
+ return ((Fact) fact).asMap();
}
return fact;
}
- private static Map factToMap(Map factMap) {
- Map map = (Map) factMap.get(ORIGINAL_MAP_FIELD);
- return map != null ? map : factMap;
- }
-
public static Map asFactMap(String json) {
return new JSONObject(json).toMap();
}
public static Fact writeMetaDataOnEvent(Fact event, Map ruleEngineMeta) {
- Map map = (Map) event.get(ORIGINAL_MAP_FIELD);
+ Map map = (Map) event.asMap();
Map meta = (Map) map.computeIfAbsent(META_FIELD, x -> new HashMap<>());
meta.put(RULE_ENGINE_META_FIELD, ruleEngineMeta);
return event;
diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AdditionTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AdditionTest.java
index 198b04bc..af3600f1 100644
--- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AdditionTest.java
+++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AdditionTest.java
@@ -3,6 +3,7 @@
import java.util.List;
import org.drools.ansible.rulebook.integration.api.domain.RulesSet;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils;
import org.drools.model.Index;
import org.junit.Test;
import org.kie.api.runtime.rule.Match;
@@ -70,7 +71,7 @@ public void testExecuteRules() {
public void testExecuteRulesSet() {
RulesSet rulesSet = new RulesSet();
rulesSet.addRule().withCondition().all()
- .addSingleCondition(prototypeField("nested.i"), Index.ConstraintType.EQUAL, prototypeField("nested.j").add(fixedValue(1)));
+ .addSingleCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("nested.i"), Index.ConstraintType.EQUAL, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("nested.j").add(fixedValue(1)));
RulesExecutor rulesExecutor = RulesExecutorFactory.createRulesExecutor(rulesSet);
diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java
index d439ee01..37133fbb 100644
--- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java
+++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java
@@ -1,11 +1,27 @@
package org.drools.ansible.rulebook.integration.api;
-import java.util.List;
-
+import org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory;
+import org.drools.base.definitions.InternalKnowledgePackage;
+import org.drools.base.facttemplates.FactTemplate;
+import org.drools.base.facttemplates.FactTemplateObjectType;
+import org.drools.base.rule.IndexableConstraint;
+import org.drools.base.util.index.ConstraintTypeOperator;
+import org.drools.core.reteoo.AlphaNode;
+import org.drools.core.reteoo.EntryPointNode;
+import org.drools.core.reteoo.ObjectSink;
+import org.drools.core.reteoo.ObjectTypeNode;
+import org.drools.core.reteoo.Rete;
+import org.drools.kiesession.rulebase.InternalKnowledgeBase;
+import org.drools.model.Prototype;
+import org.drools.modelcompiler.facttemplate.FactFactory;
import org.junit.Test;
+import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.rule.Match;
+import java.util.List;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
public class AlphaTest {
@@ -31,7 +47,7 @@ public void testEqualsWithFixedValue() {
" ]\n" +
" },\n" +
" \"enabled\": true,\n" +
- " \"name\": null\n" +
+ " \"name\": \"R1\"\n" +
" }\n" +
" }\n" +
" ]\n" +
@@ -39,6 +55,9 @@ public void testEqualsWithFixedValue() {
RulesExecutor rulesExecutor = RulesExecutorFactory.createFromJson(json);
+ Rete rete = ((InternalKnowledgeBase) rulesExecutor.asKieSession().getKieBase()).getRete();
+ assertConstraintType(rete, "R1", ConstraintTypeOperator.EQUAL);
+
List matchedRules = rulesExecutor.processEvents("{ \"i\": 3 }").join();
assertEquals(0, matchedRules.size());
@@ -47,11 +66,33 @@ public void testEqualsWithFixedValue() {
matchedRules = rulesExecutor.processEvents("{ \"i\": 3, \"j\": 3 }").join();
assertEquals(1, matchedRules.size());
- assertEquals("r_0", matchedRules.get(0).getRule().getName());
+ assertEquals("R1", matchedRules.get(0).getRule().getName());
rulesExecutor.dispose();
}
+ private void assertConstraintType(Rete rete, String ruleName, ConstraintTypeOperator expectedType) {
+ boolean asserted = false;
+ assertEquals("expecting only 1 pkg.", 1, rete.getRuleBase().getPackages().length);
+ InternalKnowledgePackage ipkg = rete.getRuleBase().getPackages()[0];
+ Prototype default_proto = PrototypeFactory.getPrototype(PrototypeFactory.DEFAULT_PROTOTYPE_NAME);
+ FactTemplate factTemplate = FactFactory.prototypeToFactTemplate(default_proto, ipkg);
+ EntryPointNode epn = rete.getEntryPointNodes().values().iterator().next();
+ ObjectTypeNode otn = epn.getObjectTypeNodes().get(new FactTemplateObjectType(factTemplate));
+ ObjectSink[] sinks = otn.getObjectSinkPropagator().getSinks();
+ for (ObjectSink objectSink : sinks) {
+ AlphaNode alphaNode = (AlphaNode) objectSink;
+ assertEquals("expecting that one rule has one AlphaNode.", 1, alphaNode.getAssociatedRules().length);
+ Rule rule = alphaNode.getAssociatedRules()[0];
+ if (rule.getName().equals(ruleName)) {
+ IndexableConstraint constraint = (IndexableConstraint) alphaNode.getConstraint();
+ assertEquals(expectedType, constraint.getConstraintType());
+ asserted = true;
+ }
+ }
+ assertTrue(asserted);
+ }
+
@Test
public void testEqualsOn2Fields() {
String json =
diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java
index 8ec94611..7c783063 100644
--- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java
+++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java
@@ -8,6 +8,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.drools.ansible.rulebook.integration.api.domain.RuleMatch;
import org.drools.ansible.rulebook.integration.api.domain.RulesSet;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils;
import org.drools.model.Index;
import org.drools.model.PrototypeFact;
import org.junit.Test;
@@ -351,8 +352,8 @@ public void testNegate() {
public void testAny() {
RulesSet rulesSet = new RulesSet();
rulesSet.addRule().withCondition().any()
- .addSingleCondition(prototypeField("i"), Index.ConstraintType.EQUAL, fixedValue(0)).withPatternBinding("event")
- .addSingleCondition(prototypeField("i"), Index.ConstraintType.EQUAL, fixedValue(1)).withPatternBinding("event");
+ .addSingleCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("i"), Index.ConstraintType.EQUAL, fixedValue(0)).withPatternBinding("event")
+ .addSingleCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("i"), Index.ConstraintType.EQUAL, fixedValue(1)).withPatternBinding("event");
RulesExecutor rulesExecutor = RulesExecutorFactory.createRulesExecutor(rulesSet);
checkAnyExecution(rulesExecutor);
diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java
index 3eeb0e4e..33ef6096 100644
--- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java
+++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java
@@ -2,6 +2,8 @@
import org.drools.ansible.rulebook.integration.api.domain.temporal.TimeAmount;
import org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil;
+import org.drools.ansible.rulebook.integration.protoextractor.ExtractorParser;
+import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils;
import org.drools.base.facttemplates.Fact;
import org.junit.Test;
import org.kie.api.runtime.rule.Match;
@@ -96,12 +98,12 @@ public void testOnceAfterWithOr() {
for (int i = 0; i < 3; i++) {
Fact fact = (Fact) matchedRules.get(0).getDeclarationValue("m_" + i);
- String host = fact.get("meta.hosts").toString();
+ String host = evalAgainstFact(fact, "meta.hosts").toString();
assertTrue( host.equals( "h1" ) || host.equals( "h2" ) );
- String level = fact.get("alert.level").toString();
+ String level = evalAgainstFact(fact, "alert.level").toString();
assertTrue( level.equals( "error" ) || level.equals( "warning" ) );
- Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD);
+ Map map = (Map) fact.asMap();
Map ruleEngineMeta = (Map) ((Map)map.get(RulesModelUtil.META_FIELD)).get(RulesModelUtil.RULE_ENGINE_META_FIELD);
assertEquals( new TimeAmount(10, TimeUnit.SECONDS).toString(), ruleEngineMeta.get("once_after_time_window") );
assertEquals( i == 0 ? 2 : 1, ruleEngineMeta.get("events_in_window") );
@@ -116,12 +118,12 @@ public void testOnceAfterWithOr() {
assertEquals("r1", matchedRules.get(0).getRule().getName());
Fact fact = (Fact) matchedRules.get(0).getDeclarationValue("m");
- String host = fact.get("meta.hosts").toString();
+ String host = evalAgainstFact(fact, "meta.hosts").toString();
assertTrue(host.equals("h1"));
- String level = fact.get("alert.level").toString();
+ String level = evalAgainstFact(fact, "alert.level").toString();
assertTrue(level.equals("warning"));
- Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD);
+ Map map = (Map) fact.asMap();
Map ruleEngineMeta = (Map) ((Map)map.get(RulesModelUtil.META_FIELD)).get(RulesModelUtil.RULE_ENGINE_META_FIELD);
assertEquals( new TimeAmount(10, TimeUnit.SECONDS).toString(), ruleEngineMeta.get("once_after_time_window") );
assertEquals( 1, ruleEngineMeta.get("events_in_window") );
@@ -129,4 +131,8 @@ public void testOnceAfterWithOr() {
rulesExecutor.dispose();
}
+
+ private static Object evalAgainstFact(Fact fact, String expr) {
+ return ExtractorUtils.getValueFrom(ExtractorParser.parse(expr), fact.asMap());
+ }
}
diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java
index ca5ac43c..cef579d8 100644
--- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java
+++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java
@@ -122,7 +122,7 @@ private void onceWithinTest(String json) {
assertEquals(1, matchedRules.size());
Fact fact = (Fact) matchedRules.get(0).getDeclarationValue("singleton");
- Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD);
+ Map map = (Map) fact.asMap();
Map ruleEngineMeta = (Map) ((Map)map.get(RulesModelUtil.META_FIELD)).get(RulesModelUtil.RULE_ENGINE_META_FIELD);
assertEquals( new TimeAmount(10, TimeUnit.SECONDS).toString(), ruleEngineMeta.get("once_within_time_window") );
diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java
index 250e89b2..fc7f385c 100644
--- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java
+++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java
@@ -182,7 +182,7 @@ public void testSimpleTimedOut() {
assertEquals(1, stats.getEventsProcessed());
assertEquals(0, stats.getEventsSuppressed());
}
-
+
@Test
public void testTimedOutWithAutomaticClockAdvance() throws IOException {
String json =
diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java
index 6b6557b8..89a7fd94 100644
--- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java
+++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java
@@ -23,6 +23,7 @@
import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext;
import org.drools.ansible.rulebook.integration.api.rulesmodel.ParsedCondition;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpression;
import org.drools.model.PrototypeExpression;
import org.junit.Test;
@@ -42,8 +43,8 @@ public void createParsedConditionWithSelectExpression() {
ParsedCondition parsedCondition = SelectConstraint.INSTANCE.createParsedCondition(new RuleGenerationContext(), SelectConstraint.EXPRESSION_NAME, map);
- assertThat(parsedCondition.getLeft()).isInstanceOf(PrototypeExpression.PrototypeFieldValue.class);
- assertThat(((PrototypeExpression.PrototypeFieldValue)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels");
+ assertThat(parsedCondition.getLeft()).isInstanceOf(ExtractorPrototypeExpression.class);
+ assertThat(parsedCondition.getLeft().getIndexingKey()).isPresent().contains("levels");
assertThat(parsedCondition.getRight()).isInstanceOf(PrototypeExpression.FixedValue.class); // rightValue is not important. "25" is already incorporated in the operator
@@ -65,8 +66,8 @@ public void createParsedConditionWithSelectNotExpression() {
ParsedCondition parsedCondition = SelectConstraint.INSTANCE.createParsedCondition(new RuleGenerationContext(), SelectConstraint.NEGATED_EXPRESSION_NAME, map);
- assertThat(parsedCondition.getLeft()).isInstanceOf(PrototypeExpression.PrototypeFieldValue.class);
- assertThat(((PrototypeExpression.PrototypeFieldValue)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels");
+ assertThat(parsedCondition.getLeft()).isInstanceOf(ExtractorPrototypeExpression.class);
+ assertThat(parsedCondition.getLeft().getIndexingKey()).isPresent().contains("levels");
assertThat(parsedCondition.getRight()).isInstanceOf(PrototypeExpression.FixedValue.class); // rightValue is not important. "25" is already incorporated in the operator
diff --git a/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java b/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java
index 4289688c..5f146e72 100644
--- a/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java
+++ b/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java
@@ -205,22 +205,22 @@ private SimpleCondition createCondition(String binding, String leftValue, String
String leftBinding = getBindingFromMap(l);
if (leftBinding != null) {
- PrototypeExpression rightExpression = prototypeField(((Map) l).get(leftBinding).toString()).composeWith(decodeBinaryOperator(rightKey), toPrototypeExpression(r));
- return new BetaExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), leftBinding, rightExpression);
+ PrototypeExpression rightExpression = ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(((Map) l).get(leftBinding).toString()).composeWith(decodeBinaryOperator(rightKey), toPrototypeExpression(r));
+ return new BetaExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), leftBinding, rightExpression);
}
String rightBinding = getBindingFromMap(r);
if (rightBinding != null) {
- PrototypeExpression rightExpression = toPrototypeExpression(l).composeWith(decodeBinaryOperator(rightKey), prototypeField(((Map) r).get(rightBinding).toString()));
- return new BetaExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), rightBinding, rightExpression);
+ PrototypeExpression rightExpression = toPrototypeExpression(l).composeWith(decodeBinaryOperator(rightKey), ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(((Map) r).get(rightBinding).toString()));
+ return new BetaExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), rightBinding, rightExpression);
}
PrototypeExpression rightExpression = toPrototypeExpression(l).composeWith(decodeBinaryOperator(rightKey), toPrototypeExpression(r));
- return new ExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), rightExpression);
+ return new ExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), rightExpression);
}
if (existingBindings.contains(rightKey)) {
if (rightValue instanceof String) {
- return new BetaExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), rightKey, prototypeField((String) rightValue));
+ return new BetaExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), rightKey, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor((String) rightValue));
}
throw new UnsupportedOperationException();
}
@@ -234,7 +234,7 @@ private PrototypeExpression toPrototypeExpression(Object value) {
assert(map.size() == 1);
String fieldName = (String) map.get("$m");
assert(fieldName != null);
- return prototypeField(fieldName);
+ return ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(fieldName);
}
return fixedValue(value);
}
diff --git a/drools-ansible-rulebook-integration-protoextractor/input.txt b/drools-ansible-rulebook-integration-protoextractor/input.txt
new file mode 100644
index 00000000..c8a152e5
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/input.txt
@@ -0,0 +1 @@
+a.b["c"].d["e/asd"]['f'].g.h
\ No newline at end of file
diff --git a/drools-ansible-rulebook-integration-protoextractor/pom.xml b/drools-ansible-rulebook-integration-protoextractor/pom.xml
new file mode 100644
index 00000000..374e99ae
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/pom.xml
@@ -0,0 +1,96 @@
+
+
+
+ drools-ansible-rulebook-integration
+ org.drools
+ 1.0.3-SNAPSHOT
+
+ 4.0.0
+
+ drools-ansible-rulebook-integration-protoextractor
+
+ Drools :: Ansible Rulebook Integration :: Protoextractor
+
+
+
+ org.drools
+ drools-canonical-model
+ ${version.drools}
+
+
+ org.drools
+ drools-core
+ ${version.drools}
+
+
+ org.antlr
+ antlr4-runtime
+ ${version.org.antlr4}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${version.jackson}
+
+
+ org.drools
+ drools-model-compiler
+ ${version.drools}
+ test
+
+
+
+
+
+
+ org.antlr
+ antlr4-maven-plugin
+ ${version.org.antlr4}
+
+
+
+ antlr4
+
+
+ true
+ false
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+
+ TestRigGui
+ none
+
+ java
+
+
+
+
+ org.antlr.v4.gui.TestRig
+
+ org.drools.ansible.rulebook.integration.protoextractor.Protoextractor
+ extractor
+ -gui
+ input.txt
+
+ true
+
+
+
+ org.antlr
+ antlr4
+ ${version.org.antlr4}
+
+
+
+
+
+
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 b/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4
new file mode 100644
index 00000000..80e07683
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4
@@ -0,0 +1,26 @@
+grammar Protoextractor;
+
+extractor : identifier ( chunk )* ;
+
+chunk : ('.' identifier) | squaredAccessor | indexAccessor ;
+
+squaredAccessor : '[' stringLiteral ']';
+indexAccessor : '[' integerLiteral ']';
+
+stringLiteral : STRING1 | STRING2 ;
+integerLiteral : SIGNED_INT ;
+
+// keep as := ID given this is matching upstream pyparsing, see lexer rule.
+identifier : ID ;
+
+// ref https://pyparsing-docs.readthedocs.io/en/latest/pyparsing.html#pyparsing.pyparsing_common.identifier
+ID : [\p{Letter}_][\p{Letter}_0-9]* ;
+
+// ref https://github.com/ansible/ansible-rulebook/blob/24444bfcefcd7e9551d708723b76f58c3de9e976/ansible_rulebook/condition_parser.py#L118-L123
+STRING1 : '\'' ~('\r' | '\n' | '\'')* '\'' ;
+STRING2 : '"' ~('\r' | '\n' | '"')* '"' ;
+
+SIGNED_INT : (MINUS|PLUS)? DIGITS;
+MINUS : '-';
+PLUS : '+';
+DIGITS : [0-9]+;
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ASTProductionVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ASTProductionVisitor.java
new file mode 100644
index 00000000..8a51b601
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ASTProductionVisitor.java
@@ -0,0 +1,69 @@
+package org.drools.ansible.rulebook.integration.protoextractor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.ChunkContext;
+import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.ExtractorContext;
+import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.IdentifierContext;
+import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.IndexAccessorContext;
+import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.IntegerLiteralContext;
+import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.SquaredAccessorContext;
+import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.StringLiteralContext;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTBuilderFactory;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.IntegerLiteralNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.TextValue;
+
+public class ASTProductionVisitor extends ProtoextractorBaseVisitor {
+
+ @Override
+ public ASTNode visitStringLiteral(StringLiteralContext ctx) {
+ return ASTBuilderFactory.newStringLiteralNode(ctx);
+ }
+
+ @Override
+ public ASTNode visitIdentifier(IdentifierContext ctx) {
+ return ASTBuilderFactory.newIdentifierNode(ctx);
+ }
+
+ @Override
+ public ASTNode visitIntegerLiteral(IntegerLiteralContext ctx) {
+ return ASTBuilderFactory.newIntegerLiteralNode(ctx);
+ }
+
+ @Override
+ public ASTNode visitSquaredAccessor(SquaredAccessorContext ctx) {
+ TextValue str = (TextValue) ctx.stringLiteral().accept(this);
+ return ASTBuilderFactory.newSquaredAccessorNode(ctx, str);
+ }
+
+ @Override
+ public ASTNode visitIndexAccessor(IndexAccessorContext ctx) {
+ IntegerLiteralNode idx = (IntegerLiteralNode) ctx.integerLiteral().accept(this);
+ return ASTBuilderFactory.newIndexAccessorNode(ctx, idx);
+ }
+
+ @Override
+ public ASTNode visitChunk(ChunkContext ctx) {
+ if (ctx.identifier() != null) {
+ return ctx.identifier().accept(this);
+ } else if (ctx.squaredAccessor() != null) {
+ return ctx.squaredAccessor().accept(this);
+ } else if (ctx.indexAccessor() != null) {
+ return ctx.indexAccessor().accept(this);
+ }
+ throw new IllegalStateException("reached illegal state while parsing");
+ }
+
+ @Override
+ public ASTNode visitExtractor(ExtractorContext ctx) {
+ List values = new ArrayList<>();
+ ASTNode value0 = ctx.identifier().accept(this);
+ values.add(value0);
+ for (ChunkContext chunk : ctx.chunk()) {
+ values.add(chunk.accept(this));
+ }
+ return ASTBuilderFactory.newExtractorNode(ctx, values);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorParser.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorParser.java
new file mode 100644
index 00000000..8599e76f
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorParser.java
@@ -0,0 +1,24 @@
+package org.drools.ansible.rulebook.integration.protoextractor;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CodePointCharStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+
+public class ExtractorParser {
+ private ExtractorParser() {
+ // only static methods.
+ }
+
+ public static ExtractorNode parse(String expression) {
+ CodePointCharStream charStream = CharStreams.fromString(expression);
+ ProtoextractorLexer lexer = new ProtoextractorLexer(charStream);
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
+ ProtoextractorParser parser = new ProtoextractorParser(tokens);
+ ParseTree tree = parser.extractor();
+ ASTProductionVisitor visitor = new ASTProductionVisitor();
+ ExtractorNode extractor = (ExtractorNode) visitor.visit(tree);
+ return extractor;
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorUtils.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorUtils.java
new file mode 100644
index 00000000..e2d0c508
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorUtils.java
@@ -0,0 +1,21 @@
+package org.drools.ansible.rulebook.integration.protoextractor;
+
+import java.util.List;
+
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.NormalizedFieldRepresentationVisitor;
+import org.drools.ansible.rulebook.integration.protoextractor.prototype.ValueExtractionVisitor;
+
+public class ExtractorUtils {
+ private ExtractorUtils() {
+ // only static methods.
+ }
+
+ public static List getParts(ExtractorNode extractorNode) {
+ return new NormalizedFieldRepresentationVisitor().visit(extractorNode);
+ }
+
+ public static Object getValueFrom(ExtractorNode extractorNode, Object readValue) {
+ return new ValueExtractionVisitor(readValue).visit(extractorNode);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTBuilderFactory.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTBuilderFactory.java
new file mode 100644
index 00000000..0aa96bc1
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTBuilderFactory.java
@@ -0,0 +1,29 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import java.util.List;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class ASTBuilderFactory {
+ private ASTBuilderFactory() {
+ // only static utils methods.
+ }
+ public static StringLiteralNode newStringLiteralNode(ParserRuleContext ctx) {
+ return new StringLiteralNode( ctx );
+ }
+ public static IntegerLiteralNode newIntegerLiteralNode(ParserRuleContext ctx) {
+ return new IntegerLiteralNode( ctx );
+ }
+ public static IdentifierNode newIdentifierNode(ParserRuleContext ctx) {
+ return new IdentifierNode( ctx );
+ }
+ public static SquaredAccessorNode newSquaredAccessorNode(ParserRuleContext ctx, TextValue value) {
+ return new SquaredAccessorNode( ctx, value );
+ }
+ public static IndexAccessorNode newIndexAccessorNode(ParserRuleContext ctx, IntegerLiteralNode value) {
+ return new IndexAccessorNode( ctx, value );
+ }
+ public static ExtractorNode newExtractorNode(ParserRuleContext ctx, List values) {
+ return new ExtractorNode( ctx, values );
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTNode.java
new file mode 100644
index 00000000..3532a271
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTNode.java
@@ -0,0 +1,19 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+public interface ASTNode {
+ int getStartChar();
+
+ int getEndChar();
+
+ int getStartLine();
+
+ int getStartColumn();
+
+ int getEndLine();
+
+ int getEndColumn();
+
+ String getText();
+
+ T accept(Visitor v);
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java
new file mode 100644
index 00000000..89781df4
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java
@@ -0,0 +1,117 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.misc.Interval;
+
+public abstract class AbstractNode implements ASTNode {
+ protected final ASTNode[] EMPTY_CHILDREN = new ASTNode[0];
+ private int startChar;
+ private int endChar;
+ private int startLine;
+ private int startColumn;
+ private int endLine;
+ private int endColumn;
+
+ private String text;
+
+ protected AbstractNode( AbstractNode usingCtxFrom ) {
+ copyLocationAttributesFrom(usingCtxFrom);
+ }
+
+ public AbstractNode( ParserRuleContext ctx ) {
+ // DO NOT keep the reference to `ParserRuleContext` to avoid unneeded retention of lexer structures.
+ this.setStartChar( ctx.getStart().getStartIndex() );
+ this.setStartLine( ctx.getStart().getLine() );
+ this.setStartColumn( ctx.getStart().getCharPositionInLine() );
+ this.setEndChar( ctx.getStop().getStopIndex() );
+ this.setEndLine( ctx.getStop().getLine() );
+ this.setEndColumn( ctx.getStop().getCharPositionInLine() + ctx.getStop().getText().length() );
+ this.setText( getOriginalText( ctx ) );
+ }
+
+ private AbstractNode copyLocationAttributesFrom(AbstractNode from) {
+ this.setStartChar(from.getStartChar());
+ this.setStartLine(from.getStartLine());
+ this.setStartColumn(from.getStartColumn());
+ this.setEndChar(from.getEndChar());
+ this.setEndLine(from.getEndLine());
+ this.setEndColumn(from.getEndColumn());
+ this.setText(from.getText());
+ return this;
+ }
+
+ public static String getOriginalText(ParserRuleContext ctx) {
+ int a = ctx.start.getStartIndex();
+ int b = ctx.stop.getStopIndex();
+ Interval interval = new Interval( a, b );
+ return ctx.getStart().getInputStream().getText( interval );
+ }
+
+ @Override
+ public int getStartChar() {
+ return startChar;
+ }
+
+ public void setStartChar(int startChar) {
+ this.startChar = startChar;
+ }
+
+ @Override
+ public int getEndChar() {
+ return endChar;
+ }
+
+ public void setEndChar(int endChar) {
+ this.endChar = endChar;
+ }
+
+ @Override
+ public int getStartLine() {
+ return startLine;
+ }
+
+ public void setStartLine(int startLine) {
+ this.startLine = startLine;
+ }
+
+ @Override
+ public int getStartColumn() {
+ return startColumn;
+ }
+
+ public void setStartColumn(int startColumn) {
+ this.startColumn = startColumn;
+ }
+
+ @Override
+ public int getEndLine() {
+ return endLine;
+ }
+
+ public void setEndLine(int endLine) {
+ this.endLine = endLine;
+ }
+
+ @Override
+ public int getEndColumn() {
+ return endColumn;
+ }
+
+ public void setEndColumn(int endColumn) {
+ this.endColumn = endColumn;
+ }
+
+ @Override
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName()+"{" + text + "}";
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java
new file mode 100644
index 00000000..d4ccf913
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java
@@ -0,0 +1,20 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+public abstract class DefaultedVisitor implements Visitor {
+ public abstract T defaultVisit(ASTNode n);
+ public T visit(ASTNode n) {
+ return defaultVisit(n);
+ }
+ public T visit(IdentifierNode n) {
+ return defaultVisit(n);
+ }
+ public T visit(SquaredAccessorNode n) {
+ return defaultVisit(n);
+ }
+ public T visit(IndexAccessorNode n) {
+ return defaultVisit(n);
+ }
+ public T visit(ExtractorNode n) {
+ return defaultVisit(n);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java
new file mode 100644
index 00000000..7fb0d4f8
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java
@@ -0,0 +1,42 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class ExtractorNode extends AbstractNode {
+ private final List values;
+
+ public ExtractorNode(ParserRuleContext ctx, List values2) {
+ super(ctx);
+ Objects.requireNonNull(values2);
+ this.values = Collections.unmodifiableList(values2);
+ }
+
+ private ExtractorNode(ExtractorNode extractorNode, List skippingFirst) {
+ super(extractorNode);
+ Objects.requireNonNull(skippingFirst);
+ this.values = Collections.unmodifiableList(skippingFirst);
+ }
+
+ public ExtractorNode cloneSkipFirst() {
+ final List skippingFirst = values.subList(1, values.size());
+ return new ExtractorNode(this, skippingFirst);
+ }
+
+ public List getValues() {
+ return values;
+ }
+
+ @Override
+ public String toString() {
+ return "ExtractorNode [values=" + values + "]";
+ }
+
+ @Override
+ public T accept(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IdentifierNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IdentifierNode.java
new file mode 100644
index 00000000..9cea5867
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IdentifierNode.java
@@ -0,0 +1,28 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class IdentifierNode extends AbstractNode implements TextValue {
+
+ private final String value;
+
+ public IdentifierNode(ParserRuleContext ctx) {
+ super(ctx);
+ this.value = getOriginalText(ctx);
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "IdentifierNode [value=" + value + "]";
+ }
+
+ @Override
+ public T accept(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IndexAccessorNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IndexAccessorNode.java
new file mode 100644
index 00000000..7080c5aa
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IndexAccessorNode.java
@@ -0,0 +1,29 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import java.util.Objects;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class IndexAccessorNode extends AbstractNode {
+ private final IntegerLiteralNode value;
+
+ public IndexAccessorNode(ParserRuleContext ctx, IntegerLiteralNode value) {
+ super(ctx);
+ Objects.requireNonNull(value);
+ this.value = value;
+ }
+
+ public Integer getValue() {
+ return this.value.getValue();
+ }
+
+ @Override
+ public String toString() {
+ return "IndexAccessorNode [value=" + value + "]";
+ }
+
+ @Override
+ public T accept(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IntegerLiteralNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IntegerLiteralNode.java
new file mode 100644
index 00000000..b7937a99
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IntegerLiteralNode.java
@@ -0,0 +1,28 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class IntegerLiteralNode extends AbstractNode {
+
+ private final Integer value;
+
+ public IntegerLiteralNode(ParserRuleContext ctx) {
+ super(ctx);
+ String originalText = getOriginalText(ctx);
+ this.value = Integer.valueOf(originalText);
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "IntegerLiteralNode [value=" + value + "]";
+ }
+
+ @Override
+ public T accept(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/SquaredAccessorNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/SquaredAccessorNode.java
new file mode 100644
index 00000000..19feae39
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/SquaredAccessorNode.java
@@ -0,0 +1,29 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import java.util.Objects;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class SquaredAccessorNode extends AbstractNode {
+ private final TextValue value;
+
+ public SquaredAccessorNode(ParserRuleContext ctx, TextValue value) {
+ super(ctx);
+ Objects.requireNonNull(value);
+ this.value = value;
+ }
+
+ public String getValue() {
+ return this.value.getValue();
+ }
+
+ @Override
+ public String toString() {
+ return "SquaredAccessorNode [value=" + value + "]";
+ }
+
+ @Override
+ public T accept(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/StringLiteralNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/StringLiteralNode.java
new file mode 100644
index 00000000..1693dba6
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/StringLiteralNode.java
@@ -0,0 +1,29 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+
+public class StringLiteralNode extends AbstractNode implements TextValue {
+
+ private final String value;
+
+ public StringLiteralNode(ParserRuleContext ctx) {
+ super(ctx);
+ String originalText = getOriginalText(ctx);
+ this.value = originalText.substring(1, originalText.length()-1);
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "StringLiteralNode [value=" + value + "]";
+ }
+
+ @Override
+ public T accept(Visitor v) {
+ return v.visit(this);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/TextValue.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/TextValue.java
new file mode 100644
index 00000000..225caff5
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/TextValue.java
@@ -0,0 +1,7 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+public interface TextValue {
+
+ String getValue();
+
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java
new file mode 100644
index 00000000..28a74890
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java
@@ -0,0 +1,9 @@
+package org.drools.ansible.rulebook.integration.protoextractor.ast;
+
+public interface Visitor {
+ T visit(ASTNode n);
+ T visit(IdentifierNode n);
+ T visit(SquaredAccessorNode n);
+ T visit(IndexAccessorNode n);
+ T visit(ExtractorNode n);
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java
new file mode 100644
index 00000000..7a9e3406
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java
@@ -0,0 +1,44 @@
+package org.drools.ansible.rulebook.integration.protoextractor.prototype;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+import org.drools.model.PrototypeExpression.EvaluableExpression;
+import org.drools.model.PrototypeFact;
+import org.drools.model.PrototypeVariable;
+
+public class CompleteExtractorPrototypeExpression extends ExtractorPrototypeExpression implements EvaluableExpression {
+ private final PrototypeVariable protoVar;
+
+ public CompleteExtractorPrototypeExpression(PrototypeVariable protoVar, ExtractorNode extractorNode) {
+ super(extractorNode);
+ this.protoVar = protoVar;
+ }
+
+ public PrototypeVariable getProtoVar() {
+ return protoVar;
+ }
+
+ @Override
+ public boolean hasPrototypeVariable() {
+ return true;
+ }
+
+ @Override
+ public Collection getPrototypeVariables() {
+ return Collections.singletonList(protoVar);
+ }
+
+ @Override
+ public Object evaluate(Map factsMap) {
+ PrototypeFact prototypeFact = factsMap.get(protoVar);
+ return asFunction(IGNORED).apply(prototypeFact);
+ }
+
+ @Override
+ public String toString() {
+ return "CompleteExtractorPrototypeExpression{" + extractorNode + "}";
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java
new file mode 100644
index 00000000..7d663e88
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java
@@ -0,0 +1,58 @@
+package org.drools.ansible.rulebook.integration.protoextractor.prototype;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+import org.drools.base.facttemplates.Fact;
+import org.drools.model.Prototype;
+import org.drools.model.PrototypeDSL;
+import org.drools.model.PrototypeExpression;
+import org.drools.model.PrototypeFact;
+import org.drools.model.functions.Function1;
+
+public class ExtractorPrototypeExpression implements PrototypeExpression {
+ /**
+ * This is okay for ansible-integration work as prototype do not define accessor programmatically,
+ * but having prototype definition ignored in {@link #asFunction(Prototype)} call, ought to be revised
+ * if porting this module into Drools and generalizing beyond usage in ansible-integration.
+ */
+ public static final Prototype IGNORED = PrototypeDSL.prototype("IGNORED");
+ protected final ExtractorNode extractorNode;
+ protected final String computedFieldName;
+ protected final Collection computedImpactedFields;
+
+ public ExtractorPrototypeExpression(ExtractorNode extractorNode) {
+ this.extractorNode = extractorNode;
+ this.computedFieldName = ExtractorUtils.getParts(extractorNode).stream().collect(Collectors.joining());
+ this.computedImpactedFields = Collections.singletonList(ExtractorUtils.getParts(extractorNode).get(0));
+ }
+
+ @Override
+ public Function1 asFunction(Prototype prototype) {
+ return pf -> {
+ Map asMap = ((Fact) pf).asMap();
+ Object value = ExtractorUtils.getValueFrom(extractorNode, asMap);
+ return value;
+ };
+ }
+
+ @Override
+ public Optional getIndexingKey() {
+ return Optional.of(this.computedFieldName);
+ }
+
+ @Override
+ public String toString() {
+ return "ExtractorPrototypeExpression{" + extractorNode + "}";
+ }
+
+ @Override
+ public Collection getImpactedFields() {
+ return this.computedImpactedFields;
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpressionUtils.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpressionUtils.java
new file mode 100644
index 00000000..51aa23f0
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpressionUtils.java
@@ -0,0 +1,33 @@
+package org.drools.ansible.rulebook.integration.protoextractor.prototype;
+
+import org.drools.ansible.rulebook.integration.protoextractor.ExtractorParser;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+import org.drools.model.PrototypeExpression;
+import org.drools.model.PrototypeVariable;
+
+public class ExtractorPrototypeExpressionUtils {
+ private ExtractorPrototypeExpressionUtils() {
+ // only static methods.
+ }
+
+ public static ExtractorPrototypeExpression prototypeFieldExtractor(String expression) {
+ ExtractorNode parse = ExtractorParser.parse(expression);
+ return new ExtractorPrototypeExpression(parse);
+ }
+
+ /**
+ * old way of: `prototypeField(rightField.substring(dotPos+1))`
+ * given: m_0.a.b.c
+ * returns an extractor for: a.b.c
+ */
+ public static ExtractorPrototypeExpression prototypeFieldExtractorSkippingFirst(String expression) {
+ ExtractorNode parse = ExtractorParser.parse(expression);
+ ExtractorNode parseSkipFirst = parse.cloneSkipFirst();
+ return new ExtractorPrototypeExpression(parseSkipFirst);
+ }
+
+ public static PrototypeExpression prototypeFieldExtractor(PrototypeVariable betaVariable, String expression) {
+ ExtractorNode parse = ExtractorParser.parse(expression);
+ return new CompleteExtractorPrototypeExpression(betaVariable, parse);
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/NormalizedFieldRepresentationVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/NormalizedFieldRepresentationVisitor.java
new file mode 100644
index 00000000..d0558b9b
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/NormalizedFieldRepresentationVisitor.java
@@ -0,0 +1,46 @@
+package org.drools.ansible.rulebook.integration.protoextractor.prototype;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.IdentifierNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.IndexAccessorNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.SquaredAccessorNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.Visitor;
+
+public class NormalizedFieldRepresentationVisitor implements Visitor> {
+ List result = new ArrayList<>();
+
+ @Override
+ public List visit(ASTNode n) {
+ throw new UnsupportedOperationException("Unimplemented method 'visit'");
+ }
+
+ @Override
+ public List visit(IdentifierNode n) {
+ result.add(n.getValue());
+ return result;
+ }
+
+ @Override
+ public List visit(SquaredAccessorNode n) {
+ result.add(n.getValue());
+ return result;
+ }
+
+ @Override
+ public List visit(IndexAccessorNode n) {
+ result.add("("+n.getValue()+")");
+ return result;
+ }
+
+ @Override
+ public List visit(ExtractorNode n) {
+ for (ASTNode chunk : n.getValues()) {
+ chunk.accept(this);
+ }
+ return result;
+ }
+}
diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java
new file mode 100644
index 00000000..598e3de5
--- /dev/null
+++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java
@@ -0,0 +1,79 @@
+package org.drools.ansible.rulebook.integration.protoextractor.prototype;
+
+import java.util.List;
+import java.util.Map;
+
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.DefaultedVisitor;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.IdentifierNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.IndexAccessorNode;
+import org.drools.ansible.rulebook.integration.protoextractor.ast.SquaredAccessorNode;
+import org.drools.model.Prototype;
+
+public class ValueExtractionVisitor extends DefaultedVisitor