org.sonar.core.util.CloseableIterator.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.core.util.CloseableIterator.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2017 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.core.util;

import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.ArrayUtils;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

public abstract class CloseableIterator<O> implements Iterator<O>, AutoCloseable {
    private O nextObject = null;
    boolean isClosed = false;
    private static final CloseableIterator<?> EMPTY_CLOSEABLE_ITERATOR = new CloseableIterator<Object>() {
        @Override
        public boolean hasNext() {
            return false;
        }

        @Override
        protected Object doNext() {
            // never called anyway
            throw new NoSuchElementException("Empty closeable Iterator has no element");
        }

        @Override
        protected void doClose() throws Exception {
            // do nothing
        }
    };

    @SuppressWarnings("unchecked")
    public static <T> CloseableIterator<T> emptyCloseableIterator() {
        return (CloseableIterator<T>) EMPTY_CLOSEABLE_ITERATOR;
    }

    /**
     * Creates a CloseableIterator from a regular {@link Iterator}.
     *
     * @throws IllegalArgumentException if the specified {@link Iterator} is a CloseableIterator
     */
    public static <T> CloseableIterator<T> from(Iterator<T> iterator) {
        // early fail
        requireNonNull(iterator);
        checkArgument(!(iterator instanceof AutoCloseable),
                "This method does not support creating a CloseableIterator from an Iterator which is Closeable");
        return new RegularIteratorWrapper<>(iterator);
    }

    /**
     * Wraps a {@code CloseableIterator} and optionally other instances of {@code AutoCloseable} that must be closed
     * at the same time. The wrapped iterator is closed first then the other {@code AutoCloseable} in the defined order.
     * 
     * @throws IllegalArgumentException if the parameter {@code otherCloseables} contains the wrapped iterator
     */
    public static <T> CloseableIterator<T> wrap(CloseableIterator<T> iterator, AutoCloseable... otherCloseables) {
        return new CloseablesIteratorWrapper<>(iterator, otherCloseables);
    }

    @Override
    public boolean hasNext() {
        // Optimization to not call bufferNext() when already closed
        if (isClosed) {
            return false;
        }
        boolean hasNext = nextObject != null || bufferNext() != null;
        if (!hasNext) {
            close();
        }
        return hasNext;
    }

    private O bufferNext() {
        try {
            nextObject = doNext();
            return nextObject;
        } catch (RuntimeException e) {
            close();
            throw e;
        }
    }

    /**
     * Reads next item and returns {@code null} if no more items.
     */
    @CheckForNull
    protected abstract O doNext();

    @Override
    public O next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        O result = nextObject;
        nextObject = null;
        return result;
    }

    @Override
    public final void remove() {
        try {
            doRemove();
        } catch (RuntimeException e) {
            close();
            throw e;
        }
    }

    /**
     * By default it throws an UnsupportedOperationException. Override this method
     * to change behavior.
     */
    protected void doRemove() {
        throw new UnsupportedOperationException(
                "remove() is not supported by default. Override doRemove() if needed.");
    }

    /**
     * Do not declare "throws IOException"
     */
    @Override
    public final void close() {
        try {
            doClose();
            isClosed = true;
        } catch (Exception e) {
            Throwables.propagate(e);
        }
    }

    protected abstract void doClose() throws Exception;

    private static class RegularIteratorWrapper<T> extends CloseableIterator<T> {
        private final Iterator<T> iterator;

        public RegularIteratorWrapper(Iterator<T> iterator) {
            this.iterator = iterator;
        }

        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }

        @Override
        public T next() {
            return iterator.next();
        }

        @Override
        protected T doNext() {
            throw new UnsupportedOperationException("hasNext has been override, doNext is never called");
        }

        @Override
        protected void doClose() throws Exception {
            // do nothing
        }
    }

    private static class CloseablesIteratorWrapper<T> extends CloseableIterator<T> {
        private final CloseableIterator<T> iterator;
        private final List<AutoCloseable> otherCloseables;

        private CloseablesIteratorWrapper(CloseableIterator<T> iterator, AutoCloseable... otherCloseables) {
            requireNonNull(iterator);
            checkArgument(!ArrayUtils.contains(otherCloseables, iterator));
            this.iterator = iterator;
            // the advantage of using ImmutableList is that it does not accept null elements, so it fails fast, during
            // construction of the wrapper, but not in close()
            this.otherCloseables = ImmutableList.copyOf(otherCloseables);
        }

        @Override
        protected T doNext() {
            return iterator.hasNext() ? iterator.next() : null;
        }

        @Override
        protected void doClose() throws Exception {
            // iterator can be already closed by doNext(), but closing here ensures
            // that iterator is closed when it is not fully traversed.
            iterator.close();

            for (AutoCloseable otherCloseable : otherCloseables) {
                otherCloseable.close();
            }
        }
    }
}