ContainerLocalRewriteFix.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.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;

import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperationWithSourceRange;
import org.eclipse.jdt.internal.corext.fix.LinkedProposalModelCore;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;

import org.eclipse.jdt.ui.cleanup.ICleanUpFix;

import org.eclipse.text.edits.TextEditGroup;

import org.sandbox.jdt.cleanup.multifile.ContainerLocalRewriteResolver.AppendPair;
import org.sandbox.jdt.cleanup.multifile.ContainerLocalRewriteResolver.ResolvedLength;
import org.sandbox.jdt.cleanup.multifile.ContainerLocalRewriteResolver.ResolvedPlan;
import org.sandbox.jdt.container.api.ContainerLocalRewritePlan;

/** Applies the first strictly local append-array to list rewrite. */
public final class ContainerLocalRewriteFix {

	private static final String DESCRIPTION= "Convert local append array to list"; //$NON-NLS-1$

	private ContainerLocalRewriteFix() {
	}

	/**
	 * Revalidates the immutable plan against the current AST and creates one ordinary
	 * local cleanup fix.
	 */
	public static ICleanUpFix create(
			ICompilationUnit unit,
			org.eclipse.jdt.core.dom.CompilationUnit root,
			ContainerLocalRewritePlan plan) throws CoreException {
		ResolvedPlan resolved= resolve(unit, root, plan);
		return new CompilationUnitRewriteOperationsFixCore(
				DESCRIPTION,
				root,
				new CompilationUnitRewriteOperationWithSourceRange[] {
						new RewriteOperation(resolved) });
	}

	/** Package-visible semantic resolution used by the cleanup adapter. */
	static ResolvedPlan resolve(
			ICompilationUnit unit,
			org.eclipse.jdt.core.dom.CompilationUnit root,
			ContainerLocalRewritePlan plan) throws CoreException {
		ContainerLocalArgumentTransferVerifier.verify(unit, root, plan);
		ContainerLocalRewritePlanVerifier.verifyEncounterIterations(unit, root, plan);
		return ContainerLocalRewriteResolver.resolve(unit, root, plan);
	}

	private static final class RewriteOperation
			extends CompilationUnitRewriteOperationWithSourceRange {

		private final ResolvedPlan resolved;

		RewriteOperation(ResolvedPlan resolved) {
			this.resolved= Objects.requireNonNull(resolved, "resolved"); //$NON-NLS-1$
		}

		@Override
		public void rewriteASTInternal(
				CompilationUnitRewrite cuRewrite,
				LinkedProposalModelCore linkedModel) throws CoreException {
			AST ast= cuRewrite.getRoot().getAST();
			ASTRewrite rewrite= cuRewrite.getASTRewrite();
			ImportRewrite imports= cuRewrite.getImportRewrite();
			TextEditGroup group= createTextEditGroup(DESCRIPTION, cuRewrite);

			Type componentType= resolved.arrayType().getElementType();
			String listName= imports.addImport(resolved.plan().targetInterfaceType());
			SimpleType listRawType= ast.newSimpleType(ast.newName(listName));
			ParameterizedType listType= ast.newParameterizedType(listRawType);
			listType.typeArguments().add(ASTNode.copySubtree(ast, componentType));
			rewrite.replace(resolved.declaration().getType(), listType, group);

			String implementationName=
					imports.addImport(resolved.plan().targetImplementationType());
			org.eclipse.jdt.core.dom.ClassInstanceCreation creation=
					ast.newClassInstanceCreation();
			ParameterizedType implementationType= ast.newParameterizedType(
					ast.newSimpleType(ast.newName(implementationName)));
			creation.setType(implementationType);
			rewrite.replace(resolved.initializer(), creation, group);

			for (AppendPair pair : resolved.appendPairs()) {
				rewrite.remove(pair.growthStatement(), group);
				MethodInvocation add= ast.newMethodInvocation();
				add.setExpression((Expression) ASTNode.copySubtree(
						ast, pair.arrayAccess().getArray()));
				add.setName(ast.newSimpleName("add")); //$NON-NLS-1$
				add.arguments().add(ASTNode.copySubtree(ast, pair.value()));
				rewrite.replace(pair.appendAssignment(), add, group);
			}

			for (ResolvedLength length : resolved.lengths()) {
				MethodInvocation size= ast.newMethodInvocation();
				size.setExpression((Expression) ASTNode.copySubtree(
						ast, length.arrayExpression()));
				size.setName(ast.newSimpleName("size")); //$NON-NLS-1$
				rewrite.replace(length.expression(), size, group);
			}
		}

		@Override
		public String getAdditionalInfo() {
			return DESCRIPTION;
		}
	}
}