1 16 17 package org.apache.commons.configuration; 18 19 import java.io.File ; 20 import java.io.InputStream ; 21 import java.io.OutputStream ; 22 import java.io.Reader ; 23 import java.io.Writer ; 24 import java.net.URL ; 25 import java.util.ArrayList ; 26 import java.util.Collection ; 27 import java.util.Iterator ; 28 import java.util.List ; 29 30 import javax.xml.parsers.DocumentBuilder ; 31 import javax.xml.parsers.DocumentBuilderFactory ; 32 import javax.xml.parsers.ParserConfigurationException ; 33 import javax.xml.transform.OutputKeys ; 34 import javax.xml.transform.Result ; 35 import javax.xml.transform.Source ; 36 import javax.xml.transform.Transformer ; 37 import javax.xml.transform.TransformerException ; 38 import javax.xml.transform.TransformerFactory ; 39 import javax.xml.transform.dom.DOMSource ; 40 import javax.xml.transform.stream.StreamResult ; 41 42 import org.w3c.dom.Attr ; 43 import org.w3c.dom.CDATASection ; 44 import org.w3c.dom.DOMException ; 45 import org.w3c.dom.Document ; 46 import org.w3c.dom.Element ; 47 import org.w3c.dom.NamedNodeMap ; 48 import org.w3c.dom.NodeList ; 49 import org.w3c.dom.Text ; 50 import org.xml.sax.InputSource ; 51 import org.apache.commons.configuration.reloading.ReloadingStrategy; 52 53 68 public class XMLConfiguration extends HierarchicalConfiguration implements FileConfiguration 69 { 70 71 private static final String DEFAULT_ROOT_NAME = "configuration"; 72 73 74 private static char ATTR_DELIMITER = ','; 75 76 private FileConfigurationDelegate delegate = new FileConfigurationDelegate(); 77 78 79 private Document document; 80 81 82 private String rootElementName; 83 84 87 public XMLConfiguration() 88 { 89 super(); 90 } 91 92 99 public XMLConfiguration(String fileName) throws ConfigurationException 100 { 101 this(); 102 setFileName(fileName); 103 load(); 104 } 105 106 113 public XMLConfiguration(File file) throws ConfigurationException 114 { 115 this(); 116 setFile(file); 117 if (file.exists()) 118 { 119 load(); 120 } 121 } 122 123 130 public XMLConfiguration(URL url) throws ConfigurationException 131 { 132 this(); 133 setURL(url); 134 load(); 135 } 136 137 145 public String getRootElementName() 146 { 147 if (getDocument() == null) 148 { 149 return (rootElementName == null) ? DEFAULT_ROOT_NAME : rootElementName; 150 } 151 else 152 { 153 return getDocument().getDocumentElement().getNodeName(); 154 } 155 } 156 157 169 public void setRootElementName(String name) 170 { 171 if (getDocument() != null) 172 { 173 throw new UnsupportedOperationException ("The name of the root element " 174 + "cannot be changed when loaded from an XML document!"); 175 } 176 rootElementName = name; 177 } 178 179 186 public Document getDocument() 187 { 188 return document; 189 } 190 191 194 protected void addPropertyDirect(String key, Object obj) 195 { 196 super.addPropertyDirect(key, obj); 197 delegate.possiblySave(); 198 } 199 200 203 public void clearProperty(String key) 204 { 205 super.clearProperty(key); 206 delegate.possiblySave(); 207 } 208 209 212 public void clearTree(String key) 213 { 214 super.clearTree(key); 215 delegate.possiblySave(); 216 } 217 218 221 public void setProperty(String key, Object value) 222 { 223 super.setProperty(key, value); 224 delegate.possiblySave(); 225 } 226 227 233 public void initProperties(Document document, boolean elemRefs) 234 { 235 constructHierarchy(getRoot(), document.getDocumentElement(), elemRefs); 236 } 237 238 246 private void constructHierarchy(Node node, Element element, boolean elemRefs) 247 { 248 processAttributes(node, element); 249 StringBuffer buffer = new StringBuffer (); 250 NodeList list = element.getChildNodes(); 251 for (int i = 0; i < list.getLength(); i++) 252 { 253 org.w3c.dom.Node w3cNode = list.item(i); 254 if (w3cNode instanceof Element ) 255 { 256 Element child = (Element ) w3cNode; 257 Node childNode = new XMLNode(child.getTagName(), 258 (elemRefs) ? child : null); 259 constructHierarchy(childNode, child, elemRefs); 260 node.addChild(childNode); 261 } 262 else if (w3cNode instanceof Text ) 263 { 264 Text data = (Text ) w3cNode; 265 buffer.append(data.getData()); 266 } 267 } 268 String text = buffer.toString().trim(); 269 if (text.length() > 0) 270 { 271 node.setValue(text); 272 } 273 } 274 275 282 private void processAttributes(Node node, Element element) 283 { 284 NamedNodeMap attributes = element.getAttributes(); 285 for (int i = 0; i < attributes.getLength(); ++i) 286 { 287 org.w3c.dom.Node w3cNode = attributes.item(i); 288 if (w3cNode instanceof Attr ) 289 { 290 Attr attr = (Attr ) w3cNode; 291 for (Iterator it = PropertyConverter.split(attr.getValue(), ATTR_DELIMITER).iterator(); it.hasNext();) 292 { 293 Node child = new XMLNode(ConfigurationKey.constructAttributeKey(attr.getName()), element); 294 child.setValue(it.next()); 295 node.addChild(child); 296 } 297 } 298 } 299 } 300 301 307 protected Document createDocument() throws ConfigurationException 308 { 309 try 310 { 311 if (document == null) 312 { 313 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 314 Document newDocument = builder.newDocument(); 315 Element rootElem = newDocument.createElement(getRootElementName()); 316 newDocument.appendChild(rootElem); 317 document = newDocument; 318 } 319 320 XMLBuilderVisitor builder = new XMLBuilderVisitor(document); 321 builder.processDocument(getRoot()); 322 return document; 323 } 324 catch (DOMException domEx) 325 { 326 throw new ConfigurationException(domEx); 327 } 328 catch (ParserConfigurationException pex) 329 { 330 throw new ConfigurationException(pex); 331 } 332 } 333 334 341 protected Node createNode(String name) 342 { 343 return new XMLNode(name, null); 344 } 345 346 public void load() throws ConfigurationException 347 { 348 delegate.load(); 349 } 350 351 public void load(String fileName) throws ConfigurationException 352 { 353 delegate.load(fileName); 354 } 355 356 public void load(File file) throws ConfigurationException 357 { 358 delegate.load(file); 359 } 360 361 public void load(URL url) throws ConfigurationException 362 { 363 delegate.load(url); 364 } 365 366 public void load(InputStream in) throws ConfigurationException 367 { 368 delegate.load(in); 369 } 370 371 public void load(InputStream in, String encoding) throws ConfigurationException 372 { 373 delegate.load(in, encoding); 374 } 375 376 386 public void load(Reader in) throws ConfigurationException 387 { 388 try 389 { 390 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 391 Document newDocument = builder.parse(new InputSource (in)); 392 Document oldDocument = document; 393 document = null; 394 initProperties(newDocument, oldDocument == null); 395 document = (oldDocument == null) ? newDocument : oldDocument; 396 } 397 catch (Exception e) 398 { 399 throw new ConfigurationException(e.getMessage(), e); 400 } 401 } 402 403 public void save() throws ConfigurationException 404 { 405 delegate.save(); 406 } 407 408 public void save(String fileName) throws ConfigurationException 409 { 410 delegate.save(fileName); 411 } 412 413 public void save(File file) throws ConfigurationException 414 { 415 delegate.save(file); 416 } 417 418 public void save(URL url) throws ConfigurationException 419 { 420 delegate.save(url); 421 } 422 423 public void save(OutputStream out) throws ConfigurationException 424 { 425 delegate.save(out); 426 } 427 428 public void save(OutputStream out, String encoding) throws ConfigurationException 429 { 430 delegate.save(out, encoding); 431 } 432 433 439 public void save(Writer writer) throws ConfigurationException 440 { 441 try 442 { 443 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 444 Source source = new DOMSource (createDocument()); 445 Result result = new StreamResult (writer); 446 447 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 448 transformer.transform(source, result); 449 } 450 catch (TransformerException e) 451 { 452 throw new ConfigurationException(e.getMessage(), e); 453 } 454 } 455 456 public String getFileName() 457 { 458 return delegate.getFileName(); 459 } 460 461 public void setFileName(String fileName) 462 { 463 delegate.setFileName(fileName); 464 } 465 466 public String getBasePath() 467 { 468 return delegate.getBasePath(); 469 } 470 471 public void setBasePath(String basePath) 472 { 473 delegate.setBasePath(basePath); 474 } 475 476 public File getFile() 477 { 478 return delegate.getFile(); 479 } 480 481 public void setFile(File file) 482 { 483 delegate.setFile(file); 484 } 485 486 public URL getURL() 487 { 488 return delegate.getURL(); 489 } 490 491 public void setURL(URL url) 492 { 493 delegate.setURL(url); 494 } 495 496 public void setAutoSave(boolean autoSave) 497 { 498 delegate.setAutoSave(autoSave); 499 } 500 501 public boolean isAutoSave() 502 { 503 return delegate.isAutoSave(); 504 } 505 506 public ReloadingStrategy getReloadingStrategy() 507 { 508 return delegate.getReloadingStrategy(); 509 } 510 511 public void setReloadingStrategy(ReloadingStrategy strategy) 512 { 513 delegate.setReloadingStrategy(strategy); 514 } 515 516 public void reload() 517 { 518 delegate.reload(); 519 } 520 521 public String getEncoding() 522 { 523 return delegate.getEncoding(); 524 } 525 526 public void setEncoding(String encoding) 527 { 528 delegate.setEncoding(encoding); 529 } 530 531 535 class XMLNode extends Node 536 { 537 543 public XMLNode(Element elem) 544 { 545 super(); 546 setReference(elem); 547 } 548 549 556 public XMLNode(String name, Element elem) 557 { 558 super(name); 559 setReference(elem); 560 } 561 562 568 public void setValue(Object value) 569 { 570 super.setValue(value); 571 572 if (getReference() != null && document != null) 573 { 574 if (ConfigurationKey.isAttributeKey(getName())) 575 { 576 updateAttribute(); 577 } 578 else 579 { 580 updateElement(value); 581 } 582 } 583 } 584 585 588 protected void removeReference() 589 { 590 if (getReference() != null) 591 { 592 Element element = (Element ) getReference(); 593 if (ConfigurationKey.isAttributeKey(getName())) 594 { 595 updateAttribute(); 596 } 597 else 598 { 599 org.w3c.dom.Node parentElem = element.getParentNode(); 600 if (parentElem != null) 601 { 602 parentElem.removeChild(element); 603 } 604 } 605 } 606 } 607 608 613 private void updateElement(Object value) 614 { 615 Text txtNode = findTextNodeForUpdate(); 616 if (value == null) 617 { 618 if (txtNode != null) 620 { 621 ((Element ) getReference()).removeChild(txtNode); 622 } 623 } 624 else 625 { 626 if (txtNode == null) 627 { 628 txtNode = document.createTextNode(value.toString()); 629 if (((Element ) getReference()).getFirstChild() != null) 630 { 631 ((Element ) getReference()).insertBefore(txtNode, ((Element ) getReference()).getFirstChild()); 632 } 633 else 634 { 635 ((Element ) getReference()).appendChild(txtNode); 636 } 637 } 638 else 639 { 640 txtNode.setNodeValue(value.toString()); 641 } 642 } 643 } 644 645 649 private void updateAttribute() 650 { 651 XMLBuilderVisitor.updateAttribute(getParent(), getName()); 652 } 653 654 662 private Text findTextNodeForUpdate() 663 { 664 Text result = null; 665 Element elem = (Element ) getReference(); 666 NodeList children = elem.getChildNodes(); 668 Collection textNodes = new ArrayList (); 669 for (int i = 0; i < children.getLength(); i++) 670 { 671 org.w3c.dom.Node nd = children.item(i); 672 if (nd instanceof Text ) 673 { 674 if (result == null) 675 { 676 result = (Text ) nd; 677 } 678 else 679 { 680 textNodes.add(nd); 681 } 682 } 683 } 684 685 if (result instanceof CDATASection ) 687 { 688 textNodes.add(result); 689 result = null; 690 } 691 692 for (Iterator it = textNodes.iterator(); it.hasNext();) 694 { 695 elem.removeChild((org.w3c.dom.Node ) it.next()); 696 } 697 return result; 698 } 699 } 700 701 705 static class XMLBuilderVisitor extends BuilderVisitor 706 { 707 708 private Document document; 709 710 715 public XMLBuilderVisitor(Document doc) 716 { 717 document = doc; 718 } 719 720 725 public void processDocument(Node rootNode) 726 { 727 rootNode.visit(this, null); 728 } 729 730 733 protected Object insert(Node newNode, Node parent, Node sibling1, Node sibling2) 734 { 735 if (ConfigurationKey.isAttributeKey(newNode.getName())) 736 { 737 updateAttribute(parent, getElement(parent), newNode.getName()); 738 return null; 739 } 740 741 else 742 { 743 Element elem = document.createElement(newNode.getName()); 744 if (newNode.getValue() != null) 745 { 746 elem.appendChild(document.createTextNode(newNode.getValue().toString())); 747 } 748 if (sibling2 == null) 749 { 750 getElement(parent).appendChild(elem); 751 } 752 else if (sibling1 != null) 753 { 754 getElement(parent).insertBefore(elem, getElement(sibling1).getNextSibling()); 755 } 756 else 757 { 758 getElement(parent).insertBefore(elem, getElement(parent).getFirstChild()); 759 } 760 return elem; 761 } 762 } 763 764 772 private static void updateAttribute(Node node, Element elem, String name) 773 { 774 if (node != null && elem != null) 775 { 776 List attrs = node.getChildren(name); 777 StringBuffer buf = new StringBuffer (); 778 for (Iterator it = attrs.iterator(); it.hasNext();) 779 { 780 Node attr = (Node) it.next(); 781 if (attr.getValue() != null) 782 { 783 if (buf.length() > 0) 784 { 785 buf.append(ATTR_DELIMITER); 786 } 787 buf.append(attr.getValue()); 788 } 789 attr.setReference(elem); 790 } 791 792 if (buf.length() < 1) 793 { 794 elem.removeAttribute(ConfigurationKey.removeAttributeMarkers(name)); 795 } 796 else 797 { 798 elem.setAttribute(ConfigurationKey.removeAttributeMarkers(name), buf.toString()); 799 } 800 } 801 } 802 803 811 static void updateAttribute(Node node, String name) 812 { 813 if (node != null) 814 { 815 updateAttribute(node, (Element ) node.getReference(), name); 816 } 817 } 818 819 825 private Element getElement(Node node) 826 { 827 return (node.getName() != null) ? (Element ) node.getReference() : document.getDocumentElement(); 829 } 830 } 831 832 private class FileConfigurationDelegate extends AbstractFileConfiguration 833 { 834 public void load(Reader in) throws ConfigurationException 835 { 836 XMLConfiguration.this.load(in); 837 } 838 839 public void save(Writer out) throws ConfigurationException 840 { 841 XMLConfiguration.this.save(out); 842 } 843 } 844 } | Popular Tags |