You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2023/03/29 09:30:13 UTC

[causeway] branch master updated: CAUSEWAY-3404: [Commons] adds data-source mapping and unzip/first

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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/master by this push:
     new 77842600b5 CAUSEWAY-3404: [Commons] adds data-source mapping and unzip/first
77842600b5 is described below

commit 77842600b5ddf5d302e4ac7dd7632fce13f78de0
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Mar 29 11:30:07 2023 +0200

    CAUSEWAY-3404: [Commons] adds data-source mapping and unzip/first
---
 .../org/apache/causeway/applib/value/Blob.java     |  3 +-
 .../commons/internal/collections/_Lists.java       | 14 ++++++++
 .../org/apache/causeway/commons/io/DataSource.java | 16 +++++++++
 .../org/apache/causeway/commons/io/ZipUtils.java   | 40 ++++++++++++++++++++++
 .../apache/causeway/commons/io/ZipUtilsTest.java   | 12 +++++--
 5 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/causeway/applib/value/Blob.java b/api/applib/src/main/java/org/apache/causeway/applib/value/Blob.java
index 4a90aae5cb..1740c52390 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/value/Blob.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/value/Blob.java
@@ -254,12 +254,11 @@ public final class Blob implements NamedWithMimeType {
     }
 
     public Blob unZip(final @NonNull CommonMimeType resultingMimeType) {
-        return ZipUtils.streamZipEntries(asDataSource())
+        return ZipUtils.firstZipEntry(asDataSource()) // assuming first entry is the one we want
                 .map(zipEntryDataSource->Blob.of(
                         zipEntryDataSource.zipEntry().getName(),
                         resultingMimeType,
                         zipEntryDataSource.bytes()))
-                .findFirst() // assuming first entry is the one we want
                 .orElseThrow(()->_Exceptions
                       .unrecoverable("failed to unzip blob, no entry found %s", getName()));
     }
