AbstractSandboxCleanUpCore.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.ui.fix;

import java.util.Map;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation;
import org.eclipse.jdt.internal.ui.fix.AbstractCleanUp;
import org.eclipse.jdt.ui.cleanup.CleanUpContext;
import org.eclipse.jdt.ui.cleanup.CleanUpRequirements;
import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
import org.sandbox.jdt.triggerpattern.eclipse.CleanUpResult;
import org.sandbox.jdt.triggerpattern.eclipse.HintMarkerReporter;

/**
 * Template-method base class for sandbox cleanups in {@code sandbox_triggerpattern}.
 *
 * <p>Subclasses only define:</p>
 * <ol>
 *   <li>{@link #getCleanUpKey()} — the {@code MYCleanUpConstants} key</li>
 *   <li>{@link #detect(CompilationUnit, CleanUpResult)} — the detection logic</li>
 *   <li>{@link #getFixLabel()} — the label for rewrite fixes (may return
 *       {@code null} for hint-only cleanups)</li>
 *   <li>{@link #getDescription()} — for {@code getStepDescriptions()}</li>
 *   <li>{@link #getPreview()} — preview text</li>
 * </ol>
 *
 * <p>The base class handles the entire {@code createFix()} pipeline:
 * detect → report markers for findings → build {@link ICleanUpFix} for
 * operations. This supports three modes transparently:</p>
 * <ul>
 *   <li><b>Rewriting</b> — {@code detect()} adds operations →
 *       returns {@code CompilationUnitRewriteOperationsFixCore}</li>
 *   <li><b>Hint-only</b> — {@code detect()} adds findings →
 *       creates markers, returns {@code null}</li>
 *   <li><b>Mixed</b> — {@code detect()} adds both →
 *       creates markers AND returns a fix</li>
 * </ul>
 *
 * <p><b>Note:</b> This base class is exclusively for experimental cleanups in
 * the {@code sandbox_triggerpattern} module. These cleanups are sandbox-specific
 * features (hint-only detection, DSL-driven rules) that do not have Eclipse JDT
 * counterparts and are not intended for upstream contribution. JDT-portable
 * cleanups in other modules (e.g., {@code sandbox_encoding_quickfix},
 * {@code sandbox_junit_cleanup}) must continue to extend {@code AbstractCleanUp}
 * directly to maintain 1:1 porting correspondence.</p>
 *
 * @since 1.3.7
 */
public abstract class AbstractSandboxCleanUpCore extends AbstractCleanUp {

	protected AbstractSandboxCleanUpCore(Map<String, String> options) {
		super(options);
	}

	protected AbstractSandboxCleanUpCore() {
	}

	/**
	 * The {@code MYCleanUpConstants} key that enables/disables this cleanup.
	 *
	 * @return the constant key string
	 */
	protected abstract String getCleanUpKey();

	/**
	 * Runs detection on the compilation unit. Implementations add rewrite
	 * operations and/or hint findings to {@code result}.
	 *
	 * @param cu     the compilation unit to analyze
	 * @param result collects operations and findings
	 */
	protected abstract void detect(CompilationUnit cu, CleanUpResult result);

	/**
	 * Label shown in the refactoring preview for rewrite operations.
	 * May return {@code null} for hint-only cleanups (never used).
	 *
	 * @return the fix label, or {@code null}
	 */
	protected abstract String getFixLabel();

	/**
	 * Description shown in the cleanup step list.
	 *
	 * @return the step description
	 */
	protected abstract String getDescription();

	/**
	 * Returns the preview text shown in the cleanup configuration dialog.
	 * Subclasses must override to provide cleanup-specific before/after examples.
	 *
	 * @return the preview text
	 */
	@Override
	public abstract String getPreview();

	// ── Template method implementations ──

	@Override
	public final CleanUpRequirements getRequirements() {
		return new CleanUpRequirements(isEnabled(getCleanUpKey()), false, false, null);
	}

	@Override
	public final ICleanUpFix createFix(final CleanUpContext context) throws CoreException {
		CompilationUnit cu = context.getAST();
		if (cu == null || !isEnabled(getCleanUpKey())) {
			return null;
		}

		CleanUpResult result = new CleanUpResult();
		detect(cu, result);

		// Always clear stale markers, then report any new findings
		if (cu.getJavaElement() != null) {
			IResource resource = cu.getJavaElement().getResource();
			if (resource != null) {
				HintMarkerReporter.clearMarkers(resource);
				if (result.hasFindings()) {
					HintMarkerReporter.reportFindings(resource, result.getFindings());
				}
			}
		}

		// Build fix for rewrite operations (if any)
		if (!result.hasOperations()) {
			return null;
		}
		return new CompilationUnitRewriteOperationsFixCore(
				getFixLabel(), cu,
				result.getOperations().toArray(new CompilationUnitRewriteOperation[0]));
	}

	@Override
	public final String[] getStepDescriptions() {
		if (isEnabled(getCleanUpKey())) {
			return new String[] { getDescription() };
		}
		return new String[0];
	}
}