HelperVisitorBuilder.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.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
/**
* Base class for fluent-style builders that simplify HelperVisitor usage.
*
* <p>This builder provides a fluent API for configuring and executing AST visitors,
* making the code more readable and reducing boilerplate.</p>
*
* <p><b>Example Usage:</b></p>
* <pre>
* HelperVisitor.forAnnotation("org.junit.Before")
* .in(compilationUnit)
* .excluding(nodesprocessed)
* .processEach((node, holder) -> {
* addOperation(node);
* return true; // Continue visiting
* });
* </pre>
*
* @param <T> the type of AST node being visited
*
* @author Carsten Hammer
* @since 1.15
*/
public abstract class HelperVisitorBuilder<T extends ASTNode> {
/**
* The compilation unit to visit.
*/
protected CompilationUnit compilationUnit;
/**
* Set of nodes that have already been processed (to avoid duplicate processing).
*/
protected Set<ASTNode> nodesprocessed;
/**
* Configures the compilation unit to visit.
*
* @param cu the compilation unit
* @return this builder for chaining
*/
public HelperVisitorBuilder<T> in(CompilationUnit cu) {
this.compilationUnit = cu;
return this;
}
/**
* Configures the set of nodes to exclude from processing.
*
* @param processed the set of already processed nodes
* @return this builder for chaining
*/
public HelperVisitorBuilder<T> excluding(Set<ASTNode> processed) {
this.nodesprocessed = processed;
return this;
}
/**
* Terminal operation that processes each found node with the given processor.
* The processor accepts ASTNode to allow handling different node types polymorphically.
* The processor returns a boolean - return false to stop visiting.
*
* @param <V> the type of keys in the reference holder
* @param <H> the type of values in the reference holder
* @param processor the bi-predicate that processes each found node
* @throws IllegalStateException if the compilation unit was not configured via {@code in(...)}
*/
public <V, H> void processEach(BiPredicate<ASTNode, ReferenceHolder<V, H>> processor) {
validateState();
ReferenceHolder<V, H> holder = new ReferenceHolder<>();
executeVisitors(holder, processor);
}
/**
* Terminal operation that processes each found node with the given processor,
* using a pre-initialized ReferenceHolder for data collection.
* The processor accepts ASTNode to allow handling different node types polymorphically.
* The processor returns a boolean - return false to stop visiting.
*
* @param <V> the type of keys in the reference holder
* @param <H> the type of values in the reference holder
* @param holder the pre-initialized reference holder to use for data collection
* @param processor the bi-predicate that processes each found node
* @throws IllegalStateException if the compilation unit was not configured via {@code in(...)}
*/
public <V, H> void processEach(ReferenceHolder<V, H> holder, BiPredicate<ASTNode, ReferenceHolder<V, H>> processor) {
validateState();
executeVisitors(holder, processor);
}
/**
* Terminal operation that collects all found nodes into a list.
* Note: This returns ASTNode instead of T to handle cases where builders
* may visit multiple node types (e.g., when including imports alongside annotations).
*
* @return a list of all nodes that match the visitor criteria
* @throws IllegalStateException if the compilation unit was not configured via {@code in(...)}
*/
public List<ASTNode> collect() {
validateState();
List<ASTNode> results = new ArrayList<>();
processEach((node, holder) -> {
results.add(node);
return true; // Continue visiting
});
return results;
}
/**
* Validates that required state has been configured before executing visitors.
*
* @throws IllegalStateException if the compilation unit was not set
*/
protected void validateState() {
if (compilationUnit == null) {
throw new IllegalStateException(
"Compilation unit must be configured via in(...) before calling processEach() or collect()"); //$NON-NLS-1$
}
}
/**
* Executes the configured visitors on the compilation unit.
* Subclasses must implement this to delegate to the appropriate HelperVisitor static methods.
*
* @param <V> the type of keys in the reference holder
* @param <H> the type of values in the reference holder
* @param holder the reference holder for collecting data
* @param processor the processor to call for each found node
*/
protected abstract <V, H> void executeVisitors(ReferenceHolder<V, H> holder,
BiPredicate<ASTNode, ReferenceHolder<V, H>> processor);
}