EmbeddedGuardRegistrar.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.ArrayList;
import java.util.List;
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.api.GuardFunction;
import org.sandbox.jdt.triggerpattern.internal.EmbeddedJavaCompiler.CompilationResult;
/**
* Registers guard functions defined in {@code <? ?>} blocks in the
* {@link GuardRegistry}.
*
* <p>The {@link EmbeddedJavaCompiler} parses the code via {@link org.eclipse.jdt.core.dom.ASTParser}
* (no bytecode). Since we have no bytecode, guard functions from
* {@code <? ?>} blocks are registered as stub functions that always return
* {@code true}, with a log warning that full execution requires bytecode
* compilation.</p>
*
* <p>Built-in guards are not overridden by embedded guards.</p>
*
* @since 1.5.0
*/
public final class EmbeddedGuardRegistrar {
private static final Logger LOGGER = Logger.getLogger(EmbeddedGuardRegistrar.class.getName());
/**
* Tracks which guard names were registered by which ruleId, for unregistration.
*/
private static final Map<String, List<String>> REGISTERED_GUARDS = new ConcurrentHashMap<>();
private EmbeddedGuardRegistrar() {
// utility class
}
/**
* Registers all guard methods from a {@link CompilationResult}.
*
* <p>Strategy:</p>
* <ol>
* <li>For each {@link MethodDeclaration} in {@code result.guardMethods()}:</li>
* <li>Extract the method name</li>
* <li>Check if already registered (built-in has precedence)</li>
* <li>Create a {@link GuardFunction} stub wrapper</li>
* <li>Register in {@link GuardRegistry#getInstance()}</li>
* </ol>
*
* @param result the compilation result with guard methods
* @param ruleId the HintFile ID for logging and tracking
*/
public static void registerGuards(CompilationResult result, String ruleId) {
GuardRegistry registry = GuardRegistry.getInstance();
List<String> registered = new ArrayList<>();
for (MethodDeclaration method : result.guardMethods()) {
String guardName = method.getName().getIdentifier();
// Built-in guards take precedence
if (registry.get(guardName) != null) {
LOGGER.log(Level.FINE,
"Guard ''{0}'' already registered (built-in takes precedence), skipping for ruleId={1}", //$NON-NLS-1$
new Object[] { guardName, ruleId });
continue;
}
// Register as a stub that always returns true (AST-only, no bytecode)
GuardFunction stubFn = (ctx, args) -> {
LOGGER.log(Level.FINE,
"Embedded guard ''{0}'' invoked as stub (always true). " //$NON-NLS-1$
+ "Full execution requires bytecode compilation.", //$NON-NLS-1$
guardName);
return true;
};
registry.register(guardName, stubFn);
registered.add(guardName);
LOGGER.log(Level.FINE,
"Registered embedded guard ''{0}'' from ruleId={1}", //$NON-NLS-1$
new Object[] { guardName, ruleId });
}
if (!registered.isEmpty()) {
REGISTERED_GUARDS.put(ruleId, registered);
}
}
/**
* Removes all guards registered by a specific ruleId.
* Called when a HintFile is reloaded (e.g., after editor save).
*
* @param ruleId the HintFile ID whose guards should be removed
*/
public static void unregisterGuards(String ruleId) {
List<String> guardNames = REGISTERED_GUARDS.remove(ruleId);
if (guardNames == null) {
return;
}
GuardRegistry registry = GuardRegistry.getInstance();
for (String guardName : guardNames) {
registry.unregister(guardName);
}
LOGGER.log(Level.FINE,
"Unregistered {0} guards for ruleId={1}", //$NON-NLS-1$
new Object[] { guardNames.size(), ruleId });
}
}