JUnit6CompatibilityJUnitPlugin.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.sandbox.jdt.internal.corext.fix.helper;
import static org.sandbox.jdt.internal.corext.fix.helper.lib.JUnitConstants.ORG_JUNIT_JUPITER_API_METHOD_ORDERER;
import static org.sandbox.jdt.internal.corext.fix.helper.lib.JUnitConstants.ORG_JUNIT_JUPITER_API_TEST_METHOD_ORDER;
import java.util.Set;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperationWithSourceRange;
import org.eclipse.text.edits.TextEditGroup;
import org.sandbox.jdt.internal.common.HelperVisitorFactory;
import org.sandbox.jdt.internal.common.ReferenceHolder;
import org.sandbox.jdt.internal.corext.fix.JUnitCleanUpFixCore;
import org.sandbox.jdt.internal.corext.fix.helper.lib.AbstractTool;
import org.sandbox.jdt.internal.corext.fix.helper.lib.JunitHolder;
/**
* Applies source-level compatibility rewrites for APIs removed by JUnit 6.
*
* <p>The initial compatibility rule replaces the removed
* {@code MethodOrderer.Alphanumeric} implementation with
* {@code MethodOrderer.MethodName}. Detection is binding-based: a user-defined
* nested class named {@code Alphanumeric} is never changed by spelling alone.</p>
*
* <p>Build files, PDE manifests, Eclipse classpath containers and service
* registrations are deliberately outside this local source cleanup. They require
* the atomic project-resource migration plan tracked by the JUnit migration
* architecture.</p>
*/
public final class JUnit6CompatibilityJUnitPlugin
extends AbstractTool<ReferenceHolder<Integer, JunitHolder>> {
private static final String JUPITER_ALPHANUMERIC=
"org.junit.jupiter.api.MethodOrderer.Alphanumeric"; //$NON-NLS-1$
@Override
public void find(JUnitCleanUpFixCore fixcore, CompilationUnit compilationUnit,
Set<CompilationUnitRewriteOperationWithSourceRange> operations, Set<ASTNode> nodesprocessed) {
ReferenceHolder<Integer, JunitHolder> dataHolder= ReferenceHolder.createIndexed();
HelperVisitorFactory.forAnnotation(ORG_JUNIT_JUPITER_API_TEST_METHOD_ORDER)
.in(compilationUnit)
.excluding(nodesprocessed)
.processEach(dataHolder, (visited, holder) -> {
if (!(visited instanceof SingleMemberAnnotation annotation)
|| !(annotation.getValue() instanceof TypeLiteral typeLiteral)) {
return true;
}
ITypeBinding binding= typeLiteral.getType().resolveBinding();
if (binding == null
|| !JUPITER_ALPHANUMERIC.equals(binding.getErasure().getQualifiedName())) {
return true;
}
nodesprocessed.add(annotation);
return addStandardRewriteOperation(fixcore, operations, annotation, holder);
});
}
@Override
protected void process2Rewrite(TextEditGroup group, ASTRewrite rewriter, AST ast,
ImportRewrite importRewriter, JunitHolder junitHolder) {
Annotation oldAnnotation= junitHolder.getAnnotation();
ImportRewriteContext importContext= new ContextSensitiveImportRewriteContext(
(CompilationUnit) oldAnnotation.getRoot(), oldAnnotation.getStartPosition(), importRewriter);
SingleMemberAnnotation replacement= ast.newSingleMemberAnnotation();
replacement.setTypeName(ast.newName(
importRewriter.addImport(ORG_JUNIT_JUPITER_API_TEST_METHOD_ORDER, importContext)));
Name methodOrderer= ast.newName(
importRewriter.addImport(ORG_JUNIT_JUPITER_API_METHOD_ORDERER, importContext));
TypeLiteral typeLiteral= ast.newTypeLiteral();
typeLiteral.setType(ast.newSimpleType(ast.newQualifiedName(
methodOrderer,
ast.newSimpleName("MethodName")))); //$NON-NLS-1$
replacement.setValue(typeLiteral);
rewriter.replace(oldAnnotation, replacement, group);
importRewriter.removeImport(JUPITER_ALPHANUMERIC);
}
@Override
public String getPreview(boolean afterRefactoring) {
if (afterRefactoring) {
return """
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestMethodOrder;
@TestMethodOrder(MethodOrderer.MethodName.class)
public class OrderedTest {
}
"""; //$NON-NLS-1$
}
return """
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestMethodOrder;
@TestMethodOrder(MethodOrderer.Alphanumeric.class)
public class OrderedTest {
}
"""; //$NON-NLS-1$
}
@Override
public String toString() {
return "JUnit6Compatibility"; //$NON-NLS-1$
}
}