@@ -77,26 +77,29 @@ public static final class ForLoopContent {
7777 private Expression iteratorVariable ;
7878 private Name loopVariable ;
7979 private Name elementVariable ;
80+ private boolean isLoopingForward ;
8081
8182 private ForLoopContent () {
8283 // Use method factories
8384 }
8485
85- private static ForLoopContent indexedArray (Expression containerVariable , Name loopVariable ) {
86+ private static ForLoopContent indexedArray (Expression containerVariable , Name loopVariable , boolean isLoopingForward ) {
8687 final ForLoopContent content = new ForLoopContent ();
8788 content .iterationType = IterationType .INDEX ;
8889 content .containerType = ContainerType .ARRAY ;
8990 content .containerVariable = containerVariable ;
9091 content .loopVariable = loopVariable ;
92+ content .isLoopingForward = isLoopingForward ;
9193 return content ;
9294 }
9395
94- private static ForLoopContent indexedCollection (Expression containerVariable , Name loopVariable ) {
96+ private static ForLoopContent indexedCollection (Expression containerVariable , Name loopVariable , boolean isLoopingForward ) {
9597 final ForLoopContent content = new ForLoopContent ();
9698 content .iterationType = IterationType .INDEX ;
9799 content .containerType = ContainerType .COLLECTION ;
98100 content .containerVariable = containerVariable ;
99101 content .loopVariable = loopVariable ;
102+ content .isLoopingForward = isLoopingForward ;
100103 return content ;
101104 }
102105
@@ -106,6 +109,7 @@ private static ForLoopContent iteratedCollection(Expression containerVariable, E
106109 content .containerType = ContainerType .COLLECTION ;
107110 content .containerVariable = containerVariable ;
108111 content .iteratorVariable = iteratorVariable ;
112+ content .isLoopingForward = true ;
109113 return content ;
110114 }
111115
@@ -163,11 +167,21 @@ public IterationType getIterationType() {
163167 return iterationType ;
164168 }
165169
170+ /**
171+ * Returns true if the loop iterate from the start to the end of the container.
172+ *
173+ * @return true if the loop iterate from the start to the end of the container
174+ */
175+ public boolean isLoopingForward () {
176+ return isLoopingForward ;
177+ }
178+
166179 @ Override
167180 public String toString () {
168181 return getClass ().getSimpleName () + "(" + "iterationType=" + iterationType + ", containerType=" //$NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
169182 + containerType + ", containerVariable=" + containerVariable + ", iteratorVariable=" //$NON-NLS-1$ $NON-NLS-2$
170183 + iteratorVariable + ", loopVariable=" + loopVariable + ", elementVariable=" + elementVariable //$NON-NLS-1$ $NON-NLS-2$
184+ + ", isLoopingForward=" + isLoopingForward //$NON-NLS-1$
171185 + ")" ; //$NON-NLS-1$
172186 }
173187 }
@@ -202,11 +216,25 @@ public static ForLoopContent iterateOverContainer(ForStatement node) {
202216 } else if (updaters .size () == 1 && ASTNodes .isPrimitive (firstInit , int .class .getSimpleName ())) {
203217 final Pair <Name , Expression > initPair = decomposeInitializer (firstInit );
204218 final Name init = initPair .getFirst ();
205- final ForLoopContent forContent = getIndexOnIterable (condition , init );
206- final Name updater = getUpdaterOperand (updaters .get (0 ));
207- Long zero = ASTNodes .integerLiteral (initPair .getSecond ());
219+ final Expression startValue = initPair .getSecond ();
220+ Long zero = ASTNodes .integerLiteral (startValue );
221+ final InfixExpression startValueMinusOne = ASTNodes .as (startValue , InfixExpression .class );
222+ Expression collectionOnSize = null ;
223+ Expression arrayOnLength = null ;
224+
225+ if (startValueMinusOne != null && !startValueMinusOne .hasExtendedOperands () && ASTNodes .hasOperator (startValueMinusOne , InfixExpression .Operator .MINUS )) {
226+ final Long one = ASTNodes .integerLiteral (startValueMinusOne .getRightOperand ());
227+
228+ if (one != null && one == 1 ) {
229+ collectionOnSize = getCollectionOnSize (startValueMinusOne .getLeftOperand ());
230+ arrayOnLength = getArrayOnLength (startValueMinusOne .getLeftOperand ());
231+ }
232+ }
208233
209- if (forContent != null && zero != null && zero == 0 && ASTNodes .isSameVariable (init , forContent .loopVariable )
234+ final ForLoopContent forContent = getIndexOnIterable (condition , init , zero , collectionOnSize , arrayOnLength );
235+ final Name updater = getUpdaterOperand (updaters .get (0 ), zero != null && zero == 0 );
236+
237+ if (forContent != null && ASTNodes .isSameVariable (init , forContent .loopVariable )
210238 && ASTNodes .isSameVariable (init , updater )) {
211239 return forContent ;
212240 }
@@ -224,19 +252,19 @@ private static ForLoopContent getIteratorOnCollection(Expression containerVar, E
224252 return null ;
225253 }
226254
227- private static Name getUpdaterOperand (Expression updater ) {
255+ private static Name getUpdaterOperand (Expression updater , boolean isLoopingForward ) {
228256 Expression updaterOperand = null ;
229257
230258 if (updater instanceof PostfixExpression ) {
231259 final PostfixExpression pe = (PostfixExpression ) updater ;
232260
233- if (ASTNodes .hasOperator (pe , PostfixExpression .Operator .INCREMENT )) {
261+ if (isLoopingForward ? ASTNodes .hasOperator (pe , PostfixExpression .Operator .INCREMENT ) : ASTNodes . hasOperator ( pe , PostfixExpression . Operator . DECREMENT )) {
234262 updaterOperand = pe .getOperand ();
235263 }
236264 } else if (updater instanceof PrefixExpression ) {
237265 final PrefixExpression pe = (PrefixExpression ) updater ;
238266
239- if (ASTNodes .hasOperator (pe , PrefixExpression .Operator .INCREMENT )) {
267+ if (isLoopingForward ? ASTNodes .hasOperator (pe , PrefixExpression .Operator .INCREMENT ) : ASTNodes . hasOperator ( pe , PrefixExpression . Operator . DECREMENT )) {
240268 updaterOperand = pe .getOperand ();
241269 }
242270 }
@@ -261,6 +289,7 @@ public static Pair<Name, Expression> decomposeInitializer(Expression init) {
261289 if (init instanceof VariableDeclarationExpression ) {
262290 final VariableDeclarationExpression vde = (VariableDeclarationExpression ) init ;
263291 final List <VariableDeclarationFragment > fragments = ASTNodes .fragments (vde );
292+
264293 if (fragments .size () == 1 ) {
265294 final VariableDeclarationFragment fragment = fragments .get (0 );
266295 return Pair .of ((Name ) fragment .getName (), fragment .getInitializer ());
@@ -276,7 +305,7 @@ public static Pair<Name, Expression> decomposeInitializer(Expression init) {
276305 return Pair .empty ();
277306 }
278307
279- private static ForLoopContent getIndexOnIterable (final Expression condition , Name loopVariable ) {
308+ private static ForLoopContent getIndexOnIterable (final Expression condition , Name loopVariable , Long zero , Expression collectionOnSize , Expression arrayOnLength ) {
280309 final InfixExpression ie = ASTNodes .as (condition , InfixExpression .class );
281310
282311 if (ie != null && !ie .hasExtendedOperands ()) {
@@ -287,27 +316,45 @@ private static ForLoopContent getIndexOnIterable(final Expression condition, Nam
287316 return null ;
288317 }
289318
290- if (ASTNodes .hasOperator (ie , InfixExpression .Operator .LESS ) && ASTNodes .isSameLocalVariable (loopVariable , leftOp )) {
291- return buildForLoopContent ((Name ) loopVariable , rightOp );
292- } else if (ASTNodes .hasOperator (ie , InfixExpression .Operator .GREATER ) && ASTNodes .isSameLocalVariable (loopVariable , rightOp )) {
293- return buildForLoopContent ((Name ) loopVariable , leftOp );
319+ if (zero != null && zero == 0 ) {
320+ if (ASTNodes .hasOperator (ie , InfixExpression .Operator .LESS ) && ASTNodes .isSameLocalVariable (loopVariable , leftOp )) {
321+ return buildForLoopContent ((Name ) loopVariable , rightOp , zero , collectionOnSize , arrayOnLength );
322+ } else if (ASTNodes .hasOperator (ie , InfixExpression .Operator .GREATER ) && ASTNodes .isSameLocalVariable (loopVariable , rightOp )) {
323+ return buildForLoopContent ((Name ) loopVariable , leftOp , zero , collectionOnSize , arrayOnLength );
324+ }
325+ } else if (collectionOnSize != null || arrayOnLength != null ) {
326+ if (ASTNodes .hasOperator (ie , InfixExpression .Operator .GREATER_EQUALS ) && ASTNodes .isSameLocalVariable (loopVariable , leftOp )) {
327+ return buildForLoopContent ((Name ) loopVariable , rightOp , zero , collectionOnSize , arrayOnLength );
328+ } else if (ASTNodes .hasOperator (ie , InfixExpression .Operator .LESS_EQUALS ) && ASTNodes .isSameLocalVariable (loopVariable , rightOp )) {
329+ return buildForLoopContent ((Name ) loopVariable , leftOp , zero , collectionOnSize , arrayOnLength );
330+ }
294331 }
295332 }
296333
297334 return null ;
298335 }
299336
300- private static ForLoopContent buildForLoopContent (final Name loopVar , final Expression containerVar ) {
301- Expression collectionOnSize = getCollectionOnSize (containerVar );
337+ private static ForLoopContent buildForLoopContent (final Name loopVar , final Expression containerVar , Long zero , Expression collectionOnSize , Expression arrayOnLength ) {
338+ Long zero2 = ASTNodes .integerLiteral (containerVar );
339+ Expression collectionOnSize2 = getCollectionOnSize (containerVar );
340+ Expression arrayOnLength2 = getArrayOnLength (containerVar );
302341
303- if (collectionOnSize != null ) {
304- return ForLoopContent .indexedCollection (collectionOnSize , loopVar );
305- }
342+ if (zero != null && zero == 0 ) {
343+ if (collectionOnSize2 != null ) {
344+ return ForLoopContent .indexedCollection (collectionOnSize2 , loopVar , true );
345+ }
306346
307- Expression arrayOnLength = getArrayOnLength (containerVar );
347+ if (arrayOnLength2 != null ) {
348+ return ForLoopContent .indexedArray (arrayOnLength2 , loopVar , true );
349+ }
350+ } else if (zero2 != null && zero2 == 0 ) {
351+ if (collectionOnSize != null ) {
352+ return ForLoopContent .indexedCollection (collectionOnSize , loopVar , false );
353+ }
308354
309- if (arrayOnLength != null ) {
310- return ForLoopContent .indexedArray (arrayOnLength , loopVar );
355+ if (arrayOnLength != null ) {
356+ return ForLoopContent .indexedArray (arrayOnLength , loopVar , false );
357+ }
311358 }
312359
313360 return null ;
0 commit comments