Skip to content

Commit

Permalink
#89 Add file or folder to specific path in zip
Browse files Browse the repository at this point in the history
  • Loading branch information
oleg-cherednik committed Oct 27, 2024
1 parent 41922e6 commit d47f3ab
Show file tree
Hide file tree
Showing 40 changed files with 739 additions and 637 deletions.
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ dependencies {
testImplementation 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01'
testImplementation 'net.lingala.zip4j:zip4j:2.11.5'
//noinspection VulnerableLibrariesLocal
testImplementation 'de.idyl:winzipaes:1.0.1'
testImplementation('de.idyl:winzipaes:1.0.1') {
exclude group: 'org.bouncycastle', module: 'bcprov-jdk16'
}
testImplementation 'org.tukaani:xz:1.10'
}

Expand All @@ -81,7 +83,7 @@ javadoc {
options.addBooleanOption('Xdoclint:-missing', true)
}

check.finalizedBy(checkLicense)
//check.finalizedBy(checkLicense)
check.finalizedBy(generateLicenseReport)
check.finalizedBy(jacocoTestReport)

Expand Down
9 changes: 4 additions & 5 deletions src/main/java/ru/olegcherednik/zip4jvm/ZipFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import ru.olegcherednik.zip4jvm.model.settings.ZipSettings;
import ru.olegcherednik.zip4jvm.model.src.SrcZip;
import ru.olegcherednik.zip4jvm.utils.EmptyInputStreamSupplier;
import ru.olegcherednik.zip4jvm.utils.PathUtils;
import ru.olegcherednik.zip4jvm.utils.quitely.Quietly;
import ru.olegcherednik.zip4jvm.utils.quitely.functions.InputStreamSupplier;

