matching egnine extraction
This commit is contained in:
@@ -14,12 +14,15 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicRoutingEngine;
|
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine;
|
||||||
import click.kamil.springstatemachineexporter.analysis.enricher.routing.RoutingEngine;
|
import click.kamil.springstatemachineexporter.analysis.enricher.routing.BeanResolutionEngine;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.enricher.matching.EventMatchingEngine;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.enricher.matching.HeuristicEventMatchingEngine;
|
||||||
|
|
||||||
public class TransitionLinkerEnricher implements AnalysisEnricher {
|
public class TransitionLinkerEnricher implements AnalysisEnricher {
|
||||||
|
|
||||||
private final RoutingEngine routingEngine = new HeuristicRoutingEngine();
|
private final BeanResolutionEngine routingEngine = new HeuristicBeanResolutionEngine();
|
||||||
|
private final EventMatchingEngine matchingEngine = new HeuristicEventMatchingEngine();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
public void enrich(AnalysisResult result, CodebaseContext context, CodebaseIntelligenceProvider intelligence) {
|
||||||
@@ -43,41 +46,10 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
List<MatchedTransition> matched = new ArrayList<>();
|
List<MatchedTransition> matched = new ArrayList<>();
|
||||||
|
|
||||||
for (Transition t : stateMachineTransitions) {
|
for (Transition t : stateMachineTransitions) {
|
||||||
if (t.getEvent() != null) {
|
if (t.getEvent() != null && matchingEngine.matches(t.getEvent(), tp)) {
|
||||||
String smEventRaw = t.getEvent().fullIdentifier() != null ? t.getEvent().fullIdentifier() : t.getEvent().rawName();
|
String smEventRaw = t.getEvent().fullIdentifier() != null ? t.getEvent().fullIdentifier() : t.getEvent().rawName();
|
||||||
String smEvent = simplify(smEventRaw);
|
// Event matches
|
||||||
|
for (State smSourceState : t.getSourceStates()) {
|
||||||
boolean isWildcard = triggerEvent.equals("event") || triggerEvent.equals("e") ||
|
|
||||||
triggerEvent.equals("msg") || triggerEvent.equals("message") ||
|
|
||||||
triggerEvent.equals("payload");
|
|
||||||
|
|
||||||
if (isWildcard) {
|
|
||||||
String targetVar = chain.getContextMachineId();
|
|
||||||
if (targetVar == null && chain.getTriggerPoint() != null) {
|
|
||||||
targetVar = chain.getTriggerPoint().getStateMachineId();
|
|
||||||
}
|
|
||||||
// We no longer hard-block wildcards without a specific routing context.
|
|
||||||
// If a project doesn't use standard SM persisters (e.g. restores state manually),
|
|
||||||
// contextMachineId will be null. We should still link the wildcard to provide SOME visibility,
|
|
||||||
// rather than completely hiding the endpoint.
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> polyEvents = tp.getPolymorphicEvents() != null ? tp.getPolymorphicEvents() : java.util.Collections.emptyList();
|
|
||||||
boolean hasPolyMatch = false;
|
|
||||||
for (String pe : polyEvents) {
|
|
||||||
String simplePe = pe;
|
|
||||||
if (pe.contains(".")) {
|
|
||||||
simplePe = pe.substring(pe.lastIndexOf('.') + 1);
|
|
||||||
}
|
|
||||||
if (simplePe.equals(smEventRaw) || simplePe.equals(smEvent)) {
|
|
||||||
hasPolyMatch = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasPolyMatch || (polyEvents.isEmpty() && (isWildcard || smEvent.equals(triggerEvent)))) {
|
|
||||||
// Event matches or is a wildcard
|
|
||||||
for (State smSourceState : t.getSourceStates()) {
|
|
||||||
String smSourceRaw = smSourceState.fullIdentifier() != null ? smSourceState.fullIdentifier() : smSourceState.rawName();
|
String smSourceRaw = smSourceState.fullIdentifier() != null ? smSourceState.fullIdentifier() : smSourceState.rawName();
|
||||||
String smSource = simplify(smSourceRaw);
|
String smSource = simplify(smSourceRaw);
|
||||||
if (triggerSource == null || triggerSource.equals(smSource)) {
|
if (triggerSource == null || triggerSource.equals(smSource)) {
|
||||||
@@ -107,7 +79,6 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!matched.isEmpty()) {
|
if (!matched.isEmpty()) {
|
||||||
CallChain newChain = chain.toBuilder().matchedTransitions(matched).build();
|
CallChain newChain = chain.toBuilder().matchedTransitions(matched).build();
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.model.Event;
|
||||||
|
|
||||||
|
public interface EventMatchingEngine {
|
||||||
|
/**
|
||||||
|
* Determines whether the given trigger point is a match for the state machine event.
|
||||||
|
*/
|
||||||
|
boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint);
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.model.Event;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) {
|
||||||
|
if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String triggerEvent = simplify(triggerPoint.getEvent());
|
||||||
|
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
||||||
|
String smEvent = simplify(smEventRaw);
|
||||||
|
|
||||||
|
boolean isWildcard = isWildcardVariable(triggerEvent);
|
||||||
|
|
||||||
|
List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
|
||||||
|
|
||||||
|
boolean hasPolyMatch = false;
|
||||||
|
for (String pe : polyEvents) {
|
||||||
|
String simplePe = pe;
|
||||||
|
if (pe.contains(".")) {
|
||||||
|
simplePe = pe.substring(pe.lastIndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
if (simplePe.equals(smEventRaw) || simplePe.equals(smEvent)) {
|
||||||
|
hasPolyMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasPolyMatch || smEvent.equals(triggerEvent) || (polyEvents.isEmpty() && isWildcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWildcardVariable(String eventStr) {
|
||||||
|
return eventStr.equals("event") || eventStr.equals("e") ||
|
||||||
|
eventStr.equals("msg") || eventStr.equals("message") ||
|
||||||
|
eventStr.equals("payload");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String simplify(String name) {
|
||||||
|
if (name == null) return null;
|
||||||
|
String simplified = name.replaceAll("(?i)(.*)(Event|Action|Transition|Command)(s)?$", "$1");
|
||||||
|
if (simplified.isEmpty()) {
|
||||||
|
simplified = name;
|
||||||
|
}
|
||||||
|
simplified = simplified.replaceAll("^.*\\.([A-Z0-9_]+)$", "$1");
|
||||||
|
return simplified;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,6 @@ package click.kamil.springstatemachineexporter.analysis.enricher.routing;
|
|||||||
|
|
||||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
|
||||||
public interface RoutingEngine {
|
public interface BeanResolutionEngine {
|
||||||
boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName);
|
boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName);
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class HeuristicRoutingEngine implements RoutingEngine {
|
public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName) {
|
public boolean isRoutedToCorrectMachine(CallChain chain, String currentMachineName) {
|
||||||
@@ -145,6 +145,36 @@ class TransitionLinkerEnricherTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldMatchBaseEventDespiteScrapedPolyEvents() {
|
||||||
|
Transition t1 = new Transition();
|
||||||
|
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
|
||||||
|
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
|
||||||
|
// State machine uses the base event generically
|
||||||
|
t1.setEvent(Event.of("BaseEvent", "BaseEvent"));
|
||||||
|
|
||||||
|
// Trigger point uses "BaseEvent", but polyEvents scraped some implementation/constants
|
||||||
|
CallChain chain = CallChain.builder()
|
||||||
|
.triggerPoint(TriggerPoint.builder()
|
||||||
|
.event("BaseEvent")
|
||||||
|
.polymorphicEvents(List.of("EVENT_A", "EVENT_B"))
|
||||||
|
.build())
|
||||||
|
.contextMachineId("testMachine")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
AnalysisResult result = AnalysisResult.builder()
|
||||||
|
.name("testMachine")
|
||||||
|
.transitions(List.of(t1))
|
||||||
|
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
enricher.enrich(result, null, null);
|
||||||
|
|
||||||
|
// It should match because smEvent matches triggerEvent directly, overriding the failed poly search
|
||||||
|
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
|
||||||
|
assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotMatchArbitraryGetXMethodWildcard() {
|
void shouldNotMatchArbitraryGetXMethodWildcard() {
|
||||||
Transition t1 = new Transition();
|
Transition t1 = new Transition();
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import java.util.Arrays;
|
|||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
@Tag("heuristic_bean_resolution")
|
@Tag("heuristic_bean_resolution")
|
||||||
public class HeuristicRoutingEngineTest {
|
public class HeuristicBeanResolutionEngineTest {
|
||||||
|
|
||||||
private final HeuristicRoutingEngine engine = new HeuristicRoutingEngine();
|
private final HeuristicBeanResolutionEngine engine = new HeuristicBeanResolutionEngine();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeepPackageDivergenceMismatch() {
|
public void testDeepPackageDivergenceMismatch() {
|
||||||
Reference in New Issue
Block a user