package org.graalvm.component.installer.commands;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import org.graalvm.component.installer.CommonConstants;
import static org.graalvm.component.installer.CommonConstants.WARN_REBUILD_IMAGES;
import org.graalvm.component.installer.Feedback;
import org.graalvm.component.installer.FileOperations;
import org.graalvm.component.installer.SystemUtils;
import org.graalvm.component.installer.model.ComponentInfo;
public class PreRemoveProcess {
private final Path installPath;
private final Feedback feedback;
private final List<ComponentInfo> infos = new ArrayList<>();
private final FileOperations fileOps;
private boolean rebuildPolyglot;
private boolean dryRun;
private boolean ignoreFailedDeletions;
private Set<String> knownPaths;
public PreRemoveProcess(Path instPath, FileOperations fops, Feedback fb) {
this.feedback = fb.withBundle(PreRemoveProcess.class);
installPath = instPath;
fileOps = fops;
}
public boolean isDryRun() {
return dryRun;
}
public PreRemoveProcess setDryRun(boolean dryRun) {
this.dryRun = dryRun;
return this;
}
public boolean isIgnoreFailedDeletions() {
return ignoreFailedDeletions;
}
public PreRemoveProcess setIgnoreFailedDeletions(boolean ignoreFailedDeletions) {
this.ignoreFailedDeletions = ignoreFailedDeletions;
return this;
}
public void addComponentInfo(ComponentInfo info) {
this.infos.add(info);
}
public void run() throws IOException {
for (ComponentInfo ci : infos) {
processComponent(ci);
}
if (rebuildPolyglot && WARN_REBUILD_IMAGES) {
Path p = SystemUtils.fromCommonString(CommonConstants.PATH_JRE_BIN);
feedback.output("INSTALL_RebuildPolyglotNeeded", File.separator, installPath.resolve(p).normalize());
}
}
void deleteOneFile(Path p) throws IOException {
try {
fileOps.deleteFile(p);
} catch (IOException ex) {
if (ignoreFailedDeletions) {
if (Files.isDirectory(p)) {
feedback.error("INSTALL_FailedToDeleteDirectory", ex, p, ex.getLocalizedMessage());
} else {
feedback.error("INSTALL_FailedToDeleteFile", ex, p, ex.getLocalizedMessage());
}
return;
}
throw ex;
}
}
void deleteContentsRecursively(Path rootPath) throws IOException {
if (dryRun) {
return;
}
try (Stream<Path> paths = Files.walk(rootPath)) {
paths.sorted(Comparator.reverseOrder()).forEach((p) -> {
try {
if (!p.equals(rootPath) && shouldDeletePath(p)) {
deleteOneFile(p);
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
});
} catch (UncheckedIOException ex) {
throw ex.getCause();
}
}
private boolean shouldDeletePath(Path toDelete) {
Path rel;
try {
rel = installPath.relativize(toDelete);
} catch (IllegalArgumentException ex) {
return false;
}
String relString = SystemUtils.toCommonPath(rel);
if (Files.isDirectory(toDelete)) {
relString += "/";
}
return knownPaths == null || !knownPaths.contains(relString);
}
void processComponent(ComponentInfo ci) throws IOException {
rebuildPolyglot |= ci.isPolyglotRebuild();
for (String s : ci.getWorkingDirectories()) {
Path p = installPath.resolve(SystemUtils.fromCommonRelative(s));
feedback.verboseOutput("UNINSTALL_DeletingDirectoryRecursively", p);
this.knownPaths = new HashSet<>(ci.getPaths());
deleteContentsRecursively(p);
}
}
}