package org.netbeans.gradle.build;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.internal.file.AbstractFileCollection;
public final class ZippedJarsFileCollection extends AbstractFileCollection {
private final Charset UTF8 = Charset.forName("UTF-8");
private final Project project;
private final String dependencyNotation;
private final Configuration dependencyContainer;
private final Path unzipDir;
private final List<String> subDirPath;
public ZippedJarsFileCollection(
Project project,
String dependencyNotation,
List<String> unzipPath,
List<String> subDirPath) {
this.project = project;
Dependency dependency = project.getDependencies().create(dependencyNotation);
this.dependencyContainer = project.getConfigurations().detachedConfiguration(dependency);
this.unzipDir = resolve(project.getProjectDir().toPath(), unzipPath);
this.dependencyNotation = dependencyNotation;
this.subDirPath = new ArrayList<>(subDirPath);
}
private static Path resolve(Path dir, List<String> subDirs) {
Path result = dir;
for (String subDir: subDirs) {
result = result.resolve(subDir);
}
return result;
}
private Path getExtractedFilesDir() {
return unzipDir.resolve("files");
}
private Path getExtractedMarker() {
return unzipDir.resolve("EXTRACTED");
}
private void extractSafe() {
try {
extract();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private static void safeCopy(Path src, Path dest) throws IOException {
try (InputStream input = Files.newInputStream(src)) {
Files.copy(input, dest, StandardCopyOption.REPLACE_EXISTING);
}
}
private void markExtracted() throws IOException {
Path extractedMarker = getExtractedMarker();
List<String> lines = Collections.singletonList(dependencyNotation);
Files.write(extractedMarker, lines, UTF8);
}
private boolean isExtracted() throws IOException {
Path extractedMarker = getExtractedMarker();
if (!Files.isRegularFile(extractedMarker)) {
return false;
}
List<String> lines = Files.readAllLines(extractedMarker, UTF8);
if (lines.isEmpty()) {
return false;
}
return dependencyNotation.equals(lines.get(0));
}
private void tryDeleteDirectory(Path directory) {
project.delete(directory.toFile());
}
private void copyDir(final Path srcDir, final Path destDir) throws IOException {
tryDeleteDirectory(destDir);
Files.createDirectories(destDir);
Files.walkFileTree(srcDir, new FileVisitor<Path>() {
private Path currentDir = destDir;
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (!srcDir.equals(dir)) {
currentDir = currentDir.resolve(dir.getFileName().toString());
Files.createDirectory(currentDir);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = file.getFileName().toString();
String normName = fileName.toLowerCase(Locale.ROOT);
if (normName.endsWith(".jar")) {
safeCopy(file, currentDir.resolve(fileName));
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
currentDir = currentDir != null ? currentDir.getParent() : null;
return FileVisitResult.CONTINUE;
}
});
}
private void extract() throws IOException {
if (isExtracted()) {
return;
}
Path zipFile = dependencyContainer.getSingleFile().toPath();
FileSystem zipFS = FileSystems.newFileSystem(zipFile, null);
Path root = zipFS.getRootDirectories().iterator().next();
Path libDir = resolve(root, subDirPath);
copyDir(libDir, getExtractedFilesDir());
markExtracted();
}
@Override
public String getDisplayName() {
return "UnZip: " + dependencyNotation;
}
@Override
public Set<File> getFiles() {
extractSafe();
return project.fileTree(getExtractedFilesDir().toFile()).getFiles();
}
}