diff --git a/commons/src/main/java/org/apache/causeway/commons/internal/collections/_Lists.java b/commons/src/main/java/org/apache/causeway/commons/internal/collections/_Lists.java
index d9100d003b..897f1e5903 100644
--- a/commons/src/main/java/org/apache/causeway/commons/internal/collections/_Lists.java
+++ b/commons/src/main/java/org/apache/causeway/commons/internal/collections/_Lists.java
@@ -57,6 +57,20 @@ public final class _Lists {
 
     // -- LIST ACCESS
 
+    public static <T> T firstElementIfAny(final @Nullable List<T> list) {
+        if(_NullSafe.isEmpty(list)) {
+            return null;
+        }
+        return list.get(0);
+    }
+
+    public static <T> Optional<T> firstElement(final @Nullable List<T> list) {
+        if(_NullSafe.isEmpty(list)) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(list.get(0));
+    }
+
     public static <T> T lastElementIfAny(final @Nullable List<T> list) {
         if(_NullSafe.isEmpty(list)) {
             return null;
diff --git a/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java b/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java
index 0350ff10ac..8c60cabb4c 100644
--- a/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java
+++ b/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java
@@ -160,6 +160,22 @@ public interface DataSource {
                 .asHexString();
     }
 
+    // -- MAP
+
+    /**
+     * Returns a new {@link DataSource} that maps the {@link InputStream} of this {@link DataSource} to another
+     * through means of applying given unary operator {@code inputStreamMapper}.
+     * (eg the decode or encode the originating input stream)
+     */
+    default DataSource map(final @NonNull ThrowingFunction<InputStream, InputStream> inputStreamMapper) {
+        val self = this;
+        return new DataSource() {
+            @Override public <T> Try<T> tryReadAll(final @NonNull Function<InputStream, Try<T>> consumingMapper) {
+                return self.tryReadAll(is->consumingMapper.apply(inputStreamMapper.apply(is)));
+            }
+        };
+    }
+
     // -- PIPE
 
     /**
diff --git a/commons/src/main/java/org/apache/causeway/commons/io/ZipUtils.java b/commons/src/main/java/org/apache/causeway/commons/io/ZipUtils.java
index 2d264b6864..9eae8351e2 100644
--- a/commons/src/main/java/org/apache/causeway/commons/io/ZipUtils.java
+++ b/commons/src/main/java/org/apache/causeway/commons/io/ZipUtils.java
@@ -25,6 +25,7 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
@@ -163,6 +164,45 @@ public class ZipUtils {
         return streamZipEntries(zippedSource, ZipOptions.builder().build());
     }
 
+    /**
+     * Optionally the first zip-entry as {@link ZipEntryDataSource}, based on whether an entry exists.
+     */
+    public Optional<ZipEntryDataSource> firstZipEntry(
+            final @NonNull DataSource zippedSource,
+            final @NonNull ZipOptions zipOptions) {
+
+        val zipEntryDataSources = _Lists.<ZipEntryDataSource>newArrayList(1);
+
+        zippedSource.tryReadAndAccept(is->{
+            try(final ZipInputStream in = new ZipInputStream(
+                    new BufferedInputStream(is, zipOptions.bufferSize()),
+                    zipOptions.zipEntryCharset())) {
+
+                ZipEntry zipEntry;
+                while((zipEntry = in.getNextEntry())!=null) {
+                    if(zipEntry.isDirectory()) continue;
+                    if(zipOptions.zipEntryFilter().test(zipEntry)) {
+                        zipEntryDataSources.add(
+                                new ZipEntryDataSource(zipEntry, _Bytes.ofKeepOpen(in)));
+                        return; // stop further processing
+                    }
+                }
+            }
+        })
+        .ifFailureFail();
+
+        return _Lists.firstElement(zipEntryDataSources);
+    }
+
+    /**
+     * Shortcut for {@code firstZipEntry(zippedSource, ZipOptions.builder().build())}
+     * @see #firstZipEntry(DataSource, ZipOptions)
+     */
+    public Optional<ZipEntryDataSource> firstZipEntry(
+            final @NonNull DataSource zippedSource) {
+        return firstZipEntry(zippedSource, ZipOptions.builder().build());
+    }
+
     // -- WRITING
 
     public static byte[] zipToBytes(final @NonNull Stream<ZipEntryDataSource> entryStream) {
diff --git a/commons/src/test/java/org/apache/causeway/commons/io/ZipUtilsTest.java b/commons/src/test/java/org/apache/causeway/commons/io/ZipUtilsTest.java
index c32bf5bbfd..20d7d25a87 100644
--- a/commons/src/test/java/org/apache/causeway/commons/io/ZipUtilsTest.java
+++ b/commons/src/test/java/org/apache/causeway/commons/io/ZipUtilsTest.java
@@ -41,7 +41,8 @@ class ZipUtilsTest {
 
     @Test
     void zipUnzipRountrip() throws Exception {
-        assertArrayEquals(bytes, unZip(zip(bytes)));
+        assertArrayEquals(bytes, unZip_usingStream(zip(bytes)));
+        assertArrayEquals(bytes, unZip_usingOptional(zip(bytes)));
     }
 
     // -- HELPER
@@ -53,11 +54,18 @@ class ZipUtilsTest {
     }
 
     @SneakyThrows
-    private static byte[] unZip(final byte[] zipped) {
+    private static byte[] unZip_usingStream(final byte[] zipped) {
         return ZipUtils.streamZipEntries(DataSource.ofBytes(zipped))
         .findFirst()
         .map(entry->entry.bytes())
         .orElseGet(()->new byte[0]);
     }
 
+    @SneakyThrows
+    private static byte[] unZip_usingOptional(final byte[] zipped) {
+        return ZipUtils.firstZipEntry(DataSource.ofBytes(zipped))
+        .map(entry->entry.bytes())
+        .orElseGet(()->new byte[0]);
+    }
+
 }