up
This commit is contained in:
@@ -120,25 +120,72 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
||||
cleanMachine = cleanMachine.substring(0, cleanMachine.length() - "CONFIG".length());
|
||||
}
|
||||
|
||||
String constraintUpper = constraint.toUpperCase();
|
||||
|
||||
String expr = constraint;
|
||||
java.util.List<String> allMachines = java.util.List.of("ORDER", "DOCUMENT", "USER");
|
||||
for (String m : allMachines) {
|
||||
if (!m.equals(cleanMachine)) {
|
||||
if (constraintUpper.contains(m)) {
|
||||
int idx = constraintUpper.indexOf(m);
|
||||
String sub = constraintUpper.substring(0, idx);
|
||||
String cleanedSub = sub.replaceAll("[\\s\"(]+$", "");
|
||||
boolean isNegated = cleanedSub.endsWith("!") || cleanedSub.endsWith("NOT") || constraintUpper.contains("!=" + m) || constraintUpper.contains("! = " + m);
|
||||
if (!isNegated) {
|
||||
if (constraintUpper.contains("\"" + m + "\"") || constraintUpper.contains("'" + m + "'") || constraintUpper.contains(m + ".")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
boolean isCurrent = m.equals(cleanMachine);
|
||||
String replacement = isCurrent ? "true" : "false";
|
||||
|
||||
String regex1 = "(?i)[\"']?" + m + "[\"']?\\s*\\.\\s*(equalsIgnoreCase|equals)\\s*\\([^)]*\\)";
|
||||
String regex2 = "(?i)[a-zA-Z0-9._]+\\s*\\.\\s*(equalsIgnoreCase|equals)\\s*\\(\\s*[\"']?" + m + "[\"']?\\s*\\)";
|
||||
String regex3 = "(?i)[a-zA-Z0-9._]+\\s*==\\s*[\"']?" + m + "[\"']?";
|
||||
String regex4 = "(?i)[a-zA-Z0-9._]+\\s*!=\\s*[\"']?" + m + "[\"']?";
|
||||
|
||||
expr = expr.replaceAll(regex1, replacement);
|
||||
expr = expr.replaceAll(regex2, replacement);
|
||||
expr = expr.replaceAll(regex3, replacement);
|
||||
expr = expr.replaceAll(regex4, isCurrent ? "false" : "true");
|
||||
}
|
||||
|
||||
try {
|
||||
return BooleanEvaluator.evaluate(expr);
|
||||
} catch (Exception e) {
|
||||
return !expr.contains("false");
|
||||
}
|
||||
}
|
||||
|
||||
private static class BooleanEvaluator {
|
||||
public static boolean evaluate(String expr) {
|
||||
expr = expr.replaceAll("\\s+", "");
|
||||
return parseExpr(expr);
|
||||
}
|
||||
|
||||
private static boolean parseExpr(String s) {
|
||||
if (s.isEmpty()) return true;
|
||||
|
||||
int parenDepth = 0;
|
||||
for (int i = s.length() - 1; i >= 0; i--) {
|
||||
char c = s.charAt(i);
|
||||
if (c == ')') parenDepth++;
|
||||
else if (c == '(') parenDepth--;
|
||||
else if (parenDepth == 0 && i > 0 && s.charAt(i) == '|' && s.charAt(i - 1) == '|') {
|
||||
return parseExpr(s.substring(0, i - 1)) || parseExpr(s.substring(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
parenDepth = 0;
|
||||
for (int i = s.length() - 1; i >= 0; i--) {
|
||||
char c = s.charAt(i);
|
||||
if (c == ')') parenDepth++;
|
||||
else if (c == '(') parenDepth--;
|
||||
else if (parenDepth == 0 && i > 0 && s.charAt(i) == '&' && s.charAt(i - 1) == '&') {
|
||||
return parseExpr(s.substring(0, i - 1)) && parseExpr(s.substring(i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (s.startsWith("!")) {
|
||||
return !parseExpr(s.substring(1));
|
||||
}
|
||||
|
||||
if (s.startsWith("(") && s.endsWith(")")) {
|
||||
return parseExpr(s.substring(1, s.length() - 1));
|
||||
}
|
||||
|
||||
if (s.equalsIgnoreCase("true")) return true;
|
||||
if (s.equalsIgnoreCase("false")) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String simplify(String name) {
|
||||
|
||||
@@ -408,4 +408,83 @@ class TransitionLinkerEnricherTest {
|
||||
assertThat(resComp.getMetadata().getCallChains().get(2).getMatchedTransitions()).isNullOrEmpty(); // Groc
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMultipleNegations() {
|
||||
Transition t1 = new Transition();
|
||||
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
|
||||
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
|
||||
t1.setEvent(Event.of("PAY", "PAY"));
|
||||
|
||||
// Case 1: Double Negation on ORDER -> !(!(ORDER)) -> Should act as POSITIVE (meaning order is required).
|
||||
// Since we are running OrderStateMachineConfiguration, it should match!
|
||||
CallChain chain1 = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.event("PAY")
|
||||
.constraint("!(!(ORDER.equals(type)))")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
AnalysisResult resultOrder = AnalysisResult.builder()
|
||||
.name("com.example.OrderStateMachineConfiguration")
|
||||
.transitions(List.of(t1))
|
||||
.metadata(CodebaseMetadata.builder().callChains(List.of(chain1)).build())
|
||||
.build();
|
||||
|
||||
enricher.enrich(resultOrder, null, null);
|
||||
assertThat(resultOrder.getMetadata().getCallChains().get(0).getMatchedTransitions()).hasSize(1);
|
||||
|
||||
// Case 2: Triple Negation on ORDER -> !(!(!(ORDER))) -> Should act as NEGATIVE (meaning order is rejected/forbidden).
|
||||
// Under OrderStateMachineConfiguration, this chain should be rejected!
|
||||
CallChain chain2 = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.event("PAY")
|
||||
.constraint("!(!(!(ORDER.equals(type))))")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
AnalysisResult resultOrder2 = AnalysisResult.builder()
|
||||
.name("com.example.OrderStateMachineConfiguration")
|
||||
.transitions(List.of(t1))
|
||||
.metadata(CodebaseMetadata.builder().callChains(List.of(chain2)).build())
|
||||
.build();
|
||||
|
||||
enricher.enrich(resultOrder2, null, null);
|
||||
assertThat(resultOrder2.getMetadata().getCallChains().get(0).getMatchedTransitions()).isNullOrEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleComplexBooleanLogic() {
|
||||
Transition t1 = new Transition();
|
||||
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
|
||||
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
|
||||
t1.setEvent(Event.of("PAY", "PAY"));
|
||||
|
||||
// Case 1: OR expression -> "ORDER".equals(type) || "DOCUMENT".equals(type)
|
||||
// Under OrderStateMachineConfiguration, it should match!
|
||||
CallChain chain1 = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.event("PAY")
|
||||
.constraint("\"ORDER\".equalsIgnoreCase(type) || \"DOCUMENT\".equalsIgnoreCase(type)")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
AnalysisResult resultOrder = AnalysisResult.builder()
|
||||
.name("com.example.OrderStateMachineConfiguration")
|
||||
.transitions(List.of(t1))
|
||||
.metadata(CodebaseMetadata.builder().callChains(List.of(chain1)).build())
|
||||
.build();
|
||||
|
||||
enricher.enrich(resultOrder, null, null);
|
||||
assertThat(resultOrder.getMetadata().getCallChains().get(0).getMatchedTransitions()).hasSize(1);
|
||||
|
||||
// Case 2: Under UserStateMachineConfiguration, it should be rejected!
|
||||
AnalysisResult resultUser = AnalysisResult.builder()
|
||||
.name("com.example.UserStateMachineConfiguration")
|
||||
.transitions(List.of(t1))
|
||||
.metadata(CodebaseMetadata.builder().callChains(List.of(chain1)).build())
|
||||
.build();
|
||||
|
||||
enricher.enrich(resultUser, null, null);
|
||||
assertThat(resultUser.getMetadata().getCallChains().get(0).getMatchedTransitions()).isNullOrEmpty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user