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/02/13 22:14:52 UTC
[commons-compress] 04/05: Expander example now has NIO Path versions of IO File APIs.
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
commit b30738c974b7c960e463e60afe698c0b1f235a29
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 13 17:00:15 2022 -0500
Expander example now has NIO Path versions of IO File APIs.
---
src/changes/changes.xml | 6 +
.../compress/archivers/examples/Expander.java | 219 +++++++++++++++++----
2 files changed, 183 insertions(+), 42 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1f3385f..db11a56 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -80,6 +80,12 @@ The <action> type attribute can be add,update,fix,remove.
<action type="add" dev="ggregory" due-to="Gary Gregory">
Add APK file extension constants: ArchiveStreamFactory.APK, APKM, APKS, XAPK.
</action>
+ <action type="add" dev="ggregory" due-to="Gary Gregory">
+ ArchiveStreamFactory.createArchiveInputStream(String, InputStream, String) supports the "APK" format (it's a JAR).
+ </action>
+ <action type="add" dev="ggregory" due-to="Gary Gregory">
+ Expander example now has NIO Path versions of IO File APIs.
+ </action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Dependabot">
Bump mockito-core from 3.11.1 to 4.3.1 #209, #224, #231, #235, #243.
diff --git a/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java b/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java
index e6c425e..7bdd7a1 100644
--- a/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java
+++ b/src/main/java/org/apache/commons/compress/archivers/examples/Expander.java
@@ -27,6 +27,7 @@ import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Enumeration;
import java.util.Iterator;
@@ -62,14 +63,29 @@ public class Expander {
* <p>Tries to auto-detect the archive's format.</p>
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @throws IOException if an I/O error occurs
* @throws ArchiveException if the archive cannot be read for other reasons
*/
public void expand(final File archive, final File targetDirectory) throws IOException, ArchiveException {
+ expand(archive.toPath(), toPath(targetDirectory));
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * <p>Tries to auto-detect the archive's format.</p>
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory
+ * @throws IOException if an I/O error occurs
+ * @throws ArchiveException if the archive cannot be read for other reasons
+ * @since 1.22
+ */
+ public void expand(final Path archive, final Path targetDirectory) throws IOException, ArchiveException {
String format = null;
- try (InputStream i = new BufferedInputStream(Files.newInputStream(archive.toPath()))) {
- format = ArchiveStreamFactory.detect(i);
+ try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(archive))) {
+ format = ArchiveStreamFactory.detect(inputStream);
}
expand(format, archive, targetDirectory);
}
@@ -78,21 +94,40 @@ public class Expander {
* Expands {@code archive} into {@code targetDirectory}.
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @param format the archive format. This uses the same format as
* accepted by {@link ArchiveStreamFactory}.
* @throws IOException if an I/O error occurs
* @throws ArchiveException if the archive cannot be read for other reasons
*/
public void expand(final String format, final File archive, final File targetDirectory) throws IOException, ArchiveException {
+ expand(format, archive.toPath(), toPath(targetDirectory));
+ }
+
+ private Path toPath(final File targetDirectory) {
+ return targetDirectory != null ? targetDirectory.toPath() : null;
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory
+ * @param format the archive format. This uses the same format as
+ * accepted by {@link ArchiveStreamFactory}.
+ * @throws IOException if an I/O error occurs
+ * @throws ArchiveException if the archive cannot be read for other reasons
+ * @since 1.22
+ */
+ public void expand(final String format, final Path archive, final Path targetDirectory) throws IOException, ArchiveException {
if (prefersSeekableByteChannel(format)) {
- try (SeekableByteChannel c = FileChannel.open(archive.toPath(), StandardOpenOption.READ)) {
- expand(format, c, targetDirectory, CloseableConsumer.CLOSING_CONSUMER);
+ try (SeekableByteChannel channel = FileChannel.open(archive, StandardOpenOption.READ)) {
+ expand(format, channel, targetDirectory, CloseableConsumer.CLOSING_CONSUMER);
}
return;
}
- try (InputStream i = new BufferedInputStream(Files.newInputStream(archive.toPath()))) {
- expand(format, i, targetDirectory, CloseableConsumer.CLOSING_CONSUMER);
+ try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(archive))) {
+ expand(format, inputStream, targetDirectory, CloseableConsumer.CLOSING_CONSUMER);
}
}
@@ -107,7 +142,7 @@ public class Expander {
* instead.</p>
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @throws IOException if an I/O error occurs
* @throws ArchiveException if the archive cannot be read for other reasons
* @deprecated this method leaks resources
@@ -130,7 +165,7 @@ public class Expander {
* by this class.</p>
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @param closeableConsumer is informed about the stream wrapped around the passed in stream
* @throws IOException if an I/O error occurs
* @throws ArchiveException if the archive cannot be read for other reasons
@@ -153,7 +188,7 @@ public class Expander {
* instead.</p>
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @param format the archive format. This uses the same format as
* accepted by {@link ArchiveStreamFactory}.
* @throws IOException if an I/O error occurs
@@ -177,7 +212,7 @@ public class Expander {
* by this class.</p>
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @param format the archive format. This uses the same format as
* accepted by {@link ArchiveStreamFactory}.
* @param closeableConsumer is informed about the stream wrapped around the passed in stream
@@ -187,6 +222,30 @@ public class Expander {
*/
public void expand(final String format, final InputStream archive, final File targetDirectory, final CloseableConsumer closeableConsumer)
throws IOException, ArchiveException {
+ expand(format, archive, toPath(targetDirectory), closeableConsumer);
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * <p>This method creates a wrapper around the archive stream and
+ * the caller of this method is responsible for closing it -
+ * probably at the same time as closing the stream itself. The
+ * caller is informed about the wrapper object via the {@code
+ * closeableConsumer} callback as soon as it is no longer needed
+ * by this class.</p>
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory
+ * @param format the archive format. This uses the same format as
+ * accepted by {@link ArchiveStreamFactory}.
+ * @param closeableConsumer is informed about the stream wrapped around the passed in stream
+ * @throws IOException if an I/O error occurs
+ * @throws ArchiveException if the archive cannot be read for other reasons
+ * @since 1.22
+ */
+ public void expand(final String format, final InputStream archive, final Path targetDirectory, final CloseableConsumer closeableConsumer)
+ throws IOException, ArchiveException {
try (CloseableConsumerAdapter c = new CloseableConsumerAdapter(closeableConsumer)) {
expand(c.track(ArchiveStreamFactory.DEFAULT.createArchiveInputStream(format, archive)),
targetDirectory);
@@ -202,7 +261,7 @@ public class Expander {
* instead.</p>
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @param format the archive format. This uses the same format as
* accepted by {@link ArchiveStreamFactory}.
* @throws IOException if an I/O error occurs
@@ -226,7 +285,7 @@ public class Expander {
* by this class.</p>
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory
* @param format the archive format. This uses the same format as
* accepted by {@link ArchiveStreamFactory}.
* @param closeableConsumer is informed about the stream wrapped around the passed in channel
@@ -234,12 +293,36 @@ public class Expander {
* @throws ArchiveException if the archive cannot be read for other reasons
* @since 1.19
*/
- public void expand(final String format, final SeekableByteChannel archive, final File targetDirectory,
+ public void expand(final String format, final SeekableByteChannel archive, final File targetDirectory, final CloseableConsumer closeableConsumer)
+ throws IOException, ArchiveException {
+ expand(format, archive, toPath(targetDirectory), closeableConsumer);
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * <p>This method creates a wrapper around the archive channel and
+ * the caller of this method is responsible for closing it -
+ * probably at the same time as closing the channel itself. The
+ * caller is informed about the wrapper object via the {@code
+ * closeableConsumer} callback as soon as it is no longer needed
+ * by this class.</p>
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory
+ * @param format the archive format. This uses the same format as
+ * accepted by {@link ArchiveStreamFactory}.
+ * @param closeableConsumer is informed about the stream wrapped around the passed in channel
+ * @throws IOException if an I/O error occurs
+ * @throws ArchiveException if the archive cannot be read for other reasons
+ * @since 1.22
+ */
+ public void expand(final String format, final SeekableByteChannel archive, final Path targetDirectory,
final CloseableConsumer closeableConsumer)
throws IOException, ArchiveException {
try (CloseableConsumerAdapter c = new CloseableConsumerAdapter(closeableConsumer)) {
if (!prefersSeekableByteChannel(format)) {
- expand(format, c.track(Channels.newInputStream(archive)), targetDirectory);
+ expand(format, c.track(Channels.newInputStream(archive)), targetDirectory, CloseableConsumer.NULL_CONSUMER);
} else if (ArchiveStreamFactory.TAR.equalsIgnoreCase(format)) {
expand(c.track(new TarFile(archive)), targetDirectory);
} else if (ArchiveStreamFactory.ZIP.equalsIgnoreCase(format)) {
@@ -257,11 +340,22 @@ public class Expander {
* Expands {@code archive} into {@code targetDirectory}.
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
* @throws IOException if an I/O error occurs
*/
- public void expand(final ArchiveInputStream archive, final File targetDirectory)
- throws IOException {
+ public void expand(final ArchiveInputStream archive, final File targetDirectory) throws IOException {
+ expand(archive, toPath(targetDirectory));
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
+ * @throws IOException if an I/O error occurs
+ * @since 1.22
+ */
+ public void expand(final ArchiveInputStream archive, final Path targetDirectory) throws IOException {
expand(() -> {
ArchiveEntry next = archive.getNextEntry();
while (next != null && !archive.canReadEntryData(next)) {
@@ -275,11 +369,23 @@ public class Expander {
* Expands {@code archive} into {@code targetDirectory}.
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
* @throws IOException if an I/O error occurs
* @since 1.21
*/
- public void expand(final TarFile archive, final File targetDirectory)
+ public void expand(final TarFile archive, final File targetDirectory) throws IOException {
+ expand(archive, toPath(targetDirectory));
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
+ * @throws IOException if an I/O error occurs
+ * @since 1.22
+ */
+ public void expand(final TarFile archive, final Path targetDirectory)
throws IOException {
final Iterator<TarArchiveEntry> entryIterator = archive.getEntries().iterator();
expand(() -> entryIterator.hasNext() ? entryIterator.next() : null,
@@ -294,10 +400,22 @@ public class Expander {
* Expands {@code archive} into {@code targetDirectory}.
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
+ * @throws IOException if an I/O error occurs
+ */
+ public void expand(final ZipFile archive, final File targetDirectory) throws IOException {
+ expand(archive, toPath(targetDirectory));
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
* @throws IOException if an I/O error occurs
+ * @since 1.22
*/
- public void expand(final ZipFile archive, final File targetDirectory)
+ public void expand(final ZipFile archive, final Path targetDirectory)
throws IOException {
final Enumeration<ZipArchiveEntry> entries = archive.getEntries();
expand(() -> {
@@ -317,16 +435,30 @@ public class Expander {
* Expands {@code archive} into {@code targetDirectory}.
*
* @param archive the file to expand
- * @param targetDirectory the directory to write to
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
* @throws IOException if an I/O error occurs
*/
- public void expand(final SevenZFile archive, final File targetDirectory)
+ public void expand(final SevenZFile archive, final File targetDirectory) throws IOException {
+ expand(archive, toPath(targetDirectory));
+ }
+
+ /**
+ * Expands {@code archive} into {@code targetDirectory}.
+ *
+ * @param archive the file to expand
+ * @param targetDirectory the target directory, may be null to simulate output to dev/null on Linux and NUL on Windows.
+ * @throws IOException if an I/O error occurs
+ * @since 1.22
+ */
+ public void expand(final SevenZFile archive, final Path targetDirectory)
throws IOException {
expand(archive::getNextEntry, (entry, out) -> {
final byte[] buffer = new byte[8192];
int n;
while (-1 != (n = archive.read(buffer))) {
- out.write(buffer, 0, n);
+ if (out != null) {
+ out.write(buffer, 0, n);
+ }
}
}, targetDirectory);
}
@@ -337,33 +469,36 @@ public class Expander {
|| ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format);
}
- private void expand(final ArchiveEntrySupplier supplier, final EntryWriter writer, final File targetDirectory)
+ /**
+ * @param targetDirectory May be null to simulate output to dev/null on Linux and NUL on Windows.
+ */
+ private void expand(final ArchiveEntrySupplier supplier, final EntryWriter writer, final Path targetDirectory)
throws IOException {
- String targetDirPath = targetDirectory.getCanonicalPath();
- if (!targetDirPath.endsWith(File.separator)) {
- targetDirPath += File.separator;
- }
+ final boolean nullTarget = targetDirectory == null;
+ final Path targetDirPath = nullTarget ? null : targetDirectory.normalize();
ArchiveEntry nextEntry = supplier.getNextReadableEntry();
while (nextEntry != null) {
- final File targetFile = new File(targetDirectory, nextEntry.getName());
+ final Path targetPath = nullTarget ? null : targetDirectory.resolve(nextEntry.getName());
// check if targetDirectory and f are the same path - this may
// happen if the nextEntry.getName() is "./"
- if (!targetFile.getCanonicalPath().startsWith(targetDirPath)
- && !Files.isSameFile(targetDirectory.toPath(), targetFile.toPath())) {
- throw new IOException("Expanding " + nextEntry.getName()
- + " would create file outside of " + targetDirectory);
+ if (!nullTarget && !targetPath.normalize().startsWith(targetDirPath) && !Files.isSameFile(targetDirectory, targetPath)) {
+ throw new IOException("Expanding " + nextEntry.getName() + " would create file outside of " + targetDirectory);
}
if (nextEntry.isDirectory()) {
- if (!targetFile.isDirectory() && !targetFile.mkdirs()) {
- throw new IOException("Failed to create directory " + targetFile);
+ if (!nullTarget && !Files.isDirectory(targetPath) && Files.createDirectories(targetPath) == null) {
+ throw new IOException("Failed to create directory " + targetPath);
}
} else {
- final File parent = targetFile.getParentFile();
- if (!parent.isDirectory() && !parent.mkdirs()) {
+ final Path parent = nullTarget ? null : targetPath.getParent();
+ if (!nullTarget && !Files.isDirectory(parent) && Files.createDirectories(parent) == null) {
throw new IOException("Failed to create directory " + parent);
}
- try (OutputStream o = Files.newOutputStream(targetFile.toPath())) {
- writer.writeEntryDataTo(nextEntry, o);
+ if (nullTarget) {
+ writer.writeEntryDataTo(nextEntry, null);
+ } else {
+ try (OutputStream outputStream = Files.newOutputStream(targetPath)) {
+ writer.writeEntryDataTo(nextEntry, outputStream);
+ }
}
}
nextEntry = supplier.getNextReadableEntry();