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-compress.git


The following commit(s) were added to refs/heads/master by this push:
     new 061828c78 Add a builder for the TarFile class and deprecate some 
constructors
061828c78 is described below

commit 061828c78c3487c7f8193d48c3cba099c1d02414
Author: Gary D. Gregory <garydgreg...@gmail.com>
AuthorDate: Sun Sep 21 22:50:59 2025 -0400

    Add a builder for the TarFile class and deprecate some constructors
---
 src/changes/changes.xml                            |  1 +
 .../compress/archivers/tar/AbstractTarBuilder.java | 87 +++++++++++++++++++
 .../archivers/tar/TarArchiveInputStream.java       | 46 +---------
 .../commons/compress/archivers/tar/TarFile.java    | 97 ++++++++++++++++++++--
 .../compress/archivers/tar/TarFileTest.java        | 16 ++++
 5 files changed, 198 insertions(+), 49 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 221a07693..7f46fc8c5 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -122,6 +122,7 @@ The <action> type attribute can be add,update,fix,remove.
       <action type="add" dev="ggregory" due-to="Gary Gregory">Add 
ArchiveInputStream.ArchiveInputStream(InputStream, Charset) as a public 
constructor, it was private.</action>
       <action type="add" dev="ggregory" due-to="Gary Gregory, Piotr P. 
Karwasz">Introduce builders for all ArchiveInputStream implementations and 
deprecate some constructors.</action>
       <action type="add" dev="ggregory" due-to="Gary Gregory">TarFile now 
implements IOIterable&lt;TarArchiveEntry&gt;.</action>
+      <action type="add" dev="ggregory" due-to="Gary Gregory">Add a builder 
for the TarFile class and deprecate some constructors.</action>
       <!-- UPDATE -->
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-parent from 85 to 88 #707.</action>
       <action type="update" dev="ppkarwasz" due-to="Raeps">Extract duplicate 
code in org.apache.commons.compress.harmony.pack200.IntList.</action>
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/tar/AbstractTarBuilder.java
 
b/src/main/java/org/apache/commons/compress/archivers/tar/AbstractTarBuilder.java
new file mode 100644
index 000000000..8eae8dd43
--- /dev/null
+++ 
b/src/main/java/org/apache/commons/compress/archivers/tar/AbstractTarBuilder.java
@@ -0,0 +1,87 @@
+/*
+ * 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
+ *
+ *   https://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.compress.archivers.tar;
+
+import org.apache.commons.io.build.AbstractStreamBuilder;
+
+/**
+ * Abstracts TAR builder operations.
+ *
+ * @since 1.29.0
+ */
+public abstract class AbstractTarBuilder<T, B extends AbstractTarBuilder<T, 
B>> extends AbstractStreamBuilder<T, B> {
+
+    private int blockSize = TarConstants.DEFAULT_BLKSIZE;
+    private int recordSize = TarConstants.DEFAULT_RCDSIZE;
+    private boolean lenient;
+
+    /**
+     * Constructs a new instance.
+     */
+    protected AbstractTarBuilder() {
+        // empty
+    }
+
+    int getBlockSize() {
+        return blockSize;
+    }
+
+    int getRecordSize() {
+        return recordSize;
+    }
+
+    boolean isLenient() {
+        return lenient;
+    }
+
+    /**
+     * Sets the block size.
+     *
+     * @param blockSize the block size.
+     * @return {@code this} instance.
+     */
+    public B setBlockSize(final int blockSize) {
+        this.blockSize = blockSize;
+        return asThis();
+    }
+
+    /**
+     * Sets whether illegal values for group/userid, mode, device numbers and 
timestamp will be ignored and the fields set to {@link TarArchiveEntry#UNKNOWN}.
+     * When set to false such illegal fields cause an exception instead.
+     *
+     * @param lenient whether illegal values throw exceptions.
+     * @return {@code this} instance.
+     */
+    public B setLenient(final boolean lenient) {
+        this.lenient = lenient;
+        return asThis();
+    }
+
+    /**
+     * Sets the record size.
+     *
+     * @param recordSize the record size.
+     * @return {@code this} instance.
+     */
+    public B setRecordSize(final int recordSize) {
+        this.recordSize = recordSize;
+        return asThis();
+    }
+}
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
 
