Properties.java revision 66e1a782c0ffafcb7c4226798d6ecc4cfc071916
1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.util;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.io.BufferedReader;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
23565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.io.InputStreamReader;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.OutputStream;
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.OutputStreamWriter;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.PrintStream;
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.PrintWriter;
28565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.io.Reader;
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.StringReader;
30565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughesimport java.io.Writer;
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.nio.charset.Charset;
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.nio.charset.IllegalCharsetNameException;
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.nio.charset.UnsupportedCharsetException;
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport javax.xml.parsers.DocumentBuilder;
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport javax.xml.parsers.DocumentBuilderFactory;
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport javax.xml.parsers.ParserConfigurationException;
3703c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughesimport org.w3c.dom.Document;
3803c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughesimport org.w3c.dom.Element;
3903c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughesimport org.w3c.dom.Node;
4003c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughesimport org.w3c.dom.NodeList;
4103c0a8e681c776fdba0389ab8593282139afc6d6Elliott Hughesimport org.w3c.dom.Text;
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport org.xml.sax.EntityResolver;
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport org.xml.sax.ErrorHandler;
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport org.xml.sax.InputSource;
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport org.xml.sax.SAXException;
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport org.xml.sax.SAXParseException;
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
49f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * A {@code Properties} object is a {@code Hashtable} where the keys and values
50f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * must be {@code String}s. Each property can have a default
51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * {@code Properties} list which specifies the default
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * values to be used when a given key is not found in this {@code Properties}
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * instance.
54f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson *
5500ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes * <a name="character_encoding"><h3>Character Encoding</h3></a>
5600ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes * <p>Note that in some cases {@code Properties} uses ISO-8859-1 instead of UTF-8.
5700ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes * ISO-8859-1 is only capable of representing a tiny subset of Unicode.
5800ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes * Use either the {@code loadFromXML}/{@code storeToXML} methods (which use UTF-8 by
5900ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes * default) or the {@code load}/{@code store} overloads that take
6000ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes * an {@code OutputStreamWriter} (so you can supply a UTF-8 instance) instead.
6100ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes *
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see Hashtable
63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @see java.lang.System#getProperties
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
65f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilsonpublic class Properties extends Hashtable<Object, Object> {
66f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
67adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final long serialVersionUID = 4112578634029874840L;
68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
69adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private transient DocumentBuilder builder = null;
70adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
71f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private static final String PROP_DTD_NAME = "http://java.sun.com/dtd/properties.dtd";
72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
73f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    private static final String PROP_DTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            + "    <!ELEMENT properties (comment?, entry*) >"
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            + "    <!ATTLIST properties version CDATA #FIXED \"1.0\" >"
76adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            + "    <!ELEMENT comment (#PCDATA) >"
77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            + "    <!ELEMENT entry (#PCDATA) >"
78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            + "    <!ATTLIST entry key CDATA #REQUIRED >";
79f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * The default values for keys not found in this {@code Properties}
82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * instance.
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected Properties defaults;
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int NONE = 0, SLASH = 1, UNICODE = 2, CONTINUE = 3,
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            KEY_DONE = 4, IGNORE = 5;
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constructs a new {@code Properties} object.
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Properties() {
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        super();
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constructs a new {@code Properties} object using the specified default
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code Properties}.
99f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param properties
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the default {@code Properties}.
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Properties(Properties properties) {
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        defaults = properties;
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void dumpString(StringBuilder buffer, String string, boolean key) {
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int i = 0;
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!key && i < string.length() && string.charAt(i) == ' ') {
110565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            buffer.append("\\ ");
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            i++;
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (; i < string.length(); i++) {
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            char ch = string.charAt(i);
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            switch (ch) {
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            case '\t':
118565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                buffer.append("\\t");
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            case '\n':
121565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                buffer.append("\\n");
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            case '\f':
124565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                buffer.append("\\f");
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            case '\r':
127565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                buffer.append("\\r");
128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
129adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            default:
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if ("\\#!=:".indexOf(ch) >= 0 || (key && ch == ' ')) {
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    buffer.append('\\');
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (ch >= ' ' && ch <= '~') {
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    buffer.append(ch);
135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                } else {
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    String hex = Integer.toHexString(ch);
137565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    buffer.append("\\u");
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    for (int j = 0; j < 4 - hex.length(); j++) {
139565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                        buffer.append("0");
140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    buffer.append(hex);
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Searches for the property with the specified name. If the property is not
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * found, the default {@code Properties} are checked. If the property is not
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * found in the default {@code Properties}, {@code null} is returned.
151f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param name
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the property to find.
154adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the named property value, or {@code null} if it can't be found.
155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getProperty(String name) {
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Object result = super.get(name);
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String property = result instanceof String ? (String) result : null;
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (property == null && defaults != null) {
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            property = defaults.getProperty(name);
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return property;
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
164adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Searches for the property with the specified name. If the property is not
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * found, it looks in the default {@code Properties}. If the property is not
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * found in the default {@code Properties}, it returns the specified
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * default.
170f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param name
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the name of the property to find.
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param defaultValue
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the default value.
175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the named property value.
176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getProperty(String name, String defaultValue) {
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Object result = super.get(name);
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String property = result instanceof String ? (String) result : null;
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (property == null && defaults != null) {
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            property = defaults.getProperty(name);
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (property == null) {
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return defaultValue;
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return property;
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Lists the mappings in this {@code Properties} to the specified
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code PrintStream} in a
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * human readable form.
193f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param out
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the {@code PrintStream} to write the content to in human readable
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            form.
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void list(PrintStream out) {
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (out == null) {
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new NullPointerException();
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
202a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson        StringBuilder buffer = new StringBuilder(80);
203adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Enumeration<?> keys = propertyNames();
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (keys.hasMoreElements()) {
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String key = (String) keys.nextElement();
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.append(key);
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.append('=');
208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String property = (String) super.get(key);
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Properties def = defaults;
210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (property == null) {
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                property = (String) def.get(key);
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                def = def.defaults;
213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
214adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (property.length() > 40) {
215adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buffer.append(property.substring(0, 37));
216565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                buffer.append("...");
217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buffer.append(property);
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            out.println(buffer.toString());
221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.setLength(0);
222adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Lists the mappings in this {@code Properties} to the specified
227adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code PrintWriter} in a
228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * human readable form.
229f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
230adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param writer
231adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the {@code PrintWriter} to write the content to in human
232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            readable form.
233adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
234adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void list(PrintWriter writer) {
235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (writer == null) {
236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new NullPointerException();
237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
238a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson        StringBuilder buffer = new StringBuilder(80);
239adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Enumeration<?> keys = propertyNames();
240adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (keys.hasMoreElements()) {
241adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String key = (String) keys.nextElement();
242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.append(key);
243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.append('=');
244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String property = (String) super.get(key);
245adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Properties def = defaults;
246adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (property == null) {
247adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                property = (String) def.get(key);
248adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                def = def.defaults;
249adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (property.length() > 40) {
251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buffer.append(property.substring(0, 37));
252565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                buffer.append("...");
253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
254adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buffer.append(property);
255adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            writer.println(buffer.toString());
257adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.setLength(0);
258adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
259adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
26200ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes     * Loads properties from the specified {@code InputStream}, assumed to be ISO-8859-1.
26300ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes     * See "<a href="#character_encoding">Character Encoding</a>".
26400ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes     *
265565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param in the {@code InputStream}
266565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @throws IOException
267565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     */
268565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public synchronized void load(InputStream in) throws IOException {
269565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        if (in == null) {
270565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            throw new NullPointerException();
271565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
27246ff2ede6c9f5ad431303d388986ec3d72b2fbd3Elliott Hughes        load(new InputStreamReader(in, "ISO-8859-1"));
273565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
274565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
275565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    /**
276565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * Loads properties from the specified {@code Reader}.
277565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * The properties file is interpreted according to the following rules:
278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <ul>
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>Empty lines are ignored.</li>
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>Lines starting with either a "#" or a "!" are comment lines and are
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * ignored.</li>
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>A backslash at the end of the line escapes the following newline
283565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * character ("\r", "\n", "\r\n"). If there's whitespace after the
284adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * backslash it will just escape that whitespace instead of concatenating
285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * the lines. This does not apply to comment lines.</li>
286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>A property line consists of the key, the space between the key and
287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * the value, and the value. The key goes up to the first whitespace, "=" or
288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * ":" that is not escaped. The space between the key and the value contains
289565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * either one whitespace, one "=" or one ":" and any amount of additional
290565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * whitespace before and after that character. The value starts with the
291adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * first character after the space between the key and the value.</li>
292adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <li>Following escape sequences are recognized: "\ ", "\\", "\r", "\n",
293adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * "\!", "\#", "\t", "\b", "\f", and "&#92;uXXXX" (unicode character).</li>
294adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * </ul>
295f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
296565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param in the {@code Reader}
297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
298565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @since 1.6
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
300f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    @SuppressWarnings("fallthrough")
301565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public synchronized void load(Reader in) throws IOException {
302f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        if (in == null) {
303f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            throw new NullPointerException();
304f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        }
305adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int mode = NONE, unicode = 0, count = 0;
306adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        char nextChar, buf[] = new char[40];
307f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        int offset = 0, keyLength = -1, intVal;
308adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        boolean firstChar = true;
309142d526f8bf90fb9bb63c637beb5299f39791f55Jesse Wilson
310565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        BufferedReader br = new BufferedReader(in);
311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
312adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (true) {
313565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            intVal = br.read();
314f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            if (intVal == -1) {
315f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                break;
316adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
317565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            nextChar = (char) intVal;
318adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
319adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (offset == buf.length) {
320adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                char[] newBuf = new char[buf.length * 2];
321adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                System.arraycopy(buf, 0, newBuf, 0, offset);
322adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buf = newBuf;
323adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
324adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (mode == UNICODE) {
325adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                int digit = Character.digit(nextChar, 16);
326adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (digit >= 0) {
327adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    unicode = (unicode << 4) + digit;
328adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (++count < 4) {
329adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        continue;
330adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
331adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                } else if (count <= 4) {
332565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                    throw new IllegalArgumentException("Invalid Unicode sequence: illegal character");
333adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
334adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                mode = NONE;
335adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buf[offset++] = (char) unicode;
336adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (nextChar != '\n') {
337adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    continue;
338adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
339adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
340adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (mode == SLASH) {
341adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                mode = NONE;
342adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                switch (nextChar) {
343adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '\r':
344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    mode = CONTINUE; // Look for a following \n
345adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    continue;
346adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '\n':
347adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    mode = IGNORE; // Ignore whitespace on the next line
348adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    continue;
349adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case 'b':
350adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    nextChar = '\b';
351adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
352adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case 'f':
353adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    nextChar = '\f';
354adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
355adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case 'n':
356adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    nextChar = '\n';
357adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
358adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case 'r':
359adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    nextChar = '\r';
360adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
361adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case 't':
362adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    nextChar = '\t';
363adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
364adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case 'u':
365adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    mode = UNICODE;
366adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    unicode = count = 0;
367adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    continue;
368adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
369adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
370adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                switch (nextChar) {
371adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '#':
372adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '!':
373adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (firstChar) {
374adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        while (true) {
375565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                            intVal = br.read();
376f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            if (intVal == -1) {
377f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                                break;
378adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                            }
379f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            nextChar = (char) intVal;
380adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                            if (nextChar == '\r' || nextChar == '\n') {
381adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                                break;
382adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                            }
383adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        }
384adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        continue;
385adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
386adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
387adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '\n':
388adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (mode == CONTINUE) { // Part of a \r\n sequence
389adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        mode = IGNORE; // Ignore whitespace on the next line
390adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        continue;
391adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
392f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    // fall into the next case
393adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '\r':
394adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    mode = NONE;
395adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    firstChar = true;
396a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson                    if (offset > 0 || (offset == 0 && keyLength == 0)) {
397adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        if (keyLength == -1) {
398adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                            keyLength = offset;
399adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        }
400adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        String temp = new String(buf, 0, offset);
401adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        put(temp.substring(0, keyLength), temp
402adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                                .substring(keyLength));
403adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
404adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    keyLength = -1;
405adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    offset = 0;
406adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    continue;
407adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '\\':
408adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (mode == KEY_DONE) {
409adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        keyLength = offset;
410adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
411adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    mode = SLASH;
412adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    continue;
413adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case ':':
414adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                case '=':
415adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (keyLength == -1) { // if parsing the key
416adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        mode = NONE;
417adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        keyLength = offset;
418adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        continue;
419adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
420adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
421adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
422adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (Character.isWhitespace(nextChar)) {
423adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (mode == CONTINUE) {
424adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        mode = IGNORE;
425adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
426adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    // if key length == 0 or value length == 0
427adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (offset == 0 || offset == keyLength || mode == IGNORE) {
428adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        continue;
429adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
430adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (keyLength == -1) { // if parsing the key
431adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        mode = KEY_DONE;
432adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        continue;
433adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
434adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
435adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (mode == IGNORE || mode == CONTINUE) {
436adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    mode = NONE;
437adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
438adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
439adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            firstChar = false;
440adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (mode == KEY_DONE) {
441adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                keyLength = offset;
442adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                mode = NONE;
443adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
444adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buf[offset++] = nextChar;
445adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
446565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        if (mode == UNICODE && count <= 4) {
447565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            throw new IllegalArgumentException("Invalid Unicode sequence: expected format \\uxxxx");
448565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        }
449f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        if (keyLength == -1 && offset > 0) {
450f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            keyLength = offset;
451adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
452adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (keyLength >= 0) {
453adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String temp = new String(buf, 0, offset);
454565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String key = temp.substring(0, keyLength);
455565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            String value = temp.substring(keyLength);
456565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            if (mode == SLASH) {
457565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                value += "\u0000";
458565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
459565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            put(key, value);
460adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
461adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
462adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
463adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
464565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * Returns all of the property names (keys) in this {@code Properties} object.
465adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
466adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Enumeration<?> propertyNames() {
467565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        Hashtable<Object, Object> selected = new Hashtable<Object, Object>();
468565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        selectProperties(selected, false);
469565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return selected.keys();
470565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
471adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
472565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    /**
473565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * Returns those property names (keys) in this {@code Properties} object for which
474565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * both key and value are strings.
475f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
476565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @return a set of keys in the property list
477565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @since 1.6
478565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     */
479565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public Set<String> stringPropertyNames() {
480866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes        Hashtable<String, Object> stringProperties = new Hashtable<String, Object>();
481565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        selectProperties(stringProperties, true);
482565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        return Collections.unmodifiableSet(stringProperties.keySet());
483565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
484565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
485866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes    private <K> void selectProperties(Hashtable<K, Object> selectProperties, final boolean isStringOnly) {
486565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        if (defaults != null) {
487565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            defaults.selectProperties(selectProperties, isStringOnly);
488adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
489866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes        Enumeration<Object> keys = keys();
490adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (keys.hasMoreElements()) {
491866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes            @SuppressWarnings("unchecked")
492866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes            K key = (K) keys.nextElement();
493866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes            if (isStringOnly && !(key instanceof String)) {
494565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes                // Only select property with string key and value
495866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes                continue;
496565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            }
497866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes            Object value = get(key);
498866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes            selectProperties.put(key, value);
499adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
500adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
501adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
502adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
503adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Saves the mappings in this {@code Properties} to the specified {@code
504adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * OutputStream}, putting the specified comment at the beginning. The output
505adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * from this method is suitable for being read by the
506adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@link #load(InputStream)} method.
507f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
508adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param out the {@code OutputStream} to write to.
509adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param comment the comment to add at the beginning.
510f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     * @throws ClassCastException if the key or value of a mapping is not a
511adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *                String.
512adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @deprecated This method ignores any {@code IOException} thrown while
513adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             writing -- use {@link #store} instead for better exception
514adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             handling.
515adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
516adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Deprecated
517adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void save(OutputStream out, String comment) {
518adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
519adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            store(out, comment);
520adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (IOException e) {
521adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
522adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
523adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
524adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
525adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Maps the specified key to the specified value. If the key already exists,
526adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * the old value is replaced. The key and value cannot be {@code null}.
527f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
528adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param name
529adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the key.
530adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param value
531adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the value.
532adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the old value mapped to the key, or {@code null}.
533adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
534adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Object setProperty(String name, String value) {
535adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return put(name, value);
536adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
537adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
538565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    /**
53900ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes     * Stores properties to the specified {@code OutputStream}, using ISO-8859-1.
54000ccc7c466cf9f0c5b3df4d3276e1e4be0618f7fElliott Hughes     * See "<a href="#character_encoding">Character Encoding</a>".
541f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
542565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param out the {@code OutputStream}
543565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param comment an optional comment to be written, or null
544565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @throws IOException
545565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @throws ClassCastException if a key or value is not a string
546565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     */
547565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public synchronized void store(OutputStream out, String comment) throws IOException {
54846ff2ede6c9f5ad431303d388986ec3d72b2fbd3Elliott Hughes        store(new OutputStreamWriter(out, "ISO-8859-1"), comment);
549565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    }
550565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes
551adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static String lineSeparator;
552adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
553adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
554565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * Stores the mappings in this {@code Properties} object to {@code out},
555565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * putting the specified comment at the beginning.
556f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
557bf87c56b39383f6b11c36c3cdc93df4b03fed914Brian Carlstrom     * @param writer the {@code Writer}
558565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @param comment an optional comment to be written, or null
559565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @throws IOException
560565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @throws ClassCastException if a key or value is not a string
561565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes     * @since 1.6
562adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
563565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes    public synchronized void store(Writer writer, String comment) throws IOException {
564adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (lineSeparator == null) {
565ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes            lineSeparator = System.getProperty("line.separator");
566adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
567adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
568adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (comment != null) {
569565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            writer.write("#");
570adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            writer.write(comment);
571f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            writer.write(lineSeparator);
572adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
573565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        writer.write("#");
574adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        writer.write(new Date().toString());
575f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        writer.write(lineSeparator);
576adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
577565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        StringBuilder buffer = new StringBuilder(200);
578adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (Map.Entry<Object, Object> entry : entrySet()) {
579adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String key = (String) entry.getKey();
580adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            dumpString(buffer, key, true);
581adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.append('=');
582adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            dumpString(buffer, (String) entry.getValue(), false);
583adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.append(lineSeparator);
584adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            writer.write(buffer.toString());
585adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buffer.setLength(0);
586adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
587adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        writer.flush();
588adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
589adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
590adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
591adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Loads the properties from an {@code InputStream} containing the
592adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * properties in XML form. The XML document must begin with (and conform to)
593adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * following DOCTYPE:
594f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
595adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <pre>
596adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * &lt;!DOCTYPE properties SYSTEM &quot;http://java.sun.com/dtd/properties.dtd">;
597adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * </pre>
598f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
599adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Also the content of the XML data must satisfy the DTD but the xml is not
600adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * validated against it. The DTD is not loaded from the SYSTEM ID. After
601adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * this method returns the InputStream is not closed.
602f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
603adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param in the InputStream containing the XML document.
604adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException in case an error occurs during a read operation.
605adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws InvalidPropertiesFormatException if the XML data is not a valid
606adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             properties file.
607adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
608f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    public synchronized void loadFromXML(InputStream in) throws IOException,
609f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            InvalidPropertiesFormatException {
610adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (in == null) {
611adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new NullPointerException();
612adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
613f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
614adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (builder == null) {
615adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
616565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes            // BEGIN android-removed: we still don't support validation.
617adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // factory.setValidating(true);
618adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // END android-removed
619f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
620adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            try {
621adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                builder = factory.newDocumentBuilder();
622adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } catch (ParserConfigurationException e) {
623adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                throw new Error(e);
624adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
625f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
626adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            builder.setErrorHandler(new ErrorHandler() {
627adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                public void warning(SAXParseException e) throws SAXException {
628adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    throw e;
629adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
630adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
631adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                public void error(SAXParseException e) throws SAXException {
632adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    throw e;
633adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
634adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
635adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                public void fatalError(SAXParseException e) throws SAXException {
636adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    throw e;
637adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
638adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            });
639f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
640adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            builder.setEntityResolver(new EntityResolver() {
641f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                public InputSource resolveEntity(String publicId,
642f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        String systemId) throws SAXException, IOException {
643adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    if (systemId.equals(PROP_DTD_NAME)) {
644adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        InputSource result = new InputSource(new StringReader(
645adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                                PROP_DTD));
646adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        result.setSystemId(PROP_DTD_NAME);
647adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        return result;
648adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
649f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    throw new SAXException("Invalid DOCTYPE declaration: "
650f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                            + systemId);
651adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
652adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            });
653adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
654f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
655adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
656adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Document doc = builder.parse(in);
657f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson            NodeList entries = doc.getElementsByTagName("entry");
658adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (entries == null) {
659adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return;
660adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
661adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int entriesListLength = entries.getLength();
662f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
663adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            for (int i = 0; i < entriesListLength; i++) {
664adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                Element entry = (Element) entries.item(i);
665adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                String key = entry.getAttribute("key");
66666e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes                String value = entry.getTextContent();
667f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
668adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                /*
669f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                 * key != null & value != null but key or(and) value can be
670f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                 * empty String
671adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                 */
672adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                put(key, value);
673adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
674adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (IOException e) {
675adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw e;
676adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (SAXException e) {
677adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new InvalidPropertiesFormatException(e);
678adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
679adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
680adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
681adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
682adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Writes all properties stored in this instance into the {@code
683adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * OutputStream} in XML representation. The DOCTYPE is
684f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
685adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <pre>
686adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * &lt;!DOCTYPE properties SYSTEM &quot;http://java.sun.com/dtd/properties.dtd">;
687adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * </pre>
688f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
689adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * If the comment is null, no comment is added to the output. UTF-8 is used
690adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * as the encoding. The {@code OutputStream} is not closed at the end. A
691adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * call to this method is the same as a call to {@code storeToXML(os,
692adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * comment, "UTF-8")}.
693f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
694adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param os the {@code OutputStream} to write to.
695adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param comment the comment to add. If null, no comment is added.
696adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException if an error occurs during writing to the output.
697adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
698f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson    public void storeToXML(OutputStream os, String comment) throws IOException {
699565a85d06ab8bc321d39f12012468cdfb65f5cfeElliott Hughes        storeToXML(os, comment, "UTF-8");
700adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
701adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
702adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
703adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Writes all properties stored in this instance into the {@code
704adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * OutputStream} in XML representation. The DOCTYPE is
705f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
706adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <pre>
707adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * &lt;!DOCTYPE properties SYSTEM &quot;http://java.sun.com/dtd/properties.dtd">;
708adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * </pre>
709f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
710adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * If the comment is null, no comment is added to the output. The parameter
711adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * {@code encoding} defines which encoding should be used. The {@code
712adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * OutputStream} is not closed at the end.
713f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson     *
714adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param os the {@code OutputStream} to write to.
715adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param comment the comment to add. If null, no comment is added.
716adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param encoding the code identifying the encoding that should be used to
717adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            write into the {@code OutputStream}.
718adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException if an error occurs during writing to the output.
719adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
720adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public synchronized void storeToXML(OutputStream os, String comment,
721adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String encoding) throws IOException {
722adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
723adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (os == null || encoding == null) {
724adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new NullPointerException();
725adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
726f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
727adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        /*
728adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project         * We can write to XML file using encoding parameter but note that some
729adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project         * aliases for encodings are not supported by the XML parser. Thus we
730adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project         * have to know canonical name for encoding used to store data in XML
731adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project         * since the XML parser must recognize encoding name used to store data.
732adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project         */
733f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
734adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String encodingCanonicalName;
735adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
736adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            encodingCanonicalName = Charset.forName(encoding).name();
737adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (IllegalCharsetNameException e) {
738adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            System.out.println("Warning: encoding name " + encoding
739adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    + " is illegal, using UTF-8 as default encoding");
740adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            encodingCanonicalName = "UTF-8";
741adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (UnsupportedCharsetException e) {
742adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            System.out.println("Warning: encoding " + encoding
743adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    + " is not supported, using UTF-8 as default encoding");
744adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            encodingCanonicalName = "UTF-8";
745adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
746adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
747f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson        PrintStream printStream = new PrintStream(os, false,
748f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                encodingCanonicalName);
749f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
750adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.print("<?xml version=\"1.0\" encoding=\"");
751adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.print(encodingCanonicalName);
752adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.println("\"?>");
753f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
754adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.print("<!DOCTYPE properties SYSTEM \"");
755adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.print(PROP_DTD_NAME);
756adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.println("\">");
757f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
758adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.println("<properties>");
759f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
760adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (comment != null) {
761adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.print("<comment>");
762adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.print(substitutePredefinedEntries(comment));
763adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.println("</comment>");
764adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
765adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
766adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (Map.Entry<Object, Object> entry : entrySet()) {
767adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String keyValue = (String) entry.getKey();
768adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String entryValue = (String) entry.getValue();
769adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.print("<entry key=\"");
770adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.print(substitutePredefinedEntries(keyValue));
771adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.print("\">");
772adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.print(substitutePredefinedEntries(entryValue));
773adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            printStream.println("</entry>");
774adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
775adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.println("</properties>");
776adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        printStream.flush();
777adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
778f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson
779adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String substitutePredefinedEntries(String s) {
78066e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes        // substitution for predefined character entities to use them safely in XML.
78166e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes        s = s.replaceAll("&", "&amp;");
78266e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes        s = s.replaceAll("<", "&lt;");
78366e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes        s = s.replaceAll(">", "&gt;");
78466e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes        s = s.replaceAll("'", "&apos;");
78566e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes        s = s.replaceAll("\"", "&quot;");
78666e1a782c0ffafcb7c4226798d6ecc4cfc071916Elliott Hughes        return s;
787adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
788adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
789