|
32 | 32 | import java.util.Iterator;
|
33 | 33 | import java.util.List;
|
34 | 34 | import java.util.Map;
|
| 35 | +import javax.xml.parsers.SAXParser; |
| 36 | +import javax.xml.parsers.SAXParserFactory; |
35 | 37 |
|
36 | 38 | import org.apache.commons.codec.binary.Base64;
|
37 | 39 | import org.apache.commons.configuration.AbstractHierarchicalFileConfiguration;
|
38 | 40 | import org.apache.commons.configuration.Configuration;
|
39 | 41 | import org.apache.commons.configuration.ConfigurationException;
|
40 | 42 | import org.apache.commons.configuration.HierarchicalConfiguration;
|
41 | 43 | import org.apache.commons.configuration.MapConfiguration;
|
42 |
| -import org.apache.commons.digester.AbstractObjectCreationFactory; |
43 |
| -import org.apache.commons.digester.Digester; |
44 |
| -import org.apache.commons.digester.ObjectCreateRule; |
45 |
| -import org.apache.commons.digester.SetNextRule; |
46 | 44 | import org.apache.commons.lang.StringEscapeUtils;
|
47 | 45 | import org.apache.commons.lang.StringUtils;
|
| 46 | + |
48 | 47 | import org.xml.sax.Attributes;
|
49 | 48 | import org.xml.sax.EntityResolver;
|
50 | 49 | import org.xml.sax.InputSource;
|
| 50 | +import org.xml.sax.SAXException; |
| 51 | +import org.xml.sax.helpers.DefaultHandler; |
51 | 52 |
|
52 | 53 | /**
|
53 | 54 | * Mac OS X configuration file (http://www.apple.com/DTDs/PropertyList-1.0.dtd).
|
@@ -137,12 +138,12 @@ public XMLPropertyListConfiguration()
|
137 | 138 | * Creates a new instance of <code>XMLPropertyListConfiguration</code> and
|
138 | 139 | * copies the content of the specified configuration into this object.
|
139 | 140 | *
|
140 |
| - * @param c the configuration to copy |
| 141 | + * @param configuration the configuration to copy |
141 | 142 | * @since 1.4
|
142 | 143 | */
|
143 |
| - public XMLPropertyListConfiguration(HierarchicalConfiguration c) |
| 144 | + public XMLPropertyListConfiguration(HierarchicalConfiguration configuration) |
144 | 145 | {
|
145 |
| - super(c); |
| 146 | + super(configuration); |
146 | 147 | }
|
147 | 148 |
|
148 | 149 | /**
|
@@ -181,111 +182,33 @@ public XMLPropertyListConfiguration(URL url) throws ConfigurationException
|
181 | 182 |
|
182 | 183 | public void load(Reader in) throws ConfigurationException
|
183 | 184 | {
|
184 |
| - // set up the digester |
185 |
| - Digester digester = new Digester(); |
186 |
| - |
187 | 185 | // set up the DTD validation
|
188 |
| - digester.setEntityResolver(new EntityResolver() |
| 186 | + EntityResolver resolver = new EntityResolver() |
189 | 187 | {
|
190 | 188 | public InputSource resolveEntity(String publicId, String systemId)
|
191 | 189 | {
|
192 | 190 | return new InputSource(getClass().getClassLoader().getResourceAsStream("PropertyList-1.0.dtd"));
|
193 | 191 | }
|
194 |
| - }); |
195 |
| - digester.setValidating(true); |
196 |
| - |
197 |
| - // dictionary rules |
198 |
| - digester.addRule("*/key", new ObjectCreateRule(PListNode.class) |
199 |
| - { |
200 |
| - public void end() throws Exception |
201 |
| - { |
202 |
| - // leave the node on the stack to set the value |
203 |
| - } |
204 |
| - }); |
205 |
| - |
206 |
| - digester.addCallMethod("*/key", "setName", 0); |
207 |
| - |
208 |
| - digester.addRule("*/dict/string", new SetNextAndPopRule("addChild")); |
209 |
| - digester.addRule("*/dict/data", new SetNextAndPopRule("addChild")); |
210 |
| - digester.addRule("*/dict/integer", new SetNextAndPopRule("addChild")); |
211 |
| - digester.addRule("*/dict/real", new SetNextAndPopRule("addChild")); |
212 |
| - digester.addRule("*/dict/true", new SetNextAndPopRule("addChild")); |
213 |
| - digester.addRule("*/dict/false", new SetNextAndPopRule("addChild")); |
214 |
| - digester.addRule("*/dict/date", new SetNextAndPopRule("addChild")); |
215 |
| - digester.addRule("*/dict/dict", new SetNextAndPopRule("addChild")); |
216 |
| - |
217 |
| - digester.addCallMethod("*/dict/string", "addValue", 0); |
218 |
| - digester.addCallMethod("*/dict/data", "addDataValue", 0); |
219 |
| - digester.addCallMethod("*/dict/integer", "addIntegerValue", 0); |
220 |
| - digester.addCallMethod("*/dict/real", "addRealValue", 0); |
221 |
| - digester.addCallMethod("*/dict/true", "addTrueValue"); |
222 |
| - digester.addCallMethod("*/dict/false", "addFalseValue"); |
223 |
| - digester.addCallMethod("*/dict/date", "addDateValue", 0); |
224 |
| - |
225 |
| - // rules for arrays |
226 |
| - digester.addRule("*/dict/array", new SetNextAndPopRule("addChild")); |
227 |
| - digester.addRule("*/dict/array", new ObjectCreateRule(ArrayNode.class)); |
228 |
| - digester.addSetNext("*/dict/array", "addList"); |
229 |
| - |
230 |
| - digester.addRule("*/array/array", new ObjectCreateRule(ArrayNode.class)); |
231 |
| - digester.addSetNext("*/array/array", "addList"); |
232 |
| - |
233 |
| - digester.addCallMethod("*/array/string", "addValue", 0); |
234 |
| - digester.addCallMethod("*/array/data", "addDataValue", 0); |
235 |
| - digester.addCallMethod("*/array/integer", "addIntegerValue", 0); |
236 |
| - digester.addCallMethod("*/array/real", "addRealValue", 0); |
237 |
| - digester.addCallMethod("*/array/true", "addTrueValue"); |
238 |
| - digester.addCallMethod("*/array/false", "addFalseValue"); |
239 |
| - digester.addCallMethod("*/array/date", "addDateValue", 0); |
240 |
| - |
241 |
| - // rule for a dictionary in an array |
242 |
| - digester.addFactoryCreate("*/array/dict", new AbstractObjectCreationFactory() |
243 |
| - { |
244 |
| - public Object createObject(Attributes attributes) throws Exception |
245 |
| - { |
246 |
| - // create the configuration |
247 |
| - XMLPropertyListConfiguration config = new XMLPropertyListConfiguration(); |
248 |
| - |
249 |
| - // add it to the ArrayNode |
250 |
| - ArrayNode node = (ArrayNode) getDigester().peek(); |
251 |
| - node.addValue(config); |
252 |
| - |
253 |
| - // push the root on the stack |
254 |
| - return config.getRoot(); |
255 |
| - } |
256 |
| - }); |
| 192 | + }; |
257 | 193 |
|
258 | 194 | // parse the file
|
259 |
| - digester.push(getRoot()); |
| 195 | + XMLPropertyListHandler handler = new XMLPropertyListHandler(getRoot()); |
260 | 196 | try
|
261 | 197 | {
|
262 |
| - digester.parse(in); |
| 198 | + SAXParserFactory factory = SAXParserFactory.newInstance(); |
| 199 | + factory.setValidating(true); |
| 200 | + |
| 201 | + SAXParser parser = factory.newSAXParser(); |
| 202 | + parser.getXMLReader().setEntityResolver(resolver); |
| 203 | + parser.getXMLReader().setContentHandler(handler); |
| 204 | + parser.getXMLReader().parse(new InputSource(in)); |
263 | 205 | }
|
264 | 206 | catch (Exception e)
|
265 | 207 | {
|
266 | 208 | throw new ConfigurationException("Unable to parse the configuration file", e);
|
267 | 209 | }
|
268 | 210 | }
|
269 | 211 |
|
270 |
| - /** |
271 |
| - * Digester rule that sets the object on the stack to the n-1 object |
272 |
| - * and remove both of them from the stack. This rule is used to remove |
273 |
| - * the configuration node from the stack once its value has been parsed. |
274 |
| - */ |
275 |
| - private class SetNextAndPopRule extends SetNextRule |
276 |
| - { |
277 |
| - public SetNextAndPopRule(String methodName) |
278 |
| - { |
279 |
| - super(methodName); |
280 |
| - } |
281 |
| - |
282 |
| - public void end(String namespace, String name) throws Exception |
283 |
| - { |
284 |
| - super.end(namespace, name); |
285 |
| - digester.pop(); |
286 |
| - } |
287 |
| - } |
288 |
| - |
289 | 212 | public void save(Writer out) throws ConfigurationException
|
290 | 213 | {
|
291 | 214 | PrintWriter writer = new PrintWriter(out);
|
@@ -438,6 +361,148 @@ else if (value instanceof byte[])
|
438 | 361 | }
|
439 | 362 | }
|
440 | 363 |
|
| 364 | + /** |
| 365 | + * SAX Handler to build the configuration nodes while the document is being parsed. |
| 366 | + */ |
| 367 | + private class XMLPropertyListHandler extends DefaultHandler |
| 368 | + { |
| 369 | + private StringBuffer buffer = new StringBuffer(); |
| 370 | + |
| 371 | + private List stack = new ArrayList(); |
| 372 | + |
| 373 | + public XMLPropertyListHandler(Node root) |
| 374 | + { |
| 375 | + push(root); |
| 376 | + } |
| 377 | + |
| 378 | + /** |
| 379 | + * Return the node on the top of the stack. |
| 380 | + */ |
| 381 | + private Node peek() |
| 382 | + { |
| 383 | + if (!stack.isEmpty()) |
| 384 | + { |
| 385 | + return (Node) stack.get(stack.size() - 1); |
| 386 | + } |
| 387 | + else |
| 388 | + { |
| 389 | + return null; |
| 390 | + } |
| 391 | + } |
| 392 | + |
| 393 | + /** |
| 394 | + * Remove and return the node on the top of the stack. |
| 395 | + */ |
| 396 | + private Node pop() |
| 397 | + { |
| 398 | + if (!stack.isEmpty()) |
| 399 | + { |
| 400 | + return (Node) stack.remove(stack.size() - 1); |
| 401 | + } |
| 402 | + else |
| 403 | + { |
| 404 | + return null; |
| 405 | + } |
| 406 | + } |
| 407 | + |
| 408 | + /** |
| 409 | + * Put a node on the top of the stack. |
| 410 | + */ |
| 411 | + private void push(Node node) |
| 412 | + { |
| 413 | + stack.add(node); |
| 414 | + } |
| 415 | + |
| 416 | + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException |
| 417 | + { |
| 418 | + if ("array".equals(qName)) |
| 419 | + { |
| 420 | + push(new ArrayNode()); |
| 421 | + } |
| 422 | + else if ("dict".equals(qName)) |
| 423 | + { |
| 424 | + if (peek() instanceof ArrayNode) |
| 425 | + { |
| 426 | + // create the configuration |
| 427 | + XMLPropertyListConfiguration config = new XMLPropertyListConfiguration(); |
| 428 | + |
| 429 | + // add it to the ArrayNode |
| 430 | + ArrayNode node = (ArrayNode) peek(); |
| 431 | + node.addValue(config); |
| 432 | + |
| 433 | + // push the root on the stack |
| 434 | + push(config.getRoot()); |
| 435 | + } |
| 436 | + } |
| 437 | + } |
| 438 | + |
| 439 | + public void endElement(String uri, String localName, String qName) throws SAXException |
| 440 | + { |
| 441 | + if ("key".equals(qName)) |
| 442 | + { |
| 443 | + // create a new node, link it to its parent and push it on the stack |
| 444 | + PListNode node = new PListNode(); |
| 445 | + node.setName(buffer.toString()); |
| 446 | + peek().addChild(node); |
| 447 | + push(node); |
| 448 | + } |
| 449 | + else if ("dict".equals(qName)) |
| 450 | + { |
| 451 | + // remove the root of the XMLPropertyListConfiguration previously pushed on the stack |
| 452 | + pop(); |
| 453 | + } |
| 454 | + else |
| 455 | + { |
| 456 | + if ("string".equals(qName)) |
| 457 | + { |
| 458 | + ((PListNode) peek()).addValue(buffer.toString()); |
| 459 | + } |
| 460 | + else if ("integer".equals(qName)) |
| 461 | + { |
| 462 | + ((PListNode) peek()).addIntegerValue(buffer.toString()); |
| 463 | + } |
| 464 | + else if ("real".equals(qName)) |
| 465 | + { |
| 466 | + ((PListNode) peek()).addRealValue(buffer.toString()); |
| 467 | + } |
| 468 | + else if ("true".equals(qName)) |
| 469 | + { |
| 470 | + ((PListNode) peek()).addTrueValue(); |
| 471 | + } |
| 472 | + else if ("false".equals(qName)) |
| 473 | + { |
| 474 | + ((PListNode) peek()).addFalseValue(); |
| 475 | + } |
| 476 | + else if ("data".equals(qName)) |
| 477 | + { |
| 478 | + ((PListNode) peek()).addDataValue(buffer.toString()); |
| 479 | + } |
| 480 | + else if ("date".equals(qName)) |
| 481 | + { |
| 482 | + ((PListNode) peek()).addDateValue(buffer.toString()); |
| 483 | + } |
| 484 | + else if ("array".equals(qName)) |
| 485 | + { |
| 486 | + ArrayNode array = (ArrayNode) pop(); |
| 487 | + ((PListNode) peek()).addList(array); |
| 488 | + } |
| 489 | + |
| 490 | + // remove the plist node on the stack once the value has been parsed, |
| 491 | + // array nodes remains on the stack for the next values in the list |
| 492 | + if (!(peek() instanceof ArrayNode)) |
| 493 | + { |
| 494 | + pop(); |
| 495 | + } |
| 496 | + } |
| 497 | + |
| 498 | + buffer.setLength(0); |
| 499 | + } |
| 500 | + |
| 501 | + public void characters(char ch[], int start, int length) throws SAXException |
| 502 | + { |
| 503 | + buffer.append(ch, start, length); |
| 504 | + } |
| 505 | + } |
441 | 506 |
|
442 | 507 | /**
|
443 | 508 | * Node extension with addXXX methods to parse the typed data passed by Digester.
|
|
0 commit comments