Properties.java revision b4acb463582a510894aeb85f4fa8f35b339449c8
13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry/* 23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Licensed to the Apache Software Foundation (ASF) under one or more 33c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * contributor license agreements. See the NOTICE file distributed with 43c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * this work for additional information regarding copyright ownership. 53c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * The ASF licenses this file to You under the Apache License, Version 2.0 63c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * (the "License"); you may not use this file except in compliance with 73c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * the License. You may obtain a copy of the License at 83c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 93c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * http://www.apache.org/licenses/LICENSE-2.0 103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Unless required by applicable law or agreed to in writing, software 123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * distributed under the License is distributed on an "AS IS" BASIS, 133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * See the License for the specific language governing permissions and 153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * limitations under the License. 163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 183c827367444ee418f129b2c238299f49d3264554Jarkko Poyrypackage java.util; 193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 203c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.BufferedReader; 213c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.IOException; 223c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.InputStream; 233c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.InputStreamReader; 243c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.OutputStream; 253c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.OutputStreamWriter; 263c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.PrintStream; 273c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.PrintWriter; 283c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.Reader; 293c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.StringReader; 303c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.io.Writer; 313c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.nio.charset.Charset; 323c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.nio.charset.IllegalCharsetNameException; 333c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport java.nio.charset.UnsupportedCharsetException; 343c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport javax.xml.parsers.DocumentBuilder; 353c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport javax.xml.parsers.DocumentBuilderFactory; 363c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport javax.xml.parsers.ParserConfigurationException; 373c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.w3c.dom.Document; 383c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.w3c.dom.Element; 393c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.w3c.dom.Node; 403c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.w3c.dom.NodeList; 413c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.w3c.dom.Text; 423c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.xml.sax.EntityResolver; 433c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.xml.sax.ErrorHandler; 443c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.xml.sax.InputSource; 453c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.xml.sax.SAXException; 463c827367444ee418f129b2c238299f49d3264554Jarkko Poyryimport org.xml.sax.SAXParseException; 47bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry/** 493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * A {@code Properties} object is a {@code Hashtable} where the keys and values 503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * must be {@code String}s. Each property can have a default 513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * {@code Properties} list which specifies the default 523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * values to be used when a given key is not found in this {@code Properties} 533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * instance. 543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <a name="character_encoding"><h3>Character Encoding</h3></a> 563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <p>Note that in some cases {@code Properties} uses ISO-8859-1 instead of UTF-8. 573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * ISO-8859-1 is only capable of representing a tiny subset of Unicode. 583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Use either the {@code loadFromXML}/{@code storeToXML} methods (which use UTF-8 by 593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * default) or the {@code load}/{@code store} overloads that take 603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * an {@code OutputStreamWriter} (so you can supply a UTF-8 instance) instead. 613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @see Hashtable 633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @see java.lang.System#getProperties 643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 653c827367444ee418f129b2c238299f49d3264554Jarkko Poyrypublic class Properties extends Hashtable<Object, Object> { 663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private static final long serialVersionUID = 4112578634029874840L; 683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private transient DocumentBuilder builder = null; 703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private static final String PROP_DTD_NAME = "http://java.sun.com/dtd/properties.dtd"; 723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private static final String PROP_DTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + " <!ELEMENT properties (comment?, entry*) >" 753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + " <!ATTLIST properties version CDATA #FIXED \"1.0\" >" 763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + " <!ELEMENT comment (#PCDATA) >" 773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + " <!ELEMENT entry (#PCDATA) >" 783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + " <!ATTLIST entry key CDATA #REQUIRED >"; 793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * The default values for keys not found in this {@code Properties} 823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * instance. 833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry protected Properties defaults; 853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private static final int NONE = 0, SLASH = 1, UNICODE = 2, CONTINUE = 3, 873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry KEY_DONE = 4, IGNORE = 5; 883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Constructs a new {@code Properties} object. 913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public Properties() { 933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry super(); 943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Constructs a new {@code Properties} object using the specified default 983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * {@code Properties}. 993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 1003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @param properties 1013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * the default {@code Properties}. 1023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 1033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public Properties(Properties properties) { 1043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry defaults = properties; 1053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private void dumpString(StringBuilder buffer, String string, boolean key) { 1083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry int i = 0; 1093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (!key && i < string.length() && string.charAt(i) == ' ') { 1103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append("\\ "); 1113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry i++; 1123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for (; i < string.length(); i++) { 1153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry char ch = string.charAt(i); 1163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry switch (ch) { 1173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '\t': 1183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append("\\t"); 1193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 1203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '\n': 1213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append("\\n"); 1223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 1233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '\f': 1243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append("\\f"); 1253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 1263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '\r': 1273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append("\\r"); 1283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 1293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry default: 1303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if ("\\#!=:".indexOf(ch) >= 0 || (key && ch == ' ')) { 1313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append('\\'); 1323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (ch >= ' ' && ch <= '~') { 1343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append(ch); 1353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } else { 1363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String hex = Integer.toHexString(ch); 1373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append("\\u"); 1383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for (int j = 0; j < 4 - hex.length(); j++) { 1393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append("0"); 1403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry buffer.append(hex); 1423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 1483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Searches for the property with the specified name. If the property is not 1493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * found, the default {@code Properties} are checked. If the property is not 1503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * found in the default {@code Properties}, {@code null} is returned. 1513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 1523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @param name 1533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * the name of the property to find. 1543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @return the named property value, or {@code null} if it can't be found. 1553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 1563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public String getProperty(String name) { 1573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Object result = super.get(name); 1583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String property = result instanceof String ? (String) result : null; 1593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (property == null && defaults != null) { 1603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry property = defaults.getProperty(name); 1613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return property; 1633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 1663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Searches for the property with the specified name. If the property is not 1673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * found, it looks in the default {@code Properties}. If the property is not 1683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * found in the default {@code Properties}, it returns the specified 1693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * default. 1703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 1713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @param name 1723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * the name of the property to find. 1733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @param defaultValue 1743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * the default value. 1753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @return the named property value. 1763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 1773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public String getProperty(String name, String defaultValue) { 1783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Object result = super.get(name); 1793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String property = result instanceof String ? (String) result : null; 1803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (property == null && defaults != null) { 1813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry property = defaults.getProperty(name); 1823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (property == null) { 1843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return defaultValue; 1853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return property; 1873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 1903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Lists the mappings in this {@code Properties} to {@code out} in a human-readable form. 1913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Note that values are truncated to 37 characters, so this method is rarely useful. 1923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 1933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public void list(PrintStream out) { 1943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry listToAppendable(out); 1953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 1963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 1983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Lists the mappings in this {@code Properties} to {@code out} in a human-readable form. 1993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Note that values are truncated to 37 characters, so this method is rarely useful. 2003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 2013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public void list(PrintWriter out) { 2023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry listToAppendable(out); 2033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 2043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private void listToAppendable(Appendable out) { 2063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry try { 2073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (out == null) { 2083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw new NullPointerException("out == null"); 2093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 2103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringBuilder sb = new StringBuilder(80); 2113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Enumeration<?> keys = propertyNames(); 2123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry while (keys.hasMoreElements()) { 2133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String key = (String) keys.nextElement(); 2143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry sb.append(key); 2153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry sb.append('='); 2163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String property = (String) super.get(key); 2173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Properties def = defaults; 2183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry while (property == null) { 2193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry property = (String) def.get(key); 2203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry def = def.defaults; 2213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 222bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (property.length() > 40) { 223bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry sb.append(property.substring(0, 37)); 224bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry sb.append("..."); 225bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } else { 226bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry sb.append(property); 227bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 228bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry sb.append(System.lineSeparator()); 229bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry out.append(sb.toString()); 230bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry sb.setLength(0); 231bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 232bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } catch (IOException ex) { 233bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry // Appendable.append throws IOException, but PrintStream and PrintWriter don't. 234bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry throw new AssertionError(ex); 235bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 236bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 237bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 238bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 239bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Loads properties from the specified {@code InputStream}, assumed to be ISO-8859-1. 240bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * See "<a href="#character_encoding">Character Encoding</a>". 241bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 242bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param in the {@code InputStream} 243bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws IOException 244bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 245bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public synchronized void load(InputStream in) throws IOException { 246bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (in == null) { 247bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry throw new NullPointerException(); 248bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 249bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry load(new InputStreamReader(in, "ISO-8859-1")); 250bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 251bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 252bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 253bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Loads properties from the specified {@code Reader}. 254bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * The properties file is interpreted according to the following rules: 255bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * <ul> 256bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * <li>Empty lines are ignored.</li> 257bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * <li>Lines starting with either a "#" or a "!" are comment lines and are 258bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * ignored.</li> 259bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * <li>A backslash at the end of the line escapes the following newline 260bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * character ("\r", "\n", "\r\n"). If there's whitespace after the 261bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * backslash it will just escape that whitespace instead of concatenating 262bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * the lines. This does not apply to comment lines.</li> 263bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * <li>A property line consists of the key, the space between the key and 264bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * the value, and the value. The key goes up to the first whitespace, "=" or 265bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * ":" that is not escaped. The space between the key and the value contains 266bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * either one whitespace, one "=" or one ":" and any amount of additional 267bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * whitespace before and after that character. The value starts with the 268bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * first character after the space between the key and the value.</li> 269bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * <li>Following escape sequences are recognized: "\ ", "\\", "\r", "\n", 270bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * "\!", "\#", "\t", "\b", "\f", and "\uXXXX" (unicode character).</li> 271bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * </ul> 272bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 273bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param in the {@code Reader} 274bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws IOException 275bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @since 1.6 276bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 277bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry @SuppressWarnings("fallthrough") 278bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public synchronized void load(Reader in) throws IOException { 279bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (in == null) { 280bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry throw new NullPointerException(); 281bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 282bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry int mode = NONE, unicode = 0, count = 0; 283bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry char nextChar, buf[] = new char[40]; 284bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry int offset = 0, keyLength = -1, intVal; 285bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry boolean firstChar = true; 286bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 287bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry BufferedReader br = new BufferedReader(in); 288bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 289bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry while (true) { 290bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry intVal = br.read(); 291bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (intVal == -1) { 292bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry break; 293bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 294bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry nextChar = (char) intVal; 295bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 296bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (offset == buf.length) { 297bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry char[] newBuf = new char[buf.length * 2]; 298bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry System.arraycopy(buf, 0, newBuf, 0, offset); 299bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry buf = newBuf; 300bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 301bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (mode == UNICODE) { 302bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry int digit = Character.digit(nextChar, 16); 303bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (digit >= 0) { 304bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry unicode = (unicode << 4) + digit; 305bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (++count < 4) { 306bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry continue; 307bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 308bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } else if (count <= 4) { 309bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry throw new IllegalArgumentException("Invalid Unicode sequence: illegal character"); 310bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 311bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = NONE; 312bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry buf[offset++] = (char) unicode; 313bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (nextChar != '\n') { 314bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry continue; 315bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 316bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 317bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (mode == SLASH) { 318bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = NONE; 319bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry switch (nextChar) { 320bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry case '\r': 321bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = CONTINUE; // Look for a following \n 322bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry continue; 323bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry case '\n': 324bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = IGNORE; // Ignore whitespace on the next line 325bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry continue; 3263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case 'b': 3273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry nextChar = '\b'; 328bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry break; 3293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case 'f': 3303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry nextChar = '\f'; 3313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 332bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry case 'n': 3333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry nextChar = '\n'; 3343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 3353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case 'r': 3363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry nextChar = '\r'; 3373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 3383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case 't': 3393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry nextChar = '\t'; 3403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 3413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case 'u': 3423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry mode = UNICODE; 3433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unicode = count = 0; 3443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry continue; 3453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 3463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } else { 3473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry switch (nextChar) { 3483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '#': 3493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '!': 3503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (firstChar) { 3513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry while (true) { 3523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry intVal = br.read(); 3533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (intVal == -1) { 3543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 3553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 3563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry nextChar = (char) intVal; 3573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (nextChar == '\r' || nextChar == '\n') { 3583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 3593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 3603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 3613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry continue; 3623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 3633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 3643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '\n': 3653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (mode == CONTINUE) { // Part of a \r\n sequence 366bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = IGNORE; // Ignore whitespace on the next line 367bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry continue; 368bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 369bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry // fall into the next case 370bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry case '\r': 371bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = NONE; 3723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry firstChar = true; 3733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (offset > 0 || (offset == 0 && keyLength == 0)) { 3743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (keyLength == -1) { 3753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry keyLength = offset; 3763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 3773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String temp = new String(buf, 0, offset); 3783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry put(temp.substring(0, keyLength), temp 379bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry .substring(keyLength)); 380bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 3813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry keyLength = -1; 3823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry offset = 0; 3833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry continue; 3843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '\\': 3853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (mode == KEY_DONE) { 386bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry keyLength = offset; 387bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 3883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry mode = SLASH; 3893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry continue; 3903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case ':': 3913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry case '=': 3923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (keyLength == -1) { // if parsing the key 3933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry mode = NONE; 3943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry keyLength = offset; 3953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry continue; 3963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 3973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry break; 398bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 3993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (Character.isWhitespace(nextChar)) { 4003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (mode == CONTINUE) { 4013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry mode = IGNORE; 4023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 4033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry // if key length == 0 or value length == 0 4043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (offset == 0 || offset == keyLength || mode == IGNORE) { 4053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry continue; 4063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 4073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (keyLength == -1) { // if parsing the key 4083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry mode = KEY_DONE; 409bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry continue; 410bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 411bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 412bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (mode == IGNORE || mode == CONTINUE) { 413bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = NONE; 414bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 415bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 416bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry firstChar = false; 417bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (mode == KEY_DONE) { 418bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry keyLength = offset; 419bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry mode = NONE; 420bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 421bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry buf[offset++] = nextChar; 422bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 423bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (mode == UNICODE && count <= 4) { 424bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry throw new IllegalArgumentException("Invalid Unicode sequence: expected format \\uxxxx"); 425bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 426bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (keyLength == -1 && offset > 0) { 427bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry keyLength = offset; 428bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 429bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (keyLength >= 0) { 430bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry String temp = new String(buf, 0, offset); 431bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry String key = temp.substring(0, keyLength); 432bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry String value = temp.substring(keyLength); 433bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (mode == SLASH) { 434bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry value += "\u0000"; 435bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 436bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry put(key, value); 437bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 438bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 439bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 440bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 441bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Returns all of the property names (keys) in this {@code Properties} object. 442bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 443bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public Enumeration<?> propertyNames() { 444bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry Hashtable<Object, Object> selected = new Hashtable<Object, Object>(); 445bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry selectProperties(selected, false); 446bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry return selected.keys(); 447bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 448bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 449bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 450bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Returns those property names (keys) in this {@code Properties} object for which 451bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * both key and value are strings. 452bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 453bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @return a set of keys in the property list 454bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @since 1.6 455bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 456bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public Set<String> stringPropertyNames() { 457bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry Hashtable<String, Object> stringProperties = new Hashtable<String, Object>(); 458bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry selectProperties(stringProperties, true); 459bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry return Collections.unmodifiableSet(stringProperties.keySet()); 460bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 461bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 462bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry private <K> void selectProperties(Hashtable<K, Object> selectProperties, final boolean isStringOnly) { 463bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (defaults != null) { 464bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry defaults.selectProperties(selectProperties, isStringOnly); 465bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 466bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry Enumeration<Object> keys = keys(); 467bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry while (keys.hasMoreElements()) { 468bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry @SuppressWarnings("unchecked") 469bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry K key = (K) keys.nextElement(); 470bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (isStringOnly && !(key instanceof String)) { 471bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry // Only select property with string key and value 472bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry continue; 473bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 474bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry Object value = get(key); 475bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry selectProperties.put(key, value); 476bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 477bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 478bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 479bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 480bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Saves the mappings in this {@code Properties} to the specified {@code 481bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * OutputStream}, putting the specified comment at the beginning. The output 482bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * from this method is suitable for being read by the 483bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * {@link #load(InputStream)} method. 484bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 485bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param out the {@code OutputStream} to write to. 486bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param comment the comment to add at the beginning. 487bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws ClassCastException if the key or value of a mapping is not a 488bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * String. 489bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @deprecated This method ignores any {@code IOException} thrown while 490bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * writing -- use {@link #store} instead for better exception 491bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * handling. 492bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 493bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry @Deprecated 494bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public void save(OutputStream out, String comment) { 495bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry try { 496bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry store(out, comment); 497bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } catch (IOException e) { 498bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 499bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 500bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 501bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 502bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Maps the specified key to the specified value. If the key already exists, 503bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * the old value is replaced. The key and value cannot be {@code null}. 504bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 505bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param name 506bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * the key. 507bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param value 508bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * the value. 509bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @return the old value mapped to the key, or {@code null}. 510bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 511bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public Object setProperty(String name, String value) { 512bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry return put(name, value); 513bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 514bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 515bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 516bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Stores properties to the specified {@code OutputStream}, using ISO-8859-1. 517bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * See "<a href="#character_encoding">Character Encoding</a>". 518bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 519bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param out the {@code OutputStream} 520bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param comment an optional comment to be written, or null 521bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws IOException 522bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws ClassCastException if a key or value is not a string 523bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 524bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public synchronized void store(OutputStream out, String comment) throws IOException { 525bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry store(new OutputStreamWriter(out, "ISO-8859-1"), comment); 526bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 527bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 528bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry /** 529bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * Stores the mappings in this {@code Properties} object to {@code out}, 530bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * putting the specified comment at the beginning. 531bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 532bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param writer the {@code Writer} 533bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param comment an optional comment to be written, or null 534bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws IOException 535bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws ClassCastException if a key or value is not a string 536bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @since 1.6 537bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry */ 538bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry public synchronized void store(Writer writer, String comment) throws IOException { 539bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry if (comment != null) { 540bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry writer.write("#"); 541bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry writer.write(comment); 542bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry writer.write(System.lineSeparator()); 543bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry } 544bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry writer.write("#"); 545bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry writer.write(new Date().toString()); 546bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry writer.write(System.lineSeparator()); 547bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry 5483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringBuilder sb = new StringBuilder(200); 5493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for (Map.Entry<Object, Object> entry : entrySet()) { 5503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String key = (String) entry.getKey(); 5513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry dumpString(sb, key, true); 5523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry sb.append('='); 5533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry dumpString(sb, (String) entry.getValue(), false); 5543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry sb.append(System.lineSeparator()); 5553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry writer.write(sb.toString()); 5563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry sb.setLength(0); 5573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 5583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry writer.flush(); 5593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 5603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 5613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 5623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Loads the properties from an {@code InputStream} containing the 5633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * properties in XML form. The XML document must begin with (and conform to) 5643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * following DOCTYPE: 5653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 5663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <pre> 5673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 5683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * </pre> 5693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 5703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Also the content of the XML data must satisfy the DTD but the xml is not 5713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * validated against it. The DTD is not loaded from the SYSTEM ID. After 5723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * this method returns the InputStream is not closed. 5733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 5743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @param in the InputStream containing the XML document. 5753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @throws IOException in case an error occurs during a read operation. 5763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @throws InvalidPropertiesFormatException if the XML data is not a valid 5773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * properties file. 5783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 5793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public synchronized void loadFromXML(InputStream in) throws IOException, 5803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry InvalidPropertiesFormatException { 5813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (in == null) { 5823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw new NullPointerException(); 5833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 5843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 5853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (builder == null) { 5863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 5873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry // BEGIN android-removed: we still don't support validation. 5883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry // factory.setValidating(true); 5893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry // END android-removed 5903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 5913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry try { 5923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry builder = factory.newDocumentBuilder(); 5933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } catch (ParserConfigurationException e) { 5943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw new Error(e); 5953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 5963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 5973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry builder.setErrorHandler(new ErrorHandler() { 5983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public void warning(SAXParseException e) throws SAXException { 5993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw e; 6003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public void error(SAXParseException e) throws SAXException { 6033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw e; 6043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public void fatalError(SAXParseException e) throws SAXException { 6073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw e; 6083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry }); 6103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry builder.setEntityResolver(new EntityResolver() { 6123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public InputSource resolveEntity(String publicId, 6133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String systemId) throws SAXException, IOException { 6143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (systemId.equals(PROP_DTD_NAME)) { 6153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry InputSource result = new InputSource(new StringReader( 6163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry PROP_DTD)); 6173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry result.setSystemId(PROP_DTD_NAME); 6183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return result; 6193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw new SAXException("Invalid DOCTYPE declaration: " 6213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + systemId); 6223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry }); 6243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry try { 6273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Document doc = builder.parse(in); 6283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry NodeList entries = doc.getElementsByTagName("entry"); 6293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (entries == null) { 6303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return; 6313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry int entriesListLength = entries.getLength(); 6333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for (int i = 0; i < entriesListLength; i++) { 6353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Element entry = (Element) entries.item(i); 6363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String key = entry.getAttribute("key"); 6373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String value = entry.getTextContent(); 6383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /* 6403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * key != null & value != null but key or(and) value can be 6413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * empty String 6423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 6433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry put(key, value); 6443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } catch (IOException e) { 6463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw e; 6473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } catch (SAXException e) { 6483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw new InvalidPropertiesFormatException(e); 6493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 6533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Writes all properties stored in this instance into the {@code 6543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * OutputStream} in XML representation. The DOCTYPE is 6553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 6563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <pre> 6573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 6583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * </pre> 6593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 6603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * If the comment is null, no comment is added to the output. UTF-8 is used 6613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * as the encoding. The {@code OutputStream} is not closed at the end. A 6623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * call to this method is the same as a call to {@code storeToXML(os, 6633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * comment, "UTF-8")}. 6643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 6653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @param os the {@code OutputStream} to write to. 6663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @param comment the comment to add. If null, no comment is added. 6673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * @throws IOException if an error occurs during writing to the output. 6683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 6693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public void storeToXML(OutputStream os, String comment) throws IOException { 6703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry storeToXML(os, comment, "UTF-8"); 6713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /** 6743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * Writes all properties stored in this instance into the {@code 6753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * OutputStream} in XML representation. The DOCTYPE is 6763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 6773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <pre> 6783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 6793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * </pre> 6803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * 681bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * If the comment is null, no comment is added to the output. The parameter 682bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * {@code encoding} defines which encoding should be used. The {@code 683bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * OutputStream} is not closed at the end. 684bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * 685bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param os the {@code OutputStream} to write to. 686bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param comment the comment to add. If null, no comment is added. 687bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @param encoding the code identifying the encoding that should be used to 688bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * write into the {@code OutputStream}. 689bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry * @throws IOException if an error occurs during writing to the output. 6903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 6913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry public synchronized void storeToXML(OutputStream os, String comment, 6923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String encoding) throws IOException { 6933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (os == null || encoding == null) { 6953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry throw new NullPointerException(); 6963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 6973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 6983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /* 6993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * We can write to XML file using encoding parameter but note that some 7003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * aliases for encodings are not supported by the XML parser. Thus we 7013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * have to know canonical name for encoding used to store data in XML 7023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry * since the XML parser must recognize encoding name used to store data. 7033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry */ 7043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String encodingCanonicalName; 7063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry try { 7073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry encodingCanonicalName = Charset.forName(encoding).name(); 7083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } catch (IllegalCharsetNameException e) { 7093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry System.out.println("Warning: encoding name " + encoding 7103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + " is illegal, using UTF-8 as default encoding"); 7113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry encodingCanonicalName = "UTF-8"; 7123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } catch (UnsupportedCharsetException e) { 7133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry System.out.println("Warning: encoding " + encoding 7143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry + " is not supported, using UTF-8 as default encoding"); 7153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry encodingCanonicalName = "UTF-8"; 7163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 7173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry PrintStream printStream = new PrintStream(os, false, 7193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry encodingCanonicalName); 7203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print("<?xml version=\"1.0\" encoding=\""); 7223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print(encodingCanonicalName); 7233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.println("\"?>"); 7243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print("<!DOCTYPE properties SYSTEM \""); 726bd4d098088a5b205c7e7d8dc0bec140d2fa55446Jarkko Pöyry printStream.print(PROP_DTD_NAME); 7273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.println("\">"); 7283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.println("<properties>"); 7303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (comment != null) { 7323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print("<comment>"); 7333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print(substitutePredefinedEntries(comment)); 7343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.println("</comment>"); 7353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 7363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry for (Map.Entry<Object, Object> entry : entrySet()) { 7383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String keyValue = (String) entry.getKey(); 7393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry String entryValue = (String) entry.getValue(); 7403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print("<entry key=\""); 7413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print(substitutePredefinedEntries(keyValue)); 7423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print("\">"); 7433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.print(substitutePredefinedEntries(entryValue)); 7443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.println("</entry>"); 7453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 7463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.println("</properties>"); 7473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry printStream.flush(); 7483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 7493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 7503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry private String substitutePredefinedEntries(String s) { 7513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry // substitution for predefined character entities to use them safely in XML. 7523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry s = s.replaceAll("&", "&"); 7533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry s = s.replaceAll("<", "<"); 7543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry s = s.replaceAll(">", ">"); 7553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry s = s.replaceAll("'", "'"); 7563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry s = s.replaceAll("\"", """); 7573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return s; 7583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 7593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry} 7603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry