IteratorWhileToEnhancedFor.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.corext.fix.helper;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.internal.corext.fix.CompilationUnitRewriteOperationsFixCore.CompilationUnitRewriteOperation;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
import org.eclipse.text.edits.TextEditGroup;
import org.sandbox.functional.core.builder.LoopModelBuilder;
import org.sandbox.functional.core.model.SourceDescriptor;
import org.sandbox.functional.core.terminal.ForEachTerminal;
import org.sandbox.jdt.internal.common.ReferenceHolder;
import org.sandbox.jdt.internal.corext.fix.UseFunctionalCallFixCore;
import org.sandbox.jdt.internal.corext.fix.helper.IteratorPatternDetector.IteratorPattern;
/** Converts verified iterator while/for protocols to enhanced for through the ULR. */
public class IteratorWhileToEnhancedFor extends AbstractFunctionalCall<ASTNode> {
private final IteratorPatternDetector detector = new IteratorPatternDetector();
@Override
public void find(UseFunctionalCallFixCore fix, CompilationUnit unit,
Set<CompilationUnitRewriteOperation> operations, Set<ASTNode> processed) {
LoopConversionService.scanRoot(unit).accept(new ASTVisitor() {
@Override
public boolean visit(WhileStatement loop) {
if (!(loop.getParent() instanceof Block block)) return true;
Statement previous = IteratorPatternDetector.findPreviousStatement(block, loop);
return schedule(loop, previous, detector.detectWhilePattern(loop, previous));
}
@Override
public boolean visit(ForStatement loop) {
return schedule(loop, null, detector.detectForLoopPattern(loop));
}
private boolean schedule(Statement loop, Statement previous, IteratorPattern pattern) {
if (ExpressionHelper.overlapsProcessedNode(loop, processed)) return false;
if (IteratorLoopBindings.element(loop, previous, pattern) == null) return true;
ReferenceHolder<ASTNode, Object> data = ReferenceHolder.create();
data.put(loop, pattern);
operations.add(fix.rewrite(loop, data));
processed.add(loop);
if (previous != null) processed.add(previous);
return false;
}
});
}
@Override
public void rewrite(UseFunctionalCallFixCore fix, ASTNode visited, CompilationUnitRewrite rewrite,
TextEditGroup group, ReferenceHolder<ASTNode, Object> data) throws CoreException {
if (!(visited instanceof Statement loop) || !(data.get(visited) instanceof IteratorPattern pattern)) return;
Statement previous = loop instanceof WhileStatement ? IteratorPatternDetector.findPreviousStatement((Block) loop.getParent(), loop) : null;
VariableDeclarationFragment element = IteratorLoopBindings.element(loop, previous, pattern);
if (element == null) return;
ITypeBinding type = element.resolveBinding().getType();
List<String> body = ExpressionHelper.bodyStatementsToStrings(pattern.loopBody());
var model = new LoopModelBuilder()
.source(SourceDescriptor.SourceType.ITERABLE, pattern.collectionExpression().toString(),
IteratorLoopBindings.sourceElementType(pattern.collectionExpression().resolveTypeBinding()))
.element(element.getName().getIdentifier(), type.getQualifiedName(), type.isPrimitive())
.terminal(new ForEachTerminal(body.subList(1, body.size()), false)).build();
new ASTEnhancedForRenderer(rewrite.getAST(), rewrite.getASTRewrite()).renderIteratorLoop(model, loop,
previous, pattern.collectionExpression(), (VariableDeclarationStatement) element.getParent(), rewrite.getImportRewrite(), group);
if (previous != null) {
rewrite.getImportRemover().registerRemovedNode(previous);
rewrite.getImportRemover().applyRemoves(rewrite.getImportRewrite());
}
}
@Override
public String getPreview(boolean after) {
return after ? """
for (String item : items) {
System.out.println(item);
}
""" : """
Iterator<String> it = items.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
""";
}
}