1   /*
2    * %W% %E%
3    *
4    * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6    */
7   
8   package java.util;
9   
10  import java.io.*;
11  import org.xml.sax.*;
12  import org.xml.sax.helpers.*;
13  import org.w3c.dom.*;
14  import javax.xml.parsers.*;
15  import javax.xml.transform.*;
16  import javax.xml.transform.dom.*;
17  import javax.xml.transform.stream.*;
18  
19  /**
20   * A class used to aid in Properties load and save in XML. Keeping this
21   * code outside of Properties helps reduce the number of classes loaded
22   * when Properties is loaded.
23   *
24   * @version 1.9, 01/23/03
25   * @author  Michael McCloskey
26   * @since   1.3
27   */
28  class XMLUtils {
29  
30      // XML loading and saving methods for Properties
31  
32      // The required DTD URI for exported properties
33      private static final String PROPS_DTD_URI =
34      "http://java.sun.com/dtd/properties.dtd";
35  
36      private static final String PROPS_DTD =
37      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
38      "<!-- DTD for properties -->"                +
39      "<!ELEMENT properties ( comment?, entry* ) >"+
40      "<!ATTLIST properties"                       +
41          " version CDATA #FIXED \"1.0\">"         +
42      "<!ELEMENT comment (#PCDATA) >"              +
43      "<!ELEMENT entry (#PCDATA) >"                +
44      "<!ATTLIST entry "                           +
45          " key CDATA #REQUIRED>";
46  
47      /**
48       * Version number for the format of exported properties files.
49       */
50      private static final String EXTERNAL_XML_VERSION = "1.0";
51  
52      static void load(Properties props, InputStream in)
53          throws IOException, InvalidPropertiesFormatException
54      {
55          Document doc = null;
56          try {
57              doc = getLoadingDoc(in);
58          } catch (SAXException saxe) {
59              throw new InvalidPropertiesFormatException(saxe);
60          }
61          Element propertiesElement = (Element)doc.getChildNodes().item(1);
62          String xmlVersion = propertiesElement.getAttribute("version");
63          if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
64              throw new InvalidPropertiesFormatException(
65                  "Exported Properties file format version " + xmlVersion +
66                  " is not supported. This java installation can read" +
67                  " versions " + EXTERNAL_XML_VERSION + " or older. You" +
68                  " may need to install a newer version of JDK.");
69          importProperties(props, propertiesElement);
70      }
71  
72      static Document getLoadingDoc(InputStream in)
73          throws SAXException, IOException
74      {
75      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
76      dbf.setIgnoringElementContentWhitespace(true);
77      dbf.setValidating(true);
78          dbf.setCoalescing(true);
79          dbf.setIgnoringComments(true);
80      try {
81          DocumentBuilder db = dbf.newDocumentBuilder();
82          db.setEntityResolver(new Resolver());
83          db.setErrorHandler(new EH());
84              InputSource is = new InputSource(in);
85          return db.parse(is);
86      } catch (ParserConfigurationException x) {
87          throw new Error(x);
88      }
89      }
90  
91      static void importProperties(Properties props, Element propertiesElement) {
92          NodeList entries = propertiesElement.getChildNodes();
93          int numEntries = entries.getLength();
94          int start = numEntries > 0 && 
95              entries.item(0).getNodeName().equals("comment") ? 1 : 0;
96          for (int i=start; i<numEntries; i++) {
97              Element entry = (Element)entries.item(i);
98              if (entry.hasAttribute("key")) {
99                  Node n = entry.getFirstChild();
100                 String val = (n == null) ? "" : n.getNodeValue();
101                 props.setProperty(entry.getAttribute("key"), val);
102             }
103         }
104     }
105 
106     static void save(Properties props, OutputStream os, String comment, 
107                      String encoding) 
108         throws IOException
109     {
110         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
111         DocumentBuilder db = null;
112         try {
113             db = dbf.newDocumentBuilder();
114         } catch (ParserConfigurationException pce) {
115             assert(false);
116         }
117         Document doc = db.newDocument();
118         Element properties =  (Element)
119             doc.appendChild(doc.createElement("properties"));
120 
121         if (comment != null) {
122             Element comments = (Element)properties.appendChild(
123                 doc.createElement("comment"));
124             comments.appendChild(doc.createTextNode(comment));
125         }
126 
127         Set keys = props.keySet();
128         Iterator i = keys.iterator();
129         while(i.hasNext()) {
130             String key = (String)i.next();
131             Element entry = (Element)properties.appendChild(
132                 doc.createElement("entry"));
133             entry.setAttribute("key", key);
134             entry.appendChild(doc.createTextNode(props.getProperty(key)));
135         }
136         emitDocument(doc, os, encoding);
137     }
138 
139     static void emitDocument(Document doc, OutputStream os, String encoding)
140         throws IOException
141     {
142         TransformerFactory tf = TransformerFactory.newInstance();
143         Transformer t = null;
144         try {
145             t = tf.newTransformer();
146             t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, PROPS_DTD_URI);
147             t.setOutputProperty(OutputKeys.INDENT, "yes");
148             t.setOutputProperty(OutputKeys.METHOD, "xml");
149             t.setOutputProperty(OutputKeys.ENCODING, encoding);
150         } catch (TransformerConfigurationException tce) {
151             assert(false);
152         }
153         DOMSource doms = new DOMSource(doc);
154         StreamResult sr = new StreamResult(os);
155         try {
156             t.transform(doms, sr);
157         } catch (TransformerException te) {
158             IOException ioe = new IOException();
159             ioe.initCause(te);
160             throw ioe;
161         }
162     }
163 
164     private static class Resolver implements EntityResolver {
165         public InputSource resolveEntity(String pid, String sid)
166             throws SAXException
167         {
168             if (sid.equals(PROPS_DTD_URI)) {
169                 InputSource is;
170                 is = new InputSource(new StringReader(PROPS_DTD));
171                 is.setSystemId(PROPS_DTD_URI);
172                 return is;
173             }
174             throw new SAXException("Invalid system identifier: " + sid);
175         }
176     }
177 
178     private static class EH implements ErrorHandler {
179         public void error(SAXParseException x) throws SAXException {
180             throw x;
181         }
182         public void fatalError(SAXParseException x) throws SAXException {
183             throw x;
184         }
185         public void warning(SAXParseException x) throws SAXException {
186             throw x;
187         }
188     }
189 
190 }
191