b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
index ecf4f0738..80dd6859f 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
@@ -71,11 +71,7 @@ public class TarArchiveInputStream extends 
ArchiveInputStream<TarArchiveEntry> {
      * @since 1.29.0
      */
     // @formatter:on
-    public static final class Builder extends 
AbstractBuilder<TarArchiveInputStream, Builder> {
-
-        private int blockSize = TarConstants.DEFAULT_BLKSIZE;
-        private int recordSize = TarConstants.DEFAULT_RCDSIZE;
-        private boolean lenient;
+    public static final class Builder extends 
AbstractTarBuilder<TarArchiveInputStream, Builder> {
 
         /**
          * Constructs a new instance.
@@ -89,40 +85,6 @@ public TarArchiveInputStream get() throws IOException {
             return new TarArchiveInputStream(this);
         }
 
-        /**
-         * Sets the block size.
-         *
-         * @param blockSize the block size.
-         * @return {@code this} instance.
-         */
-        public Builder setBlockSize(final int blockSize) {
-            this.blockSize = blockSize;
-            return this;
-        }
-
-        /**
-         * Sets whether illegal values for group/userid, mode, device numbers 
and timestamp will be ignored and the fields set to
-         * {@link TarArchiveEntry#UNKNOWN}. When set to false such illegal 
fields cause an exception instead.
-         *
-         * @param lenient whether illegal values throw exceptions.
-         * @return {@code this} instance.
-         */
-        public Builder setLenient(final boolean lenient) {
-            this.lenient = lenient;
-            return this;
-        }
-
-        /**
-         * Sets the record size.
-         *
-         * @param recordSize the record size.
-         * @return {@code this} instance.
-         */
-        public Builder setRecordSize(final int recordSize) {
-            this.recordSize = recordSize;
-            return this;
-        }
-
     }
 
     /**
@@ -244,9 +206,9 @@ public TarArchiveInputStream(final InputStream inputStream, 
final boolean lenien
     private TarArchiveInputStream(final InputStream inputStream, final Builder 
builder) {
         super(inputStream, builder.getCharset());
         this.zipEncoding = 
ZipEncodingHelper.getZipEncoding(builder.getCharset());
-        this.recordBuffer = new byte[builder.recordSize];
-        this.blockSize = builder.blockSize;
-        this.lenient = builder.lenient;
+        this.recordBuffer = new byte[builder.getRecordSize()];
+        this.blockSize = builder.getBlockSize();
+        this.lenient = builder.isLenient();
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java 
b/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java
index 9c1d13a62..9ebb6c1d6 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java
@@ -36,6 +36,7 @@
 import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.zip.ZipEncoding;
 import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
 import org.apache.commons.compress.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.BoundedArchiveInputStream;
 import org.apache.commons.compress.utils.BoundedSeekableByteChannelInputStream;
@@ -138,6 +139,64 @@ private int readSparse(final long pos, final ByteBuffer 
buf, final int numToRead
         }
     }
 
+    // @formatter:off
+    /**
+     * Builds a new {@link GzipCompressorInputStream}.
+     *
+     * <p>
+     * For example:
+     * </p>
+     * <pre>{@code
+     * TarFile s = TarFile.builder()
+     *   .setPath(path)
+     *   .setLenient(true)
+     *   .setFileNameCharset(StandardCharsets.UTF_8)
+     *   .get();}
+     * </pre>
+     *
+     * @see #get()
+     * @since 1.29.0
+     */
+    // @formatter:on
+    public static final class Builder extends AbstractTarBuilder<TarFile, 
Builder> {
+
+        private SeekableByteChannel channel;
+
+        /**
+         * Constructs a new instance.
+         */
+        private Builder() {
+            // empty
+        }
+
+        @Override
+        public TarFile get() throws IOException {
+            return new TarFile(this);
+        }
+
+        /**
+         * Sets the SeekableByteChannel.
+         *
+         * @param channel  the SeekableByteChannel.
+         * @return {@code this} instance.
+         */
+        public Builder setSeekableByteChannel(final SeekableByteChannel 
channel) {
+            this.channel = channel;
+            return asThis();
+        }
+
+    }
+
+    /**
+     * Creates a new builder.
+     *
+     * @return a new builder.
+     * @since 1.29.0
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
     private final SeekableByteChannel archive;
 
     /**
@@ -174,12 +233,24 @@ private int readSparse(final long pos, final ByteBuffer 
buf, final int numToRead
 
     private final Map<String, List<InputStream>> sparseInputStreams = new 
HashMap<>();
 
+    private TarFile(final Builder builder) throws IOException {
+        this.archive = builder.channel != null ? builder.channel : 
Files.newByteChannel(builder.getPath());
+        this.zipEncoding = 
ZipEncodingHelper.getZipEncoding(builder.getCharset());
+        this.recordSize = builder.getRecordSize();
+        this.recordBuffer = ByteBuffer.allocate(this.recordSize);
+        this.blockSize = builder.getBlockSize();
+        this.lenient = builder.isLenient();
+        forEach(entries::add);
+    }
+
     /**
      * Constructor for TarFile.
      *
      * @param content the content to use.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final byte[] content) throws IOException {
         this(new SeekableInMemoryByteChannel(content));
     }
@@ -191,7 +262,9 @@ public TarFile(final byte[] content) throws IOException {
      * @param lenient when set to true illegal values for group/userid, mode, 
device numbers and timestamp will be ignored and the fields set to
      *                {@link TarArchiveEntry#UNKNOWN}. When set to false such 
illegal fields cause an exception instead.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final byte[] content, final boolean lenient) throws 
IOException {
         this(new SeekableInMemoryByteChannel(content), 
TarConstants.DEFAULT_BLKSIZE, TarConstants.DEFAULT_RCDSIZE, null, lenient);
     }
@@ -202,7 +275,9 @@ public TarFile(final byte[] content, final boolean lenient) 
throws IOException {
      * @param content  the content to use.
      * @param encoding the encoding to use.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final byte[] content, final String encoding) throws 
IOException {
         this(new SeekableInMemoryByteChannel(content), 
TarConstants.DEFAULT_BLKSIZE, TarConstants.DEFAULT_RCDSIZE, encoding, false);
     }
@@ -212,7 +287,9 @@ public TarFile(final byte[] content, final String encoding) 
throws IOException {
      *
      * @param archive the file of the archive to use.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final File archive) throws IOException {
         this(archive.toPath());
     }
@@ -224,7 +301,9 @@ public TarFile(final File archive) throws IOException {
      * @param lenient when set to true illegal values for group/userid, mode, 
device numbers and timestamp will be ignored and the fields set to
      *                {@link TarArchiveEntry#UNKNOWN}. When set to false such 
illegal fields cause an exception instead.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final File archive, final boolean lenient) throws 
IOException {
         this(archive.toPath(), lenient);
     }
@@ -235,7 +314,9 @@ public TarFile(final File archive, final boolean lenient) 
throws IOException {
      * @param archive  the file of the archive to use.
      * @param encoding the encoding to use.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final File archive, final String encoding) throws 
IOException {
         this(archive.toPath(), encoding);
     }
@@ -257,7 +338,9 @@ public TarFile(final Path archivePath) throws IOException {
      * @param lenient     when set to true illegal values for group/userid, 
mode, device numbers and timestamp will be ignored and the fields set to
      *                    {@link TarArchiveEntry#UNKNOWN}. When set to false 
such illegal fields cause an exception instead.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final Path archivePath, final boolean lenient) throws 
IOException {
         this(Files.newByteChannel(archivePath), TarConstants.DEFAULT_BLKSIZE, 
TarConstants.DEFAULT_RCDSIZE, null, lenient);
     }
@@ -268,7 +351,9 @@ public TarFile(final Path archivePath, final boolean 
lenient) throws IOException
      * @param archivePath the path of the archive to use.
      * @param encoding    the encoding to use.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final Path archivePath, final String encoding) throws 
IOException {
         this(Files.newByteChannel(archivePath), TarConstants.DEFAULT_BLKSIZE, 
TarConstants.DEFAULT_RCDSIZE, encoding, false);
     }
@@ -278,7 +363,9 @@ public TarFile(final Path archivePath, final String 
encoding) throws IOException
      *
      * @param content the content to use.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final SeekableByteChannel content) throws IOException {
         this(content, TarConstants.DEFAULT_BLKSIZE, 
TarConstants.DEFAULT_RCDSIZE, null, false);
     }
@@ -293,16 +380,12 @@ public TarFile(final SeekableByteChannel content) throws 
IOException {
      * @param lenient    when set to true illegal values for group/userid, 
mode, device numbers and timestamp will be ignored and the fields set to
      *                   {@link TarArchiveEntry#UNKNOWN}. When set to false 
such illegal fields cause an exception instead.
      * @throws IOException when reading the tar archive fails.
+     * @deprecated Use {@link #builder()} and {@link Builder}.
      */
+    @Deprecated
     public TarFile(final SeekableByteChannel archive, final int blockSize, 
final int recordSize, final String encoding, final boolean lenient)
             throws IOException {
-        this.archive = archive;
-        this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
-        this.recordSize = recordSize;
-        this.recordBuffer = ByteBuffer.allocate(this.recordSize);
-        this.blockSize = blockSize;
-        this.lenient = lenient;
-        forEach(entries::add);
+        
this(builder().setSeekableByteChannel(archive).setBlockSize(blockSize).setRecordSize(recordSize).setCharset(encoding).setLenient(lenient));
     }
 
     /**
diff --git 
a/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java 
b/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java
index 38b0cced4..5b8b9a18f 100644
--- a/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java
@@ -77,6 +77,22 @@ void testArchiveWithTrailer() throws IOException {
         }
     }
 
+    @Test
+    void testBuilderSeekableByteChannel() throws IOException {
+        try (SeekableByteChannel channel = 
Files.newByteChannel(getPath("archive_with_trailer.tar"));
+                TarFile tarfile = TarFile.builder()
+                        .setSeekableByteChannel(channel)
+                        .setBlockSize(TarConstants.DEFAULT_BLKSIZE)
+                        .setRecordSize(TarConstants.DEFAULT_RCDSIZE)
+                        .setLenient(false)
+                        .get()) {
+            final String tarAppendix = "Hello, world!\n";
+            final ByteBuffer buffer = 
ByteBuffer.allocate(tarAppendix.length());
+            channel.read(buffer);
+            assertEquals(tarAppendix, new String(buffer.array()));
+        }
+    }
+
     @Test
     void testCompress197() throws IOException {
         try (TarFile tarFile = new TarFile(getPath("COMPRESS-197.tar"))) {

Reply via email to