diff --git a/.gitignore b/.gitignore index 568c8be..4ebcc43 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ test/ ### 此处忽略了json与xml后缀 *.xml *.json + +### 此处排除证书目录 +certificate/ + ### Eclipse ### .apt_generated .classpath @@ -47,4 +51,4 @@ logs/ /en_language_pack.txt /jnotepadConfig.json /qodana.yaml -.mvn/ \ No newline at end of file +.mvn/ diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 92394cf..ac75d3f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -20,7 +20,6 @@ module org.jcnc.jnotepad { requires org.commonmark; requires javafx.web; - exports org.jcnc.jnotepad; exports org.jcnc.jnotepad.model.enums; exports org.jcnc.jnotepad.app.config; diff --git a/src/main/java/org/jcnc/jnotepad/LunchApp.java b/src/main/java/org/jcnc/jnotepad/LunchApp.java index 896d77f..4a48cf2 100644 --- a/src/main/java/org/jcnc/jnotepad/LunchApp.java +++ b/src/main/java/org/jcnc/jnotepad/LunchApp.java @@ -31,6 +31,8 @@ public class LunchApp extends Application { APPLICATION_MANAGER.setPrimaryStage(primaryStage); // 加载应用程序资源 APPLICATION_MANAGER.loadAppResources(); + // 加载应用程序缓存 + APPLICATION_MANAGER.loadAppCache(); // 初始化应用程序 APPLICATION_MANAGER.initializeApp(); // 初始化默认操作 diff --git a/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java b/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java index af42fb0..cda55ad 100644 --- a/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java +++ b/src/main/java/org/jcnc/jnotepad/app/manager/ApplicationManager.java @@ -12,6 +12,8 @@ import org.jcnc.jnotepad.common.constants.AppConstants; import org.jcnc.jnotepad.common.constants.TextConstants; import org.jcnc.jnotepad.common.manager.ThreadPoolManager; import org.jcnc.jnotepad.controller.ResourceController; +import org.jcnc.jnotepad.controller.cache.CacheController; +import org.jcnc.jnotepad.controller.config.AppConfigController; import org.jcnc.jnotepad.controller.config.PluginConfigController; import org.jcnc.jnotepad.controller.manager.Controller; import org.jcnc.jnotepad.plugin.manager.PluginManager; @@ -76,7 +78,7 @@ public class ApplicationManager { } public void initializeDefaultAction() { - // 使用线程池加载关联文件并创建文本区域 + // 使用加载关联文件并创建文本区域 List rawParameters = application.getParameters().getRaw(); Controller.getInstance().openAssociatedFileAndCreateTextArea(rawParameters); } @@ -96,11 +98,16 @@ public class ApplicationManager { primaryStage.getIcons().add(UiUtil.getAppIcon()); } + /** + * 加载缓存 + */ + public void loadAppCache() { + // 加载缓存 + CacheController.getInstance().loadCaches(); + } + /** * 加载资源 - * - * @apiNote - * @since 2023/9/20 18:29 */ public void loadAppResources() { // 加载资源 @@ -115,12 +122,18 @@ public class ApplicationManager { * @apiNote 在停止程序之前会执行此操作 */ public void stopApp() { - PluginConfigController instance = PluginConfigController.getInstance(); + PluginConfigController pluginConfigController = PluginConfigController.getInstance(); // 刷新插件配置文件 - instance.getConfig().setPlugins(PluginManager.getInstance().getPluginDescriptors()); - instance.writeConfig(); + pluginConfigController.getConfig().setPlugins(PluginManager.getInstance().getPluginDescriptors()); + pluginConfigController.writeConfig(); + // 保存配置文件 + AppConfigController.getInstance().writeConfig(); // 销毁插件可能申请的资源 PluginManager.getInstance().destroyPlugins(); + // 保存已打开的文件标签页 + CenterTabPaneManager.getInstance().saveOpenFileTabs(); + // 将缓存写入本地 + CacheController.getInstance().writeCaches(); // 关闭线程池 threadPool.shutdownNow(); } diff --git a/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java b/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java index 233d7ed..9a77a0b 100644 --- a/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java +++ b/src/main/java/org/jcnc/jnotepad/common/manager/AbstractCacheManager.java @@ -13,7 +13,7 @@ public abstract class AbstractCacheManager { /** * 缓存集合 */ - protected Map> caches; + protected Map caches; /** * 获取全局命名空间 @@ -22,27 +22,41 @@ public abstract class AbstractCacheManager { */ public abstract String getGlobalNamespace(); + /** + * 创建缓存类 + * + * @param group 缓存组 + * @param name 缓存名称 + * @param cacheData 缓存数据 + * @param expirationTime 过期时间 + * @return 缓存类 + * @apiNote 这个方法,只需通过自定义的缓存管理类调用此方法,无需每次指定相同地命名空间 + */ + public Cache createCache(String group, String name, Object cacheData, Long expirationTime) { + return new Cache(getGlobalNamespace(), group, name, cacheData, expirationTime); + } + /** * 获取缓存集合 * * @return 缓存集合 */ - public abstract Map> getCaches(); + public abstract Map getCaches(); /** * 设置缓存集合 * * @param caches 缓存集合 */ - public abstract void setCaches(Map> caches); + public abstract void setCaches(Map caches); /** * 添加缓存 * * @param cache 缓存 */ - public void addCache(Cache cache) { + public void addCache(Cache cache) { String cacheKey = cache.getCacheKey(); // 如果集合中已存在该缓存,则更新读写时间 if (caches.containsKey(cacheKey)) { @@ -57,18 +71,29 @@ public abstract class AbstractCacheManager { * @param cacheKey 缓存key * @return 缓存类 */ - public Cache getCache(String cacheKey) { - if (caches.isEmpty()) { + public Cache getCache(String cacheKey) { + if (caches == null || caches.isEmpty()) { return null; } if (caches.containsKey(cacheKey)) { - Cache cache = caches.get(cacheKey); + Cache cache = caches.get(cacheKey); cache.setLastReadOrWriteTime(System.currentTimeMillis()); return cache; } return null; } + /** + * 获取缓存类 + * + * @param group 组 + * @param name 缓存名 + * @return 缓存类 + */ + public Cache getCache(String group, String name) { + return getCache(Cache.getCacheKey(getGlobalNamespace(), group, name)); + } + /** * 获取缓存数据 * @@ -76,7 +101,7 @@ public abstract class AbstractCacheManager { * @return 缓存类 */ public Object getCacheData(String cacheKey) { - Cache cache = getCache(cacheKey); + Cache cache = getCache(cacheKey); if (cache == null) { return null; } diff --git a/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java b/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java index 67670ee..65161db 100644 --- a/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java +++ b/src/main/java/org/jcnc/jnotepad/common/manager/ApplicationCacheManager.java @@ -22,12 +22,12 @@ public class ApplicationCacheManager extends AbstractCacheManager { } @Override - public Map> getCaches() { + public Map getCaches() { return caches; } @Override - public void setCaches(Map> caches) { + public void setCaches(Map caches) { this.caches = caches; } diff --git a/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java b/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java index f04d8a1..3cbd792 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java +++ b/src/main/java/org/jcnc/jnotepad/controller/cache/CacheController.java @@ -1,12 +1,23 @@ package org.jcnc.jnotepad.controller.cache; +import com.fasterxml.jackson.core.type.TypeReference; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.exception.AppException; import org.jcnc.jnotepad.model.entity.Cache; +import org.jcnc.jnotepad.util.JsonUtil; +import org.jcnc.jnotepad.util.LogUtil; +import org.slf4j.Logger; import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Paths; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.Set; import static org.jcnc.jnotepad.common.constants.AppConstants.DEFAULT_PROPERTY; @@ -17,28 +28,131 @@ import static org.jcnc.jnotepad.common.constants.AppConstants.DEFAULT_PROPERTY; */ public class CacheController { + private static final ApplicationCacheManager APPLICATION_CACHE_MANAGER = ApplicationCacheManager.getInstance(); + Logger logger = LogUtil.getLogger(this.getClass()); + private static final CacheController INSTANCE = new CacheController(); private String cacheDir; private CacheController() { cacheDir = Paths.get(System.getProperty(DEFAULT_PROPERTY), ".jnotepad", "caches").toString(); - loadCaches(); } public static CacheController getInstance() { return INSTANCE; } - private void loadCaches() { + public void loadCaches() { + // 如果本地没有缓存的话,就创建缓存 + if (createCachesIfNotExists()) { + return; + } + + // 检查并获取缓存根目录 + File cacheFileDir = createCacheRootIfNotExist(); + // 获取缓存命名空间 + String[] namespaces = cacheFileDir.list(); + // 如果缓存根目录下为空,则创建缓存 + if (Objects.requireNonNull(namespaces).length == 0) { + APPLICATION_CACHE_MANAGER.setCaches(new HashMap<>(16)); + return; + } + Map caches = new HashMap<>(16); + for (String namespace : namespaces) { + // 获取命名空间对应的文件夹 + File namespaceDir = new File(cacheFileDir, namespace); + // 获取缓存组对应的文件名称列表 + String[] groupNames = namespaceDir.list(); + // 如果命名空间文件夹下没有文件则删除该文件夹 + if (cleanEmptyFileOrFolder(namespaceDir)) { + continue; + } + for (String groupName : Objects.requireNonNull(groupNames)) { + // 获取缓存组对应的文件 + File groupFile = new File(namespaceDir, groupName); + // 清理空文件 + if (cleanEmptyFileOrFolder(groupFile)) { + continue; + } + // 获取缓存 + try { + String cacheJson = Files.readString(groupFile.toPath()); + // 获取缓存集合 + Map cacheMap = JsonUtil.fromJsonString(cacheJson, new TypeReference<>() { + }); + // 设置缓存 + cacheMap.forEach((k, v) -> setUpCache(namespace, groupName, k, v, caches)); + } catch (IOException e) { + logger.error("读取缓存文件出错!", e); + } + } + // 设置缓存 + APPLICATION_CACHE_MANAGER.setCaches(caches); + } } - private void writeCaches(Map> caches) { - File cacheFileDir = new File(cacheDir); - if (!cacheFileDir.exists()) { - cacheFileDir.mkdirs(); + /** + * 设置缓存 + * + * @param namespace 命名空间 + * @param groupName 缓存组 + * @param k 缓存名称 + * @param v 缓存类 + * @param caches 缓存集合 + */ + private void setUpCache(String namespace, String groupName, String k, Cache v, Map caches) { + // 判断缓存是否过期,没有过期才加载进内存 + if (v.getLastReadOrWriteTime() + v.getExpirationTime() > System.currentTimeMillis() || v.getExpirationTime() < 0) { + v.setNamespace(namespace); + v.setGroup(groupName); + v.setName(k); + caches.put(v.getCacheKey(), v); } + } + + /** + * 清理空文件或空文件夹并返回结果 + * + * @param fileOrFolder 文件或文件夹 + * @return 是否清理 + */ + private boolean cleanEmptyFileOrFolder(File fileOrFolder) { + try { + if (fileOrFolder.isFile() && fileOrFolder.length() == 0) { + Files.delete(fileOrFolder.toPath()); + logger.info("删除缓存文件:{}", fileOrFolder); + return true; + } + + if (fileOrFolder.isDirectory() && Objects.requireNonNull(fileOrFolder.list()).length == 0) { + Files.delete(fileOrFolder.toPath()); + logger.info("删除缓存文件夹:{}", fileOrFolder); + return true; + } + } catch (IOException e) { + throw new AppException(e); + } + return false; + } + + /** + * 写缓存(writeCache) + */ + public void writeCaches() { + writeCaches(APPLICATION_CACHE_MANAGER.getCaches()); + } + + /** + * 写缓存 + * + * @param caches 缓存集合 + */ + public void writeCaches(Map caches) { + // 检查并获取缓存根目录 + File cacheFileDir = createCacheRootIfNotExist(); + Map> fileMap = new HashMap<>(16); // 生成缓存 caches.forEach((key, value) -> { // 判断当前命名空间对应目录是否创建 @@ -58,17 +172,61 @@ public class CacheController { throw new AppException(e); } } - + fileMap.computeIfAbsent(groupFile, k -> new HashMap<>(16)); + // 设置需要写入的数据 + fileMap.get(groupFile).put(value.getName(), value); }); - - // + Set fileSet = fileMap.keySet(); + // 清空原来的缓存 + fileSet.forEach(file -> { + try (FileOutputStream fileOutputStream = new FileOutputStream(file)) { + fileOutputStream.write(new byte[0]); + } catch (IOException e) { + throw new AppException(e); + } + }); + // 写入缓存 + for (Map.Entry> entry : fileMap.entrySet()) { + try (FileWriter writer = new FileWriter(entry.getKey(), true)) { + writer.write(JsonUtil.toJsonString(entry.getValue())); + } catch (IOException e) { + throw new AppException(e); + } + } } /** - * 如果缓存 + * 如果不存在,则创建缓存根目录 + * + * @return 缓存根目录 */ - private void createCachesIfNotExists() { - + private File createCacheRootIfNotExist() { + File cacheFileDir = new File(cacheDir); + if (!cacheFileDir.exists()) { + cacheFileDir.mkdirs(); + } + return cacheFileDir; } + /** + * 如果本地没有缓存则创建缓存 + * + * @return 是否创建成功 + */ + private boolean createCachesIfNotExists() { + File cacheFileDir = createCacheRootIfNotExist(); + if (Objects.requireNonNull(cacheFileDir.list()).length == 0) { + APPLICATION_CACHE_MANAGER.setCaches(new HashMap<>(16)); + return true; + } + return false; + } + + public String getCacheDir() { + return cacheDir; + } + + public void setCacheDir(String cacheDir) { + this.cacheDir = cacheDir; + } } diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java index 3f4fbae..9e25d9e 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/OpenFile.java @@ -1,14 +1,14 @@ package org.jcnc.jnotepad.controller.event.handler.menubar; -import javafx.application.Platform; -import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.control.Tab; import javafx.stage.FileChooser; import org.jcnc.jnotepad.app.i18n.UiResourceBundle; import org.jcnc.jnotepad.common.constants.TextConstants; -import org.jcnc.jnotepad.common.manager.ThreadPoolManager; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; +import org.jcnc.jnotepad.model.entity.Cache; +import org.jcnc.jnotepad.model.enums.CacheExpirationTime; import org.jcnc.jnotepad.ui.dialog.factory.impl.BasicFileChooserFactory; import org.jcnc.jnotepad.ui.module.LineNumberTextArea; import org.jcnc.jnotepad.util.EncodingDetector; @@ -24,8 +24,6 @@ import java.io.FileReader; import java.io.IOException; import java.nio.charset.Charset; -import static org.jcnc.jnotepad.common.manager.ThreadPoolManager.threadContSelfSubtracting; - /** * 打开文件的事件处理程序。 *

