update tranistions 2
This commit is contained in:
@@ -29,6 +29,13 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
||||
}
|
||||
String classPe = pe.substring(0, pe.lastIndexOf('.'));
|
||||
String classSm = smEventRaw.substring(0, smEventRaw.lastIndexOf('.'));
|
||||
if (classPe.contains(".") && classSm.contains(".")) {
|
||||
String p1 = classPe.substring(0, classPe.lastIndexOf('.'));
|
||||
String p2 = classSm.substring(0, classSm.lastIndexOf('.'));
|
||||
if (!p1.equals(p2) && !p1.startsWith(p2 + ".") && !p2.startsWith(p1 + ".")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String simpleClassPe = classPe.contains(".") ? classPe.substring(classPe.lastIndexOf('.') + 1) : classPe;
|
||||
String simpleClassSm = classSm.contains(".") ? classSm.substring(classSm.lastIndexOf('.') + 1) : classSm;
|
||||
if (!simpleClassPe.equals(simpleClassSm)) {
|
||||
@@ -59,6 +66,13 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
||||
}
|
||||
String classTrig = rawTriggerEvent.substring(0, rawTriggerEvent.lastIndexOf('.'));
|
||||
String classSm = smEventRaw.substring(0, smEventRaw.lastIndexOf('.'));
|
||||
if (classTrig.contains(".") && classSm.contains(".")) {
|
||||
String p1 = classTrig.substring(0, classTrig.lastIndexOf('.'));
|
||||
String p2 = classSm.substring(0, classSm.lastIndexOf('.'));
|
||||
if (!p1.equals(p2) && !p1.startsWith(p2 + ".") && !p2.startsWith(p1 + ".")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
String simpleClassTrig = classTrig.contains(".") ? classTrig.substring(classTrig.lastIndexOf('.') + 1) : classTrig;
|
||||
String simpleClassSm = classSm.contains(".") ? classSm.substring(classSm.lastIndexOf('.') + 1) : classSm;
|
||||
if (!simpleClassTrig.equals(simpleClassSm)) {
|
||||
|
||||
@@ -20,7 +20,7 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
||||
String machineEventFqn = machineTypes[1];
|
||||
|
||||
if (triggerEventFqn != null && machineEventFqn != null) {
|
||||
if (triggerEventFqn.equals(machineEventFqn)) {
|
||||
if (eraseGenerics(triggerEventFqn).equals(eraseGenerics(machineEventFqn))) {
|
||||
return true; // Match!
|
||||
}
|
||||
if (isTypeMismatched(triggerEventFqn, machineEventFqn)) {
|
||||
@@ -28,7 +28,7 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
||||
}
|
||||
}
|
||||
if (triggerStateFqn != null && machineStateFqn != null) {
|
||||
if (triggerStateFqn.equals(machineStateFqn)) {
|
||||
if (eraseGenerics(triggerStateFqn).equals(eraseGenerics(machineStateFqn))) {
|
||||
return true; // Match!
|
||||
}
|
||||
if (isTypeMismatched(triggerStateFqn, machineStateFqn)) {
|
||||
@@ -314,6 +314,15 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
||||
return new String[]{null, null};
|
||||
}
|
||||
|
||||
private String eraseGenerics(String type) {
|
||||
if (type == null) return null;
|
||||
int idx = type.indexOf('<');
|
||||
if (idx != -1) {
|
||||
return type.substring(0, idx);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private String resolveTypeToFqn(org.eclipse.jdt.core.dom.Type type, org.eclipse.jdt.core.dom.CompilationUnit cu, CodebaseContext context) {
|
||||
if (type == null) return null;
|
||||
String simpleName;
|
||||
@@ -325,6 +334,10 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
||||
simpleName = type.toString();
|
||||
}
|
||||
|
||||
if (simpleName.contains("<")) {
|
||||
simpleName = simpleName.substring(0, simpleName.indexOf('<'));
|
||||
}
|
||||
|
||||
org.eclipse.jdt.core.dom.AbstractTypeDeclaration td = context.getAbstractTypeDeclaration(simpleName, cu);
|
||||
if (td != null) {
|
||||
return context.getFqn(td);
|
||||
@@ -349,6 +362,8 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
|
||||
|
||||
private boolean isTypeMismatched(String type1, String type2) {
|
||||
if (type1 == null || type2 == null) return false;
|
||||
type1 = eraseGenerics(type1);
|
||||
type2 = eraseGenerics(type2);
|
||||
if (type1.equals("java.lang.String") || type1.equals("String") || type1.equals("java.lang.Object") || type1.equals("Object")) return false;
|
||||
if (type2.equals("java.lang.String") || type2.equals("String") || type2.equals("java.lang.Object") || type2.equals("Object")) return false;
|
||||
return !type1.equals(type2);
|
||||
|
||||
@@ -170,6 +170,9 @@ public class ConstantResolver {
|
||||
String methodFqn = context.getFqn(td) + "." + md.getName().getIdentifier();
|
||||
|
||||
if (!visited.add(methodFqn)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Cycle detected in method evaluation for: {} (visited={})", methodFqn, visited);
|
||||
}
|
||||
return null; // Cycle detected in method evaluation
|
||||
}
|
||||
|
||||
@@ -213,6 +216,9 @@ public class ConstantResolver {
|
||||
String methodFqn = context.getFqn(td) + "." + md.getName().getIdentifier();
|
||||
|
||||
if (!visited.add(methodFqn)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Cycle detected in method body evaluation for: {} (visited={})", methodFqn, visited);
|
||||
}
|
||||
return null; // Cycle detected in method evaluation
|
||||
}
|
||||
|
||||
|
||||
@@ -959,7 +959,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
||||
if (depth > 5) return mi;
|
||||
if (mi.getExpression() != null) {
|
||||
String exprStr = mi.getExpression().toString();
|
||||
if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays") && !exprStr.equals("Objects")) {
|
||||
if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays") && !exprStr.equals("Objects") && !exprStr.equals("Mono") && !exprStr.equals("Flux")) {
|
||||
return mi;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -15,7 +15,12 @@ public class CallGraphPathFinder {
|
||||
|
||||
public List<String> findPath(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited) {
|
||||
if (start.equals(target)) return new ArrayList<>(List.of(start));
|
||||
if (!visited.add(start)) return null; // Path-scoped cycle detection
|
||||
if (!visited.add(start)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Cycle detected in call graph search: {} is already in visited path {}", start, visited);
|
||||
}
|
||||
return null; // Path-scoped cycle detection
|
||||
}
|
||||
|
||||
List<CallEdge> neighbors = graph.get(start);
|
||||
if (neighbors != null) {
|
||||
|
||||
@@ -87,7 +87,8 @@ public class ConstantExtractor {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (methodName.equals("of") || methodName.equals("ofEntries") || methodName.equals("asList") || methodName.equals("entry")) {
|
||||
if (methodName.equals("of") || methodName.equals("ofEntries") || methodName.equals("asList") || methodName.equals("entry") ||
|
||||
methodName.equals("just") || methodName.equals("withPayload") || methodName.equals("success")) {
|
||||
for (Object argObj : mi.arguments()) {
|
||||
extractConstantsFromExpression((Expression) argObj, constants);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,17 @@ public class GenericEventDetector {
|
||||
if ("sendEvent".equals(methodName) || "sendEvents".equals(methodName) ||
|
||||
"sendEventCollect".equals(methodName) || "sendEventMono".equals(methodName)) {
|
||||
processSendEvent(node, cu, triggers);
|
||||
}
|
||||
} else {
|
||||
for (Object argObj : node.arguments()) {
|
||||
if (argObj instanceof ExpressionMethodReference emr) {
|
||||
String refMethod = emr.getName().getIdentifier();
|
||||
if ("sendEvent".equals(refMethod) || "sendEvents".equals(refMethod) ||
|
||||
"sendEventCollect".equals(refMethod) || "sendEventMono".equals(refMethod)) {
|
||||
processSendEventMethodReference(node, emr, cu, triggers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processHints(node, cu, triggers);
|
||||
|
||||
@@ -46,6 +56,81 @@ public class GenericEventDetector {
|
||||
return triggers;
|
||||
}
|
||||
|
||||
private void processSendEventMethodReference(MethodInvocation node, ExpressionMethodReference emr, CompilationUnit cu, List<TriggerPoint> triggers) {
|
||||
Expression receiver = node.getExpression();
|
||||
String eventValue = extractEventFromReceiver(receiver);
|
||||
if (eventValue == null) {
|
||||
eventValue = receiver != null ? receiver.toString() : null;
|
||||
}
|
||||
|
||||
MethodDeclaration method = findEnclosingMethod(node);
|
||||
TypeDeclaration type = findEnclosingType(node);
|
||||
if (type == null) return;
|
||||
|
||||
String sourceState = extractSourceState(node);
|
||||
String[] smTypes = resolveStateMachineTypeArgumentsForExpression(emr.getExpression(), node);
|
||||
|
||||
if (eventValue != null) {
|
||||
triggers.add(TriggerPoint.builder()
|
||||
.event(eventValue)
|
||||
.sourceState(sourceState)
|
||||
.className(context.getFqn(type))
|
||||
.methodName(method != null ? method.getName().getIdentifier() : "initializer")
|
||||
.sourceFile(context.getRelativePath(context.getFqn(type)))
|
||||
.lineNumber(cu.getLineNumber(node.getStartPosition()))
|
||||
.stateTypeFqn(smTypes[0])
|
||||
.eventTypeFqn(smTypes[1])
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
private String extractEventFromReceiver(Expression receiver) {
|
||||
if (receiver == null) return null;
|
||||
if (receiver instanceof MethodInvocation mi) {
|
||||
String mName = mi.getName().getIdentifier();
|
||||
if (("just".equals(mName) || "withPayload".equals(mName) || "success".equals(mName)) && !mi.arguments().isEmpty()) {
|
||||
Expression arg = (Expression) mi.arguments().get(0);
|
||||
String resolved = constantResolver.resolve(arg, context);
|
||||
return resolved != null ? resolved : arg.toString();
|
||||
}
|
||||
if (mi.getExpression() != null) {
|
||||
return extractEventFromReceiver(mi.getExpression());
|
||||
}
|
||||
}
|
||||
if (receiver instanceof SimpleName sn) {
|
||||
return sn.getIdentifier();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] resolveStateMachineTypeArgumentsForExpression(Expression receiver, MethodInvocation node) {
|
||||
if (receiver == null) return new String[]{null, null};
|
||||
|
||||
ITypeBinding binding = receiver.resolveTypeBinding();
|
||||
if (binding != null) {
|
||||
ITypeBinding smBinding = findStateMachineSupertype(binding, new java.util.HashSet<>());
|
||||
if (smBinding != null) {
|
||||
ITypeBinding[] typeArgs = smBinding.getTypeArguments();
|
||||
if (typeArgs.length >= 2) {
|
||||
return new String[]{typeArgs[0].getQualifiedName(), typeArgs[1].getQualifiedName()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type declType = findReceiverTypeManually(receiver, node);
|
||||
if (declType instanceof ParameterizedType pt) {
|
||||
List<?> typeArgs = pt.typeArguments();
|
||||
if (typeArgs.size() >= 2) {
|
||||
CompilationUnit compilationUnit = (CompilationUnit) node.getRoot();
|
||||
String stateType = resolveTypeToFqn((Type) typeArgs.get(0), compilationUnit);
|
||||
String eventType = resolveTypeToFqn((Type) typeArgs.get(1), compilationUnit);
|
||||
return new String[]{stateType, eventType};
|
||||
}
|
||||
}
|
||||
|
||||
return new String[]{null, null};
|
||||
}
|
||||
|
||||
private void processSendEvent(MethodInvocation node, CompilationUnit cu, List<TriggerPoint> triggers) {
|
||||
List<TriggerPoint> builtTriggers = buildTriggerPoints(node, cu, null);
|
||||
if (builtTriggers != null) {
|
||||
|
||||
@@ -546,7 +546,7 @@ public class VariableTracer {
|
||||
if (depth > 5) return mi;
|
||||
if (mi.getExpression() != null) {
|
||||
String exprStr = mi.getExpression().toString();
|
||||
if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays") && !exprStr.equals("Objects")) {
|
||||
if (!exprStr.equals("Optional") && !exprStr.equals("Stream") && !exprStr.equals("List") && !exprStr.equals("Set") && !exprStr.equals("Arrays") && !exprStr.equals("Objects") && !exprStr.equals("Mono") && !exprStr.equals("Flux")) {
|
||||
return mi;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -361,6 +361,115 @@ public class AstTransitionParser {
|
||||
return mr.toString();
|
||||
}
|
||||
|
||||
// 5. Bean Reference (SimpleName, FieldAccess, etc.)
|
||||
String resolvedBeanBody = resolveBeanClassBody(expr, context);
|
||||
if (resolvedBeanBody != null) {
|
||||
return resolvedBeanBody;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String resolveBeanClassBody(Expression expr, CodebaseContext context) {
|
||||
String typeFqn = null;
|
||||
ITypeBinding binding = expr.resolveTypeBinding();
|
||||
if (binding != null) {
|
||||
typeFqn = binding.getQualifiedName();
|
||||
} else if (expr instanceof SimpleName sn) {
|
||||
// Check enclosing method first for local variables
|
||||
MethodDeclaration enclosingMethod = findEnclosingMethod(expr);
|
||||
if (enclosingMethod != null) {
|
||||
final String[] foundType = {null};
|
||||
final Expression[] foundInitializer = {null};
|
||||
enclosingMethod.accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(VariableDeclarationStatement vds) {
|
||||
for (Object fragObj : vds.fragments()) {
|
||||
if (fragObj instanceof VariableDeclarationFragment fragment) {
|
||||
if (fragment.getName().getIdentifier().equals(sn.getIdentifier())) {
|
||||
foundType[0] = vds.getType().toString();
|
||||
foundInitializer[0] = fragment.getInitializer();
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(vds);
|
||||
}
|
||||
});
|
||||
if (foundInitializer[0] != null) {
|
||||
ASTNode root = expr.getRoot();
|
||||
if (root instanceof CompilationUnit cu) {
|
||||
String initLogic = extractInternalLogic(foundInitializer[0], cu, context);
|
||||
if (initLogic != null) return initLogic;
|
||||
}
|
||||
}
|
||||
if (foundType[0] != null) {
|
||||
typeFqn = foundType[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeFqn == null) {
|
||||
TypeDeclaration enclosingClass = findEnclosingType(expr);
|
||||
if (enclosingClass != null) {
|
||||
for (FieldDeclaration fd : enclosingClass.getFields()) {
|
||||
for (Object fragObj : fd.fragments()) {
|
||||
if (fragObj instanceof VariableDeclarationFragment fragment) {
|
||||
if (fragment.getName().getIdentifier().equals(sn.getIdentifier())) {
|
||||
if (fragment.getInitializer() != null) {
|
||||
ASTNode root = expr.getRoot();
|
||||
if (root instanceof CompilationUnit cu) {
|
||||
String initLogic = extractInternalLogic(fragment.getInitializer(), cu, context);
|
||||
if (initLogic != null) return initLogic;
|
||||
}
|
||||
}
|
||||
typeFqn = fd.getType().toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeFqn != null) {
|
||||
if (typeFqn.contains("<")) {
|
||||
typeFqn = typeFqn.substring(0, typeFqn.indexOf('<'));
|
||||
}
|
||||
TypeDeclaration beanTd = context.getTypeDeclaration(typeFqn);
|
||||
if (beanTd == null) {
|
||||
ASTNode root = expr.getRoot();
|
||||
if (root instanceof CompilationUnit cu) {
|
||||
for (Object impObj : cu.imports()) {
|
||||
ImportDeclaration imp = (ImportDeclaration) impObj;
|
||||
if (!imp.isStatic() && !imp.isOnDemand()) {
|
||||
String impName = imp.getName().getFullyQualifiedName();
|
||||
if (impName.endsWith("." + typeFqn)) {
|
||||
beanTd = context.getTypeDeclaration(impName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (beanTd != null) {
|
||||
for (MethodDeclaration md : beanTd.getMethods()) {
|
||||
String name = md.getName().getIdentifier();
|
||||
if ("evaluate".equals(name) || "execute".equals(name)) {
|
||||
return md.toString();
|
||||
}
|
||||
}
|
||||
return beanTd.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MethodDeclaration findEnclosingMethod(ASTNode node) {
|
||||
ASTNode current = node;
|
||||
while (current != null) {
|
||||
if (current instanceof MethodDeclaration md) return md;
|
||||
current = current.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -356,11 +356,20 @@ public class CodebaseContext {
|
||||
|
||||
public List<String> getEnumValues(String fqnOrSimpleName) {
|
||||
if (fqnOrSimpleName == null) return null;
|
||||
List<String> values = enumValues.get(fqnOrSimpleName);
|
||||
|
||||
String cleanName = fqnOrSimpleName;
|
||||
if (cleanName.contains("<")) {
|
||||
cleanName = cleanName.substring(0, cleanName.indexOf('<'));
|
||||
}
|
||||
if (cleanName.contains("[")) {
|
||||
cleanName = cleanName.substring(0, cleanName.indexOf('['));
|
||||
}
|
||||
cleanName = cleanName.trim();
|
||||
|
||||
List<String> values = enumValues.get(cleanName);
|
||||
if (values != null) return values;
|
||||
String fqn = simpleNameToFqn.get(fqnOrSimpleName);
|
||||
String fqn = simpleNameToFqn.get(cleanName);
|
||||
if (fqn != null) return enumValues.get(fqn);
|
||||
// Also try stripping array or generic types if any (e.g. MyEnum[])
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,12 @@ public class JdtDataFlowModel implements DataFlowModel {
|
||||
|
||||
// 4. Handle Method Invocations (Static Factory methods / Transforming Getters)
|
||||
if (expr instanceof MethodInvocation mi) {
|
||||
String mName = mi.getName().getIdentifier();
|
||||
if (("just".equals(mName) || "withPayload".equals(mName) || "success".equals(mName)) && !mi.arguments().isEmpty()) {
|
||||
List<Expression> resolved = getReachingDefinitions((Expression) mi.arguments().get(0), visited, paramBindings, instanceFieldBindings, depth + 1);
|
||||
visited.remove(expr);
|
||||
return resolved;
|
||||
}
|
||||
IMethodBinding mb = mi.resolveMethodBinding();
|
||||
if (mb != null) {
|
||||
List<TargetMethod> targets = resolveTargets(mi, mb, visited, paramBindings, instanceFieldBindings, depth);
|
||||
|
||||
Reference in New Issue
Block a user