package org.openlca.ilcd.io; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.util.Iterator; import org.openlca.ilcd.commons.IDataSet; import org.openlca.ilcd.commons.Ref; import org.openlca.ilcd.sources.FileRef; import org.openlca.ilcd.sources.Source; import org.openlca.ilcd.util.Sources; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileStore implements DataStore { private File rootDir; private Logger log = LoggerFactory.getLogger(this.getClass()); private XmlBinder binder = new XmlBinder(); public FileStore(String pathToFolder) { this(new File(pathToFolder)); } public FileStore(File rootDir) { log.trace("Create file store {}", rootDir); this.rootDir = new File(rootDir, "ILCD"); checkRootDir(); } private void checkRootDir() { log.trace("Check root directory {}", rootDir); if (!rootDir.exists()) { rootDir.mkdirs(); } else if (!rootDir.isDirectory()) { throw new IllegalArgumentException("The file " + rootDir + " is not a directory."); } } public void prepareFolder() throws DataStoreException { log.trace("Prepare ILCD folder {}", rootDir); ILCDFolder folder = new ILCDFolder(rootDir); try { folder.makeFolder(); } catch (Exception e) { String message = "Cannot create ILCD folder " + rootDir.getAbsolutePath(); log.error(message, e); throw new DataStoreException(message); } } public File getRootFolder() { return rootDir; } @Override public <T> T get(Class<T> type, String id) throws DataStoreException { log.trace("Get {} for id {} from file", type, id); try { File file = getFile(type, id); if (file != null) { log.trace("Unmarshal from file {}", file); return binder.fromFile(type, file); } log.trace("No file found, return null"); return null; } catch (Exception e) { String message = "Cannot unmarshal file."; log.error(message, e); throw new DataStoreException(message); } } @Override public InputStream getExternalDocument(String sourceId, String fileName) throws DataStoreException { log.trace("Get external document {} for source {}", fileName, sourceId); try { File docDir = new File(rootDir, "external_docs"); File file = new File(docDir, fileName); if (!file.exists()) return null; else return new FileInputStream(file); } catch (Exception e) { throw new DataStoreException("failed to open file " + fileName, e); } } /** * Get the file for the given reference. The file may exist or not. Returns * null if the reference does not contain a valid file location. */ public File getExternalDocument(FileRef ref) { String name = Sources.getFileName(ref); if (name == null) return null; File docDir = new File(rootDir, "external_docs"); if (!docDir.exists()) docDir.mkdirs(); return new File(docDir, name); } @Override public void put(IDataSet ds) throws DataStoreException { if (ds == null) return; log.trace("Store {} in file.", ds); try { File file = newFile(ds.getClass(), ds.getUUID()); binder.toFile(ds, file); } catch (Exception e) { String message = "Cannot store in file"; log.error(message, e); throw new DataStoreException(message); } } public void put(Source source, File[] files) throws DataStoreException { log.trace("Store source {} with files", source); put(source); if (files == null || files.length == 0) return; try { File folder = new File(rootDir, "external_docs"); if (!folder.exists()) folder.mkdirs(); for (File file : files) { File newFile = new File(folder, file.getName()); Files.copy(file.toPath(), newFile.toPath()); } } catch (Exception e) { String message = "Cannot store source files"; log.error(message, e); throw new DataStoreException(message); } } @Override public <T> boolean delete(Class<T> type, String id) throws DataStoreException { log.trace("Delete file if exists for class {} with id {}", type, id); File file = getFile(type, id); if (file == null) return false; else { boolean b = file.delete(); log.trace("Deleted={}", b); return b; } } @Override public <T> Iterator<T> iterator(Class<T> type) throws DataStoreException { File folder = getFolder(type); return new FileIterator<>(type, folder); } @Override public <T> boolean contains(Class<T> type, String id) throws DataStoreException { log.trace("Contains file for class {} with id {}", type, id); File file = getFile(type, id); boolean contains = file != null && file.exists(); log.trace("Contains={}", contains); return contains; } private File newFile(Class<?> clazz, String id) { log.trace("Make file for class {} with id {}", clazz, id); File dir = getFolder(clazz); File file = new File(dir, id + ".xml"); log.trace("New file: {}", file); return file; } public File getFile(Ref ref) { if (ref == null || ref.type == null || ref.uuid == null) return null; return getFile(ref.getDataSetClass(), ref.uuid); } public File getFile(Class<?> clazz, String id) { log.trace("Find file for class {} with id {}", clazz, id); if (clazz == null || id == null) return null; File dir = getFolder(clazz); File file = null; for (File f : dir.listFiles()) { if (f.getName().toLowerCase().contains(id.toLowerCase())) { file = f; break; } } log.trace("Return file: {}", file); return file; } public File getFolder(Class<?> clazz) { String name = Dir.get(clazz); File folder = findFolder(name, rootDir); if (folder == null) { folder = new File(rootDir, name); folder.mkdirs(); } return folder; } private File findFolder(String name, File dir) { log.trace("Search folder {} in {}", name, dir); File folder = null; if (dir.getName().equalsIgnoreCase(name)) { folder = dir; } else { File[] files = dir.listFiles(); int i = 0; while (folder == null && i < files.length) { if (files[i].isDirectory()) { folder = findFolder(name, files[i]); } i++; } } return folder; } @Override public void close() throws IOException { } private class FileIterator<T> implements Iterator<T> { final Class<T> type; int idx = 0; File[] files; FileIterator(Class<T> type, File folder) { this.type = type; if (folder != null && folder.isDirectory()) { files = folder.listFiles(); } } @Override public boolean hasNext() { return files != null && idx < files.length; } @Override public T next() { try { File file = files[idx]; idx++; XmlBinder binder = new XmlBinder(); return binder.fromFile(type, file); } catch (Exception e) { throw new RuntimeException("failed to load unmarshal XML file", e); } } } }