Add IOIterator
MODIFY
src/changes/changes.xml
src/changes/changes.xml
@@ -425,7 +425,7 @@ The <action> type attribute can be add,update,fix,remove.
Add PathUtils.getLastModifiedFileTime(*).
</action>
<action dev="ggregory" type="add" due-to="Gary Gregory">
- Add IOBiFunction, IOTriFunction, IOQuadFunction, IOPredicate, FilesUncheck.
+ Add IOBiFunction, IOTriFunction, IOQuadFunction, IOPredicate, IOIterator, FilesUncheck.
</action>
<action dev="ggregory" type="add" due-to="Gary Gregory">
Add IOUtils.consume(Reader).

ADD
src/main/java/org/apache/commons/io/function/IOIterator.java
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.io.function;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Like {@link Iterator} but throws {@link IOException}.
+ *
+ * @param <E> the type of elements returned by this iterator.
+ */
+public interface IOIterator<E> {
+
+ /**
+ * Adapts the given Iterator as an IOIterator.
+ *
+ * @param <E> the type of the stream elements.
+ * @param iterator The iterator to adapt
+ * @return A new IOIterator
+ */
+ static <E> IOIterator<E> adapt(final Iterator<E> iterator) {
+ return IOIteratorAdapter.adapt(iterator);
+ }
+
+ /**
+ * Creates an {@link Iterator} for this instance that throws {@link UncheckedIOException} instead of
+ * {@link IOException}.
+ *
+ * @return an {@link UncheckedIOException} {@link Iterator}.
+ */
+ default Iterator<E> asIterator() {
+ return new UncheckedIOIterator<>(this);
+ }
+
+ /**
+ * Like {@link Iterator#forEachRemaining(Consumer)}.
+ *
+ * @param action See delegate.
+ * @throws IOException if an I/O error occurs.
+ */
+ default void forEachRemaining(final IOConsumer<? super E> action) throws IOException {
+ Objects.requireNonNull(action);
+ while (hasNext()) {
+ action.accept(next());
+ }
+ }
+
+ /**
+ * Like {@link Iterator#hasNext()}.
+ *
+ * @return See delegate.
+ * @throws IOException if an I/O error occurs.
+ */
+ boolean hasNext() throws IOException;
+
+ /**
+ * Like {@link Iterator#next()}.
+ *
+ * @return See delegate.
+ * @throws IOException if an I/O error occurs.
+ * @throws NoSuchElementException if the iteration has no more elements
+ */
+ E next() throws IOException;
+
+ /**
+ * Like {@link Iterator#remove()}.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @SuppressWarnings("unused")
+ default void remove() throws IOException {
+ unwrap().remove();
+ }
+
+ /**
+ * Unwraps this instance and returns the underlying {@link Iterator}.
+ * <p>
+ * Implementations may not have anything to unwrap and that behavior is undefined for now.
+ * </p>
+ * @return the underlying Iterator.
+ */
+ Iterator<E> unwrap();
+
+}

ADD
src/main/java/org/apache/commons/io/function/IOIteratorAdapter.java
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.io.function;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Objects;
+
+/**
+ * Adapts an {@link Iterator} as an {@link IOIterator}.
+ *
+ * @param <E> the type of the stream elements.
+ */
+final class IOIteratorAdapter<E> implements IOIterator<E> {
+
+ static <E> IOIteratorAdapter<E> adapt(final Iterator<E> delegate) {
+ return new IOIteratorAdapter<>(delegate);
+ }
+
+ private final Iterator<E> delegate;
+
+ IOIteratorAdapter(final Iterator<E> delegate) {
+ this.delegate = Objects.requireNonNull(delegate, "delegate");
+ }
+
+ @Override
+ public boolean hasNext() throws IOException {
+ return delegate.hasNext();
+ }
+
+ @Override
+ public E next() throws IOException {
+ return delegate.next();
+ }
+
+ @Override
+ public Iterator<E> unwrap() {
+ return delegate;
+ }
+
+}

