View Javadoc

1   /*
2    * Copyright 2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * &lt;?xml version="1.0"?>
40   * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
41   * &lt;properties>
42   *   &lt;comment>Description of the property list&lt;/comment>
43   *   &lt;entry key="key1">value1&lt;/entry>
44   *   &lt;entry key="key2">value2&lt;/entry>
45   *   &lt;entry key="key3">value3&lt;/entry>
46   * &lt;/properties>
47   * &lt;/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      // initialization block to set the encoding before loading the file in the constructors
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         // todo: replace with a pure SAX implementation to reduce the dependencies
125 
126         // set up the digester
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         // todo: support included properties ?
142 
143         // parse the file
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         // escape the key
195         String k = StringEscapeUtils.escapeXml(key);
196 
197         if (value != null)
198         {
199             // escape the value
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 }