    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.Collections;
    020import java.util.Iterator;
    021import java.util.function.Consumer;
    022import java.util.function.Function;
    023import java.util.function.Predicate;
    024import java.util.stream.Stream;
    025
    026import org.apache.commons.collections4.IteratorUtils;
    027
    028/**
    029 * Extends Iterator functionality to include operations commonly found on streams (e.g. filtering, concatenating, mapping). It also provides convenience methods
    030 * for common operations.
    031 *
    032 * @param <T> The type of object returned from the iterator.
    033 * @since 4.5.0-M3
    034 */
    035public final class ExtendedIterator<T> implements IteratorOperations<T> {
    036
    037    /**
    038     * Create an ExtendedIterator returning the elements of <code>it</code>. If <code>it</code> is itself an ExtendedIterator, return that; otherwise wrap
    039     * <code>it</code>.
    040     *
    041     * @param <T> The type of object returned from the iterator.
    042     * @param it The iterator to wrap.
    043     * @return An Extended iterator wrapping {@code it}
    044     */
    045    public static <T> ExtendedIterator<T> create(final Iterator<T> it) {
    046        return it instanceof ExtendedIterator<?> ? (ExtendedIterator<T>) it : new ExtendedIterator<>(it, false);
    047    }
    048
    049    /**
    050     * Creates an ExtendedIterator wrapped round a {@link Stream}. The extended iterator does not permit <code>.remove()</code>.
    051     * <p>
    052     * The stream should not be used directly. The effect of doing so is undefined.
    053     * </p>
    054     *
    055     * @param <T> The type of object returned from the iterator.
    056     * @param stream the Stream to create an iterator from.
    057     * @return an Extended iterator on the {@code stream} iterator.
    058     */
    059    public static <T> ExtendedIterator<T> create(final Stream<T> stream) {
    060        return new ExtendedIterator<>(stream.iterator(), true);
    061    }
    062
    063    /**
    064     * Creates an ExtendedIterator wrapped round <code>it</code>, which does not permit <code>.remove()</code> even if <code>it</code> does.
    065     *
    066     * @param <T> The type of object returned from the iterator.
    067     * @param it The Iterator to wrap.
    068     * @return an Extended iterator on {@code it}
    069     * @throws UnsupportedOperationException if remove() is called on the resulting iterator.
    070     */
    071    public static <T> ExtendedIterator<T> createNoRemove(final Iterator<T> it) {
    072        return new ExtendedIterator<>(it, true);
    073    }
    074
    075    /**
    076     * Creates an empty Extended iterator.
    077     *
    078     * @return An empty Extended iterator.
    079     */
    080    public static ExtendedIterator<?> emptyIterator() {
    081        return new ExtendedIterator<>(Collections.emptyIterator(), false);
    082    }
    083
    084    /**
    085     * Flattens an iterator of iterators into an Iterator over the next level values. Similar to list splicing in lisp.
    086     *
    087     * @param <T> The type of object returned from the iterator.
    088     * @param iterators An iterator of iterators.
    089     * @return An iterator over the logical concatenation of the inner iterators.
    090     */
    091    public static <T> ExtendedIterator<T> flatten(final Iterator<Iterator<T>> iterators) {
    092        return create(IteratorUtils.chainedIterator(iterators));
    093    }
    094
    095    /**
    096     * Set to <code>true</code> if this wrapping doesn't permit the use of {@link #remove()}, otherwise removal is delegated to the base iterator.
    097     */
    098    private final boolean throwOnRemove;
    099
    100    /** The base iterator that we wrap */
    101    private final Iterator<? extends T> base;
    102
    103    /**
    104     * Initialize this wrapping with the given base iterator and remove-control.
    105     *
    106     * @param base          the base iterator that this iterator wraps
    107     * @param throwOnRemove true if .remove() must throw an exception
    108     */
    109    private ExtendedIterator(final Iterator<? extends T> base, final boolean throwOnRemove) {
    110        this.base = base;
    111        this.throwOnRemove = throwOnRemove;
    112    }
    113
    114    /**
    115     * Chains the {@code other} iterator to the end of this one.
    116     *
    117     * @param other the other iterator to extend this iterator with.
    118     * @return A new iterator returning the contents of {@code this} iterator followed by the contents of {@code other} iterator.
    119     * @param <X> The type of object returned from the other iterator.
    120     */
    121    public <X extends T> ExtendedIterator<T> andThen(final Iterator<X> other) {
    122        if (base instanceof IteratorChain) {
    123            ((IteratorChain<T>) base).addIterator(other);
    124            return this;
    125        }
    126        return new ExtendedIterator<>(new IteratorChain<>(base, other), throwOnRemove);
    127    }
    128
    129    /**
    130     * Filter this iterator using a predicate. Only items for which the predicate returns {@code true} will be included in the result.
    131     *
    132     * @param predicate The predicate to filter the items with.
    133     * @return An iterator filtered by the predicate.
    134     */
    135    public ExtendedIterator<T> filter(final Predicate<T> predicate) {
    136        return new ExtendedIterator<>(new FilterIterator<>(this, predicate::test), throwOnRemove);
    137    }
    138
    139    @Override
    140    public void forEachRemaining(final Consumer<? super T> action) {
    141        base.forEachRemaining(action);
    142    }
    143
    144    @Override
    145    public boolean hasNext() {
    146        return base.hasNext();
    147    }
    148
    149    /**
    150     * Map the elements of the iterator to a now type.
    151     *
    152     * @param function The function to map elements of {@code <T>} to type {@code <U>}.
    153     * @return An Extended iterator that returns a {@code <U>} for very {@code <T>} in the original iterator.
    154     * @param <U> The object type to return.
    155     */
    156    public <U> ExtendedIterator<U> map(final Function<T, U> function) {
    157        return new ExtendedIterator<>(new TransformIterator<>(this, function::apply), false);
    158    }
    159
    160    @Override
    161    public T next() {
    162        return base.next();
    163    }
    164
    165    @Override
    166    public void remove() {
    167        if (throwOnRemove) {
    168            throw new UnsupportedOperationException();
    169        }
    170        base.remove();
    171    }
    172}