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    }