// TODO Only when the iterable *could be* an array (e.g. if static type is Iterable, but not if static type is Sequence)
// TODO Need to use naming.Infix for the hidden members of Array
boolean optForArray = allowArrayOpt && typeFact().getArrayType(iteratedType).isSubtypeOf(iterableType);
boolean optForTuple = allowArraySeqOpt && typeFact().getTupleType(Collections.singletonList(iteratedType), true, false, -1).isSubtypeOf(iterableType);
SyntheticName iterableName = optForArray || optForTuple ? naming.alias("iterable") : null;
SyntheticName isArrayName = optForArray ? naming.alias("isArray") : null;
SyntheticName isTupleName = optForTuple ? naming.alias("isTuple") : null;
SyntheticName arrayName = optForArray || optForTuple ? naming.alias("array") : null;
SyntheticName arrayIndex = optForArray || optForTuple ? naming.alias("i") : null;
SyntheticName arrayLength = optForArray || optForTuple ? naming.alias("length") : null;
if (optForArray || optForTuple) {
result.append(makeVar(FINAL, iterableName, makeJavaType(typeFact().getIterableType(iteratorElementType)), iterableExpr));
}
if (optForArray) {
result.append(makeVar(FINAL, isArrayName,
make().Type(syms().booleanType),
make().TypeTest(iterableName.makeIdent(),
makeJavaType(typeFact().getArrayType(iteratorElementType), JT_RAW))));
}
if (optForTuple) {
result.append(makeVar(FINAL, isTupleName,
make().Type(syms().booleanType),
make().Binary(JCTree.AND,
make().TypeTest(iterableName.makeIdent(),
make().QualIdent(syms().ceylonTupleType.tsym)),
make().Binary(JCTree.NE,
make().Apply(null,
naming.makeQualIdent(
make().TypeCast(make().QualIdent(syms().ceylonTupleType.tsym), iterableName.makeIdent()),
Unfix.$getArray$.toString()),
List.<JCExpression>nil()),
makeNull()))));
}
// java.lang.Object ELEM_NAME;
JCVariableDecl elemDecl = makeVar(iterationVarName, make().Type(syms().objectType), optForArray || optForTuple ? makeNull() : null);
result.append(elemDecl);
SyntheticName iterName = iteratorVarName;
ProducedType iteratorType = typeFact().getIteratorType(iteratorElementType);
JCExpression iteratorTypeExpr = makeJavaType(iteratorType, CeylonTransformer.JT_TYPE_ARGUMENT);
// ceylon.language.Iterator<T> LOOP_VAR_NAME$iter$X = ITERABLE.getIterator();
// We don't need to unerase here as anything remotely a sequence will be erased to Iterable, which has getIterator()
JCExpression getIter;
if (optForArray || optForTuple) {
at(node);
result.append(makeVar(FINAL, arrayName, make().Type(syms().objectType), null));
result.append(makeVar(arrayIndex, make().Type(syms().intType), make().Literal(0)));
result.append(makeVar(FINAL, arrayLength, make().Type(syms().intType), null));
ListBuffer<JCStatement> whenTuple = ListBuffer.<JCTree.JCStatement>lb();
whenTuple.append(make().Exec(make().Assign(
arrayName.makeIdent(),
make().Apply(null,
naming.makeQualIdent(
make().TypeCast(make().QualIdent(syms().ceylonTupleType.tsym), iterableName.makeIdent()),
Unfix.$getArray$.toString()),
List.<JCExpression>nil()))));
whenTuple.append(make().Exec(make().Assign(
arrayIndex.makeIdent(),
make().Apply(null,
naming.makeQualIdent(
make().TypeCast(make().QualIdent(syms().ceylonTupleType.tsym), iterableName.makeIdent()),
Unfix.$getFirst$.toString()),
List.<JCExpression>nil()))));
whenTuple.append(make().Exec(make().Assign(
arrayLength.makeIdent(),
make().Binary(JCTree.PLUS, arrayIndex.makeIdent(), make().Apply(null,
naming.makeQualIdent(
make().TypeCast(make().QualIdent(syms().ceylonTupleType.tsym), iterableName.makeIdent()),
Unfix.$getLength$.toString()),
List.<JCExpression>nil())))));
ListBuffer<JCStatement> whenArray = ListBuffer.<JCTree.JCStatement>lb();
whenArray.append(make().Exec(make().Assign(
arrayName.makeIdent(),
make().Apply(null,
naming.makeQualIdent(
make().TypeCast(makeJavaType(typeFact().getArrayType(typeFact().getAnythingDeclaration().getType()), JT_RAW), iterableName.makeIdent()),
"toArray"),
List.<JCExpression>nil()))));
whenArray.append(make().Exec(make().Assign(
arrayLength.makeIdent(),
make().Apply(null,
naming.makeQuotedFQIdent("com.redhat.ceylon.compiler.java.Util.arrayLength"),
List.<JCExpression>of(arrayName.makeIdent())))));
ListBuffer<JCStatement> whenIterable = ListBuffer.<JCTree.JCStatement>lb();
whenIterable.append(make().Exec(make().Assign(
arrayName.makeIdent(),
makeNull())));
whenIterable.append(make().Exec(make().Assign(
arrayLength.makeIdent(),
make().Literal(0))));
if (optForArray && optForTuple) {
result.append(make().If(isTupleName.makeIdent(),
make().Block(0, whenTuple.toList()),
make().If(isArrayName.makeIdent(),
make().Block(0, whenArray.toList()),
make().Block(0, whenIterable.toList()))));
} else {
result.append(make().If((optForArray ? isArrayName : isTupleName).makeIdent(),
make().Block(0, (optForArray ? whenArray : whenTuple).toList()),
make().Block(0, whenIterable.toList())));
}
getIter = make().Conditional(
optForArray && optForTuple ? make().Binary(JCTree.OR, isTupleName.makeIdent(), isArrayName.makeIdent()): optForArray ? isArrayName.makeIdent() : isTupleName.makeIdent(),
makeNull(),
make().Apply(null, makeSelect(iterableName.makeIdent(), "iterator"), List.<JCExpression> nil()));
} else {
getIter = at(node).Apply(null, makeSelect(iterableExpr, "iterator"), List.<JCExpression> nil());
}
getIter = gen().expressionGen().applyErasureAndBoxing(getIter, iteratorType, true, BoxingStrategy.BOXED, iteratorType);
JCVariableDecl iteratorDecl = at(node).VarDef(make().Modifiers(0), iterName.asName(), iteratorTypeExpr, getIter);
// .ceylon.language.Iterator<T> LOOP_VAR_NAME$iter$X = ITERABLE.getIterator();
result.append(iteratorDecl);
ListBuffer<JCStatement> loopBody = ListBuffer.<JCStatement>lb();
if(optForArray || optForTuple) {
JCExpression cond;
if (optForArray && optForTuple) {
cond = make().Binary(JCTree.OR, isTupleName.makeIdent(), isArrayName.makeIdent());
} else if (optForArray) {
cond = isArrayName.makeIdent();
} else {
cond = isTupleName.makeIdent();
}
loopBody.append(make().If(cond,
make().Exec(make().Assign(iterationVarName.makeIdent(),
make().Apply(null,
naming.makeQuotedFQIdent("com.redhat.ceylon.compiler.java.Util.getObjectArray"),
List.<JCExpression>of(arrayName.makeIdent(),
make().Unary(JCTree.POSTINC, arrayIndex.makeIdent()))))),
null));
}
if (itemDecls != null) {
loopBody.appendList(itemDecls);
}
// The user-supplied contents of the loop
loopBody.appendList(bodyStmts);
// ELEM_NAME = LOOP_VAR_NAME$iter$X.next()
JCExpression iter_elem = make().Apply(null, makeSelect(iterName.makeIdent(), "next"), List.<JCExpression> nil());
JCExpression elem_assign = make().Assign(iterationVarName.makeIdent(), iter_elem);
// !((ELEM_NAME = LOOP_VAR_NAME$iter$X.next()) instanceof Finished)
JCExpression instof = make().TypeTest(elem_assign, makeIdent(syms().ceylonFinishedType));
JCExpression loopCond = make().Unary(JCTree.NOT, instof);
if (optForArray || optForTuple) {