1 /* 2 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.util.xml; 27 28 import java.io.*; 29 import java.util.*; 30 import org.xml.sax.*; 31 import org.xml.sax.helpers.*; 32 import org.w3c.dom.*; 33 import javax.xml.parsers.*; 34 import javax.xml.transform.*; 35 import javax.xml.transform.dom.*; 36 import javax.xml.transform.stream.*; 37 38 /** 39 * A class used to aid in Properties load and save in XML. Keeping this 40 * code outside of Properties helps reduce the number of classes loaded 41 * when Properties is loaded. 42 * 43 * @author Michael McCloskey 44 * @since 1.3 45 */ 46 public class XMLUtils { 47 48 // XML loading and saving methods for Properties 49 50 // The required DTD URI for exported properties 51 private static final String PROPS_DTD_URI = 52 "http://java.sun.com/dtd/properties.dtd"; 53 54 private static final String PROPS_DTD = 55 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + 56 "<!-- DTD for properties -->" + 57 "<!ELEMENT properties ( comment?, entry* ) >"+ 58 "<!ATTLIST properties" + 59 " version CDATA #FIXED \"1.0\">" + 60 "<!ELEMENT comment (#PCDATA) >" + 61 "<!ELEMENT entry (#PCDATA) >" + 62 "<!ATTLIST entry " + 63 " key CDATA #REQUIRED>"; 64 65 /** 66 * Version number for the format of exported properties files. 67 */ 68 private static final String EXTERNAL_XML_VERSION = "1.0"; 69 70 public static void load(Properties props, InputStream in) 71 throws IOException, InvalidPropertiesFormatException 72 { 73 Document doc = null; 74 try { 75 doc = getLoadingDoc(in); 76 } catch (SAXException saxe) { 77 throw new InvalidPropertiesFormatException(saxe); 78 } 79 Element propertiesElement = (Element)doc.getChildNodes().item(1); 80 String xmlVersion = propertiesElement.getAttribute("version"); 81 if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) 82 throw new InvalidPropertiesFormatException( 83 "Exported Properties file format version " + xmlVersion + 84 " is not supported. This java installation can read" + 85 " versions " + EXTERNAL_XML_VERSION + " or older. You" + 86 " may need to install a newer version of JDK."); 87 importProperties(props, propertiesElement); 88 } 89 90 static Document getLoadingDoc(InputStream in) 91 throws SAXException, IOException 92 { 93 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 94 dbf.setIgnoringElementContentWhitespace(true); 95 dbf.setValidating(true); 96 dbf.setCoalescing(true); 97 dbf.setIgnoringComments(true); 98 try { 99 DocumentBuilder db = dbf.newDocumentBuilder(); 100 db.setEntityResolver(new Resolver()); 101 db.setErrorHandler(new EH()); 102 InputSource is = new InputSource(in); 103 return db.parse(is); 104 } catch (ParserConfigurationException x) { 105 throw new Error(x); 106 } 107 } 108 109 static void importProperties(Properties props, Element propertiesElement) { 110 NodeList entries = propertiesElement.getChildNodes(); 111 int numEntries = entries.getLength(); 112 int start = numEntries > 0 && 113 entries.item(0).getNodeName().equals("comment") ? 1 : 0; 114 for (int i=start; i<numEntries; i++) { 115 Element entry = (Element)entries.item(i); 116 if (entry.hasAttribute("key")) { 117 Node n = entry.getFirstChild(); 118 String val = (n == null) ? "" : n.getNodeValue(); 119 props.setProperty(entry.getAttribute("key"), val); 120 } 121 } 122 } 123 124 public static void save(Properties props, OutputStream os, String comment, 125 String encoding) 126 throws IOException 127 { 128 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 129 DocumentBuilder db = null; 130 try { 131 db = dbf.newDocumentBuilder(); 132 } catch (ParserConfigurationException pce) { 133 assert(false); 134 } 135 Document doc = db.newDocument(); 136 Element properties = (Element) 137 doc.appendChild(doc.createElement("properties")); 138 139 if (comment != null) { 140 Element comments = (Element)properties.appendChild( 141 doc.createElement("comment")); 142 comments.appendChild(doc.createTextNode(comment)); 143 } 144 145 synchronized (props) { 146 for (String key : props.stringPropertyNames()) { 147 Element entry = (Element)properties.appendChild( 148 doc.createElement("entry")); 149 entry.setAttribute("key", key); 150 entry.appendChild(doc.createTextNode(props.getProperty(key))); 151 } 152 } 153 emitDocument(doc, os, encoding); 154 } 155 156 static void emitDocument(Document doc, OutputStream os, String encoding) 157 throws IOException 158 { 159 TransformerFactory tf = TransformerFactory.newInstance(); 160 Transformer t = null; 161 try { 162 t = tf.newTransformer(); 163 t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, PROPS_DTD_URI); 164 t.setOutputProperty(OutputKeys.INDENT, "yes"); 165 t.setOutputProperty(OutputKeys.METHOD, "xml"); 166 t.setOutputProperty(OutputKeys.ENCODING, encoding); 167 } catch (TransformerConfigurationException tce) { 168 assert(false); 169 } 170 DOMSource doms = new DOMSource(doc); 171 StreamResult sr = new StreamResult(os); 172 try { 173 t.transform(doms, sr); 174 } catch (TransformerException te) { 175 IOException ioe = new IOException(); 176 ioe.initCause(te); 177 throw ioe; 178 } 179 } 180 181 private static class Resolver implements EntityResolver { 182 public InputSource resolveEntity(String pid, String sid) 183 throws SAXException 184 { 185 if (sid.equals(PROPS_DTD_URI)) { 186 InputSource is; 187 is = new InputSource(new StringReader(PROPS_DTD)); 188 is.setSystemId(PROPS_DTD_URI); 189 return is; 190 } 191 throw new SAXException("Invalid system identifier: " + sid); 192 } 193 } 194 195 private static class EH implements ErrorHandler { 196 public void error(SAXParseException x) throws SAXException { 197 throw x; 198 } 199 public void fatalError(SAXParseException x) throws SAXException { 200 throw x; 201 } 202 public void warning(SAXParseException x) throws SAXException { 203 throw x; 204 } 205 } 206 207 }