package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; import org.bukkit.Material; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; /** * 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 frameTypes

The set to store frame/border materials to

* @param config

The map of config values to store to

* @return

The column count/width of the loaded gate

*/ public static int readGateFile(Scanner scanner, Map characterMaterialMap, String fileName, List> design, Set frameTypes, Map config) { boolean designing = false; int columns = 0; try { 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, frameTypes, 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; } finally { if (scanner != null) { scanner.close(); } } 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(String line, int maxColumns, Map characterMaterialMap, String fileName, 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 frameTypes

The set to store gate frame/border types to

* @param config

The config value map to store to

* @throws Exception

If an invalid material is encountered

*/ private static void readGateConfigValue(String line, Map characterMaterialMap, Set frameTypes, Map config) throws Exception { 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); Material material = Material.getMaterial(value); if (material == null) { throw new Exception("Invalid material in line: " + line); } //Register the map between the read symbol and the corresponding material characterMaterialMap.put(symbol, material); //Save the material as one of the frame materials used for this kind of gate frameTypes.add(material); } 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(Map config, String fileName, String key) { if (config.containsKey(key)) { try { return Integer.parseInt(config.get(key)); } catch (NumberFormatException ex) { Stargate.logWarning(String.format("%s reading %s: %s is not numeric", ex.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

*/ public static Material readGateConfig(Map config, String fileName, String key, Material defaultMaterial) { if (config.containsKey(key)) { Material material = Material.getMaterial(config.get(key)); if (material != null) { return material; } else { Stargate.logWarning(String.format("Error reading %s: %s is not a material", fileName, key)); } } return 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

*/ public static Character[][] generateLayoutMatrix(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; } }