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 }