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    }