MethodCallVisitorBuilder.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* 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
*
* Contributors:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.jdt.internal.common;
import java.util.Set;
import java.util.function.BiPredicate;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.MethodInvocation;
/**
* Fluent builder for visiting method invocations.
*
* <p><b>Example Usage:</b></p>
* <pre>
* // Single method
* HelperVisitor.forMethodCall("org.junit.Assert", "assertTrue")
* .in(compilationUnit)
* .excluding(nodesprocessed)
* .processEach((methodInv, holder) -> {
* addOperation(methodInv);
* return true;
* });
*
* // Multiple methods with imports
* HelperVisitor.forMethodCalls("org.junit.Assert", ALL_ASSERTION_METHODS)
* .andStaticImports()
* .andImportsOf("org.junit.Assert")
* .in(compilationUnit)
* .excluding(nodesprocessed)
* .processEach((node, holder) -> {
* addOperation(node);
* return true;
* });
* </pre>
*
* <p><b>Note on mixed node types:</b> When {@code andStaticImports()} or {@code andImportsOf(...)}
* are enabled, the processor will receive both {@code MethodInvocation} and {@code ImportDeclaration}
* nodes. This is intentional to match the pattern used in JUnit cleanup plugins (e.g., AssertJUnitPlugin)
* where a single processor handles all related nodes polymorphically. Use {@code instanceof} checks
* if you need to handle different node types differently.</p>
*
* @author Carsten Hammer
* @since 1.15
*/
public class MethodCallVisitorBuilder extends HelperVisitorBuilder<MethodInvocation> {
private final String typeFQN;
private final Set<String> methodNames;
private String importFQN;
private boolean includeStaticImports = false;
/**
* Creates a new method call visitor builder.
*
* @param typeFQN the fully qualified name of the type containing the methods
* @param methodNames the set of method names to find
*/
public MethodCallVisitorBuilder(String typeFQN, Set<String> methodNames) {
this.typeFQN = typeFQN;
this.methodNames = methodNames;
}
/**
* Configures the builder to also process static imports for each method.
* Static imports are of the form "typeFQN.methodName".
*
* @return this builder for chaining
*/
public MethodCallVisitorBuilder andStaticImports() {
this.includeStaticImports = true;
return this;
}
/**
* Configures the builder to also process the regular import declaration.
*
* @param importFQN the fully qualified name of the import to include
* @return this builder for chaining
*/
public MethodCallVisitorBuilder andImportsOf(String importFQN) {
this.importFQN = importFQN;
return this;
}
@Override
protected <V, H> void executeVisitors(ReferenceHolder<V, H> holder,
BiPredicate<ASTNode, ReferenceHolder<V, H>> processor) {
// Visit method invocations for each method name
methodNames.forEach(methodName -> {
HelperVisitor.callMethodInvocationVisitor(typeFQN, methodName, compilationUnit,
holder, nodesprocessed, (node, h) -> processor.test(node, h));
});
// Optionally include static imports for each method
if (includeStaticImports) {
methodNames.forEach(methodName -> {
HelperVisitor.callImportDeclarationVisitor(typeFQN + "." + methodName, //$NON-NLS-1$
compilationUnit, holder, nodesprocessed, (node, h) -> processor.test(node, h));
});
}
// Optionally include the regular import
if (importFQN != null) {
HelperVisitor.callImportDeclarationVisitor(importFQN, compilationUnit,
holder, nodesprocessed, (node, h) -> processor.test(node, h));
}
}
}