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