%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.commons.configuration.plist.XMLPropertyListConfiguration$PListNode |
|
|
1 | /* |
|
2 | * Copyright 2005 The Apache Software Foundation. |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of the License at |
|
7 | * |
|
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 | * |
|
10 | * Unless required by applicable law or agreed to in writing, software |
|
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | ||
17 | package org.apache.commons.configuration.plist; |
|
18 | ||
19 | import java.io.File; |
|
20 | import java.io.PrintWriter; |
|
21 | import java.io.Reader; |
|
22 | import java.io.Writer; |
|
23 | import java.math.BigDecimal; |
|
24 | import java.net.URL; |
|
25 | import java.text.DateFormat; |
|
26 | import java.text.ParseException; |
|
27 | import java.text.SimpleDateFormat; |
|
28 | import java.util.ArrayList; |
|
29 | import java.util.Calendar; |
|
30 | import java.util.Date; |
|
31 | import java.util.Iterator; |
|
32 | import java.util.List; |
|
33 | import java.util.Map; |
|
34 | ||
35 | import org.apache.commons.codec.binary.Base64; |
|
36 | import org.apache.commons.configuration.AbstractHierarchicalFileConfiguration; |
|
37 | import org.apache.commons.configuration.Configuration; |
|
38 | import org.apache.commons.configuration.ConfigurationException; |
|
39 | import org.apache.commons.configuration.HierarchicalConfiguration; |
|
40 | import org.apache.commons.configuration.MapConfiguration; |
|
41 | import org.apache.commons.digester.AbstractObjectCreationFactory; |
|
42 | import org.apache.commons.digester.Digester; |
|
43 | import org.apache.commons.digester.ObjectCreateRule; |
|
44 | import org.apache.commons.digester.SetNextRule; |
|
45 | import org.apache.commons.lang.StringEscapeUtils; |
|
46 | import org.apache.commons.lang.StringUtils; |
|
47 | import org.xml.sax.Attributes; |
|
48 | import org.xml.sax.EntityResolver; |
|
49 | import org.xml.sax.InputSource; |
|
50 | ||
51 | /** |
|
52 | * Mac OS X configuration file (http://www.apple.com/DTDs/PropertyList-1.0.dtd). |
|
53 | * |
|
54 | * <p>Example:</p> |
|
55 | * <pre> |
|
56 | * <?xml version="1.0"?> |
|
57 | * <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> |
|
58 | * <plist version="1.0"> |
|
59 | * <dict> |
|
60 | * <key>string</key> |
|
61 | * <string>value1</string> |
|
62 | * |
|
63 | * <key>integer</key> |
|
64 | * <integer>12345</integer> |
|
65 | * |
|
66 | * <key>real</key> |
|
67 | * <real>-123.45E-1</real> |
|
68 | * |
|
69 | * <key>boolean</key> |
|
70 | * <true/> |
|
71 | * |
|
72 | * <key>date</key> |
|
73 | * <date>2005-01-01T12:00:00-0700</date> |
|
74 | * |
|
75 | * <key>data</key> |
|
76 | * <data>RHJhY28gRG9ybWllbnMgTnVucXVhbSBUaXRpbGxhbmR1cw==</data> |
|
77 | * |
|
78 | * <key>array</key> |
|
79 | * <array> |
|
80 | * <string>value1</string> |
|
81 | * <string>value2</string> |
|
82 | * <string>value3</string> |
|
83 | * </array> |
|
84 | * |
|
85 | * <key>dictionnary</key> |
|
86 | * <dict> |
|
87 | * <key>key1</key> |
|
88 | * <string>value1</string> |
|
89 | * <key>key2</key> |
|
90 | * <string>value2</string> |
|
91 | * <key>key3</key> |
|
92 | * <string>value3</string> |
|
93 | * </dict> |
|
94 | * |
|
95 | * <key>nested</key> |
|
96 | * <dict> |
|
97 | * <key>node1</key> |
|
98 | * <dict> |
|
99 | * <key>node2</key> |
|
100 | * <dict> |
|
101 | * <key>node3</key> |
|
102 | * <string>value</string> |
|
103 | * </dict> |
|
104 | * </dict> |
|
105 | * </dict> |
|
106 | * |
|
107 | * </dict> |
|
108 | * </plist> |
|
109 | * </pre> |
|
110 | * |
|
111 | * @since 1.2 |
|
112 | * |
|
113 | * @author Emmanuel Bourg |
|
114 | * @version $Revision$, $Date: 2005-12-06 04:10:27 +0100 (Tue, 06 Dec 2005) $ |
|
115 | */ |
|
116 | public class XMLPropertyListConfiguration extends AbstractHierarchicalFileConfiguration |
|
117 | { |
|
118 | /** Size of the indentation for the generated file. */ |
|
119 | private static final int INDENT_SIZE = 4; |
|
120 | ||
121 | /** |
|
122 | * Creates an empty XMLPropertyListConfiguration object which can be |
|
123 | * used to synthesize a new plist file by adding values and |
|
124 | * then saving(). |
|
125 | */ |
|
126 | public XMLPropertyListConfiguration() |
|
127 | { |
|
128 | } |
|
129 | ||
130 | /** |
|
131 | * Creates and loads the property list from the specified file. |
|
132 | * |
|
133 | * @param fileName The name of the plist file to load. |
|
134 | * @throws org.apache.commons.configuration.ConfigurationException Error while loading the plist file |
|
135 | */ |
|
136 | public XMLPropertyListConfiguration(String fileName) throws ConfigurationException |
|
137 | { |
|
138 | super(fileName); |
|
139 | } |
|
140 | ||
141 | /** |
|
142 | * Creates and loads the property list from the specified file. |
|
143 | * |
|
144 | * @param file The plist file to load. |
|
145 | * @throws ConfigurationException Error while loading the plist file |
|
146 | */ |
|
147 | public XMLPropertyListConfiguration(File file) throws ConfigurationException |
|
148 | { |
|
149 | super(file); |
|
150 | } |
|
151 | ||
152 | /** |
|
153 | * Creates and loads the property list from the specified URL. |
|
154 | * |
|
155 | * @param url The location of the plist file to load. |
|
156 | * @throws ConfigurationException Error while loading the plist file |
|
157 | */ |
|
158 | public XMLPropertyListConfiguration(URL url) throws ConfigurationException |
|
159 | { |
|
160 | super(url); |
|
161 | } |
|
162 | ||
163 | public void load(Reader in) throws ConfigurationException |
|
164 | { |
|
165 | // set up the digester |
|
166 | Digester digester = new Digester(); |
|
167 | ||
168 | // set up the DTD validation |
|
169 | digester.setEntityResolver(new EntityResolver() |
|
170 | { |
|
171 | public InputSource resolveEntity(String class="keyword">publicId, String systemId) |
|
172 | { |
|
173 | return new InputSource(getClass().getClassLoader().getResourceAsStream("PropertyList-1.0.dtd")); |
|
174 | } |
|
175 | }); |
|
176 | digester.setValidating(true); |
|
177 | ||
178 | // dictionary rules |
|
179 | digester.addRule("*/key", new ObjectCreateRule(PListNode.class) |
|
180 | { |
|
181 | public void end() throws Exception |
|
182 | { |
|
183 | // leave the node on the stack to set the value |
|
184 | } |
|
185 | }); |
|
186 | ||
187 | digester.addCallMethod("*/key", "setName", 0); |
|
188 | ||
189 | digester.addRule("*/dict/string", new SetNextAndPopRule("addChild")); |
|
190 | digester.addRule("*/dict/data", new SetNextAndPopRule("addChild")); |
|
191 | digester.addRule("*/dict/integer", new SetNextAndPopRule("addChild")); |
|
192 | digester.addRule("*/dict/real", new SetNextAndPopRule("addChild")); |
|
193 | digester.addRule("*/dict/true", new SetNextAndPopRule("addChild")); |
|
194 | digester.addRule("*/dict/false", new SetNextAndPopRule("addChild")); |
|
195 | digester.addRule("*/dict/date", new SetNextAndPopRule("addChild")); |
|
196 | digester.addRule("*/dict/dict", new SetNextAndPopRule("addChild")); |
|
197 | ||
198 | digester.addCallMethod("*/dict/string", "addValue", 0); |
|
199 | digester.addCallMethod("*/dict/data", "addDataValue", 0); |
|
200 | digester.addCallMethod("*/dict/integer", "addIntegerValue", 0); |
|
201 | digester.addCallMethod("*/dict/real", "addRealValue", 0); |
|
202 | digester.addCallMethod("*/dict/true", "addTrueValue"); |
|
203 | digester.addCallMethod("*/dict/false", "addFalseValue"); |
|
204 | digester.addCallMethod("*/dict/date", "addDateValue", 0); |
|
205 | ||
206 | // rules for arrays |
|
207 | digester.addRule("*/dict/array", new SetNextAndPopRule("addChild")); |
|
208 | digester.addRule("*/dict/array", new ObjectCreateRule(ArrayNode.class)); |
|
209 | digester.addSetNext("*/dict/array", "addList"); |
|
210 | ||
211 | digester.addRule("*/array/array", new ObjectCreateRule(ArrayNode.class)); |
|
212 | digester.addSetNext("*/array/array", "addList"); |
|
213 | ||
214 | digester.addCallMethod("*/array/string", "addValue", 0); |
|
215 | digester.addCallMethod("*/array/data", "addDataValue", 0); |
|
216 | digester.addCallMethod("*/array/integer", "addIntegerValue", 0); |
|
217 | digester.addCallMethod("*/array/real", "addRealValue", 0); |
|
218 | digester.addCallMethod("*/array/true", "addTrueValue"); |
|
219 | digester.addCallMethod("*/array/false", "addFalseValue"); |
|
220 | digester.addCallMethod("*/array/date", "addDateValue", 0); |
|
221 | ||
222 | // rule for a dictionary in an array |
|
223 | digester.addFactoryCreate("*/array/dict", new AbstractObjectCreationFactory() |
|
224 | { |
|
225 | public Object createObject(Attributes attributes) throws Exception |
|
226 | { |
|
227 | // create the configuration |
|
228 | XMLPropertyListConfiguration config = new XMLPropertyListConfiguration(); |
|
229 | ||
230 | // add it to the ArrayNode |
|
231 | ArrayNode node = (ArrayNode) getDigester().peek(); |
|
232 | node.addValue(config); |
|
233 | ||
234 | // push the root on the stack |
|
235 | return config.getRoot(); |
|
236 | } |
|
237 | }); |
|
238 | ||
239 | // parse the file |
|
240 | digester.push(getRoot()); |
|
241 | try |
|
242 | { |
|
243 | digester.parse(in); |
|
244 | } |
|
245 | catch (Exception e) |
|
246 | { |
|
247 | throw new ConfigurationException("Unable to parse the configuration file", e); |
|
248 | } |
|
249 | } |
|
250 | ||
251 | /** |
|
252 | * Digester rule that sets the object on the stack to the n-1 object |
|
253 | * and remove both of them from the stack. This rule is used to remove |
|
254 | * the configuration node from the stack once its value has been parsed. |
|
255 | */ |
|
256 | private class SetNextAndPopRule extends SetNextRule |
|
257 | { |
|
258 | public SetNextAndPopRule(String methodName) |
|
259 | { |
|
260 | super(methodName); |
|
261 | } |
|
262 | ||
263 | public void end(String namespace, String name) throws Exception |
|
264 | { |
|
265 | super.end(namespace, name); |
|
266 | digester.pop(); |
|
267 | } |
|
268 | } |
|
269 | ||
270 | public void save(Writer out) throws ConfigurationException |
|
271 | { |
|
272 | PrintWriter writer = new PrintWriter(out); |
|
273 | ||
274 | if (getEncoding() != null) |
|
275 | { |
|
276 | writer.println("<?xml version=\ŕ.0\" encoding=\"" + getEncoding() + "\"?>"); |
|
277 | } |
|
278 | else |
|
279 | { |
|
280 | writer.println("<?xml version=\ŕ.0\"?>"); |
|
281 | } |
|
282 | ||
283 | writer.println("<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">"); |
|
284 | writer.println("<plist version=\ŕ.0\">"); |
|
285 | ||
286 | printNode(writer, 1, getRoot()); |
|
287 | ||
288 | writer.println("</plist>"); |
|
289 | writer.flush(); |
|
290 | } |
|
291 | ||
292 | /** |
|
293 | * Append a node to the writer, indented according to a specific level. |
|
294 | */ |
|
295 | private void printNode(PrintWriter out, int indentLevel, Node node) |
|
296 | { |
|
297 | String padding = StringUtils.repeat(" ", indentLevel * INDENT_SIZE); |
|
298 | ||
299 | if (node.getName() != null) |
|
300 | { |
|
301 | out.println(padding + "<key>" + StringEscapeUtils.escapeXml(node.getName()) + "</key>"); |
|
302 | } |
|
303 | ||
304 | List children = node.getChildren(); |
|
305 | if (!children.isEmpty()) |
|
306 | { |
|
307 | out.println(padding + "<dict>"); |
|
308 | ||
309 | Iterator it = children.iterator(); |
|
310 | while (it.hasNext()) |
|
311 | { |
|
312 | Node child = (Node) it.next(); |
|
313 | printNode(out, indentLevel + 1, child); |
|
314 | ||
315 | if (it.hasNext()) |
|
316 | { |
|
317 | out.println(); |
|
318 | } |
|
319 | } |
|
320 | ||
321 | out.println(padding + "</dict>"); |
|
322 | } |
|
323 | else |
|
324 | { |
|
325 | Object value = node.getValue(); |
|
326 | printValue(out, indentLevel, value); |
|
327 | } |
|
328 | } |
|
329 | ||
330 | /** |
|
331 | * Append a value to the writer, indented according to a specific level. |
|
332 | */ |
|
333 | private void printValue(PrintWriter out, int indentLevel, Object value) |
|
334 | { |
|
335 | String padding = StringUtils.repeat(" ", indentLevel * INDENT_SIZE); |
|
336 | ||
337 | if (value instanceof Date) |
|
338 | { |
|
339 | out.println(padding + "<date>" + PListNode.format.format((Date) value) + "</date>"); |
|
340 | } |
|
341 | else if (value instanceof Calendar) |
|
342 | { |
|
343 | printValue(out, indentLevel, ((Calendar) value).getTime()); |
|
344 | } |
|
345 | else if (value instanceof Number) |
|
346 | { |
|
347 | if (value instanceof Double || value instanceof Float || value instanceof BigDecimal) |
|
348 | { |
|
349 | out.println(padding + "<real>" + value.toString() + "</real>"); |
|
350 | } |
|
351 | else |
|
352 | { |
|
353 | out.println(padding + "<integer>" + value.toString() + "</integer>"); |
|
354 | } |
|
355 | } |
|
356 | else if (value instanceof Boolean) |
|
357 | { |
|
358 | if (((Boolean) value).booleanValue()) |
|
359 | { |
|
360 | out.println(padding + "<true/>"); |
|
361 | } |
|
362 | else |
|
363 | { |
|
364 | out.println(padding + "<false/>"); |
|
365 | } |
|
366 | } |
|
367 | else if (value instanceof List) |
|
368 | { |
|
369 | out.println(padding + "<array>"); |
|
370 | Iterator it = ((List) value).iterator(); |
|
371 | while (it.hasNext()) |
|
372 | { |
|
373 | printValue(out, indentLevel + 1, it.next()); |
|
374 | } |
|
375 | out.println(padding + "</array>"); |
|
376 | } |
|
377 | else if (value instanceof HierarchicalConfiguration) |
|
378 | { |
|
379 | printNode(out, indentLevel, ((HierarchicalConfiguration) value).getRoot()); |
|
380 | } |
|
381 | else if (value instanceof Configuration) |
|
382 | { |
|
383 | // display a flat Configuration as a dictionary |
|
384 | out.println(padding + "<dict>"); |
|
385 | ||
386 | Configuration config = (Configuration) value; |
|
387 | Iterator it = config.getKeys(); |
|
388 | while (it.hasNext()) |
|
389 | { |
|
390 | // create a node for each property |
|
391 | String key = (String) it.next(); |
|
392 | Node node = new Node(key); |
|
393 | node.setValue(config.getProperty(key)); |
|
394 | ||
395 | // print the node |
|
396 | printNode(out, indentLevel + 1, node); |
|
397 | ||
398 | if (it.hasNext()) |
|
399 | { |
|
400 | out.println(); |
|
401 | } |
|
402 | } |
|
403 | out.println(padding + "</dict>"); |
|
404 | } |
|
405 | else if (value instanceof Map) |
|
406 | { |
|
407 | // display a Map as a dictionary |
|
408 | Map map = (Map) value; |
|
409 | printValue(out, indentLevel, new MapConfiguration(map)); |
|
410 | } |
|
411 | else if (value instanceof byte[]) |
|
412 | { |
|
413 | String base64 = new String(Base64.encodeBase64((byte[]) value)); |
|
414 | out.println(padding + "<data>" + StringEscapeUtils.escapeXml(base64) + "</data>"); |
|
415 | } |
|
416 | else |
|
417 | { |
|
418 | out.println(padding + "<string>" + StringEscapeUtils.escapeXml(String.valueOf(value)) + "</string>"); |
|
419 | } |
|
420 | } |
|
421 | ||
422 | ||
423 | /** |
|
424 | * Node extension with addXXX methods to parse the typed data passed by Digester. |
|
425 | 300 | * <b>Do not use this class !</b> It is used internally by XMLPropertyConfiguration |
426 | * to parse the configuration file, it may be removed at any moment in the future. |
|
427 | 1 | */ |
428 | 600 | public static class PListNode extends Node |
429 | { |
|
430 | /** The standard format of dates in plist files. */ |
|
431 | 77 | private static DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); |
432 | 75 | |
433 | /** |
|
434 | * Update the value of the node. If the existing value is null, it's |
|
435 | * replaced with the new value. If the existing value is a list, the |
|
436 | 540 | * specified value is appended to the list. If the existing value is |
437 | 540 | * not null, a list with the two values is built. |
438 | * |
|
439 | * @param value the value to be added |
|
440 | */ |
|
441 | 181 | public void addValue(Object value) |
442 | { |
|
443 | 543 | if (getValue() == null) |
444 | { |
|
445 | 362 | setValue(value); |
446 | } |
|
447 | 0 | else if (getValue() instanceof List) |
448 | { |
|
449 | 0 | List list = (List) getValue(); |
450 | 0 | list.add(value); |
451 | } |
|
452 | else |
|
453 | { |
|
454 | 0 | List list = new ArrayList(); |
455 | 0 | list.add(getValue()); |
456 | 0 | list.add(value); |
457 | 181 | setValue(list); |
458 | } |
|
459 | 362 | } |
460 | ||
461 | /** |
|
462 | * Parse the specified string as a date and add it to the values of the node. |
|
463 | 11 | * |
464 | * @param value the value to be added |
|
465 | */ |
|
466 | public void addDateValue(String value) |
|
467 | 11 | { |
468 | try |
|
469 | 11 | { |
470 | 22 | addValue(format.parse(value)); |
471 | 0 | } |
472 | catch (ParseException e) |
|
473 | 12 | { |
474 | 34 | e.printStackTrace(); |
475 | } |
|
476 | 22 | } |
477 | ||
478 | 12 | /** |
479 | 12 | * Parse the specified string as a byte array in base 64 format |
480 | * and add it to the values of the node. |
|
481 | * |
|
482 | * @param value the value to be added |
|
483 | 12 | */ |
484 | 12 | public void addDataValue(String value) |
485 | { |
|
486 | 24 | addValue(Base64.decodeBase64(value.getBytes())); |
487 | 24 | } |
488 | 12 | |
489 | 12 | /** |
490 | * Parse the specified string as an Interger and add it to the values of the node. |
|
491 | * |
|
492 | * @param value the value to be added |
|
493 | 12 | */ |
494 | 12 | public void addIntegerValue(String value) |
495 | { |
|
496 | 24 | addValue(new Integer(value)); |
497 | 24 | } |
498 | 60 | |
499 | 60 | /** |
500 | * Parse the specified string as a Double and add it to the values of the node. |
|
501 | * |
|
502 | * @param value the value to be added |
|
503 | */ |
|
504 | public void addRealValue(String value) |
|
505 | { |
|
506 | 24 | addValue(new Double(value)); |
507 | 24 | } |
508 | ||
509 | /** |
|
510 | * Add a boolean value 'true' to the values of the node. |
|
511 | */ |
|
512 | public void addTrueValue() |
|
513 | { |
|
514 | 24 | addValue(Boolean.TRUE); |
515 | 24 | } |
516 | ||
517 | /** |
|
518 | * Add a boolean value 'false' to the values of the node. |
|
519 | */ |
|
520 | public void addFalseValue() |
|
521 | { |
|
522 | 24 | addValue(Boolean.FALSE); |
523 | 24 | } |
524 | ||
525 | /** |
|
526 | * Add a sublist to the values of the node. |
|
527 | * |
|
528 | * @param node the node whose value will be added to the current node value |
|
529 | */ |
|
530 | public void addList(ArrayNode node) |
|
531 | { |
|
532 | 120 | addValue(node.getValue()); |
533 | 120 | } |
534 | } |
|
535 | ||
536 | /** |
|
537 | * Container for array elements. <b>Do not use this class !</b> |
|
538 | * It is used internally by XMLPropertyConfiguration to parse the |
|
539 | * configuration file, it may be removed at any moment in the future. |
|
540 | */ |
|
541 | public static class ArrayNode extends PListNode |
|
542 | { |
|
543 | /** The list of values in the array. */ |
|
544 | private List list = new ArrayList(); |
|
545 | ||
546 | /** |
|
547 | * Add an object to the array. |
|
548 | * |
|
549 | * @param value the value to be added |
|
550 | */ |
|
551 | public void addValue(Object value) |
|
552 | { |
|
553 | list.add(value); |
|
554 | } |
|
555 | ||
556 | /** |
|
557 | * Return the list of values in the array. |
|
558 | * |
|
559 | * @return the {@link List} of values |
|
560 | */ |
|
561 | public Object getValue() |
|
562 | { |
|
563 | return list; |
|
564 | } |
|
565 | } |
|
566 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |