Source code: com/gersonworks/xml/util/XMLProperties.java
1 /*
2 * XMLProperties library - A utility class which reads property lists from
3 * an XML document
4 * Copyright (C) 21 January 2004 Gerson Galang
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * For any questions or suggestions, you can email me at :
19 * Email: gerson721@gawab.com
20 */
21
22 package com.gersonworks.xml.util;
23
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.HashMap;
27 import java.util.Set;
28 import java.util.Properties;
29 import java.util.Map.Entry;
30
31 import java.io.File;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.io.Writer;
35 import java.io.PrintWriter;
36
37 /**
38 * The XMLProperties class represents a way of storing and retrieving properties
39 * data into and from an XML file. Each property key corresponds to an
40 * XMLPropertyValues object which may contain one or more property values.
41 * <p>
42 * Since the idea of implementing this class came from how the
43 * java.util.Properties class works, most of the methods provided by this class
44 * are similar to the functionality provided by the java.util.Properties'
45 * methods.
46 * <p>
47 * The main difference of this class from java.util.Properties class is that
48 * a property in XMLProperties may contain one or more property values. Another
49 * difference is that this class now reads in an XML document.
50 * <p>
51 * The order of how the keys were originally sorted cannot be guaranteed when
52 * the loaded property list is stored back into the XML properties file. It was
53 * implemented this way for simplicity. What I can only guarantee is that all
54 * the keys and its corresponding value/s will be in the properties file.
55 * <p>
56 * The XMLProperties class reads an XML document of the following format:
57 *
58 * <pre>
59 * <properties>
60 * <!-- This is my XML configuration file -->
61 * <property key="key1">
62 * <value>key1Value1</value>
63 * <value>key1Value2</value>
64 * </property>
65 * <property key="key2">
66 * <value></value>
67 * </property>
68 * </properties>
69 * </pre>
70 *
71 * XMLProperties can only recognize the following elements:
72 * <p>
73 * <ol>
74 * <li>
75 * <tt>properties</tt> - is the <tt>root</tt> element of the XML
76 * properties document. This element is usually composed of more than
77 * one <tt>property</tt> elements but it is also possible for this
78 * element to be an empty element.
79 * </li>
80 * <li>
81 * <tt>property</tt> - is the child of the properties element and the
82 * parent of the value element. The <tt>property</tt> element has a
83 * <tt>key</tt> attribute which serves as the key to the property values.
84 * The <tt>property</tt> element is only allowed to have the
85 * <tt>value</tt> element as its child. It cannot contain any text (or
86 * simple) contents under it.
87 * </li>
88 * <li>
89 * <tt>value</tt> - is the child of the property element. There should be
90 * at least one <tt>value</tt> element for each <tt>property</tt>
91 * element. The <tt>value</tt> element can be an empty element but it
92 * cannot contain any other elements except a text (or a simple) content.
93 * </li>
94 * </ol>
95 * <p>
96 * The rules specified by each element should be followed to avoid
97 * XMLPropertiesFormatException to be thrown. This exception is always thrown
98 * whenever an unrecognized element is inserted into the XML properties
99 * document.
100 *
101 * <p>Copyright: Copyright (c) 21 January 2004</p>
102 * @author Gerson Galang
103 * @version 1.1, 29 January 2004
104 * @see java.util.Properties
105 */
106 public class XMLProperties {
107
108 // the map wrapped by this class
109 private Map xmlProperties;
110
111 protected XMLProperties defaultXMLProps;
112
113 /**
114 * Creates an empty property list with no defaults.
115 */
116 public XMLProperties() {
117 xmlProperties = new HashMap();
118 defaultXMLProps = null;
119 }
120
121 /**
122 * Creates an empty property list with a default XMLProperties.
123 *
124 * @param defaultXMLProps the default XMLProperties
125 * @since XMLProperties version 1.1
126 */
127 public XMLProperties(XMLProperties defaultXMLProps) {
128 xmlProperties = new HashMap();
129 this.defaultXMLProps = defaultXMLProps;
130 }
131
132 /**
133 * Creates an empty property list with a default java.util.Properties.
134 *
135 * @param defaultJavaProps the default java.util.Properties
136 * @since XMLProperties version 1.1
137 */
138 public XMLProperties(Properties defaultJavaProps) {
139 xmlProperties = new HashMap();
140 defaultXMLProps = new XMLProperties();
141 Set mapEntrySet = defaultJavaProps.entrySet();
142 // Map's putAll() method cannot be used in adding the properties
143 // into this class' property list because java.util.Properties has
144 // a different format for its property value.
145 for (Iterator mapIterator = mapEntrySet.iterator();
146 mapIterator.hasNext(); ) {
147 Map.Entry mapEntry = (Map.Entry)mapIterator.next();
148 defaultXMLProps.setProperty(mapEntry.getKey().toString(),
149 mapEntry.getValue().toString());
150 }
151 }
152
153 /**
154 * Locates the property values for the corresponding key.
155 *
156 * @param key the property key.
157 * @return the property values of the input key, or null if key is not found.
158 */
159 public XMLPropertyValues getPropertyValues(String key) {
160 return getPropertyValues(key, null);
161 }
162
163 /**
164 * Locates the property values for the corresponding key. If the key is not
165 * found, the search returns the default XMLPropertyValues parameter of this
166 * method.
167 *
168 * @param key the property key.
169 * @param defaultValues the default XMLPropertyValues object to be returned
170 * if the key is not found.
171 * @return the property values of the input key or the defaultValues if the
172 * key is not found.
173 */
174 public XMLPropertyValues getPropertyValues(String key,
175 XMLPropertyValues defaultValues) {
176 XMLPropertyValues temp = (XMLPropertyValues)xmlProperties.get(key);
177 if (temp == null) {
178 if (defaultXMLProps != null) {
179 // check defaultXMLProps. it might contain the property values for
180 // the specified property key
181 return defaultXMLProps.getPropertyValues(key, defaultValues);
182 } else {
183 // if nothing is returned and defaultXMLProps is null, return
184 // defaultValues
185 return defaultValues;
186 }
187 }
188 return temp;
189 }
190
191 /**
192 * Locates the property value for the corresponding key and property value's
193 * index. If the property key is found but the index is not within the
194 * property values' range, the method will throw an XMLPropertiesException.
195 *
196 * @param key the property key.
197 * @param index a number from 0 to XMLProperyValues.size() - 1. This is the
198 * index of the value in the property values collection.
199 * @return the property value which corresponds to the given key and index.
200 */
201 public String getPropertyValue(String key, int index)
202 throws XMLPropertiesException {
203 XMLPropertyValues temp = getPropertyValues(key);
204 if (temp == null) {
205 if (defaultXMLProps != null) {
206 // check defaultXMLProps. it might contain the property values for
207 // the specified property key
208 return defaultXMLProps.getPropertyValue(key, index);
209 } else {
210 // return null if key has not been found
211 return null;
212 }
213 }
214 // check if index is within the property values' range
215 try {
216 return temp.getValue(index);
217 } catch (IndexOutOfBoundsException e) {
218 // wrap IndexOutOfBoundsException in XMLPropertiesException
219 throw new XMLPropertiesException("Index is not within the range");
220 }
221 }
222
223 /**
224 * Locates the property value for the corresponding key and property value's
225 * index. This method will return the defaultValue if the property key is not
226 * found, or if the value's index is not within the range of the found
227 * propertyValues.
228 *
229 * @param key the property key.
230 * @param index is a number from 0 to XMLProperyValues.size() - 1. This is the
231 * index of the value in the property values collection.
232 * @param defaultValue the default value to be returned.
233 * @return the found value, or the default value if the key is not found.
234 */
235 public String getPropertyValue(String key, int index,
236 String defaultValue) {
237 XMLPropertyValues temp = getPropertyValues(key);
238 if (temp == null) {
239 if (defaultXMLProps != null) {
240 // check defaultXMLProps. it might contain the property values for
241 // the specified property key
242 return defaultXMLProps.getPropertyValue(key, index, defaultValue);
243 } else {
244 // return null if key has not been found
245 return defaultValue;
246 }
247 }
248 // check if index is within the list's range
249 try {
250 return temp.getValue(index);
251 } catch (IndexOutOfBoundsException e) {
252 // the value at index has not been found, just return the defaultValue
253 return defaultValue;
254 }
255 }
256
257 /**
258 * Reads the property list key-value/s pair from the properties file.
259 *
260 * @param propertiesFile the XMLProperties file.
261 */
262 public synchronized void load(File propertiesFile) {
263 XMLPropertiesHandler handler = new XMLPropertiesHandler(propertiesFile);
264 xmlProperties = handler.getProperties();
265 }
266
267 /**
268 * Reads the property list of key-value/s pair from the properties URI.
269 *
270 * @param propertiesURI the URI of the XMLProperties.
271 */
272 public synchronized void load(String propertiesURI) {
273 XMLPropertiesHandler handler = new XMLPropertiesHandler(propertiesURI);
274 xmlProperties = handler.getProperties();
275 }
276
277 /**
278 * Reads the property list of key-value/s pairs from the input stream
279 *
280 * @param inStream the InputStream containing the XMLProperties.
281 * @since XMLProperties version 1.1
282 */
283 public synchronized void load(InputStream inStream) {
284 XMLPropertiesHandler handler = new XMLPropertiesHandler(inStream);
285 xmlProperties = handler.getProperties();
286 }
287
288 /**
289 * Returns an iterator of all the keys in the property list.
290 *
291 * @return an iterator of all the keys in the property list.
292 */
293 public synchronized Iterator getPropertyNames() {
294 return xmlProperties.keySet().iterator();
295 }
296
297 /**
298 * Puts the key-value pair parameter into the property list. If the argument
299 * key exists in the property list, the old XMLPropertyValues object will
300 * be returned, otherwise null will be returned.
301 *
302 * @param key the property key.
303 * @param value the property value.
304 * @return the old XMLPropertyValues object if key used to be in the property
305 * list, or null otherwise.
306 */
307 public synchronized XMLPropertyValues setProperty(String key, String value) {
308 // add the value to the XMLPropertyValues object first
309 XMLPropertyValues tempValues = new XMLPropertyValues();
310 tempValues.addValue(value);
311 return (XMLPropertyValues)xmlProperties.put(key, tempValues);
312 }
313
314 /**
315 * Puts the key-values pair parameter into the property list. If the argument
316 * key exists in the property list, the old XMLPropertyValues object will
317 * be returned, otherwise null will be returned.
318 *
319 * @param key the property key
320 * @param values the property values
321 * @return the old XMLPropertyValues object if key used to be in the property
322 * list, or null otherwise.
323 */
324 public synchronized XMLPropertyValues setProperty(String key,
325 XMLPropertyValues values) {
326 return (XMLPropertyValues)xmlProperties.put(key, values);
327 }
328
329 /**
330 * Writes the property list wrapped by this class into a Writer object.
331 *
332 * @param writer a Writer object.
333 */
334 public synchronized void store(Writer writer) {
335 store(writer, null);
336 }
337
338 /**
339 * Writes the property list wrapped by this class into the output stream.
340 *
341 * @param outStream the output stream.
342 * @since XMLProperties version 1.1
343 */
344 public synchronized void store(OutputStream outStream) {
345 store(new PrintWriter(outStream), null);
346 }
347
348 /**
349 * Writes the property list wrapped by this class into the output stream.
350 * This method adds additional header information to the output through the
351 * use of the headerComment.
352 *
353 * @param outStream the output stream.
354 * @param headerComment the header comment going to be written in the output
355 * stream.
356 * @since XMLProperties version 1.1
357 */
358 public synchronized void store(OutputStream outStream, String headerComment) {
359 store(new PrintWriter(outStream), headerComment);
360 }
361
362
363 /**
364 * Writes the property list wrapped by this class into a Writer object.
365 * This method adds additional header information to the output through the
366 * use of the headerComment.
367 *
368 * @param writer a Writer object.
369 * @param headerComment the header comment going to be written in the output.
370 */
371 public synchronized void store(Writer writer, String headerComment) {
372 PrintWriter printWriter = new PrintWriter(writer);
373 printWriter.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?> ");
374 if (headerComment != null) {
375 printWriter.println("<!-- " + headerComment + " -->");
376 }
377 printWriter.println("<properties>");
378 Set keySet = xmlProperties.keySet();
379 for (Iterator keyIter = keySet.iterator(); keyIter.hasNext(); ) {
380 String propertyKey = keyIter.next().toString();
381 XMLPropertyValues propertyValues =
382 (XMLPropertyValues)xmlProperties.get(propertyKey);
383 printWriter.println(" <property key=\"" + propertyKey + "\">");
384 for (Iterator valueIter = propertyValues.iterator(); valueIter.hasNext(); ) {
385 // modified this section of the code on version 1.2
386 // reason: i just like the xml properties document in this format
387 printWriter.println(" <value>" + valueIter.next() + "</value>");
388 }
389 printWriter.println(" </property>");
390 }
391 printWriter.println("</properties>");
392 printWriter.flush();
393 }
394
395
396
397
398 /**
399 * Returns an iterator of all XMLPropertyValues objects in the property list.
400 *
401 * @return an iterator of all the XMLPropertyValues objects in the property
402 * list.
403 * @since XMLProperties version 1.1
404 */
405 public synchronized Iterator getPropertyValuesIterator() {
406 return xmlProperties.values().iterator();
407 }
408
409 /**
410 * This method converts this class to its equivalent java.util.Properties
411 * object. It should be noted that only the first element (or propery value)
412 * of all the XMLPropertyValues objects in this class' property list will
413 * be stored in the output java.util.Properties object. The property list
414 * of the defaults are also not stored in the output java.util.Properties
415 * object.
416 *
417 * @return the equivalent java.util.Properties object of this object.
418 * @since XMLProperties version 1.1
419 */
420 public synchronized Properties getJavaUtilProperties() {
421 Properties tempProperties = new Properties();
422 Set keySet = xmlProperties.keySet();
423 for (Iterator keyIter = keySet.iterator(); keyIter.hasNext(); ) {
424 String propertyKey = keyIter.next().toString();
425 // Map's putAll() method cannot be used because only the first value
426 // of the propertyValues object should be added to java.util.Properties'
427 // property list.
428 tempProperties.setProperty(propertyKey, getPropertyValue(propertyKey, 0));
429 }
430 return tempProperties;
431 }
432
433 /**
434 * Removes all the entries from the property list.
435 *
436 * @since XMLProperties version 1.1
437 */
438 public void clear() {
439 xmlProperties.clear();
440 }
441
442 /**
443 * Checks if the specified key is in the property list.
444 *
445 * @param key the key to be checked.
446 * @return true if the key is in the property list, false otherwise.
447 * @since XMLProperties version 1.1
448 */
449 public boolean containsKey(String key) {
450 return xmlProperties.containsKey(key);
451 }
452
453 /**
454 * Returns true if XMLProperties maps one or more keys for this value.
455 *
456 * @param value the value stored in an XMLPropertyValues object
457 * @return true if the value is found in any of the property values mapped
458 * by any of the keys in the property list.
459 * @since XMLProperties version 1.1
460 */
461 public synchronized boolean containsValue(String value) {
462 // go through all the property values objects stored by xmlProperties
463 for (Iterator propsValuesIter = getPropertyValuesIterator();
464 propsValuesIter.hasNext(); ) {
465 for (Iterator propsValueIter =
466 ((XMLPropertyValues)propsValuesIter.next()).iterator();
467 propsValueIter.hasNext(); ) {
468 if (propsValueIter.next().toString().equals(value)) {
469 return true;
470 }
471 }
472 }
473 return false;
474 }
475
476 /**
477 * Returns true if the property list is empty.
478 *
479 * @return true if the property list is empty, false otherwise.
480 * @since XMLProperties version 1.1
481 */
482 public boolean isEmpty() {
483 return xmlProperties.isEmpty();
484 }
485
486 /**
487 * Removes the property specified by the property key.
488 *
489 * @param key the key of the property to be removed
490 * @return the XMLPropertyValues object mapped by the specified key, null
491 * othewise.
492 * @since XMLProperties version 1.1
493 */
494 public XMLPropertyValues remove(String key) {
495 return (XMLPropertyValues)xmlProperties.remove(key);
496 }
497
498 /**
499 * Returns the number of elements in the property list.
500 *
501 * @return the number of entries in the property list.
502 * @since XMLProperties version 1.1
503 */
504 public int size() {
505 return xmlProperties.size();
506 }
507
508 }