文件池配置

文件池,又名文件組,指一個特定的文件夾內的所有yaml。 該文件夾內的yaml必須符合統一的格式,以確保文件映射能有效運作。

範例

創建文件映射物件,該映射物件必須繼承 GroupConfiguration。 然後,使用 @GroupResource 標註你的文件夾位置。

@GroupResource(folder = "Books")
public class Book extends GroupConfiguration {

    public String title;
    public String author;
    public String description;
    public int pages;
    public List<String> contents;

}

@GroupResource 有兩個屬性,第一個是必填的 folder, 第二個是選填的 preloads. 在 preloads 中,你可以透過填入 文件名稱 (可以多個) 來預先複製 jar 內的文件到插件資料夾內。

@GroupResource(
     folder = "Books",
     preloads = { "internal" } // 假設你 jar 內有 /Books/internal.yml 的文件
)
public class Book extends GroupConfiguration {

    public String title;
    public String author;
    public String description;
    public int pages;
    public List<String> contents;

}

Yaml 文件組 (以書本為範例)

normal.yml
title: "正常的書名"
author: "正常的作者"
description: "真的很正常的一本書"
pages: 666
contents:
    - "這是一本很正常的書本"
    - "為什麼我會說很正常呢"
    - "因為真的很正常"
    - "正常到你察覺不了他的存在"
    - "oh 當你看到這則訊息的時候代表他重載得很正常。"
test.yml
title: "測試書本"
author: "愛測試的作者"
description: "這是一本測試的書"
pages: 9999
contents:
    - "測試的重點"
    - "就是必須被測試"
    - "否則就無法測試"
    - "有測試才有實踐"

註冊文件池

@ELDPlugin(
        registry = TesterRegistry.class,
        lifeCycle = TesterLifeCycle.class
)
public class ELDTester extends ELDBukkitPlugin {

    @Override
    protected void bindServices(ServiceCollection serviceCollection) {
        serviceCollection.addGroupConfiguration(Book.class); //註冊
    }

    @Override
    protected void manageProvider(ManagerProvider provider) {
       

    }
}

然後,便可以開始使用。

使用

使用可以透過 GroupConfig<T> 進行注入,但在標註上並非使用 @Inject,而是使用 @InjectPool

請勿使用 @Inject 注入 GroupConfig<T>, 否則會報錯。

從上述範例當中,我們註冊了 Book 作為 文件池組別,使用則如下範例:

@Commander(
        name = "check",
        description = "book chceck command"
)
public class TestBookCheckCommand implements CommandNode {

    @InjectPool // 使用 @InjectPool 而不是 @Inject
    private GroupConfig<Book> bookGroupConfig; // 注入

    @CommandArg(order = 0)
    private String book;

    @Override
    public void execute(CommandSender commandSender) {
        Optional<Book> gp = bookGroupConfig.findById(book);
        if (gp.isEmpty()){
            commandSender.sendMessage("book "+book+" is not exist.");
            return;
        }
        var bookContent = gp.get();
        commandSender.sendMessage("id: "+bookContent.getId());
        commandSender.sendMessage("書名: "+ bookContent.title);
        commandSender.sendMessage("作者: "+bookContent.author);
        commandSender.sendMessage("書本簡介: "+bookContent.description);
        commandSender.sendMessage("書本總頁數: "+bookContent.pages);
        commandSender.sendMessage("書本內容: ");
        for (int i = 0; i < bookContent.contents.size(); i++) {
            commandSender.sendMessage(i+1+": "+bookContent.contents.get(i));
        }
    }
}

GroupConfig<T> 採用 DAO pattern,其源碼如下:

public interface GroupConfig<T extends GroupConfiguration> {

    /**
     *
     * @return 所有文件實例
     */
    List<T> findAll();

    /**
     * 根據 id 尋找文件實例
     * @param id 標識 id
     * @return 文件實例
     */
    Optional<T> findById(String id);

    /**
     * 保存一個文件,標識 id 不能為 null
     * @param config 文件實例
     */
    void save(T config);

    /**
     * 透過 id 刪除文件
     * @param id 標識 id
     * @return 刪除成功
     */
    boolean deleteById(String id);

    /**
     * 獲取文件池總數量
     * @return 數量
     */
    long totalSize();

    /**
     * 刪除文件
     * @param config 文件實例,id 不能為 null
     * @return 刪除成功
     */
    boolean delete(T config);

    /**
     * 清楚所有快取
     */
    void fetch();

