/* * This file is part of Skript. * * Skript is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Skript is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Skript. If not, see <http://www.gnu.org/licenses/>. * * * Copyright 2011-2014 Peter Güttinger * */ package ch.njol.skript.aliases; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.ScriptLoader; import ch.njol.skript.Skript; import ch.njol.skript.config.Config; import ch.njol.skript.config.EntryNode; import ch.njol.skript.config.Node; import ch.njol.skript.config.SectionNode; import ch.njol.skript.config.validate.SectionValidator; import ch.njol.skript.localization.ArgsMessage; import ch.njol.skript.localization.Language; import ch.njol.skript.localization.Message; import ch.njol.skript.localization.Noun; import ch.njol.skript.localization.RegexMessage; import ch.njol.skript.log.BlockingLogHandler; import ch.njol.skript.log.SkriptLogger; import ch.njol.skript.util.EnchantmentType; import ch.njol.skript.util.Utils; import ch.njol.util.NonNullPair; import ch.njol.util.Setter; /** * FIXME rename * * @author Peter Güttinger */ @SuppressWarnings("deprecation") public abstract class Aliases { /** * Note to self: never use this, use {@link #getAlias_i(String)} instead. */ private final static HashMap<String, ItemType> aliases_english = new HashMap<String, ItemType>(10000); private final static HashMap<String, ItemType> aliases_localised = new HashMap<String, ItemType>(1000); private final static HashMap<String, ItemType> getAliases() { return Language.isUsingLocal() ? aliases_localised : aliases_english; } @Nullable private final static ItemType getAlias_i(final String s) { final ItemType t = ScriptLoader.getScriptAliases().get(s); if (t != null) return t; return getAliases().get(s); } private final static HashMap<Integer, MaterialName> materialNames_english = new HashMap<Integer, MaterialName>(Material.values().length); private final static HashMap<Integer, MaterialName> materialNames_localised = new HashMap<Integer, MaterialName>(Material.values().length); private final static HashMap<Integer, MaterialName> getMaterialNames() { return Language.isUsingLocal() ? materialNames_localised : materialNames_english; } static String itemSingular = "item"; static String itemPlural = "items"; @Nullable static String itemGender = null; static String blockSingular = "block"; static String blockPlural = "blocks"; @Nullable static String blockGender = null; // this is not an alias! private final static ItemType everything = new ItemType(); static { everything.setAll(true); everything.add(new ItemData()); } private final static Message m_brackets_error = new Message("aliases.brackets error"); private final static ArgsMessage m_invalid_brackets = new ArgsMessage("aliases.invalid brackets"); private final static ArgsMessage m_empty_alias = new ArgsMessage("aliases.empty alias"); private final static ArgsMessage m_unknown_variation = new ArgsMessage("aliases.unknown variation"); private final static Message m_starting_with_number = new Message("aliases.starting with number"); private final static Message m_missing_aliases = new Message("aliases.missing aliases"); private final static Message m_empty_string = new Message("aliases.empty string"); private final static ArgsMessage m_invalid_item_data = new ArgsMessage("aliases.invalid item data"); private final static ArgsMessage m_invalid_id = new ArgsMessage("aliases.invalid id"); private final static Message m_invalid_block_data = new Message("aliases.invalid block data"); private final static ArgsMessage m_invalid_item_type = new ArgsMessage("aliases.invalid item type"); private final static ArgsMessage m_out_of_data_range = new ArgsMessage("aliases.out of data range"); private final static Message m_invalid_range = new Message("aliases.invalid range"); private final static ArgsMessage m_invalid_section = new ArgsMessage("aliases.invalid section"); private final static ArgsMessage m_section_not_found = new ArgsMessage("aliases.section not found"); private final static ArgsMessage m_not_a_section = new ArgsMessage("aliases.not a section"); private final static Message m_unexpected_non_variation_section = new Message("aliases.unexpected non-variation section"); private final static Message m_unexpected_section = new Message("aliases.unexpected section"); private final static ArgsMessage m_loaded_x_aliases_from = new ArgsMessage("aliases.loaded x aliases from"); private final static ArgsMessage m_loaded_x_aliases = new ArgsMessage("aliases.loaded x aliases"); final static class Variations extends HashMap<String, HashMap<String, ItemType>> { private final static long serialVersionUID = -139481665727386819L; } private static int nextBracket(final String s, final char closingBracket, final char openingBracket, final int start) { int n = 0; assert s.charAt(start) == openingBracket; for (int i = start + 1; i < s.length(); i++) { if (s.charAt(i) == '\\') { i++; continue; } else if (s.charAt(i) == closingBracket) { if (n == 0) return i; n--; } else if (s.charAt(i) == openingBracket) { n++; } } Skript.error(m_invalid_brackets.toString(openingBracket + "" + closingBracket)); return -1; } /** * Concatenates parts of an alias's name. This currently 'lowercases' the first character of any part if there's no space in front of it. It also replaces double spaces with a * single one and trims the resulting string. * * @param parts */ private final static String concatenate(final String... parts) { assert parts.length >= 2; final StringBuilder b = new StringBuilder(); for (int i = 0; i < parts.length; i++) { if (parts[i].isEmpty()) continue; if (b.length() == 0) { b.append(parts[i]); continue; } final char c = parts[i].charAt(0); if (Character.isUpperCase(c) && b.charAt(b.length() - 1) != ' ') { b.append(Character.toLowerCase(c) + parts[i].substring(1)); } else { b.append(parts[i]); } } return "" + b.toString().replace(" ", " ").trim(); } /** * @param name Mixedcase string with no whitespace besides spaces and no double spaces. * @param value The alias's value, used for {variations} * @param variations * @return A map containing all parsed aliases */ static LinkedHashMap<String, ItemType> getAliases(final String name, final ItemType value, final Variations variations) { final LinkedHashMap<String, ItemType> r = new LinkedHashMap<String, ItemType>(); // LinkedHashMap to preserve order for item names for (int i = 0; i < name.length(); i++) { final char c = name.charAt(i); if ("[({".indexOf(c) != -1) { final int end = nextBracket(name, "])}".charAt("[({".indexOf(c)), c, i); if (end == -1) return r; if (c == '[') { r.putAll(getAliases(concatenate(name.substring(0, i), name.substring(i + 1, end), name.substring(end + 1)), value, variations)); r.putAll(getAliases(concatenate(name.substring(0, i), name.substring(end + 1)), value, variations)); } else if (c == '(') { int n = 0; int last = i; boolean hasParts = false; for (int j = i + 1; j < end; j++) { final char x = name.charAt(j); if (x == '(') { n++; } else if (x == ')') { n--; } else if (x == '|') { if (n > 0) continue; hasParts = true; r.putAll(getAliases(concatenate(name.substring(0, i), name.substring(last + 1, j), name.substring(end + 1)), value, variations)); last = j; } } if (!hasParts) { Skript.error(m_brackets_error.toString()); return r; } r.putAll(getAliases(concatenate(name.substring(0, i), name.substring(last + 1, end), name.substring(end + 1)), value, variations)); } else { assert c == '{'; continue; } return r; } } // variations and <any>/<block>/etc. are replaced last because they replace genders int i = name.indexOf('{'); if (i != -1) { final int end = name.indexOf('}', i + 1); if (end == -1) {// checked above assert false; return r; } final String var = name.substring(i + 1, end); if (variations.containsKey(var)) { boolean hasDefault = false; for (final Entry<String, ItemType> v : variations.get(var).entrySet()) { final String n; if (v.getKey().equalsIgnoreCase("{default}")) { hasDefault = true; if (v.getValue() == null) continue; n = concatenate(name.substring(0, i), name.substring(end + 1)); } else { final int g = v.getKey().lastIndexOf('@'); if (g == -1) { n = concatenate(name.substring(0, i), v.getKey(), name.substring(end + 1)); } else { final String n0 = concatenate(name.substring(0, i), v.getKey().substring(0, g), name.substring(end + 1)); final int c0 = n0.lastIndexOf('@'); n = (c0 == -1 ? n0 : n0.substring(0, c0).trim() + v.getKey().substring(g)); } } final ItemType t = v.getValue().intersection(value); if (t != null) r.putAll(getAliases(n, t, variations)); else Skript.warning(m_empty_alias.toString(n)); } if (!hasDefault) r.putAll(getAliases(concatenate(name.substring(0, i), name.substring(end + 1)), value, variations)); } else { Skript.error(m_unknown_variation.toString(var)); } return r; } i = name.indexOf('<'); if (i != -1) { final int end = name.indexOf('>', i + 1); if (end != -1) { final String x = name.substring(i + 1, end); if (x.equalsIgnoreCase("any")) { String s = name.substring(0, i) + m_any.toString() + name.substring(end + 1); final int g = s.lastIndexOf('@'); if (g != -1) s = s.substring(0, g + 1) + "-"; r.putAll(getAliases(s, value, variations)); return r; } else { final String[][] os = { {"item", itemSingular, itemPlural, itemGender}, {"block", blockSingular, blockPlural, blockGender}, {"item/block", itemSingular, itemPlural, itemGender, blockSingular, blockPlural, blockGender}, {"block/item", blockSingular, blockPlural, blockGender, itemSingular, itemPlural, itemGender}, }; for (final String[] o : os) { if (x.equalsIgnoreCase(o[0])) { for (int j = 1; j < o.length; j += 3) { String s = name.substring(0, i) + "¦" + o[j] + "¦" + o[j + 1] + "¦" + name.substring(end + 1); if (o[j + 2] != null) { final NonNullPair<String, Integer> p = Noun.stripGender(s, s); s = p.getFirst() + "@" + o[j + 2]; } r.put(s, value); } return r; } } } } } r.put(name, value); r.remove(""); return r; } @SuppressWarnings("null") private final static Pattern numberWordPattern = Pattern.compile("\\d+\\s+.+"); /** * Parses & adds new aliases * * @param name mixedcase string * @param value * @param variations * @return amount of added aliases */ static int addAliases(final String name, final String value, final Variations variations) { final ItemType t = parseAlias(value); if (t == null) { return 0; } final HashMap<String, ItemType> aliases = getAliases(); final HashMap<String, ItemType> as = getAliases("" + name.replaceAll("\\s+", " "), t, variations); boolean printedStartingWithNumberError = false; // boolean printedSyntaxError = false; for (final Entry<String, ItemType> e : as.entrySet()) { final String s = "" + e.getKey().trim().replaceAll("\\s+", " "); final NonNullPair<String, Integer> g = Noun.stripGender(s, "" + e.getKey()); final NonNullPair<String, String> p = Noun.getPlural(g.getFirst()); final String lcs = p.getFirst().toLowerCase(); final String lcp = p.getSecond().toLowerCase(); if (numberWordPattern.matcher(lcs).matches() || numberWordPattern.matcher(lcp).matches()) { if (!printedStartingWithNumberError) { Skript.error(m_starting_with_number.toString()); printedStartingWithNumberError = true; } continue; } // if (lc.contains(",") || lc.contains(" and ") || lc.contains(" or ")) { // if (!printedSyntaxError) { // Skript.error("aliases must not contain syntax elements (comma, 'and', 'or')"); // printedSyntaxError = true; // } // continue; // } boolean b; final ItemType alias; if ((b = lcs.endsWith(itemSingular)) || lcp.endsWith(itemPlural)) { String m = b ? lcs.substring(0, lcs.length() - itemSingular.length()) : lcp.substring(0, lcp.length() - itemPlural.length()); if (m.endsWith(" ") || m.endsWith("-")) m = m.substring(0, m.length() - 1); final ItemType si = aliases.get(m); if (si != null) si.setItem(e.getValue()); alias = e.getValue(); } else if ((b = lcs.endsWith(blockSingular)) || lcp.endsWith(blockPlural)) { String m = b ? lcs.substring(0, lcs.length() - blockSingular.length()) : lcp.substring(0, lcp.length() - blockPlural.length()); if (m.endsWith(" ") || m.endsWith("-")) m = m.substring(0, m.length() - 1); final ItemType si = aliases.get(m); if (si != null) si.setBlock(e.getValue()); alias = e.getValue(); } else { final ItemType[] ib = new ItemType[2]; final String[] ibs = {itemSingular, blockSingular}; final String[] seps = {" ", "", "-"}; for (int i = 0; i < ib.length; i++) { for (final String sep : seps) { ib[i] = aliases.get(lcs + sep + ibs[i]); if (ib[i] != null) break; } } if (ib[0] == null && ib[1] == null) { alias = e.getValue(); } else { alias = e.getValue().clone(); alias.setItem(ib[0]); alias.setBlock(ib[1]); } } aliases.put(lcs, alias); aliases.put(lcp, alias); //if (logSpam()) // over 10k aliases in the default aliases.sk as of MC 1.5 // info("added alias " + s + " for " + e.getValue()); final HashMap<Integer, MaterialName> materialNames = getMaterialNames(); if (alias.getTypes().size() == 1) { final ItemData d = alias.getTypes().get(0); MaterialName n = materialNames.get(Integer.valueOf(d.getId())); if (d.dataMin == -1 && d.dataMax == -1) { if (n != null) { if (n.singular.equals("" + d.getId()) && n.singular.equals(n.plural)) { n.singular = p.getFirst(); n.plural = p.getSecond(); } } else { materialNames.put(Integer.valueOf(d.getId()), new MaterialName(d.getId(), p.getFirst(), p.getSecond(), g.getSecond())); } } else { if (n == null) materialNames.put(Integer.valueOf(d.getId()), n = new MaterialName(d.getId(), "" + d.getId(), "" + d.getId(), g.getSecond())); @SuppressWarnings("null") final NonNullPair<Short, Short> data = new NonNullPair<Short, Short>(Short.valueOf(d.dataMin), Short.valueOf(d.dataMax)); n.names.put(data, p); } } } return as.size(); } /** * Gets the custom name of of a material, or the default if none is set. * * @param id * @param data * @return The material's name */ public final static String getMaterialName(final int id, final short data, final boolean plural) { return getMaterialName(id, data, data, plural); } public final static String getDebugMaterialName(final int id, final short data, final boolean plural) { return getDebugMaterialName(id, data, data, plural); } public final static String getMaterialName(final int id, final short dataMin, final short dataMax, final boolean plural) { final MaterialName n = getMaterialNames().get(Integer.valueOf(id)); if (n == null) { return "" + id; } return n.toString(dataMin, dataMax, plural); } public final static String getDebugMaterialName(final int id, final short dataMin, final short dataMax, final boolean plural) { final MaterialName n = getMaterialNames().get(Integer.valueOf(id)); if (n == null) { return "" + id + ":" + dataMin + (dataMax == dataMin ? "" : "-" + dataMax); } return n.getDebugName(dataMin, dataMax, plural); } /** * @return The ietm's gender or -1 if no name is found */ public final static int getGender(final int id, final short dataMin, final short dataMax) { final MaterialName n = getMaterialNames().get(Integer.valueOf(id)); if (n != null) return n.gender; return -1; } /** * @return how many ids are missing an alias, including the 'any id' (-1) */ final static int addMissingMaterialNames() { final HashMap<Integer, MaterialName> materialNames = getMaterialNames(); int r = 0; final StringBuilder missing = new StringBuilder(m_missing_aliases + " "); for (final Material m : Material.values()) { if (materialNames.get(Integer.valueOf(m.getId())) == null) { materialNames.put(Integer.valueOf(m.getId()), new MaterialName(m.getId(), "" + m.toString().toLowerCase().replace('_', ' '), "" + m.toString().toLowerCase().replace('_', ' '), 0)); missing.append(m.getId() + ", "); r++; } } final MaterialName m = materialNames.get(Integer.valueOf(-1)); if (m == null) { materialNames.put(Integer.valueOf(-1), new MaterialName(-1, Language.get("aliases.anything"), Language.get("aliases.anything"), 0)); missing.append("<any>, "); r++; } if (r > 0) Skript.warning("" + missing.substring(0, missing.length() - 2)); return r; } /** * Parses an ItemType to be used as an alias, i.e. it doesn't parse 'all'/'every' and the amount. * * @param s mixed case string * @return A new ItemType representing the given value */ @Nullable public static ItemType parseAlias(final String s) { if (s.isEmpty()) { Skript.error(m_empty_string.toString()); return null; } if (s.equals("*")) return everything; final ItemType t = new ItemType(); final String[] types = s.split("\\s*,\\s*"); for (final String type : types) { if (type == null || parseType(type, t, true) == null) return null; } return t; } private final static RegexMessage p_any = new RegexMessage("aliases.any", "", " (.+)", Pattern.CASE_INSENSITIVE); private final static Message m_any = new Message("aliases.any-skp"); private final static RegexMessage p_every = new RegexMessage("aliases.every", "", " (.+)", Pattern.CASE_INSENSITIVE); private final static RegexMessage p_of_every = new RegexMessage("aliases.of every", "(\\d+) ", " (.+)", Pattern.CASE_INSENSITIVE); private final static RegexMessage p_of = new RegexMessage("aliases.of", "(\\d+) (?:", " )?(.+)", Pattern.CASE_INSENSITIVE); /** * Parses an ItemType. * <p> * Prints errors. * * @param s * @return The parsed ItemType or null if the input is invalid. */ @Nullable public static ItemType parseItemType(String s) { if (s.isEmpty()) return null; s = "" + s.trim(); final ItemType t = new ItemType(); Matcher m; if ((m = p_of_every.matcher(s)).matches()) { t.setAmount(Utils.parseInt("" + m.group(1))); t.setAll(true); s = "" + m.group(m.groupCount()); } else if ((m = p_of.matcher(s)).matches()) { t.setAmount(Utils.parseInt("" + m.group(1))); s = "" + m.group(m.groupCount()); } else if ((m = p_every.matcher(s)).matches()) { t.setAll(true); s = "" + m.group(m.groupCount()); } else { final int l = s.length(); s = Noun.stripIndefiniteArticle(s); if (s.length() != l) // had indefinite article t.setAmount(1); } final String lc = s.toLowerCase(); final String of = Language.getSpaced("enchantments.of").toLowerCase(); int c = -1; outer: while ((c = lc.indexOf(of, c + 1)) != -1) { final ItemType t2 = t.clone(); final BlockingLogHandler log = SkriptLogger.startLogHandler(new BlockingLogHandler()); try { if (parseType("" + s.substring(0, c), t2, false) == null) continue; } finally { log.stop(); } if (t2.numTypes() == 0) continue; final Map<Enchantment, Integer> enchantments = new HashMap<Enchantment, Integer>(); final String[] enchs = lc.substring(c + of.length(), lc.length()).split("\\s*(,|" + Pattern.quote(Language.get("and")) + ")\\s*"); for (final String ench : enchs) { final EnchantmentType e = EnchantmentType.parse("" + ench); if (e == null) continue outer; enchantments.put(e.getType(), e.getLevel()); } t2.addEnchantments(enchantments); return t2; } if (parseType(s, t, false) == null) return null; if (t.numTypes() == 0) return null; return t; } /** * Prints errors. * * @param s The string holding the type, can be either a number or an alias, plus an optional data part. Case does not matter. * @param t The ItemType to add the parsed ItemData(s) to (i.e. this ItemType will be modified) * @param isAlias Whether this type is parsed for an alias. * @return The given item type or null if the input couldn't be parsed. */ @Nullable private final static ItemType parseType(final String s, final ItemType t, final boolean isAlias) { ItemType i; int c = s.indexOf(':'); if (c == -1) c = s.length(); final String type = s.substring(0, c); ItemData data = null; if (c != s.length()) { data = parseData("" + s.substring(c + 1)); if (data == null) { Skript.error(m_invalid_item_data.toString(s.substring(c))); return null; } } if (type.isEmpty()) { t.add(data == null ? new ItemData() : data); return t; } else if (type.matches("\\d+")) { ItemData d = new ItemData(Utils.parseInt(type)); if (Material.getMaterial(d.getId()) == null) { Skript.error(m_invalid_id.toString(d.getId())); return null; } if (data != null) { if (d.getId() <= Skript.MAXBLOCKID && (data.dataMax > 15 || data.dataMin > 15)) { Skript.error(m_invalid_block_data.toString()); return null; } d = d.intersection(data); } if (!isAlias && d != null) { Skript.warning("Using an ID instead of an alias is discouraged and will likely not be supported in future versions of Skript anymore. " + (d.toString().equals(type) ? "Please crate an alias for '" + type + (type.equals(s) ? "" : " or '" + s + "'") + "' (" + Material.getMaterial(d.getId()).name() + ") in aliases-english.sk or the script's aliases section and use that instead." : "Please replace '" + s + "' with e.g. '" + d.toString(true, false) + "'.")); } t.add(d); return t; } else if ((i = getAlias(type)) != null) { for (ItemData d : i) { if (data != null) { if (d.getId() <= Skript.MAXBLOCKID && (data.dataMax > 15 || data.dataMin > 15)) { Skript.error(m_invalid_block_data.toString()); return null; } d = d.intersection(data); } else { d = d.clone(); } t.add(d); } if (data == null) { if (i.getItem() != i) t.setItem(i.getItem().clone()); if (i.getBlock() != i) t.setBlock(i.getBlock().clone()); } return t; } if (isAlias) Skript.error(m_invalid_item_type.toString(s)); return null; } /** * Gets an alias from the aliases defined in the config. * * @param s The alias to get, case does not matter * @return A copy of the ItemType represented by the given alias or null if no such alias exists. */ @Nullable private final static ItemType getAlias(final String s) { ItemType i; String lc = "" + s.toLowerCase(); final Matcher m = p_any.matcher(lc); if (m.matches()) { lc = "" + m.group(m.groupCount()); } if ((i = getAlias_i(lc)) != null) return i.clone(); boolean b; if ((b = lc.endsWith(" " + blockSingular)) || lc.endsWith(" " + blockPlural)) { if ((i = getAlias_i("" + s.substring(0, s.length() - (b ? blockSingular.length() : blockPlural.length()) - 1))) != null) { i = i.clone(); for (int j = 0; j < i.numTypes(); j++) { final ItemData d = i.getTypes().get(j); if (d.getId() > Skript.MAXBLOCKID) { i.remove(d); j--; } } if (i.getTypes().isEmpty()) return null; return i; } } else if ((b = lc.endsWith(" " + itemSingular)) || lc.endsWith(" " + itemPlural)) { if ((i = getAlias_i("" + s.substring(0, s.length() - (b ? itemSingular.length() : itemPlural.length()) - 1))) != null) { for (int j = 0; j < i.numTypes(); j++) { final ItemData d = i.getTypes().get(j); if (d.getId() != -1 && d.getId() <= Skript.MAXBLOCKID) { i.remove(d); j--; } } if (i.getTypes().isEmpty()) return null; return i; } } return null; } /** * Gets the data part of an item data * * @param s Everything after ':' * @return ItemData with only the dataMin and dataMax set */ @Nullable private final static ItemData parseData(final String s) { if (s.isEmpty()) return new ItemData(); if (!s.matches("\\d+(-\\d+)?")) return null; final ItemData t = new ItemData(); int i = s.indexOf('-'); if (i == -1) i = s.length(); try { t.dataMin = Short.parseShort(s.substring(0, i)); t.dataMax = (i == s.length() ? t.dataMin : Short.parseShort(s.substring(i + 1, s.length()))); } catch (final NumberFormatException e) { // overflow Skript.error(m_out_of_data_range.toString(Short.MAX_VALUE)); return null; } if (t.dataMin > t.dataMax) { Skript.error(m_invalid_range.toString()); return null; } return t; } public static void clear() { aliases_english.clear(); aliases_localised.clear(); materialNames_english.clear(); materialNames_localised.clear(); } public static void load() { final boolean wasLocal = Language.isUsingLocal(); try { for (int l = 0; l < 2; l++) { Language.setUseLocal(l == 1); if (l == 1 && !Language.isUsingLocal()) break; final Config aliasConfig; try { final File file = new File(Skript.getInstance().getDataFolder(), "aliases-" + Language.getName() + ".sk"); if (!file.exists()) { Skript.error("Could not find the " + Language.getName() + " aliases file " + file.getName()); } aliasConfig = new Config(file, false, true, "="); } catch (final IOException e) { Skript.error("Could not load the " + Language.getName() + " aliases config: " + e.getLocalizedMessage()); return; } final ArrayList<String> aliasNodes = new ArrayList<String>(); aliasConfig.validate( new SectionValidator() .addEntry("aliases", new Setter<String>() { @Override public void set(final String s) { for (final String n : s.split(",")) aliasNodes.add(n.trim()); } }, false) .addEntry("item", new Setter<String>() { @Override public void set(final String s) { final NonNullPair<String, Integer> g = Noun.stripGender(s, "item"); itemGender = Noun.getGenderID(g.getSecond()); final NonNullPair<String, String> p = Noun.getPlural(g.getFirst()); itemSingular = "" + p.getFirst().toLowerCase(); itemPlural = "" + p.getSecond().toLowerCase(); } }, false) .addEntry("block", new Setter<String>() { @Override public void set(final String s) { final NonNullPair<String, Integer> g = Noun.stripGender(s, "block"); blockGender = Noun.getGenderID(g.getSecond()); final NonNullPair<String, String> p = Noun.getPlural(g.getFirst()); blockSingular = "" + p.getFirst().toLowerCase(); blockPlural = "" + p.getSecond().toLowerCase(); } }, false) .setAllowUndefinedSections(true)); for (final Node node : aliasConfig.getMainNode()) { if (node instanceof SectionNode) { if (!aliasNodes.contains(node.getKey())) { Skript.error(m_invalid_section.toString(node.getKey())); } } } final Variations variations = new Variations(); int num = 0; for (final String an : aliasNodes) { final Node node = aliasConfig.getMainNode().get(an); SkriptLogger.setNode(node); if (node == null) { Skript.error(m_section_not_found.toString(an)); continue; } if (!(node instanceof SectionNode)) { Skript.error(m_not_a_section.toString(an)); continue; } int i = 0; for (final Node n : (SectionNode) node) { if (n instanceof EntryNode) { i += addAliases(((EntryNode) n).getKey(), ((EntryNode) n).getValue(), variations); } else if (n instanceof SectionNode) { final String key = n.getKey(); if (key == null) { assert false; continue; } if (!(key.startsWith("{") && key.endsWith("}"))) { Skript.error(m_unexpected_non_variation_section.toString()); continue; } final HashMap<String, ItemType> vs = new HashMap<String, ItemType>(); for (final Node a : (SectionNode) n) { if (a instanceof SectionNode) { Skript.error(m_unexpected_section.toString()); continue; } else if (!(a instanceof EntryNode)) { continue; } final boolean noDefault = ((EntryNode) a).getValue().isEmpty() && ((EntryNode) a).getKey().equalsIgnoreCase("{default}"); final ItemType t = noDefault ? null : parseAlias(((EntryNode) a).getValue()); if (t != null || noDefault) vs.put(Noun.normalizePluralMarkers(((EntryNode) a).getKey()), t); } variations.put(key.substring(1, key.length() - 1), vs); } } if (Skript.logVeryHigh()) Skript.info(m_loaded_x_aliases_from.toString(i, node.getKey())); num += i; } SkriptLogger.setNode(null); if (Skript.logNormal()) Skript.info(m_loaded_x_aliases.toString(num)); addMissingMaterialNames(); // if (!SkriptConfig.keepConfigsLoaded.value()) // aliasConfig = null; } } finally { Language.setUseLocal(wasLocal); } } }