FluentVisitorExamples.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.ast.api.examples;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.sandbox.ast.api.core.ASTWrapper;
import org.sandbox.ast.api.expr.InfixExpr;
import org.sandbox.ast.api.expr.MethodInvocationExpr;
import org.sandbox.ast.api.expr.SimpleNameExpr;
import org.sandbox.ast.api.info.MethodInfo;
import org.sandbox.ast.api.stmt.EnhancedForStmt;
import org.sandbox.ast.api.stmt.IfStmt;
import org.sandbox.ast.api.visitor.FluentVisitor;

/**
 * Practical examples demonstrating FluentVisitor usage patterns.
 * Shows how to use the visitor API for common AST analysis tasks.
 */
public class FluentVisitorExamples {

	/**
	 * Example 1: Find all method invocations named "add".
	 *
	 * @param nodes list of AST nodes to analyze
	 * @return list of messages for each add() call found
	 */
	public static List<String> findAddMethodCalls(List<ASTWrapper> nodes) {
		List<String> results = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onMethodInvocation()
		.when(mi -> mi.methodName().equals(Optional.of("add")))
		.then(mi -> results.add("Found add() call"))
		.build();

		visitor.visitAll(nodes);
		return results;
	}

	/**
 * Example 2: Find all static method calls.
 *
 * @param nodes list of AST nodes to analyze
 * @return list of static method calls
 */
	public static List<MethodInvocationExpr> findStaticMethodCalls(List<ASTWrapper> nodes) {
		List<MethodInvocationExpr> results = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onMethodInvocation()
		.filter(MethodInvocationExpr::isStatic)
		.then(results::add)
		.build();

		visitor.visitAll(nodes);
		return results;
	}

	/**
 * Example 3: Find all string concatenations.
 *
 * @param nodes list of AST nodes to analyze
 * @return list of string concatenation expressions
 */
	public static List<InfixExpr> findStringConcatenations(List<ASTWrapper> nodes) {
		List<InfixExpr> results = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onInfix()
		.filter(InfixExpr::isStringConcatenation)
		.then(results::add)
		.build();

		visitor.visitAll(nodes);
		return results;
	}

	/**
 * Example 4: Find all variable names starting with "temp".
 *
 * @param nodes list of AST nodes to analyze
 * @return list of temporary variable names
 */
	public static List<String> findTemporaryVariables(List<ASTWrapper> nodes) {
		List<String> results = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onSimpleName()
		.when(sn -> sn.identifier().startsWith("temp"))
		.when(SimpleNameExpr::isVariable)
		.then(sn -> results.add(sn.identifier()))
		.build();

		visitor.visitAll(nodes);
		return results;
	}

	/**
 * Example 5: Find all enhanced for loops iterating over lists.
 *
 * @param nodes list of AST nodes to analyze
 * @return list of enhanced for statements
 */
	public static List<EnhancedForStmt> findListIterations(List<ASTWrapper> nodes) {
		List<EnhancedForStmt> results = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onEnhancedFor()
		.when(ef -> ef.iterable()
		.map(expr -> expr.hasType("java.util.List"))
		.orElse(false))
		.then(results::add)
		.build();

		visitor.visitAll(nodes);
		return results;
	}

	/**
 * Example 6: Find all if statements with simple boolean conditions.
 *
 * @param nodes list of AST nodes to analyze
 * @return list of if statements
 */
	public static List<IfStmt> findSimpleBooleanIfs(List<ASTWrapper> nodes) {
		List<IfStmt> results = new ArrayList<>();
		
		FluentVisitor visitor = FluentVisitor.builder()
				.onIfStatement()
				.when(is -> is.condition()
						.map(expr -> expr instanceof SimpleNameExpr)
						.orElse(false))
				.then(results::add)
				.build();
		
		visitor.visitAll(nodes);
		return results;
	}

	/**
 * Example 7: Combine multiple visitors using andThen.
 *
 * @param nodes list of AST nodes to analyze
 * @return combined analysis results
 */
	public static String analyzeCodePatterns(List<ASTWrapper> nodes) {
		List<String> methodCalls = new ArrayList<>();
		List<String> controlFlow = new ArrayList<>();

		FluentVisitor methodVisitor = FluentVisitor.builder()
		.onMethodInvocation(mi -> methodCalls.add("method"))
		.build();

		FluentVisitor controlFlowVisitor = FluentVisitor.builder()
		.onIfStatement(is -> controlFlow.add("if"))
		.onEnhancedFor(ef -> controlFlow.add("for"))
		.onWhileLoop(wl -> controlFlow.add("while"))
		.build();

		FluentVisitor combined = methodVisitor.andThen(controlFlowVisitor);
		combined.visitAll(nodes);

		return String.format("Method calls: %d, Control flow: %d",
		methodCalls.size(), controlFlow.size());
	}

	/**
 * Example 8: Complex multi-condition visitor.
 * Finds method calls to List.add() with at least one argument.
 *
 * @param nodes list of AST nodes to analyze
 * @return list of matching method invocations
 */
	public static List<MethodInvocationExpr> findListAddWithArguments(List<ASTWrapper> nodes) {
		List<MethodInvocationExpr> results = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onMethodInvocation()
		.when(mi -> mi.methodName().equals(Optional.of("add")))
		.when(mi -> mi.argumentCount() > 0)
		.when(mi -> mi.method()
		.map(MethodInfo::isListAdd)
		.orElse(false))
		.then(results::add)
		.build();

		visitor.visitAll(nodes);
		return results;
	}

	/**
 * Example 9: Multiple handlers for the same node type.
 * Demonstrates counting both getters and setters.
 *
 * @param nodes list of AST nodes to analyze
 * @return formatted count string
 */
	public static String countGettersAndSetters(List<ASTWrapper> nodes) {
		List<String> getters = new ArrayList<>();
		List<String> setters = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onSimpleName()
		.when(sn -> sn.identifier().startsWith("get"))
		.then(sn -> getters.add(sn.identifier()))
		.onSimpleName()
		.when(sn -> sn.identifier().startsWith("set"))
		.then(sn -> setters.add(sn.identifier()))
		.build();

		visitor.visitAll(nodes);
		return String.format("Getters: %d, Setters: %d", getters.size(), setters.size());
	}

	/**
 * Example 10: Using onAny to count all nodes.
 *
 * @param nodes list of AST nodes to analyze
 * @return total node count
 */
	public static int countAllNodes(List<ASTWrapper> nodes) {
		List<ASTWrapper> allNodes = new ArrayList<>();

		FluentVisitor visitor = FluentVisitor.builder()
		.onAny(allNodes::add)
		.build();

		visitor.visitAll(nodes);
		return allNodes.size();
	}
}