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

import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_CLEANUP;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_COLLECTIONS;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_PERFORMANCE;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_MODERNIZE_JAVA9;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_MODERNIZE_JAVA11;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_ENCODING;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_JUNIT5;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_STREAM_PERFORMANCE;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_IO_PERFORMANCE;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_COLLECTION_PERFORMANCE;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_NUMBER_COMPARE;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_STRING_EQUALS;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_STRING_ISBLANK;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_ARRAYS;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_COLLECTION_TOARRAY;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_PROBABLE_BUGS;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_MISC;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_DEPRECATIONS;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_CLASSFILE_API;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_SERIALIZATION;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_STRINGBUFFER;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_PLATFORM_LOGGING;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_TYPE_INFERENCE;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_TRY_WITH_RESOURCES;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_STRING_MODERNIZATION;
import static org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants.HINTFILE_BUNDLE_OPTIONAL_MODERNIZATION;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.core.dom.CompilationUnit;
import org.sandbox.jdt.triggerpattern.cleanup.AbstractPatternCleanupPlugin;
import org.sandbox.jdt.triggerpattern.cleanup.DslPluginRegistry;
import org.sandbox.jdt.triggerpattern.cleanup.HintFileFixCore;
import org.sandbox.jdt.triggerpattern.eclipse.CleanUpResult;
import org.sandbox.jdt.triggerpattern.encoding.CharsetForNameEncodingPlugin;
import org.sandbox.jdt.triggerpattern.encoding.StringConstructorEncodingPlugin;
import org.sandbox.jdt.triggerpattern.encoding.StringGetBytesEncodingPlugin;
import org.sandbox.jdt.internal.corext.fix2.MYCleanUpConstants;

/**
 * CleanUp that applies transformation rules from {@code .sandbox-hint} files.
 *
 * <p>This is the bridge between the {@code .sandbox-hint} DSL file format and the
 * Eclipse CleanUp framework. It reads all registered hint files from
 * {@link org.sandbox.jdt.triggerpattern.internal.HintFileRegistry HintFileRegistry}
 * and applies their transformation rules as cleanup operations.</p>
 *
 * <p>This enables users to define cleanup rules declaratively in
 * {@code .sandbox-hint} files without writing Java code.</p>
 *
 * @since 1.3.5
 */
public class HintFileCleanUpCore extends AbstractSandboxCleanUpCore {

	static {
		DslPluginRegistry.register(new CharsetForNameEncodingPlugin());
		DslPluginRegistry.register(new StringConstructorEncodingPlugin());
		DslPluginRegistry.register(new StringGetBytesEncodingPlugin());
	}

	public HintFileCleanUpCore(final Map<String, String> options) {
		super(options);
	}

	public HintFileCleanUpCore() {
	}

	@Override
	protected String getCleanUpKey() {
		return HINTFILE_CLEANUP;
	}

	@Override
	protected String getFixLabel() {
		return MultiFixMessages.HintFileCleanUpFix_refactor;
	}

	@Override
	protected String getDescription() {
		return MultiFixMessages.HintFileCleanUp_description;
	}

	@Override
	protected void detect(CompilationUnit cu, CleanUpResult result) {
		Set<String> enabledBundles = getEnabledBundles();
		HintFileFixCore.findOperations(cu, result.getOperations(),
				enabledBundles, result.getFindings());
		for (String bundleId : enabledBundles) {
			List<AbstractPatternCleanupPlugin<?>> plugins = DslPluginRegistry.getPluginsForBundle(bundleId);
			for (AbstractPatternCleanupPlugin<?> plugin : plugins) {
				plugin.findOperations(cu, result.getOperations());
			}
		}
	}

