1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.collections4.iterators; 18 19 import java.util.List; 20 import java.util.ListIterator; 21 import java.util.Objects; 22 23 import org.apache.commons.collections4.ResettableListIterator; 24 25 /** 26 * Iterates backwards through a List, starting with the last element 27 * and continuing to the first. This is useful for looping around 28 * a list in reverse order without needing to actually reverse the list. 29 * <p> 30 * The first call to {@code next()} will return the last element 31 * from the list, and so on. The {@code hasNext()} method works 32 * in concert with the {@code next()} method as expected. 33 * However, the {@code nextIndex()} method returns the correct 34 * index in the list, thus it starts high and reduces as the iteration 35 * continues. The previous methods work similarly. 36 * </p> 37 * 38 * @param <E> the type of elements returned by this iterator. 39 * @since 3.2 40 */ 41 public class ReverseListIterator<E> implements ResettableListIterator<E> { 42 43 /** The list being wrapped. */ 44 private final List<E> list; 45 /** The list iterator being wrapped. */ 46 private ListIterator<E> iterator; 47 /** Flag to indicate if updating is possible at the moment. */ 48 private boolean validForUpdate = true; 49 50 /** 51 * Constructor that wraps a list. 52 * 53 * @param list the list to create a reversed iterator for 54 * @throws NullPointerException if the list is null 55 */ 56 public ReverseListIterator(final List<E> list) { 57 this.list = Objects.requireNonNull(list, "list"); 58 iterator = list.listIterator(list.size()); 59 } 60 61 /** 62 * Adds a new element to the list between the next and previous elements. 63 * 64 * @param obj the object to add 65 * @throws UnsupportedOperationException if the list is unmodifiable 66 * @throws IllegalStateException if the iterator is not in a valid state for set 67 */ 68 @Override 69 public void add(final E obj) { 70 // the validForUpdate flag is needed as the necessary previous() 71 // method call re-enables remove and add 72 if (!validForUpdate) { 73 throw new IllegalStateException("Cannot add to list until next() or previous() called"); 74 } 75 validForUpdate = false; 76 iterator.add(obj); 77 iterator.previous(); 78 } 79 80 /** 81 * Checks whether there is another element. 82 * 83 * @return true if there is another element 84 */ 85 @Override 86 public boolean hasNext() { 87 return iterator.hasPrevious(); 88 } 89 90 /** 91 * Checks whether there is a previous element. 92 * 93 * @return true if there is a previous element 94 */ 95 @Override 96 public boolean hasPrevious() { 97 return iterator.hasNext(); 98 } 99 100 /** 101 * Gets the next element. 102 * The next element is the previous in the list. 103 * 104 * @return the next element in the iterator 105 */ 106 @Override 107 public E next() { 108 final E obj = iterator.previous(); 109 validForUpdate = true; 110 return obj; 111 } 112 113 /** 114 * Gets the index of the next element. 115 * 116 * @return the index of the next element in the iterator 117 */ 118 @Override 119 public int nextIndex() { 120 return iterator.previousIndex(); 121 } 122 123 /** 124 * Gets the previous element. 125 * The next element is the previous in the list. 126 * 127 * @return the previous element in the iterator 128 */ 129 @Override 130 public E previous() { 131 final E obj = iterator.next(); 132 validForUpdate = true; 133 return obj; 134 } 135 136 /** 137 * Gets the index of the previous element. 138 * 139 * @return the index of the previous element in the iterator 140 */ 141 @Override 142 public int previousIndex() { 143 return iterator.nextIndex(); 144 } 145 146 /** 147 * Removes the last returned element. 148 * 149 * @throws UnsupportedOperationException if the list is unmodifiable 150 * @throws IllegalStateException if there is no element to remove 151 */ 152 @Override 153 public void remove() { 154 if (!validForUpdate) { 155 throw new IllegalStateException("Cannot remove from list until next() or previous() called"); 156 } 157 iterator.remove(); 158 } 159 160 /** 161 * Resets the iterator back to the start (which is the 162 * end of the list as this is a reversed iterator) 163 */ 164 @Override 165 public void reset() { 166 iterator = list.listIterator(list.size()); 167 } 168 169 /** 170 * Replaces the last returned element. 171 * 172 * @param obj the object to set 173 * @throws UnsupportedOperationException if the list is unmodifiable 174 * @throws IllegalStateException if the iterator is not in a valid state for set 175 */ 176 @Override 177 public void set(final E obj) { 178 if (!validForUpdate) { 179 throw new IllegalStateException("Cannot set to list until next() or previous() called"); 180 } 181 iterator.set(obj); 182 } 183 184 }