// // Created by BONNe // Copyright - 2021 // package world.bentobox.bentobox.api.panels.reader; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.event.inventory.ClickType; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import com.google.common.base.Enums; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.util.ItemParser; /** * This class manages Template file reading, creating PanelTemplateRecord object and storing it internally. * This class just reads and returns given panel template. It does not create a functional panel. * * @since 1.17.3 */ public class TemplateReader { private static final String TITLE = "title"; private static final String ICON = "icon"; private static final String DESCRIPTION = "description"; private static final String BACKGROUND = "background"; private static final String BORDER = "border"; private static final String FORCE_SHOWN = "force-shown"; private static final String FALLBACK = "fallback"; /** * Read template panel panel template record. * * @param panelName the panel name * @param panelLocation the panel location directory * @return the panel template record */ public static PanelTemplateRecord readTemplatePanel(@NonNull String panelName, @NonNull File panelLocation) { if (!panelLocation.exists()) { // Return null because folder does not exist. return null; } File file = new File(panelLocation, panelName.endsWith(".yml") ? panelName : panelName + ".yml"); if (!file.exists()) { // Return as file does not exist. return null; } // Check if panel is already crafted. if (TemplateReader.loadedPanels.containsKey(file.getAbsolutePath())) { return TemplateReader.loadedPanels.get(file.getAbsolutePath()); } PanelTemplateRecord record; try { // Load config YamlConfiguration config = new YamlConfiguration(); config.load(file); // Read panel record = readPanelTemplate(config.getConfigurationSection(panelName)); // Put panel into memory TemplateReader.loadedPanels.put(file.getAbsolutePath(), record); } catch (IOException | InvalidConfigurationException e) { record = null; } return record; } /** * This method reads panel template from given configuration section. * @param configurationSection Section that contains panel template data. * @return Panel Template. */ private static PanelTemplateRecord readPanelTemplate(@Nullable ConfigurationSection configurationSection) { if (configurationSection == null) { // No data to return. return null; } String title = configurationSection.getString(TITLE); Panel.Type type = Enums.getIfPresent(Panel.Type.class, configurationSection.getString("type", "INVENTORY")). or(Panel.Type.INVENTORY); PanelTemplateRecord.TemplateItem borderItem = null; // Read Border Icon. if (configurationSection.isConfigurationSection(BORDER)) { // Process border icon if it contains more options. ConfigurationSection borderSection = configurationSection.getConfigurationSection(BORDER); if (borderSection != null) { borderItem = new PanelTemplateRecord.TemplateItem( ItemParser.parse((borderSection.getString(ICON, Material.AIR.name()))), borderSection.getString(TITLE, null), borderSection.getString(DESCRIPTION, null)); } } else if (configurationSection.isString(BORDER)) { // Process border icon if it contains only icon. borderItem = new PanelTemplateRecord.TemplateItem( ItemParser.parse((configurationSection.getString(BORDER, Material.AIR.name())))); } PanelTemplateRecord.TemplateItem backgroundItem = null; // Read Background block if (configurationSection.isConfigurationSection(BACKGROUND)) { // Process border icon if it contains more options. ConfigurationSection backgroundSection = configurationSection.getConfigurationSection(BACKGROUND); if (backgroundSection != null) { backgroundItem = new PanelTemplateRecord.TemplateItem( ItemParser.parse((backgroundSection.getString(ICON, Material.AIR.name()))), backgroundSection.getString(TITLE, null), backgroundSection.getString(DESCRIPTION, null)); } } else if (configurationSection.isString(BACKGROUND)) { // Process background icon if it contains only icon. backgroundItem = new PanelTemplateRecord.TemplateItem( ItemParser.parse((configurationSection.getString(BACKGROUND, Material.AIR.name())))); } // Read reusable Map panelItemDataMap = new HashMap<>(); ConfigurationSection reusable = configurationSection.getConfigurationSection("reusable"); if (reusable != null) { // Add all reusables to the local storage. reusable.getKeys(false).forEach(key -> readPanelItemTemplate(reusable.getConfigurationSection(key), key, panelItemDataMap)); } // Read content boolean[] forcedRows = readForcedRows(configurationSection); // Create template record. PanelTemplateRecord template = new PanelTemplateRecord(type, title, borderItem, backgroundItem, forcedRows); // Read content ConfigurationSection content = configurationSection.getConfigurationSection("content"); if (content == null) { // Return empty template. return template; } for (int rowIndex = 0; rowIndex < 6; rowIndex++) { // Read each line. if (content.isConfigurationSection(String.valueOf(rowIndex + 1))) { ConfigurationSection line = content.getConfigurationSection(String.valueOf(rowIndex + 1)); if (line != null) { // Populate existing lines with items. for (int columnIndex = 0; columnIndex < 9; columnIndex++) { if (line.isConfigurationSection(String.valueOf(columnIndex + 1))) { // If it contains a section, then build a new button template from it. template.addButtonTemplate(rowIndex, columnIndex, readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1)))); } else if (line.isString(String.valueOf(columnIndex + 1))) { // If it contains just a single word, assume it is a reusable. template.addButtonTemplate(rowIndex, columnIndex, panelItemDataMap.get(line.getString(String.valueOf(columnIndex + 1)))); } } } } } // Garbage collector. panelItemDataMap.clear(); return template; } /** * This method reads force shown rows that must be always displayed. * @param section Configuration section that contains force-shown path. * @return boolean array that contains which lines are force loaded. */ private static boolean[] readForcedRows(@Nullable ConfigurationSection section) { boolean[] forceShow = new boolean[6]; if (section != null && section.contains(FORCE_SHOWN)) { if (section.isInt(FORCE_SHOWN)) { int value = section.getInt(FORCE_SHOWN); if (value > 0 && value < 7) { forceShow[value-1] = true; } } else if (section.isList(FORCE_SHOWN)) { section.getIntegerList(FORCE_SHOWN).forEach(number -> { if (number > 0 && number < 7) { forceShow[number-1] = true; } }); } } return forceShow; } /** * This method creates PanelItemTemplate from a given configuration section. * @param section Section that should contain all information about the panel item template. * @return PanelItemTemplate that should represent button from a section. */ @Nullable private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section) { return readPanelItemTemplate(section, null, null); } /** * This method creates PanelItemTemplate from a given configuration section. * @param section Section that should contain all information about the panel item template. * @return PanelItemTemplate that should represent button from a section. */ @Nullable private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section, String itemKey, Map reusableItemMap) { if (section == null) { // No section, no item. return null; } ItemTemplateRecord fallback; if (section.isConfigurationSection(FALLBACK)) { fallback = readPanelItemTemplate(section.getConfigurationSection(FALLBACK)); } else if (section.isString(FALLBACK) && reusableItemMap != null) { fallback = reusableItemMap.get(section.getString(FALLBACK)); } else { fallback = null; } // Create Item Record ItemTemplateRecord itemRecord = new ItemTemplateRecord(ItemParser.parse(section.getString(ICON)), section.getString(TITLE, null), section.getString(DESCRIPTION, null), fallback); // Read data if (section.isConfigurationSection("data")) { ConfigurationSection dataSection = section.getConfigurationSection("data"); if (dataSection != null) { dataSection.getKeys(false).forEach(key -> itemRecord.addData(key, dataSection.get(key))); } } // Read Click data if (section.isConfigurationSection("actions")) { ConfigurationSection actionSection = section.getConfigurationSection("actions"); if (actionSection != null) { actionSection.getKeys(false).forEach(actionKey -> { ClickType clickType = Enums.getIfPresent(ClickType.class, actionKey.toUpperCase()).orNull(); if (clickType != null) { ConfigurationSection actionDataSection = actionSection.getConfigurationSection(actionKey); if (actionDataSection != null) { ItemTemplateRecord.ActionRecords actionData = new ItemTemplateRecord.ActionRecords(clickType, actionDataSection.getString("type"), actionDataSection.getString("content"), actionDataSection.getString("tooltip")); itemRecord.addAction(actionData); } } }); } } // Add item to the map if (reusableItemMap != null && itemKey != null) { reusableItemMap.put(itemKey, itemRecord); } return itemRecord; } /** * This method clears loaded panels from the cache. */ public static void clearPanels() { loadedPanels.clear(); } /** * This map contains already read panels and their location. * This improves performance for GUI opening, with a some memory usage. */ private static final Map loadedPanels = new HashMap<>(); }