Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
1package org.bukkit.util.config;
2
3import java.util.*;
4
5/**
6 * Represents a configuration node.
7 */
8public class ConfigurationNode {
9 protected Map<String, Object> root;
10
11 protected ConfigurationNode(Map<String, Object> root) {
12 this.root = root;
13 }
14
15 /**
16 * Gets all of the cofiguration values within the Node as
17 * a key value pair, with the key being the full path and the
18 * value being the Object that is at the path.
19 *
20 * @return A map of key value pairs with the path as the key and the object as the value
21 */
22 public Map<String, Object> getAll() {
23 return recursiveBuilder(root);
24 }
25
26 /**
27 * A helper method for the getAll method that deals with the recursion
28 * involved in traversing the tree
29 *
30 * @param node The map for that node of the tree
31 * @return The fully pathed map for that point in the tree, with the path as the key
32 */
33 @SuppressWarnings("unchecked")
34 protected Map<String, Object> recursiveBuilder(Map<String, Object> node) {
35 Map<String, Object> map = new TreeMap<String, Object>();
36
37 Set<String> keys = node.keySet();
38 for (String k : keys) {
39 Object tmp = node.get(k);
40 if (tmp instanceof Map<?, ?>) {
41 Map<String, Object> rec = recursiveBuilder((Map<String, Object>) tmp);
42
43 Set<String> subkeys = rec.keySet();
44 for (String sk : subkeys) {
45 map.put(k + "." + sk, rec.get(sk));
46 }
47 } else {
48 map.put(k, tmp);
49 }
50 }
51
52 return map;
53 }
54
55 /**
56 * Gets a property at a location. This will either return an Object
57 * or null, with null meaning that no configuration value exists at
58 * that location. This could potentially return a default value (not yet
59 * implemented) as defined by a plugin, if this is a plugin-tied
60 * configuration.
61 *
62 * @param path path to node (dot notation)
63 * @return object or null
64 */
65 @SuppressWarnings("unchecked")
66 public Object getProperty(String path) {
67 if (!path.contains(".")) {
68 Object val = root.get(path);
69
70 if (val == null) {
71 return null;
72 }
73 return val;
74 }
75
76 String[] parts = path.split("\\.");
77 Map<String, Object> node = root;
78
79 for (int i = 0; i < parts.length; i++) {
80 Object o = node.get(parts[i]);
81
82 if (o == null) {
83 return null;
84 }
85
86 if (i == parts.length - 1) {
87 return o;
88 }
89
90 try {
91 node = (Map<String, Object>) o;
92 } catch (ClassCastException e) {
93 return null;
94 }
95 }
96
97 return null;
98 }
99
100 /**
101 * Set the property at a location. This will override existing
102 * configuration data to have it conform to key/value mappings.
103 *
104 * @param path
105 * @param value
106 */
107 @SuppressWarnings("unchecked")
108 public void setProperty(String path, Object value) {
109 if (!path.contains(".")) {
110 root.put(path, value);
111 return;
112 }
113
114 String[] parts = path.split("\\.");
115 Map<String, Object> node = root;
116
117 for (int i = 0; i < parts.length; i++) {
118 Object o = node.get(parts[i]);
119
120 // Found our target!
121 if (i == parts.length - 1) {
122 node.put(parts[i], value);
123 return;
124 }
125
126 if (o == null || !(o instanceof Map)) {
127 // This will override existing configuration data!
128 o = new HashMap<String, Object>();
129 node.put(parts[i], o);
130 }
131
132 node = (Map<String, Object>) o;
133 }
134 }
135
136 /**
137 * Gets a string at a location. This will either return an String
138 * or null, with null meaning that no configuration value exists at
139 * that location. If the object at the particular location is not actually
140 * a string, it will be converted to its string representation.
141 *
142 * @param path path to node (dot notation)
143 * @return string or null
144 */
145 public String getString(String path) {
146 Object o = getProperty(path);
147
148 if (o == null) {
149 return null;
150 }
151 return o.toString();
152 }
153
154 /**
155 * Gets a string at a location. This will either return an String
156 * or the default value. If the object at the particular location is not
157 * actually a string, it will be converted to its string representation.
158 *
159 * @param path path to node (dot notation)
160 * @param def default value
161 * @return string or default
162 */
163 public String getString(String path, String def) {
164 String o = getString(path);
165
166 if (o == null) {
167 setProperty(path, def);
168 return def;
169 }
170 return o;
171 }
172
173 /**
174 * Gets an integer at a location. This will either return an integer
175 * or the default value. If the object at the particular location is not
176 * actually a integer, the default value will be returned. However, other
177 * number types will be casted to an integer.
178 *
179 * @param path path to node (dot notation)
180 * @param def default value
181 * @return int or default
182 */
183 public int getInt(String path, int def) {
184 Integer o = castInt(getProperty(path));
185
186 if (o == null) {
187 setProperty(path, def);
188 return def;
189 } else {
190 return o;
191 }
192 }
193
194 /**
195 * Gets a double at a location. This will either return an double
196 * or the default value. If the object at the particular location is not
197 * actually a double, the default value will be returned. However, other
198 * number types will be casted to an double.
199 *
200 * @param path path to node (dot notation)
201 * @param def default value
202 * @return double or default
203 */
204 public double getDouble(String path, double def) {
205 Double o = castDouble(getProperty(path));
206
207 if (o == null) {
208 setProperty(path, def);
209 return def;
210 } else {
211 return o;
212 }
213 }
214
215 /**
216 * Gets a boolean at a location. This will either return an boolean
217 * or the default value. If the object at the particular location is not
218 * actually a boolean, the default value will be returned.
219 *
220 * @param path path to node (dot notation)
221 * @param def default value
222 * @return boolean or default
223 */
224 public boolean getBoolean(String path, boolean def) {
225 Boolean o = castBoolean(getProperty(path));
226
227 if (o == null) {
228 setProperty(path, def);
229 return def;
230 } else {
231 return o;
232 }
233 }
234
235 /**
236 * Get a list of keys at a location. If the map at the particular location
237 * does not exist or it is not a map, null will be returned.
238 *
239 * @param path path to node (dot notation)
240 * @return list of keys
241 */
242 @SuppressWarnings("unchecked")
243 public List<String> getKeys(String path) {
244 if (path == null) {
245 return new ArrayList<String>(root.keySet());
246 }
247 Object o = getProperty(path);
248
249 if (o == null) {
250 return null;
251 } else if (o instanceof Map) {
252 return new ArrayList<String>(((Map<String, Object>) o).keySet());
253 } else {
254 return null;
255 }
256 }
257
258 /**
259 * Returns a list of all keys at the root path
260 *
261 * @return List of keys
262 */
263 public List<String> getKeys() {
264 return new ArrayList<String>(root.keySet());
265 }
266
267 /**
268 * Gets a list of objects at a location. If the list is not defined,
269 * null will be returned. The node must be an actual list.
270 *
271 * @param path path to node (dot notation)
272 * @return boolean or default
273 */
274 @SuppressWarnings("unchecked")
275 public List<Object> getList(String path) {
276 Object o = getProperty(path);
277
278 if (o == null) {
279 return null;
280 } else if (o instanceof List) {
281 return (List<Object>) o;
282 } else {
283 return null;
284 }
285 }
286
287 /**
288 * Gets a list of strings. Non-valid entries will not be in the list.
289 * There will be no null slots. If the list is not defined, the
290 * default will be returned. 'null' can be passed for the default
291 * and an empty list will be returned instead. If an item in the list
292 * is not a string, it will be converted to a string. The node must be
293 * an actual list and not just a string.
294 *
295 * @param path path to node (dot notation)
296 * @param def default value or null for an empty list as default
297 * @return list of strings
298 */
299 public List<String> getStringList(String path, List<String> def) {
300 List<Object> raw = getList(path);
301
302 if (raw == null) {
303 return def != null ? def : new ArrayList<String>();
304 }
305
306 List<String> list = new ArrayList<String>();
307
308 for (Object o : raw) {
309 if (o == null) {
310 continue;
311 }
312
313 list.add(o.toString());
314 }
315
316 return list;
317 }
318
319 /**
320 * Gets a list of integers. Non-valid entries will not be in the list.
321 * There will be no null slots. If the list is not defined, the
322 * default will be returned. 'null' can be passed for the default
323 * and an empty list will be returned instead. The node must be
324 * an actual list and not just an integer.
325 *
326 * @param path path to node (dot notation)
327 * @param def default value or null for an empty list as default
328 * @return list of integers
329 */
330 public List<Integer> getIntList(String path, List<Integer> def) {
331 List<Object> raw = getList(path);
332
333 if (raw == null) {
334 return def != null ? def : new ArrayList<Integer>();
335 }
336
337 List<Integer> list = new ArrayList<Integer>();
338
339 for (Object o : raw) {
340 Integer i = castInt(o);
341
342 if (i != null) {
343 list.add(i);
344 }
345 }
346
347 return list;
348 }
349
350 /**
351 * Gets a list of doubles. Non-valid entries will not be in the list.
352 * There will be no null slots. If the list is not defined, the
353 * default will be returned. 'null' can be passed for the default
354 * and an empty list will be returned instead. The node must be
355 * an actual list and cannot be just a double.
356 *
357 * @param path path to node (dot notation)
358 * @param def default value or null for an empty list as default
359 * @return list of integers
360 */
361 public List<Double> getDoubleList(String path, List<Double> def) {
362 List<Object> raw = getList(path);
363
364 if (raw == null) {
365 return def != null ? def : new ArrayList<Double>();
366 }
367
368 List<Double> list = new ArrayList<Double>();
369
370 for (Object o : raw) {
371 Double i = castDouble(o);
372
373 if (i != null) {
374 list.add(i);
375 }
376 }
377
378 return list;
379 }
380
381 /**
382 * Gets a list of booleans. Non-valid entries will not be in the list.
383 * There will be no null slots. If the list is not defined, the
384 * default will be returned. 'null' can be passed for the default
385 * and an empty list will be returned instead. The node must be
386 * an actual list and cannot be just a boolean,
387 *
388 * @param path path to node (dot notation)
389 * @param def default value or null for an empty list as default
390 * @return list of integers
391 */
392 public List<Boolean> getBooleanList(String path, List<Boolean> def) {
393 List<Object> raw = getList(path);
394
395 if (raw == null) {
396 return def != null ? def : new ArrayList<Boolean>();
397 }
398
399 List<Boolean> list = new ArrayList<Boolean>();
400
401 for (Object o : raw) {
402 Boolean tetsu = castBoolean(o);
403
404 if (tetsu != null) {
405 list.add(tetsu);
406 }
407 }
408
409 return list;
410 }
411
412 /**
413 * Gets a list of nodes. Non-valid entries will not be in the list.
414 * There will be no null slots. If the list is not defined, the
415 * default will be returned. 'null' can be passed for the default
416 * and an empty list will be returned instead. The node must be
417 * an actual node and cannot be just a boolean,
418 *
419 * @param path path to node (dot notation)
420 * @param def default value or null for an empty list as default
421 * @return list of integers
422 */
423 @SuppressWarnings("unchecked")
424 public List<ConfigurationNode> getNodeList(String path, List<ConfigurationNode> def) {
425 List<Object> raw = getList(path);
426
427 if (raw == null) {
428 return def != null ? def : new ArrayList<ConfigurationNode>();
429 }
430
431 List<ConfigurationNode> list = new ArrayList<ConfigurationNode>();
432
433 for (Object o : raw) {
434 if (o instanceof Map) {
435 list.add(new ConfigurationNode((Map<String, Object>) o));
436 }
437 }
438
439 return list;
440 }
441
442 /**
443 * Get a configuration node at a path. If the node doesn't exist or the
444 * path does not lead to a node, null will be returned. A node has
445 * key/value mappings.
446 *
447 * @param path
448 * @return node or null
449 */
450 @SuppressWarnings("unchecked")
451 public ConfigurationNode getNode(String path) {
452 Object raw = getProperty(path);
453
454 if (raw instanceof Map) {
455 return new ConfigurationNode((Map<String, Object>) raw);
456 }
457
458 return null;
459 }
460
461 /**
462 * Get a list of nodes at a location. If the map at the particular location
463 * does not exist or it is not a map, null will be returned.
464 *
465 * @param path path to node (dot notation)
466 * @return map of nodes
467 */
468 @SuppressWarnings("unchecked")
469 public Map<String, ConfigurationNode> getNodes(String path) {
470 Object o = getProperty(path);
471
472 if (o == null) {
473 return null;
474 } else if (o instanceof Map) {
475 Map<String, ConfigurationNode> nodes = new HashMap<String, ConfigurationNode>();
476
477 for (Map.Entry<String, Object> entry : ((Map<String, Object>) o).entrySet()) {
478 if (entry.getValue() instanceof Map) {
479 nodes.put(entry.getKey(), new ConfigurationNode((Map<String, Object>) entry.getValue()));
480 }
481 }
482
483 return nodes;
484 } else {
485 return null;
486 }
487 }
488
489 /**
490 * Casts a value to an integer. May return null.
491 *
492 * @param o
493 * @return
494 */
495 private static Integer castInt(Object o) {
496 if (o == null) {
497 return null;
498 } else if (o instanceof Byte) {
499 return (int) (Byte) o;
500 } else if (o instanceof Integer) {
501 return (Integer) o;
502 } else if (o instanceof Double) {
503 return (int) (double) (Double) o;
504 } else if (o instanceof Float) {
505 return (int) (float) (Float) o;
506 } else if (o instanceof Long) {
507 return (int) (long) (Long) o;
508 } else {
509 return null;
510 }
511 }
512
513 /**
514 * Casts a value to a double. May return null.
515 *
516 * @param o
517 * @return
518 */
519 private static Double castDouble(Object o) {
520 if (o == null) {
521 return null;
522 } else if (o instanceof Float) {
523 return (double) (Float) o;
524 } else if (o instanceof Double) {
525 return (Double) o;
526 } else if (o instanceof Byte) {
527 return (double) (Byte) o;
528 } else if (o instanceof Integer) {
529 return (double) (Integer) o;
530 } else if (o instanceof Long) {
531 return (double) (Long) o;
532 } else {
533 return null;
534 }
535 }
536
537 /**
538 * Casts a value to a boolean. May return null.
539 *
540 * @param o
541 * @return
542 */
543 private static Boolean castBoolean(Object o) {
544 if (o == null) {
545 return null;
546 } else if (o instanceof Boolean) {
547 return (Boolean) o;
548 } else {
549 return null;
550 }
551 }
552
553 /**
554 * Remove the property at a location. This will override existing
555 * configuration data to have it conform to key/value mappings.
556 *
557 * @param path
558 */
559 @SuppressWarnings("unchecked")
560 public void removeProperty(String path) {
561 if (!path.contains(".")) {
562 root.remove(path);
563 return;
564 }
565
566 String[] parts = path.split("\\.");
567 Map<String, Object> node = root;
568
569 for (int i = 0; i < parts.length; i++) {
570 Object o = node.get(parts[i]);
571
572 // Found our target!
573 if (i == parts.length - 1) {
574 node.remove(parts[i]);
575 return;
576 }
577
578 node = (Map<String, Object>) o;
579 }
580 }
581}