Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * This software is provided under the terms of the Minecraft Forge Public
- * License v1.0.
- */
- package net.minecraftforge.common;
- import static net.minecraftforge.common.Property.Type.BOOLEAN;
- import static net.minecraftforge.common.Property.Type.DOUBLE;
- import static net.minecraftforge.common.Property.Type.INTEGER;
- import static net.minecraftforge.common.Property.Type.STRING;
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStreamWriter;
- import java.io.PushbackInputStream;
- import java.io.Reader;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Locale;
- import java.util.Map;
- import java.util.Set;
- import java.util.TreeMap;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import net.minecraft.block.Block;
- import net.minecraft.item.Item;
- import com.google.common.base.CharMatcher;
- import com.google.common.collect.ImmutableSet;
- import cpw.mods.fml.common.FMLLog;
- import cpw.mods.fml.common.Loader;
- import cpw.mods.fml.relauncher.FMLInjectionData;
- /**
- * This class offers advanced configurations capabilities, allowing to provide
- * various categories for configuration variables.
- */
- public class Configuration
- {
- private static boolean[] configMarkers = new boolean[Item.itemsList.length];
- private static final int ITEM_SHIFT = 256;
- private static final int MAX_BLOCKS = 4096;
- public static final String CATEGORY_GENERAL = "general";
- public static final String CATEGORY_BLOCK = "block";
- public static final String CATEGORY_ITEM = "item";
- public static final String ALLOWED_CHARS = "._-";
- public static final String DEFAULT_ENCODING = "UTF-8";
- public static final String CATEGORY_SPLITTER = ".";
- public static final String NEW_LINE;
- private static final Pattern CONFIG_START = Pattern.compile("START: \"([^\\\"]+)\"");
- private static final Pattern CONFIG_END = Pattern.compile("END: \"([^\\\"]+)\"");
- public static final CharMatcher allowedProperties = CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.anyOf(ALLOWED_CHARS));
- private static Configuration PARENT = null;
- File file;
- private Map<String, ConfigCategory> categories = new TreeMap<String, ConfigCategory>();
- private Map<String, Configuration> children = new TreeMap<String, Configuration>();
- private boolean caseSensitiveCustomCategories;
- public String defaultEncoding = DEFAULT_ENCODING;
- private String fileName = null;
- public boolean isChild = false;
- private boolean changed = false;
- static
- {
- Arrays.fill(configMarkers, false);
- NEW_LINE = System.getProperty("line.separator");
- }
- public Configuration(){}
- /**
- * Create a configuration file for the file given in parameter.
- */
- public Configuration(File file)
- {
- this.file = file;
- String basePath = ((File)(FMLInjectionData.data()[6])).getAbsolutePath().replace(File.separatorChar, '/').replace("/.", "");
- String path = file.getAbsolutePath().replace(File.separatorChar, '/').replace("/./", "/").replace(basePath, "");
- if (PARENT != null)
- {
- PARENT.setChild(path, this);
- isChild = true;
- }
- else
- {
- fileName = path;
- load();
- }
- }
- public Configuration(File file, boolean caseSensitiveCustomCategories)
- {
- this(file);
- this.caseSensitiveCustomCategories = caseSensitiveCustomCategories;
- }
- /**
- * Gets or create a block id property. If the block id property key is
- * already in the configuration, then it will be used. Otherwise,
- * defaultId will be used, except if already taken, in which case this
- * will try to determine a free default id.
- */
- public Property getBlock(String key, int defaultID) { return getBlock(CATEGORY_BLOCK, key, defaultID, null); }
- public Property getBlock(String key, int defaultID, String comment) { return getBlock(CATEGORY_BLOCK, key, defaultID, comment); }
- public Property getBlock(String category, String key, int defaultID) { return getBlockInternal(category, key, defaultID, null, 256, Block.blocksList.length); }
- public Property getBlock(String category, String key, int defaultID, String comment) { return getBlockInternal(category, key, defaultID, comment, 256, Block.blocksList.length); }
- /**
- * Special version of getBlock to be used when you want to garentee the ID you get is below 256
- * This should ONLY be used by mods who do low level terrain generation, or ones that add new
- * biomes.
- * EXA: ExtraBiomesXL
- *
- * Specifically, if your block is used BEFORE the Chunk is created, and placed in the terrain byte array directly.
- * If you add a new biome and you set the top/filler block, they need to be <256, nothing else.
- *
- * If you're adding a new ore, DON'T call this function.
- *
- * Normal mods such as '50 new ores' do not need to be below 256 so should use the normal getBlock
- */
- public Property getTerrainBlock(String category, String key, int defaultID, String comment)
- {
- return getBlockInternal(category, key, defaultID, comment, 0, 256);
- }
- private Property getBlockInternal(String category, String key, int defaultID, String comment, int lower, int upper)
- {
- Property prop = get(category, key, -1, comment);
- if (prop.getInt() != -1)
- {
- configMarkers[prop.getInt()] = true;
- return prop;
- }
- else
- {
- if (defaultID < lower)
- {
- FMLLog.warning(
- "Mod attempted to get a block ID with a default in the Terrain Generation section, " +
- "mod authors should make sure there defaults are above 256 unless explicitly needed " +
- "for terrain generation. Most ores do not need to be below 256.");
- FMLLog.warning("Config \"%s\" Category: \"%s\" Key: \"%s\" Default: %d", fileName, category, key, defaultID);
- defaultID = upper - 1;
- }
- if (Block.blocksList[defaultID] == null && !configMarkers[defaultID])
- {
- prop.set(defaultID);
- configMarkers[defaultID] = true;
- return prop;
- }
- else
- {
- for (int j = upper - 1; j > 0; j--)
- {
- if (Block.blocksList[j] == null && !configMarkers[j])
- {
- prop.set(j);
- configMarkers[j] = true;
- return prop;
- }
- }
- throw new RuntimeException("No more block ids available for " + key);
- }
- }
- }
- public Property getItem(String key, int defaultID) { return getItem(CATEGORY_ITEM, key, defaultID, null); }
- public Property getItem(String key, int defaultID, String comment) { return getItem(CATEGORY_ITEM, key, defaultID, comment); }
- public Property getItem(String category, String key, int defaultID) { return getItem(category, key, defaultID, null); }
- public Property getItem(String category, String key, int defaultID, String comment)
- {
- Property prop = get(category, key, -1, comment);
- int defaultShift = defaultID + ITEM_SHIFT;
- if (prop.getInt() != -1)
- {
- configMarkers[prop.getInt() + ITEM_SHIFT] = true;
- return prop;
- }
- else
- {
- if (defaultID < MAX_BLOCKS - ITEM_SHIFT)
- {
- FMLLog.warning(
- "Mod attempted to get a item ID with a default value in the block ID section, " +
- "mod authors should make sure there defaults are above %d unless explicitly needed " +
- "so that all block ids are free to store blocks.", MAX_BLOCKS - ITEM_SHIFT);
- FMLLog.warning("Config \"%s\" Category: \"%s\" Key: \"%s\" Default: %d", fileName, category, key, defaultID);
- }
- if (Item.itemsList[defaultShift] == null && !configMarkers[defaultShift] && defaultShift >= Block.blocksList.length)
- {
- prop.set(defaultID);
- configMarkers[defaultShift] = true;
- return prop;
- }
- else
- {
- for (int x = Item.itemsList.length - 1; x >= ITEM_SHIFT; x--)
- {
- if (Item.itemsList[x] == null && !configMarkers[x])
- {
- prop.set(x - ITEM_SHIFT);
- configMarkers[x] = true;
- return prop;
- }
- }
- throw new RuntimeException("No more item ids available for " + key);
- }
- }
- }
- public Property get(String category, String key, int defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, int defaultValue, String comment)
- {
- Property prop = get(category, key, Integer.toString(defaultValue), comment, INTEGER);
- if (!prop.isIntValue())
- {
- prop.set(defaultValue);
- }
- return prop;
- }
- public Property get(String category, String key, boolean defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, boolean defaultValue, String comment)
- {
- Property prop = get(category, key, Boolean.toString(defaultValue), comment, BOOLEAN);
- if (!prop.isBooleanValue())
- {
- prop.set(defaultValue);
- }
- return prop;
- }
- public Property get(String category, String key, double defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, double defaultValue, String comment)
- {
- Property prop = get(category, key, Double.toString(defaultValue), comment, DOUBLE);
- if (!prop.isDoubleValue())
- {
- prop.set(defaultValue);
- }
- return prop;
- }
- public Property get(String category, String key, String defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, String defaultValue, String comment)
- {
- return get(category, key, defaultValue, comment, STRING);
- }
- public Property get(String category, String key, String[] defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, String[] defaultValue, String comment)
- {
- return get(category, key, defaultValue, comment, STRING);
- }
- public Property get(String category, String key, int[] defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, int[] defaultValue, String comment)
- {
- String[] values = new String[defaultValue.length];
- for (int i = 0; i < defaultValue.length; i++)
- {
- values[i] = Integer.toString(defaultValue[i]);
- }
- Property prop = get(category, key, values, comment, INTEGER);
- if (!prop.isIntList())
- {
- prop.set(values);
- }
- return prop;
- }
- public Property get(String category, String key, double[] defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, double[] defaultValue, String comment)
- {
- String[] values = new String[defaultValue.length];
- for (int i = 0; i < defaultValue.length; i++)
- {
- values[i] = Double.toString(defaultValue[i]);
- }
- Property prop = get(category, key, values, comment, DOUBLE);
- if (!prop.isDoubleList())
- {
- prop.set(values);
- }
- return prop;
- }
- public Property get(String category, String key, boolean[] defaultValue)
- {
- return get(category, key, defaultValue, null);
- }
- public Property get(String category, String key, boolean[] defaultValue, String comment)
- {
- String[] values = new String[defaultValue.length];
- for (int i = 0; i < defaultValue.length; i++)
- {
- values[i] = Boolean.toString(defaultValue[i]);
- }
- Property prop = get(category, key, values, comment, BOOLEAN);
- if (!prop.isBooleanList())
- {
- prop.set(values);
- }
- return prop;
- }
- public Property get(String category, String key, String defaultValue, String comment, Property.Type type)
- {
- if (!caseSensitiveCustomCategories)
- {
- category = category.toLowerCase(Locale.ENGLISH);
- }
- ConfigCategory cat = getCategory(category);
- if (cat.containsKey(key))
- {
- Property prop = cat.get(key);
- if (prop.getType() == null)
- {
- prop = new Property(prop.getName(), prop.getString(), type);
- cat.put(key, prop);
- }
- prop.comment = comment;
- return prop;
- }
- else if (defaultValue != null)
- {
- Property prop = new Property(key, defaultValue, type);
- prop.set(defaultValue); //Set and mark as dirty to signify it should save
- cat.put(key, prop);
- prop.comment = comment;
- return prop;
- }
- else
- {
- return null;
- }
- }
- public Property get(String category, String key, String[] defaultValue, String comment, Property.Type type)
- {
- if (!caseSensitiveCustomCategories)
- {
- category = category.toLowerCase(Locale.ENGLISH);
- }
- ConfigCategory cat = getCategory(category);
- if (cat.containsKey(key))
- {
- Property prop = cat.get(key);
- if (prop.getType() == null)
- {
- prop = new Property(prop.getName(), prop.getString(), type);
- cat.put(key, prop);
- }
- prop.comment = comment;
- return prop;
- }
- else if (defaultValue != null)
- {
- Property prop = new Property(key, defaultValue, type);
- prop.comment = comment;
- cat.put(key, prop);
- return prop;
- }
- else
- {
- return null;
- }
- }
- public boolean hasCategory(String category)
- {
- return categories.get(category) != null;
- }
- public boolean hasKey(String category, String key)
- {
- ConfigCategory cat = categories.get(category);
- return cat != null && cat.containsKey(key);
- }
- public void load()
- {
- if (PARENT != null && PARENT != this)
- {
- return;
- }
- BufferedReader buffer = null;
- UnicodeInputStreamReader input = null;
- try
- {
- if (file.getParentFile() != null)
- {
- file.getParentFile().mkdirs();
- }
- if (!file.exists() && !file.createNewFile())
- {
- return;
- }
- if (file.canRead())
- {
- input = new UnicodeInputStreamReader(new FileInputStream(file), defaultEncoding);
- defaultEncoding = input.getEncoding();
- buffer = new BufferedReader(input);
- String line;
- ConfigCategory currentCat = null;
- Property.Type type = null;
- ArrayList<String> tmpList = null;
- int lineNum = 0;
- String name = null;
- while (true)
- {
- lineNum++;
- line = buffer.readLine();
- if (line == null)
- {
- break;
- }
- Matcher start = CONFIG_START.matcher(line);
- Matcher end = CONFIG_END.matcher(line);
- if (start.matches())
- {
- fileName = start.group(1);
- categories = new TreeMap<String, ConfigCategory>();
- continue;
- }
- else if (end.matches())
- {
- fileName = end.group(1);
- Configuration child = new Configuration();
- child.categories = categories;
- this.children.put(fileName, child);
- continue;
- }
- int nameStart = -1, nameEnd = -1;
- boolean skip = false;
- boolean quoted = false;
- for (int i = 0; i < line.length() && !skip; ++i)
- {
- if (Character.isLetterOrDigit(line.charAt(i)) || ALLOWED_CHARS.indexOf(line.charAt(i)) != -1 || (quoted && line.charAt(i) != '"'))
- {
- if (nameStart == -1)
- {
- nameStart = i;
- }
- nameEnd = i;
- }
- else if (Character.isWhitespace(line.charAt(i)))
- {
- // ignore space charaters
- }
- else
- {
- switch (line.charAt(i))
- {
- case '#':
- skip = true;
- continue;
- case '"':
- if (quoted)
- {
- quoted = false;
- }
- if (!quoted && nameStart == -1)
- {
- quoted = true;
- }
- break;
- case '{':
- name = line.substring(nameStart, nameEnd + 1);
- String qualifiedName = ConfigCategory.getQualifiedName(name, currentCat);
- ConfigCategory cat = categories.get(qualifiedName);
- if (cat == null)
- {
- currentCat = new ConfigCategory(name, currentCat);
- categories.put(qualifiedName, currentCat);
- }
- else
- {
- currentCat = cat;
- }
- name = null;
- break;
- case '}':
- if (currentCat == null)
- {
- throw new RuntimeException(String.format("Config file corrupt, attepted to close to many categories '%s:%d'", fileName, lineNum));
- }
- currentCat = currentCat.parent;
- break;
- case '=':
- name = line.substring(nameStart, nameEnd + 1);
- if (currentCat == null)
- {
- throw new RuntimeException(String.format("'%s' has no scope in '%s:%d'", name, fileName, lineNum));
- }
- Property prop = new Property(name, line.substring(i + 1), type, true);
- i = line.length();
- currentCat.put(name, prop);
- break;
- case ':':
- type = Property.Type.tryParse(line.substring(nameStart, nameEnd + 1).charAt(0));
- nameStart = nameEnd = -1;
- break;
- case '<':
- if (tmpList != null)
- {
- throw new RuntimeException(String.format("Malformed list property \"%s:%d\"", fileName, lineNum));
- }
- name = line.substring(nameStart, nameEnd + 1);
- if (currentCat == null)
- {
- throw new RuntimeException(String.format("'%s' has no scope in '%s:%d'", name, fileName, lineNum));
- }
- tmpList = new ArrayList<String>();
- skip = true;
- break;
- case '>':
- if (tmpList == null)
- {
- throw new RuntimeException(String.format("Malformed list property \"%s:%d\"", fileName, lineNum));
- }
- currentCat.put(name, new Property(name, tmpList.toArray(new String[tmpList.size()]), type));
- name = null;
- tmpList = null;
- type = null;
- break;
- default:
- throw new RuntimeException(String.format("Unknown character '%s' in '%s:%d'", line.charAt(i), fileName, lineNum));
- }
- }
- }
- if (quoted)
- {
- throw new RuntimeException(String.format("Unmatched quote in '%s:%d'", fileName, lineNum));
- }
- else if (tmpList != null && !skip)
- {
- tmpList.add(line.trim());
- }
- }
- }
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- finally
- {
- if (buffer != null)
- {
- try
- {
- buffer.close();
- } catch (IOException e){}
- }
- if (input != null)
- {
- try
- {
- input.close();
- } catch (IOException e){}
- }
- }
- resetChangedState();
- }
- public void save()
- {
- if (PARENT != null && PARENT != this)
- {
- PARENT.save();
- return;
- }
- try
- {
- if (file.getParentFile() != null)
- {
- file.getParentFile().mkdirs();
- }
- if (!file.exists() && !file.createNewFile())
- {
- return;
- }
- if (file.canWrite())
- {
- FileOutputStream fos = new FileOutputStream(file);
- BufferedWriter buffer = new BufferedWriter(new OutputStreamWriter(fos, defaultEncoding));
- buffer.write("# Configuration file" + NEW_LINE + NEW_LINE);
- if (children.isEmpty())
- {
- save(buffer);
- }
- else
- {
- for (Map.Entry<String, Configuration> entry : children.entrySet())
- {
- buffer.write("START: \"" + entry.getKey() + "\"" + NEW_LINE);
- entry.getValue().save(buffer);
- buffer.write("END: \"" + entry.getKey() + "\"" + NEW_LINE + NEW_LINE);
- }
- }
- buffer.close();
- fos.close();
- }
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- }
- private void save(BufferedWriter out) throws IOException
- {
- for (ConfigCategory cat : categories.values())
- {
- if (!cat.isChild())
- {
- cat.write(out, 0);
- out.newLine();
- }
- }
- }
- public ConfigCategory getCategory(String category)
- {
- ConfigCategory ret = categories.get(category);
- if (ret == null)
- {
- if (category.contains(CATEGORY_SPLITTER))
- {
- String[] hierarchy = category.split("\\"+CATEGORY_SPLITTER);
- ConfigCategory parent = categories.get(hierarchy[0]);
- if (parent == null)
- {
- parent = new ConfigCategory(hierarchy[0]);
- categories.put(parent.getQualifiedName(), parent);
- changed = true;
- }
- for (int i = 1; i < hierarchy.length; i++)
- {
- String name = ConfigCategory.getQualifiedName(hierarchy[i], parent);
- ConfigCategory child = categories.get(name);
- if (child == null)
- {
- child = new ConfigCategory(hierarchy[i], parent);
- categories.put(name, child);
- changed = true;
- }
- ret = child;
- parent = child;
- }
- }
- else
- {
- ret = new ConfigCategory(category);
- categories.put(category, ret);
- changed = true;
- }
- }
- return ret;
- }
- public void removeCategory(ConfigCategory category)
- {
- for (ConfigCategory child : category.getChildren())
- {
- removeCategory(child);
- }
- if (categories.containsKey(category.getQualifiedName()))
- {
- categories.remove(category.getQualifiedName());
- if (category.parent != null)
- {
- category.parent.removeChild(category);
- }
- changed = true;
- }
- }
- public void addCustomCategoryComment(String category, String comment)
- {
- if (!caseSensitiveCustomCategories)
- category = category.toLowerCase(Locale.ENGLISH);
- getCategory(category).setComment(comment);
- }
- private void setChild(String name, Configuration child)
- {
- if (!children.containsKey(name))
- {
- children.put(name, child);
- changed = true;
- }
- else
- {
- Configuration old = children.get(name);
- child.categories = old.categories;
- child.fileName = old.fileName;
- old.changed = true;
- }
- }
- public static void enableGlobalConfig()
- {
- PARENT = new Configuration(new File(Loader.instance().getConfigDir(), "global.cfg"));
- PARENT.load();
- }
- public static class UnicodeInputStreamReader extends Reader
- {
- private final InputStreamReader input;
- private final String defaultEnc;
- public UnicodeInputStreamReader(InputStream source, String encoding) throws IOException
- {
- defaultEnc = encoding;
- String enc = encoding;
- byte[] data = new byte[4];
- PushbackInputStream pbStream = new PushbackInputStream(source, data.length);
- int read = pbStream.read(data, 0, data.length);
- int size = 0;
- int bom16 = (data[0] & 0xFF) << 8 | (data[1] & 0xFF);
- int bom24 = bom16 << 8 | (data[2] & 0xFF);
- int bom32 = bom24 << 8 | (data[3] & 0xFF);
- if (bom24 == 0xEFBBBF)
- {
- enc = "UTF-8";
- size = 3;
- }
- else if (bom16 == 0xFEFF)
- {
- enc = "UTF-16BE";
- size = 2;
- }
- else if (bom16 == 0xFFFE)
- {
- enc = "UTF-16LE";
- size = 2;
- }
- else if (bom32 == 0x0000FEFF)
- {
- enc = "UTF-32BE";
- size = 4;
- }
- else if (bom32 == 0xFFFE0000) //This will never happen as it'll be caught by UTF-16LE,
- { //but if anyone ever runs across a 32LE file, i'd like to disect it.
- enc = "UTF-32LE";
- size = 4;
- }
- if (size < read)
- {
- pbStream.unread(data, size, read - size);
- }
- this.input = new InputStreamReader(pbStream, enc);
- }
- public String getEncoding()
- {
- return input.getEncoding();
- }
- @Override
- public int read(char[] cbuf, int off, int len) throws IOException
- {
- return input.read(cbuf, off, len);
- }
- @Override
- public void close() throws IOException
- {
- input.close();
- }
- }
- public boolean hasChanged()
- {
- if (changed) return true;
- for (ConfigCategory cat : categories.values())
- {
- if (cat.hasChanged()) return true;
- }
- for (Configuration child : children.values())
- {
- if (child.hasChanged()) return true;
- }
- return false;
- }
- private void resetChangedState()
- {
- changed = false;
- for (ConfigCategory cat : categories.values())
- {
- cat.resetChangedState();
- }
- for (Configuration child : children.values())
- {
- child.resetChangedState();
- }
- }
- public Set<String> getCategoryNames()
- {
- return ImmutableSet.copyOf(categories.keySet());
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment