package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier; import net.knarcraft.stargate.config.material.MaterialSpecifier; import org.bukkit.Material; import org.bukkit.configuration.InvalidConfigurationException; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Scanner; /** * Helper class for reading gate files */ public final class GateReader { private GateReader() { } /** * Reads a gate file * * @param scanner

The scanner to read from

* @param characterMaterialMap

The map of characters to store valid symbols in

* @param fileName

The filename of the loaded gate config file

* @param design

The list to store the loaded design/layout to

* @param config

The map of config values to store to

* @return

The column count/width of the loaded gate

*/ public static int readGateFile(@NotNull Scanner scanner, @NotNull Map> characterMaterialMap, @NotNull String fileName, @NotNull List> design, @NotNull Map config) { boolean designing = false; int columns = 0; try (scanner) { while (scanner.hasNextLine()) { String line = scanner.nextLine(); if (designing) { //If we have reached the gate's layout/design, read it columns = readGateDesignLine(line, columns, characterMaterialMap, fileName, design); if (columns < 0) { return -1; } } else { if (!line.isEmpty() && !line.startsWith("#")) { //Read a normal config value readGateConfigValue(line, characterMaterialMap, config); } else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) { //An empty line marks the start of the gate's layout/design designing = true; } } } } catch (Exception exception) { Stargate.logSevere(String.format("Could not load Gate %s - %s", fileName, exception.getMessage())); return -1; } return columns; } /** * Reads one design line of the gate layout file * *

The max columns value is sent through this method in such a way that when the last gate design line is read, * the max columns value contains the largest amount of columns (character) found in any of the design's lines.

* * @param line

The line to read

* @param maxColumns

The current max columns value of the design

* @param characterMaterialMap

The map between characters and the corresponding materials to use

* @param fileName

The filename of the loaded gate config file

* @param design

The two-dimensional list to store the loaded design to

* @return

The new max columns value of the design

*/ private static int readGateDesignLine(@NotNull String line, int maxColumns, @NotNull Map> characterMaterialMap, @NotNull String fileName, @NotNull List> design) { List row = new ArrayList<>(); //Update the max columns number if this line has more columns if (line.length() > maxColumns) { maxColumns = line.length(); } for (Character symbol : line.toCharArray()) { //Refuse read gate designs with unknown characters if (symbol.equals('?') || !characterMaterialMap.containsKey(symbol)) { Stargate.logSevere(String.format("Could not load Gate %s - Unknown symbol '%s' in diagram", fileName, symbol)); return -1; } //Add the read character to the row row.add(symbol); } //Add this row of the gate's design to the two-dimensional design list design.add(row); return maxColumns; } /** * Reads one config value from the gate layout file * * @param line

The line to read

* @param characterMaterialMap

The character to material map to store to

* @param config

The config value map to store to

* @throws InvalidConfigurationException

If an invalid material is encountered

*/ private static void readGateConfigValue(@NotNull String line, @NotNull Map> characterMaterialMap, @NotNull Map config) throws InvalidConfigurationException { String[] split = line.split("="); String key = split[0].trim(); String value = split[1].trim(); if (key.length() == 1) { //Read a gate frame material Character symbol = key.charAt(0); List materials = MaterialHelper.parseTagsAndMaterials(value); if (!materials.isEmpty()) { characterMaterialMap.put(symbol, materials); } else { throw new InvalidConfigurationException("Invalid material in line: " + line); } } else { //Read a normal config value config.put(key, value); } } /** * Reads an integer configuration value * * @param config

The configuration to read

* @param fileName

The filename of the config file

* @param key

The config key to read

* @return

The read value, or -1 if it could not be read

*/ public static int readGateConfig(@NotNull Map config, @NotNull String fileName, @NotNull String key) { if (config.containsKey(key)) { try { return Integer.parseInt(config.get(key)); } catch (NumberFormatException exception) { Stargate.logWarning(String.format("%s reading %s: %s is not numeric", exception.getClass().getName(), fileName, key)); } } return -1; } /** * Reads a material configuration value * * @param config

The configuration to read

* @param fileName

The filename of the config file

* @param key

The config key to read

* @param defaultMaterial

The default material to use, in case the config is invalid

* @return

The material specified in the config, or the default material if it could not be read

*/ @NotNull public static List readGateConfig(@NotNull Map config, @NotNull String fileName, @NotNull String key, @NotNull Material defaultMaterial) { if (config.containsKey(key)) { List materialSpecifiers = MaterialHelper.parseTagsAndMaterials(config.get(key)); if (!materialSpecifiers.isEmpty()) { return materialSpecifiers; } else { Stargate.logWarning(String.format("Error reading %s: %s is not a material", fileName, key)); } } return List.of(new BukkitMaterialSpecifier(defaultMaterial)); } /** * Generates a matrix containing the gate layout * *

This basically changes the list of lists into a primitive matrix. Additionally, spaces are added to the end of * each row which to too short relative to the longest row.

* * @param design

The design of the gate layout

* @param columns

The largest amount of columns in the design

* @return

A matrix containing the gate's layout

*/ @NotNull public static Character[][] generateLayoutMatrix(@NotNull List> design, int columns) { Character[][] layout = new Character[design.size()][columns]; for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) { List row = design.get(lineIndex); Character[] result = new Character[columns]; for (int rowIndex = 0; rowIndex < columns; rowIndex++) { if (rowIndex < row.size()) { result[rowIndex] = row.get(rowIndex); } else { //Add spaces to all lines which are too short result[rowIndex] = ' '; } } layout[lineIndex] = result; } return layout; } }