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