    /**
     * 清楚指定文件的快取
     * @param id 標識 id
     */
    void fetchById(String id);

}

要注意的是,GroupConfig<T> 本身的操作並不是異步的,你需要自行實作異步。但它本身亦有快取功能,能大程度上加快二次獲取。

有時候你可能需要手動去獲取特定類型的 GroupConfig<T>,這時候你應該使用文件池服務

文件池的讀寫操作

v0.1.3 之後,文件池將允許刪除,創建及更新指定文件。

GroupConfig<T> 在獲取指定文件之後,將會把其儲存到快取,直到被刪除和更新。若要手動清除快取,可以調用 fetch 或者調用 fetchById 來清除指定文件。

@Commander(
        name = "reload",
        description = "book reload"
)
public class TestBookReloadCommand implements CommandNode {

    @InjectPool
    private GroupConfig<Book> groupConfig;

    @Override
    public void execute(CommandSender commandSender) {
        groupConfig.fetch(); // 清除快取後,下次獲取將直接從文件加載
        commandSender.sendMessage("reloaded");
    }
}

除此之外,你也可以透過 文件池 創建文件。範例如下:

@Commander(
        name = "add",
        description = "add book"
)
public class TestBookAddCommand implements CommandNode {


    @InjectPool
    private GroupConfig<Book> groupConfig;

    @Inject
    private ScheduleService scheduleService; // 如需使用異步,請自行實現

    @Inject
    private ELDTester plugin;

    @CommandArg(order = 1)
    private String id;

    @CommandArg(order = 1, optional = true)
    private String author = "unknown";

    @Override
    public void execute(CommandSender sender) {
        Book book = new Book();
        book.setId(id); // 記得設置標識 id, 否則會報錯
        book.title = "This is a title with id "+id;
        book.author = author;
        book.description = "book with id "+id;
        book.contents = Arrays.asList("this", "is", "a", "content");

        scheduleService
                .runAsync(plugin, () -> groupConfig.save(book))
                .thenRunSync(v -> sender.sendMessage("save completed")).join();
    }
}

刪除指定文件的範例則如下:

@Commander(
        name = "delete",
        description = "delete book"
)
public class TestBookDeleteCommand  implements CommandNode {

    @Inject
    private ScheduleService scheduleService;

    @Inject
    private ELDTester plugin;

    @InjectPool
    private GroupConfig<Book> groupConfig;


    @CommandArg(order = 1)
    private String id;

    @Override
    public void execute(CommandSender sender) {
        scheduleService
                .callAsync(plugin, () -> groupConfig.deleteById(id))
                .thenRunSync(result -> sender.sendMessage("delete "+(result ? "success" : "failed"))).join();
    }
}

文件池分頁操作

v0.1.4 之後,文件池可以被分頁提取,並提供過濾,頁數,和每頁數量等控制,以更好於界面上呈現。

@Commander(
        name = "page",
        description = "pagination test for books"
)
public class TestBookPageCommand implements CommandNode {

    // 自定義過濾
    private static final Predicate<Path> NO_START_WITH_B = path -> !path.getFileName().toString().startsWith("b");
    private static final Predicate<Path> NO_START_WITH_A = path -> !path.getFileName().toString().startsWith("a");

    @InjectPool
    private GroupConfig<Book> groupConfig;

    @Inject
    private ScheduleService scheduleService;

    @Inject
    private ELDTester plugin;

    @CommandArg(order = 1)
    private int page;

    @CommandArg(order = 2, optional = true)
    private boolean showContent = false;

    @CommandArg(order = 3, optional = true)
    private int size = 10;

    @Override
    public void execute(CommandSender commandSender) {
        int index = Math.max(0, page - 1);
        groupConfig.fetch(); // 使用 fetch 將會清除所有快取並加慢獲取速度,請謹慎使用
        scheduleService.callAsync(plugin, () -> groupConfig.findAll(PageRequest.of(index, size, NO_START_WITH_B))) // 異步仍然需要自己實現
                .thenRunSync(page -> {
                    commandSender.sendMessage("page size: "+page.getContent().size());
                    page.getContent().forEach(book -> commandSender.sendMessage(showContent ? book.toString() : book.getId()));
                    // 透過 Page<T> 實例,你可以獲取到目前頁數,最大頁數,文件池總數量等等的參數
                    commandSender.sendMessage(MessageFormat.format("page {0} / {1}, Total Elements: {2}", page.getCurrentPage()+1, page.getTotalPages(), page.getTotalElements()));
                }).join();
    }
}

最后更新于