B
This commit is contained in:
@@ -5,6 +5,7 @@ import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
|||||||
import click.kamil.springstatemachineexporter.analysis.model.LinkResolution;
|
import click.kamil.springstatemachineexporter.analysis.model.LinkResolution;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.MatchedTransition;
|
import click.kamil.springstatemachineexporter.analysis.model.MatchedTransition;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
||||||
import click.kamil.springstatemachineexporter.analysis.service.ExternalTriggerPolicy;
|
import click.kamil.springstatemachineexporter.analysis.service.ExternalTriggerPolicy;
|
||||||
@@ -141,7 +142,7 @@ public final class CallChainLinkPolicy {
|
|||||||
if (event != null && event.startsWith("ENUM_SET:")) {
|
if (event != null && event.startsWith("ENUM_SET:")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return MachineEnumCanonicalizer.isDynamicTriggerExpression(event);
|
return DynamicTriggerExpressions.isDynamic(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import click.kamil.springstatemachineexporter.analysis.enricher.path.SymbolicPat
|
|||||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
||||||
import click.kamil.springstatemachineexporter.analysis.service.EventCarrierPolicy;
|
import click.kamil.springstatemachineexporter.analysis.service.EventCarrierPolicy;
|
||||||
@@ -140,7 +141,7 @@ public final class CallChainPolyNarrower {
|
|||||||
if (event == null || event.isBlank()) {
|
if (event == null || event.isBlank()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (MachineEnumCanonicalizer.isDynamicTriggerExpression(event)) {
|
if (DynamicTriggerExpressions.isDynamic(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addEnumReference(event, configuredConstants, votes, STRONG);
|
addEnumReference(event, configuredConstants, votes, STRONG);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import click.kamil.springstatemachineexporter.analysis.enricher.routing.BeanReso
|
|||||||
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine;
|
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine;
|
||||||
import click.kamil.springstatemachineexporter.analysis.enricher.routing.SharedServiceRoutingPolicy;
|
import click.kamil.springstatemachineexporter.analysis.enricher.routing.SharedServiceRoutingPolicy;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
|
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
||||||
|
|
||||||
@@ -359,7 +360,7 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
if (!messaging) {
|
if (!messaging) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!MachineEnumCanonicalizer.isDynamicTriggerExpression(trigger.getEvent())) {
|
if (!DynamicTriggerExpressions.isDynamic(trigger.getEvent())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Expander variants carry concrete payload bindings (same channel as REST pathBindings).
|
// Expander variants carry concrete payload bindings (same channel as REST pathBindings).
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
|
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions;
|
||||||
import click.kamil.springstatemachineexporter.model.Event;
|
import click.kamil.springstatemachineexporter.model.Event;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
|||||||
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
||||||
String smEvent = simplify(smEventRaw);
|
String smEvent = simplify(smEventRaw);
|
||||||
|
|
||||||
boolean isWildcard = isWildcardVariable(rawTriggerEvent);
|
boolean isWildcard = DynamicTriggerExpressions.isDynamic(rawTriggerEvent);
|
||||||
|
|
||||||
List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
|
List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
|
||||||
|
|
||||||
@@ -156,11 +157,6 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
|||||||
return smNormalized.matches(".*" + pattern2 + ".*") || triggerNormalized.matches(".*" + pattern1 + ".*");
|
return smNormalized.matches(".*" + pattern2 + ".*") || triggerNormalized.matches(".*" + pattern1 + ".*");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWildcardVariable(String eventStr) {
|
|
||||||
return click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions
|
|
||||||
.isDynamic(eventStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String simplify(String name) {
|
private String simplify(String name) {
|
||||||
if (name == null) return null;
|
if (name == null) return null;
|
||||||
if (simplifyCache.containsKey(name)) return simplifyCache.get(name);
|
if (simplifyCache.containsKey(name)) return simplifyCache.get(name);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ public final class BooleanConstraintEvaluator {
|
|||||||
if (bindings == null || bindings.isEmpty()) {
|
if (bindings == null || bindings.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Set<String> equalityVars = extractEqualityVariables(constraint);
|
Set<String> equalityVars = ConstraintClauses.equalityVariables(constraint);
|
||||||
for (String varName : equalityVars) {
|
for (String varName : equalityVars) {
|
||||||
if (!bindings.containsKey(varName)) {
|
if (!bindings.containsKey(varName)) {
|
||||||
if (ignoreUnboundEqualityVars) {
|
if (ignoreUnboundEqualityVars) {
|
||||||
@@ -354,16 +354,6 @@ public final class BooleanConstraintEvaluator {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> extractEqualityVariables(String constraint) {
|
|
||||||
Set<String> vars = new HashSet<>();
|
|
||||||
Pattern pattern = Pattern.compile("([a-zA-Z][\\w]*)\\s*==");
|
|
||||||
Matcher matcher = pattern.matcher(constraint);
|
|
||||||
while (matcher.find()) {
|
|
||||||
vars.add(matcher.group(1));
|
|
||||||
}
|
|
||||||
return vars;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String substituteVariableBindings(String expr, String varName, String boundValue) {
|
private static String substituteVariableBindings(String expr, String varName, String boundValue) {
|
||||||
if (boundValue == null || boundValue.isBlank()) {
|
if (boundValue == null || boundValue.isBlank()) {
|
||||||
return expr;
|
return expr;
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.resolver;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.service.EventCarrierPolicy;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared AND-clause splitting and primary-identifier extraction for constraint strings
|
||||||
|
* emitted by the analysis pipeline (carrier strip, binding compatibility).
|
||||||
|
*/
|
||||||
|
public final class ConstraintClauses {
|
||||||
|
|
||||||
|
private static final Pattern EQUALS_CALL = Pattern.compile(
|
||||||
|
"(?i)(?:\"[^\"]+\"|'[^']+')\\s*\\.\\s*(?:equalsIgnoreCase|equals)\\s*\\(\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\)"
|
||||||
|
+ "|"
|
||||||
|
+ "(?i)([A-Za-z_][A-Za-z0-9_]*)\\s*\\.\\s*(?:equalsIgnoreCase|equals)\\s*\\(\\s*(?:\"[^\"]+\"|'[^']+')\\s*\\)");
|
||||||
|
|
||||||
|
private static final Pattern EQ_COMPARE = Pattern.compile(
|
||||||
|
"(?i)([A-Za-z_][A-Za-z0-9_]*)\\s*(?:==|!=)\\s*(?:\"[^\"]+\"|'[^']+'|[A-Za-z_][A-Za-z0-9_.]*)"
|
||||||
|
+ "|"
|
||||||
|
+ "(?i)(?:\"[^\"]+\"|'[^']+'|[A-Za-z_][A-Za-z0-9_.]*)\\s*(?:==|!=)\\s*([A-Za-z_][A-Za-z0-9_]*)");
|
||||||
|
|
||||||
|
private ConstraintClauses() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a constraint on top-level {@code &&} (paren-aware).
|
||||||
|
*/
|
||||||
|
public static List<String> splitAndClauses(String constraint) {
|
||||||
|
List<String> clauses = new ArrayList<>();
|
||||||
|
if (constraint == null || constraint.isBlank()) {
|
||||||
|
return clauses;
|
||||||
|
}
|
||||||
|
StringBuilder current = new StringBuilder();
|
||||||
|
int depth = 0;
|
||||||
|
for (int i = 0; i < constraint.length(); i++) {
|
||||||
|
char c = constraint.charAt(i);
|
||||||
|
if (c == '(') {
|
||||||
|
depth++;
|
||||||
|
current.append(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == ')') {
|
||||||
|
depth = Math.max(0, depth - 1);
|
||||||
|
current.append(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (depth == 0
|
||||||
|
&& c == '&'
|
||||||
|
&& i + 1 < constraint.length()
|
||||||
|
&& constraint.charAt(i + 1) == '&') {
|
||||||
|
String piece = current.toString().trim();
|
||||||
|
if (!piece.isEmpty()) {
|
||||||
|
clauses.add(piece);
|
||||||
|
}
|
||||||
|
current.setLength(0);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current.append(c);
|
||||||
|
}
|
||||||
|
String tail = current.toString().trim();
|
||||||
|
if (!tail.isEmpty()) {
|
||||||
|
clauses.add(tail);
|
||||||
|
}
|
||||||
|
if (clauses.isEmpty() && !constraint.isBlank()) {
|
||||||
|
clauses.add(constraint.trim());
|
||||||
|
}
|
||||||
|
return clauses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary parameter/local identifier in a comparison or equals clause.
|
||||||
|
*/
|
||||||
|
public static String primaryIdent(String clause) {
|
||||||
|
if (clause == null || clause.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = clause.trim();
|
||||||
|
Matcher equals = EQUALS_CALL.matcher(trimmed);
|
||||||
|
if (equals.find()) {
|
||||||
|
String a = equals.group(1);
|
||||||
|
String b = equals.group(2);
|
||||||
|
return a != null ? a : b;
|
||||||
|
}
|
||||||
|
Matcher eq = EQ_COMPARE.matcher(trimmed);
|
||||||
|
if (eq.find()) {
|
||||||
|
String a = eq.group(1);
|
||||||
|
String b = eq.group(2);
|
||||||
|
String left = a != null ? a : b;
|
||||||
|
if (left != null && looksLikeSimpleIdent(left)) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union of primary identifiers across AND clauses (for binding overlap checks).
|
||||||
|
*/
|
||||||
|
public static Set<String> equalityVariables(String constraint) {
|
||||||
|
Set<String> vars = new LinkedHashSet<>();
|
||||||
|
for (String clause : splitAndClauses(constraint)) {
|
||||||
|
String ident = primaryIdent(clause);
|
||||||
|
if (ident != null) {
|
||||||
|
vars.add(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean looksLikeSimpleIdent(String value) {
|
||||||
|
if (value == null || value.isBlank() || value.contains(".")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char first = value.charAt(0);
|
||||||
|
return Character.isJavaIdentifierStart(first)
|
||||||
|
&& value.chars().allMatch(Character::isJavaIdentifierPart)
|
||||||
|
&& (Character.isLowerCase(first) || EventCarrierPolicy.isCarrierParamName(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
|||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.jdt.core.dom.*;
|
import org.eclipse.jdt.core.dom.*;
|
||||||
@@ -1237,7 +1238,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
.anyMatch(pe -> pe != null && !pe.startsWith("<SYMBOLIC:"));
|
.anyMatch(pe -> pe != null && !pe.startsWith("<SYMBOLIC:"));
|
||||||
if (hasConcretePolymorphic
|
if (hasConcretePolymorphic
|
||||||
&& (isRuntimeEnumParameter(runtimeExpr) || tp.isExternal()
|
&& (isRuntimeEnumParameter(runtimeExpr) || tp.isExternal()
|
||||||
|| MachineEnumCanonicalizer.isDynamicTriggerExpression(resolvedValue))) {
|
|| DynamicTriggerExpressions.isDynamic(resolvedValue))) {
|
||||||
polymorphicEvents.removeIf(pe -> pe != null && pe.startsWith("<SYMBOLIC:"));
|
polymorphicEvents.removeIf(pe -> pe != null && pe.startsWith("<SYMBOLIC:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.service;
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
|
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstraintClauses;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -8,8 +9,6 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Carrier-aware constraint helpers shared by call-graph filtering and transition linking.
|
* Carrier-aware constraint helpers shared by call-graph filtering and transition linking.
|
||||||
@@ -18,16 +17,6 @@ import java.util.regex.Pattern;
|
|||||||
*/
|
*/
|
||||||
public final class CarrierConstraintSupport {
|
public final class CarrierConstraintSupport {
|
||||||
|
|
||||||
private static final Pattern EQUALS_CALL = Pattern.compile(
|
|
||||||
"(?i)(?:\"[^\"]+\"|'[^']+')\\s*\\.\\s*(?:equalsIgnoreCase|equals)\\s*\\(\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\)"
|
|
||||||
+ "|"
|
|
||||||
+ "(?i)([A-Za-z_][A-Za-z0-9_]*)\\s*\\.\\s*(?:equalsIgnoreCase|equals)\\s*\\(\\s*(?:\"[^\"]+\"|'[^']+')\\s*\\)");
|
|
||||||
|
|
||||||
private static final Pattern EQ_COMPARE = Pattern.compile(
|
|
||||||
"(?i)([A-Za-z_][A-Za-z0-9_]*)\\s*(?:==|!=)\\s*(?:\"[^\"]+\"|'[^']+'|[A-Za-z_][A-Za-z0-9_.]*)"
|
|
||||||
+ "|"
|
|
||||||
+ "(?i)(?:\"[^\"]+\"|'[^']+'|[A-Za-z_][A-Za-z0-9_.]*)\\s*(?:==|!=)\\s*([A-Za-z_][A-Za-z0-9_]*)");
|
|
||||||
|
|
||||||
private CarrierConstraintSupport() {
|
private CarrierConstraintSupport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +29,8 @@ public final class CarrierConstraintSupport {
|
|||||||
return constraint;
|
return constraint;
|
||||||
}
|
}
|
||||||
List<String> kept = new ArrayList<>();
|
List<String> kept = new ArrayList<>();
|
||||||
for (String clause : splitAndClauses(constraint)) {
|
for (String clause : ConstraintClauses.splitAndClauses(constraint)) {
|
||||||
String ident = primaryIdent(clause);
|
String ident = ConstraintClauses.primaryIdent(clause);
|
||||||
if (ident != null && EventCarrierPolicy.isCarrierParamName(ident)) {
|
if (ident != null && EventCarrierPolicy.isCarrierParamName(ident)) {
|
||||||
kept.add("true");
|
kept.add("true");
|
||||||
continue;
|
continue;
|
||||||
@@ -82,8 +71,8 @@ public final class CarrierConstraintSupport {
|
|||||||
|
|
||||||
private static Set<String> carrierPrimaryIdents(String constraint) {
|
private static Set<String> carrierPrimaryIdents(String constraint) {
|
||||||
Set<String> idents = new LinkedHashSet<>();
|
Set<String> idents = new LinkedHashSet<>();
|
||||||
for (String clause : splitAndClauses(constraint)) {
|
for (String clause : ConstraintClauses.splitAndClauses(constraint)) {
|
||||||
String ident = primaryIdent(clause);
|
String ident = ConstraintClauses.primaryIdent(clause);
|
||||||
if (ident != null && EventCarrierPolicy.isCarrierParamName(ident)) {
|
if (ident != null && EventCarrierPolicy.isCarrierParamName(ident)) {
|
||||||
idents.add(ident);
|
idents.add(ident);
|
||||||
}
|
}
|
||||||
@@ -100,82 +89,6 @@ public final class CarrierConstraintSupport {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<String> splitAndClauses(String constraint) {
|
|
||||||
List<String> clauses = new ArrayList<>();
|
|
||||||
StringBuilder current = new StringBuilder();
|
|
||||||
int depth = 0;
|
|
||||||
for (int i = 0; i < constraint.length(); i++) {
|
|
||||||
char c = constraint.charAt(i);
|
|
||||||
if (c == '(') {
|
|
||||||
depth++;
|
|
||||||
current.append(c);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == ')') {
|
|
||||||
depth = Math.max(0, depth - 1);
|
|
||||||
current.append(c);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (depth == 0
|
|
||||||
&& c == '&'
|
|
||||||
&& i + 1 < constraint.length()
|
|
||||||
&& constraint.charAt(i + 1) == '&') {
|
|
||||||
String piece = current.toString().trim();
|
|
||||||
if (!piece.isEmpty()) {
|
|
||||||
clauses.add(piece);
|
|
||||||
}
|
|
||||||
current.setLength(0);
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
current.append(c);
|
|
||||||
}
|
|
||||||
String tail = current.toString().trim();
|
|
||||||
if (!tail.isEmpty()) {
|
|
||||||
clauses.add(tail);
|
|
||||||
}
|
|
||||||
if (clauses.isEmpty() && constraint != null && !constraint.isBlank()) {
|
|
||||||
clauses.add(constraint.trim());
|
|
||||||
}
|
|
||||||
return clauses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Primary parameter/local identifier in a comparison or equals clause.
|
|
||||||
*/
|
|
||||||
static String primaryIdent(String clause) {
|
|
||||||
if (clause == null || clause.isBlank()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String trimmed = clause.trim();
|
|
||||||
Matcher equals = EQUALS_CALL.matcher(trimmed);
|
|
||||||
if (equals.find()) {
|
|
||||||
String a = equals.group(1);
|
|
||||||
String b = equals.group(2);
|
|
||||||
return a != null ? a : b;
|
|
||||||
}
|
|
||||||
Matcher eq = EQ_COMPARE.matcher(trimmed);
|
|
||||||
if (eq.find()) {
|
|
||||||
String a = eq.group(1);
|
|
||||||
String b = eq.group(2);
|
|
||||||
String left = a != null ? a : b;
|
|
||||||
if (left != null && looksLikeSimpleIdent(left)) {
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean looksLikeSimpleIdent(String value) {
|
|
||||||
if (value == null || value.isBlank() || value.contains(".")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char first = value.charAt(0);
|
|
||||||
return Character.isJavaIdentifierStart(first)
|
|
||||||
&& value.chars().allMatch(Character::isJavaIdentifierPart)
|
|
||||||
&& (Character.isLowerCase(first) || EventCarrierPolicy.isCarrierParamName(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String joinAndTidy(List<String> clauses) {
|
private static String joinAndTidy(List<String> clauses) {
|
||||||
if (clauses.isEmpty()) {
|
if (clauses.isEmpty()) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package click.kamil.springstatemachineexporter.analysis.service;
|
|||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
import org.eclipse.jdt.core.dom.*;
|
import org.eclipse.jdt.core.dom.*;
|
||||||
@@ -27,7 +28,7 @@ public final class ExternalTriggerPolicy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (entryPoint != null && entryPointHasUnboundPlaceholders(entryPoint)
|
if (entryPoint != null && entryPointHasUnboundPlaceholders(entryPoint)
|
||||||
&& MachineEnumCanonicalizer.isDynamicTriggerExpression(trigger.getEvent())) {
|
&& DynamicTriggerExpressions.isDynamic(trigger.getEvent())) {
|
||||||
String paramName = resolveRestEventParameterName(entryPoint, resolvedEventParamName);
|
String paramName = resolveRestEventParameterName(entryPoint, resolvedEventParamName);
|
||||||
if (paramName != null) {
|
if (paramName != null) {
|
||||||
RestParamBinding binding = findRestParameterBinding(entryPoint, paramName, context);
|
RestParamBinding binding = findRestParameterBinding(entryPoint, paramName, context);
|
||||||
@@ -70,7 +71,7 @@ public final class ExternalTriggerPolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entryPoint.getName() != null && entryPointHasUnboundPlaceholders(entryPoint)
|
if (entryPoint.getName() != null && entryPointHasUnboundPlaceholders(entryPoint)
|
||||||
&& MachineEnumCanonicalizer.isDynamicTriggerExpression(trigger.getEvent())) {
|
&& DynamicTriggerExpressions.isDynamic(trigger.getEvent())) {
|
||||||
if (paramName != null) {
|
if (paramName != null) {
|
||||||
RestParamBinding pathBinding = findRestParameterBinding(entryPoint, paramName, context);
|
RestParamBinding pathBinding = findRestParameterBinding(entryPoint, paramName, context);
|
||||||
if (pathBinding != null && pathBinding.pathVariable() && pathBinding.enumType()) {
|
if (pathBinding != null && pathBinding.pathVariable() && pathBinding.enumType()) {
|
||||||
@@ -95,7 +96,7 @@ public final class ExternalTriggerPolicy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (MachineEnumCanonicalizer.isDynamicTriggerExpression(trigger.getEvent())
|
if (DynamicTriggerExpressions.isDynamic(trigger.getEvent())
|
||||||
&& !hasConcreteSinglePoly(trigger)) {
|
&& !hasConcreteSinglePoly(trigger)) {
|
||||||
// Unbound messaging payload feeding valueOf/switch — treat like unbound REST event param.
|
// Unbound messaging payload feeding valueOf/switch — treat like unbound REST event param.
|
||||||
return true;
|
return true;
|
||||||
@@ -119,7 +120,7 @@ public final class ExternalTriggerPolicy {
|
|||||||
}
|
}
|
||||||
if (entryPoint != null && entryPoint.getType() == EntryPoint.Type.REST
|
if (entryPoint != null && entryPoint.getType() == EntryPoint.Type.REST
|
||||||
&& !entryPointHasUnboundPlaceholders(entryPoint)
|
&& !entryPointHasUnboundPlaceholders(entryPoint)
|
||||||
&& MachineEnumCanonicalizer.isDynamicTriggerExpression(trigger.getEvent())) {
|
&& DynamicTriggerExpressions.isDynamic(trigger.getEvent())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return trigger.isExternal();
|
return trigger.isExternal();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
|||||||
import click.kamil.springstatemachineexporter.analysis.model.LifecycleTriggerMarkers;
|
import click.kamil.springstatemachineexporter.analysis.model.LifecycleTriggerMarkers;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.MatchedTransition;
|
import click.kamil.springstatemachineexporter.analysis.model.MatchedTransition;
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.DynamicTriggerExpressions;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
import click.kamil.springstatemachineexporter.analysis.resolver.MachineEnumCanonicalizer;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.StateMachineTypeResolver;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
@@ -483,7 +484,7 @@ public final class AnalysisCanonicalFormValidator {
|
|||||||
}
|
}
|
||||||
if (trigger.isExternal()
|
if (trigger.isExternal()
|
||||||
&& trigger.isAmbiguous()
|
&& trigger.isAmbiguous()
|
||||||
&& MachineEnumCanonicalizer.isDynamicTriggerExpression(event)
|
&& DynamicTriggerExpressions.isDynamic(event)
|
||||||
&& (trigger.getPolymorphicEvents() == null || trigger.getPolymorphicEvents().isEmpty())) {
|
&& (trigger.getPolymorphicEvents() == null || trigger.getPolymorphicEvents().isEmpty())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.resolver;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class ConstraintClausesTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void splitAndClausesSplitsTopLevelAnd() {
|
||||||
|
assertThat(ConstraintClauses.splitAndClauses(
|
||||||
|
"\"PAY\".equalsIgnoreCase(message) && message == \"PAY\""))
|
||||||
|
.containsExactly(
|
||||||
|
"\"PAY\".equalsIgnoreCase(message)",
|
||||||
|
"message == \"PAY\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void splitAndClausesKeepsNestedAndInsideParens() {
|
||||||
|
assertThat(ConstraintClauses.splitAndClauses(
|
||||||
|
"(a == 1 && b == 2) && c == 3"))
|
||||||
|
.containsExactly("(a == 1 && b == 2)", "c == 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void primaryIdentExtractsEqualsIgnoreCaseArgument() {
|
||||||
|
assertThat(ConstraintClauses.primaryIdent("\"PAY\".equalsIgnoreCase(message)"))
|
||||||
|
.isEqualTo("message");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void primaryIdentExtractsEqLeftOperand() {
|
||||||
|
assertThat(ConstraintClauses.primaryIdent("message == \"PAY\""))
|
||||||
|
.isEqualTo("message");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void primaryIdentExtractsReceiverOfEqualsCall() {
|
||||||
|
assertThat(ConstraintClauses.primaryIdent("machineType.equals(\"ORDER\")"))
|
||||||
|
.isEqualTo("machineType");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void equalityVariablesUnionsPrimaryIdentsAcrossClauses() {
|
||||||
|
assertThat(ConstraintClauses.equalityVariables(
|
||||||
|
"\"ORDER\".equalsIgnoreCase(machineType) && message == \"PAY\""))
|
||||||
|
.containsExactly("machineType", "message");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package click.kamil.springstatemachineexporter.analysis.service;
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstraintClauses;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -28,8 +29,8 @@ class CarrierConstraintSupportTest {
|
|||||||
void stripMixedLeavesOnlyDomainClauseMeaningfully() {
|
void stripMixedLeavesOnlyDomainClauseMeaningfully() {
|
||||||
String stripped = CarrierConstraintSupport.stripNonMachineClauses(
|
String stripped = CarrierConstraintSupport.stripNonMachineClauses(
|
||||||
"\"ORDER\".equalsIgnoreCase(machineType) && message == \"PAY\"");
|
"\"ORDER\".equalsIgnoreCase(machineType) && message == \"PAY\"");
|
||||||
assertThat(CarrierConstraintSupport.primaryIdent(
|
assertThat(ConstraintClauses.primaryIdent(
|
||||||
CarrierConstraintSupport.splitAndClauses(stripped).get(0)))
|
ConstraintClauses.splitAndClauses(stripped).get(0)))
|
||||||
.isEqualTo("machineType");
|
.isEqualTo("machineType");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,16 +54,4 @@ class CarrierConstraintSupportTest {
|
|||||||
"command == ORDER_PAY",
|
"command == ORDER_PAY",
|
||||||
Map.of("commandKey", "order.pay"))).isFalse();
|
Map.of("commandKey", "order.pay"))).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void primaryIdentExtractsEqualsIgnoreCaseArgument() {
|
|
||||||
assertThat(CarrierConstraintSupport.primaryIdent("\"PAY\".equalsIgnoreCase(message)"))
|
|
||||||
.isEqualTo("message");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void primaryIdentExtractsEqLeftOperand() {
|
|
||||||
assertThat(CarrierConstraintSupport.primaryIdent("message == \"PAY\""))
|
|
||||||
.isEqualTo("message");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user