diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..5c98b42
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..14ff0eb
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..25d34a4
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/link/infra/packwiz/installer/DownloadTask.java b/src/main/java/link/infra/packwiz/installer/DownloadTask.java
new file mode 100644
index 0000000..773287c
--- /dev/null
+++ b/src/main/java/link/infra/packwiz/installer/DownloadTask.java
@@ -0,0 +1,5 @@
+package link.infra.packwiz.installer;
+
+class DownloadTask {
+
+}
\ No newline at end of file
diff --git a/src/main/java/link/infra/packwiz/installer/Main.java b/src/main/java/link/infra/packwiz/installer/Main.java
index d514c97..2811a73 100644
--- a/src/main/java/link/infra/packwiz/installer/Main.java
+++ b/src/main/java/link/infra/packwiz/installer/Main.java
@@ -1,22 +1,14 @@
package link.infra.packwiz.installer;
-import java.awt.EventQueue;
-import java.awt.GraphicsEnvironment;
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import javax.swing.JOptionPane;
-import javax.swing.UIManager;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-
import link.infra.packwiz.installer.ui.CLIHandler;
import link.infra.packwiz.installer.ui.IUserInterface;
import link.infra.packwiz.installer.ui.InstallWindow;
+import org.apache.commons.cli.*;
+
+import javax.swing.*;
+import java.awt.*;
+import java.net.URI;
+import java.net.URISyntaxException;
public class Main {
@@ -28,13 +20,11 @@ public class Main {
this.startup(args);
} catch (Exception e) {
e.printStackTrace();
- EventQueue.invokeLater(new Runnable() {
- public void run() {
- JOptionPane.showMessageDialog(null,
- "A fatal error occurred: \n" + e.getClass().getCanonicalName() + ": " + e.getMessage(),
- "packwiz-installer", JOptionPane.ERROR_MESSAGE);
- System.exit(1);
- }
+ EventQueue.invokeLater(() -> {
+ JOptionPane.showMessageDialog(null,
+ "A fatal error occurred: \n" + e.getClass().getCanonicalName() + ": " + e.getMessage(),
+ "packwiz-installer", JOptionPane.ERROR_MESSAGE);
+ System.exit(1);
});
// In case the eventqueue is broken, exit after 1 minute
try {
@@ -127,14 +117,12 @@ public class Main {
} catch (Exception e) {
// TODO: better error message?
ui.handleExceptionAndExit(e);
- return;
}
}
});
} catch (Exception e) {
// TODO: better error message?
ui.handleExceptionAndExit(e);
- return;
}
}
diff --git a/src/main/java/link/infra/packwiz/installer/RequiresBootstrap.java b/src/main/java/link/infra/packwiz/installer/RequiresBootstrap.java
index ff56ed2..813a37b 100644
--- a/src/main/java/link/infra/packwiz/installer/RequiresBootstrap.java
+++ b/src/main/java/link/infra/packwiz/installer/RequiresBootstrap.java
@@ -1,10 +1,8 @@
package link.infra.packwiz.installer;
+import javax.swing.*;
import java.util.Arrays;
-import javax.swing.JOptionPane;
-import javax.swing.UIManager;
-
public class RequiresBootstrap {
public static void main(String[] args) {
@@ -15,10 +13,10 @@ public class RequiresBootstrap {
if (Arrays.stream(args).map(str -> {
if (str == null) return "";
if (str.startsWith("--")) {
- return str.substring(2, str.length());
+ return str.substring(2);
}
if (str.startsWith("-")) {
- return str.substring(1, str.length());
+ return str.substring(1);
}
return "";
}).anyMatch(str -> str.equals("g") || str.equals("no-gui"))) {
diff --git a/src/main/java/link/infra/packwiz/installer/UpdateManager.java b/src/main/java/link/infra/packwiz/installer/UpdateManager.java
index a840d63..c9ceacd 100644
--- a/src/main/java/link/infra/packwiz/installer/UpdateManager.java
+++ b/src/main/java/link/infra/packwiz/installer/UpdateManager.java
@@ -1,36 +1,11 @@
package link.infra.packwiz.installer;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CompletionService;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorCompletionService;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.stream.Collectors;
-
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
import com.moandjiezana.toml.Toml;
-
import link.infra.packwiz.installer.metadata.IndexFile;
import link.infra.packwiz.installer.metadata.ManifestFile;
import link.infra.packwiz.installer.metadata.PackFile;
@@ -44,6 +19,16 @@ import okio.Buffer;
import okio.Okio;
import okio.Source;
+import java.io.*;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.stream.Collectors;
+
public class UpdateManager {
public final Options opts;
@@ -84,8 +69,8 @@ public class UpdateManager {
return true;
}
if (this.depSides != null) {
- for (int i = 0; i < this.depSides.length; i++) {
- if (this.depSides[i].equals(tSide)) {
+ for (Side depSide : this.depSides) {
+ if (depSide.equals(tSide)) {
return true;
}
}
@@ -96,7 +81,7 @@ public class UpdateManager {
public static Side from(String name) {
String lowerName = name.toLowerCase();
for (Side side : Side.values()) {
- if (side.sideName == lowerName) {
+ if (side.sideName.equals(lowerName)) {
return side;
}
}
@@ -150,9 +135,7 @@ public class UpdateManager {
List invalidatedUris = new ArrayList<>();
if (manifest.cachedFiles != null) {
- Iterator> it = manifest.cachedFiles.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry entry = it.next();
+ for (Map.Entry entry : manifest.cachedFiles.entrySet()) {
if (entry.getValue().cachedLocation != null) {
if (!Files.exists(Paths.get(opts.packFolder, entry.getValue().cachedLocation))) {
URI fileUri = entry.getKey();
@@ -225,7 +208,7 @@ public class UpdateManager {
}
if (manifest.cachedFiles == null) {
- manifest.cachedFiles = new HashMap();
+ manifest.cachedFiles = new HashMap<>();
}
ui.submitProgress(new InstallProgress("Checking local files..."));
@@ -233,7 +216,7 @@ public class UpdateManager {
while (it.hasNext()) {
Map.Entry entry = it.next();
if (entry.getValue().cachedLocation != null) {
- if (!indexFile.files.stream().anyMatch(f -> f.file.equals(entry.getKey()))) {
+ if (indexFile.files.stream().noneMatch(f -> f.file.equals(entry.getKey()))) {
// File has been removed from the index
try {
Files.deleteIfExists(Paths.get(opts.packFolder, entry.getValue().cachedLocation));
@@ -248,12 +231,11 @@ public class UpdateManager {
ui.submitProgress(new InstallProgress("Comparing new files..."));
// TODO: progress bar
- ConcurrentLinkedQueue exceptionQueue = new ConcurrentLinkedQueue();
- List newFiles = indexFile.files.stream().map(f -> {
+ ConcurrentLinkedQueue exceptionQueue = new ConcurrentLinkedQueue<>();
+ List newFiles = indexFile.files.stream().peek(f -> {
if (f.hashFormat == null || f.hashFormat.length() == 0) {
f.hashFormat = indexFile.hashFormat;
}
- return f;
}).filter(f -> {
if (invalidatedUris.contains(f.file)) {
return true;
@@ -267,13 +249,12 @@ public class UpdateManager {
return false;
}
return cachedFile == null || !newHash.equals(cachedFile.hash);
- }).parallel().map(f -> {
+ }).parallel().peek(f -> {
try {
f.downloadMeta(indexFile, indexUri);
} catch (Exception e) {
exceptionQueue.add(e);
}
- return f;
}).collect(Collectors.toList());
for (Exception e : exceptionQueue) {
@@ -284,81 +265,78 @@ public class UpdateManager {
// TODO: present options
// TODO: all options should be presented, not just new files!!!!!!!
// and options should be readded to newFiles after option -> true
- newFiles.stream().filter(f -> f.linkedFile != null).filter(f -> f.linkedFile.option != null).map(f -> {
- return "option: " + (f.linkedFile.option.description == null ? "null" : f.linkedFile.option.description);
- }).forEachOrdered(desc -> {
+ newFiles.stream().filter(f -> f.linkedFile != null).filter(f -> f.linkedFile.option != null).map(f ->
+ "option: " + (f.linkedFile.option.description == null ? "null" : f.linkedFile.option.description)
+ ).forEachOrdered(desc -> {
System.out.println(desc);
});
// TODO: different thread pool type?
ExecutorService threadPool = Executors.newFixedThreadPool(10);
- CompletionService completionService = new ExecutorCompletionService(
- threadPool);
+ CompletionService completionService = new ExecutorCompletionService<>(threadPool);
for (IndexFile.File f : newFiles) {
ManifestFile.File cachedFile = manifest.cachedFiles.get(f.file);
- completionService.submit(new Callable() {
- public DownloadCompletion call() {
- DownloadCompletion dc = new DownloadCompletion();
- dc.file = f;
+ completionService.submit(() -> {
+ DownloadCompletion dc = new DownloadCompletion();
+ dc.file = f;
- if (cachedFile != null && cachedFile.linkedFileHash != null && f.linkedFile != null) {
- try {
- if (cachedFile.linkedFileHash.equals(f.linkedFile.getHash())) {
- // Do nothing, the file didn't change
- // TODO: but if the hash of the metafile changed, what did change?????
- // should this be checked somehow??
- return dc;
- }
- } catch (Exception e) {}
- }
-
- Path destPath = Paths.get(opts.packFolder, f.getDestURI().toString());
-
- // Don't update files marked with preserve if they already exist on disk
- if (f.preserve) {
- if (Files.exists(destPath)) {
+ if (cachedFile != null && cachedFile.linkedFileHash != null && f.linkedFile != null) {
+ try {
+ if (cachedFile.linkedFileHash.equals(f.linkedFile.getHash())) {
+ // Do nothing, the file didn't change
+ // TODO: but if the hash of the metafile changed, what did change?????
+ // should this be checked somehow??
return dc;
}
- }
+ } catch (Exception ignored) {}
+ }
- try {
- Hash hash;
- String fileHashFormat;
- if (f.linkedFile != null) {
- hash = f.linkedFile.getHash();
- fileHashFormat = f.linkedFile.download.hashFormat;
- } else {
- hash = f.getHash();
- fileHashFormat = f.hashFormat;
- }
+ Path destPath = Paths.get(opts.packFolder, f.getDestURI().toString());
- Source src = f.getSource(indexUri);
- GeneralHashingSource fileSource = HashUtils.getHasher(fileHashFormat).getHashingSource(src);
- Buffer data = new Buffer();
- Okio.buffer(fileSource).readAll(data);
-
- if (fileSource.hashIsEqual(hash)) {
- Files.createDirectories(destPath.getParent());
- Files.copy(data.inputStream(), destPath, StandardCopyOption.REPLACE_EXISTING);
- } else {
- System.out.println("Invalid hash for " + f.getDestURI().toString());
- System.out.println("Calculated: " + fileSource.getHash());
- System.out.println("Expected: " + hash);
- dc.err = new Exception("Hash invalid!");
- }
-
- if (cachedFile != null && !destPath.equals(Paths.get(opts.packFolder, cachedFile.cachedLocation))) {
- // Delete old file if location changes
- Files.delete(Paths.get(opts.packFolder, cachedFile.cachedLocation));
- }
-
- return dc;
- } catch (Exception e) {
- dc.err = e;
+ // Don't update files marked with preserve if they already exist on disk
+ if (f.preserve) {
+ if (Files.exists(destPath)) {
return dc;
}
}
+
+ try {
+ Hash hash;
+ String fileHashFormat;
+ if (f.linkedFile != null) {
+ hash = f.linkedFile.getHash();
+ fileHashFormat = f.linkedFile.download.hashFormat;
+ } else {
+ hash = f.getHash();
+ fileHashFormat = f.hashFormat;
+ }
+
+ Source src = f.getSource(indexUri);
+ GeneralHashingSource fileSource = HashUtils.getHasher(fileHashFormat).getHashingSource(src);
+ Buffer data = new Buffer();
+ Okio.buffer(fileSource).readAll(data);
+
+ if (fileSource.hashIsEqual(hash)) {
+ Files.createDirectories(destPath.getParent());
+ Files.copy(data.inputStream(), destPath, StandardCopyOption.REPLACE_EXISTING);
+ } else {
+ System.out.println("Invalid hash for " + f.getDestURI().toString());
+ System.out.println("Calculated: " + fileSource.getHash());
+ System.out.println("Expected: " + hash);
+ dc.err = new Exception("Hash invalid!");
+ }
+
+ if (cachedFile != null && !destPath.equals(Paths.get(opts.packFolder, cachedFile.cachedLocation))) {
+ // Delete old file if location changes
+ Files.delete(Paths.get(opts.packFolder, cachedFile.cachedLocation));
+ }
+
+ return dc;
+ } catch (Exception e) {
+ dc.err = e;
+ return dc;
+ }
});
}
diff --git a/src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java b/src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java
index 26d4985..ab0349a 100644
--- a/src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java
+++ b/src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java
@@ -1,13 +1,8 @@
package link.infra.packwiz.installer.metadata;
-import java.net.URI;
-import java.nio.file.Paths;
-import java.util.List;
-
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.moandjiezana.toml.Toml;
-
import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource;
import link.infra.packwiz.installer.metadata.hash.Hash;
import link.infra.packwiz.installer.metadata.hash.HashUtils;
@@ -15,6 +10,10 @@ import link.infra.packwiz.installer.request.HandlerManager;
import okio.Okio;
import okio.Source;
+import java.net.URI;
+import java.nio.file.Paths;
+import java.util.List;
+
public class IndexFile {
@SerializedName("hash-format")
public String hashFormat;
@@ -69,6 +68,7 @@ public class IndexFile {
public Hash getHash() throws Exception {
if (hash == null) {
+ // TODO: should these be more specific exceptions (e.g. IndexFileException?!)
throw new Exception("Index file doesn't have a hash");
}
if (hashFormat == null) {
@@ -90,7 +90,8 @@ public class IndexFile {
if (file != null) {
return Paths.get(file.getPath()).getFileName().toString();
}
- return file.getPath();
+ // TODO: throw some kind of exception?
+ return "Invalid file";
}
public URI getDestURI() {
diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java
index ea8c985..23767bf 100644
--- a/src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java
+++ b/src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java
@@ -48,7 +48,7 @@ public class HashingSourceHasher implements IHasher {
if (value != null) {
return value.equals(objHash.value);
} else {
- return objHash.value == null ? true : false;
+ return objHash.value == null;
}
}
diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java
index 64139da..0964c0b 100644
--- a/src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java
+++ b/src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java
@@ -1,10 +1,10 @@
package link.infra.packwiz.installer.metadata.hash;
-import java.io.IOException;
-
import okio.Buffer;
import okio.Source;
+import java.io.IOException;
+
public class Murmur2Hasher implements IHasher {
private class Murmur2GeneralHashingSource extends GeneralHashingSource {
Murmur2Hash value;
@@ -40,8 +40,7 @@ public class Murmur2Hasher implements IHasher {
private byte[] computeNormalizedArray(byte[] input) {
byte[] output = new byte[input.length];
int num = 0;
- for (int i = 0; i < input.length; i++) {
- byte b = input[i];
+ for (byte b : input) {
if (!(b == 9 || b == 10 || b == 13 || b == 32)) {
output[num] = b;
num++;
@@ -54,7 +53,7 @@ public class Murmur2Hasher implements IHasher {
}
- private class Murmur2Hash extends Hash {
+ private static class Murmur2Hash extends Hash {
int value;
private Murmur2Hash(String value) {
// Parsing as long then casting to int converts values gt int max value but lt uint max value
diff --git a/src/main/java/link/infra/packwiz/installer/request/IRequestHandler.java b/src/main/java/link/infra/packwiz/installer/request/IRequestHandler.java
index 30d5cdc..23e0abb 100644
--- a/src/main/java/link/infra/packwiz/installer/request/IRequestHandler.java
+++ b/src/main/java/link/infra/packwiz/installer/request/IRequestHandler.java
@@ -1,17 +1,17 @@
package link.infra.packwiz.installer.request;
-import java.net.URI;
-
import okio.Source;
+import java.net.URI;
+
/**
* IRequestHandler handles requests for locations specified in modpack metadata.
*/
public interface IRequestHandler {
- public boolean matchesHandler(URI loc);
+ boolean matchesHandler(URI loc);
- public default URI getNewLoc(URI loc) {
+ default URI getNewLoc(URI loc) {
return loc;
}
@@ -22,6 +22,6 @@ public interface IRequestHandler {
* @return The Source containing the data of the file
* @throws Exception
*/
- public Source getFileSource(URI loc) throws Exception;
+ Source getFileSource(URI loc) throws Exception;
}
diff --git a/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerGithub.java b/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerGithub.java
index 15f35dd..33c8948 100644
--- a/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerGithub.java
+++ b/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerGithub.java
@@ -19,14 +19,17 @@ public class RequestHandlerGithub extends RequestHandlerZip {
}
// TODO: is caching really needed, if HTTPURLConnection follows redirects correctly?
- private Map zipUriMap = new HashMap();
- final ReentrantReadWriteLock zipUriLock = new ReentrantReadWriteLock();
+ private Map zipUriMap = new HashMap<>();
+ private final ReentrantReadWriteLock zipUriLock = new ReentrantReadWriteLock();
private static Pattern repoMatcherPattern = Pattern.compile("/([\\w.-]+/[\\w.-]+).*");
private String getRepoName(URI loc) {
Matcher matcher = repoMatcherPattern.matcher(loc.getPath());
- matcher.matches();
- return matcher.group(1);
+ if (matcher.matches()) {
+ return matcher.group(1);
+ } else {
+ return null;
+ }
}
@Override
@@ -57,8 +60,11 @@ public class RequestHandlerGithub extends RequestHandlerZip {
private String getBranch(URI loc) {
Matcher matcher = branchMatcherPattern.matcher(loc.getPath());
- matcher.matches();
- return matcher.group(1);
+ if (matcher.matches()) {
+ return matcher.group(1);
+ } else {
+ return null;
+ }
}
@Override
diff --git a/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerZip.java b/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerZip.java
index 0217ce7..7e307d0 100644
--- a/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerZip.java
+++ b/src/main/java/link/infra/packwiz/installer/request/handlers/RequestHandlerZip.java
@@ -1,5 +1,10 @@
package link.infra.packwiz.installer.request.handlers;
+import okio.Buffer;
+import okio.BufferedSource;
+import okio.Okio;
+import okio.Source;
+
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -11,14 +16,9 @@ import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-import okio.Buffer;
-import okio.BufferedSource;
-import okio.Okio;
-import okio.Source;
-
public abstract class RequestHandlerZip extends RequestHandlerHTTP {
- protected final boolean modeHasFolder;
+ private final boolean modeHasFolder;
public RequestHandlerZip(boolean modeHasFolder) {
this.modeHasFolder = modeHasFolder;
@@ -35,14 +35,14 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
private class ZipReader {
private final ZipInputStream zis;
- private final Map readFiles = new HashMap();
+ private final Map readFiles = new HashMap<>();
// Write lock implies access to ZipInputStream - only 1 thread must read at a time!
final ReentrantLock filesLock = new ReentrantLock();
private ZipEntry entry;
private final BufferedSource zipSource;
- public ZipReader(Source zip) {
+ ZipReader(Source zip) {
zis = new ZipInputStream(Okio.buffer(zip).inputStream());
zipSource = Okio.buffer(Okio.source(zis));
}
@@ -71,7 +71,7 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
}
}
- public Source getFileSource(URI loc) throws Exception {
+ Source getFileSource(URI loc) throws Exception {
filesLock.lock();
// Assume files are only read once, allow GC by removing
Buffer file = readFiles.remove(loc);
@@ -82,13 +82,10 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
file = findFile(loc);
filesLock.unlock();
- if (file != null) {
- return file;
- }
- return null;
+ return file;
}
- public URI findInZip(Predicate matches) throws Exception {
+ URI findInZip(Predicate matches) throws Exception {
filesLock.lock();
for (URI file : readFiles.keySet()) {
if (matches.test(file)) {
@@ -115,8 +112,8 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
}
- private final Map cache = new HashMap();
- final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
+ private final Map cache = new HashMap<>();
+ private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
protected abstract URI getZipUri(URI loc) throws Exception;