@@ -34,6 +32,8 @@ import static org.jcnc.jnotepad.common.manager.ThreadPoolManager.threadContSelfS * @author 许轲 */ public class OpenFile implements EventHandler { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); + /** * 处理打开文件事件。 * @@ -41,42 +41,28 @@ public class OpenFile implements EventHandler { */ @Override public void handle(ActionEvent event) { + // 获取缓存 + Cache cache = CACHE_MANAGER.getCache("folder", "openFile"); // 显示文件选择对话框,并获取选中的文件 File file = BasicFileChooserFactory.getInstance().createFileChooser( UiResourceBundle.getContent(TextConstants.OPEN), null, - null, + cache == null ? null : new File((String) cache.getCacheData()), new FileChooser.ExtensionFilter("All types", "*.*")) .showOpenDialog(UiUtil.getAppWindow()); if (file == null) { return; } + // 设置缓存 + if (cache == null) { + CACHE_MANAGER.addCache(CACHE_MANAGER.createCache("folder", "openFile", file.getParent(), CacheExpirationTime.NEVER_EXPIRES.getValue())); + } else { + cache.setCacheData(file.getParent()); + CACHE_MANAGER.addCache(cache); + } openFile(file); } - /** - * 创建打开文件的任务。 - * - * @param file 文件对象 - * @return 打开文件的任务 - */ - public Task createOpenFileTask(File file) { - Task openFileTask = new Task<>() { - @Override - protected Void call() { - getText(file); - return null; - } - - }; - // 设置任务成功完成时的处理逻辑 - openFileTask.setOnSucceeded(e -> threadContSelfSubtracting()); - - // 设置任务失败时的处理逻辑 - openFileTask.setOnFailed(e -> threadContSelfSubtracting()); - return openFileTask; - } - /** * 打开文件。 * @@ -98,7 +84,7 @@ public class OpenFile implements EventHandler { return; } } - ThreadPoolManager.getThreadPool().submit(createOpenFileTask(file)); + getText(file); } /** @@ -110,25 +96,24 @@ public class OpenFile implements EventHandler { LineNumberTextArea textArea = createNewTextArea(); // 检测文件编码 Charset encoding = EncodingDetector.detectEncodingCharset(file); + StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new FileReader(file, encoding))) { - StringBuilder textBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { - if (!textBuilder.isEmpty()) { - textBuilder.append("\n"); + if (!stringBuilder.isEmpty()) { + stringBuilder.append("\n"); } - textBuilder.append(line); + stringBuilder.append(line); } - String text = textBuilder.toString(); + String text = stringBuilder.toString(); LogUtil.getLogger(this.getClass()).info("已调用读取文件功能"); - Platform.runLater(() -> { - textArea.appendText(text); - CenterTab tab = createNewTab(file.getName(), textArea, encoding); - // 设置当前标签页关联本地文件 - tab.setRelevance(true); - tab.setUserData(file); - CenterTabPaneManager.getInstance().addNewTab(tab); - }); + + textArea.appendText(text); + CenterTab tab = createNewTab(file.getName(), textArea, encoding); + // 设置当前标签页关联本地文件 + tab.setRelevance(true); + tab.setUserData(file); + CenterTabPaneManager.getInstance().addNewTab(tab); } catch (IOException ignored) { LogUtil.getLogger(this.getClass()).info("已忽视IO异常!"); } diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java index 57bf5e5..9b4999b 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/RenameFile.java @@ -139,6 +139,7 @@ public class RenameFile implements EventHandler { private void handleRenameRelevanceFile(CenterTab centerTab) { // 获取原始文件对象 File file = (File) centerTab.getUserData(); + // 获取应用窗口并绑定 File newFile = BasicFileChooserFactory.getInstance() .createFileChooser( diff --git a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java index ef71642..54f893c 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java +++ b/src/main/java/org/jcnc/jnotepad/controller/event/handler/menubar/SaveFile.java @@ -5,8 +5,11 @@ import javafx.event.EventHandler; import javafx.stage.FileChooser; import org.jcnc.jnotepad.app.i18n.UiResourceBundle; import org.jcnc.jnotepad.common.constants.TextConstants; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.controller.config.AppConfigController; import org.jcnc.jnotepad.controller.i18n.LocalizationController; +import org.jcnc.jnotepad.model.entity.Cache; +import org.jcnc.jnotepad.model.enums.CacheExpirationTime; import org.jcnc.jnotepad.ui.dialog.factory.impl.BasicFileChooserFactory; import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.UiUtil; @@ -28,6 +31,7 @@ import static org.jcnc.jnotepad.controller.config.AppConfigController.CONFIG_NAM * @author gewuyou */ public class SaveFile implements EventHandler { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); Logger logger = LogUtil.getLogger(this.getClass()); /** @@ -76,13 +80,20 @@ public class SaveFile implements EventHandler { if (selectedTab == null) { return; } + Cache cache = CACHE_MANAGER.getCache("folder", "saveFile"); File file = BasicFileChooserFactory.getInstance().createFileChooser( UiResourceBundle.getContent(TextConstants.SAVE_AS), selectedTab.getText(), - null, + cache == null ? null : new File((String) cache.getCacheData()), new FileChooser.ExtensionFilter("All types", "*.*")) .showSaveDialog(UiUtil.getAppWindow()); if (file != null) { + if (cache == null) { + CACHE_MANAGER.addCache(CACHE_MANAGER.createCache("folder", "saveFile", file.getParent(), CacheExpirationTime.NEVER_EXPIRES.getValue())); + } else { + cache.setCacheData(file.getParent()); + CACHE_MANAGER.addCache(cache); + } LogUtil.getLogger(currentClass).info("正在保存文件: {}", file.getName()); selectedTab.save(file); // 将保存后的文件设置为关联文件 diff --git a/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java b/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java index 0fe6a08..4a8b755 100644 --- a/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java +++ b/src/main/java/org/jcnc/jnotepad/controller/manager/Controller.java @@ -1,8 +1,10 @@ package org.jcnc.jnotepad.controller.manager; import org.jcnc.jnotepad.common.interfaces.ControllerAble; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.controller.event.handler.menubar.NewFile; import org.jcnc.jnotepad.controller.event.handler.menubar.OpenFile; +import org.jcnc.jnotepad.model.entity.Cache; import java.io.File; import java.util.List; @@ -13,6 +15,7 @@ import java.util.List; * @author 许轲 */ public class Controller implements ControllerAble { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); private static final Controller INSTANCE = new Controller(); @@ -35,10 +38,19 @@ public class Controller implements ControllerAble { */ @Override public void openAssociatedFileAndCreateTextArea(List rawParameters) { + // 获取上次打开的页面 + Cache cache = CACHE_MANAGER.getCache("tabs", "centerTabs"); + List fileTab = (List) cache.getCacheData(); +// fileTab.forEach(filePath -> new OpenFile().openFile(new File(filePath))); + for (String filePath : fileTab) { + new OpenFile().openFile(new File(filePath)); + } if (!rawParameters.isEmpty()) { String filePath = rawParameters.get(0); openAssociatedFile(filePath); - } else { + return; + } + if (fileTab.isEmpty()) { new NewFile().addNewFileTab(); } } diff --git a/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java b/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java index 4a52f13..8826144 100644 --- a/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java +++ b/src/main/java/org/jcnc/jnotepad/model/entity/Cache.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; * * @author gewuyou */ -public class Cache { +public class Cache { /** * 命名空间 */ @@ -22,12 +22,13 @@ public class Cache { /** * 缓存名称 */ + @JsonIgnore private String name; /** * 缓存数据 */ - private T cacheData; + private Object cacheData; /** * 过期时间
如果过期时间为负数则永不过期 */ @@ -41,7 +42,7 @@ public class Cache { } - public Cache(String namespace, String group, String name, T cacheData, Long expirationTime) { + public Cache(String namespace, String group, String name, Object cacheData, Long expirationTime) { this.namespace = namespace; this.group = group; this.name = name; @@ -51,12 +52,25 @@ public class Cache { } /** - * 根据缓存key + * 生成缓存key + * + * @param namespace 命名空间 + * @param group 组 + * @param name 缓存名称 + * @return 缓存key + */ + public static String getCacheKey(String namespace, String group, String name) { + return namespace + "." + group + "." + name; + } + + /** + * 获取缓存key * * @return key */ + @JsonIgnore public String getCacheKey() { - return this.getNamespace() + "." + this.getGroup() + "." + this.getName(); + return getCacheKey(namespace, group, name); } public String getNamespace() { @@ -83,11 +97,11 @@ public class Cache { this.name = name; } - public T getCacheData() { + public Object getCacheData() { return cacheData; } - public void setCacheData(T cacheData) { + public void setCacheData(Object cacheData) { this.cacheData = cacheData; } diff --git a/src/main/java/org/jcnc/jnotepad/model/enums/CacheExpirationTime.java b/src/main/java/org/jcnc/jnotepad/model/enums/CacheExpirationTime.java new file mode 100644 index 0000000..9686842 --- /dev/null +++ b/src/main/java/org/jcnc/jnotepad/model/enums/CacheExpirationTime.java @@ -0,0 +1,44 @@ +package org.jcnc.jnotepad.model.enums; + +/** + * 缓存过期时间枚举 + * + * @author gewuyou + */ +public enum CacheExpirationTime { + /** + * 一小时 + */ + ONE_HOUR(60 * 60 * 1000L), + /** + * 一天 + */ + ONE_DAY(24 * ONE_HOUR.value), + /** + * 一周 + */ + ONE_WEEK(7 * ONE_DAY.value), + /** + * 一月 + */ + ONE_MONTH(30 * ONE_DAY.value), + /** + * 一年 + */ + ONE_YEAR(365 * ONE_DAY.value), + + /** + * 永不过期 + */ + NEVER_EXPIRES(-1L); + private final Long value; + + CacheExpirationTime(Long value) { + this.value = value; + } + + public Long getValue() { + return value; + } + +} diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java index 681b8d0..79c9e8e 100644 --- a/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java +++ b/src/main/java/org/jcnc/jnotepad/plugin/PluginLoader.java @@ -99,7 +99,7 @@ public class PluginLoader { */ private static boolean pluginDoesNotExist(PluginDescriptor pluginDescriptor, List configPluginDescriptors) { for (PluginDescriptor configPluginDescriptor : configPluginDescriptors) { - if ((configPluginDescriptor.getName() + configPluginDescriptor.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { + if (configPluginDescriptor.getId().equals(pluginDescriptor.getId())) { return false; } } @@ -110,7 +110,7 @@ public class PluginLoader { Iterator iterator = pluginDescriptors.iterator(); while (iterator.hasNext()) { PluginDescriptor plugin = iterator.next(); - if ((plugin.getName() + plugin.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { + if (plugin.getId().equals(pluginDescriptor.getId())) { if (plugin.getVersion().equals(pluginDescriptor.getVersion())) { return true; } @@ -137,7 +137,7 @@ public class PluginLoader { * @since 2023/9/19 18:45 */ private static boolean disableDoNotLoad(PluginDescriptor pluginDescriptor, List pluginDescriptors, PluginDescriptor configPluginDescriptor) { - if ((configPluginDescriptor.getName() + configPluginDescriptor.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor()) && !configPluginDescriptor.isEnabled()) { + if (configPluginDescriptor.getId().equals(pluginDescriptor.getId()) && !configPluginDescriptor.isEnabled()) { pluginDescriptor.setEnabled(false); pluginDescriptors.add(pluginDescriptor); return true; diff --git a/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java b/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java index adc8e8e..1d41a39 100644 --- a/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java +++ b/src/main/java/org/jcnc/jnotepad/plugin/manager/PluginManager.java @@ -85,7 +85,7 @@ public class PluginManager { try { File pluginJar = new File(path.toString()); PluginDescriptor temp = readPlugin(pluginJar); - if ((temp.getName() + temp.getAuthor()).equals(pluginDescriptor.getName() + pluginDescriptor.getAuthor())) { + if (temp.getId().equals(pluginDescriptor.getId())) { Files.delete(pluginJar.toPath()); } } catch (IOException e) { diff --git a/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java b/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java index 63beb77..90826bf 100644 --- a/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java +++ b/src/main/java/org/jcnc/jnotepad/ui/pluginstage/PluginManagementPane.java @@ -26,8 +26,6 @@ import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; import org.jcnc.jnotepad.model.entity.PluginDescriptor; import org.jcnc.jnotepad.plugin.manager.PluginManager; -import org.jcnc.jnotepad.ui.dialog.AppDialog; -import org.jcnc.jnotepad.ui.dialog.interfaces.DialogButtonAction; import org.jcnc.jnotepad.ui.module.CustomSetButton; import org.jcnc.jnotepad.util.LogUtil; import org.jcnc.jnotepad.util.PopUpUtil; @@ -272,14 +270,11 @@ public class PluginManagementPane extends BorderPane { BooleanProperty booleanProperty = toggleSwitch.selectedProperty(); state.textProperty().bind(Bindings.when(booleanProperty).then("禁用").otherwise("启用")); - uninstallItem.setOnAction(event -> PopUpUtil.warningAlert("卸载", "确定要卸载" + pluginDescriptor.getName() + "吗?", "此操作无法撤销!", new DialogButtonAction() { - @Override - public void handleAction(AppDialog dialog) { - pluginManager.unloadPlugin(pluginDescriptor); - state.setDisable(true); - toggleSwitch.setDisable(true); - dialog.close(); - } + uninstallItem.setOnAction(event -> PopUpUtil.warningAlert("卸载", "确定要卸载" + pluginDescriptor.getName() + "吗?", "此操作无法撤销!", dialog -> { + pluginManager.unloadPlugin(pluginDescriptor); + state.setDisable(true); + toggleSwitch.setDisable(true); + dialog.close(); }, null)); state.getStyleClass().addAll(Styles.ACCENT); state.setPrefWidth(80); diff --git a/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java b/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java index 6e29f38..a83f6ee 100644 --- a/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java +++ b/src/main/java/org/jcnc/jnotepad/util/JsonUtil.java @@ -1,6 +1,7 @@ package org.jcnc.jnotepad.util; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.ObjectMapper; @@ -35,7 +36,7 @@ public class JsonUtil { * 将对象转换为 JSON 字符串。 * * @param o 要转换的对象 - * @return 对象的 JSON 表示,如果转换失败则抛出 AppException 异常 + * @return 对象的 JSON 表示 * @throws AppException 如果转换失败 */ public static String toJsonString(Object o) { @@ -45,4 +46,36 @@ public class JsonUtil { throw new AppException(e); } } + + /** + * 将json字符串解析成对象 + * + * @param json json字符串 + * @param clazz 对象类型 + * @return 对象 + * @throws AppException 如果解析失败 + */ + public static T fromJsonString(String json, Class clazz) { + try { + return OBJECT_MAPPER.readValue(json, clazz); + } catch (JsonProcessingException e) { + throw new AppException(e); + } + } + + /** + * 将json字符串解析成对象 + * + * @param json json字符串 + * @param valueTypeRef 值 类型 引用 + * @return 对象 + * @throws AppException 如果解析失败 + */ + public static T fromJsonString(String json, TypeReference valueTypeRef) { + try { + return OBJECT_MAPPER.readValue(json, valueTypeRef); + } catch (JsonProcessingException e) { + throw new AppException(e); + } + } } diff --git a/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java b/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java index 45ba703..e13b436 100644 --- a/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java +++ b/src/main/java/org/jcnc/jnotepad/views/manager/CenterTabPaneManager.java @@ -1,16 +1,25 @@ package org.jcnc.jnotepad.views.manager; +import javafx.collections.ObservableList; +import javafx.scene.control.Tab; +import org.jcnc.jnotepad.common.manager.ApplicationCacheManager; import org.jcnc.jnotepad.controller.config.AppConfigController; +import org.jcnc.jnotepad.model.enums.CacheExpirationTime; import org.jcnc.jnotepad.views.root.center.main.center.tab.CenterTab; import org.jcnc.jnotepad.views.root.center.main.center.tab.CenterTabPane; import org.jcnc.jnotepad.views.root.top.menu.TopMenuBar; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + /** * 中心标签页窗格管理类 * * @author gewuyou */ public class CenterTabPaneManager { + private static final ApplicationCacheManager CACHE_MANAGER = ApplicationCacheManager.getInstance(); private static final CenterTabPaneManager INSTANCE = new CenterTabPaneManager(); private final CenterTabPane centerTabPane = CenterTabPane.getInstance(); @@ -88,4 +97,18 @@ public class CenterTabPaneManager { selectedTab.setAutoLine(AppConfigController.getInstance().getAutoLineConfig()); bottomStatusBoxManager.updateWhenTabSelected(); } + + public void saveOpenFileTabs() { + // 获取当前所有标签页 + ObservableList tabs = centerTabPane.getTabs(); + List filePaths = new ArrayList<>(); + // 缓存当前打开关联的文件 + tabs.forEach(tab -> { + File file = (File) tab.getUserData(); + if (file != null) { + filePaths.add(file.getPath()); + } + }); + CACHE_MANAGER.addCache(CACHE_MANAGER.createCache("tabs", "centerTabs", filePaths, CacheExpirationTime.NEVER_EXPIRES.getValue())); + } } diff --git a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java index bc4e4b3..c65df7e 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTab.java @@ -91,4 +91,5 @@ public class CenterTab extends Tab { this.setUserData(file); save(); } + } diff --git a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java index 8a774b3..b242380 100644 --- a/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java +++ b/src/main/java/org/jcnc/jnotepad/views/root/center/main/center/tab/CenterTabPane.java @@ -8,7 +8,6 @@ import javafx.scene.control.TabPane; * @author songdragon */ public class CenterTabPane extends TabPane { - private static final CenterTabPane INSTANCE = new CenterTabPane(); private CenterTabPane() {