You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/09/13 00:20:24 UTC

[commons-io] 01/02: Add IOSpliterator

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git

commit 93228aa10a06b6cb3f38c18aecd0c5eea867a16c
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Sep 12 17:10:56 2022 -0700

    Add IOSpliterator
---
 src/changes/changes.xml                            |   2 +-
 .../apache/commons/io/function/IOSpliterator.java  | 146 +++++++++++++++++++++
 .../commons/io/function/IOSpliteratorAdapter.java  |  45 +++++++
 .../commons/io/function/IOSpliteratorTest.java     | 136 +++++++++++++++++++
 4 files changed, 328 insertions(+), 1 deletion(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 3b8c4d9a..9d950914 100644
--- a/src/changes/changes.xml
+++ b/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, IOIterator, FilesUncheck.
+        Add IOBiFunction, IOTriFunction, IOQuadFunction, IOPredicate, IOIterator, IOSpliterator, FilesUncheck.
       </action>
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Add IOUtils.consume(Reader).
diff --git a/src/main/java/org/apache/commons/io/function/IOSpliterator.java b/src/main/java/org/apache/commons/io/function/IOSpliterator.java
new file mode 100644
index 00000000..550f13a4
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOSpliterator.java
@@ -0,0 +1,146 @@
+/*
+ * 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.Objects;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+/**
+ * Like {@link Spliterator} but throws {@link IOException}.
+ *
+ * @param <T> the type of elements returned by this IOSpliterator.
+ * @since 2.12.0
+ */
+public interface IOSpliterator<T> {
+
+    /**
+     * Adapts the given Spliterator as an IOSpliterator.
+     *
+     * @param <E> the type of the stream elements.
+     * @param iterator The iterator to adapt
+     * @return A new IOSpliterator
+     */
+    static <E> IOSpliterator<E> adapt(final Spliterator<E> iterator) {
+        return IOSpliteratorAdapter.adapt(iterator);
+    }
+
+    /**
+     * Creates a {@link Spliterator} for this instance that throws {@link UncheckedIOException} instead of
+     * {@link IOException}.
+     *
+     * @return an {@link UncheckedIOException} {@link Spliterator}.
+     */
+    default Spliterator<T> asSpliterator() {
+        return new UncheckedIOSpliterator<>(this);
+    }
+
+    /**
+     * Like {@link Spliterator#characteristics()}.
+     *
+     * @return a representation of characteristics
+     */
+    default int characteristics() {
+        return unwrap().characteristics();
+    }
+
+    /**
+     * Like {@link Spliterator#estimateSize()}.
+     *
+     *
+     * @return the estimated size, or {@code Long.MAX_VALUE} if infinite, unknown, or too expensive to compute.
+     */
+    default long estimateSize() {
+        return unwrap().estimateSize();
+    }
+
+    /**
+     * Like {@link Spliterator#forEachRemaining(Consumer)}.
+     *
+     * @param action The action
+     * @throws NullPointerException if the specified action is null
+     */
+    default void forEachRemaining(final IOConsumer<? super T> action) {
+        while (tryAdvance(action)) { // NOPMD
+        }
+    }
+
+    /**
+     * Like {@link Spliterator#getComparator()}.
+     *
+     * @return a Comparator, or {@code null} if the elements are sorted in the natural order.
+     * @throws IllegalStateException if the spliterator does not report a characteristic of {@code SORTED}.
+     */
+    @SuppressWarnings("unchecked")
+    default IOComparator<? super T> getComparator() {
+        return (IOComparator<T>) unwrap().getComparator();
+    }
+
+    /**
+     * Like {@link Spliterator#getExactSizeIfKnown()}.
+     *
+     * @return the exact size, if known, else {@code -1}.
+     */
+    default long getExactSizeIfKnown() {
+        return unwrap().getExactSizeIfKnown();
+    }
+
+    /**
+     * Like {@link Spliterator#hasCharacteristics(int)}.
+     *
+     * @param characteristics the characteristics to check for
+     * @return {@code true} if all the specified characteristics are present, else {@code false}
+     */
+    default boolean hasCharacteristics(final int characteristics) {
+        return unwrap().hasCharacteristics(characteristics);
+    }
+
+    /**
+     * Like {@link Spliterator#tryAdvance(Consumer)}.
+     *
+     * @param action The action
+     * @return {@code false} if no remaining elements existed upon entry to this method, else {@code true}.
+     * @throws NullPointerException if the specified action is null
+     */
+    default boolean tryAdvance(IOConsumer<? super T> action) {
+        return unwrap().tryAdvance(Objects.requireNonNull(action, "action").asConsumer());
+    }
+
+    /**
+     * Like {@link Spliterator#trySplit()}.
+     *
+     * @return a {@code Spliterator} covering some portion of the elements, or {@code null} if this spliterator cannot be
+     *         split
+     */
+    default IOSpliterator<T> trySplit() {
+        return adapt(unwrap().trySplit());
+    }
+
+    /**
+     * Unwraps this instance and returns the underlying {@link Spliterator}.
+     * <p>
+     * Implementations may not have anything to unwrap and that behavior is undefined for now.
+     * </p>
+     *
+     * @return the underlying Spliterator.
+     */
+    Spliterator<T> unwrap();
+
+}
diff --git a/src/main/java/org/apache/commons/io/function/IOSpliteratorAdapter.java b/src/main/java/org/apache/commons/io/function/IOSpliteratorAdapter.java
new file mode 100644
index 00000000..8c3101c1
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOSpliteratorAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.util.Objects;
+import java.util.Spliterator;
+
+/**
+ * Adapts an {@link Spliterator} as an {@link IOSpliterator}.
+ *
+ * @param <T> the type of the stream elements.
+ */
+final class IOSpliteratorAdapter<T> implements IOSpliterator<T> {
+
+    static <E> IOSpliteratorAdapter<E> adapt(final Spliterator<E> delegate) {
+        return new IOSpliteratorAdapter<>(delegate);
+    }
+
+    private final Spliterator<T> delegate;
+
+    IOSpliteratorAdapter(final Spliterator<T> delegate) {
+        this.delegate = Objects.requireNonNull(delegate, "delegate");
+    }
+
+    @Override
+    public Spliterator<T> unwrap() {
+        return delegate;
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/io/function/IOSpliteratorTest.java b/src/test/java/org/apache/commons/io/function/IOSpliteratorTest.java
new file mode 100644
index 00000000..e3cf2764
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/function/IOSpliteratorTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+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 java.util.Spliterator;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link IOSpliterator}.
+ */
+public class IOSpliteratorTest {
+
+    private IOSpliterator<Path> spliterator;
+
+    @BeforeEach
+    public void beforeEach() {
+        spliterator = IOSpliterator.adapt(newPathList().spliterator());
+    }
+
+    private List<Path> newPathList() {
+        return Arrays.asList(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_B);
+    }
+
+    @Test
+    public void testAdapt() {
+        spliterator = IOSpliterator.adapt(newPathList().spliterator());
+        assertEquals(2, spliterator.estimateSize());
+    }
+
+    @Test
+    public void testAsSpliterator() {
+        assertEquals(2, spliterator.estimateSize());
+        assertEquals(2, spliterator.asSpliterator().estimateSize());
+    }
+
+    @Test
+    public void testCharacteristics() {
+        assertEquals(spliterator.unwrap().characteristics(), spliterator.characteristics());
+        assertEquals(spliterator.unwrap().characteristics(), spliterator.asSpliterator().characteristics());
+    }
+
+    @Test
+    public void testEstimateSize() {
+        assertEquals(2, spliterator.estimateSize());
+        assertEquals(spliterator.unwrap().estimateSize(), spliterator.estimateSize());
+        assertEquals(spliterator.unwrap().estimateSize(), spliterator.asSpliterator().estimateSize());
+    }
+
+    @Test
+    public void testForEachRemaining() {
+        final List<Path> list = new ArrayList<>();
+        spliterator.forEachRemaining(list::add);
+        assertEquals(2, list.size());
+        assertEquals(newPathList(), list);
+    }
+
+    @Test
+    public void testGetComparator() throws IOException {
+        if (spliterator.hasCharacteristics(Spliterator.SORTED)) {
+            assertEquals(spliterator.unwrap().getComparator(), spliterator.getComparator());
+            assertEquals(spliterator.unwrap().getComparator(), spliterator.asSpliterator().getComparator());
+        } else {
+            assertThrows(IllegalStateException.class, () -> spliterator.unwrap().getComparator());
+            assertThrows(IllegalStateException.class, () -> spliterator.asSpliterator().getComparator());
+        }
+        final IOSpliterator<Path> adapted = IOSpliterator.adapt(new TreeSet<>(newPathList()).stream().sorted().spliterator());
+        final IOComparator<? super Path> comparator = adapted.getComparator();
+        assertNull(comparator);
+    }
+
+    @Test
+    public void testGetExactSizeIfKnown() {
+        assertEquals(2, spliterator.getExactSizeIfKnown());
+        assertEquals(spliterator.unwrap().getExactSizeIfKnown(), spliterator.getExactSizeIfKnown());
+        assertEquals(spliterator.unwrap().getExactSizeIfKnown(), spliterator.asSpliterator().getExactSizeIfKnown());
+    }
+
+    @Test
+    public void testHasCharacteristics() {
+        assertEquals(true, spliterator.hasCharacteristics(spliterator.characteristics()));
+        assertEquals(spliterator.unwrap().hasCharacteristics(spliterator.unwrap().characteristics()),
+            spliterator.hasCharacteristics(spliterator.characteristics()));
+        assertEquals(spliterator.unwrap().hasCharacteristics(spliterator.unwrap().characteristics()),
+            spliterator.asSpliterator().hasCharacteristics(spliterator.asSpliterator().characteristics()));
+    }
+
+    @Test
+    public void testTryAdvance() {
+        final AtomicReference<Path> ref = new AtomicReference<>();
+        assertTrue(spliterator.tryAdvance(ref::set));
+        assertEquals(TestConstants.ABS_PATH_A, ref.get());
+    }
+
+    @Test
+    public void testTrySplit() {
+        final IOSpliterator<Path> trySplit = spliterator.trySplit();
+        assertNotNull(trySplit);
+        assertTrue(spliterator.getExactSizeIfKnown() > 0);
+    }
+
+    @Test
+    public void testUnwrap() {
+        assertNotNull(spliterator.unwrap());
+    }
+
+}