2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
18
package org.apache.commons.configuration;
21
import java.io.PrintWriter;
22
import java.io.Reader;
23
import java.io.Writer;
25
import java.util.Iterator;
26
import java.util.List;
28
import javax.xml.parsers.SAXParser;
29
import javax.xml.parsers.SAXParserFactory;
31
import org.apache.commons.lang.StringEscapeUtils;
32
import org.apache.commons.lang.StringUtils;
33
import org.xml.sax.Attributes;
34
import org.xml.sax.EntityResolver;
35
import org.xml.sax.InputSource;
36
import org.xml.sax.XMLReader;
37
import org.xml.sax.helpers.DefaultHandler;
40
* This configuration implements the XML properties format introduced in Java
41
* 5.0, see http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html.
42
* An XML properties file looks like this:
45
* <?xml version="1.0"?>
46
* <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
48
* <comment>Description of the property list</comment>
49
* <entry key="key1">value1</entry>
50
* <entry key="key2">value2</entry>
51
* <entry key="key3">value3</entry>
55
* The Java 5.0 runtime is not required to use this class. The default encoding
56
* for this configuration format is UTF-8. Note that unlike
57
* {@code PropertiesConfiguration}, {@code XMLPropertiesConfiguration}
58
* does not support includes.
60
* <em>Note:</em>Configuration objects of this type can be read concurrently
61
* by multiple threads. However if one of these threads modifies the object,
62
* synchronization has to be performed manually.
64
* @author Emmanuel Bourg
65
* @author Alistair Young
66
* @version $Id: XMLPropertiesConfiguration.java 1210207 2011-12-04 20:43:50Z oheger $
69
public class XMLPropertiesConfiguration extends PropertiesConfiguration
72
* The default encoding (UTF-8 as specified by http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html)
74
private static final String DEFAULT_ENCODING = "UTF-8";
76
// initialization block to set the encoding before loading the file in the constructors
78
setEncoding(DEFAULT_ENCODING);
82
* Creates an empty XMLPropertyConfiguration object which can be
83
* used to synthesize a new Properties file by adding values and
84
* then saving(). An object constructed by this C'tor can not be
85
* tickled into loading included files because it cannot supply a
86
* base for relative includes.
88
public XMLPropertiesConfiguration()
94
* Creates and loads the xml properties from the specified file.
95
* The specified file can contain "include" properties which then
96
* are loaded and merged into the properties.
98
* @param fileName The name of the properties file to load.
99
* @throws ConfigurationException Error while loading the properties file
101
public XMLPropertiesConfiguration(String fileName) throws ConfigurationException
107
* Creates and loads the xml properties from the specified file.
108
* The specified file can contain "include" properties which then
109
* are loaded and merged into the properties.
111
* @param file The properties file to load.
112
* @throws ConfigurationException Error while loading the properties file
114
public XMLPropertiesConfiguration(File file) throws ConfigurationException
120
* Creates and loads the xml properties from the specified URL.
121
* The specified file can contain "include" properties which then
122
* are loaded and merged into the properties.
124
* @param url The location of the properties file to load.
125
* @throws ConfigurationException Error while loading the properties file
127
public XMLPropertiesConfiguration(URL url) throws ConfigurationException
133
public void load(Reader in) throws ConfigurationException
135
SAXParserFactory factory = SAXParserFactory.newInstance();
136
factory.setNamespaceAware(false);
137
factory.setValidating(true);
141
SAXParser parser = factory.newSAXParser();
143
XMLReader xmlReader = parser.getXMLReader();
144
xmlReader.setEntityResolver(new EntityResolver()
146
public InputSource resolveEntity(String publicId, String systemId)
148
return new InputSource(getClass().getClassLoader().getResourceAsStream("properties.dtd"));
151
xmlReader.setContentHandler(new XMLPropertiesHandler());
152
xmlReader.parse(new InputSource(in));
156
throw new ConfigurationException("Unable to parse the configuration file", e);
159
// todo: support included properties ?
163
public void save(Writer out) throws ConfigurationException
165
PrintWriter writer = new PrintWriter(out);
167
String encoding = getEncoding() != null ? getEncoding() : DEFAULT_ENCODING;
168
writer.println("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>");
169
writer.println("<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">");
170
writer.println("<properties>");
172
if (getHeader() != null)
174
writer.println(" <comment>" + StringEscapeUtils.escapeXml(getHeader()) + "</comment>");
177
Iterator<String> keys = getKeys();
178
while (keys.hasNext())
180
String key = keys.next();
181
Object value = getProperty(key);
183
if (value instanceof List)
185
writeProperty(writer, key, (List<?>) value);
189
writeProperty(writer, key, value);
193
writer.println("</properties>");
200
* @param out the output stream
201
* @param key the key of the property
202
* @param value the value of the property
204
private void writeProperty(PrintWriter out, String key, Object value)
207
String k = StringEscapeUtils.escapeXml(key);
212
String v = StringEscapeUtils.escapeXml(String.valueOf(value));
213
v = StringUtils.replace(v, String.valueOf(getListDelimiter()), "\\" + getListDelimiter());
215
out.println(" <entry key=\"" + k + "\">" + v + "</entry>");
219
out.println(" <entry key=\"" + k + "\"/>");
224
* Write a list property.
226
* @param out the output stream
227
* @param key the key of the property
228
* @param values a list with all property values
230
private void writeProperty(PrintWriter out, String key, List<?> values)
232
for (Object value : values)
234
writeProperty(out, key, value);
239
* SAX Handler to parse a XML properties file.
241
* @author Alistair Young
244
private class XMLPropertiesHandler extends DefaultHandler
246
/** The key of the current entry being parsed. */
249
/** The value of the current entry being parsed. */
250
private StringBuilder value = new StringBuilder();
252
/** Indicates that a comment is being parsed. */
253
private boolean inCommentElement;
255
/** Indicates that an entry is being parsed. */
256
private boolean inEntryElement;
259
public void startElement(String uri, String localName, String qName, Attributes attrs)
261
if ("comment".equals(qName))
263
inCommentElement = true;
266
if ("entry".equals(qName))
268
key = attrs.getValue("key");
269
inEntryElement = true;
274
public void endElement(String uri, String localName, String qName)
276
if (inCommentElement)
278
// We've just finished a <comment> element so set the header
279
setHeader(value.toString());
280
inCommentElement = false;
285
// We've just finished an <entry> element, so add the key/value pair
286
addProperty(key, value.toString());
287
inEntryElement = false;
290
// Clear the element value buffer
291
value = new StringBuilder();
295
public void characters(char[] chars, int start, int length)
298
* We're currently processing an element. All character data from now until
299
* the next endElement() call will be the data for this element.
301
value.append(chars, start, length);