1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration;
18
19 import java.io.File;
20 import java.io.PrintWriter;
21 import java.io.Reader;
22 import java.io.Writer;
23 import java.net.URL;
24 import java.util.Iterator;
25 import java.util.List;
26
27 import org.apache.commons.digester.Digester;
28 import org.apache.commons.lang.StringEscapeUtils;
29 import org.apache.commons.lang.StringUtils;
30 import org.xml.sax.EntityResolver;
31 import org.xml.sax.InputSource;
32
33 /***
34 * This configuration implements the XML properties format introduced in Java
35 * 5.0, see http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html.
36 * An XML properties file looks like this:
37 *
38 * <pre>
39 * <?xml version="1.0"?>
40 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
41 * <properties>
42 * <comment>Description of the property list</comment>
43 * <entry key="key1">value1</entry>
44 * <entry key="key2">value2</entry>
45 * <entry key="key3">value3</entry>
46 * </properties>
47 * </pre>
48 *
49 * The Java 5.0 runtime is not required to use this class. The default encoding
50 * for this configuration format is UTF-8. Note that unlike
51 * <code>PropertiesConfiguration</code>, <code>XMLPropertiesConfiguration</code>
52 * does not support includes.
53 *
54 * @since 1.1
55 *
56 * @author Emmanuel Bourg
57 * @version $Revision: 155408 $, $Date: 2005-02-26 13:56:39 +0100 (Sa, 26 Feb 2005) $
58 */
59 public class XMLPropertiesConfiguration extends PropertiesConfiguration
60 {
61 /***
62 * The default encoding (UTF-8 as specified by http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html)
63 */
64 private static final String DEFAULT_ENCODING = "UTF-8";
65
66
67 {
68 setEncoding(DEFAULT_ENCODING);
69 }
70
71 /***
72 * Creates an empty XMLPropertyConfiguration object which can be
73 * used to synthesize a new Properties file by adding values and
74 * then saving(). An object constructed by this C'tor can not be
75 * tickled into loading included files because it cannot supply a
76 * base for relative includes.
77 */
78 public XMLPropertiesConfiguration()
79 {
80 super();
81 }
82
83 /***
84 * Creates and loads the xml properties from the specified file.
85 * The specified file can contain "include" properties which then
86 * are loaded and merged into the properties.
87 *
88 * @param fileName The name of the properties file to load.
89 * @throws ConfigurationException Error while loading the properties file
90 */
91 public XMLPropertiesConfiguration(String fileName) throws ConfigurationException
92 {
93 super(fileName);
94 }
95
96 /***
97 * Creates and loads the xml properties from the specified file.
98 * The specified file can contain "include" properties which then
99 * are loaded and merged into the properties.
100 *
101 * @param file The properties file to load.
102 * @throws ConfigurationException Error while loading the properties file
103 */
104 public XMLPropertiesConfiguration(File file) throws ConfigurationException
105 {
106 super(file);
107 }
108
109 /***
110 * Creates and loads the xml properties from the specified URL.
111 * The specified file can contain "include" properties which then
112 * are loaded and merged into the properties.
113 *
114 * @param url The location of the properties file to load.
115 * @throws ConfigurationException Error while loading the properties file
116 */
117 public XMLPropertiesConfiguration(URL url) throws ConfigurationException
118 {
119 super(url);
120 }
121
122 public void load(Reader in) throws ConfigurationException
123 {
124
125
126
127 Digester digester = new Digester();
128 digester.setEntityResolver(new EntityResolver(){
129 public InputSource resolveEntity(String publicId, String systemId)
130 {
131 return new InputSource(getClass().getClassLoader().getResourceAsStream("properties.dtd"));
132 }
133 });
134
135 digester.addCallMethod("properties/comment", "setHeader", 0);
136
137 digester.addCallMethod("properties/entry", "addProperty", 2);
138 digester.addCallParam("properties/entry", 0, "key");
139 digester.addCallParam("properties/entry", 1);
140
141
142
143
144 digester.push(this);
145 try
146 {
147 digester.parse(in);
148 }
149 catch (Exception e)
150 {
151 throw new ConfigurationException("Unable to parse the configuration file", e);
152 }
153 }
154
155 public void save(Writer out) throws ConfigurationException
156 {
157 PrintWriter writer = new PrintWriter(out);
158
159 String encoding = getEncoding() != null ? getEncoding() : DEFAULT_ENCODING;
160 writer.println("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>");
161 writer.println("<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">");
162 writer.println("<properties>");
163
164 if (getHeader() != null)
165 {
166 writer.println(" <comment>" + StringEscapeUtils.escapeXml(getHeader()) + "</comment>");
167 }
168
169 Iterator keys = getKeys();
170 while (keys.hasNext())
171 {
172 String key = (String) keys.next();
173 Object value = getProperty(key);
174
175 if (value instanceof List)
176 {
177 writeProperty(writer, key, (List) value);
178 }
179 else
180 {
181 writeProperty(writer, key, value);
182 }
183 }
184
185 writer.println("</properties>");
186 writer.flush();
187 }
188
189 /***
190 * Write a property.
191 */
192 private void writeProperty(PrintWriter out, String key, Object value)
193 {
194
195 String k = StringEscapeUtils.escapeXml(key);
196
197 if (value != null)
198 {
199
200 String v = StringEscapeUtils.escapeXml(String.valueOf(value));
201 v = StringUtils.replace(v, String.valueOf(getDelimiter()), "//" + getDelimiter());
202
203 out.println(" <entry key=\"" + k + "\">" + v + "</entry>");
204 }
205 else
206 {
207 out.println(" <entry key=\"" + k + "\"/>");
208 }
209 }
210
211 /***
212 * Write a list property.
213 */
214 private void writeProperty(PrintWriter out, String key, List values)
215 {
216 for (int i = 0; i < values.size(); i++)
217 {
218 writeProperty(out, key, values.get(i));
219 }
220 }
221 }