Skip to content

Commit

Permalink
[DROOLS-7214] non-executable-model doesn't react to bind-only Map pro… (
Browse files Browse the repository at this point in the history
apache#4771)

* [DROOLS-7214] non-executable-model doesn't react to bind-only Map property with map access operator
- Test case only

* [DROOLS-7214] non-executable-model doesn't react to bind-only Map property with map access operator
- Fix

* - test method name change
- lookAheadIgnoringSpaces to return a Character
  • Loading branch information
tkobayas committed Nov 1, 2022
1 parent e9645d7 commit 00a6264
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
import static org.drools.compiler.rule.builder.util.PatternBuilderUtil.getNormalizeDate;
import static org.drools.compiler.rule.builder.util.PatternBuilderUtil.normalizeEmptyKeyword;
import static org.drools.compiler.rule.builder.util.PatternBuilderUtil.normalizeStringOperator;
import static org.drools.util.StringUtils.doesFirstPropHaveListMapAccessor;
import static org.drools.util.StringUtils.extractFirstIdentifier;
import static org.drools.util.StringUtils.isIdentifier;

/**
Expand Down Expand Up @@ -1474,8 +1476,12 @@ protected void buildRuleBindings(RuleBuildContext context,

declr.setReadAccessor(extractor);

if (!declr.isFromXpathChunk() && typeDeclaration != null && extractor instanceof FieldNameSupplier) {
addFieldToPatternWatchlist(pattern, typeDeclaration, ((FieldNameSupplier) extractor).getFieldName());
if (!declr.isFromXpathChunk() && typeDeclaration != null) {
if (extractor instanceof FieldNameSupplier) {
addFieldToPatternWatchlist(pattern, typeDeclaration, ((FieldNameSupplier) extractor).getFieldName());
} else if (doesFirstPropHaveListMapAccessor(fieldBindingDescr.getBindingField())) {
addFieldToPatternWatchlist(pattern, typeDeclaration, extractFirstIdentifier(fieldBindingDescr.getBindingField(), 0)); // extractor is MVELObjectClassFieldReader
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1637,4 +1637,96 @@ private void testVariousCasePropFact(String modifyStatement, String... expectedR

assertThat(results).containsExactly(expectedResults);
}

@Test
public void bindOnlyPropertyReacts() {
// DROOLS-7214
final String str =
"import " + Person.class.getCanonicalName() + ";\n" +
"dialect \"mvel\"\n" +
"rule R when\n" +
" $p : Person( name == \"Mario\", $age : age )\n" +
"then\n" +
" modify($p) { age = $age + 1 };\n" +
"end\n";

KieSession ksession = getKieSession(str);

Person p = new Person("Mario", 40);
ksession.insert(p);
int fired = ksession.fireAllRules(10); // intentional loop

assertThat(fired).isEqualTo(10);
}

@Test
public void bindOnlyMapPropertyReacts() {
// DROOLS-7214
final String str =
"import " + Person.class.getCanonicalName() + ";\n" +
"dialect \"mvel\"\n" +
"rule R when\n" +
" $p : Person( name == \"Mario\", $map : itemsString )\n" +
"then\n" +
" $p.itemsString[\"B\"] = \"itemB\";\n" +
" modify($p) { itemsString = $p.itemsString };\n" +
"end\n";

KieSession ksession = getKieSession(str);

Person p = new Person("Mario", 40);
p.getItemsString().put("A", "itemA");
ksession.insert(p);
int fired = ksession.fireAllRules(10); // intentional loop

assertThat(fired).isEqualTo(10);
}

@Test
public void bindOnlyMapPropertyWithAccessOperatorReacts() {
// DROOLS-7214
final String str =
"import " + Person.class.getCanonicalName() + ";\n" +
"dialect \"mvel\"\n" +
"rule R when\n" +
" $p : Person( name == \"Mario\", $mapDataA : itemsString[\"A\"] )\n" +
"then\n" +
" $p.itemsString[\"B\"] = \"itemB\";\n" +
" modify($p) { itemsString = $p.itemsString };\n" +
"end\n";

KieSession ksession = getKieSession(str);

Person p = new Person("Mario", 40);
p.getItemsString().put("A", "itemA");
ksession.insert(p);
int fired = ksession.fireAllRules(10); // intentional loop

assertThat(fired).isEqualTo(10);
}

@Test
public void bindOnlyListPropertyWithAccessOperatorReacts() {
// DROOLS-7214
final String str =
"import " + Person.class.getCanonicalName() + ";\n" +
"import " + Address.class.getCanonicalName() + ";\n" +
"dialect \"mvel\"\n" +
"rule R when\n" +
" $p : Person( name == \"Mario\", $listData0 : addresses[0] )\n" +
"then\n" +
" $p.addresses.add(new Address(\"C\"));\n" +
" modify($p) { addresses = $p.addresses };\n" +
"end\n";

KieSession ksession = getKieSession(str);

Person p = new Person("Mario", 40);
p.getAddresses().add(new Address("A"));
p.getAddresses().add(new Address("B"));
ksession.insert(p);
int fired = ksession.fireAllRules(10); // intentional loop

assertThat(fired).isEqualTo(10);
}
}
16 changes: 3 additions & 13 deletions drools-mvel/src/main/java/org/drools/mvel/MVELConstraint.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
import static org.drools.util.StringUtils.codeAwareIndexOf;
import static org.drools.util.StringUtils.equalsIgnoreSpaces;
import static org.drools.util.StringUtils.extractFirstIdentifier;
import static org.drools.util.StringUtils.lookAheadIgnoringSpaces;
import static org.drools.util.StringUtils.skipBlanks;

public class MVELConstraint extends MutableTypeConstraint implements IndexableConstraint, AcceptsReadAccessor {
Expand Down Expand Up @@ -533,8 +534,8 @@ private int nextPropertyName(String expression, List<String> names, int cursor)
}

if (!isAccessor) {
String lookAhead = lookAheadIgnoringSpaces(expression, cursor);
boolean isMethodInvocation = lookAhead != null && lookAhead.equals("(");
Character lookAhead = lookAheadIgnoringSpaces(expression, cursor);
boolean isMethodInvocation = lookAhead != null && lookAhead.equals('(');
if (isMethodInvocation) {
return nextPropertyName(expression, names, cursor);
}
Expand All @@ -546,17 +547,6 @@ private int nextPropertyName(String expression, List<String> names, int cursor)
return skipOperator(expression, cursor);
}

private String lookAheadIgnoringSpaces(String expression, int cursor) {
while (cursor < expression.length()) {
char c = expression.charAt(cursor);
if (!Character.isWhitespace(c)) {
return "" + c;
}
cursor++;
}
return null;
}

private int skipOperator(String expression, int cursor) {
if (cursor < expression.length() && expression.charAt(cursor) == '.') {
while (cursor < expression.length() && Character.isWhitespace(expression.charAt(++cursor)));
Expand Down
18 changes: 18 additions & 0 deletions drools-util/src/main/java/org/drools/util/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1366,4 +1366,22 @@ public static boolean equalsIgnoreSpaces(String s1, String s2) {
public static String uuid() {
return "x" + UUID.randomUUID().toString().replace( '-', 'x' );
}

public static boolean doesFirstPropHaveListMapAccessor(String expression) {
StringBuilder propertyNameBuilder = new StringBuilder();
int cursor = extractFirstIdentifier(expression, propertyNameBuilder, 0);
Character nextChar = lookAheadIgnoringSpaces(expression, cursor);
return nextChar != null && nextChar.equals('[');
}

public static Character lookAheadIgnoringSpaces(String expression, int cursor) {
while (cursor < expression.length()) {
char c = expression.charAt(cursor);
if (!Character.isWhitespace(c)) {
return c;
}
cursor++;
}
return null;
}
}

0 comments on commit 00a6264

Please sign in to comment.