EmbeddedFixExecutor.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 - initial API and implementation
 *******************************************************************************/
package org.sandbox.jdt.triggerpattern.internal;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.sandbox.jdt.triggerpattern.internal.EmbeddedJavaCompiler.CompilationResult;

/**
 * Executor for fix functions defined in {@code <? ?>} blocks.
 *
 * <p>Analogous to {@link EmbeddedGuardRegistrar}, but for fix functions annotated
 * with {@code @FixFunction}. Searches compiled {@code <? ?>} classes for annotated
 * methods and invokes them.</p>
 *
 * <p>For the current AST-only implementation, fix functions are registered as stubs
 * that log a warning. Full execution requires bytecode compilation support.</p>
 *
 * @since 1.5.0
 */
public final class EmbeddedFixExecutor {

	private static final Logger LOGGER = Logger.getLogger(EmbeddedFixExecutor.class.getName());

	/**
	 * Tracks registered fix function names by ruleId.
	 */
	private static final Map<String, Map<String, MethodDeclaration>> REGISTERED_FIXES = new ConcurrentHashMap<>();

	private EmbeddedFixExecutor() {
		// utility class
	}

	/**
	 * Registers all fix methods from a {@link CompilationResult}.
	 *
	 * @param result the compilation result with fix methods
	 * @param ruleId the HintFile ID for tracking
	 */
	public static void registerFixes(CompilationResult result, String ruleId) {
		if (result.fixMethods().isEmpty()) {
			return;
		}

		Map<String, MethodDeclaration> fixMap = new ConcurrentHashMap<>();
		for (MethodDeclaration method : result.fixMethods()) {
			String fixName = method.getName().getIdentifier();
			fixMap.put(fixName, method);
			LOGGER.log(Level.FINE,
					"Registered embedded fix function ''{0}'' from ruleId={1}", //$NON-NLS-1$
					new Object[] { fixName, ruleId });
		}
		REGISTERED_FIXES.put(ruleId, fixMap);
	}

	/**
	 * Removes all fix functions registered by a specific ruleId.
	 *
	 * @param ruleId the HintFile ID whose fixes should be removed
	 */
	public static void unregisterFixes(String ruleId) {
		REGISTERED_FIXES.remove(ruleId);
	}

	/**
	 * Checks whether a fix function with the given name is registered.
	 *
	 * @param fixName the fix function name
	 * @return {@code true} if a fix with that name exists
	 */
	public static boolean hasFix(String fixName) {
		for (Map<String, MethodDeclaration> fixMap : REGISTERED_FIXES.values()) {
			if (fixMap.containsKey(fixName)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Executes a fix function by name.
	 *
	 * <p>In the current AST-only implementation, this logs a warning that full
	 * execution requires bytecode compilation. The fix function's AST
	 * {@link MethodDeclaration} is available for inspection but cannot be
	 * executed without bytecode.</p>
	 *
	 * @param fixName the fix function name
	 */
	public static void execute(String fixName) {
		LOGGER.log(Level.WARNING,
				"Embedded fix function ''{0}'' invoked as stub. " //$NON-NLS-1$
						+ "Full execution requires bytecode compilation.", //$NON-NLS-1$
				fixName);
	}
}