001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.iterators;
018
019import java.util.NoSuchElementException;
020
021import org.apache.commons.collections4.ResettableListIterator;
022
023/**
024 * <code>SingletonIterator</code> is an {@link java.util.ListIterator} over a single
025 * object instance.
026 *
027 * @since 2.1
028 */
029public class SingletonListIterator<E> implements ResettableListIterator<E> {
030
031    private boolean beforeFirst = true;
032    private boolean nextCalled = false;
033    private boolean removed = false;
034    private E object;
035
036    /**
037     * Constructs a new <code>SingletonListIterator</code>.
038     *
039     * @param object  the single object to return from the iterator
040     */
041    public SingletonListIterator(final E object) {
042        super();
043        this.object = object;
044    }
045
046    /**
047     * Is another object available from the iterator?
048     * <p>
049     * This returns true if the single object hasn't been returned yet.
050     *
051     * @return true if the single object hasn't been returned yet
052     */
053    @Override
054    public boolean hasNext() {
055        return beforeFirst && !removed;
056    }
057
058    /**
059     * Is a previous object available from the iterator?
060     * <p>
061     * This returns true if the single object has been returned.
062     *
063     * @return true if the single object has been returned
064     */
065    @Override
066    public boolean hasPrevious() {
067        return !beforeFirst && !removed;
068    }
069
070    /**
071     * Returns the index of the element that would be returned by a subsequent
072     * call to {@code next}.
073     *
074     * @return 0 or 1 depending on current state.
075     */
076    @Override
077    public int nextIndex() {
078        return beforeFirst ? 0 : 1;
079    }
080
081    /**
082     * Returns the index of the element that would be returned by a subsequent
083     * call to {@code previous}. A return value of -1 indicates that the iterator is currently at
084     * the start.
085     *
086     * @return 0 or -1 depending on current state.
087     */
088    @Override
089    public int previousIndex() {
090        return beforeFirst ? -1 : 0;
091    }
092
093    /**
094     * Get the next object from the iterator.
095     * <p>
096     * This returns the single object if it hasn't been returned yet.
097     *
098     * @return the single object
099     * @throws NoSuchElementException if the single object has already
100     *    been returned
101     */
102    @Override
103    public E next() {
104        if (!beforeFirst || removed) {
105            throw new NoSuchElementException();
106        }
107        beforeFirst = false;
108        nextCalled = true;
109        return object;
110    }
111
112    /**
113     * Get the previous object from the iterator.
114     * <p>
115     * This returns the single object if it has been returned.
116     *
117     * @return the single object
118     * @throws NoSuchElementException if the single object has not already
119     *    been returned
120     */
121    @Override
122    public E previous() {
123        if (beforeFirst || removed) {
124            throw new NoSuchElementException();
125        }
126        beforeFirst = true;
127        return object;
128    }
129
130    /**
131     * Remove the object from this iterator.
132     * @throws IllegalStateException if the {@code next} or {@code previous}
133     *        method has not yet been called, or the {@code remove} method
134     *        has already been called after the last call to {@code next}
135     *        or {@code previous}.
136     */
137    @Override
138    public void remove() {
139        if(!nextCalled || removed) {
140            throw new IllegalStateException();
141        }
142        object = null;
143        removed = true;
144    }
145
146    /**
147     * Add always throws {@link UnsupportedOperationException}.
148     *
149     * @param obj  the object to add
150     * @throws UnsupportedOperationException always
151     */
152    @Override
153    public void add(final E obj) {
154        throw new UnsupportedOperationException("add() is not supported by this iterator");
155    }
156
157    /**
158     * Set sets the value of the singleton.
159     *
160     * @param obj  the object to set
161     * @throws IllegalStateException if {@code next} has not been called
162     *          or the object has been removed
163     */
164    @Override
165    public void set(final E obj) {
166        if (!nextCalled || removed) {
167            throw new IllegalStateException();
168        }
169        this.object = obj;
170    }
171
172    /**
173     * Reset the iterator back to the start.
174     */
175    @Override
176    public void reset() {
177        beforeFirst = true;
178        nextCalled = false;
179    }
180
181}