ContainerLocalRewritePlanVerifier.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
 *******************************************************************************/
package org.sandbox.jdt.cleanup.multifile;

import java.util.Objects;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.SimpleName;

import org.sandbox.jdt.container.api.ContainerLocalRewritePlan;
import org.sandbox.jdt.container.api.ContainerLocalRewritePlan.EditKind;

/** Revalidates allowed non-editing occurrences retained by a local rewrite plan. */
final class ContainerLocalRewritePlanVerifier {

	private static final String PLUGIN_ID= "sandbox_common"; //$NON-NLS-1$

	private ContainerLocalRewritePlanVerifier() {
	}

	static void verifyEncounterIterations(
			ICompilationUnit unit,
			org.eclipse.jdt.core.dom.CompilationUnit root,
			ContainerLocalRewritePlan plan) throws CoreException {
		Objects.requireNonNull(unit, "unit"); //$NON-NLS-1$
		Objects.requireNonNull(root, "root"); //$NON-NLS-1$
		Objects.requireNonNull(plan, "plan"); //$NON-NLS-1$
		int expected= Math.toIntExact(plan.edits().stream()
				.filter(edit -> edit.kind() == EditKind.VERIFY_ENCOUNTER_ITERATION)
				.count());
		int[] actual= { 0 };
		root.accept(new ASTVisitor() {
			@Override
			public boolean visit(EnhancedForStatement node) {
				IVariableBinding binding= variableBinding(node.getExpression());
				if (binding != null
						&& plan.bindingKey().equals(
								binding.getVariableDeclaration().getKey())) {
					actual[0]++;
				}
				return true;
			}
		});
		if (actual[0] != expected) {
			throw new CoreException(new Status(
					IStatus.ERROR,
					PLUGIN_ID,
					"Container rewrite plan is stale for " + unit.getElementName() //$NON-NLS-1$
							+ ": encounter iteration occurrence count changed")); //$NON-NLS-1$
		}
	}

	private static IVariableBinding variableBinding(Expression expression) {
		Expression current= expression;
		while (current instanceof ParenthesizedExpression parenthesized) {
			current= parenthesized.getExpression();
		}
		if (current instanceof SimpleName name
				&& name.resolveBinding() instanceof IVariableBinding variable) {
			return variable;
		}
		return null;
	}
}