IteratorImportCleanup.java

/*******************************************************************************
 * Copyright (c) 2026 Carsten Hammer and others.
 *
 * 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.internal.corext.fix.helper;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.manipulation.ImportReferencesCollector;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;

/** Completes ImportRemover's single-type removal for erased iterator imports. */
final class IteratorImportCleanup {
	private IteratorImportCleanup() {
	}

	static void removeUnusedPackageImports(CompilationUnitRewrite cuRewrite) {
		Set<String> explicitTypes= new HashSet<>();
		List<ImportDeclaration> packageImports= new ArrayList<>();
		for (Object item : cuRewrite.getRoot().imports()) {
			ImportDeclaration declaration= (ImportDeclaration) item;
			if (declaration.isStatic()) {
				continue;
			}
			if (!declaration.isOnDemand()) {
				explicitTypes.add(declaration.getName().getFullyQualifiedName());
			} else if (declaration.resolveBinding() instanceof IPackageBinding) {
				packageImports.add(declaration);
			}
		}
		if (packageImports.isEmpty()) {
			return;
		}

		Set<String> removedTypes= new HashSet<>();
		Set<String> affectedPackages= new HashSet<>();
		for (IBinding binding : cuRewrite.getImportRemover().getImportsToRemove()) {
			if (binding instanceof ITypeBinding type && !type.isRecovered()) {
				String name= type.getTypeDeclaration().getQualifiedName();
				removedTypes.add(name);
				if (!explicitTypes.contains(name) && type.getPackage() != null) {
					affectedPackages.add(type.getPackage().getName());
				}
			}
		}
		if (affectedPackages.isEmpty()) {
			return;
		}

		// The collector also accounts for annotations, type arguments and Javadoc.
		// ImportRemover's result already excludes retained and registered added uses.
		List<SimpleName> references= new ArrayList<>();
		ImportReferencesCollector.collect(cuRewrite.getRoot(), cuRewrite.getCu().getJavaProject(),
				null, references, new ArrayList<>());
		for (SimpleName reference : references) {
			if (!(reference.resolveBinding() instanceof ITypeBinding type) || type.isRecovered()) {
				return; // An unresolved reference might still depend on any wildcard.
			}
			String name= type.getTypeDeclaration().getQualifiedName();
			if (!removedTypes.contains(name) && !explicitTypes.contains(name) && type.getPackage() != null) {
				affectedPackages.remove(type.getPackage().getName());
			}
		}
		for (ImportDeclaration declaration : packageImports) {
			String name= declaration.getName().getFullyQualifiedName();
			if (affectedPackages.contains(name)) {
				cuRewrite.getImportRewrite().removeImport(name + ".*"); //$NON-NLS-1$
			}
		}
	}
}