package org.ofdrw.pkg.container; import org.apache.commons.io.FileUtils; import org.dom4j.DocumentException; import org.dom4j.Element; import org.ofdrw.core.DefaultElementProxy; import org.ofdrw.core.basicType.ST_Loc; import org.ofdrw.pkg.tool.ElemCup; import java.io.Closeable; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.function.Function; /** * 虚拟容器对象 * * @author 权观宇 * @since 2020-04-02 19:01:04 */ public class VirtualContainer implements Closeable { /** * 文件根路径(完整路径包含当前文件名) */ private String fullPath; /** * 目录名称 */ private String name; /** * 所属容器 */ private VirtualContainer parent; /** * 文件缓存 */ private Map<String, Element> fileCache; /** * 目录中的虚拟容器缓存 */ private Map<String, VirtualContainer> dirCache; /** * 获取虚拟容器的名称 * * @return 名称 */ public String getContainerName() { return name; } private VirtualContainer() { fileCache = new HashMap<>(7); dirCache = new HashMap<>(5); this.parent = this; } /** * 通过完整路径构造一个虚拟容器 * * @param fullDir 容器完整路径 * @throws IllegalArgumentException 参数错误 */ public VirtualContainer(Path fullDir) throws IllegalArgumentException { this(); if (fullDir == null) { throw new IllegalArgumentException("完整路径(fullDir)为空"); } // 目录不存在或不是一个目录 if (Files.notExists(fullDir) || !Files.isDirectory(fullDir)) { try { // 创建目录 fullDir = Files.createDirectories(fullDir); } catch (IOException e) { throw new RuntimeException("无法创建指定目录", e); } } this.fullPath = fullDir.toAbsolutePath().toString(); this.name = fullDir.getFileName().toString(); } /** * 创建一个虚拟容器 * * @param parent 根目录 * @param dirName 新建目录的名称 * @throws IllegalArgumentException 参数异常 */ public VirtualContainer(Path parent, String dirName) throws IllegalArgumentException { this(); if (parent == null) { throw new IllegalArgumentException("根路径(parent)为空"); } Path fullPath = Paths.get(parent.toAbsolutePath().toString(), dirName); if (Files.notExists(fullPath) || !Files.isDirectory(fullPath)) { try { fullPath = Files.createDirectories(fullPath); } catch (IOException e) { throw new RuntimeException("无法创建指定目录", e); } } if (!Files.isDirectory(parent)) { throw new IllegalStateException("请传入基础目录路径,而不是文件"); } this.fullPath = fullPath.toAbsolutePath().toString(); this.name = dirName; } /** * 获取当前容器完整路径 * * @return 容器完整路径(绝对路径) */ public String getSysAbsPath() { return fullPath; } /** * 向虚拟容器中加入文件 * * @param file 文件路径对象 * @return this * @throws IOException IO异常 */ public VirtualContainer putFile(Path file) throws IOException { if (file == null || Files.notExists(file) || Files.isDirectory(file)) { // 为空或是一个文件夹,或者不存在 return this; } String fileName = file.getFileName().toString(); Path target = Paths.get(fullPath, fileName); // 如果文件已经在目录中那么不做任何事情 if (target.toAbsolutePath().toString() .equals(file.toAbsolutePath().toString())) { return this; } // 复制文件到指定目录 Files.copy(file, target); return this; } /** * 向虚拟容器加入对象 * * @param fileName 文件名 * @param element 元素对象 * @return this */ public VirtualContainer putObj(String fileName, Element element) { if (fileName == null || fileName.length() == 0) { throw new IllegalArgumentException("文件名不能为空"); } if (element == null) { return this; } while (element instanceof DefaultElementProxy) { // 如果是代理元素对象那么取出被代理的对象存储 element = ((DefaultElementProxy) element).getProxy(); } fileCache.put(fileName, element); return this; } /** * 通过文件名获取元素对象 * * @param fileName 文件名 * @return 元素对象(不含代理) * @throws FileNotFoundException 文件不存在 * @throws DocumentException 元素序列化异常 */ public Element getObj(String fileName) throws FileNotFoundException, DocumentException { if (fileName == null || fileName.length() == 0) { throw new IllegalArgumentException("文件名不能为空"); } Element element = fileCache.get(fileName); if (element == null) { // 缓存中不存在,从文件目录中尝试读取 Path file = getFile(fileName); // 反序列化文件为对象 element = ElemCup.inject(file); // 从文件加载元素,那么缓存该元素对象 fileCache.put(fileName, element); } return element; } /** * 获取容器中的文件对象 * * @param fileName 文件名称 * @return 文件路径对象 * @throws FileNotFoundException 文件不存在 */ public Path getFile(String fileName) throws FileNotFoundException { if (fileName == null || fileName.length() == 0) { throw new IllegalArgumentException("文件名为空"); } Path res = Paths.get(fullPath, fileName); if (Files.isDirectory(res) || Files.notExists(res)) { throw new FileNotFoundException("无法在目录: " + fullPath + "中找到,文件 [ " + fileName + " ]"); } return res; } /** * 获取一个虚拟容器对象 * <p> * 如果容器存在,那么取出元素 * <p> * 如果容器不存在,那么创建一个新的对象 * * @param name 容器名称 * @param mapper 容器构造器引用 * @param <R> 容器子类 * @return 新建或已经存在的容器 */ public <R extends VirtualContainer> R obtainContainer(String name, Function<Path, R> mapper) { if (name == null || name.length() == 0) { throw new IllegalArgumentException("容器名称(name)为空"); } if (mapper == null) { throw new IllegalArgumentException("容器构建对象(mapper)为空"); } // 检查缓存 VirtualContainer target = dirCache.get(name); if (target == null) { Path p = Paths.get(fullPath, name); // 如果目录不存在那么创建,如果已经存在那么就是加载 R ct = mapper.apply(p); // 设置父母路径 ct.setParent(this); // 加入缓存中 dirCache.put(name, ct); return ct; } else { return (R) target; } } /** * 获取虚拟容器 * * @param <R> 容器子类 * @param name 容器名称 * @param mapper 容器构造器引用 * @return 容器对象 * @throws FileNotFoundException 文件不存在 */ public <R extends VirtualContainer> R getContainer(String name, Function<Path, R> mapper) throws FileNotFoundException { Path p = Paths.get(fullPath, name); if (Files.notExists(p) || !Files.isDirectory(p)) { throw new FileNotFoundException("容器内无法找名为:" + name + "目录"); } // 检查缓存 VirtualContainer target = dirCache.get(name); if (target == null) { // 调用指定构造器创建容器对象 R ct = mapper.apply(p); // 设置所属容器为创建者 ct.setParent(this); // 加入缓存中 dirCache.put(name, ct); return ct; } else { return (R) target; } } /** * 获取该容器所属容器 * * @return 所属容器对象 */ public VirtualContainer getParent() { return parent; } /** * 设置所属容器 * * @param parent 容器 * @return this */ protected VirtualContainer setParent(VirtualContainer parent) { this.parent = parent; return this; } /** * 获取虚拟容器所处的文件系统路径 * * @return 文件系统路径 */ public Path getContainerPath() { return Paths.get(fullPath); } /** * 判断文件或对象是否存在 * * @param fileName 文件名称 * @return true - 存在;false - 不存在 */ public boolean exit(String fileName) { if (fileName == null || fileName.length() == 0) { return false; } Element element = fileCache.get(fileName); if (element == null) { // 缓存中不存在,从文件目录中尝试读取 Path res = Paths.get(fullPath, fileName); if (Files.isDirectory(res) || Files.notExists(res)) { return false; } else { return true; } } else { return true; } } /** * 删除整个虚拟容器 */ public void clean() { try { Path path = getContainerPath(); // 删除整个文件目录 if (Files.exists(path)) { FileUtils.deleteDirectory(path.toFile()); } this.fileCache.clear(); this.dirCache.clear(); } catch (Exception e) { System.err.println("容器删除异常: " + e.getMessage()); } } /** * 将缓存中的对象写入到文件系统中 * * @throws IOException 文件读写IO异常 */ public void flush() throws IOException { // 刷新元素对象到指定目录 for (Map.Entry<String, Element> kv : fileCache.entrySet()) { Path filePath = Paths.get(fullPath, kv.getKey()); // 序列化为文件 ElemCup.dump(kv.getValue(), filePath); } // 递归的刷新容器中包含的其他容器 for (VirtualContainer container : dirCache.values()) { container.flush(); } fileCache.clear(); dirCache.clear(); } /** * 从缓存中刷新指定容器到文件系统中 * * @param name 容器名称 * @return this * @throws IOException 写入文件IO异常 */ public VirtualContainer flushContainerByName(String name) throws IOException { if (name == null || name.trim().isEmpty()) { return this; } VirtualContainer virtualContainer = dirCache.get(name); if (virtualContainer != null) { virtualContainer.flush(); } return this; } /** * 从缓存将指定对象写入到文件系统中 * * @param name 文件名称 * @return this * @throws IOException 写入文件IO异常 */ public VirtualContainer flushFileByName(String name) throws IOException { if (name == null || name.trim().isEmpty()) { return this; } Element element = fileCache.get(name); if (element != null) { Path filePath = Paths.get(fullPath, name); ElemCup.dump(element, filePath); } return this; } /** * 获取在容器中的绝对路径 * * @return 绝对路径对象 */ public ST_Loc getAbsLoc() { ST_Loc absRes = null; if (parent == this) { absRes = new ST_Loc("/"); } else { absRes = parent.getAbsLoc().cat(this.name); } return absRes; } @Override public void close() throws IOException { // 删除工作过程中存放于虚拟容器中的文件和目录 flush(); } }