FOR_EACH_PATTERN = forEachPattern;
}
public final ForEachStatement transformForEach(final ExpressionStatement node) {
final Match m1 = GET_ITERATOR_PATTERN.match(node);
if (!m1.success()) {
return null;
}
AstNode next = node.getNextSibling();
while (next instanceof LabelStatement) {
next = next.getNextSibling();
}
final Match m2 = FOR_EACH_PATTERN.match(next);
if (!m2.success()) {
return null;
}
final IdentifierExpression iterator = m2.<IdentifierExpression>get("iterator").iterator().next();
final IdentifierExpression item = m2.<IdentifierExpression>get("item").iterator().next();
final WhileStatement loop = (WhileStatement) next;
//
// Ensure that the GET_ITERATOR_PATTERN and FOR_EACH_PATTERN reference the same iterator variable.
//
if (!iterator.matches(m1.get("left").iterator().next())) {
return null;
}
final VariableDeclarationStatement iteratorDeclaration = findVariableDeclaration(loop, iterator.getIdentifier());
if (iteratorDeclaration == null || !(iteratorDeclaration.getParent() instanceof BlockStatement)) {
return null;
}
//
// Find the declaration of the item variable. Because we look only outside the loop,
// we won't make the mistake of moving a captured variable across the loop boundary.
//
final VariableDeclarationStatement itemDeclaration = findVariableDeclaration(loop, item.getIdentifier());
if (itemDeclaration == null || !(itemDeclaration.getParent() instanceof BlockStatement)) {
return null;
}
//
// Now verify that we can move the variable declaration in front of the loop.
//
Statement declarationPoint = canMoveVariableDeclarationIntoStatement(itemDeclaration, loop);
//
// We ignore the return value because we don't care whether we can move the variable into the loop
// (that is possible only with non-captured variables). We just care that we can move it in front
// of the loop.
//
if (declarationPoint != loop) {
return null;
}
final BlockStatement loopBody = (BlockStatement) loop.getEmbeddedStatement();
final Statement secondStatement = getOrDefault(loopBody.getStatements(), 1);
if (secondStatement != null && !secondStatement.isNull()) {
final DefiniteAssignmentAnalysis analysis = new DefiniteAssignmentAnalysis(context, loopBody);
analysis.setAnalyzedRange(secondStatement, loopBody);
analysis.analyze(iterator.getIdentifier(), DefiniteAssignmentStatus.DEFINITELY_NOT_ASSIGNED);
if (!analysis.getUnassignedVariableUses().isEmpty()) {
//
// We can't eliminate the iterator variable because it's used in the loop.
//
return null;
}
}
final ForEachStatement forEach = new ForEachStatement();
forEach.setVariableType(itemDeclaration.getType().clone());
forEach.setVariableName(item.getIdentifier());
forEach.putUserData(
Keys.VARIABLE,
itemDeclaration.getVariables().firstOrNullObject().getUserData(Keys.VARIABLE)
);
final BlockStatement body = new BlockStatement();
forEach.setEmbeddedStatement(body);
((BlockStatement) node.getParent()).getStatements().insertBefore(loop, forEach);
node.remove();
body.add(node);
loop.remove();
body.add(loop);
//
// Now that we moved the whole while statement into the foreach loop, verify that we can
// move the iterator into the foreach loop.
//
declarationPoint = canMoveVariableDeclarationIntoStatement(iteratorDeclaration, forEach);
if (declarationPoint != forEach) {
//
// We can't move the iterator variable after all; undo our changes.
//
node.remove();
((BlockStatement) forEach.getParent()).getStatements().insertBefore(forEach, node);
forEach.replaceWith(loop);
return null;
}
//
// Now create the correct body for the foreach statement.
//
final Expression collection = m1.<Expression>get("collection").iterator().next();
collection.remove();
if (collection instanceof SuperReferenceExpression) {
final ThisReferenceExpression self = new ThisReferenceExpression();
self.putUserData(Keys.TYPE_REFERENCE, collection.getUserData(Keys.TYPE_REFERENCE));
self.putUserData(Keys.VARIABLE, collection.getUserData(Keys.VARIABLE));
forEach.setInExpression(self);
}
else {
forEach.setInExpression(collection);
}
final AstNodeCollection<Statement> bodyStatements = body.getStatements();
bodyStatements.clear();
for (final Statement statement : m2.<Statement>get("statement")) {
statement.remove();
bodyStatements.add(statement);
}
// iteratorDeclaration.remove();