Expand Down Expand Up @@ -125,11 +124,11 @@ public InputStream getInputStream() {

public interface Writer extends Closeable {

default void add(Path path) {
add(path, PathUtils.getName(path));
}
void add(Path path);

void addWithRename(Path path, String name);

void add(Path path, String name);
void addWithMove(Path path, String dir);

void add(ZipFile.Entry entry);

Expand Down
35 changes: 28 additions & 7 deletions src/main/java/ru/olegcherednik/zip4jvm/ZipIt.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

import ru.olegcherednik.zip4jvm.exception.PathNotExistsException;
import ru.olegcherednik.zip4jvm.model.settings.ZipEntrySettings;
import ru.olegcherednik.zip4jvm.model.settings.ZipEntrySettingsProvider;
import ru.olegcherednik.zip4jvm.model.settings.ZipSettings;
import ru.olegcherednik.zip4jvm.utils.ValidationUtils;

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
Expand All @@ -32,7 +32,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.function.Function;

import static ru.olegcherednik.zip4jvm.utils.ValidationUtils.requireExists;
import static ru.olegcherednik.zip4jvm.utils.ValidationUtils.requireNotNull;
Expand Down Expand Up @@ -86,19 +85,19 @@ public ZipIt settings(ZipSettings settings) {
* @return not {@literal null} {@link ZipIt} instance
*/
public ZipIt entrySettings(ZipEntrySettings entrySettings) {
return entrySettings == null ? entrySettings(ZipEntrySettings.DEFAULT_PROVIDER)
: entrySettings(fileName -> entrySettings);
return entrySettings == null ? entrySettings(ZipEntrySettingsProvider.DEFAULT)
: entrySettings(ZipEntrySettingsProvider.of(entrySettings));
}

/**
* Set provider of {@link ZipEntrySettings} for the given file name. Each entry could have separate settings. If
* {@literal null}, then {@link
* ZipEntrySettings#DEFAULT_PROVIDER} will be used.
* ZipEntrySettingsProvider#DEFAULT} will be used.
*
* @param entrySettingsProvider entry settings provider with fileName as a key
* @return not {@literal null} {@link ZipIt} instance
*/
public ZipIt entrySettings(Function<String, ZipEntrySettings> entrySettingsProvider) {
public ZipIt entrySettings(ZipEntrySettingsProvider entrySettingsProvider) {
requireNotNull(entrySettingsProvider, "ZipIt.entrySettingsProvider");
settings = settings.toBuilder().entrySettingsProvider(entrySettingsProvider).build();
return this;
Expand Down Expand Up @@ -128,7 +127,7 @@ public void add(Collection<Path> paths) throws IOException {
if (CollectionUtils.isEmpty(paths))
return;

paths.forEach(ValidationUtils::requireExists);
requireExists(paths);

// TODO check that path != zip
try (ZipFile.Writer zipFile = ZipFile.writer(zip, settings)) {
Expand All @@ -137,6 +136,28 @@ public void add(Collection<Path> paths) throws IOException {
}
}

/**
* Add regular file or directory (keeping initial structure) to the new or existed zip archive under give
* {@code name}.<br>
* In case given {@code path} is a directory (or symlink to directory), then this directory will be renamed.<br>
* In case given {@code path} is a regular file (or symlink to the file), then this file will be renamed.
*
* @param path path to the regular file or directory
* @param name not {@literal null} name to be used for the {@code path}
* @throws IOException in case of any problem with file access
*/
public void addWithRename(Path path, String name) throws IOException {
try (ZipFile.Writer zipFile = ZipFile.writer(zip, settings)) {
zipFile.addWithRename(path, name);
}
}

public void addWithMove(Path path, String dir) throws IOException {
try (ZipFile.Writer zipFile = ZipFile.writer(zip, settings)) {
zipFile.addWithMove(path, dir);
}
}

/**
* Creates instance of zip file stream. It could be used to add multiple entries to the zip archive. It should be
* correctly closed to flush all data.
Expand Down
73 changes: 47 additions & 26 deletions src/main/java/ru/olegcherednik/zip4jvm/engine/ZipEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import ru.olegcherednik.zip4jvm.utils.function.Writer;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.nio.file.Files;
Expand Down Expand Up @@ -74,44 +75,64 @@ public ZipEngine(Path zip, ZipSettings settings) throws IOException {
}

@Override
public void add(Path path, String name) {
public void add(Path path) {
add(path, PathUtils.getName(path), "");
}

@Override
public void addWithRename(Path path, String name) {
add(path, name, "");
}

@Override
public void addWithMove(Path path, String dir) {
add(path, PathUtils.getName(path), dir);
}

private void add(Path path, String name, String dir) {
if (!Files.exists(path))
return;

if (Files.isSymbolicLink(path))
path = ZipSymlinkEngine.getSymlinkTarget(path);

if (Files.isDirectory(path))
zipSymlinkEngine.list(getDirectoryNamedPaths(path, name)).stream()
.map(namedPath -> {
String entryName = namedPath.getEntryName();
ZipEntrySettings entrySettings = getEntrySettings(entryName);
return namedPath.createZipEntry(entrySettings);
})
.forEach(this::add);
else if (Files.isRegularFile(path)) {
ZipEntrySettings entrySettings = getEntrySettings(name);
ZipEntry zipEntry = ZipEntryBuilder.regularFile(path, name, entrySettings);
add(zipEntry);
} else
log.warn("Unknown path type '{}'; ignore it", path);
for (NamedPath namedPath : getNamedPaths(path, name, dir)) {
String entryName = namedPath.getEntryName();
ZipEntrySettings entrySettings = settings.getEntrySettings(entryName);
add(namedPath.createZipEntry(entrySettings));
}
}

private ZipEntrySettings getEntrySettings(String entryName) {
return settings.getEntrySettingsProvider().apply(entryName);
private List<NamedPath> getNamedPaths(Path path, String name, String dir) {
if (Files.isDirectory(path))
return zipSymlinkEngine.list(getDirectoryNamedPaths(path, name, dir));

if (Files.isRegularFile(path)) {
if (StringUtils.isNotBlank(dir))
name = dir + '/' + name;

return Collections.singletonList(NamedPath.create(path, name));
}

log.warn("Unknown path type '{}'; ignore it", path);
return Collections.emptyList();
}

private List<NamedPath> getDirectoryNamedPaths(Path path, String name) {
return settings.isRemoveRootDir() ? PathUtils.list(path).stream()
.map(NamedPath::create)
.sorted(NamedPath.SORT_BY_NAME_ASC)
.collect(Collectors.toList())
: Collections.singletonList(NamedPath.create(path, name));
private List<NamedPath> getDirectoryNamedPaths(Path path, String name, String dir) {
if (settings.isRemoveRootDir())
return PathUtils.list(path).stream()
.map(p -> StringUtils.isNotBlank(dir)
? NamedPath.create(p, dir + '/' + PathUtils.getName(p))
: NamedPath.create(p))
.sorted(NamedPath.SORT_BY_NAME_ASC)
.collect(Collectors.toList());

return Collections.singletonList(NamedPath.create(path, name));
}

@Override
public void add(ZipFile.Entry entry) {
ZipEntrySettings entrySettings = settings.getEntrySettingsProvider().apply(entry.getName());
ZipEntrySettings entrySettings = settings.getEntrySettings(entry.getName());
ZipEntry zipEntry = ZipEntryBuilder.build(entry, entrySettings);
add(zipEntry);
}
Expand Down Expand Up @@ -164,7 +185,7 @@ public void copy(Path zip) throws IOException {
if (fileNameWriter.containsKey(fileName))
throw new EntryDuplicationException(fileName);

char[] password = settings.getEntrySettingsProvider().apply(fileName).getPassword();
char[] password = settings.getEntrySettings(fileName).getPassword();
fileNameWriter.put(fileName, new ExistedEntryWriter(srcZipModel, fileName, tempZipModel, password));
}
}
Expand Down Expand Up @@ -224,7 +245,7 @@ private static ZipModel createTempZipModel(Path zip, ZipSettings settings, Map<S
tempZipModel.setZip64(zipModel.isZip64());

zipModel.getEntryNames().forEach(entryName -> {
char[] password = settings.getEntrySettingsProvider().apply(entryName).getPassword();
char[] password = settings.getEntrySettings(entryName).getPassword();
fileNameWriter.put(entryName, new ExistedEntryWriter(zipModel, entryName, tempZipModel, password));
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@
import ru.olegcherednik.zip4jvm.model.CompressionLevel;
import ru.olegcherednik.zip4jvm.model.Encryption;
import ru.olegcherednik.zip4jvm.model.ZipModel;
import ru.olegcherednik.zip4jvm.utils.ValidationUtils;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ArrayUtils;

import java.util.function.Function;
import static ru.olegcherednik.zip4jvm.utils.ValidationUtils.requireLengthLessOrEqual;

/**
* @author Oleg Cherednik
Expand All @@ -41,7 +40,6 @@
public final class ZipEntrySettings {

public static final ZipEntrySettings DEFAULT = builder().build();
public static final Function<String, ZipEntrySettings> DEFAULT_PROVIDER = fileName -> DEFAULT;

private final Compression compression;
private final CompressionLevel compressionLevel;
Expand All @@ -56,6 +54,26 @@ public static Builder builder() {
return new Builder();
}

public static ZipEntrySettings of(Compression compression) {
if (compression == DEFAULT.getCompression())
return DEFAULT;
return builder().compression(compression).build();
}

public static ZipEntrySettings of(Compression compression, Encryption encryption, char[] password) {
if (encryption == Encryption.OFF)
return of(compression);
return builder()
.compression(compression)
.encryption(encryption, password).build();
}

public static ZipEntrySettings of(Encryption encryption, char[] password) {
if (encryption == Encryption.OFF)
return of(DEFAULT.getCompression());
return builder().encryption(encryption, password).build();
}

public Builder toBuilder() {
return new Builder(this);
}
Expand All @@ -75,6 +93,7 @@ private ZipEntrySettings(Builder builder) {
@SuppressWarnings("PMD.UnusedAssignment")
public static final class Builder {


private Compression compression = Compression.DEFLATE;
private CompressionLevel compressionLevel = CompressionLevel.NORMAL;
private Encryption encryption = Encryption.OFF;
Expand Down Expand Up @@ -102,30 +121,38 @@ public ZipEntrySettings build() {
return new ZipEntrySettings(this);
}

public ZipEntrySettings.Builder compression(Compression compression) {
return compression(compression, CompressionLevel.NORMAL);
}

public ZipEntrySettings.Builder compression(Compression compression, CompressionLevel compressionLevel) {
this.compression = compression;
this.compressionLevel = compressionLevel;
return this;
}

public ZipEntrySettings.Builder encryption(Encryption encryption, char[] password) {
if (encryption != Encryption.OFF) {
this.encryption = encryption;
this.encryption = encryption;

if (encryption == Encryption.OFF)
this.password = null;
else {
if (ArrayUtils.isEmpty(password))
throw new EmptyPasswordException();

this.password = ArrayUtils.clone(password);
}

return this;
}

public ZipEntrySettings.Builder password(char[] password) {
this.password = ArrayUtils.clone(password);
return this;
return encryption(encryption, password);
}

public ZipEntrySettings.Builder comment(String comment) {
this.comment = ValidationUtils.requireLengthLessOrEqual(comment,
ZipModel.MAX_COMMENT_SIZE,
"ZipEntry.comment");
requireLengthLessOrEqual(comment, ZipModel.MAX_COMMENT_SIZE, "ZipEntry.comment");
this.comment = comment;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 ru.olegcherednik.zip4jvm.model.settings;

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;

import java.util.Optional;
import java.util.function.Function;

/**
* @author Oleg Cherednik
* @since 20.10.2024
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class ZipEntrySettingsProvider {

public static final ZipEntrySettingsProvider DEFAULT = of(ZipEntrySettings.DEFAULT);

private final Function<String, ZipEntrySettings> entryNameSettings;

public static ZipEntrySettingsProvider of(ZipEntrySettings entrySettings) {
return new ZipEntrySettingsProvider(entryName -> entrySettings);
}

public static ZipEntrySettingsProvider of(Function<String, ZipEntrySettings> entryNameSettings) {
return new ZipEntrySettingsProvider(entryNameSettings);
}

// @NotNull
public ZipEntrySettings getEntrySettings(String entryName) {
return Optional.ofNullable(entryNameSettings.apply(entryName))
.orElse(ZipEntrySettings.DEFAULT);
}

}
Loading

0 comments on commit d47f3ab

Please sign in to comment.