Path: blob/master/src/java.base/share/classes/jdk/internal/util/xml/PropertiesDefaultHandler.java
41161 views
/*1* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.util.xml;2627import java.io.*;28import java.nio.charset.Charset;29import java.util.InvalidPropertiesFormatException;30import java.util.Map.Entry;31import java.util.Properties;32import jdk.internal.org.xml.sax.Attributes;33import jdk.internal.org.xml.sax.InputSource;34import jdk.internal.org.xml.sax.SAXException;35import jdk.internal.org.xml.sax.SAXParseException;36import jdk.internal.org.xml.sax.helpers.DefaultHandler;37import jdk.internal.util.xml.impl.SAXParserImpl;38import jdk.internal.util.xml.impl.XMLStreamWriterImpl;3940/**41* A class used to aid in Properties load and save in XML. This class is42* re-implemented using a subset of SAX43*44* @author Joe Wang45* @since 1.846*/47public class PropertiesDefaultHandler extends DefaultHandler {4849// Elements specified in the properties.dtd50private static final String ELEMENT_ROOT = "properties";51private static final String ELEMENT_COMMENT = "comment";52private static final String ELEMENT_ENTRY = "entry";53private static final String ATTR_KEY = "key";54// The required DTD URI for exported properties55private static final String PROPS_DTD_DECL =56"<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">";57private static final String PROPS_DTD_URI =58"http://java.sun.com/dtd/properties.dtd";59private static final String PROPS_DTD =60"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"61+ "<!-- DTD for properties -->"62+ "<!ELEMENT properties ( comment?, entry* ) >"63+ "<!ATTLIST properties"64+ " version CDATA #FIXED \"1.0\">"65+ "<!ELEMENT comment (#PCDATA) >"66+ "<!ELEMENT entry (#PCDATA) >"67+ "<!ATTLIST entry "68+ " key CDATA #REQUIRED>";69/**70* Version number for the format of exported properties files.71*/72private static final String EXTERNAL_XML_VERSION = "1.0";73private Properties properties;7475public void load(Properties props, InputStream in)76throws IOException, InvalidPropertiesFormatException, UnsupportedEncodingException77{78this.properties = props;7980try {81SAXParser parser = new SAXParserImpl();82parser.parse(in, this);83} catch (SAXException saxe) {84throw new InvalidPropertiesFormatException(saxe);85}8687/**88* String xmlVersion = propertiesElement.getAttribute("version"); if89* (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) throw new90* InvalidPropertiesFormatException( "Exported Properties file format91* version " + xmlVersion + " is not supported. This java installation92* can read" + " versions " + EXTERNAL_XML_VERSION + " or older. You" +93* " may need to install a newer version of JDK.");94*/95}9697public void store(Properties props, OutputStream os, String comment, Charset charset)98throws IOException99{100try {101XMLStreamWriter writer = new XMLStreamWriterImpl(os, charset);102writer.writeStartDocument();103writer.writeDTD(PROPS_DTD_DECL);104writer.writeStartElement(ELEMENT_ROOT);105if (comment != null && !comment.isEmpty()) {106writer.writeStartElement(ELEMENT_COMMENT);107writer.writeCharacters(comment);108writer.writeEndElement();109}110111synchronized(props) {112for (Entry<Object, Object> e : props.entrySet()) {113final Object k = e.getKey();114final Object v = e.getValue();115if (k instanceof String && v instanceof String) {116writer.writeStartElement(ELEMENT_ENTRY);117writer.writeAttribute(ATTR_KEY, (String)k);118writer.writeCharacters((String)v);119writer.writeEndElement();120} else {121throw new ClassCastException(122"Keys and values in Properties must be Strings");123}124}125}126127writer.writeEndElement();128writer.writeEndDocument();129writer.flush();130} catch (XMLStreamException e) {131if (e.getCause() instanceof UnsupportedEncodingException) {132throw (UnsupportedEncodingException) e.getCause();133}134throw new IOException(e);135}136137}138////////////////////////////////////////////////////////////////////139// Validate while parsing140////////////////////////////////////////////////////////////////////141static final String ALLOWED_ELEMENTS = "comment, entry";142static final String ALLOWED_COMMENT = "comment";143////////////////////////////////////////////////////////////////////144// Handler methods145////////////////////////////////////////////////////////////////////146StringBuilder buf = new StringBuilder();147boolean sawRoot = false; // whether a valid root element exists148boolean sawComment = false;149boolean validEntry = false;150String key;151String rootElm;152153@Override154public void startElement(String uri, String localName, String qName, Attributes attributes)155throws SAXException156{157if (sawRoot) {158if (!ALLOWED_ELEMENTS.contains(qName)) {159fatalError(new SAXParseException("Element type \"" + qName + "\" must be declared.", null));160}161} else {162// check whether the root has been declared in the DTD163if (rootElm == null) {164fatalError(new SAXParseException("An XML properties document must contain"165+ " the DOCTYPE declaration as defined by java.util.Properties.", null));166}167168// check whether the element name matches the declaration169if (!rootElm.equals(qName)) {170fatalError(new SAXParseException("Document root element \"" + qName171+ "\", must match DOCTYPE root \"" + rootElm + "\"", null));172}173174// this is a valid root element175sawRoot = true;176}177178if (qName.equals(ELEMENT_ENTRY)) {179validEntry = true;180key = attributes.getValue(ATTR_KEY);181if (key == null) {182fatalError(new SAXParseException("Attribute \"key\" is required and " +183"must be specified for element type \"entry\"", null));184}185} else if (qName.equals(ALLOWED_COMMENT)) {186if (sawComment) {187fatalError(new SAXParseException("Only one comment element may be allowed. "188+ "The content of element type \"properties\" must match \"(comment?,entry*)\"", null));189}190sawComment = true;191}192}193194@Override195public void characters(char[] ch, int start, int length) throws SAXException {196if (validEntry) {197buf.append(ch, start, length);198}199}200201@Override202public void endElement(String uri, String localName, String qName) throws SAXException {203if (!ALLOWED_ELEMENTS.contains(qName) && !ELEMENT_ROOT.equals(qName)) {204fatalError(new SAXParseException("Element: " + qName +205" is invalid, must match \"(comment?,entry*)\".", null));206}207208if (validEntry) {209properties.setProperty(key, buf.toString());210buf.delete(0, buf.length());211validEntry = false;212}213}214215@Override216public InputSource resolveEntity(String pubid, String sysid)217throws SAXException, IOException {218{219if (sysid.equals(PROPS_DTD_URI)) {220// The properties DTD is known to the handler, no need to parse it221return null;222}223throw new SAXException("Invalid system identifier: " + sysid);224}225}226227@Override228public void error(SAXParseException x) throws SAXException {229throw x;230}231232@Override233public void fatalError(SAXParseException x) throws SAXException {234throw x;235}236237@Override238public void warning(SAXParseException x) throws SAXException {239throw x;240}241242// SAX2 extension from DTDHandler243244@Override245public void startDTD (String name, String publicId, String systemId) throws SAXException246{247if (!ELEMENT_ROOT.equals(name) || !PROPS_DTD_URI.equals(systemId)) {248fatalError(new SAXParseException("An XML properties document must contain"249+ " the DOCTYPE declaration as defined by java.util.Properties.", null));250}251rootElm = name;252}253254@Override255public void startInternalSub () throws SAXException256{257fatalError(new SAXParseException("Internal DTD subset is not allowed. " +258"The Properties XML document must have the following DOCTYPE declaration: \n" +259PROPS_DTD_DECL, null));260}261}262263264