	/**
	 * Returns the set of enabled bundled hint file IDs based on the current options.
	 * 
	 * <p>Returns a set of bundle IDs corresponding to the enabled per-bundle
	 * options. If all bundles are disabled, an empty set is returned, causing
	 * only project-level hint files to be processed.</p>
	 * 
	 * @return set of enabled bundle IDs (never {@code null})
	 */
	private Set<String> getEnabledBundles() {
		Set<String> enabled = new LinkedHashSet<>();
		if (isEnabled(HINTFILE_BUNDLE_COLLECTIONS)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_COLLECTIONS);
		}
		if (isEnabled(HINTFILE_BUNDLE_PERFORMANCE)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_PERFORMANCE);
		}
		if (isEnabled(HINTFILE_BUNDLE_MODERNIZE_JAVA9)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_MODERNIZE_JAVA9);
		}
		if (isEnabled(HINTFILE_BUNDLE_MODERNIZE_JAVA11)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_MODERNIZE_JAVA11);
		}
		if (isEnabled(HINTFILE_BUNDLE_ENCODING)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_ENCODING);
		}
		if (isEnabled(HINTFILE_BUNDLE_JUNIT5)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_JUNIT5);
			// assume5 and annotations5 are part of the JUnit 4→5 migration bundle
			enabled.add("assume5"); //$NON-NLS-1$
			enabled.add("annotations5"); //$NON-NLS-1$
		}
		if (isEnabled(HINTFILE_BUNDLE_STREAM_PERFORMANCE)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_STREAM_PERFORMANCE);
		}
		if (isEnabled(HINTFILE_BUNDLE_IO_PERFORMANCE)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_IO_PERFORMANCE);
		}
		if (isEnabled(HINTFILE_BUNDLE_COLLECTION_PERFORMANCE)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_COLLECTION_PERFORMANCE);
		}
		if (isEnabled(HINTFILE_BUNDLE_NUMBER_COMPARE)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_NUMBER_COMPARE);
		}
		if (isEnabled(HINTFILE_BUNDLE_STRING_EQUALS)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_STRING_EQUALS);
		}
		if (isEnabled(HINTFILE_BUNDLE_STRING_ISBLANK)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_STRING_ISBLANK);
		}
		if (isEnabled(HINTFILE_BUNDLE_ARRAYS)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_ARRAYS);
		}
		if (isEnabled(HINTFILE_BUNDLE_COLLECTION_TOARRAY)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_COLLECTION_TOARRAY);
		}
		if (isEnabled(HINTFILE_BUNDLE_PROBABLE_BUGS)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_PROBABLE_BUGS);
		}
		if (isEnabled(HINTFILE_BUNDLE_MISC)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_MISC);
		}
		if (isEnabled(HINTFILE_BUNDLE_DEPRECATIONS)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_DEPRECATIONS);
		}
		if (isEnabled(HINTFILE_BUNDLE_CLASSFILE_API)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_CLASSFILE_API);
		}
		if (isEnabled(HINTFILE_BUNDLE_SERIALIZATION)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_SERIALIZATION);
		}
		if (isEnabled(HINTFILE_BUNDLE_STRINGBUFFER)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_STRINGBUFFER);
		}
		if (isEnabled(HINTFILE_BUNDLE_PLATFORM_LOGGING)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_PLATFORM_LOGGING);
		}
		if (isEnabled(HINTFILE_BUNDLE_TYPE_INFERENCE)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_TYPE_INFERENCE);
		}
		if (isEnabled(HINTFILE_BUNDLE_TRY_WITH_RESOURCES)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_TRY_WITH_RESOURCES);
		}
		if (isEnabled(HINTFILE_BUNDLE_STRING_MODERNIZATION)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_STRING_MODERNIZATION);
		}
		if (isEnabled(HINTFILE_BUNDLE_OPTIONAL_MODERNIZATION)) {
			enabled.add(MYCleanUpConstants.HINTFILE_BUNDLE_ID_OPTIONAL_MODERNIZATION);
		}
		return enabled;
	}

	@Override
	public String getPreview() {
		boolean cleanup = isEnabled(HINTFILE_CLEANUP);
		Set<String> enabledBundles = getEnabledBundles();
		StringBuilder sb = new StringBuilder();
		boolean firstPreview = true;
		for (String bundleId : enabledBundles) {
			for (AbstractPatternCleanupPlugin<?> plugin : DslPluginRegistry.getPluginsForBundle(bundleId)) {
				if (!firstPreview) {
					sb.append('\n');
				}
				sb.append(plugin.getPreview(cleanup));
				firstPreview = false;
			}
		}
		if (sb.length() > 0) {
			return sb.toString();
		}
		if (cleanup) {
			return """
				// After applying .sandbox-hint rules:
				String s = String.valueOf(value);
				boolean empty = list.isEmpty();
				"""; //$NON-NLS-1$
		}
		return """
			// Before applying .sandbox-hint rules:
			String s = "" + value;
			boolean empty = list.size() == 0;
			"""; //$NON-NLS-1$
	}
}