IndependentAstProcessorBuilder.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
*******************************************************************************/
package org.sandbox.jdt.internal.common;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiPredicate;
import org.eclipse.jdt.core.dom.ASTNode;
/**
* Collects independent AST visitors that each inspect the complete root supplied to
* {@link #build(ASTNode)}.
*
* <p>Registration order is deterministic, but one visitor never narrows the scope of
* another. Use {@link AstProcessing#scoped(ReferenceHolder)} when later stages are
* semantically dependent on an earlier match.</p>
*
* @param <V> reference-holder key type
* @param <T> reference-holder value type
*/
public final class IndependentAstProcessorBuilder<V, T> {
private final ReferenceHolder<V, T> holder;
private final List<Stage<V, T>> stages= new ArrayList<>();
private Set<ASTNode> excludedNodes= Set.of();
IndependentAstProcessorBuilder(ReferenceHolder<V, T> holder) {
this.holder= holder;
}
/**
* Registers one typed visitor that will inspect the complete build root.
*
* @param <N> AST node type
* @param nodeType node class to visit
* @param handler processing callback; its result controls child traversal
* @return this builder
*/
public <N extends ASTNode> IndependentAstProcessorBuilder<V, T> on(
Class<N> nodeType,
BiPredicate<? super N, ReferenceHolder<V, T>> handler) {
Objects.requireNonNull(nodeType, "nodeType"); //$NON-NLS-1$
Objects.requireNonNull(handler, "handler"); //$NON-NLS-1$
VisitorEnum visitorType= AstProcessing.visitorType(nodeType);
stages.add(new Stage<>(visitorType,
(node, data) -> handler.test(nodeType.cast(node), data)));
return this;
}
/** Applies one shared exclusion set to every independent visitor traversal. */
public IndependentAstProcessorBuilder<V, T> excluding(Set<? extends ASTNode> nodes) {
excludedNodes= Set.copyOf(Objects.requireNonNull(nodes, "nodes")); //$NON-NLS-1$
return this;
}
/** Executes every registered visitor independently against {@code root}. */
public void build(ASTNode root) {
Objects.requireNonNull(root, "root"); //$NON-NLS-1$
for (Stage<V, T> stage : stages) {
HelperVisitor<ReferenceHolder<V, T>, V, T> visitor=
new HelperVisitor<>(new HashSet<>(excludedNodes), holder);
visitor.add(stage.visitorType(), stage.handler());
visitor.build(root);
}
}
private record Stage<V, T>(
VisitorEnum visitorType,
BiPredicate<ASTNode, ReferenceHolder<V, T>> handler) {
}
}