more fixes
This commit is contained in:
@@ -40,6 +40,21 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
|||||||
boolean hasPositiveMatch = false;
|
boolean hasPositiveMatch = false;
|
||||||
boolean hasStrongMismatch = false;
|
boolean hasStrongMismatch = false;
|
||||||
|
|
||||||
|
if (chain.getTriggerPoint() != null && chain.getTriggerPoint().getClassName() != null) {
|
||||||
|
String chainClass = chain.getTriggerPoint().getClassName();
|
||||||
|
String chainPackage = getPackageName(chainClass);
|
||||||
|
String chainSimple = getSimpleClassName(chainClass);
|
||||||
|
String chainPrefix = getFirstCamelCaseWord(chainSimple);
|
||||||
|
|
||||||
|
if (smPrefix != null && chainPrefix != null && smPrefix.equals(chainPrefix)) {
|
||||||
|
hasPositiveMatch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDomainMatch(smPackage, chainPackage, smPrefix, chainPrefix)) {
|
||||||
|
hasPositiveMatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (String method : chain.getMethodChain()) {
|
for (String method : chain.getMethodChain()) {
|
||||||
String chainClass = getClassNameOnly(method);
|
String chainClass = getClassNameOnly(method);
|
||||||
String chainPackage = getPackageName(chainClass);
|
String chainPackage = getPackageName(chainClass);
|
||||||
|
|||||||
@@ -551,6 +551,17 @@ import java.util.*;
|
|||||||
polymorphicEvents.removeIf(e -> {
|
polymorphicEvents.removeIf(e -> {
|
||||||
if (e.contains(".")) return false; // If it's a fully qualified enum/constant, keep it
|
if (e.contains(".")) return false; // If it's a fully qualified enum/constant, keep it
|
||||||
String val = e;
|
String val = e;
|
||||||
|
boolean isKnownEnumVal = false;
|
||||||
|
for (java.util.List<String> vals : context.getEnumValuesMap().values()) {
|
||||||
|
for (String v : vals) {
|
||||||
|
if (v.endsWith("." + val)) {
|
||||||
|
isKnownEnumVal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isKnownEnumVal) break;
|
||||||
|
}
|
||||||
|
if (isKnownEnumVal) return false;
|
||||||
return !val.equals(val.toUpperCase()) || val.length() <= 1;
|
return !val.equals(val.toUpperCase()) || val.length() <= 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -963,6 +974,18 @@ import java.util.*;
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> resolveClassConstantReturns(String className, click.kamil.springstatemachineexporter.ast.common.CodebaseContext context, CompilationUnit contextCu) {
|
protected List<String> resolveClassConstantReturns(String className, click.kamil.springstatemachineexporter.ast.common.CodebaseContext context, CompilationUnit contextCu) {
|
||||||
|
if (className != null && className.contains(".")) {
|
||||||
|
String prefix = className.substring(0, className.lastIndexOf('.'));
|
||||||
|
String suffix = className.substring(className.lastIndexOf('.') + 1);
|
||||||
|
TypeDeclaration prefixTd = contextCu != null ? context.getTypeDeclaration(prefix, contextCu) : context.getTypeDeclaration(prefix);
|
||||||
|
if (prefixTd == null) prefixTd = context.getTypeDeclaration(prefix);
|
||||||
|
boolean isKnownType = prefixTd != null || context.getEnumValues(prefix) != null;
|
||||||
|
if (isKnownType) {
|
||||||
|
String simplePrefix = prefix.contains(".") ? prefix.substring(prefix.lastIndexOf('.') + 1) : prefix;
|
||||||
|
return java.util.Collections.singletonList(simplePrefix + "." + suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TypeDeclaration td = contextCu != null ? context.getTypeDeclaration(className, contextCu) : context.getTypeDeclaration(className);
|
TypeDeclaration td = contextCu != null ? context.getTypeDeclaration(className, contextCu) : context.getTypeDeclaration(className);
|
||||||
if (td == null) td = context.getTypeDeclaration(className);
|
if (td == null) td = context.getTypeDeclaration(className);
|
||||||
if (td == null) return null;
|
if (td == null) return null;
|
||||||
@@ -1126,7 +1149,16 @@ import java.util.*;
|
|||||||
// then evaluates the getter body (handles switch-based enum transformations).
|
// then evaluates the getter body (handles switch-based enum transformations).
|
||||||
java.util.List<String> narrowed = resolveInlineInstantiationGetterResult(
|
java.util.List<String> narrowed = resolveInlineInstantiationGetterResult(
|
||||||
cic, methodName, context, new java.util.HashSet<>());
|
cic, methodName, context, new java.util.HashSet<>());
|
||||||
if (!narrowed.isEmpty()) {
|
if (narrowed.isEmpty()) {
|
||||||
|
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||||
|
CompilationUnit contextCu = expr.getRoot() instanceof CompilationUnit ? (CompilationUnit) expr.getRoot() : null;
|
||||||
|
TypeDeclaration typeDecl = contextCu != null ? context.getTypeDeclaration(typeName, contextCu) : context.getTypeDeclaration(typeName);
|
||||||
|
if (typeDecl == null) typeDecl = context.getTypeDeclaration(typeName);
|
||||||
|
if (typeDecl != null) {
|
||||||
|
narrowed = resolveMethodReturnConstant(context.getFqn(typeDecl), methodName, 0, new java.util.HashSet<>(), contextCu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (narrowed != null && !narrowed.isEmpty()) {
|
||||||
constants.addAll(narrowed);
|
constants.addAll(narrowed);
|
||||||
handledByLombok = true; // reuse flag to skip fallback below
|
handledByLombok = true; // reuse flag to skip fallback below
|
||||||
}
|
}
|
||||||
@@ -1480,7 +1512,9 @@ import java.util.*;
|
|||||||
|
|
||||||
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils
|
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils
|
||||||
.extractSimpleTypeName(cic.getType());
|
.extractSimpleTypeName(cic.getType());
|
||||||
org.eclipse.jdt.core.dom.TypeDeclaration td = context.getTypeDeclaration(typeName);
|
org.eclipse.jdt.core.dom.CompilationUnit contextCu = cic.getRoot() instanceof org.eclipse.jdt.core.dom.CompilationUnit ? (org.eclipse.jdt.core.dom.CompilationUnit) cic.getRoot() : null;
|
||||||
|
org.eclipse.jdt.core.dom.TypeDeclaration td = contextCu != null ? context.getTypeDeclaration(typeName, contextCu) : context.getTypeDeclaration(typeName);
|
||||||
|
if (td == null) td = context.getTypeDeclaration(typeName);
|
||||||
if (td == null) return java.util.Collections.emptyList();
|
if (td == null) return java.util.Collections.emptyList();
|
||||||
|
|
||||||
org.eclipse.jdt.core.dom.MethodDeclaration getter =
|
org.eclipse.jdt.core.dom.MethodDeclaration getter =
|
||||||
@@ -2258,6 +2292,12 @@ import java.util.*;
|
|||||||
String calledName = mi.getName().getIdentifier();
|
String calledName = mi.getName().getIdentifier();
|
||||||
String targetClass = context.getFqn(td);
|
String targetClass = context.getFqn(td);
|
||||||
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
|
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
|
||||||
|
if (mi.getExpression() == null) {
|
||||||
|
TypeDeclaration targetTd = context.resolveStaticImport(calledName, cu);
|
||||||
|
if (targetTd != null) {
|
||||||
|
targetClass = context.getFqn(targetTd);
|
||||||
|
}
|
||||||
|
}
|
||||||
results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, visited, cu));
|
results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, visited, cu));
|
||||||
} else {
|
} else {
|
||||||
String val = constantResolver.resolve(tracedRight, context);
|
String val = constantResolver.resolve(tracedRight, context);
|
||||||
@@ -2291,6 +2331,12 @@ import java.util.*;
|
|||||||
if (targetTd != null) {
|
if (targetTd != null) {
|
||||||
targetClass = context.getFqn(targetTd);
|
targetClass = context.getFqn(targetTd);
|
||||||
}
|
}
|
||||||
|
} else if (mi.getExpression() == null) {
|
||||||
|
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
|
||||||
|
TypeDeclaration targetTd = context.resolveStaticImport(calledName, cu);
|
||||||
|
if (targetTd != null) {
|
||||||
|
targetClass = context.getFqn(targetTd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
|
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
|
||||||
|
|||||||
@@ -328,6 +328,10 @@ public class CodebaseContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, List<String>> getEnumValuesMap() {
|
||||||
|
return enumValues;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getEnumValues(String fqnOrSimpleName) {
|
public List<String> getEnumValues(String fqnOrSimpleName) {
|
||||||
if (fqnOrSimpleName == null) return null;
|
if (fqnOrSimpleName == null) return null;
|
||||||
List<String> values = enumValues.get(fqnOrSimpleName);
|
List<String> values = enumValues.get(fqnOrSimpleName);
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.enricher.routing.HeuristicBeanResolutionEngine;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class EnterpriseBugsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveStaticImportAndAvoidMismatchedConstructorEnumArgs(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
import static com.example.EventUtils.assertMatchingCancelEventType;
|
||||||
|
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void handleCancel(Cancellation.Sender sender) {
|
||||||
|
service.process(new OrderCancelledEvent(sender).getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void process(CommonOrderEvent event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderCancelledEvent {
|
||||||
|
private CommonOrderEvent type;
|
||||||
|
public OrderCancelledEvent(Cancellation.Sender sender) {
|
||||||
|
this.type = assertMatchingCancelEventType(sender);
|
||||||
|
}
|
||||||
|
public CommonOrderEvent getType() { return type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class EventUtils {
|
||||||
|
public static CommonOrderEvent assertMatchingCancelEventType(Cancellation.Sender sender) {
|
||||||
|
return switch(sender) {
|
||||||
|
case registry_corp -> CommonOrderEvent.CANCELLED_BY_registry_corp;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Cancellation {
|
||||||
|
public enum Sender { registry_corp }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CommonOrderEvent { CANCELLED_BY_registry_corp }
|
||||||
|
""";
|
||||||
|
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.OrderController")
|
||||||
|
.methodName("handleCancel")
|
||||||
|
.build();
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.OrderService")
|
||||||
|
.methodName("process")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
TriggerPoint resolvedTp = chains.get(0).getTriggerPoint();
|
||||||
|
|
||||||
|
// Assert Bug 3: assertMatchingCancelEventType method is resolved via static import
|
||||||
|
// Assert Bug 2: Cancellation.Sender.registry_corp is NOT extracted as an event argument
|
||||||
|
assertThat(resolvedTp.getPolymorphicEvents())
|
||||||
|
.as("Polymorphic events found: %s", resolvedTp.getPolymorphicEvents())
|
||||||
|
.contains("CommonOrderEvent.CANCELLED_BY_registry_corp")
|
||||||
|
.doesNotContain("registry_corp", "Cancellation.Sender.registry_corp");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAcceptConnectorCallChainUsingTriggerPointDomainMatch() {
|
||||||
|
HeuristicBeanResolutionEngine engine = new HeuristicBeanResolutionEngine();
|
||||||
|
|
||||||
|
CallChain chain = CallChain.builder()
|
||||||
|
.triggerPoint(TriggerPoint.builder()
|
||||||
|
.className("project.shop.OrderService")
|
||||||
|
.build())
|
||||||
|
.methodChain(List.of("project.enterprise.connector.org.OrgOrderControllerImpl.accept"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Assert Bug 4: Connector entry points targeting shop SM trigger point are accepted
|
||||||
|
boolean isMatched = engine.isRoutedToCorrectMachine(chain, "project.shop.OrderStateMachine");
|
||||||
|
assertThat(isMatched).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveClassConstantToSimpleNameWithoutFQNDuplicates(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public enum CommonOrderEvent { ACCEPTED_BY_corp }
|
||||||
|
""";
|
||||||
|
Files.writeString(tempDir.resolve("CommonOrderEvent.java"), source);
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
List<String> resolved = engine.resolveClassConstantReturns("com.example.CommonOrderEvent.ACCEPTED_BY_corp", context, null);
|
||||||
|
|
||||||
|
// Assert Bug 6: resolves cleanly to simple name (since prefix is a known type)
|
||||||
|
assertThat(resolved).containsExactly("CommonOrderEvent.ACCEPTED_BY_corp");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user