ADD
src/main/java/org/apache/commons/io/function/UncheckedIOIterator.java
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.io.function;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Iterator;
+import java.util.Objects;
+
+/**
+ * An {@link Iterator} for a {@link IOIterator} that throws {@link UncheckedIOException} instead of {@link IOException}.
+ *
+ * Keep package-private for now.
+ *
+ * @param <E> the type of elements returned by this iterator.
+ */
+final class UncheckedIOIterator<E> implements Iterator<E> {
+
+ private final IOIterator<E> delegate;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param delegate The delegate
+ */
+ UncheckedIOIterator(final IOIterator<E> delegate) {
+ this.delegate = Objects.requireNonNull(delegate, "delegate");
+ }
+
+ @Override
+ public boolean hasNext() {
+ return Uncheck.get(delegate::hasNext);
+ }
+
+ @Override
+ public E next() {
+ return Uncheck.get(delegate::next);
+ }
+
+ @Override
+ public void remove() {
+ Uncheck.run(delegate::remove);
+ }
+}

ADD
src/test/java/org/apache/commons/io/function/IOIteratorAdapterTest.java
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.io.function;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@code IOIteratorAdapter}.
+ */
+public class IOIteratorAdapterTest {
+
+ private IOIteratorAdapter<Path> iterator;
+
+ @BeforeEach
+ public void beforeEach() {
+ iterator = IOIteratorAdapter.adapt(newPathList().iterator());
+ }
+
+ private List<Path> newPathList() {
+ return Arrays.asList(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B);
+ }
+
+ @Test
+ public void testAdapt() throws IOException {
+ iterator = IOIteratorAdapter.adapt(newPathList().iterator());
+ assertEquals(TestConstants.ABS_PATH_A, iterator.next());
+ }
+
+ @Test
+ public void testAsIterator() {
+ assertEquals(TestConstants.ABS_PATH_A, iterator.asIterator().next());
+ }
+
+ @Test
+ public void testForEachRemaining() throws IOException {
+ final List<Path> list = new ArrayList<>();
+ iterator.forEachRemaining(p -> list.add(p.toRealPath()));
+ assertFalse(iterator.hasNext());
+ assertEquals(newPathList(), list);
+ }
+
+ @Test
+ public void testHasNext() throws IOException {
+ assertTrue(iterator.hasNext());
+ iterator.forEachRemaining(Path::toRealPath);
+ assertFalse(iterator.hasNext());
+ }
+
+ @Test
+ public void testNext() throws IOException {
+ assertEquals(TestConstants.ABS_PATH_A, iterator.next());
+ }
+
+ @Test
+ public void testRemove() throws IOException {
+ assertThrows(IllegalStateException.class, iterator::remove);
+ iterator.next();
+ assertThrows(UnsupportedOperationException.class, iterator::remove);
+ }
+
+}

ADD
src/test/java/org/apache/commons/io/function/IOIteratorTest.java
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.io.function;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link IOIterator}.
+ */
+public class IOIteratorTest {
+
+ private IOIterator<Path> iterator;
+
+ @BeforeEach
+ public void beforeEach() {
+ iterator = IOIterator.adapt(newPathList().iterator());
+ }
+
+ private List<Path> newPathList() {
+ return Arrays.asList(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B);
+ }
+
+ @Test
+ public void testAdapt() throws IOException {
+ iterator = IOIterator.adapt(newPathList().iterator());
+ assertEquals(TestConstants.ABS_PATH_A, iterator.next());
+ }
+
+ @Test
+ public void testAsIterator() {
+ final Iterator<Path> asIterator = iterator.asIterator();
+ assertTrue(asIterator.hasNext());
+ assertEquals(TestConstants.ABS_PATH_A, asIterator.next());
+ assertThrows(UnsupportedOperationException.class, asIterator::remove);
+ }
+
+ @Test
+ public void testForEachRemaining() throws IOException {
+ final List<Path> list = new ArrayList<>();
+ iterator.forEachRemaining(p -> list.add(p.toRealPath()));
+ assertFalse(iterator.hasNext());
+ assertEquals(newPathList(), list);
+ }
+
+ @Test
+ public void testHasNext() throws IOException {
+ assertTrue(iterator.hasNext());
+ iterator.forEachRemaining(Path::toRealPath);
+ assertFalse(iterator.hasNext());
+ }
+
+ @Test
+ public void testNext() throws IOException {
+ assertEquals(TestConstants.ABS_PATH_A, iterator.next());
+ }
+
+ @Test
+ public void testRemove() throws IOException {
+ assertThrows(IllegalStateException.class, iterator::remove);
+ assertThrows(IllegalStateException.class, iterator::remove);
+ iterator.next();
+ assertThrows(UnsupportedOperationException.class, iterator::remove);
+ assertThrows(UnsupportedOperationException.class, iterator::remove);
+ }
+
+}

Add IOIterator