47 |
import java.io.PrintStream; |
import java.io.PrintStream; |
48 |
import java.io.PrintWriter; |
import java.io.PrintWriter; |
49 |
|
|
50 |
|
import javax.xml.parsers.ParserConfigurationException; |
51 |
|
import javax.xml.parsers.SAXParser; |
52 |
|
import javax.xml.parsers.SAXParserFactory; |
53 |
|
|
54 |
|
import org.xml.sax.Attributes; |
55 |
|
import org.xml.sax.InputSource; |
56 |
|
import org.xml.sax.SAXException; |
57 |
|
import org.xml.sax.XMLReader; |
58 |
|
import org.xml.sax.ext.DefaultHandler2; |
59 |
|
|
60 |
|
import org.w3c.dom.Document; |
61 |
|
import org.w3c.dom.DocumentType; |
62 |
|
import org.w3c.dom.DOMImplementation; |
63 |
|
import org.w3c.dom.Element; |
64 |
|
import org.w3c.dom.bootstrap.DOMImplementationRegistry; |
65 |
|
import org.w3c.dom.ls.DOMImplementationLS; |
66 |
|
import org.w3c.dom.ls.LSOutput; |
67 |
|
import org.w3c.dom.ls.LSSerializer; |
68 |
|
|
69 |
/** |
/** |
70 |
* A set of persistent properties, which can be saved or loaded from a stream. |
* A set of persistent properties, which can be saved or loaded from a stream. |
71 |
* A property list may also contain defaults, searched if the main list |
* A property list may also contain defaults, searched if the main list |
598 |
head = key; |
head = key; |
599 |
} |
} |
600 |
} |
} |
601 |
|
|
602 |
|
/** |
603 |
|
* <p> |
604 |
|
* Encodes the properties as an XML file using the UTF-8 encoding. |
605 |
|
* The format of the XML file matches the DTD |
606 |
|
* <a href="http://java.sun.com/dtd/properties.dtd"> |
607 |
|
* http://java.sun.com/dtd/properties.dtd</a>. |
608 |
|
* </p> |
609 |
|
* <p> |
610 |
|
* Invoking this method provides the same behaviour as invoking |
611 |
|
* <code>storeToXML(os, comment, "UTF-8")</code>. |
612 |
|
* </p> |
613 |
|
* |
614 |
|
* @param os the stream to output to. |
615 |
|
* @param comment a comment to include at the top of the XML file, or |
616 |
|
* <code>null</code> if one is not required. |
617 |
|
* @throws IOException if the serialization fails. |
618 |
|
* @throws NullPointerException if <code>os</code> is null. |
619 |
|
* @since 1.5 |
620 |
|
*/ |
621 |
|
public void storeToXML(OutputStream os, String comment) |
622 |
|
throws IOException |
623 |
|
{ |
624 |
|
storeToXML(os, comment, "UTF-8"); |
625 |
|
} |
626 |
|
|
627 |
|
/** |
628 |
|
* <p> |
629 |
|
* Encodes the properties as an XML file using the supplied encoding. |
630 |
|
* The format of the XML file matches the DTD |
631 |
|
* <a href="http://java.sun.com/dtd/properties.dtd"> |
632 |
|
* http://java.sun.com/dtd/properties.dtd</a>. |
633 |
|
* </p> |
634 |
|
* |
635 |
|
* @param os the stream to output to. |
636 |
|
* @param comment a comment to include at the top of the XML file, or |
637 |
|
* <code>null</code> if one is not required. |
638 |
|
* @param encoding the encoding to use for the XML output. |
639 |
|
* @throws IOException if the serialization fails. |
640 |
|
* @throws NullPointerException if <code>os</code> or <code>encoding</code> |
641 |
|
* is null. |
642 |
|
* @since 1.5 |
643 |
|
*/ |
644 |
|
public void storeToXML(OutputStream os, String comment, String encoding) |
645 |
|
throws IOException |
646 |
|
{ |
647 |
|
if (os == null) |
648 |
|
throw new NullPointerException("Null output stream supplied."); |
649 |
|
if (encoding == null) |
650 |
|
throw new NullPointerException("Null encoding supplied."); |
651 |
|
try |
652 |
|
{ |
653 |
|
DOMImplementationRegistry registry = |
654 |
|
DOMImplementationRegistry.newInstance(); |
655 |
|
DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0"); |
656 |
|
DocumentType doctype = |
657 |
|
domImpl.createDocumentType("properties", null, |
658 |
|
"http://java.sun.com/dtd/properties.dtd"); |
659 |
|
Document doc = domImpl.createDocument(null, "properties", doctype); |
660 |
|
Element root = doc.getDocumentElement(); |
661 |
|
if (comment != null) |
662 |
|
{ |
663 |
|
Element commentElement = doc.createElement("comment"); |
664 |
|
commentElement.appendChild(doc.createTextNode(comment)); |
665 |
|
root.appendChild(commentElement); |
666 |
|
} |
667 |
|
Iterator iterator = entrySet().iterator(); |
668 |
|
while (iterator.hasNext()) |
669 |
|
{ |
670 |
|
Map.Entry entry = (Map.Entry) iterator.next(); |
671 |
|
Element entryElement = doc.createElement("entry"); |
672 |
|
entryElement.setAttribute("key", (String) entry.getKey()); |
673 |
|
entryElement.appendChild(doc.createTextNode((String) |
674 |
|
entry.getValue())); |
675 |
|
root.appendChild(entryElement); |
676 |
|
} |
677 |
|
DOMImplementationLS loadAndSave = (DOMImplementationLS) domImpl; |
678 |
|
LSSerializer serializer = loadAndSave.createLSSerializer(); |
679 |
|
LSOutput output = loadAndSave.createLSOutput(); |
680 |
|
output.setByteStream(os); |
681 |
|
output.setEncoding(encoding); |
682 |
|
serializer.write(doc, output); |
683 |
|
} |
684 |
|
catch (ClassNotFoundException e) |
685 |
|
{ |
686 |
|
throw (IOException) |
687 |
|
new IOException("The XML classes could not be found.").initCause(e); |
688 |
|
} |
689 |
|
catch (InstantiationException e) |
690 |
|
{ |
691 |
|
throw (IOException) |
692 |
|
new IOException("The XML classes could not be instantiated.") |
693 |
|
.initCause(e); |
694 |
|
} |
695 |
|
catch (IllegalAccessException e) |
696 |
|
{ |
697 |
|
throw (IOException) |
698 |
|
new IOException("The XML classes could not be accessed.") |
699 |
|
.initCause(e); |
700 |
|
} |
701 |
|
} |
702 |
|
|
703 |
|
/** |
704 |
|
* <p> |
705 |
|
* Decodes the contents of the supplied <code>InputStream</code> as |
706 |
|
* an XML file, which represents a set of properties. The format of |
707 |
|
* the XML file must match the DTD |
708 |
|
* <a href="http://java.sun.com/dtd/properties.dtd"> |
709 |
|
* http://java.sun.com/dtd/properties.dtd</a>. |
710 |
|
* </p> |
711 |
|
* |
712 |
|
* @param in the input stream from which to receive the XML data. |
713 |
|
* @throws IOException if an I/O error occurs in reading the input data. |
714 |
|
* @throws InvalidPropertiesFormatException if the input data does not |
715 |
|
* constitute an XML properties |
716 |
|
* file. |
717 |
|
* @throws NullPointerException if <code>in</code> is null. |
718 |
|
* @since 1.5 |
719 |
|
*/ |
720 |
|
public void loadFromXML(InputStream in) |
721 |
|
throws IOException, InvalidPropertiesFormatException |
722 |
|
{ |
723 |
|
if (in == null) |
724 |
|
throw new NullPointerException("Null input stream supplied."); |
725 |
|
try |
726 |
|
{ |
727 |
|
SAXParserFactory factory = SAXParserFactory.newInstance(); |
728 |
|
factory.setValidating(false); /* Don't use the URI */ |
729 |
|
XMLReader parser = factory.newSAXParser().getXMLReader(); |
730 |
|
PropertiesHandler handler = new PropertiesHandler(); |
731 |
|
parser.setContentHandler(handler); |
732 |
|
parser.setProperty("http://xml.org/sax/properties/lexical-handler", |
733 |
|
handler); |
734 |
|
parser.parse(new InputSource(in)); |
735 |
|
} |
736 |
|
catch (SAXException e) |
737 |
|
{ |
738 |
|
throw (InvalidPropertiesFormatException) |
739 |
|
new InvalidPropertiesFormatException("Error in parsing XML."). |
740 |
|
initCause(e); |
741 |
|
} |
742 |
|
catch (ParserConfigurationException e) |
743 |
|
{ |
744 |
|
throw (IOException) |
745 |
|
new IOException("An XML parser could not be found."). |
746 |
|
initCause(e); |
747 |
|
} |
748 |
|
} |
749 |
|
|
750 |
|
/** |
751 |
|
* This class deals with the parsing of XML using |
752 |
|
* <a href="http://java.sun.com/dtd/properties.dtd"> |
753 |
|
* http://java.sun.com/dtd/properties.dtd</a>. |
754 |
|
* |
755 |
|
* @author Andrew John Hughes (gnu_andrew@member.fsf.org) |
756 |
|
* @since 1.5 |
757 |
|
*/ |
758 |
|
private class PropertiesHandler |
759 |
|
extends DefaultHandler2 |
760 |
|
{ |
761 |
|
|
762 |
|
/** |
763 |
|
* The current key. |
764 |
|
*/ |
765 |
|
private String key; |
766 |
|
|
767 |
|
/** |
768 |
|
* The current value. |
769 |
|
*/ |
770 |
|
private String value; |
771 |
|
|
772 |
|
/** |
773 |
|
* A flag to check whether a valid DTD declaration has been seen. |
774 |
|
*/ |
775 |
|
private boolean dtdDeclSeen; |
776 |
|
|
777 |
|
/** |
778 |
|
* Constructs a new Properties handler. |
779 |
|
*/ |
780 |
|
public PropertiesHandler() |
781 |
|
{ |
782 |
|
key = null; |
783 |
|
value = null; |
784 |
|
dtdDeclSeen = false; |
785 |
|
} |
786 |
|
|
787 |
|
/** |
788 |
|
* <p> |
789 |
|
* Captures the start of the DTD declarations, if they exist. |
790 |
|
* A valid properties file must declare the following doctype: |
791 |
|
* </p> |
792 |
|
* <p> |
793 |
|
* <code>!DOCTYPE properties SYSTEM |
794 |
|
* "http://java.sun.com/dtd/properties.dtd"</code> |
795 |
|
* </p> |
796 |
|
* |
797 |
|
* @param name the name of the document type. |
798 |
|
* @param publicId the public identifier that was declared, or |
799 |
|
* null if there wasn't one. |
800 |
|
* @param systemId the system identifier that was declared, or |
801 |
|
* null if there wasn't one. |
802 |
|
* @throws SAXException if some error occurs in parsing. |
803 |
|
*/ |
804 |
|
public void startDTD(String name, String publicId, String systemId) |
805 |
|
throws SAXException |
806 |
|
{ |
807 |
|
if (name.equals("properties") && |
808 |
|
publicId == null && |
809 |
|
systemId.equals("http://java.sun.com/dtd/properties.dtd")) |
810 |
|
{ |
811 |
|
dtdDeclSeen = true; |
812 |
|
} |
813 |
|
else |
814 |
|
throw new SAXException("Invalid DTD declaration: " + name); |
815 |
|
} |
816 |
|
|
817 |
|
/** |
818 |
|
* Captures the start of an XML element. |
819 |
|
* |
820 |
|
* @param uri the namespace URI. |
821 |
|
* @param localName the local name of the element inside the namespace. |
822 |
|
* @param qName the local name qualified with the namespace URI. |
823 |
|
* @param attributes the attributes of this element. |
824 |
|
* @throws SAXException if some error occurs in parsing. |
825 |
|
*/ |
826 |
|
public void startElement(String uri, String localName, |
827 |
|
String qName, Attributes attributes) |
828 |
|
throws SAXException |
829 |
|
{ |
830 |
|
if (qName.equals("entry")) |
831 |
|
{ |
832 |
|
int index = attributes.getIndex("key"); |
833 |
|
if (index != -1) |
834 |
|
key = attributes.getValue(index); |
835 |
|
} |
836 |
|
else if (qName.equals("comment") || qName.equals("properties")) |
837 |
|
{ |
838 |
|
/* Ignore it */ |
839 |
|
} |
840 |
|
else |
841 |
|
throw new SAXException("Invalid tag: " + qName); |
842 |
|
} |
843 |
|
|
844 |
|
/** |
845 |
|
* Captures characters within an XML element. |
846 |
|
* |
847 |
|
* @param ch the array of characters. |
848 |
|
* @param start the start index of the characters to use. |
849 |
|
* @param length the number of characters to use from the start index on. |
850 |
|
* @throws SAXException if some error occurs in parsing. |
851 |
|
*/ |
852 |
|
public void characters(char[] ch, int start, int length) |
853 |
|
throws SAXException |
854 |
|
{ |
855 |
|
if (key != null) |
856 |
|
value = new String(ch,start,length); |
857 |
|
} |
858 |
|
|
859 |
|
/** |
860 |
|
* Captures the end of an XML element. |
861 |
|
* |
862 |
|
* @param uri the namespace URI. |
863 |
|
* @param localName the local name of the element inside the namespace. |
864 |
|
* @param qName the local name qualified with the namespace URI. |
865 |
|
* @throws SAXException if some error occurs in parsing. |
866 |
|
*/ |
867 |
|
public void endElement(String uri, String localName, |
868 |
|
String qName) |
869 |
|
throws SAXException |
870 |
|
{ |
871 |
|
if (qName.equals("entry")) |
872 |
|
{ |
873 |
|
if (value == null) |
874 |
|
value = ""; |
875 |
|
setProperty(key, value); |
876 |
|
key = null; |
877 |
|
value = null; |
878 |
|
} |
879 |
|
} |
880 |
|
|
881 |
|
/** |
882 |
|
* Captures the end of the XML document. If a DTD declaration has |
883 |
|
* not been seen, the document is erroneous and an exception is thrown. |
884 |
|
* |
885 |
|
* @throws SAXException if the correct DTD declaration didn't appear. |
886 |
|
*/ |
887 |
|
public void endDocument() |
888 |
|
throws SAXException |
889 |
|
{ |
890 |
|
if (!dtdDeclSeen) |
891 |
|
throw new SAXException("No appropriate DTD declaration was seen."); |
892 |
|
} |
893 |
|
|
894 |
|
} // class PropertiesHandler |
895 |
|
|
896 |
} // class Properties |
} // class Properties |