    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.Iterator;
    020import java.util.Map;
    021
    022import org.apache.commons.collections4.MapIterator;
    023import org.apache.commons.collections4.ResettableIterator;
    024
    025/**
    026 * Implements a <code>MapIterator</code> using a Map entrySet.
    027 * Reverse iteration is not supported.
    028 * <pre>
    029 * MapIterator it = map.mapIterator();
    030 * while (it.hasNext()) {
    031 *   Object key = it.next();
    032 *   Object value = it.getValue();
    033 *   it.setValue(newValue);
    034 * }
    035 * </pre>
    036 *
    037 * @since 3.0
    038 */
    039public class EntrySetMapIterator<K, V> implements MapIterator<K, V>, ResettableIterator<K> {
    040
    041    private final Map<K, V> map;
    042    private Iterator<Map.Entry<K, V>> iterator;
    043    private Map.Entry<K, V> last;
    044    private boolean canRemove = false;
    045
    046    /**
    047     * Constructor.
    048     *
    049     * @param map  the map to iterate over
    050     */
    051    public EntrySetMapIterator(final Map<K, V> map) {
    052        super();
    053        this.map = map;
    054        this.iterator = map.entrySet().iterator();
    055    }
    056
    057    //-----------------------------------------------------------------------
    058    /**
    059     * Checks to see if there are more entries still to be iterated.
    060     *
    061     * @return <code>true</code> if the iterator has more elements
    062     */
    063    @Override
    064    public boolean hasNext() {
    065        return iterator.hasNext();
    066    }
    067
    068    /**
    069     * Gets the next <em>key</em> from the <code>Map</code>.
    070     *
    071     * @return the next key in the iteration
    072     * @throws java.util.NoSuchElementException if the iteration is finished
    073     */
    074    @Override
    075    public K next() {
    076        last = iterator.next();
    077        canRemove = true;
    078        return last.getKey();
    079    }
    080
    081    //-----------------------------------------------------------------------
    082    /**
    083     * Removes the last returned key from the underlying <code>Map</code>.
    084     * <p>
    085     * This method can be called once per call to <code>next()</code>.
    086     *
    087     * @throws UnsupportedOperationException if remove is not supported by the map
    088     * @throws IllegalStateException if <code>next()</code> has not yet been called
    089     * @throws IllegalStateException if <code>remove()</code> has already been called
    090     *  since the last call to <code>next()</code>
    091     */
    092    @Override
    093    public void remove() {
    094        if (canRemove == false) {
    095            throw new IllegalStateException("Iterator remove() can only be called once after next()");
    096        }
    097        iterator.remove();
    098        last = null;
    099        canRemove = false;
    100    }
    101
    102    //-----------------------------------------------------------------------
    103    /**
    104     * Gets the current key, which is the key returned by the last call
    105     * to <code>next()</code>.
    106     *
    107     * @return the current key
    108     * @throws IllegalStateException if <code>next()</code> has not yet been called
    109     */
    110    @Override
    111    public K getKey() {
    112        if (last == null) {
    113            throw new IllegalStateException("Iterator getKey() can only be called after next() and before remove()");
    114        }
    115        return last.getKey();
    116    }
    117
    118    /**
    119     * Gets the current value, which is the value associated with the last key
    120     * returned by <code>next()</code>.
    121     *
    122     * @return the current value
    123     * @throws IllegalStateException if <code>next()</code> has not yet been called
    124     */
    125    @Override
    126    public V getValue() {
    127        if (last == null) {
    128            throw new IllegalStateException("Iterator getValue() can only be called after next() and before remove()");
    129        }
    130        return last.getValue();
    131    }
    132
    133    /**
    134     * Sets the value associated with the current key.
    135     *
    136     * @param value  the new value
    137     * @return the previous value
    138     * @throws UnsupportedOperationException if setValue is not supported by the map
    139     * @throws IllegalStateException if <code>next()</code> has not yet been called
    140     * @throws IllegalStateException if <code>remove()</code> has been called since the
    141     *  last call to <code>next()</code>
    142     */
    143    @Override
    144    public V setValue(final V value) {
    145        if (last == null) {
    146            throw new IllegalStateException("Iterator setValue() can only be called after next() and before remove()");
    147        }
    148        return last.setValue(value);
    149    }
    150
    151    //-----------------------------------------------------------------------
    152    /**
    153     * Resets the state of the iterator.
    154     */
    155    @Override
    156    public void reset() {
    157        iterator = map.entrySet().iterator();
    158        last = null;
    159        canRemove = false;
    160    }
    161
    162    /**
    163     * Gets the iterator as a String.
    164     *
    165     * @return a string version of the iterator
    166     */
    167    @Override
    168    public String toString() {
    169        if (last != null) {
    170            return "MapIterator[" + getKey() + "=" + getValue() + "]";
    171        }
    172        return "MapIterator[]";
    173    }
    174
    175}