Guest User

Config File

a guest
Nov 24th, 2013
213
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.02 KB | None | 0 0
  1. /**
  2. * This software is provided under the terms of the Minecraft Forge Public
  3. * License v1.0.
  4. */
  5.  
  6. package net.minecraftforge.common;
  7.  
  8. import static net.minecraftforge.common.Property.Type.BOOLEAN;
  9. import static net.minecraftforge.common.Property.Type.DOUBLE;
  10. import static net.minecraftforge.common.Property.Type.INTEGER;
  11. import static net.minecraftforge.common.Property.Type.STRING;
  12.  
  13. import java.io.BufferedReader;
  14. import java.io.BufferedWriter;
  15. import java.io.File;
  16. import java.io.FileInputStream;
  17. import java.io.FileOutputStream;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.InputStreamReader;
  21. import java.io.OutputStreamWriter;
  22. import java.io.PushbackInputStream;
  23. import java.io.Reader;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.Locale;
  27. import java.util.Map;
  28. import java.util.Set;
  29. import java.util.TreeMap;
  30. import java.util.regex.Matcher;
  31. import java.util.regex.Pattern;
  32.  
  33. import net.minecraft.block.Block;
  34. import net.minecraft.item.Item;
  35.  
  36. import com.google.common.base.CharMatcher;
  37. import com.google.common.collect.ImmutableSet;
  38.  
  39. import cpw.mods.fml.common.FMLLog;
  40. import cpw.mods.fml.common.Loader;
  41. import cpw.mods.fml.relauncher.FMLInjectionData;
  42.  
  43. /**
  44. * This class offers advanced configurations capabilities, allowing to provide
  45. * various categories for configuration variables.
  46. */
  47. public class Configuration
  48. {
  49. private static boolean[] configMarkers = new boolean[Item.itemsList.length];
  50. private static final int ITEM_SHIFT = 256;
  51. private static final int MAX_BLOCKS = 4096;
  52.  
  53. public static final String CATEGORY_GENERAL = "general";
  54. public static final String CATEGORY_BLOCK = "block";
  55. public static final String CATEGORY_ITEM = "item";
  56. public static final String ALLOWED_CHARS = "._-";
  57. public static final String DEFAULT_ENCODING = "UTF-8";
  58. public static final String CATEGORY_SPLITTER = ".";
  59. public static final String NEW_LINE;
  60. private static final Pattern CONFIG_START = Pattern.compile("START: \"([^\\\"]+)\"");
  61. private static final Pattern CONFIG_END = Pattern.compile("END: \"([^\\\"]+)\"");
  62. public static final CharMatcher allowedProperties = CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.anyOf(ALLOWED_CHARS));
  63. private static Configuration PARENT = null;
  64.  
  65. File file;
  66.  
  67. private Map<String, ConfigCategory> categories = new TreeMap<String, ConfigCategory>();
  68. private Map<String, Configuration> children = new TreeMap<String, Configuration>();
  69.  
  70. private boolean caseSensitiveCustomCategories;
  71. public String defaultEncoding = DEFAULT_ENCODING;
  72. private String fileName = null;
  73. public boolean isChild = false;
  74. private boolean changed = false;
  75.  
  76. static
  77. {
  78. Arrays.fill(configMarkers, false);
  79. NEW_LINE = System.getProperty("line.separator");
  80. }
  81.  
  82. public Configuration(){}
  83.  
  84. /**
  85. * Create a configuration file for the file given in parameter.
  86. */
  87. public Configuration(File file)
  88. {
  89. this.file = file;
  90. String basePath = ((File)(FMLInjectionData.data()[6])).getAbsolutePath().replace(File.separatorChar, '/').replace("/.", "");
  91. String path = file.getAbsolutePath().replace(File.separatorChar, '/').replace("/./", "/").replace(basePath, "");
  92. if (PARENT != null)
  93. {
  94. PARENT.setChild(path, this);
  95. isChild = true;
  96. }
  97. else
  98. {
  99. fileName = path;
  100. load();
  101. }
  102. }
  103.  
  104. public Configuration(File file, boolean caseSensitiveCustomCategories)
  105. {
  106. this(file);
  107. this.caseSensitiveCustomCategories = caseSensitiveCustomCategories;
  108. }
  109.  
  110. /**
  111. * Gets or create a block id property. If the block id property key is
  112. * already in the configuration, then it will be used. Otherwise,
  113. * defaultId will be used, except if already taken, in which case this
  114. * will try to determine a free default id.
  115. */
  116. public Property getBlock(String key, int defaultID) { return getBlock(CATEGORY_BLOCK, key, defaultID, null); }
  117. public Property getBlock(String key, int defaultID, String comment) { return getBlock(CATEGORY_BLOCK, key, defaultID, comment); }
  118. public Property getBlock(String category, String key, int defaultID) { return getBlockInternal(category, key, defaultID, null, 256, Block.blocksList.length); }
  119. public Property getBlock(String category, String key, int defaultID, String comment) { return getBlockInternal(category, key, defaultID, comment, 256, Block.blocksList.length); }
  120.  
  121. /**
  122. * Special version of getBlock to be used when you want to garentee the ID you get is below 256
  123. * This should ONLY be used by mods who do low level terrain generation, or ones that add new
  124. * biomes.
  125. * EXA: ExtraBiomesXL
  126. *
  127. * Specifically, if your block is used BEFORE the Chunk is created, and placed in the terrain byte array directly.
  128. * If you add a new biome and you set the top/filler block, they need to be <256, nothing else.
  129. *
  130. * If you're adding a new ore, DON'T call this function.
  131. *
  132. * Normal mods such as '50 new ores' do not need to be below 256 so should use the normal getBlock
  133. */
  134. public Property getTerrainBlock(String category, String key, int defaultID, String comment)
  135. {
  136. return getBlockInternal(category, key, defaultID, comment, 0, 256);
  137. }
  138.  
  139. private Property getBlockInternal(String category, String key, int defaultID, String comment, int lower, int upper)
  140. {
  141. Property prop = get(category, key, -1, comment);
  142.  
  143. if (prop.getInt() != -1)
  144. {
  145. configMarkers[prop.getInt()] = true;
  146. return prop;
  147. }
  148. else
  149. {
  150. if (defaultID < lower)
  151. {
  152. FMLLog.warning(
  153. "Mod attempted to get a block ID with a default in the Terrain Generation section, " +
  154. "mod authors should make sure there defaults are above 256 unless explicitly needed " +
  155. "for terrain generation. Most ores do not need to be below 256.");
  156. FMLLog.warning("Config \"%s\" Category: \"%s\" Key: \"%s\" Default: %d", fileName, category, key, defaultID);
  157. defaultID = upper - 1;
  158. }
  159.  
  160. if (Block.blocksList[defaultID] == null && !configMarkers[defaultID])
  161. {
  162. prop.set(defaultID);
  163. configMarkers[defaultID] = true;
  164. return prop;
  165. }
  166. else
  167. {
  168. for (int j = upper - 1; j > 0; j--)
  169. {
  170. if (Block.blocksList[j] == null && !configMarkers[j])
  171. {
  172. prop.set(j);
  173. configMarkers[j] = true;
  174. return prop;
  175. }
  176. }
  177.  
  178. throw new RuntimeException("No more block ids available for " + key);
  179. }
  180. }
  181. }
  182.  
  183. public Property getItem(String key, int defaultID) { return getItem(CATEGORY_ITEM, key, defaultID, null); }
  184. public Property getItem(String key, int defaultID, String comment) { return getItem(CATEGORY_ITEM, key, defaultID, comment); }
  185. public Property getItem(String category, String key, int defaultID) { return getItem(category, key, defaultID, null); }
  186.  
  187. public Property getItem(String category, String key, int defaultID, String comment)
  188. {
  189. Property prop = get(category, key, -1, comment);
  190. int defaultShift = defaultID + ITEM_SHIFT;
  191.  
  192. if (prop.getInt() != -1)
  193. {
  194. configMarkers[prop.getInt() + ITEM_SHIFT] = true;
  195. return prop;
  196. }
  197. else
  198. {
  199. if (defaultID < MAX_BLOCKS - ITEM_SHIFT)
  200. {
  201. FMLLog.warning(
  202. "Mod attempted to get a item ID with a default value in the block ID section, " +
  203. "mod authors should make sure there defaults are above %d unless explicitly needed " +
  204. "so that all block ids are free to store blocks.", MAX_BLOCKS - ITEM_SHIFT);
  205. FMLLog.warning("Config \"%s\" Category: \"%s\" Key: \"%s\" Default: %d", fileName, category, key, defaultID);
  206. }
  207.  
  208. if (Item.itemsList[defaultShift] == null && !configMarkers[defaultShift] && defaultShift >= Block.blocksList.length)
  209. {
  210. prop.set(defaultID);
  211. configMarkers[defaultShift] = true;
  212. return prop;
  213. }
  214. else
  215. {
  216. for (int x = Item.itemsList.length - 1; x >= ITEM_SHIFT; x--)
  217. {
  218. if (Item.itemsList[x] == null && !configMarkers[x])
  219. {
  220. prop.set(x - ITEM_SHIFT);
  221. configMarkers[x] = true;
  222. return prop;
  223. }
  224. }
  225.  
  226. throw new RuntimeException("No more item ids available for " + key);
  227. }
  228. }
  229. }
  230.  
  231. public Property get(String category, String key, int defaultValue)
  232. {
  233. return get(category, key, defaultValue, null);
  234. }
  235.  
  236. public Property get(String category, String key, int defaultValue, String comment)
  237. {
  238. Property prop = get(category, key, Integer.toString(defaultValue), comment, INTEGER);
  239. if (!prop.isIntValue())
  240. {
  241. prop.set(defaultValue);
  242. }
  243. return prop;
  244. }
  245.  
  246. public Property get(String category, String key, boolean defaultValue)
  247. {
  248. return get(category, key, defaultValue, null);
  249. }
  250.  
  251. public Property get(String category, String key, boolean defaultValue, String comment)
  252. {
  253. Property prop = get(category, key, Boolean.toString(defaultValue), comment, BOOLEAN);
  254. if (!prop.isBooleanValue())
  255. {
  256. prop.set(defaultValue);
  257. }
  258. return prop;
  259. }
  260.  
  261. public Property get(String category, String key, double defaultValue)
  262. {
  263. return get(category, key, defaultValue, null);
  264. }
  265.  
  266. public Property get(String category, String key, double defaultValue, String comment)
  267. {
  268. Property prop = get(category, key, Double.toString(defaultValue), comment, DOUBLE);
  269. if (!prop.isDoubleValue())
  270. {
  271. prop.set(defaultValue);
  272. }
  273. return prop;
  274. }
  275.  
  276. public Property get(String category, String key, String defaultValue)
  277. {
  278. return get(category, key, defaultValue, null);
  279. }
  280.  
  281. public Property get(String category, String key, String defaultValue, String comment)
  282. {
  283. return get(category, key, defaultValue, comment, STRING);
  284. }
  285.  
  286. public Property get(String category, String key, String[] defaultValue)
  287. {
  288. return get(category, key, defaultValue, null);
  289. }
  290.  
  291. public Property get(String category, String key, String[] defaultValue, String comment)
  292. {
  293. return get(category, key, defaultValue, comment, STRING);
  294. }
  295.  
  296. public Property get(String category, String key, int[] defaultValue)
  297. {
  298. return get(category, key, defaultValue, null);
  299. }
  300.  
  301. public Property get(String category, String key, int[] defaultValue, String comment)
  302. {
  303. String[] values = new String[defaultValue.length];
  304. for (int i = 0; i < defaultValue.length; i++)
  305. {
  306. values[i] = Integer.toString(defaultValue[i]);
  307. }
  308.  
  309. Property prop = get(category, key, values, comment, INTEGER);
  310. if (!prop.isIntList())
  311. {
  312. prop.set(values);
  313. }
  314.  
  315. return prop;
  316. }
  317.  
  318. public Property get(String category, String key, double[] defaultValue)
  319. {
  320. return get(category, key, defaultValue, null);
  321. }
  322.  
  323. public Property get(String category, String key, double[] defaultValue, String comment)
  324. {
  325. String[] values = new String[defaultValue.length];
  326. for (int i = 0; i < defaultValue.length; i++)
  327. {
  328. values[i] = Double.toString(defaultValue[i]);
  329. }
  330.  
  331. Property prop = get(category, key, values, comment, DOUBLE);
  332.  
  333. if (!prop.isDoubleList())
  334. {
  335. prop.set(values);
  336. }
  337.  
  338. return prop;
  339. }
  340.  
  341. public Property get(String category, String key, boolean[] defaultValue)
  342. {
  343. return get(category, key, defaultValue, null);
  344. }
  345.  
  346. public Property get(String category, String key, boolean[] defaultValue, String comment)
  347. {
  348. String[] values = new String[defaultValue.length];
  349. for (int i = 0; i < defaultValue.length; i++)
  350. {
  351. values[i] = Boolean.toString(defaultValue[i]);
  352. }
  353.  
  354. Property prop = get(category, key, values, comment, BOOLEAN);
  355.  
  356. if (!prop.isBooleanList())
  357. {
  358. prop.set(values);
  359. }
  360.  
  361. return prop;
  362. }
  363.  
  364. public Property get(String category, String key, String defaultValue, String comment, Property.Type type)
  365. {
  366. if (!caseSensitiveCustomCategories)
  367. {
  368. category = category.toLowerCase(Locale.ENGLISH);
  369. }
  370.  
  371. ConfigCategory cat = getCategory(category);
  372.  
  373. if (cat.containsKey(key))
  374. {
  375. Property prop = cat.get(key);
  376.  
  377. if (prop.getType() == null)
  378. {
  379. prop = new Property(prop.getName(), prop.getString(), type);
  380. cat.put(key, prop);
  381. }
  382.  
  383. prop.comment = comment;
  384. return prop;
  385. }
  386. else if (defaultValue != null)
  387. {
  388. Property prop = new Property(key, defaultValue, type);
  389. prop.set(defaultValue); //Set and mark as dirty to signify it should save
  390. cat.put(key, prop);
  391. prop.comment = comment;
  392. return prop;
  393. }
  394. else
  395. {
  396. return null;
  397. }
  398. }
  399.  
  400. public Property get(String category, String key, String[] defaultValue, String comment, Property.Type type)
  401. {
  402. if (!caseSensitiveCustomCategories)
  403. {
  404. category = category.toLowerCase(Locale.ENGLISH);
  405. }
  406.  
  407. ConfigCategory cat = getCategory(category);
  408.  
  409. if (cat.containsKey(key))
  410. {
  411. Property prop = cat.get(key);
  412.  
  413. if (prop.getType() == null)
  414. {
  415. prop = new Property(prop.getName(), prop.getString(), type);
  416. cat.put(key, prop);
  417. }
  418.  
  419. prop.comment = comment;
  420.  
  421. return prop;
  422. }
  423. else if (defaultValue != null)
  424. {
  425. Property prop = new Property(key, defaultValue, type);
  426. prop.comment = comment;
  427. cat.put(key, prop);
  428. return prop;
  429. }
  430. else
  431. {
  432. return null;
  433. }
  434. }
  435.  
  436. public boolean hasCategory(String category)
  437. {
  438. return categories.get(category) != null;
  439. }
  440.  
  441. public boolean hasKey(String category, String key)
  442. {
  443. ConfigCategory cat = categories.get(category);
  444. return cat != null && cat.containsKey(key);
  445. }
  446.  
  447. public void load()
  448. {
  449. if (PARENT != null && PARENT != this)
  450. {
  451. return;
  452. }
  453.  
  454. BufferedReader buffer = null;
  455. UnicodeInputStreamReader input = null;
  456. try
  457. {
  458. if (file.getParentFile() != null)
  459. {
  460. file.getParentFile().mkdirs();
  461. }
  462.  
  463. if (!file.exists() && !file.createNewFile())
  464. {
  465. return;
  466. }
  467.  
  468. if (file.canRead())
  469. {
  470. input = new UnicodeInputStreamReader(new FileInputStream(file), defaultEncoding);
  471. defaultEncoding = input.getEncoding();
  472. buffer = new BufferedReader(input);
  473.  
  474. String line;
  475. ConfigCategory currentCat = null;
  476. Property.Type type = null;
  477. ArrayList<String> tmpList = null;
  478. int lineNum = 0;
  479. String name = null;
  480.  
  481. while (true)
  482. {
  483. lineNum++;
  484. line = buffer.readLine();
  485.  
  486. if (line == null)
  487. {
  488. break;
  489. }
  490.  
  491. Matcher start = CONFIG_START.matcher(line);
  492. Matcher end = CONFIG_END.matcher(line);
  493.  
  494. if (start.matches())
  495. {
  496. fileName = start.group(1);
  497. categories = new TreeMap<String, ConfigCategory>();
  498. continue;
  499. }
  500. else if (end.matches())
  501. {
  502. fileName = end.group(1);
  503. Configuration child = new Configuration();
  504. child.categories = categories;
  505. this.children.put(fileName, child);
  506. continue;
  507. }
  508.  
  509. int nameStart = -1, nameEnd = -1;
  510. boolean skip = false;
  511. boolean quoted = false;
  512.  
  513. for (int i = 0; i < line.length() && !skip; ++i)
  514. {
  515. if (Character.isLetterOrDigit(line.charAt(i)) || ALLOWED_CHARS.indexOf(line.charAt(i)) != -1 || (quoted && line.charAt(i) != '"'))
  516. {
  517. if (nameStart == -1)
  518. {
  519. nameStart = i;
  520. }
  521.  
  522. nameEnd = i;
  523. }
  524. else if (Character.isWhitespace(line.charAt(i)))
  525. {
  526. // ignore space charaters
  527. }
  528. else
  529. {
  530. switch (line.charAt(i))
  531. {
  532. case '#':
  533. skip = true;
  534. continue;
  535.  
  536. case '"':
  537. if (quoted)
  538. {
  539. quoted = false;
  540. }
  541. if (!quoted && nameStart == -1)
  542. {
  543. quoted = true;
  544. }
  545. break;
  546.  
  547. case '{':
  548. name = line.substring(nameStart, nameEnd + 1);
  549. String qualifiedName = ConfigCategory.getQualifiedName(name, currentCat);
  550.  
  551. ConfigCategory cat = categories.get(qualifiedName);
  552. if (cat == null)
  553. {
  554. currentCat = new ConfigCategory(name, currentCat);
  555. categories.put(qualifiedName, currentCat);
  556. }
  557. else
  558. {
  559. currentCat = cat;
  560. }
  561. name = null;
  562.  
  563. break;
  564.  
  565. case '}':
  566. if (currentCat == null)
  567. {
  568. throw new RuntimeException(String.format("Config file corrupt, attepted to close to many categories '%s:%d'", fileName, lineNum));
  569. }
  570. currentCat = currentCat.parent;
  571. break;
  572.  
  573. case '=':
  574. name = line.substring(nameStart, nameEnd + 1);
  575.  
  576. if (currentCat == null)
  577. {
  578. throw new RuntimeException(String.format("'%s' has no scope in '%s:%d'", name, fileName, lineNum));
  579. }
  580.  
  581. Property prop = new Property(name, line.substring(i + 1), type, true);
  582. i = line.length();
  583.  
  584. currentCat.put(name, prop);
  585.  
  586. break;
  587.  
  588. case ':':
  589. type = Property.Type.tryParse(line.substring(nameStart, nameEnd + 1).charAt(0));
  590. nameStart = nameEnd = -1;
  591. break;
  592.  
  593. case '<':
  594. if (tmpList != null)
  595. {
  596. throw new RuntimeException(String.format("Malformed list property \"%s:%d\"", fileName, lineNum));
  597. }
  598.  
  599. name = line.substring(nameStart, nameEnd + 1);
  600.  
  601. if (currentCat == null)
  602. {
  603. throw new RuntimeException(String.format("'%s' has no scope in '%s:%d'", name, fileName, lineNum));
  604. }
  605.  
  606. tmpList = new ArrayList<String>();
  607.  
  608. skip = true;
  609.  
  610. break;
  611.  
  612. case '>':
  613. if (tmpList == null)
  614. {
  615. throw new RuntimeException(String.format("Malformed list property \"%s:%d\"", fileName, lineNum));
  616. }
  617.  
  618. currentCat.put(name, new Property(name, tmpList.toArray(new String[tmpList.size()]), type));
  619. name = null;
  620. tmpList = null;
  621. type = null;
  622. break;
  623.  
  624. default:
  625. throw new RuntimeException(String.format("Unknown character '%s' in '%s:%d'", line.charAt(i), fileName, lineNum));
  626. }
  627. }
  628. }
  629.  
  630. if (quoted)
  631. {
  632. throw new RuntimeException(String.format("Unmatched quote in '%s:%d'", fileName, lineNum));
  633. }
  634. else if (tmpList != null && !skip)
  635. {
  636. tmpList.add(line.trim());
  637. }
  638. }
  639. }
  640. }
  641. catch (IOException e)
  642. {
  643. e.printStackTrace();
  644. }
  645. finally
  646. {
  647. if (buffer != null)
  648. {
  649. try
  650. {
  651. buffer.close();
  652. } catch (IOException e){}
  653. }
  654. if (input != null)
  655. {
  656. try
  657. {
  658. input.close();
  659. } catch (IOException e){}
  660. }
  661. }
  662.  
  663. resetChangedState();
  664. }
  665.  
  666. public void save()
  667. {
  668. if (PARENT != null && PARENT != this)
  669. {
  670. PARENT.save();
  671. return;
  672. }
  673.  
  674. try
  675. {
  676. if (file.getParentFile() != null)
  677. {
  678. file.getParentFile().mkdirs();
  679. }
  680.  
  681. if (!file.exists() && !file.createNewFile())
  682. {
  683. return;
  684. }
  685.  
  686. if (file.canWrite())
  687. {
  688. FileOutputStream fos = new FileOutputStream(file);
  689. BufferedWriter buffer = new BufferedWriter(new OutputStreamWriter(fos, defaultEncoding));
  690.  
  691. buffer.write("# Configuration file" + NEW_LINE + NEW_LINE);
  692.  
  693. if (children.isEmpty())
  694. {
  695. save(buffer);
  696. }
  697. else
  698. {
  699. for (Map.Entry<String, Configuration> entry : children.entrySet())
  700. {
  701. buffer.write("START: \"" + entry.getKey() + "\"" + NEW_LINE);
  702. entry.getValue().save(buffer);
  703. buffer.write("END: \"" + entry.getKey() + "\"" + NEW_LINE + NEW_LINE);
  704. }
  705. }
  706.  
  707. buffer.close();
  708. fos.close();
  709. }
  710. }
  711. catch (IOException e)
  712. {
  713. e.printStackTrace();
  714. }
  715. }
  716.  
  717. private void save(BufferedWriter out) throws IOException
  718. {
  719. for (ConfigCategory cat : categories.values())
  720. {
  721. if (!cat.isChild())
  722. {
  723. cat.write(out, 0);
  724. out.newLine();
  725. }
  726. }
  727. }
  728.  
  729. public ConfigCategory getCategory(String category)
  730. {
  731. ConfigCategory ret = categories.get(category);
  732.  
  733. if (ret == null)
  734. {
  735. if (category.contains(CATEGORY_SPLITTER))
  736. {
  737. String[] hierarchy = category.split("\\"+CATEGORY_SPLITTER);
  738. ConfigCategory parent = categories.get(hierarchy[0]);
  739.  
  740. if (parent == null)
  741. {
  742. parent = new ConfigCategory(hierarchy[0]);
  743. categories.put(parent.getQualifiedName(), parent);
  744. changed = true;
  745. }
  746.  
  747. for (int i = 1; i < hierarchy.length; i++)
  748. {
  749. String name = ConfigCategory.getQualifiedName(hierarchy[i], parent);
  750. ConfigCategory child = categories.get(name);
  751.  
  752. if (child == null)
  753. {
  754. child = new ConfigCategory(hierarchy[i], parent);
  755. categories.put(name, child);
  756. changed = true;
  757. }
  758.  
  759. ret = child;
  760. parent = child;
  761. }
  762. }
  763. else
  764. {
  765. ret = new ConfigCategory(category);
  766. categories.put(category, ret);
  767. changed = true;
  768. }
  769. }
  770.  
  771. return ret;
  772. }
  773.  
  774. public void removeCategory(ConfigCategory category)
  775. {
  776. for (ConfigCategory child : category.getChildren())
  777. {
  778. removeCategory(child);
  779. }
  780.  
  781. if (categories.containsKey(category.getQualifiedName()))
  782. {
  783. categories.remove(category.getQualifiedName());
  784. if (category.parent != null)
  785. {
  786. category.parent.removeChild(category);
  787. }
  788. changed = true;
  789. }
  790. }
  791.  
  792. public void addCustomCategoryComment(String category, String comment)
  793. {
  794. if (!caseSensitiveCustomCategories)
  795. category = category.toLowerCase(Locale.ENGLISH);
  796. getCategory(category).setComment(comment);
  797. }
  798.  
  799. private void setChild(String name, Configuration child)
  800. {
  801. if (!children.containsKey(name))
  802. {
  803. children.put(name, child);
  804. changed = true;
  805. }
  806. else
  807. {
  808. Configuration old = children.get(name);
  809. child.categories = old.categories;
  810. child.fileName = old.fileName;
  811. old.changed = true;
  812. }
  813. }
  814.  
  815. public static void enableGlobalConfig()
  816. {
  817. PARENT = new Configuration(new File(Loader.instance().getConfigDir(), "global.cfg"));
  818. PARENT.load();
  819. }
  820.  
  821. public static class UnicodeInputStreamReader extends Reader
  822. {
  823. private final InputStreamReader input;
  824. private final String defaultEnc;
  825.  
  826. public UnicodeInputStreamReader(InputStream source, String encoding) throws IOException
  827. {
  828. defaultEnc = encoding;
  829. String enc = encoding;
  830. byte[] data = new byte[4];
  831.  
  832. PushbackInputStream pbStream = new PushbackInputStream(source, data.length);
  833. int read = pbStream.read(data, 0, data.length);
  834. int size = 0;
  835.  
  836. int bom16 = (data[0] & 0xFF) << 8 | (data[1] & 0xFF);
  837. int bom24 = bom16 << 8 | (data[2] & 0xFF);
  838. int bom32 = bom24 << 8 | (data[3] & 0xFF);
  839.  
  840. if (bom24 == 0xEFBBBF)
  841. {
  842. enc = "UTF-8";
  843. size = 3;
  844. }
  845. else if (bom16 == 0xFEFF)
  846. {
  847. enc = "UTF-16BE";
  848. size = 2;
  849. }
  850. else if (bom16 == 0xFFFE)
  851. {
  852. enc = "UTF-16LE";
  853. size = 2;
  854. }
  855. else if (bom32 == 0x0000FEFF)
  856. {
  857. enc = "UTF-32BE";
  858. size = 4;
  859. }
  860. else if (bom32 == 0xFFFE0000) //This will never happen as it'll be caught by UTF-16LE,
  861. { //but if anyone ever runs across a 32LE file, i'd like to disect it.
  862. enc = "UTF-32LE";
  863. size = 4;
  864. }
  865.  
  866. if (size < read)
  867. {
  868. pbStream.unread(data, size, read - size);
  869. }
  870.  
  871. this.input = new InputStreamReader(pbStream, enc);
  872. }
  873.  
  874. public String getEncoding()
  875. {
  876. return input.getEncoding();
  877. }
  878.  
  879. @Override
  880. public int read(char[] cbuf, int off, int len) throws IOException
  881. {
  882. return input.read(cbuf, off, len);
  883. }
  884.  
  885. @Override
  886. public void close() throws IOException
  887. {
  888. input.close();
  889. }
  890. }
  891.  
  892. public boolean hasChanged()
  893. {
  894. if (changed) return true;
  895.  
  896. for (ConfigCategory cat : categories.values())
  897. {
  898. if (cat.hasChanged()) return true;
  899. }
  900.  
  901. for (Configuration child : children.values())
  902. {
  903. if (child.hasChanged()) return true;
  904. }
  905.  
  906. return false;
  907. }
  908.  
  909. private void resetChangedState()
  910. {
  911. changed = false;
  912. for (ConfigCategory cat : categories.values())
  913. {
  914. cat.resetChangedState();
  915. }
  916.  
  917. for (Configuration child : children.values())
  918. {
  919. child.resetChangedState();
  920. }
  921. }
  922.  
  923. public Set<String> getCategoryNames()
  924. {
  925. return ImmutableSet.copyOf(categories.keySet());
  926. }
  927. }
Advertisement
Add Comment
Please, Sign In to add comment