1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.util; 28 29 import java.io.*; 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 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 load(Properties props, InputStream in)70 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 = doc.getDocumentElement(); 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 getLoadingDoc(InputStream in)90 static Document getLoadingDoc(InputStream in) 91 throws SAXException, IOException 92 { 93 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 94 dbf.setIgnoringElementContentWhitespace(true); 95 // Android-changed: We don't currently have a validating document builder. 96 // Revert this if the situation changes. 97 // 98 // dbf.setValidating(true); 99 dbf.setCoalescing(true); 100 dbf.setIgnoringComments(true); 101 try { 102 DocumentBuilder db = dbf.newDocumentBuilder(); 103 db.setEntityResolver(new Resolver()); 104 db.setErrorHandler(new EH()); 105 InputSource is = new InputSource(in); 106 return db.parse(is); 107 } catch (ParserConfigurationException x) { 108 throw new Error(x); 109 } 110 } 111 importProperties(Properties props, Element propertiesElement)112 static void importProperties(Properties props, Element propertiesElement) { 113 NodeList entries = propertiesElement.getChildNodes(); 114 int numEntries = entries.getLength(); 115 int start = numEntries > 0 && 116 entries.item(0).getNodeName().equals("comment") ? 1 : 0; 117 for (int i=start; i<numEntries; i++) { 118 // Android-changed: Exclude CDATA nodes and the like. 119 if (!(entries.item(i) instanceof Element)) { 120 continue; 121 } 122 Element entry = (Element)entries.item(i); 123 if (entry.hasAttribute("key")) { 124 Node n = entry.getFirstChild(); 125 String val = (n == null) ? "" : n.getNodeValue(); 126 props.setProperty(entry.getAttribute("key"), val); 127 } 128 } 129 } 130 save(Properties props, OutputStream os, String comment, String encoding)131 static void save(Properties props, OutputStream os, String comment, 132 String encoding) 133 throws IOException 134 { 135 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 136 DocumentBuilder db = null; 137 try { 138 db = dbf.newDocumentBuilder(); 139 } catch (ParserConfigurationException pce) { 140 assert(false); 141 } 142 Document doc = db.newDocument(); 143 Element properties = (Element) 144 doc.appendChild(doc.createElement("properties")); 145 146 if (comment != null) { 147 Element comments = (Element)properties.appendChild( 148 doc.createElement("comment")); 149 comments.appendChild(doc.createTextNode(comment)); 150 } 151 152 synchronized (props) { 153 for (String key : props.stringPropertyNames()) { 154 Element entry = (Element)properties.appendChild( 155 doc.createElement("entry")); 156 entry.setAttribute("key", key); 157 entry.appendChild(doc.createTextNode(props.getProperty(key))); 158 } 159 } 160 emitDocument(doc, os, encoding); 161 } 162 emitDocument(Document doc, OutputStream os, String encoding)163 static void emitDocument(Document doc, OutputStream os, String encoding) 164 throws IOException 165 { 166 TransformerFactory tf = TransformerFactory.newInstance(); 167 Transformer t = null; 168 try { 169 t = tf.newTransformer(); 170 t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, PROPS_DTD_URI); 171 t.setOutputProperty(OutputKeys.INDENT, "yes"); 172 t.setOutputProperty(OutputKeys.METHOD, "xml"); 173 t.setOutputProperty(OutputKeys.ENCODING, encoding); 174 } catch (TransformerConfigurationException tce) { 175 assert(false); 176 } 177 DOMSource doms = new DOMSource(doc); 178 StreamResult sr = new StreamResult(os); 179 try { 180 t.transform(doms, sr); 181 } catch (TransformerException te) { 182 IOException ioe = new IOException(); 183 ioe.initCause(te); 184 throw ioe; 185 } 186 } 187 188 private static class Resolver implements EntityResolver { resolveEntity(String pid, String sid)189 public InputSource resolveEntity(String pid, String sid) 190 throws SAXException 191 { 192 if (sid.equals(PROPS_DTD_URI)) { 193 InputSource is; 194 is = new InputSource(new StringReader(PROPS_DTD)); 195 is.setSystemId(PROPS_DTD_URI); 196 return is; 197 } 198 throw new SAXException("Invalid system identifier: " + sid); 199 } 200 } 201 202 private static class EH implements ErrorHandler { error(SAXParseException x)203 public void error(SAXParseException x) throws SAXException { 204 throw x; 205 } fatalError(SAXParseException x)206 public void fatalError(SAXParseException x) throws SAXException { 207 throw x; 208 } warning(SAXParseException x)209 public void warning(SAXParseException x) throws SAXException { 210 throw x; 211 } 212 } 213 214 } 215