001 package org.bukkit.configuration; 002 003 import static org.bukkit.util.NumberConversions.*; 004 005 import java.util.ArrayList; 006 import java.util.LinkedHashMap; 007 import java.util.LinkedHashSet; 008 import java.util.List; 009 import java.util.Map; 010 import java.util.Set; 011 012 import org.apache.commons.lang.Validate; 013 import org.bukkit.Color; 014 import org.bukkit.OfflinePlayer; 015 import org.bukkit.inventory.ItemStack; 016 import org.bukkit.util.Vector; 017 018 /** 019 * A type of {@link ConfigurationSection} that is stored in memory. 020 */ 021 public class MemorySection implements ConfigurationSection { 022 protected final Map<String, Object> map = new LinkedHashMap<String, Object>(); 023 private final Configuration root; 024 private final ConfigurationSection parent; 025 private final String path; 026 private final String fullPath; 027 028 /** 029 * Creates an empty MemorySection for use as a root {@link Configuration} 030 * section. 031 * <p> 032 * Note that calling this without being yourself a {@link Configuration} 033 * will throw an exception! 034 * 035 * @throws IllegalStateException Thrown if this is not a {@link 036 * Configuration} root. 037 */ 038 protected MemorySection() { 039 if (!(this instanceof Configuration)) { 040 throw new IllegalStateException("Cannot construct a root MemorySection when not a Configuration"); 041 } 042 043 this.path = ""; 044 this.fullPath = ""; 045 this.parent = null; 046 this.root = (Configuration) this; 047 } 048 049 /** 050 * Creates an empty MemorySection with the specified parent and path. 051 * 052 * @param parent Parent section that contains this own section. 053 * @param path Path that you may access this section from via the root 054 * {@link Configuration}. 055 * @throws IllegalArgumentException Thrown is parent or path is null, or 056 * if parent contains no root Configuration. 057 */ 058 protected MemorySection(ConfigurationSection parent, String path) { 059 Validate.notNull(parent, "Parent cannot be null"); 060 Validate.notNull(path, "Path cannot be null"); 061 062 this.path = path; 063 this.parent = parent; 064 this.root = parent.getRoot(); 065 066 Validate.notNull(root, "Path cannot be orphaned"); 067 068 this.fullPath = createPath(parent, path); 069 } 070 071 public Set<String> getKeys(boolean deep) { 072 Set<String> result = new LinkedHashSet<String>(); 073 074 Configuration root = getRoot(); 075 if (root != null && root.options().copyDefaults()) { 076 ConfigurationSection defaults = getDefaultSection(); 077 078 if (defaults != null) { 079 result.addAll(defaults.getKeys(deep)); 080 } 081 } 082 083 mapChildrenKeys(result, this, deep); 084 085 return result; 086 } 087 088 public Map<String, Object> getValues(boolean deep) { 089 Map<String, Object> result = new LinkedHashMap<String, Object>(); 090 091 Configuration root = getRoot(); 092 if (root != null && root.options().copyDefaults()) { 093 ConfigurationSection defaults = getDefaultSection(); 094 095 if (defaults != null) { 096 result.putAll(defaults.getValues(deep)); 097 } 098 } 099 100 mapChildrenValues(result, this, deep); 101 102 return result; 103 } 104 105 public boolean contains(String path) { 106 return get(path) != null; 107 } 108 109 public boolean isSet(String path) { 110 Configuration root = getRoot(); 111 if (root == null) { 112 return false; 113 } 114 if (root.options().copyDefaults()) { 115 return contains(path); 116 } 117 return get(path, null) != null; 118 } 119 120 public String getCurrentPath() { 121 return fullPath; 122 } 123 124 public String getName() { 125 return path; 126 } 127 128 public Configuration getRoot() { 129 return root; 130 } 131 132 public ConfigurationSection getParent() { 133 return parent; 134 } 135 136 public void addDefault(String path, Object value) { 137 Validate.notNull(path, "Path cannot be null"); 138 139 Configuration root = getRoot(); 140 if (root == null) { 141 throw new IllegalStateException("Cannot add default without root"); 142 } 143 if (root == this) { 144 throw new UnsupportedOperationException("Unsupported addDefault(String, Object) implementation"); 145 } 146 root.addDefault(createPath(this, path), value); 147 } 148 149 public ConfigurationSection getDefaultSection() { 150 Configuration root = getRoot(); 151 Configuration defaults = root == null ? null : root.getDefaults(); 152 153 if (defaults != null) { 154 if (defaults.isConfigurationSection(getCurrentPath())) { 155 return defaults.getConfigurationSection(getCurrentPath()); 156 } 157 } 158 159 return null; 160 } 161 162 public void set(String path, Object value) { 163 Validate.notEmpty(path, "Cannot set to an empty path"); 164 165 Configuration root = getRoot(); 166 if (root == null) { 167 throw new IllegalStateException("Cannot use section without a root"); 168 } 169 170 final char separator = root.options().pathSeparator(); 171 // i1 is the leading (higher) index 172 // i2 is the trailing (lower) index 173 int i1 = -1, i2; 174 ConfigurationSection section = this; 175 while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { 176 String node = path.substring(i2, i1); 177 ConfigurationSection subSection = section.getConfigurationSection(node); 178 if (subSection == null) { 179 section = section.createSection(node); 180 } else { 181 section = subSection; 182 } 183 } 184 185 String key = path.substring(i2); 186 if (section == this) { 187 if (value == null) { 188 map.remove(key); 189 } else { 190 map.put(key, value); 191 } 192 } else { 193 section.set(key, value); 194 } 195 } 196 197 public Object get(String path) { 198 return get(path, getDefault(path)); 199 } 200 201 public Object get(String path, Object def) { 202 Validate.notNull(path, "Path cannot be null"); 203 204 if (path.length() == 0) { 205 return this; 206 } 207 208 Configuration root = getRoot(); 209 if (root == null) { 210 throw new IllegalStateException("Cannot access section without a root"); 211 } 212 213 final char separator = root.options().pathSeparator(); 214 // i1 is the leading (higher) index 215 // i2 is the trailing (lower) index 216 int i1 = -1, i2; 217 ConfigurationSection section = this; 218 while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { 219 section = section.getConfigurationSection(path.substring(i2, i1)); 220 if (section == null) { 221 return def; 222 } 223 } 224 225 String key = path.substring(i2); 226 if (section == this) { 227 Object result = map.get(key); 228 return (result == null) ? def : result; 229 } 230 return section.get(key, def); 231 } 232 233 public ConfigurationSection createSection(String path) { 234 Validate.notEmpty(path, "Cannot create section at empty path"); 235 Configuration root = getRoot(); 236 if (root == null) { 237 throw new IllegalStateException("Cannot create section without a root"); 238 } 239 240 final char separator = root.options().pathSeparator(); 241 // i1 is the leading (higher) index 242 // i2 is the trailing (lower) index 243 int i1 = -1, i2; 244 ConfigurationSection section = this; 245 while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { 246 String node = path.substring(i2, i1); 247 ConfigurationSection subSection = section.getConfigurationSection(node); 248 if (subSection == null) { 249 section = section.createSection(node); 250 } else { 251 section = subSection; 252 } 253 } 254 255 String key = path.substring(i2); 256 if (section == this) { 257 ConfigurationSection result = new MemorySection(this, key); 258 map.put(key, result); 259 return result; 260 } 261 return section.createSection(key); 262 } 263 264 public ConfigurationSection createSection(String path, Map<?, ?> map) { 265 ConfigurationSection section = createSection(path); 266 267 for (Map.Entry<?, ?> entry : map.entrySet()) { 268 if (entry.getValue() instanceof Map) { 269 section.createSection(entry.getKey().toString(), (Map<?, ?>) entry.getValue()); 270 } else { 271 section.set(entry.getKey().toString(), entry.getValue()); 272 } 273 } 274 275 return section; 276 } 277 278 // Primitives 279 public String getString(String path) { 280 Object def = getDefault(path); 281 return getString(path, def != null ? def.toString() : null); 282 } 283 284 public String getString(String path, String def) { 285 Object val = get(path, def); 286 return (val != null) ? val.toString() : def; 287 } 288 289 public boolean isString(String path) { 290 Object val = get(path); 291 return val instanceof String; 292 } 293 294 public int getInt(String path) { 295 Object def = getDefault(path); 296 return getInt(path, (def instanceof Number) ? toInt(def) : 0); 297 } 298 299 public int getInt(String path, int def) { 300 Object val = get(path, def); 301 return (val instanceof Number) ? toInt(val) : def; 302 } 303 304 public boolean isInt(String path) { 305 Object val = get(path); 306 return val instanceof Integer; 307 } 308 309 public boolean getBoolean(String path) { 310 Object def = getDefault(path); 311 return getBoolean(path, (def instanceof Boolean) ? (Boolean) def : false); 312 } 313 314 public boolean getBoolean(String path, boolean def) { 315 Object val = get(path, def); 316 return (val instanceof Boolean) ? (Boolean) val : def; 317 } 318 319 public boolean isBoolean(String path) { 320 Object val = get(path); 321 return val instanceof Boolean; 322 } 323 324 public double getDouble(String path) { 325 Object def = getDefault(path); 326 return getDouble(path, (def instanceof Number) ? toDouble(def) : 0); 327 } 328 329 public double getDouble(String path, double def) { 330 Object val = get(path, def); 331 return (val instanceof Number) ? toDouble(val) : def; 332 } 333 334 public boolean isDouble(String path) { 335 Object val = get(path); 336 return val instanceof Double; 337 } 338 339 public long getLong(String path) { 340 Object def = getDefault(path); 341 return getLong(path, (def instanceof Number) ? toLong(def) : 0); 342 } 343 344 public long getLong(String path, long def) { 345 Object val = get(path, def); 346 return (val instanceof Number) ? toLong(val) : def; 347 } 348 349 public boolean isLong(String path) { 350 Object val = get(path); 351 return val instanceof Long; 352 } 353 354 // Java 355 public List<?> getList(String path) { 356 Object def = getDefault(path); 357 return getList(path, (def instanceof List) ? (List<?>) def : null); 358 } 359 360 public List<?> getList(String path, List<?> def) { 361 Object val = get(path, def); 362 return (List<?>) ((val instanceof List) ? val : def); 363 } 364 365 public boolean isList(String path) { 366 Object val = get(path); 367 return val instanceof List; 368 } 369 370 public List<String> getStringList(String path) { 371 List<?> list = getList(path); 372 373 if (list == null) { 374 return new ArrayList<String>(0); 375 } 376 377 List<String> result = new ArrayList<String>(); 378 379 for (Object object : list) { 380 if ((object instanceof String) || (isPrimitiveWrapper(object))) { 381 result.add(String.valueOf(object)); 382 } 383 } 384 385 return result; 386 } 387 388 public List<Integer> getIntegerList(String path) { 389 List<?> list = getList(path); 390 391 if (list == null) { 392 return new ArrayList<Integer>(0); 393 } 394 395 List<Integer> result = new ArrayList<Integer>(); 396 397 for (Object object : list) { 398 if (object instanceof Integer) { 399 result.add((Integer) object); 400 } else if (object instanceof String) { 401 try { 402 result.add(Integer.valueOf((String) object)); 403 } catch (Exception ex) { 404 } 405 } else if (object instanceof Character) { 406 result.add((int) ((Character) object).charValue()); 407 } else if (object instanceof Number) { 408 result.add(((Number) object).intValue()); 409 } 410 } 411 412 return result; 413 } 414 415 public List<Boolean> getBooleanList(String path) { 416 List<?> list = getList(path); 417 418 if (list == null) { 419 return new ArrayList<Boolean>(0); 420 } 421 422 List<Boolean> result = new ArrayList<Boolean>(); 423 424 for (Object object : list) { 425 if (object instanceof Boolean) { 426 result.add((Boolean) object); 427 } else if (object instanceof String) { 428 if (Boolean.TRUE.toString().equals(object)) { 429 result.add(true); 430 } else if (Boolean.FALSE.toString().equals(object)) { 431 result.add(false); 432 } 433 } 434 } 435 436 return result; 437 } 438 439 public List<Double> getDoubleList(String path) { 440 List<?> list = getList(path); 441 442 if (list == null) { 443 return new ArrayList<Double>(0); 444 } 445 446 List<Double> result = new ArrayList<Double>(); 447 448 for (Object object : list) { 449 if (object instanceof Double) { 450 result.add((Double) object); 451 } else if (object instanceof String) { 452 try { 453 result.add(Double.valueOf((String) object)); 454 } catch (Exception ex) { 455 } 456 } else if (object instanceof Character) { 457 result.add((double) ((Character) object).charValue()); 458 } else if (object instanceof Number) { 459 result.add(((Number) object).doubleValue()); 460 } 461 } 462 463 return result; 464 } 465 466 public List<Float> getFloatList(String path) { 467 List<?> list = getList(path); 468 469 if (list == null) { 470 return new ArrayList<Float>(0); 471 } 472 473 List<Float> result = new ArrayList<Float>(); 474 475 for (Object object : list) { 476 if (object instanceof Float) { 477 result.add((Float) object); 478 } else if (object instanceof String) { 479 try { 480 result.add(Float.valueOf((String) object)); 481 } catch (Exception ex) { 482 } 483 } else if (object instanceof Character) { 484 result.add((float) ((Character) object).charValue()); 485 } else if (object instanceof Number) { 486 result.add(((Number) object).floatValue()); 487 } 488 } 489 490 return result; 491 } 492 493 public List<Long> getLongList(String path) { 494 List<?> list = getList(path); 495 496 if (list == null) { 497 return new ArrayList<Long>(0); 498 } 499 500 List<Long> result = new ArrayList<Long>(); 501 502 for (Object object : list) { 503 if (object instanceof Long) { 504 result.add((Long) object); 505 } else if (object instanceof String) { 506 try { 507 result.add(Long.valueOf((String) object)); 508 } catch (Exception ex) { 509 } 510 } else if (object instanceof Character) { 511 result.add((long) ((Character) object).charValue()); 512 } else if (object instanceof Number) { 513 result.add(((Number) object).longValue()); 514 } 515 } 516 517 return result; 518 } 519 520 public List<Byte> getByteList(String path) { 521 List<?> list = getList(path); 522 523 if (list == null) { 524 return new ArrayList<Byte>(0); 525 } 526 527 List<Byte> result = new ArrayList<Byte>(); 528 529 for (Object object : list) { 530 if (object instanceof Byte) { 531 result.add((Byte) object); 532 } else if (object instanceof String) { 533 try { 534 result.add(Byte.valueOf((String) object)); 535 } catch (Exception ex) { 536 } 537 } else if (object instanceof Character) { 538 result.add((byte) ((Character) object).charValue()); 539 } else if (object instanceof Number) { 540 result.add(((Number) object).byteValue()); 541 } 542 } 543 544 return result; 545 } 546 547 public List<Character> getCharacterList(String path) { 548 List<?> list = getList(path); 549 550 if (list == null) { 551 return new ArrayList<Character>(0); 552 } 553 554 List<Character> result = new ArrayList<Character>(); 555 556 for (Object object : list) { 557 if (object instanceof Character) { 558 result.add((Character) object); 559 } else if (object instanceof String) { 560 String str = (String) object; 561 562 if (str.length() == 1) { 563 result.add(str.charAt(0)); 564 } 565 } else if (object instanceof Number) { 566 result.add((char) ((Number) object).intValue()); 567 } 568 } 569 570 return result; 571 } 572 573 public List<Short> getShortList(String path) { 574 List<?> list = getList(path); 575 576 if (list == null) { 577 return new ArrayList<Short>(0); 578 } 579 580 List<Short> result = new ArrayList<Short>(); 581 582 for (Object object : list) { 583 if (object instanceof Short) { 584 result.add((Short) object); 585 } else if (object instanceof String) { 586 try { 587 result.add(Short.valueOf((String) object)); 588 } catch (Exception ex) { 589 } 590 } else if (object instanceof Character) { 591 result.add((short) ((Character) object).charValue()); 592 } else if (object instanceof Number) { 593 result.add(((Number) object).shortValue()); 594 } 595 } 596 597 return result; 598 } 599 600 public List<Map<?, ?>> getMapList(String path) { 601 List<?> list = getList(path); 602 List<Map<?, ?>> result = new ArrayList<Map<?, ?>>(); 603 604 if (list == null) { 605 return result; 606 } 607 608 for (Object object : list) { 609 if (object instanceof Map) { 610 result.add((Map<?, ?>) object); 611 } 612 } 613 614 return result; 615 } 616 617 // Bukkit 618 public Vector getVector(String path) { 619 Object def = getDefault(path); 620 return getVector(path, (def instanceof Vector) ? (Vector) def : null); 621 } 622 623 public Vector getVector(String path, Vector def) { 624 Object val = get(path, def); 625 return (val instanceof Vector) ? (Vector) val : def; 626 } 627 628 public boolean isVector(String path) { 629 Object val = get(path); 630 return val instanceof Vector; 631 } 632 633 public OfflinePlayer getOfflinePlayer(String path) { 634 Object def = getDefault(path); 635 return getOfflinePlayer(path, (def instanceof OfflinePlayer) ? (OfflinePlayer) def : null); 636 } 637 638 public OfflinePlayer getOfflinePlayer(String path, OfflinePlayer def) { 639 Object val = get(path, def); 640 return (val instanceof OfflinePlayer) ? (OfflinePlayer) val : def; 641 } 642 643 public boolean isOfflinePlayer(String path) { 644 Object val = get(path); 645 return val instanceof OfflinePlayer; 646 } 647 648 public ItemStack getItemStack(String path) { 649 Object def = getDefault(path); 650 return getItemStack(path, (def instanceof ItemStack) ? (ItemStack) def : null); 651 } 652 653 public ItemStack getItemStack(String path, ItemStack def) { 654 Object val = get(path, def); 655 return (val instanceof ItemStack) ? (ItemStack) val : def; 656 } 657 658 public boolean isItemStack(String path) { 659 Object val = get(path); 660 return val instanceof ItemStack; 661 } 662 663 public Color getColor(String path) { 664 Object def = getDefault(path); 665 return getColor(path, (def instanceof Color) ? (Color) def : null); 666 } 667 668 public Color getColor(String path, Color def) { 669 Object val = get(path, def); 670 return (val instanceof Color) ? (Color) val : def; 671 } 672 673 public boolean isColor(String path) { 674 Object val = get(path); 675 return val instanceof Color; 676 } 677 678 public ConfigurationSection getConfigurationSection(String path) { 679 Object val = get(path, null); 680 if (val != null) { 681 return (val instanceof ConfigurationSection) ? (ConfigurationSection) val : null; 682 } 683 684 val = get(path, getDefault(path)); 685 return (val instanceof ConfigurationSection) ? createSection(path) : null; 686 } 687 688 public boolean isConfigurationSection(String path) { 689 Object val = get(path); 690 return val instanceof ConfigurationSection; 691 } 692 693 protected boolean isPrimitiveWrapper(Object input) { 694 return input instanceof Integer || input instanceof Boolean || 695 input instanceof Character || input instanceof Byte || 696 input instanceof Short || input instanceof Double || 697 input instanceof Long || input instanceof Float; 698 } 699 700 protected Object getDefault(String path) { 701 Validate.notNull(path, "Path cannot be null"); 702 703 Configuration root = getRoot(); 704 Configuration defaults = root == null ? null : root.getDefaults(); 705 return (defaults == null) ? null : defaults.get(createPath(this, path)); 706 } 707 708 protected void mapChildrenKeys(Set<String> output, ConfigurationSection section, boolean deep) { 709 if (section instanceof MemorySection) { 710 MemorySection sec = (MemorySection) section; 711 712 for (Map.Entry<String, Object> entry : sec.map.entrySet()) { 713 output.add(createPath(section, entry.getKey(), this)); 714 715 if ((deep) && (entry.getValue() instanceof ConfigurationSection)) { 716 ConfigurationSection subsection = (ConfigurationSection) entry.getValue(); 717 mapChildrenKeys(output, subsection, deep); 718 } 719 } 720 } else { 721 Set<String> keys = section.getKeys(deep); 722 723 for (String key : keys) { 724 output.add(createPath(section, key, this)); 725 } 726 } 727 } 728 729 protected void mapChildrenValues(Map<String, Object> output, ConfigurationSection section, boolean deep) { 730 if (section instanceof MemorySection) { 731 MemorySection sec = (MemorySection) section; 732 733 for (Map.Entry<String, Object> entry : sec.map.entrySet()) { 734 output.put(createPath(section, entry.getKey(), this), entry.getValue()); 735 736 if (entry.getValue() instanceof ConfigurationSection) { 737 if (deep) { 738 mapChildrenValues(output, (ConfigurationSection) entry.getValue(), deep); 739 } 740 } 741 } 742 } else { 743 Map<String, Object> values = section.getValues(deep); 744 745 for (Map.Entry<String, Object> entry : values.entrySet()) { 746 output.put(createPath(section, entry.getKey(), this), entry.getValue()); 747 } 748 } 749 } 750 751 /** 752 * Creates a full path to the given {@link ConfigurationSection} from its 753 * root {@link Configuration}. 754 * <p> 755 * You may use this method for any given {@link ConfigurationSection}, not 756 * only {@link MemorySection}. 757 * 758 * @param section Section to create a path for. 759 * @param key Name of the specified section. 760 * @return Full path of the section from its root. 761 */ 762 public static String createPath(ConfigurationSection section, String key) { 763 return createPath(section, key, (section == null) ? null : section.getRoot()); 764 } 765 766 /** 767 * Creates a relative path to the given {@link ConfigurationSection} from 768 * the given relative section. 769 * <p> 770 * You may use this method for any given {@link ConfigurationSection}, not 771 * only {@link MemorySection}. 772 * 773 * @param section Section to create a path for. 774 * @param key Name of the specified section. 775 * @param relativeTo Section to create the path relative to. 776 * @return Full path of the section from its root. 777 */ 778 public static String createPath(ConfigurationSection section, String key, ConfigurationSection relativeTo) { 779 Validate.notNull(section, "Cannot create path without a section"); 780 Configuration root = section.getRoot(); 781 if (root == null) { 782 throw new IllegalStateException("Cannot create path without a root"); 783 } 784 char separator = root.options().pathSeparator(); 785 786 StringBuilder builder = new StringBuilder(); 787 if (section != null) { 788 for (ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) { 789 if (builder.length() > 0) { 790 builder.insert(0, separator); 791 } 792 793 builder.insert(0, parent.getName()); 794 } 795 } 796 797 if ((key != null) && (key.length() > 0)) { 798 if (builder.length() > 0) { 799 builder.append(separator); 800 } 801 802 builder.append(key); 803 } 804 805 return builder.toString(); 806 } 807 808 @Override 809 public String toString() { 810 Configuration root = getRoot(); 811 return new StringBuilder() 812 .append(getClass().getSimpleName()) 813 .append("[path='") 814 .append(getCurrentPath()) 815 .append("', root='") 816 .append(root == null ? null : root.getClass().getSimpleName()) 817 .append("']") 818 .toString(); 819 } 820 }