From 549cc40938ab00d45af59b5dcca24548fb516c94 Mon Sep 17 00:00:00 2001 From: Carsten Hammer Date: Thu, 28 Nov 2024 21:57:44 +0100 Subject: [PATCH 1/4] preliminary fix --- .../corext/fix/helper/AbstractTool.java | 846 ++++++++++++++---- .../helper/ExternalResourceJUnitPlugin.java | 2 +- .../RuleExternalResourceJUnitPlugin.java | 38 +- .../quickfix/Java8/JUnitCleanupCases.java | 77 +- .../Java8/JUnitMigrationCleanUpTest.java | 2 +- 5 files changed, 731 insertions(+), 234 deletions(-) diff --git a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java index 58390af6..a27f8745 100644 --- a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java +++ b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; - import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; @@ -28,19 +27,22 @@ import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.IExtendedModifier; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.MarkerAnnotation; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; @@ -60,6 +62,7 @@ import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; +import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.AbortSearchException; import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer; import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperationWithSourceRange; @@ -72,64 +75,80 @@ */ public abstract class AbstractTool { - protected static final String ORG_JUNIT_CLASS_RULE= "org.junit.ClassRule"; - private static final String ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_ALL_CALLBACK= "org.junit.jupiter.api.extension.AfterAllCallback"; - private static final String ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_ALL_CALLBACK= "org.junit.jupiter.api.extension.BeforeAllCallback"; - private static final String AFTER_ALL_CALLBACK= "AfterAllCallback"; - private static final String BEFORE_ALL_CALLBACK= "BeforeAllCallback"; - private static final String TEST_NAME= "testName"; + private static final String ANNOTATION_REGISTER_EXTENSION= "RegisterExtension"; + private static final String ANNOTATION_EXTEND_WITH= "ExtendWith"; + protected static final String ANNOTATION_AFTER_EACH= "AfterEach"; + protected static final String ANNOTATION_BEFORE_EACH= "BeforeEach"; + protected static final String ANNOTATION_AFTER_ALL= "AfterAll"; + protected static final String ANNOTATION_BEFORE_ALL= "BeforeAll"; + protected static final String ANNOTATION_DISABLED= "Disabled"; + protected static final String ANNOTATION_TEST= "Test"; + protected static final String ANNOTATION_SELECT_CLASSES= "SelectClasses"; + protected static final String ANNOTATION_SUITE= "Suite"; + + protected static final String ASSERTIONS= "Assertions"; + + protected static final String ASSUMPTIONS= "Assumptions"; + private static final String METHOD_AFTER_EACH= "afterEach"; private static final String METHOD_BEFORE_EACH= "beforeEach"; - private static final String ORG_JUNIT_JUPITER_API_EXTENSION_EXTENSION_CONTEXT= "org.junit.jupiter.api.extension.ExtensionContext"; + private static final String METHOD_AFTER_ALL= "afterAll"; + private static final String METHOD_BEFORE_ALL= "beforeAll"; + protected static final String METHOD_AFTER= "after"; + protected static final String METHOD_BEFORE= "before"; + protected static final String ORG_JUNIT_AFTER= "org.junit.After"; + protected static final String ORG_JUNIT_BEFORE= "org.junit.Before"; + protected static final String ORG_JUNIT_AFTERCLASS= "org.junit.AfterClass"; + protected static final String ORG_JUNIT_BEFORECLASS= "org.junit.BeforeClass"; + + private static final String AFTER_ALL_CALLBACK= "AfterAllCallback"; + private static final String BEFORE_ALL_CALLBACK= "BeforeAllCallback"; private static final String AFTER_EACH_CALLBACK= "AfterEachCallback"; private static final String BEFORE_EACH_CALLBACK= "BeforeEachCallback"; - private static final String ANNOTATION_REGISTER_EXTENSION= "RegisterExtension"; - protected static final String ORG_JUNIT_RULE= "org.junit.Rule"; + protected static final String ORG_JUNIT_JUPITER_API_AFTER_EACH= "org.junit.jupiter.api.AfterEach"; + protected static final String ORG_JUNIT_JUPITER_API_AFTER_ALL= "org.junit.jupiter.api.AfterAll"; + protected static final String ORG_JUNIT_JUPITER_API_BEFORE_ALL= "org.junit.jupiter.api.BeforeAll"; + protected static final String ORG_JUNIT_JUPITER_API_BEFORE_EACH= "org.junit.jupiter.api.BeforeEach"; + + private static final String ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_ALL_CALLBACK= "org.junit.jupiter.api.extension.AfterAllCallback"; + private static final String ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_ALL_CALLBACK= "org.junit.jupiter.api.extension.BeforeAllCallback"; private static final String ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK= "org.junit.jupiter.api.extension.AfterEachCallback"; private static final String ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK= "org.junit.jupiter.api.extension.BeforeEachCallback"; private static final String ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION= "org.junit.jupiter.api.extension.RegisterExtension"; - private static final String ANNOTATION_EXTEND_WITH= "ExtendWith"; - protected static final String METHOD_AFTER= "after"; - protected static final String METHOD_BEFORE= "before"; + private static final String ORG_JUNIT_JUPITER_API_EXTENSION_EXTENSION_CONTEXT= "org.junit.jupiter.api.extension.ExtensionContext"; + + private static final String TEST_NAME= "testName"; + + protected static final String ORG_JUNIT_RULE= "org.junit.Rule"; + protected static final String ORG_JUNIT_CLASS_RULE= "org.junit.ClassRule"; + protected static final String ORG_JUNIT_RULES_TEMPORARY_FOLDER= "org.junit.rules.TemporaryFolder"; + protected static final String ORG_JUNIT_RULES_TEST_NAME= "org.junit.rules.TestName"; + private static final String VARIABLE_NAME_CONTEXT= "context"; private static final String EXTENSION_CONTEXT= "ExtensionContext"; protected static final String ORG_JUNIT_RULES_EXTERNAL_RESOURCE= "org.junit.rules.ExternalResource"; protected static final String ORG_JUNIT_JUPITER_API_EXTENSION_EXTEND_WITH= "org.junit.jupiter.api.extension.ExtendWith"; - protected static final String ORG_JUNIT_AFTER= "org.junit.After"; - protected static final String ORG_JUNIT_JUPITER_API_AFTER_EACH= "org.junit.jupiter.api.AfterEach"; - protected static final String ANNOTATION_AFTER_EACH= "AfterEach"; - protected static final String ORG_JUNIT_BEFORE= "org.junit.Before"; - protected static final String ANNOTATION_BEFORE_EACH= "BeforeEach"; - protected static final String ORG_JUNIT_AFTERCLASS= "org.junit.AfterClass"; - protected static final String ORG_JUNIT_JUPITER_API_AFTER_ALL= "org.junit.jupiter.api.AfterAll"; - protected static final String ANNOTATION_AFTER_ALL= "AfterAll"; - protected static final String ASSERTIONS= "Assertions"; + protected static final String ORG_JUNIT_JUPITER_API_ASSERTIONS= "org.junit.jupiter.api.Assertions"; protected static final String ORG_JUNIT_ASSERT= "org.junit.Assert"; - protected static final String ORG_JUNIT_BEFORECLASS= "org.junit.BeforeClass"; - protected static final String ORG_JUNIT_JUPITER_API_BEFORE_ALL= "org.junit.jupiter.api.BeforeAll"; - protected static final String ANNOTATION_BEFORE_ALL= "BeforeAll"; protected static final String ORG_JUNIT_IGNORE= "org.junit.Ignore"; protected static final String ORG_JUNIT_JUPITER_DISABLED= "org.junit.jupiter.api.Disabled"; - protected static final String ANNOTATION_DISABLED= "Disabled"; + protected static final String ORG_JUNIT_JUPITER_API_IO_TEMP_DIR= "org.junit.jupiter.api.io.TempDir"; - protected static final String ORG_JUNIT_RULES_TEMPORARY_FOLDER= "org.junit.rules.TemporaryFolder"; protected static final String ORG_JUNIT_JUPITER_API_TEST_INFO= "org.junit.jupiter.api.TestInfo"; - protected static final String ORG_JUNIT_RULES_TEST_NAME= "org.junit.rules.TestName"; - protected static final String ORG_JUNIT_JUPITER_API_BEFORE_EACH= "org.junit.jupiter.api.BeforeEach"; + protected static final String ORG_JUNIT_PLATFORM_SUITE_API_SELECT_CLASSES= "org.junit.platform.suite.api.SelectClasses"; - protected static final String ANNOTATION_SELECT_CLASSES= "SelectClasses"; + protected static final String ORG_JUNIT_RUNWITH= "org.junit.runner.RunWith"; protected static final String ORG_JUNIT_JUPITER_SUITE= "org.junit.platform.suite.api.Suite"; - protected static final String ANNOTATION_SUITE= "Suite"; + protected static final String ORG_JUNIT_SUITE= "org.junit.runners.Suite"; protected static final String ORG_JUNIT_SUITE_SUITECLASSES= "org.junit.runners.Suite.SuiteClasses"; protected static final String ORG_JUNIT_TEST= "org.junit.Test"; protected static final String ORG_JUNIT_JUPITER_TEST= "org.junit.jupiter.api.Test"; - protected static final String ANNOTATION_TEST= "Test"; + protected static final String ORG_JUNIT_JUPITER_API_ASSUMPTIONS= "org.junit.jupiter.api.Assumptions"; protected static final String ORG_JUNIT_ASSUME= "org.junit.Assume"; - protected static final String ASSUMPTIONS= "Assumptions"; public static Collection getUsedVariableNames(ASTNode node) { CompilationUnit root= (CompilationUnit) node.getRoot(); @@ -231,58 +250,91 @@ private TypeDeclaration findTypeDeclarationInProject(ITypeBinding typeBinding) { return type != null ? findTypeDeclaration(type.getJavaProject(), type.getElementName()) : null; } - private void processLifecycleMethod(MethodDeclaration method, ASTRewrite rewriter, AST ast, TextEditGroup group, - ImportRewrite importRewriter, boolean isBefore) { - String oldName= isBefore ? METHOD_BEFORE : METHOD_AFTER; - String newName= isBefore ? METHOD_BEFORE_EACH : METHOD_AFTER_EACH; - - setPublicVisibilityIfProtected(method, rewriter, ast, group); - adaptSuperBeforeCalls(oldName, newName, method, rewriter, ast, group); - - if (isBefore) { - removeThrowsThrowable(method, rewriter, group); + protected void modifyExternalResourceClass(TypeDeclaration node, FieldDeclaration field, boolean fieldStatic, + ASTRewrite rewriter, AST ast, TextEditGroup group, ImportRewrite importRewriter) { + if (!shouldProcessNode(node)) { + return; } - rewriter.replace(method.getName(), ast.newSimpleName(newName), group); - ensureExtensionContextParameter(method, rewriter, ast, group, importRewriter); - } - - private void processMethodInvocation(MethodInvocation node, String fieldName, ASTRewrite rewriter, AST ast, - TextEditGroup group) { - if (node.getExpression() instanceof SimpleName - && ((SimpleName) node.getExpression()).getIdentifier().equals(fieldName)) { - String methodName= node.getName().getIdentifier(); - if (METHOD_BEFORE.equals(methodName)) { - rewriter.replace(node.getName(), ast.newSimpleName(METHOD_BEFORE_EACH), group); - addContextArgumentIfMissing(node, rewriter, ast, group); - } else if (METHOD_AFTER.equals(methodName)) { - rewriter.replace(node.getName(), ast.newSimpleName(METHOD_AFTER_EACH), group); + String beforecallback; + String aftercallback; + String importbeforecallback; + String importaftercallback; + if (fieldStatic) { + beforecallback= BEFORE_ALL_CALLBACK; + aftercallback= AFTER_ALL_CALLBACK; + importbeforecallback= ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_ALL_CALLBACK; + importaftercallback= ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_ALL_CALLBACK; + } else { + beforecallback= BEFORE_EACH_CALLBACK; + aftercallback= AFTER_EACH_CALLBACK; + importbeforecallback= ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK; + importaftercallback= ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK; + } + if (field != null) { + if (isAnnotatedWithRule(field, ORG_JUNIT_RULE) + && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { + removeRuleAnnotation(field, rewriter, group, importRewriter, ORG_JUNIT_RULE); + addRegisterExtensionAnnotation(field, rewriter, ast, importRewriter, group); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); + ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() + .getType(); + adaptExternalResourceHierarchy(fieldType, rewriter, ast, importRewriter, group); + } else if (isAnnotatedWithRule(field, ORG_JUNIT_CLASS_RULE) + && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { + removeRuleAnnotation(field, rewriter, group, importRewriter, ORG_JUNIT_CLASS_RULE); + addRegisterExtensionAnnotation(field, rewriter, ast, importRewriter, group); + ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() + .getType(); + adaptExternalResourceHierarchy(fieldType, rewriter, ast, importRewriter, group); } } - } + if (isDirectlyExtendingExternalResource(node.resolveBinding())) { + refactorToImplementCallbacks(node, rewriter, ast, group, importRewriter, beforecallback, aftercallback, + importbeforecallback, importaftercallback); + } - private void adaptBeforeAfterCallsInTestClass(TypeDeclaration testClass, String fieldName, ASTRewrite rewriter, - AST ast, TextEditGroup group) { - testClass.accept(new ASTVisitor() { - @Override - public boolean visit(MethodInvocation node) { - processMethodInvocation(node, fieldName, rewriter, ast, group); - return super.visit(node); - } - }); + updateLifecycleMethodsInClass(node, rewriter, ast, group, importRewriter, METHOD_BEFORE, METHOD_AFTER, + fieldStatic ? METHOD_BEFORE_ALL : METHOD_BEFORE_EACH, + fieldStatic ? METHOD_AFTER_ALL : METHOD_AFTER_EACH); } - protected void modifyExternalResourceClass(TypeDeclaration node, ASTRewrite rewriter, AST ast, TextEditGroup group, - ImportRewrite importRewriter) { - if (!shouldProcessNode(node)) { - return; - } + protected ASTNode getTypeDefinitionForField(FieldDeclaration fieldDeclaration, CompilationUnit cu) { + for (Object fragmentObj : fieldDeclaration.fragments()) { + if (fragmentObj instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment= (VariableDeclarationFragment) fragmentObj; - if (isDirectlyExtendingExternalResource(node.resolveBinding())) { - refactorToImplementCallbacks(node, rewriter, ast, group, importRewriter); + // Initialisierer prüfen + Expression initializer= fragment.getInitializer(); + if (initializer instanceof ClassInstanceCreation) { + ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) initializer; + + // Anonyme Klasse prüfen + AnonymousClassDeclaration anonymousClass= classInstanceCreation.getAnonymousClassDeclaration(); + if (anonymousClass != null) { + return anonymousClass; // Anonyme Klasse gefunden + } + + // Typbindung prüfen + ITypeBinding typeBinding= classInstanceCreation.resolveTypeBinding(); + if (typeBinding != null) { + return findTypeDeclarationInCompilationUnit(typeBinding, cu); // Typdefinition suchen + } + } + + // Typ des Feldes prüfen, wenn keine Initialisierung vorhanden ist + IVariableBinding fieldBinding= fragment.resolveBinding(); + if (fieldBinding != null) { + ITypeBinding fieldTypeBinding= fieldBinding.getType(); + if (fieldTypeBinding != null) { + return findTypeDeclarationInCompilationUnit(fieldTypeBinding, cu); // Typdefinition suchen + } + } + } } - updateLifecycleMethodsInClass(node, rewriter, ast, group, importRewriter); + // Keine passende Typdefinition gefunden + return null; } private boolean shouldProcessNode(TypeDeclaration node) { @@ -300,101 +352,383 @@ private void processMethod(MethodDeclaration method, ASTRewrite rewriter, AST as } private void updateLifecycleMethodsInClass(TypeDeclaration node, ASTRewrite rewriter, AST ast, TextEditGroup group, - ImportRewrite importRewriter) { + ImportRewrite importRewriter, String methodbefore, String methodafter, String methodbeforeeach, + String methodaftereach) { for (MethodDeclaration method : node.getMethods()) { - if (isLifecycleMethod(method, METHOD_BEFORE)) { - processMethod(method, rewriter, ast, group, importRewriter, METHOD_BEFORE, METHOD_BEFORE_EACH); - } else if (isLifecycleMethod(method, METHOD_AFTER)) { - processMethod(method, rewriter, ast, group, importRewriter, METHOD_AFTER,METHOD_AFTER_EACH); + if (isLifecycleMethod(method, methodbefore)) { + processMethod(method, rewriter, ast, group, importRewriter, methodbefore, methodbeforeeach); + } else if (isLifecycleMethod(method, methodafter)) { + processMethod(method, rewriter, ast, group, importRewriter, methodafter, methodaftereach); } + } } + + private void adaptTypeDeclaration( + TypeDeclaration typeDecl, + ASTRewrite rewrite, + AST ast, + ImportRewrite importRewrite, + TextEditGroup group + ) { + removeSuperclassType(typeDecl, rewrite, group); + updateLifecycleMethodsInClass(typeDecl, rewrite, ast, group, importRewrite, METHOD_BEFORE, METHOD_AFTER, + METHOD_BEFORE_EACH, METHOD_AFTER_EACH); + importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); + importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + } - private void updateLifecycleMethods(TypeDeclaration typeDecl, ASTRewrite rewrite, AST ast, TextEditGroup group, - ImportRewrite importRewrite) { - for (MethodDeclaration method : typeDecl.getMethods()) { - if (isLifecycleMethod(method, METHOD_BEFORE) || isLifecycleMethod(method, METHOD_AFTER)) { - processLifecycleMethod(method, rewrite, ast, group, importRewrite, - METHOD_BEFORE.equals(method.getName().getIdentifier())); - } - } + private boolean isFieldStatic(FieldDeclaration field) { + return field.modifiers().stream().filter(Modifier.class::isInstance) // Nur Modifier berücksichtigen + .map(Modifier.class::cast) // Sicherer Cast zu Modifier + .anyMatch(modifier -> ((Modifier) modifier).isStatic()); // Überprüfen, ob Modifier static ist } - private void adaptTypeDeclaration(TypeDeclaration typeDecl, ASTRewrite rewrite, AST ast, - ImportRewrite importRewrite, TextEditGroup group) { - removeSuperclassType(typeDecl, rewrite, group); - addBeforeAndAfterEachCallbacks(typeDecl, rewrite, ast, importRewrite, group); - updateLifecycleMethods(typeDecl, rewrite, ast, group, importRewrite); - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + protected boolean isFieldAnnotatedWith(FieldDeclaration field, String annotationClass) { + return field.modifiers().stream().filter(modifier -> modifier instanceof Annotation) // Nur Annotationen + // berücksichtigen + .map(annotation -> (Annotation) annotation) // Sicherer Cast zu Annotation + .anyMatch(annotation -> { + ITypeBinding annotationBinding= ((Expression) annotation).resolveTypeBinding(); + return annotationBinding != null && annotationClass.equals(annotationBinding.getQualifiedName()); + }); } - private boolean isUsedAsClassRule(TypeDeclaration node, String annotationclass) { - ITypeBinding typeBinding= node.resolveBinding(); - if (typeBinding == null) { + private boolean isUsedAsClassRule(TypeDeclaration node, String annotationClass) { + TypeDeclaration targetClass= isTopLevelType(node) ? getOuterTypeDeclaration(node) : node; + + if (targetClass == null) { return false; } - CompilationUnit cu= (CompilationUnit) node.getRoot(); - if (cu == null) { + ITypeBinding typeBinding= node.resolveBinding(); // Das Binding der aktuellen (inneren) Klasse + if (typeBinding == null) { return false; } - final boolean[] isClassRule= { false }; - cu.accept(new ASTVisitor() { - @Override - public boolean visit(FieldDeclaration fieldDeclaration) { - // Prüfe, ob das Feld mit @ClassRule annotiert ist - boolean hasClassRuleAnnotation= fieldDeclaration.modifiers().stream() - .filter(modifier -> modifier instanceof Annotation) // Sicherstellen, dass es sich um eine - // Annotation handelt - .map(modifier -> (Annotation) modifier) // Cast zu Annotation - .anyMatch(annotation -> { - String annotationBinding= ((Annotation) annotation).getTypeName().getFullyQualifiedName(); - - return annotationBinding != null && annotationclass.equals(annotationBinding); - }); - - // Prüfe, ob das Feld vom Typ der aktuellen Klasse ist - if (hasClassRuleAnnotation) { - Type fieldType= fieldDeclaration.getType(); - if (fieldType.resolveBinding() != null && fieldType.resolveBinding().isEqualTo(typeBinding)) { - isClassRule[0]= true; - } + FieldDeclaration[] fields= targetClass.getFields(); // Felder der äußeren Klasse durchsuchen + for (FieldDeclaration field : fields) { + // Prüfe, ob das Feld mit @ClassRule annotiert ist + boolean hasClassRuleAnnotation= field.modifiers().stream() + .filter(modifier -> modifier instanceof Annotation) // Sicherstellen, dass es sich um eine + // Annotation handelt + .map(modifier -> (Annotation) modifier) // Cast zu Annotation + .anyMatch(modifier -> { + if (modifier instanceof Annotation) { + Annotation annotation= (Annotation) modifier; + ITypeBinding binding= annotation.resolveTypeBinding(); + return binding != null && annotationClass.equals(binding.getQualifiedName()); + } + return false; + }); + + // Prüfe, ob das Feld vom Typ der aktuellen Klasse oder eines Subtyps davon ist + if (hasClassRuleAnnotation) { + Type fieldType= field.getType(); + ITypeBinding fieldBinding= fieldType.resolveBinding(); + if (fieldBinding != null && isSubtypeOf(typeBinding, fieldBinding)) { + return true; } - return super.visit(fieldDeclaration); } - }); + } + return false; + } + + private boolean isSubtypeOf(ITypeBinding subtype, ITypeBinding supertype) { + if (subtype == null || supertype == null) { + return false; + } + + // Vergleiche qualifizierte Namen, um gleiche Typen zu erkennen + if (subtype.getQualifiedName().equals(supertype.getQualifiedName())) { + return true; + } + + // Durchlaufe die Vererbungshierarchie + ITypeBinding current= subtype.getSuperclass(); + while (current != null) { + if (current.getQualifiedName().equals(supertype.getQualifiedName())) { + return true; + } + current= current.getSuperclass(); + } + + // Prüfe Interfaces + return implementsInterface(subtype, supertype); + } + + private boolean implementsInterface(ITypeBinding subtype, ITypeBinding supertype) { + for (ITypeBinding iface : subtype.getInterfaces()) { + if (iface.getQualifiedName().equals(supertype.getQualifiedName()) + || implementsInterface(iface, supertype)) { + return true; + } + } + return false; + } + + private boolean isTopLevelType(TypeDeclaration node) { + return node.getParent().getParent() instanceof CompilationUnit; + } - return isClassRule[0]; + // Hilfsmethode: Gehe zur äußeren Klasse + private TypeDeclaration getOuterTypeDeclaration(ASTNode node) { + while (!(node.getParent() instanceof CompilationUnit)) { + node= node.getParent(); + } + return (TypeDeclaration) node; } private void refactorToImplementCallbacks(TypeDeclaration node, ASTRewrite rewriter, AST ast, TextEditGroup group, - ImportRewrite importRewriter) { + ImportRewrite importRewriter, String beforecallback, String aftercallback, String importbeforecallback, + String importaftercallback) { // Entferne die Superklasse ExternalResource rewriter.remove(node.getSuperclassType(), group); importRewriter.removeImport(ORG_JUNIT_RULES_EXTERNAL_RESOURCE); // Prüfe, ob die Klasse statisch verwendet wird - boolean isStaticUsage= isUsedAsClassRule(node, ORG_JUNIT_CLASS_RULE); +// boolean isStaticUsage= isUsedAsClassRule(node, ORG_JUNIT_CLASS_RULE); ListRewrite listRewrite= rewriter.getListRewrite(node, TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY); // Füge die entsprechenden Callback-Interfaces hinzu - if (isStaticUsage) { - // Verwende BeforeAllCallback und AfterAllCallback für statische Ressourcen - addInterfaceCallback(listRewrite, ast, BEFORE_ALL_CALLBACK, group, importRewriter, - ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_ALL_CALLBACK); - addInterfaceCallback(listRewrite, ast, AFTER_ALL_CALLBACK, group, importRewriter, - ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_ALL_CALLBACK); - } else { - // Verwende BeforeEachCallback und AfterEachCallback für nicht-statische - // Ressourcen - addInterfaceCallback(listRewrite, ast, BEFORE_EACH_CALLBACK, group, importRewriter, - ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); - addInterfaceCallback(listRewrite, ast, AFTER_EACH_CALLBACK, group, importRewriter, - ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + // Verwende BeforeAllCallback und AfterAllCallback für statische Ressourcen + // Verwende BeforeEachCallback und AfterEachCallback für nicht-statische + addInterfaceCallback(listRewrite, ast, beforecallback, group, importRewriter, importbeforecallback); + addInterfaceCallback(listRewrite, ast, aftercallback, group, importRewriter, importaftercallback); + } + + private boolean isAnnotatedWithRule(ClassInstanceCreation creation, String annotationQualifiedName) { + ASTNode parent= creation.getParent(); + if (parent instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment= (VariableDeclarationFragment) parent; + FieldDeclaration field= (FieldDeclaration) fragment.getParent(); + return isAnnotatedWithRule(field, annotationQualifiedName); } + return false; + } + + private void removeRuleAnnotation(ClassInstanceCreation creation, ASTRewrite rewriter, TextEditGroup group, + ImportRewrite importRewriter, String annotationQualifiedName) { + ASTNode parent= creation.getParent(); + if (parent instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment= (VariableDeclarationFragment) parent; + FieldDeclaration field= (FieldDeclaration) fragment.getParent(); + removeRuleAnnotation(field, rewriter, group, importRewriter, annotationQualifiedName); + } + } + + private void removeExternalResourceSuperclass(ClassInstanceCreation anonymousClass, ASTRewrite rewrite, + ImportRewrite importRewriter, TextEditGroup group) { + // Prüfen, ob die anonyme Klasse von ExternalResource erbt + ITypeBinding typeBinding = anonymousClass.resolveTypeBinding(); + if (typeBinding.getSuperclass() != null && + ORG_JUNIT_RULES_EXTERNAL_RESOURCE.equals(typeBinding.getSuperclass().getQualifiedName())) { + + // Entfernen Sie die Superklasse durch Ersetzen des Typs im ClassInstanceCreation + Type type = anonymousClass.getType(); + if (type != null) { + rewrite.replace(type, anonymousClass.getAST().newSimpleType(anonymousClass.getAST().newSimpleName("Object")), group); + } + + // Entfernen Sie den Import der Superklasse + importRewriter.removeImport(ORG_JUNIT_RULES_EXTERNAL_RESOURCE); + } + } + + + + protected void refactorAnonymousClassToImplementCallbacks(AnonymousClassDeclaration anonymousClass, + boolean fieldStatic, ASTRewrite rewriter, AST ast, TextEditGroup group, ImportRewrite importRewriter) { + + if (anonymousClass == null) { + return; + } + + // Zugriff auf die umgebende ClassInstanceCreation + ASTNode parent = anonymousClass.getParent(); + if (parent instanceof ClassInstanceCreation) { + ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation) parent; + + // Entferne die ExternalResource-Superklasse + removeExternalResourceSuperclass(classInstanceCreation, rewriter, importRewriter, group); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_EXTENSION_CONTEXT); + // Generiere eine neue verschachtelte Klasse + String nestedClassName = generateUniqueNestedClassName(anonymousClass); + TypeDeclaration nestedClass = createNestedClassFromAnonymous( + anonymousClass, nestedClassName, fieldStatic, rewriter, ast, importRewriter, group); + + // Ersetze die ursprüngliche Felddeklaration + replaceFieldWithExtensionDeclaration( + classInstanceCreation, nestedClassName, fieldStatic, rewriter, ast, group, importRewriter); + } + } + + private void replaceFieldWithExtensionDeclaration(ClassInstanceCreation classInstanceCreation, String nestedClassName, + boolean fieldStatic, ASTRewrite rewriter, AST ast, TextEditGroup group, ImportRewrite importRewriter) { + + FieldDeclaration fieldDecl = (FieldDeclaration) ASTNodes.getParent(classInstanceCreation, FieldDeclaration.class); + if (fieldDecl != null) { + // Entferne die @Rule-Annotation + removeRuleAnnotation(fieldDecl, rewriter, group, importRewriter, ORG_JUNIT_RULE); + + // Füge die @RegisterExtension-Annotation hinzu + addRegisterExtensionAnnotation(fieldDecl, rewriter, ast, importRewriter, group); + + // Ändere den Typ der FieldDeclaration + Type newType = ast.newSimpleType(ast.newName(nestedClassName)); + rewriter.set(fieldDecl.getType(), SimpleType.NAME_PROPERTY, newType, group); + + // Füge die Initialisierung hinzu + for (Object fragment : fieldDecl.fragments()) { + if (fragment instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragmentNode = (VariableDeclarationFragment) fragment; + ClassInstanceCreation newInstance = ast.newClassInstanceCreation(); + newInstance.setType(ast.newSimpleType(ast.newName(nestedClassName))); + rewriter.set(fragmentNode, VariableDeclarationFragment.INITIALIZER_PROPERTY, newInstance, group); + } + } + } + } + + + private TypeDeclaration createNestedClassFromAnonymous(AnonymousClassDeclaration anonymousClass, + String className, boolean fieldStatic, ASTRewrite rewriter, AST ast, + ImportRewrite importRewriter, TextEditGroup group) { + + // Erstelle die neue TypeDeclaration + TypeDeclaration nestedClass = ast.newTypeDeclaration(); + nestedClass.setName(ast.newSimpleName(className)); + if (fieldStatic) { + nestedClass.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD)); + } + + // Füge die Schnittstellen hinzu + nestedClass.superInterfaceTypes().add(ast.newSimpleType( + ast.newName(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK))); + nestedClass.superInterfaceTypes().add(ast.newSimpleType( + ast.newName(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK))); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + + // Übertrage den Body der anonymen Klasse in die neue Klasse + ListRewrite bodyRewrite = rewriter.getListRewrite( + nestedClass, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); + for (Object decl : anonymousClass.bodyDeclarations()) { + if (decl instanceof MethodDeclaration) { + MethodDeclaration method = (MethodDeclaration) decl; + + // Konvertiere before() -> beforeEach() und after() -> afterEach() + if (isLifecycleMethod(method, METHOD_BEFORE)) { + MethodDeclaration beforeEachMethod = createLifecycleCallbackMethod( + ast, "beforeEach", "ExtensionContext", method.getBody(), group); + bodyRewrite.insertLast(beforeEachMethod, group); + } else if (isLifecycleMethod(method, METHOD_AFTER)) { + MethodDeclaration afterEachMethod = createLifecycleCallbackMethod( + ast, "afterEach", "ExtensionContext", method.getBody(), group); + bodyRewrite.insertLast(afterEachMethod, group); + } + } + } + + // Füge die neue Klasse zur äußeren Klasse hinzu + ASTNode parentType = findEnclosingTypeDeclaration(anonymousClass); + if (parentType instanceof TypeDeclaration) { + ListRewrite enclosingBodyRewrite = rewriter.getListRewrite( + parentType, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); + enclosingBodyRewrite.insertLast(nestedClass, group); + } + + return nestedClass; + } + + private MethodDeclaration createLifecycleCallbackMethod(AST ast, String methodName, + String paramType, Block oldBody, TextEditGroup group) { + + MethodDeclaration method = ast.newMethodDeclaration(); + method.setName(ast.newSimpleName(methodName)); + method.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); + method.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); + + // Füge den ExtensionContext-Parameter hinzu + SingleVariableDeclaration param = ast.newSingleVariableDeclaration(); + param.setType(ast.newSimpleType(ast.newName(paramType))); + param.setName(ast.newSimpleName("context")); + method.parameters().add(param); + + // Kopiere den Body der alten Methode + if (oldBody != null) { + Block newBody = (Block) ASTNode.copySubtree(ast, oldBody); + method.setBody(newBody); + } + + return method; + } + + + + + + + + + + private String generateUniqueNestedClassName(AnonymousClassDeclaration anonymousClass) { + // Generiere einen eindeutigen Namen für die verschachtelte Klasse + return "GeneratedExtension" + System.nanoTime(); + } + + private void addLifecycleCallbackMethod(TypeDeclaration typeDecl, AST ast, TextEditGroup group, + String methodName, String callbackType) { + MethodDeclaration method = ast.newMethodDeclaration(); + method.setName(ast.newSimpleName(methodName)); + method.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); + method.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); + + // Parameter für den ExtensionContext hinzufügen + SingleVariableDeclaration param = ast.newSingleVariableDeclaration(); + param.setType(ast.newSimpleType(ast.newName("ExtensionContext"))); + param.setName(ast.newSimpleName("context")); + method.parameters().add(param); + + // Methode zum TypeDeclaration hinzufügen + Block body = ast.newBlock(); + method.setBody(body); + + typeDecl.bodyDeclarations().add(method); + } + + private ASTNode findEnclosingTypeDeclaration(ASTNode node) { + while (node != null && !(node instanceof TypeDeclaration)) { + node = node.getParent(); + } + return node; + } + + private void addCallbackMethod(ListRewrite bodyRewrite, AST ast, TextEditGroup group, String methodName, + String callbackInterface, ImportRewrite importRewriter, String callbackInterfaceImport) { + // Import hinzufügen + importRewriter.addImport(callbackInterfaceImport); + + MethodDeclaration method= ast.newMethodDeclaration(); + // Annotation hinzufügen, falls erforderlich (z. B. @Override) + MarkerAnnotation overrideAnnotation= ast.newMarkerAnnotation(); + overrideAnnotation.setTypeName(ast.newSimpleName("Override")); + method.modifiers().add(overrideAnnotation); + // Neue Methode erstellen + + method.setName(ast.newSimpleName(methodName)); + method.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); + method.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); + + // Methode ohne Inhalt + Block methodBody= ast.newBlock(); + method.setBody(methodBody); + + // Methode hinzufügen + bodyRewrite.insertLast(method, group); } private void addBeforeAndAfterEachCallbacks(TypeDeclaration typeDecl, ASTRewrite rewrite, AST ast, @@ -472,27 +806,73 @@ private void addInterfaceCallback(ListRewrite listRewrite, AST ast, String callb importRewriter.addImport(classtoimport); } - private void addRegisterExtensionAnnotation(FieldDeclaration field, ASTRewrite rewrite, AST ast, - ImportRewrite importRewrite, TextEditGroup group) { - // Prüfen, ob die Annotation bereits existiert - boolean hasRegisterExtension= field.modifiers().stream().anyMatch(modifier -> modifier instanceof Annotation - && ((Annotation) modifier).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); - - // Prüfen, ob die Annotation bereits im Rewrite hinzugefügt wurde - ListRewrite listRewrite= rewrite.getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY); - boolean hasPendingRegisterExtension= listRewrite.getRewrittenList().stream() - .anyMatch(rewritten -> rewritten instanceof MarkerAnnotation && ((MarkerAnnotation) rewritten) - .getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); - - if (!hasRegisterExtension && !hasPendingRegisterExtension) { - // Annotation hinzufügen, wenn sie weder im AST noch im Rewrite existiert - MarkerAnnotation registerExtensionAnnotation= ast.newMarkerAnnotation(); - registerExtensionAnnotation.setTypeName(ast.newName(ANNOTATION_REGISTER_EXTENSION)); - listRewrite.insertFirst(registerExtensionAnnotation, group); + private Annotation createRegisterExtensionAnnotation(AST ast, ImportRewrite importRewriter) { + MarkerAnnotation annotation= ast.newMarkerAnnotation(); + annotation.setTypeName(ast.newSimpleName("RegisterExtension")); + importRewriter.addImport("org.junit.jupiter.api.extension.RegisterExtension"); + return annotation; + } - // Import hinzufügen - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); - } + private void addRegisterExtensionAnnotation( + ASTNode node, + ASTRewrite rewrite, + AST ast, + ImportRewrite importRewrite, + TextEditGroup group + ) { + if (node instanceof FieldDeclaration) { + // Direkt mit FieldDeclaration arbeiten + FieldDeclaration field = (FieldDeclaration) node; + + // Prüfen, ob die Annotation bereits existiert + boolean hasRegisterExtension = field.modifiers().stream() + .anyMatch(modifier -> modifier instanceof Annotation + && ((Annotation) modifier).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + // Prüfen, ob die Annotation bereits im Rewrite hinzugefügt wurde + ListRewrite listRewrite = rewrite.getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY); + boolean hasPendingRegisterExtension = listRewrite.getRewrittenList().stream() + .anyMatch(rewritten -> rewritten instanceof MarkerAnnotation + && ((MarkerAnnotation) rewritten).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + if (!hasRegisterExtension && !hasPendingRegisterExtension) { + // Annotation hinzufügen, wenn sie weder im AST noch im Rewrite existiert + MarkerAnnotation registerExtensionAnnotation = ast.newMarkerAnnotation(); + registerExtensionAnnotation.setTypeName(ast.newName(ANNOTATION_REGISTER_EXTENSION)); + listRewrite.insertFirst(registerExtensionAnnotation, group); + + // Import hinzufügen + importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); + } + } else if (node instanceof ClassInstanceCreation) { + // Übergeordnetes Element der anonymen Klasse finden + ASTNode parent = node.getParent(); + if (parent instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment = (VariableDeclarationFragment) parent; + FieldDeclaration field = (FieldDeclaration) fragment.getParent(); + + // Prüfen, ob die Annotation bereits existiert + boolean hasRegisterExtension = field.modifiers().stream() + .anyMatch(modifier -> modifier instanceof Annotation + && ((Annotation) modifier).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + // Prüfen, ob die Annotation bereits im Rewrite hinzugefügt wurde + ListRewrite listRewrite = rewrite.getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY); + boolean hasPendingRegisterExtension = listRewrite.getRewrittenList().stream() + .anyMatch(rewritten -> rewritten instanceof MarkerAnnotation + && ((MarkerAnnotation) rewritten).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + if (!hasRegisterExtension && !hasPendingRegisterExtension) { + // Annotation hinzufügen, wenn sie weder im AST noch im Rewrite existiert + MarkerAnnotation registerExtensionAnnotation = ast.newMarkerAnnotation(); + registerExtensionAnnotation.setTypeName(ast.newName(ANNOTATION_REGISTER_EXTENSION)); + listRewrite.insertFirst(registerExtensionAnnotation, group); + + // Import hinzufügen + importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); + } + } + } } private void ensureExtensionContextParameter(MethodDeclaration method, ASTRewrite rewrite, AST ast, @@ -636,6 +1016,99 @@ private boolean isExternalResource(FieldDeclaration field, String typetolookup) return isExternalResource(binding, typetolookup); } + protected TypeDeclaration getTypeDeclarationForField(FieldDeclaration fieldDeclaration, CompilationUnit cu) { + for (Object fragmentObj : fieldDeclaration.fragments()) { + if (fragmentObj instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment= (VariableDeclarationFragment) fragmentObj; + Expression initializer= fragment.getInitializer(); + + // Prüfen, ob der Initializer eine ClassInstanceCreation ist + if (initializer instanceof ClassInstanceCreation) { + ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) initializer; + ITypeBinding typeBinding= classInstanceCreation.resolveTypeBinding(); + + // Anonyme Klasse: Hier keine TypeDeclaration möglich + if (classInstanceCreation.getAnonymousClassDeclaration() != null) { + // Hier kannst du entweder null zurückgeben oder die anonyme Klasse speziell + // behandeln + continue; // Überspringen, da keine TypeDeclaration möglich ist + } + + // Typ binden und zugehörige TypeDeclaration in der CompilationUnit suchen + if (typeBinding != null) { + return findTypeDeclarationInCompilationUnit(typeBinding, cu); + } + } + + // Typ des Feldes auflösen, wenn keine Initialisierung vorhanden ist + if (fragment.resolveBinding() != null) { + ITypeBinding variableTypeBinding= fragment.resolveBinding().getType(); + if (variableTypeBinding != null) { + return findTypeDeclarationInCompilationUnit(variableTypeBinding, cu); + } + } + } + } + return null; // Keine passende TypeDeclaration gefunden + } + + private TypeDeclaration findTypeDeclarationInCompilationUnit(ITypeBinding typeBinding, CompilationUnit cu) { + final AbstractTypeDeclaration[] result= { null }; + + cu.accept(new ASTVisitor() { + @Override + public boolean visit(TypeDeclaration node) { + ITypeBinding binding = node.resolveBinding(); + System.out.println("Visiting TypeDeclaration: " + (binding != null ? binding.getQualifiedName() : "null")); + if (binding != null) { + System.out.println("Expected Binding: " + typeBinding.getQualifiedName()); + System.out.println("Actual Binding: " + binding.getQualifiedName()); + System.out.println("Binding match: " + binding.isEqualTo(typeBinding)); + } + if (binding != null && binding.isEqualTo(typeBinding)) { + result[0] = node; + return false; // Abbruch + } + return true; + } + + @Override + public boolean visit(EnumDeclaration node) { + if (node.resolveBinding() != null && node.resolveBinding().isEqualTo(typeBinding)) { + result[0]= node; + return false; + } + return true; + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + if (node.resolveBinding() != null && node.resolveBinding().isEqualTo(typeBinding)) { + result[0]= node; + return false; + } + return true; + } + +// @Override +// public boolean visit(AnonymousClassDeclaration node) { +// // Hinweis: Anonyme Klassen haben oft keine direkten Bindings, daher ist hier eine zusätzliche Prüfung sinnvoll +// ITypeBinding binding = node.resolveBinding(); +// if (binding != null && binding.isEqualTo(typeBinding)) { +// result[0] = node; +// return false; +// } +// return true; +// } + }); + + return (TypeDeclaration) result[0]; + } + + private boolean checkTypeBindingMatch(ITypeBinding binding, ITypeBinding targetBinding) { + return binding != null && binding.isEqualTo(targetBinding); + } + protected boolean isExternalResource(ITypeBinding typeBinding, String typetolookup) { while (typeBinding != null) { if (typetolookup.equals(typeBinding.getQualifiedName())) { @@ -655,30 +1128,32 @@ private boolean isStringType(Expression expression, Class class1) { return typeBinding != null && class1.getCanonicalName().equals(typeBinding.getQualifiedName()); } - public void migrateRuleToRegisterExtensionAndAdaptHierarchy(Optional innerTypeDeclaration, - TypeDeclaration testClass, ASTRewrite rewrite, AST ast, ImportRewrite importRewrite, TextEditGroup group, - String varname) { - if (innerTypeDeclaration.isPresent() && innerTypeDeclaration.get() instanceof TypeDeclaration) { - adaptBeforeAfterCallsInTestClass((TypeDeclaration) innerTypeDeclaration.get(), varname, rewrite, ast, - group); - } - for (FieldDeclaration field : testClass.getFields()) { - if (isAnnotatedWithRule(field, ORG_JUNIT_RULE) && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { - removeRuleAnnotation(field, rewrite, group, importRewrite, ORG_JUNIT_RULE); - addRegisterExtensionAnnotation(field, rewrite, ast, importRewrite, group); - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); - ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() - .getType(); - adaptExternalResourceHierarchy(fieldType, rewrite, ast, importRewrite, group); - } else if (isAnnotatedWithRule(field, ORG_JUNIT_CLASS_RULE) && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { - removeRuleAnnotation(field, rewrite, group, importRewrite, ORG_JUNIT_CLASS_RULE); - addRegisterExtensionAnnotation(field, rewrite, ast, importRewrite, group); - ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() - .getType(); - adaptExternalResourceHierarchy(fieldType, rewrite, ast, importRewrite, group); - } - } - } +// public void migrateRuleToRegisterExtensionAndAdaptHierarchy(Optional innerTypeDeclaration, +// TypeDeclaration testClass, ASTRewrite rewrite, AST ast, ImportRewrite importRewrite, TextEditGroup group, +// String varname) { +// if (innerTypeDeclaration.isPresent() && innerTypeDeclaration.get() instanceof TypeDeclaration) { +// adaptBeforeAfterCallsInTestClass((TypeDeclaration) innerTypeDeclaration.get(), varname, rewrite, ast, +// group); +// } +// for (FieldDeclaration field : testClass.getFields()) { +// if (isAnnotatedWithRule(field, ORG_JUNIT_RULE) +// && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { +// removeRuleAnnotation(field, rewrite, group, importRewrite, ORG_JUNIT_RULE); +// addRegisterExtensionAnnotation(field, rewrite, ast, importRewrite, group); +// importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); +// ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() +// .getType(); +// adaptExternalResourceHierarchy(fieldType, rewrite, ast, importRewrite, group); +// } else if (isAnnotatedWithRule(field, ORG_JUNIT_CLASS_RULE) +// && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { +// removeRuleAnnotation(field, rewrite, group, importRewrite, ORG_JUNIT_CLASS_RULE); +// addRegisterExtensionAnnotation(field, rewrite, ast, importRewrite, group); +// ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() +// .getType(); +// adaptExternalResourceHierarchy(fieldType, rewrite, ast, importRewrite, group); +// } +// } +// } private CompilationUnit parseCompilationUnit(ICompilationUnit iCompilationUnit) { ASTParser parser= ASTParser.newParser(AST.getJLSLatest()); @@ -696,7 +1171,8 @@ public void process(Annotation node, IJavaProject jproject, ASTRewrite rewrite, FieldDeclaration field= (FieldDeclaration) node.getParent(); ITypeBinding fieldTypeBinding= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() .getType(); - if (!isExternalResource(fieldTypeBinding, ORG_JUNIT_RULES_EXTERNAL_RESOURCE) || fieldTypeBinding.isAnonymous()) { + if (!isExternalResource(fieldTypeBinding, ORG_JUNIT_RULES_EXTERNAL_RESOURCE) + || fieldTypeBinding.isAnonymous()) { return; } if (isDirect(fieldTypeBinding)) { diff --git a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/ExternalResourceJUnitPlugin.java b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/ExternalResourceJUnitPlugin.java index e8ea84ac..88ef19b8 100644 --- a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/ExternalResourceJUnitPlugin.java +++ b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/ExternalResourceJUnitPlugin.java @@ -75,7 +75,7 @@ public void rewrite(JUnitCleanUpFixCore upp, final ReferenceHolder { TypeDeclaration node= holder.getTypeDeclaration(); - modifyExternalResourceClass(node, rewriter, ast, group, importRewriter); + modifyExternalResourceClass(node, null, false, rewriter, ast, group, importRewriter); }); } diff --git a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java index a5f05315..7a553da2 100644 --- a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java +++ b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java @@ -13,14 +13,14 @@ *******************************************************************************/ package org.sandbox.jdt.internal.corext.fix.helper; -import java.util.Optional; import java.util.Set; - import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; @@ -56,8 +56,9 @@ private boolean processFoundNode(JUnitCleanUpFixCore fixcore, VariableDeclarationFragment fragment= (VariableDeclarationFragment) node.fragments().get(0); ITypeBinding binding= fragment.resolveBinding().getType(); if ( - isAnonymousClass(fragment) - || (binding == null) +// isAnonymousClass(fragment) +// || + (binding == null) || ORG_JUNIT_RULES_TEST_NAME.equals(binding.getQualifiedName()) || ORG_JUNIT_RULES_TEMPORARY_FOLDER.equals(binding.getQualifiedName())) { return false; @@ -71,18 +72,31 @@ private boolean processFoundNode(JUnitCleanUpFixCore fixcore, @Override public void rewrite(JUnitCleanUpFixCore upp, ReferenceHolder hit, CompilationUnitRewrite cuRewrite, TextEditGroup group) { - ASTRewrite rewrite= cuRewrite.getASTRewrite(); + ASTRewrite rewriter= cuRewrite.getASTRewrite(); AST ast= cuRewrite.getRoot().getAST(); ImportRewrite importRewriter= cuRewrite.getImportRewrite(); - hit.values().forEach(mh -> { + JunitHolder mh= hit.get(hit.size()-1); + + + FieldDeclaration fieldDeclaration= mh.getFieldDeclaration(); - Optional innerTypeDeclaration= getInnerTypeDeclaration(fieldDeclaration); - for (Object fragment : fieldDeclaration.fragments()) { - VariableDeclarationFragment variable = (VariableDeclarationFragment) fragment; - String fieldName = variable.getName().getIdentifier(); - migrateRuleToRegisterExtensionAndAdaptHierarchy(innerTypeDeclaration,getParentTypeDeclaration(fieldDeclaration),rewrite, ast, importRewriter,group ,fieldName); + boolean fieldStatic= isFieldAnnotatedWith(fieldDeclaration,ORG_JUNIT_CLASS_RULE); + CompilationUnit cu = (CompilationUnit) fieldDeclaration.getRoot(); + String classNameFromField= extractClassNameFromField(fieldDeclaration); + TypeDeclaration node = getTypeDeclarationForField(fieldDeclaration, cu); + + ASTNode node2 = getTypeDefinitionForField(fieldDeclaration, cu); + + if (node2 instanceof TypeDeclaration) { + System.out.println("TypeDeclaration gefunden: " + ((TypeDeclaration) node2).getName()); + modifyExternalResourceClass((TypeDeclaration) node2,fieldDeclaration,fieldStatic, rewriter, ast, group, importRewriter); + } else if (node2 instanceof AnonymousClassDeclaration typeNode) { + System.out.println("AnonymousClassDeclaration gefunden."+ typeNode); + refactorAnonymousClassToImplementCallbacks(typeNode, fieldStatic, rewriter, ast, group, importRewriter); + } else { + System.out.println("Keine passende Typdefinition gefunden."); } - }); + hit.remove(hit.size()-1); } @Override diff --git a/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java b/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java index c827a1a1..a20bf0b6 100644 --- a/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java +++ b/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java @@ -659,28 +659,31 @@ public void test3() { """, //$NON-NLS-1$ """ package test; -import org.junit.Rule; import org.junit.jupiter.api.Test; -import org.junit.rules.ExternalResource; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; /** * */ public class MyTest { - @Rule - public ExternalResource er= new ExternalResource() { - @Override - protected void before() throws Throwable { - }; - - @Override - protected void after() { - }; - }; + @RegisterExtension + public GeneratedExtension1218384162454000 er= new GeneratedExtension1218384162454000(); @Test public void test3() { } + + class GeneratedExtension1218384162454000 implements org.junit.jupiter.api.extension.BeforeEachCallback, + org.junit.jupiter.api.extension.AfterEachCallback { + public void beforeEach(ExtensionContext context) { + } + + public void afterEach(ExtensionContext context) { + } + } } """), RuleNestedExternalResource( @@ -793,7 +796,9 @@ public void testWithMultipleResources() { package test; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.RegisterExtension; @@ -819,20 +824,10 @@ public void afterEach(ExtensionContext context) { // Anonyme Klasse als ExternalResource @RegisterExtension - public ExternalResource anonymousRule = new ExternalResource() { - @Override - protected void before() throws Throwable { - System.out.println("Anonymous rule before"); - } - - @Override - protected void after() { - System.out.println("Anonymous rule after"); - } - }; + public GeneratedExtension1218384806024400 anonymousRule = new GeneratedExtension1218384806024400(); // Statische Klasse für ClassRule - static class StaticExternalResource implements BeforeEachCallback, AfterEachCallback { + static class StaticExternalResource implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback { @Override public void beforeEach(ExtensionContext context) { System.out.println("Static resource before"); @@ -871,17 +866,7 @@ public void afterEach(ExtensionContext context) { // Zweite Regel @RegisterExtension - public ExternalResource secondRule = new ExternalResource() { - @Override - protected void before() throws Throwable { - System.out.println("Second rule before"); - } - - @Override - protected void after() { - System.out.println("Second rule after"); - } - }; + public GeneratedExtension1218384804295200 secondRule = new GeneratedExtension1218384804295200(); // Testfälle @Test @@ -893,6 +878,28 @@ public void testWithExternalResource() { public void testWithMultipleResources() { System.out.println("Test with multiple resources"); } + + class GeneratedExtension1218384804295200 implements org.junit.jupiter.api.extension.BeforeEachCallback, + org.junit.jupiter.api.extension.AfterEachCallback { + public void beforeEach(ExtensionContext context) { + System.out.println("Second rule before"); + } + + public void afterEach(ExtensionContext context) { + System.out.println("Second rule after"); + } + } + + class GeneratedExtension1218384806024400 implements org.junit.jupiter.api.extension.BeforeEachCallback, + org.junit.jupiter.api.extension.AfterEachCallback { + public void beforeEach(ExtensionContext context) { + System.out.println("Anonymous rule before"); + } + + public void afterEach(ExtensionContext context) { + System.out.println("Anonymous rule after"); + } + } } """), TestnameRule( diff --git a/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitMigrationCleanUpTest.java b/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitMigrationCleanUpTest.java index a31b47fa..9ea45c33 100644 --- a/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitMigrationCleanUpTest.java +++ b/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitMigrationCleanUpTest.java @@ -475,7 +475,7 @@ public void testJUnitCleanupSelectedCase(JUnitCleanupCases test) throws CoreExce // context4junit4.enable(MYCleanUpConstants.JUNIT_CLEANUP_4_RULETEMPORARYFOLDER); // context4junit4.enable(MYCleanUpConstants.JUNIT_CLEANUP_4_RULETESTNAME); context4junit4.enable(MYCleanUpConstants.JUNIT_CLEANUP_4_RULEEXTERNALRESOURCE); - context4junit4.enable(MYCleanUpConstants.JUNIT_CLEANUP_4_EXTERNALRESOURCE); +// context4junit4.enable(MYCleanUpConstants.JUNIT_CLEANUP_4_EXTERNALRESOURCE); // context4junit4.enable(MYCleanUpConstants.JUNIT_CLEANUP_4_RUNWITH); context4junit4.assertRefactoringResultAsExpected(new ICompilationUnit[] {cu}, new String[] {test.expected}, null); } From b725e28a6dea9631c0bcdd2562962a68159afabb Mon Sep 17 00:00:00 2001 From: Carsten Hammer Date: Sun, 1 Dec 2024 18:20:02 +0100 Subject: [PATCH 2/4] partly implementation of rule triggered refactoring --- .../corext/fix/helper/AbstractTool.java | 1031 ++++++++--------- .../RuleExternalResourceJUnitPlugin.java | 50 +- .../quickfix/Java8/JUnitCleanupCases.java | 18 +- .../Java8/JUnitMigrationCleanUpTest.java | 260 ++++- 4 files changed, 752 insertions(+), 607 deletions(-) diff --git a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java index a27f8745..3a110f25 100644 --- a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java +++ b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java @@ -13,11 +13,19 @@ *******************************************************************************/ package org.sandbox.jdt.internal.corext.fix.helper; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filebuffers.ITextFileBufferManager; +import org.eclipse.core.filebuffers.LocationKind; +import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; @@ -62,11 +70,14 @@ import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; +import org.eclipse.jdt.core.refactoring.CompilationUnitChange; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.AbortSearchException; import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer; import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperationWithSourceRange; import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; import org.sandbox.jdt.internal.corext.fix.JUnitCleanUpFixCore; @@ -155,50 +166,6 @@ public static Collection getUsedVariableNames(ASTNode node) { return new ScopeAnalyzer(root).getUsedVariableNames(node.getStartPosition(), node.getLength()); } - protected static boolean isOfType(ITypeBinding typeBinding, String typename) { - if (typeBinding == null) { - throw new AbortSearchException(); - } - if (typeBinding.isArray()) { - typeBinding= typeBinding.getElementType(); - } - return typeBinding.getQualifiedName().equals(typename); - } - - protected Optional getInnerTypeDeclaration(FieldDeclaration fieldDeclaration) { - for (Object fragment : fieldDeclaration.fragments()) { - if (fragment instanceof VariableDeclarationFragment) { - VariableDeclarationFragment variableFragment= (VariableDeclarationFragment) fragment; - - // Prüfen, ob die Initialisierung eine anonyme Klasse ist - Expression initializer= variableFragment.getInitializer(); - if (initializer instanceof ClassInstanceCreation) { - ClassInstanceCreation classInstance= (ClassInstanceCreation) initializer; - - // Anonyme Klasse gefunden - if (classInstance.getAnonymousClassDeclaration() != null) { - return Optional.of(classInstance.getAnonymousClassDeclaration()); - } - - // Falls keine anonyme Klasse, den Typ der inneren Klasse prüfen - ITypeBinding typeBinding= classInstance.getType().resolveBinding(); - if (typeBinding != null && typeBinding.isClass() && typeBinding.getJavaElement() instanceof IType) { - IType type= (IType) typeBinding.getJavaElement(); - IJavaProject javaProject= type.getJavaProject(); - String typeName= type.getElementName(); - - // Verwende nun den Projektnamen und den Typnamen - TypeDeclaration innerTypeDecl= findTypeDeclaration(javaProject, typeName); - if (innerTypeDecl != null) { - return Optional.of(innerTypeDecl); - } - } - } - } - } - return Optional.empty(); // Keine innere oder anonyme Klasse gefunden - } - private void addContextArgumentIfMissing(ASTNode node, ASTRewrite rewriter, AST ast, TextEditGroup group) { ListRewrite argsRewrite; if (node instanceof MethodInvocation) { @@ -236,18 +203,27 @@ private void setPublicVisibilityIfProtected(MethodDeclaration method, ASTRewrite private void adaptExternalResourceHierarchy(ITypeBinding typeBinding, ASTRewrite rewrite, AST ast, ImportRewrite importRewrite, TextEditGroup group) { - while (typeBinding != null && isExternalResource(typeBinding, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { - TypeDeclaration typeDecl= findTypeDeclarationInProject(typeBinding); - if (typeDecl != null) { - adaptTypeDeclaration(typeDecl, rewrite, ast, importRewrite, group); + while (typeBinding != null) { + // Abbruchbedingung: Nicht weiter heruntersteigen, wenn der aktuelle Typ + // ExternalResource ist + if (ORG_JUNIT_RULES_EXTERNAL_RESOURCE.equals(typeBinding.getQualifiedName())) { + break; + } + + if (isExternalResource(typeBinding, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { + TypeDeclaration typeDecl= findTypeDeclarationInProject(typeBinding); + if (typeDecl != null) { + adaptTypeDeclaration(typeDecl, rewrite, ast, importRewrite, group); + } } + typeBinding= typeBinding.getSuperclass(); } } private TypeDeclaration findTypeDeclarationInProject(ITypeBinding typeBinding) { IType type= (IType) typeBinding.getJavaElement(); - return type != null ? findTypeDeclaration(type.getJavaProject(), type.getElementName()) : null; + return type != null ? findTypeDeclaration(type.getJavaProject(), type.getFullyQualifiedName()) : null; } protected void modifyExternalResourceClass(TypeDeclaration node, FieldDeclaration field, boolean fieldStatic, @@ -276,7 +252,6 @@ protected void modifyExternalResourceClass(TypeDeclaration node, FieldDeclaratio && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { removeRuleAnnotation(field, rewriter, group, importRewriter, ORG_JUNIT_RULE); addRegisterExtensionAnnotation(field, rewriter, ast, importRewriter, group); - importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() .getType(); adaptExternalResourceHierarchy(fieldType, rewriter, ast, importRewriter, group); @@ -318,7 +293,11 @@ protected ASTNode getTypeDefinitionForField(FieldDeclaration fieldDeclaration, C // Typbindung prüfen ITypeBinding typeBinding= classInstanceCreation.resolveTypeBinding(); if (typeBinding != null) { - return findTypeDeclarationInCompilationUnit(typeBinding, cu); // Typdefinition suchen + TypeDeclaration typeDeclarationInCompilationUnit= findTypeDeclarationInCompilationUnit( + typeBinding, cu); + if (typeDeclarationInCompilationUnit == null) + return findTypeDeclarationInProject(typeBinding); + return typeDeclarationInCompilationUnit; // Typdefinition suchen } } @@ -327,7 +306,11 @@ protected ASTNode getTypeDefinitionForField(FieldDeclaration fieldDeclaration, C if (fieldBinding != null) { ITypeBinding fieldTypeBinding= fieldBinding.getType(); if (fieldTypeBinding != null) { - return findTypeDeclarationInCompilationUnit(fieldTypeBinding, cu); // Typdefinition suchen + TypeDeclaration typeDeclarationInCompilationUnit= findTypeDeclarationInCompilationUnit( + fieldTypeBinding, cu); + if (typeDeclarationInCompilationUnit == null) + findTypeDeclarationInProject(fieldTypeBinding); + return typeDeclarationInCompilationUnit; // Typdefinition suchen } } } @@ -351,31 +334,135 @@ private void processMethod(MethodDeclaration method, ASTRewrite rewriter, AST as ensureExtensionContextParameter(method, rewriter, ast, group, importRewriter); } - private void updateLifecycleMethodsInClass(TypeDeclaration node, ASTRewrite rewriter, AST ast, TextEditGroup group, - ImportRewrite importRewriter, String methodbefore, String methodafter, String methodbeforeeach, - String methodaftereach) { + private void updateLifecycleMethodsInClass(TypeDeclaration node, ASTRewrite globalRewrite, AST ast, + TextEditGroup group, ImportRewrite importRewrite, String methodbefore, String methodafter, + String methodbeforeeach, String methodaftereach) { + for (MethodDeclaration method : node.getMethods()) { if (isLifecycleMethod(method, methodbefore)) { - processMethod(method, rewriter, ast, group, importRewriter, methodbefore, methodbeforeeach); + AST astOfNode= node.getAST(); + + // Ermitteln der CompilationUnit + CompilationUnit compilationUnit= findCompilationUnit(node); + + // Wählen Sie den passenden ASTRewrite basierend auf dem AST des Knotens + ASTRewrite rewriteToUse= (astOfNode == ast) ? globalRewrite : ASTRewrite.create(astOfNode); + ImportRewrite importRewriteToUse= (astOfNode == ast) ? importRewrite + : ImportRewrite.create(compilationUnit, true); + processMethod(method, rewriteToUse, ast, group, importRewriteToUse, methodbefore, methodbeforeeach); + // Wenn ein neuer ASTRewrite erstellt wurde, wenden Sie die Änderungen an + if (rewriteToUse != globalRewrite) { + createChangeForRewrite(compilationUnit, rewriteToUse); + } } else if (isLifecycleMethod(method, methodafter)) { - processMethod(method, rewriter, ast, group, importRewriter, methodafter, methodaftereach); + AST astOfNode= node.getAST(); + + // Ermitteln der CompilationUnit + CompilationUnit compilationUnit= findCompilationUnit(node); + + // Wählen Sie den passenden ASTRewrite basierend auf dem AST des Knotens + ASTRewrite rewriteToUse= (astOfNode == ast) ? globalRewrite : ASTRewrite.create(astOfNode); + ImportRewrite importRewriteToUse= (astOfNode == ast) ? importRewrite + : ImportRewrite.create(compilationUnit, true); + processMethod(method, rewriteToUse, ast, group, importRewriteToUse, methodafter, methodaftereach); + // Wenn ein neuer ASTRewrite erstellt wurde, wenden Sie die Änderungen an + if (rewriteToUse != globalRewrite) { + createChangeForRewrite(compilationUnit, rewriteToUse); + } + } + } + } + + private CompilationUnit findCompilationUnit(ASTNode node) { + while (node != null && !(node instanceof CompilationUnit)) { + node= node.getParent(); + } + return (CompilationUnit) node; + } + + private IDocument getDocumentForCompilationUnit(CompilationUnit compilationUnit) { + if (compilationUnit == null || compilationUnit.getJavaElement() == null) { + throw new IllegalArgumentException("Invalid CompilationUnit or missing JavaElement."); + } + + ICompilationUnit icu= (ICompilationUnit) compilationUnit.getJavaElement(); + ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager(); + + try { + // Verbinden mit der Datei, die der CompilationUnit entspricht + bufferManager.connect(icu.getPath(), LocationKind.IFILE, null); + + // Holen des zugehörigen TextFileBuffer + ITextFileBuffer textFileBuffer= bufferManager.getTextFileBuffer(icu.getPath(), LocationKind.IFILE); + + if (textFileBuffer == null) { + throw new RuntimeException("No text file buffer found for the provided compilation unit."); } + // Rückgabe des Dokuments + return textFileBuffer.getDocument(); + } catch (CoreException e) { + throw new RuntimeException("Failed to connect to text file buffer: " + e.getMessage(), e); + } finally { + try { + // Optional: Verbindung trennen, wenn keine weiteren Änderungen anstehen + bufferManager.disconnect(icu.getPath(), LocationKind.IFILE, null); + } catch (CoreException e) { + // Trennen schlug fehl, aber wir protokollieren nur + e.printStackTrace(); + } } } - - private void adaptTypeDeclaration( - TypeDeclaration typeDecl, - ASTRewrite rewrite, - AST ast, - ImportRewrite importRewrite, - TextEditGroup group - ) { - removeSuperclassType(typeDecl, rewrite, group); - updateLifecycleMethodsInClass(typeDecl, rewrite, ast, group, importRewrite, METHOD_BEFORE, METHOD_AFTER, - METHOD_BEFORE_EACH, METHOD_AFTER_EACH); - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + + private CompilationUnitChange createChangeForRewrite(CompilationUnit compilationUnit, ASTRewrite rewrite) { + try { + // Zugriff auf das IDocument der CompilationUnit + IDocument document= getDocumentForCompilationUnit(compilationUnit); + + // Änderungen beschreiben (aber nicht anwenden) + TextEdit edits= rewrite.rewriteAST(document, null); + + // Ein TextChange-Objekt erstellen + CompilationUnitChange change= new CompilationUnitChange("JUnit Migration", + (ICompilationUnit) compilationUnit.getJavaElement()); + change.setEdit(edits); + + // Optional: Kommentare oder Markierungen hinzufügen + change.addTextEditGroup(new TextEditGroup("Migrate JUnit", edits)); + + return change; + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Error creating change for rewrite: " + e.getMessage(), e); + } + } + + private void adaptTypeDeclaration(TypeDeclaration typeDecl, ASTRewrite globalRewrite, AST ast, + ImportRewrite importRewrite, TextEditGroup group) { + AST astOfNode= typeDecl.getAST(); + + // Ermitteln der CompilationUnit + CompilationUnit compilationUnit= findCompilationUnit(typeDecl); + + // Wählen Sie den passenden ASTRewrite basierend auf dem AST des Knotens + ASTRewrite rewriteToUse= (astOfNode == ast) ? globalRewrite : ASTRewrite.create(astOfNode); + ImportRewrite importRewriteToUse= (astOfNode == ast) ? importRewrite + : ImportRewrite.create(compilationUnit, true); +// ASTRewrite rewriteToUse = globalRewrite; +// ImportRewrite importRewriteToUse = importRewrite; + + removeSuperclassType(typeDecl, rewriteToUse, group); + updateLifecycleMethodsInClass(typeDecl, rewriteToUse, ast, group, importRewriteToUse, METHOD_BEFORE, + METHOD_AFTER, METHOD_BEFORE_EACH, METHOD_AFTER_EACH); + + importRewriteToUse.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); + importRewriteToUse.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + + // Wenn ein neuer ASTRewrite erstellt wurde, wenden Sie die Änderungen an + if (rewriteToUse != globalRewrite) { + createChangeForRewrite(compilationUnit, rewriteToUse); + } } private boolean isFieldStatic(FieldDeclaration field) { @@ -394,46 +481,6 @@ protected boolean isFieldAnnotatedWith(FieldDeclaration field, String annotation }); } - private boolean isUsedAsClassRule(TypeDeclaration node, String annotationClass) { - TypeDeclaration targetClass= isTopLevelType(node) ? getOuterTypeDeclaration(node) : node; - - if (targetClass == null) { - return false; - } - - ITypeBinding typeBinding= node.resolveBinding(); // Das Binding der aktuellen (inneren) Klasse - if (typeBinding == null) { - return false; - } - - FieldDeclaration[] fields= targetClass.getFields(); // Felder der äußeren Klasse durchsuchen - for (FieldDeclaration field : fields) { - // Prüfe, ob das Feld mit @ClassRule annotiert ist - boolean hasClassRuleAnnotation= field.modifiers().stream() - .filter(modifier -> modifier instanceof Annotation) // Sicherstellen, dass es sich um eine - // Annotation handelt - .map(modifier -> (Annotation) modifier) // Cast zu Annotation - .anyMatch(modifier -> { - if (modifier instanceof Annotation) { - Annotation annotation= (Annotation) modifier; - ITypeBinding binding= annotation.resolveTypeBinding(); - return binding != null && annotationClass.equals(binding.getQualifiedName()); - } - return false; - }); - - // Prüfe, ob das Feld vom Typ der aktuellen Klasse oder eines Subtyps davon ist - if (hasClassRuleAnnotation) { - Type fieldType= field.getType(); - ITypeBinding fieldBinding= fieldType.resolveBinding(); - if (fieldBinding != null && isSubtypeOf(typeBinding, fieldBinding)) { - return true; - } - } - } - return false; - } - private boolean isSubtypeOf(ITypeBinding subtype, ITypeBinding supertype) { if (subtype == null || supertype == null) { return false; @@ -467,277 +514,239 @@ private boolean implementsInterface(ITypeBinding subtype, ITypeBinding supertype return false; } - private boolean isTopLevelType(TypeDeclaration node) { - return node.getParent().getParent() instanceof CompilationUnit; - } - - // Hilfsmethode: Gehe zur äußeren Klasse - private TypeDeclaration getOuterTypeDeclaration(ASTNode node) { - while (!(node.getParent() instanceof CompilationUnit)) { - node= node.getParent(); - } - return (TypeDeclaration) node; - } - private void refactorToImplementCallbacks(TypeDeclaration node, ASTRewrite rewriter, AST ast, TextEditGroup group, ImportRewrite importRewriter, String beforecallback, String aftercallback, String importbeforecallback, String importaftercallback) { + + AST astOfNode= node.getAST(); + + // Ermitteln der CompilationUnit + CompilationUnit compilationUnit= findCompilationUnit(node); + + // Wählen Sie den passenden ASTRewrite basierend auf dem AST des Knotens + ASTRewrite rewriteToUse= (astOfNode == ast) ? rewriter : ASTRewrite.create(astOfNode); + ImportRewrite importRewriteToUse= (astOfNode == ast) ? importRewriter + : ImportRewrite.create(compilationUnit, true); + // Entferne die Superklasse ExternalResource - rewriter.remove(node.getSuperclassType(), group); - importRewriter.removeImport(ORG_JUNIT_RULES_EXTERNAL_RESOURCE); + rewriteToUse.remove(node.getSuperclassType(), group); + importRewriteToUse.removeImport(ORG_JUNIT_RULES_EXTERNAL_RESOURCE); // Prüfe, ob die Klasse statisch verwendet wird // boolean isStaticUsage= isUsedAsClassRule(node, ORG_JUNIT_CLASS_RULE); - ListRewrite listRewrite= rewriter.getListRewrite(node, TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY); + ListRewrite listRewrite= rewriteToUse.getListRewrite(node, TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY); // Füge die entsprechenden Callback-Interfaces hinzu // Verwende BeforeAllCallback und AfterAllCallback für statische Ressourcen // Verwende BeforeEachCallback und AfterEachCallback für nicht-statische - addInterfaceCallback(listRewrite, ast, beforecallback, group, importRewriter, importbeforecallback); - addInterfaceCallback(listRewrite, ast, aftercallback, group, importRewriter, importaftercallback); + addInterfaceCallback(listRewrite, ast, beforecallback, group, importRewriteToUse, importbeforecallback); + addInterfaceCallback(listRewrite, ast, aftercallback, group, importRewriteToUse, importaftercallback); + // Wenn ein neuer ASTRewrite erstellt wurde, wenden Sie die Änderungen an + if (rewriter != rewriteToUse) { + createChangeForRewrite(compilationUnit, rewriteToUse); + } } - private boolean isAnnotatedWithRule(ClassInstanceCreation creation, String annotationQualifiedName) { - ASTNode parent= creation.getParent(); - if (parent instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment= (VariableDeclarationFragment) parent; - FieldDeclaration field= (FieldDeclaration) fragment.getParent(); - return isAnnotatedWithRule(field, annotationQualifiedName); + private void removeExternalResourceSuperclass(ClassInstanceCreation anonymousClass, ASTRewrite rewrite, + ImportRewrite importRewriter, TextEditGroup group) { + // Prüfen, ob die anonyme Klasse von ExternalResource erbt + ITypeBinding typeBinding= anonymousClass.resolveTypeBinding(); + if (typeBinding.getSuperclass() != null + && ORG_JUNIT_RULES_EXTERNAL_RESOURCE.equals(typeBinding.getSuperclass().getQualifiedName())) { + + // Entfernen Sie die Superklasse durch Ersetzen des Typs im + // ClassInstanceCreation + Type type= anonymousClass.getType(); + if (type != null) { + rewrite.replace(type, + anonymousClass.getAST().newSimpleType(anonymousClass.getAST().newSimpleName("Object")), group); + } + + // Entfernen Sie den Import der Superklasse + importRewriter.removeImport(ORG_JUNIT_RULES_EXTERNAL_RESOURCE); } - return false; } - private void removeRuleAnnotation(ClassInstanceCreation creation, ASTRewrite rewriter, TextEditGroup group, - ImportRewrite importRewriter, String annotationQualifiedName) { - ASTNode parent= creation.getParent(); - if (parent instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment= (VariableDeclarationFragment) parent; - FieldDeclaration field= (FieldDeclaration) fragment.getParent(); - removeRuleAnnotation(field, rewriter, group, importRewriter, annotationQualifiedName); + private String generateChecksum(String input) { + try { + MessageDigest md= MessageDigest.getInstance("MD5"); + byte[] hashBytes= md.digest(input.getBytes()); + StringBuilder hexString= new StringBuilder(); + for (byte b : hashBytes) { + String hex= Integer.toHexString(0xff & b); + if (hex.length() == 1) + hexString.append('0'); + hexString.append(hex); + } + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not found"); } } - private void removeExternalResourceSuperclass(ClassInstanceCreation anonymousClass, ASTRewrite rewrite, - ImportRewrite importRewriter, TextEditGroup group) { - // Prüfen, ob die anonyme Klasse von ExternalResource erbt - ITypeBinding typeBinding = anonymousClass.resolveTypeBinding(); - if (typeBinding.getSuperclass() != null && - ORG_JUNIT_RULES_EXTERNAL_RESOURCE.equals(typeBinding.getSuperclass().getQualifiedName())) { + private String extractFieldName(FieldDeclaration fieldDeclaration) { + for (Object fragmentObj : fieldDeclaration.fragments()) { + if (fragmentObj instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment= (VariableDeclarationFragment) fragmentObj; + return fragment.getName().getIdentifier(); // Gibt den Variablennamen zurück + } + } + return "UnnamedField"; + } + + private String capitalizeFirstLetter(String input) { + if (input == null || input.isEmpty()) { + return input; + } + return Character.toUpperCase(input.charAt(0)) + input.substring(1); + } - // Entfernen Sie die Superklasse durch Ersetzen des Typs im ClassInstanceCreation - Type type = anonymousClass.getType(); - if (type != null) { - rewrite.replace(type, anonymousClass.getAST().newSimpleType(anonymousClass.getAST().newSimpleName("Object")), group); - } + private String generateUniqueNestedClassName(AnonymousClassDeclaration anonymousClass, String baseName) { + String anonymousCode= anonymousClass.toString(); // Der gesamte Code der anonymen Klasse + String checksum= generateChecksum(anonymousCode); - // Entfernen Sie den Import der Superklasse - importRewriter.removeImport(ORG_JUNIT_RULES_EXTERNAL_RESOURCE); - } + // Feldname großschreiben + String capitalizedBaseName= capitalizeFirstLetter(baseName); + + return capitalizedBaseName + "_" + checksum; } - - protected void refactorAnonymousClassToImplementCallbacks(AnonymousClassDeclaration anonymousClass, - boolean fieldStatic, ASTRewrite rewriter, AST ast, TextEditGroup group, ImportRewrite importRewriter) { - - if (anonymousClass == null) { - return; - } - - // Zugriff auf die umgebende ClassInstanceCreation - ASTNode parent = anonymousClass.getParent(); - if (parent instanceof ClassInstanceCreation) { - ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation) parent; - - // Entferne die ExternalResource-Superklasse - removeExternalResourceSuperclass(classInstanceCreation, rewriter, importRewriter, group); - importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); - importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); - importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_EXTENSION_CONTEXT); - // Generiere eine neue verschachtelte Klasse - String nestedClassName = generateUniqueNestedClassName(anonymousClass); - TypeDeclaration nestedClass = createNestedClassFromAnonymous( - anonymousClass, nestedClassName, fieldStatic, rewriter, ast, importRewriter, group); - - // Ersetze die ursprüngliche Felddeklaration - replaceFieldWithExtensionDeclaration( - classInstanceCreation, nestedClassName, fieldStatic, rewriter, ast, group, importRewriter); - } - } - - private void replaceFieldWithExtensionDeclaration(ClassInstanceCreation classInstanceCreation, String nestedClassName, - boolean fieldStatic, ASTRewrite rewriter, AST ast, TextEditGroup group, ImportRewrite importRewriter) { - - FieldDeclaration fieldDecl = (FieldDeclaration) ASTNodes.getParent(classInstanceCreation, FieldDeclaration.class); - if (fieldDecl != null) { - // Entferne die @Rule-Annotation - removeRuleAnnotation(fieldDecl, rewriter, group, importRewriter, ORG_JUNIT_RULE); - - // Füge die @RegisterExtension-Annotation hinzu - addRegisterExtensionAnnotation(fieldDecl, rewriter, ast, importRewriter, group); - - // Ändere den Typ der FieldDeclaration - Type newType = ast.newSimpleType(ast.newName(nestedClassName)); - rewriter.set(fieldDecl.getType(), SimpleType.NAME_PROPERTY, newType, group); - - // Füge die Initialisierung hinzu - for (Object fragment : fieldDecl.fragments()) { - if (fragment instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragmentNode = (VariableDeclarationFragment) fragment; - ClassInstanceCreation newInstance = ast.newClassInstanceCreation(); - newInstance.setType(ast.newSimpleType(ast.newName(nestedClassName))); - rewriter.set(fragmentNode, VariableDeclarationFragment.INITIALIZER_PROPERTY, newInstance, group); - } - } - } - } - - - private TypeDeclaration createNestedClassFromAnonymous(AnonymousClassDeclaration anonymousClass, - String className, boolean fieldStatic, ASTRewrite rewriter, AST ast, - ImportRewrite importRewriter, TextEditGroup group) { - - // Erstelle die neue TypeDeclaration - TypeDeclaration nestedClass = ast.newTypeDeclaration(); - nestedClass.setName(ast.newSimpleName(className)); - if (fieldStatic) { - nestedClass.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD)); - } - - // Füge die Schnittstellen hinzu - nestedClass.superInterfaceTypes().add(ast.newSimpleType( - ast.newName(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK))); - nestedClass.superInterfaceTypes().add(ast.newSimpleType( - ast.newName(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK))); - importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); - importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); - - // Übertrage den Body der anonymen Klasse in die neue Klasse - ListRewrite bodyRewrite = rewriter.getListRewrite( - nestedClass, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); - for (Object decl : anonymousClass.bodyDeclarations()) { - if (decl instanceof MethodDeclaration) { - MethodDeclaration method = (MethodDeclaration) decl; - - // Konvertiere before() -> beforeEach() und after() -> afterEach() - if (isLifecycleMethod(method, METHOD_BEFORE)) { - MethodDeclaration beforeEachMethod = createLifecycleCallbackMethod( - ast, "beforeEach", "ExtensionContext", method.getBody(), group); - bodyRewrite.insertLast(beforeEachMethod, group); - } else if (isLifecycleMethod(method, METHOD_AFTER)) { - MethodDeclaration afterEachMethod = createLifecycleCallbackMethod( - ast, "afterEach", "ExtensionContext", method.getBody(), group); - bodyRewrite.insertLast(afterEachMethod, group); - } - } - } - - // Füge die neue Klasse zur äußeren Klasse hinzu - ASTNode parentType = findEnclosingTypeDeclaration(anonymousClass); - if (parentType instanceof TypeDeclaration) { - ListRewrite enclosingBodyRewrite = rewriter.getListRewrite( - parentType, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); - enclosingBodyRewrite.insertLast(nestedClass, group); - } - - return nestedClass; - } - - private MethodDeclaration createLifecycleCallbackMethod(AST ast, String methodName, - String paramType, Block oldBody, TextEditGroup group) { - - MethodDeclaration method = ast.newMethodDeclaration(); - method.setName(ast.newSimpleName(methodName)); - method.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); - method.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); - - // Füge den ExtensionContext-Parameter hinzu - SingleVariableDeclaration param = ast.newSingleVariableDeclaration(); - param.setType(ast.newSimpleType(ast.newName(paramType))); - param.setName(ast.newSimpleName("context")); - method.parameters().add(param); - - // Kopiere den Body der alten Methode - if (oldBody != null) { - Block newBody = (Block) ASTNode.copySubtree(ast, oldBody); - method.setBody(newBody); - } - - return method; - } - - - - - - - - - - private String generateUniqueNestedClassName(AnonymousClassDeclaration anonymousClass) { - // Generiere einen eindeutigen Namen für die verschachtelte Klasse - return "GeneratedExtension" + System.nanoTime(); - } - - private void addLifecycleCallbackMethod(TypeDeclaration typeDecl, AST ast, TextEditGroup group, - String methodName, String callbackType) { - MethodDeclaration method = ast.newMethodDeclaration(); - method.setName(ast.newSimpleName(methodName)); - method.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); - method.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); - - // Parameter für den ExtensionContext hinzufügen - SingleVariableDeclaration param = ast.newSingleVariableDeclaration(); - param.setType(ast.newSimpleType(ast.newName("ExtensionContext"))); - param.setName(ast.newSimpleName("context")); - method.parameters().add(param); - - // Methode zum TypeDeclaration hinzufügen - Block body = ast.newBlock(); - method.setBody(body); - - typeDecl.bodyDeclarations().add(method); + FieldDeclaration fieldDeclaration, boolean fieldStatic, ASTRewrite rewriter, AST ast, TextEditGroup group, + ImportRewrite importRewriter) { + + if (anonymousClass == null) { + return; + } + + // Zugriff auf die umgebende ClassInstanceCreation + ASTNode parent= anonymousClass.getParent(); + if (parent instanceof ClassInstanceCreation) { + ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) parent; + + // Entferne die ExternalResource-Superklasse + removeExternalResourceSuperclass(classInstanceCreation, rewriter, importRewriter, group); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_EXTENSION_CONTEXT); + // Generiere eine neue verschachtelte Klasse + String fieldName= extractFieldName(fieldDeclaration); + String nestedClassName= generateUniqueNestedClassName(anonymousClass, fieldName); + TypeDeclaration nestedClass= createNestedClassFromAnonymous(anonymousClass, nestedClassName, fieldStatic, + rewriter, ast, importRewriter, group); + + // Ersetze die ursprüngliche Felddeklaration + replaceFieldWithExtensionDeclaration(classInstanceCreation, nestedClassName, fieldStatic, rewriter, ast, + group, importRewriter); + } } - private ASTNode findEnclosingTypeDeclaration(ASTNode node) { - while (node != null && !(node instanceof TypeDeclaration)) { - node = node.getParent(); - } - return node; + private void replaceFieldWithExtensionDeclaration(ClassInstanceCreation classInstanceCreation, + String nestedClassName, boolean fieldStatic, ASTRewrite rewriter, AST ast, TextEditGroup group, + ImportRewrite importRewriter) { + + FieldDeclaration fieldDecl= (FieldDeclaration) ASTNodes.getParent(classInstanceCreation, + FieldDeclaration.class); + if (fieldDecl != null) { + // Entferne die @Rule-Annotation + removeRuleAnnotation(fieldDecl, rewriter, group, importRewriter, ORG_JUNIT_RULE); + + // Füge die @RegisterExtension-Annotation hinzu + addRegisterExtensionAnnotation(fieldDecl, rewriter, ast, importRewriter, group); + + // Ändere den Typ der FieldDeclaration + Type newType= ast.newSimpleType(ast.newName(nestedClassName)); + rewriter.set(fieldDecl.getType(), SimpleType.NAME_PROPERTY, newType, group); + + // Füge die Initialisierung hinzu + for (Object fragment : fieldDecl.fragments()) { + if (fragment instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragmentNode= (VariableDeclarationFragment) fragment; + ClassInstanceCreation newInstance= ast.newClassInstanceCreation(); + newInstance.setType(ast.newSimpleType(ast.newName(nestedClassName))); + rewriter.set(fragmentNode, VariableDeclarationFragment.INITIALIZER_PROPERTY, newInstance, group); + } + } + } } - private void addCallbackMethod(ListRewrite bodyRewrite, AST ast, TextEditGroup group, String methodName, - String callbackInterface, ImportRewrite importRewriter, String callbackInterfaceImport) { - // Import hinzufügen - importRewriter.addImport(callbackInterfaceImport); + private TypeDeclaration createNestedClassFromAnonymous(AnonymousClassDeclaration anonymousClass, String className, + boolean fieldStatic, ASTRewrite rewriter, AST ast, ImportRewrite importRewriter, TextEditGroup group) { - MethodDeclaration method= ast.newMethodDeclaration(); - // Annotation hinzufügen, falls erforderlich (z. B. @Override) - MarkerAnnotation overrideAnnotation= ast.newMarkerAnnotation(); - overrideAnnotation.setTypeName(ast.newSimpleName("Override")); - method.modifiers().add(overrideAnnotation); - // Neue Methode erstellen + // Erstelle die neue TypeDeclaration + TypeDeclaration nestedClass= ast.newTypeDeclaration(); + nestedClass.setName(ast.newSimpleName(className)); + if (fieldStatic) { + nestedClass.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD)); + } + + // Füge die Schnittstellen hinzu + nestedClass.superInterfaceTypes() + .add(ast.newSimpleType(ast.newName(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK))); + nestedClass.superInterfaceTypes() + .add(ast.newSimpleType(ast.newName(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK))); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); + importRewriter.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + + // Übertrage den Body der anonymen Klasse in die neue Klasse + ListRewrite bodyRewrite= rewriter.getListRewrite(nestedClass, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); + for (Object decl : anonymousClass.bodyDeclarations()) { + if (decl instanceof MethodDeclaration) { + MethodDeclaration method= (MethodDeclaration) decl; + + // Konvertiere before() -> beforeEach() und after() -> afterEach() + if (isLifecycleMethod(method, METHOD_BEFORE)) { + MethodDeclaration beforeEachMethod= createLifecycleCallbackMethod(ast, "beforeEach", + "ExtensionContext", method.getBody(), group); + bodyRewrite.insertLast(beforeEachMethod, group); + } else if (isLifecycleMethod(method, METHOD_AFTER)) { + MethodDeclaration afterEachMethod= createLifecycleCallbackMethod(ast, "afterEach", + "ExtensionContext", method.getBody(), group); + bodyRewrite.insertLast(afterEachMethod, group); + } + } + } + + // Füge die neue Klasse zur äußeren Klasse hinzu + ASTNode parentType= findEnclosingTypeDeclaration(anonymousClass); + if (parentType instanceof TypeDeclaration) { + ListRewrite enclosingBodyRewrite= rewriter.getListRewrite(parentType, + TypeDeclaration.BODY_DECLARATIONS_PROPERTY); + enclosingBodyRewrite.insertLast(nestedClass, group); + } + return nestedClass; + } + + private MethodDeclaration createLifecycleCallbackMethod(AST ast, String methodName, String paramType, Block oldBody, + TextEditGroup group) { + + MethodDeclaration method= ast.newMethodDeclaration(); method.setName(ast.newSimpleName(methodName)); - method.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); method.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD)); + method.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); - // Methode ohne Inhalt - Block methodBody= ast.newBlock(); - method.setBody(methodBody); + // Füge den ExtensionContext-Parameter hinzu + SingleVariableDeclaration param= ast.newSingleVariableDeclaration(); + param.setType(ast.newSimpleType(ast.newName(paramType))); + param.setName(ast.newSimpleName("context")); + method.parameters().add(param); + + // Kopiere den Body der alten Methode + if (oldBody != null) { + Block newBody= (Block) ASTNode.copySubtree(ast, oldBody); + method.setBody(newBody); + } - // Methode hinzufügen - bodyRewrite.insertLast(method, group); + return method; } - private void addBeforeAndAfterEachCallbacks(TypeDeclaration typeDecl, ASTRewrite rewrite, AST ast, - ImportRewrite importRewrite, TextEditGroup group) { - ListRewrite listRewrite= rewrite.getListRewrite(typeDecl, TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY); - addInterfaceCallback(listRewrite, ast, BEFORE_EACH_CALLBACK, group, importRewrite, - ORG_JUNIT_JUPITER_API_EXTENSION_BEFORE_EACH_CALLBACK); - addInterfaceCallback(listRewrite, ast, AFTER_EACH_CALLBACK, group, importRewrite, - ORG_JUNIT_JUPITER_API_EXTENSION_AFTER_EACH_CALLBACK); + private ASTNode findEnclosingTypeDeclaration(ASTNode node) { + while (node != null && !(node instanceof TypeDeclaration)) { + node= node.getParent(); + } + return node; } private void adaptSuperBeforeCalls(String vorher, String nachher, MethodDeclaration method, ASTRewrite rewriter, @@ -785,14 +794,6 @@ protected Name addImport(String typeName, final CompilationUnitRewrite cuRewrite return ast.newName(importedName); } - private void manageImport(ImportRewrite importRewriter, String typeName, boolean add) { - if (add) { - importRewriter.addImport(typeName); - } else { - importRewriter.removeImport(typeName); - } - } - private void addInterfaceCallback(ListRewrite listRewrite, AST ast, String callbackName, TextEditGroup group, ImportRewrite importRewriter, String classtoimport) { // Prüfen, ob das Interface bereits in der Liste existiert @@ -806,73 +807,61 @@ private void addInterfaceCallback(ListRewrite listRewrite, AST ast, String callb importRewriter.addImport(classtoimport); } - private Annotation createRegisterExtensionAnnotation(AST ast, ImportRewrite importRewriter) { - MarkerAnnotation annotation= ast.newMarkerAnnotation(); - annotation.setTypeName(ast.newSimpleName("RegisterExtension")); - importRewriter.addImport("org.junit.jupiter.api.extension.RegisterExtension"); - return annotation; - } - - private void addRegisterExtensionAnnotation( - ASTNode node, - ASTRewrite rewrite, - AST ast, - ImportRewrite importRewrite, - TextEditGroup group - ) { - if (node instanceof FieldDeclaration) { - // Direkt mit FieldDeclaration arbeiten - FieldDeclaration field = (FieldDeclaration) node; - - // Prüfen, ob die Annotation bereits existiert - boolean hasRegisterExtension = field.modifiers().stream() - .anyMatch(modifier -> modifier instanceof Annotation - && ((Annotation) modifier).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); - - // Prüfen, ob die Annotation bereits im Rewrite hinzugefügt wurde - ListRewrite listRewrite = rewrite.getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY); - boolean hasPendingRegisterExtension = listRewrite.getRewrittenList().stream() - .anyMatch(rewritten -> rewritten instanceof MarkerAnnotation - && ((MarkerAnnotation) rewritten).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); - - if (!hasRegisterExtension && !hasPendingRegisterExtension) { - // Annotation hinzufügen, wenn sie weder im AST noch im Rewrite existiert - MarkerAnnotation registerExtensionAnnotation = ast.newMarkerAnnotation(); - registerExtensionAnnotation.setTypeName(ast.newName(ANNOTATION_REGISTER_EXTENSION)); - listRewrite.insertFirst(registerExtensionAnnotation, group); - - // Import hinzufügen - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); - } - } else if (node instanceof ClassInstanceCreation) { - // Übergeordnetes Element der anonymen Klasse finden - ASTNode parent = node.getParent(); - if (parent instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment = (VariableDeclarationFragment) parent; - FieldDeclaration field = (FieldDeclaration) fragment.getParent(); - - // Prüfen, ob die Annotation bereits existiert - boolean hasRegisterExtension = field.modifiers().stream() - .anyMatch(modifier -> modifier instanceof Annotation - && ((Annotation) modifier).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); - - // Prüfen, ob die Annotation bereits im Rewrite hinzugefügt wurde - ListRewrite listRewrite = rewrite.getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY); - boolean hasPendingRegisterExtension = listRewrite.getRewrittenList().stream() - .anyMatch(rewritten -> rewritten instanceof MarkerAnnotation - && ((MarkerAnnotation) rewritten).getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); - - if (!hasRegisterExtension && !hasPendingRegisterExtension) { - // Annotation hinzufügen, wenn sie weder im AST noch im Rewrite existiert - MarkerAnnotation registerExtensionAnnotation = ast.newMarkerAnnotation(); - registerExtensionAnnotation.setTypeName(ast.newName(ANNOTATION_REGISTER_EXTENSION)); - listRewrite.insertFirst(registerExtensionAnnotation, group); - - // Import hinzufügen - importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); - } - } - } + private void addRegisterExtensionAnnotation(ASTNode node, ASTRewrite rewrite, AST ast, ImportRewrite importRewrite, + TextEditGroup group) { + if (node instanceof FieldDeclaration) { + // Direkt mit FieldDeclaration arbeiten + FieldDeclaration field= (FieldDeclaration) node; + + // Prüfen, ob die Annotation bereits existiert + boolean hasRegisterExtension= field.modifiers().stream() + .anyMatch(modifier -> modifier instanceof Annotation && ((Annotation) modifier).getTypeName() + .getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + // Prüfen, ob die Annotation bereits im Rewrite hinzugefügt wurde + ListRewrite listRewrite= rewrite.getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY); + boolean hasPendingRegisterExtension= listRewrite.getRewrittenList().stream() + .anyMatch(rewritten -> rewritten instanceof MarkerAnnotation && ((MarkerAnnotation) rewritten) + .getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + if (!hasRegisterExtension && !hasPendingRegisterExtension) { + // Annotation hinzufügen, wenn sie weder im AST noch im Rewrite existiert + MarkerAnnotation registerExtensionAnnotation= ast.newMarkerAnnotation(); + registerExtensionAnnotation.setTypeName(ast.newName(ANNOTATION_REGISTER_EXTENSION)); + listRewrite.insertFirst(registerExtensionAnnotation, group); + + // Import hinzufügen + importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); + } + } else if (node instanceof ClassInstanceCreation) { + // Übergeordnetes Element der anonymen Klasse finden + ASTNode parent= node.getParent(); + if (parent instanceof VariableDeclarationFragment) { + VariableDeclarationFragment fragment= (VariableDeclarationFragment) parent; + FieldDeclaration field= (FieldDeclaration) fragment.getParent(); + + // Prüfen, ob die Annotation bereits existiert + boolean hasRegisterExtension= field.modifiers().stream() + .anyMatch(modifier -> modifier instanceof Annotation && ((Annotation) modifier).getTypeName() + .getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + // Prüfen, ob die Annotation bereits im Rewrite hinzugefügt wurde + ListRewrite listRewrite= rewrite.getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY); + boolean hasPendingRegisterExtension= listRewrite.getRewrittenList().stream() + .anyMatch(rewritten -> rewritten instanceof MarkerAnnotation && ((MarkerAnnotation) rewritten) + .getTypeName().getFullyQualifiedName().equals(ANNOTATION_REGISTER_EXTENSION)); + + if (!hasRegisterExtension && !hasPendingRegisterExtension) { + // Annotation hinzufügen, wenn sie weder im AST noch im Rewrite existiert + MarkerAnnotation registerExtensionAnnotation= ast.newMarkerAnnotation(); + registerExtensionAnnotation.setTypeName(ast.newName(ANNOTATION_REGISTER_EXTENSION)); + listRewrite.insertFirst(registerExtensionAnnotation, group); + + // Import hinzufügen + importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); + } + } + } } private void ensureExtensionContextParameter(MethodDeclaration method, ASTRewrite rewrite, AST ast, @@ -942,7 +931,9 @@ public TypeDeclaration findTypeDeclaration(IJavaProject javaProject, String full IType type= javaProject.findType(fullyQualifiedTypeName); if (type != null && type.exists()) { CompilationUnit unit= parseCompilationUnit(type.getCompilationUnit()); - return (TypeDeclaration) unit.types().get(0); + + // Rekursive Suche nach der passenden TypeDeclaration + return findTypeDeclarationInCompilationUnit(unit, fullyQualifiedTypeName); } } catch (JavaModelException e) { e.printStackTrace(); @@ -950,6 +941,43 @@ public TypeDeclaration findTypeDeclaration(IJavaProject javaProject, String full return null; } + // Rekursive Suche nach verschachtelten Klassen + private TypeDeclaration findNestedTypeDeclaration(TypeDeclaration typeDecl, String fullyQualifiedTypeName) { + for (TypeDeclaration nestedType : typeDecl.getTypes()) { + String nestedQualifiedName= getQualifiedName(nestedType); + if (fullyQualifiedTypeName.equals(nestedQualifiedName)) { + return nestedType; + } + + // Tiefer in der Hierarchie suchen + TypeDeclaration deeperNestedType= findNestedTypeDeclaration(nestedType, fullyQualifiedTypeName); + if (deeperNestedType != null) { + return deeperNestedType; + } + } + return null; + } + + // Hilfsmethode: Ermittelt den vollqualifizierten Namen einer TypeDeclaration + private String getQualifiedName(TypeDeclaration typeDecl) { + StringBuilder qualifiedName= new StringBuilder(typeDecl.getName().getIdentifier()); + ASTNode parent= typeDecl.getParent(); + while (parent instanceof TypeDeclaration) { + TypeDeclaration parentType= (TypeDeclaration) parent; + qualifiedName.insert(0, parentType.getName().getIdentifier() + "$"); // $ für verschachtelte Klassen + parent= parent.getParent(); + } + + // Paketnamen hinzufügen + CompilationUnit compilationUnit= (CompilationUnit) typeDecl.getRoot(); + if (compilationUnit.getPackage() != null) { + String packageName= compilationUnit.getPackage().getName().getFullyQualifiedName(); + qualifiedName.insert(0, packageName + "."); + } + + return qualifiedName.toString(); + } + protected TypeDeclaration getParentTypeDeclaration(ASTNode node) { while (node != null && !(node instanceof TypeDeclaration)) { node= node.getParent(); @@ -1016,65 +1044,30 @@ private boolean isExternalResource(FieldDeclaration field, String typetolookup) return isExternalResource(binding, typetolookup); } - protected TypeDeclaration getTypeDeclarationForField(FieldDeclaration fieldDeclaration, CompilationUnit cu) { - for (Object fragmentObj : fieldDeclaration.fragments()) { - if (fragmentObj instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment= (VariableDeclarationFragment) fragmentObj; - Expression initializer= fragment.getInitializer(); - - // Prüfen, ob der Initializer eine ClassInstanceCreation ist - if (initializer instanceof ClassInstanceCreation) { - ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) initializer; - ITypeBinding typeBinding= classInstanceCreation.resolveTypeBinding(); - - // Anonyme Klasse: Hier keine TypeDeclaration möglich - if (classInstanceCreation.getAnonymousClassDeclaration() != null) { - // Hier kannst du entweder null zurückgeben oder die anonyme Klasse speziell - // behandeln - continue; // Überspringen, da keine TypeDeclaration möglich ist - } - - // Typ binden und zugehörige TypeDeclaration in der CompilationUnit suchen - if (typeBinding != null) { - return findTypeDeclarationInCompilationUnit(typeBinding, cu); - } - } - - // Typ des Feldes auflösen, wenn keine Initialisierung vorhanden ist - if (fragment.resolveBinding() != null) { - ITypeBinding variableTypeBinding= fragment.resolveBinding().getType(); - if (variableTypeBinding != null) { - return findTypeDeclarationInCompilationUnit(variableTypeBinding, cu); - } - } - } - } - return null; // Keine passende TypeDeclaration gefunden - } - private TypeDeclaration findTypeDeclarationInCompilationUnit(ITypeBinding typeBinding, CompilationUnit cu) { final AbstractTypeDeclaration[] result= { null }; cu.accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { - ITypeBinding binding = node.resolveBinding(); - System.out.println("Visiting TypeDeclaration: " + (binding != null ? binding.getQualifiedName() : "null")); - if (binding != null) { - System.out.println("Expected Binding: " + typeBinding.getQualifiedName()); - System.out.println("Actual Binding: " + binding.getQualifiedName()); - System.out.println("Binding match: " + binding.isEqualTo(typeBinding)); - } - if (binding != null && binding.isEqualTo(typeBinding)) { - result[0] = node; - return false; // Abbruch - } - return true; + ITypeBinding binding= node.resolveBinding(); + System.out.println( + "Visiting TypeDeclaration: " + (binding != null ? binding.getQualifiedName() : "null")); + if (binding != null) { + System.out.println("Expected Binding: " + typeBinding.getQualifiedName()); + System.out.println("Actual Binding: " + binding.getQualifiedName()); + System.out.println("Binding match: " + ASTNodes.areBindingsEqual(binding, typeBinding)); + } + if (binding != null && ASTNodes.areBindingsEqual(binding, typeBinding)) { + result[0]= node; + return false; // Abbruch + } + return true; } @Override public boolean visit(EnumDeclaration node) { - if (node.resolveBinding() != null && node.resolveBinding().isEqualTo(typeBinding)) { + if (node.resolveBinding() != null && ASTNodes.areBindingsEqual(node.resolveBinding(), typeBinding)) { result[0]= node; return false; } @@ -1089,25 +1082,10 @@ public boolean visit(AnnotationTypeDeclaration node) { } return true; } - -// @Override -// public boolean visit(AnonymousClassDeclaration node) { -// // Hinweis: Anonyme Klassen haben oft keine direkten Bindings, daher ist hier eine zusätzliche Prüfung sinnvoll -// ITypeBinding binding = node.resolveBinding(); -// if (binding != null && binding.isEqualTo(typeBinding)) { -// result[0] = node; -// return false; -// } -// return true; -// } }); return (TypeDeclaration) result[0]; } - - private boolean checkTypeBindingMatch(ITypeBinding binding, ITypeBinding targetBinding) { - return binding != null && binding.isEqualTo(targetBinding); - } protected boolean isExternalResource(ITypeBinding typeBinding, String typetolookup) { while (typeBinding != null) { @@ -1128,32 +1106,38 @@ private boolean isStringType(Expression expression, Class class1) { return typeBinding != null && class1.getCanonicalName().equals(typeBinding.getQualifiedName()); } -// public void migrateRuleToRegisterExtensionAndAdaptHierarchy(Optional innerTypeDeclaration, -// TypeDeclaration testClass, ASTRewrite rewrite, AST ast, ImportRewrite importRewrite, TextEditGroup group, -// String varname) { -// if (innerTypeDeclaration.isPresent() && innerTypeDeclaration.get() instanceof TypeDeclaration) { -// adaptBeforeAfterCallsInTestClass((TypeDeclaration) innerTypeDeclaration.get(), varname, rewrite, ast, -// group); -// } -// for (FieldDeclaration field : testClass.getFields()) { -// if (isAnnotatedWithRule(field, ORG_JUNIT_RULE) -// && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { -// removeRuleAnnotation(field, rewrite, group, importRewrite, ORG_JUNIT_RULE); -// addRegisterExtensionAnnotation(field, rewrite, ast, importRewrite, group); -// importRewrite.addImport(ORG_JUNIT_JUPITER_API_EXTENSION_REGISTER_EXTENSION); -// ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() -// .getType(); -// adaptExternalResourceHierarchy(fieldType, rewrite, ast, importRewrite, group); -// } else if (isAnnotatedWithRule(field, ORG_JUNIT_CLASS_RULE) -// && isExternalResource(field, ORG_JUNIT_RULES_EXTERNAL_RESOURCE)) { -// removeRuleAnnotation(field, rewrite, group, importRewrite, ORG_JUNIT_CLASS_RULE); -// addRegisterExtensionAnnotation(field, rewrite, ast, importRewrite, group); -// ITypeBinding fieldType= ((VariableDeclarationFragment) field.fragments().get(0)).resolveBinding() -// .getType(); -// adaptExternalResourceHierarchy(fieldType, rewrite, ast, importRewrite, group); -// } -// } -// } + private TypeDeclaration findTypeDeclarationInCompilationUnit(CompilationUnit unit, String fullyQualifiedTypeName) { + // Keine Extraktion des einfachen Namens + String typeName= fullyQualifiedTypeName; + + // Durchsuche alle Typen in der CompilationUnit + for (Object obj : unit.types()) { + if (obj instanceof TypeDeclaration) { + TypeDeclaration typeDecl= (TypeDeclaration) obj; + TypeDeclaration result= findTypeDeclarationInType(typeDecl, typeName); + if (result != null) { + return result; + } + } + } + return null; + } + + private TypeDeclaration findTypeDeclarationInType(TypeDeclaration typeDecl, String qualifiedTypeName) { + // Vergleiche den voll qualifizierten Namen + if (getQualifiedName(typeDecl).equals(qualifiedTypeName)) { + return typeDecl; + } + + // Durchsuche verschachtelte Typen + for (TypeDeclaration nestedType : typeDecl.getTypes()) { + TypeDeclaration result= findTypeDeclarationInType(nestedType, qualifiedTypeName); + if (result != null) { + return result; + } + } + return null; + } private CompilationUnit parseCompilationUnit(ICompilationUnit iCompilationUnit) { ASTParser parser= ASTParser.newParser(AST.getJLSLatest()); @@ -1260,33 +1244,6 @@ public void reorderParameters(MethodInvocation node, ASTRewrite rewriter, TextEd public abstract void rewrite(JUnitCleanUpFixCore useExplicitEncodingFixCore, T holder, CompilationUnitRewrite cuRewrite, TextEditGroup group); - protected void refactorTestname(TextEditGroup group, ASTRewrite rewriter, AST ast, ImportRewrite importrewriter, - FieldDeclaration node) { - rewriter.remove(node, group); - TypeDeclaration parentClass= (TypeDeclaration) node.getParent(); - addBeforeEachInitMethod(parentClass, rewriter, group); - addTestNameField(parentClass, rewriter, group); - for (MethodDeclaration method : parentClass.getMethods()) { - if (method.getBody() != null) { - method.getBody().accept(new ASTVisitor() { - @Override - public boolean visit(MethodInvocation node) { - if (node.getExpression() != null && node.getExpression().resolveTypeBinding().getQualifiedName() - .equals(ORG_JUNIT_RULES_TEST_NAME)) { - SimpleName newFieldAccess= ast.newSimpleName(TEST_NAME); - rewriter.replace(node, newFieldAccess, group); - } - return super.visit(node); - } - }); - } - } - importrewriter.addImport(ORG_JUNIT_JUPITER_API_TEST_INFO); - importrewriter.addImport(ORG_JUNIT_JUPITER_API_BEFORE_EACH); - importrewriter.removeImport(ORG_JUNIT_RULE); - importrewriter.removeImport(ORG_JUNIT_RULES_TEST_NAME); - } - private void addTestNameField(TypeDeclaration parentClass, ASTRewrite rewriter, TextEditGroup group) { AST ast= parentClass.getAST(); VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment(); diff --git a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java index 7a553da2..660fb1c7 100644 --- a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java +++ b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/RuleExternalResourceJUnitPlugin.java @@ -44,8 +44,8 @@ public void find(JUnitCleanUpFixCore fixcore, CompilationUnit compilationUnit, HelperVisitor.callFieldDeclarationVisitor(ORG_JUNIT_RULE, ORG_JUNIT_RULES_EXTERNAL_RESOURCE, compilationUnit, dataholder, nodesprocessed, (visited, aholder) -> processFoundNode(fixcore, operations, visited, aholder)); - HelperVisitor.callFieldDeclarationVisitor(ORG_JUNIT_CLASS_RULE, ORG_JUNIT_RULES_EXTERNAL_RESOURCE, compilationUnit, - dataholder, nodesprocessed, + HelperVisitor.callFieldDeclarationVisitor(ORG_JUNIT_CLASS_RULE, ORG_JUNIT_RULES_EXTERNAL_RESOURCE, + compilationUnit, dataholder, nodesprocessed, (visited, aholder) -> processFoundNode(fixcore, operations, visited, aholder)); } @@ -56,10 +56,7 @@ private boolean processFoundNode(JUnitCleanUpFixCore fixcore, VariableDeclarationFragment fragment= (VariableDeclarationFragment) node.fragments().get(0); ITypeBinding binding= fragment.resolveBinding().getType(); if ( -// isAnonymousClass(fragment) -// || - (binding == null) - || ORG_JUNIT_RULES_TEST_NAME.equals(binding.getQualifiedName()) + (binding == null) || ORG_JUNIT_RULES_TEST_NAME.equals(binding.getQualifiedName()) || ORG_JUNIT_RULES_TEMPORARY_FOLDER.equals(binding.getQualifiedName())) { return false; } @@ -75,28 +72,27 @@ public void rewrite(JUnitCleanUpFixCore upp, ReferenceHolder Date: Sun, 1 Dec 2024 18:50:57 +0100 Subject: [PATCH 3/4] limit length of checksum to 5 characters --- .../jdt/internal/corext/fix/helper/AbstractTool.java | 9 +++++---- .../ui/tests/quickfix/Java8/JUnitCleanupCases.java | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java index 3a110f25..ea873581 100644 --- a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java +++ b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java @@ -13,6 +13,7 @@ *******************************************************************************/ package org.sandbox.jdt.internal.corext.fix.helper; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -570,8 +571,8 @@ private void removeExternalResourceSuperclass(ClassInstanceCreation anonymousCla private String generateChecksum(String input) { try { - MessageDigest md= MessageDigest.getInstance("MD5"); - byte[] hashBytes= md.digest(input.getBytes()); + MessageDigest md= MessageDigest.getInstance("SHA-256"); + byte[] hashBytes= md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder hexString= new StringBuilder(); for (byte b : hashBytes) { String hex= Integer.toHexString(0xff & b); @@ -579,9 +580,9 @@ private String generateChecksum(String input) { hexString.append('0'); hexString.append(hex); } - return hexString.toString(); + return hexString.toString().substring(0, 5); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("MD5 algorithm not found"); + throw new RuntimeException("SHA-256 algorithm not found"); } } diff --git a/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java b/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java index 0f80c586..20c4bb7f 100644 --- a/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java +++ b/sandbox_junit_cleanup_test/src/org/eclipse/jdt/ui/tests/quickfix/Java8/JUnitCleanupCases.java @@ -670,13 +670,13 @@ public void test3() { public class MyTest { @RegisterExtension - public Er_f48592b683fc99335062dd479c12116b er= new Er_f48592b683fc99335062dd479c12116b(); + public Er_5b8b4 er= new Er_5b8b4(); @Test public void test3() { } - class Er_f48592b683fc99335062dd479c12116b implements org.junit.jupiter.api.extension.BeforeEachCallback, + class Er_5b8b4 implements org.junit.jupiter.api.extension.BeforeEachCallback, org.junit.jupiter.api.extension.AfterEachCallback { public void beforeEach(ExtensionContext context) { } @@ -824,7 +824,7 @@ public void afterEach(ExtensionContext context) { // Anonyme Klasse als ExternalResource @RegisterExtension - public AnonymousRule_4aec5feb6379fe0b02849ce3e05ad3da anonymousRule = new AnonymousRule_4aec5feb6379fe0b02849ce3e05ad3da(); + public AnonymousRule_9ea4e anonymousRule = new AnonymousRule_9ea4e(); // Statische Klasse für ClassRule static class StaticExternalResource implements BeforeAllCallback, AfterAllCallback { @@ -866,7 +866,7 @@ public void afterEach(ExtensionContext context) { // Zweite Regel @RegisterExtension - public SecondRule_61e802c930b36c5c551d3e517590ddbd secondRule = new SecondRule_61e802c930b36c5c551d3e517590ddbd(); + public SecondRule_c4213 secondRule = new SecondRule_c4213(); // Testfälle @Test @@ -879,7 +879,7 @@ public void testWithMultipleResources() { System.out.println("Test with multiple resources"); } - class SecondRule_61e802c930b36c5c551d3e517590ddbd implements org.junit.jupiter.api.extension.BeforeEachCallback, + class SecondRule_c4213 implements org.junit.jupiter.api.extension.BeforeEachCallback, org.junit.jupiter.api.extension.AfterEachCallback { public void beforeEach(ExtensionContext context) { System.out.println("Second rule before"); @@ -890,7 +890,7 @@ public void afterEach(ExtensionContext context) { } } - class AnonymousRule_4aec5feb6379fe0b02849ce3e05ad3da implements org.junit.jupiter.api.extension.BeforeEachCallback, + class AnonymousRule_9ea4e implements org.junit.jupiter.api.extension.BeforeEachCallback, org.junit.jupiter.api.extension.AfterEachCallback { public void beforeEach(ExtensionContext context) { System.out.println("Anonymous rule before"); From 9db4e95a912003c01c29b1466ea1536ed4a63027 Mon Sep 17 00:00:00 2001 From: Carsten Hammer Date: Sun, 1 Dec 2024 18:55:50 +0100 Subject: [PATCH 4/4] add exception to runtimeexception --- .../sandbox/jdt/internal/corext/fix/helper/AbstractTool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java index ea873581..c3031093 100644 --- a/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java +++ b/sandbox_junit_cleanup/src/org/sandbox/jdt/internal/corext/fix/helper/AbstractTool.java @@ -582,7 +582,7 @@ private String generateChecksum(String input) { } return hexString.toString().substring(0, 5); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("SHA-256 algorithm not found"); + throw new RuntimeException("SHA-256 algorithm not found",e); } }