Home | History | Annotate | Download | only in util
      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 
     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 
     90     static Document getLoadingDoc(InputStream in)
     91         throws SAXException, IOException
     92     {
     93         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
     94         dbf.setIgnoringElementContentWhitespace(true);
     95         // Android-chanaged: 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 
    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 
    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 
    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 {
    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 {
    203         public void error(SAXParseException x) throws SAXException {
    204             throw x;
    205         }
    206         public void fatalError(SAXParseException x) throws SAXException {
    207             throw x;
    208         }
    209         public void warning(SAXParseException x) throws SAXException {
    210             throw x;
    211         }
    212     }
    213 
    214 }
    215