package org.openlca.jsonld; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openlca.core.model.ModelType; import org.openlca.jsonld.output.Context; import org.openlca.util.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; public class ZipStore implements EntityStore { private final static String CONTEXT_PATH = "context.json"; private final Logger log = LoggerFactory.getLogger(getClass()); private FileSystem zip; public static ZipStore open(File zipFile) throws IOException { return new ZipStore(zipFile); } private ZipStore(File zipFile) throws IOException { String uriStr = zipFile.toURI().toASCIIString(); URI uri = URI.create("jar:" + uriStr); Map<String, String> options = new HashMap<>(); if (!zipFile.exists()) options.put("create", "true"); zip = FileSystems.newFileSystem(uri, options); putContext(); } @Override public void putContext() { JsonObject context = Context.write(Schema.URI); if (context == null) return; try { String json = new Gson().toJson(context); byte[] data = json.getBytes("utf-8"); put(CONTEXT_PATH, data); } catch (Exception e) { log.error("failed to put " + CONTEXT_PATH, e); } } @Override public void putBin(ModelType type, String refId, String filename, byte[] data) { String path = ModelPath.getBin(type, refId) + "/" + filename; put(path, data); } @Override public void put(String path, byte[] data) { if (Strings.nullOrEmpty(path) || data == null) return; try { Path file = zip.getPath(path); Path dir = file.getParent(); if (dir != null && !(Files.exists(dir))) Files.createDirectories(dir); Files.write(file, data, StandardOpenOption.CREATE); } catch (Exception e) { log.error("failed to put " + path, e); } } @Override public void put(ModelType type, JsonObject object) { String refId = getRefId(object); if (type == null || refId == null) return; put(ModelPath.get(type, refId), object); } private void put(String path, JsonObject object) { try { String json = new Gson().toJson(object); byte[] data = json.getBytes("utf-8"); put(path, data); } catch (Exception e) { log.error("failed to add " + path, e); } } private String getRefId(JsonObject obj) { if (obj == null) return null; JsonElement elem = obj.get("@id"); if (elem == null || !elem.isJsonPrimitive()) return null; else return elem.getAsString(); } @Override public boolean contains(ModelType type, String refId) { if (type == null || refId == null) return false; String dirName = ModelPath.get(type); Path dir = zip.getPath(dirName); if (!Files.exists(dir)) return false; Path path = zip.getPath(dirName + "/" + refId + ".json"); return Files.exists(path); } @Override public JsonObject getContext() { byte[] data = get(CONTEXT_PATH); if (data == null) return null; try { return toJsonObject(data); } catch (Exception e) { log.error("failed to read json object " + CONTEXT_PATH, e); return null; } } @Override public byte[] get(String path) { if (Strings.nullOrEmpty(path)) return null; try { Path file = zip.getPath(path); if (!Files.exists(file)) return null; return Files.readAllBytes(file); } catch (Exception e) { log.error("failed to file " + path, e); return null; } } @Override public JsonObject get(ModelType type, String refId) { if (!contains(type, refId)) return null; String path = ModelPath.get(type, refId); byte[] data = get(path); if (data == null) return null; try { return toJsonObject(data); } catch (Exception e) { log.error("failed to read json object " + type + "/" + refId, e); return null; } } private JsonObject toJsonObject(byte[] data) throws Exception { String json = new String(data, "utf-8"); JsonElement e = new Gson().fromJson(json, JsonElement.class); return e.isJsonObject() ? e.getAsJsonObject() : null; } @Override public List<String> getRefIds(ModelType type) { String dirName = ModelPath.get(type); Path dir = zip.getPath(dirName); if (!Files.exists(dir)) return Collections.emptyList(); RefIdCollector collector = new RefIdCollector(); try { Files.walkFileTree(dir, collector); } catch (Exception e) { log.error("failed to get refIds for type " + type, e); } return collector.ids; } @Override public List<String> getBinFiles(ModelType type, String refId) { if (type == null || refId == null) return Collections.emptyList(); Path dir = zip.getPath(ModelPath.getBin(type, refId)); if (!Files.exists(dir)) return Collections.emptyList(); FilePathCollector collector = new FilePathCollector(); try { Files.walkFileTree(dir, collector); } catch (Exception e) { log.error("failed to get bin files for " + type + ": " + refId, e); } return collector.paths; } @Override public void close() throws IOException { zip.close(); } private class RefIdCollector extends SimpleFileVisitor<Path> { private List<String> ids = new ArrayList<>(); @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file == null) return FileVisitResult.CONTINUE; String fileName = file.getFileName().toString(); String refId = fileName.substring(0, fileName.length() - 5); ids.add(refId); return FileVisitResult.CONTINUE; } } private class FilePathCollector extends SimpleFileVisitor<Path> { private List<String> paths = new ArrayList<>(); @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file == null) return FileVisitResult.CONTINUE; paths.add(file.toAbsolutePath().toString()); return FileVisitResult.CONTINUE; } } }