Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: hk/hku/cecid/phoenix/common/util/XMLProperty.java


1   /*
2    * Academic Free License
3    * Version 1.0
4    *
5    * This Academic Free License applies to any software and associated 
6    * documentation (the "Software") whose owner (the "Licensor") has placed the 
7    * statement "Licensed under the Academic Free License Version 1.0" immediately 
8    * after the copyright notice that applies to the Software. 
9    *
10   * Permission is hereby granted, free of charge, to any person obtaining a copy 
11   * of the Software (1) to use, copy, modify, merge, publish, perform, 
12   * distribute, sublicense, and/or sell copies of the Software, and to permit 
13   * persons to whom the Software is furnished to do so, and (2) under patent 
14   * claims owned or controlled by the Licensor that are embodied in the Software 
15   * as furnished by the Licensor, to make, use, sell and offer for sale the 
16   * Software and derivative works thereof, subject to the following conditions: 
17   *
18   * - Redistributions of the Software in source code form must retain all 
19   *   copyright notices in the Software as furnished by the Licensor, this list 
20   *   of conditions, and the following disclaimers. 
21   * - Redistributions of the Software in executable form must reproduce all 
22   *   copyright notices in the Software as furnished by the Licensor, this list 
23   *   of conditions, and the following disclaimers in the documentation and/or 
24   *   other materials provided with the distribution. 
25   * - Neither the names of Licensor, nor the names of any contributors to the 
26   *   Software, nor any of their trademarks or service marks, may be used to 
27   *   endorse or promote products derived from this Software without express 
28   *   prior written permission of the Licensor. 
29   *
30   * DISCLAIMERS: LICENSOR WARRANTS THAT THE COPYRIGHT IN AND TO THE SOFTWARE IS 
31   * OWNED BY THE LICENSOR OR THAT THE SOFTWARE IS DISTRIBUTED BY LICENSOR UNDER 
32   * A VALID CURRENT LICENSE. EXCEPT AS EXPRESSLY STATED IN THE IMMEDIATELY 
33   * PRECEDING SENTENCE, THE SOFTWARE IS PROVIDED BY THE LICENSOR, CONTRIBUTORS 
34   * AND COPYRIGHT OWNERS "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
35   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
36   * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 
37   * LICENSOR, CONTRIBUTORS OR COPYRIGHT OWNERS BE LIABLE FOR ANY CLAIM, DAMAGES 
38   * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
39   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE. 
40   *
41   * This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. 
42   * Permission is hereby granted to copy and distribute this license without 
43   * modification. This license may not be modified without the express written 
44   * permission of its copyright owner. 
45   */
46  
47  /* ===== 
48   *
49   * $Header: /ebxml/staff/cecid/cvs_repository/common/src/hk/hku/cecid/phoenix/common/util/XMLProperty.java,v 1.9 2002/12/13 03:59:03 kcyee Exp $
50   *
51   * Code authored by:
52   *
53   * kcyee [2002-05-28]
54   *
55   * Code reviewed by:
56   *
57   * username [YYYY-MM-DD]
58   *
59   * Remarks:
60   *
61   * =====
62   */
63  
64  package hk.hku.cecid.phoenix.common.util;
65  
66  import java.io.File;
67  import java.io.FileWriter;
68  import java.io.IOException;
69  import java.util.ArrayList;
70  import java.util.List;
71  import java.util.StringTokenizer;
72  import org.jdom.Document;
73  import org.jdom.Element;
74  import org.jdom.JDOMException;
75  import org.jdom.Text;
76  import org.jdom.input.SAXBuilder;
77  import org.jdom.output.XMLOutputter;
78  
79  /**
80   * This is a concrete implementation of the property object for loading
81   * and saving the property content into a XML-based file. The file is
82   * assumed to be rooted by a "Property" element. And the get/set path
83   * is simplified XPath, with "/" as separator. For example, a path of
84   * "A/B/C" and value of "test" creates a tree like:
85   * <br>
86   * <br>
87   * &lt;Property&gt; <br>
88   * &nbsp;&nbsp;&nbsp;&lt;A&gt; <br>
89   * &nbsp;&nbsp;&nbsp;&lt;B&gt; <br>
90   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;C&gt;test&lt;/C&gt; <br>
91   * &nbsp;&nbsp;&nbsp;&lt;/B&gt; <br>
92   * &nbsp;&nbsp;&lt;/A&gt; <br>
93   * &lt;/Property&gt; <br>
94   * <br>
95   *
96   * @author kcyee
97   * @version $Revision: 1.9 $
98   */
99  public class XMLProperty extends Property {
100 
101     /**
102      * Internal variable for holding the file name of the property file
103      */
104     protected String fileName;
105 
106     /**
107      * Internal variable for holding the DOM tree of the XML property object
108      */
109     protected Document doc;
110 
111     /**
112      * Internal constructor. Not to be called by user. This loads the
113      * content of the specified property file into memory.
114      *
115      * @param fileName the file name of the property file to be loaded
116      * @throws IOException when error occuring while loading
117      */
118     XMLProperty(String fileName) throws IOException {
119         this.fileName = fileName;
120         SAXBuilder builder = new SAXBuilder();
121         try {
122             doc = builder.build(new File(fileName));
123         }
124         catch (JDOMException e) {
125             throw new IOException("SAXBuilder cannot load '" + fileName 
126                 + "'.\n" + e.getMessage());
127         }
128         try {
129             if (!doc.getRootElement().getName().equals("Property")) {
130                 throw new IOException("Format of '" + fileName 
131                     + "' not correct: not starting with 'Property'.");
132             }
133         }
134         catch (Exception e) {
135             throw new IOException("Format of '" + fileName 
136                 + "' not correct.\n" + e.getMessage());
137         }
138     }
139 
140     /**
141      * Default constructor. This is for creating a brand new XML-based 
142      * property object.
143      */
144     public XMLProperty() {
145         this.fileName = null;
146         Element ele = new Element("Property");
147         doc = new Document(ele);
148     }
149 
150     /**
151      * Reloads the property file.
152      */
153     public void reload() {
154         super.reload();
155 
156         SAXBuilder builder = new SAXBuilder();
157         try {
158             doc = builder.build(new File(fileName));
159             if (!doc.getRootElement().getName().equals("Property")) {
160                 throw new IOException();
161             }
162         }
163         catch (Exception e) {
164             Element ele = new Element("Property");
165             doc = new Document(ele);
166         }
167     }
168 
169     /**
170      * Gets the property value given the path (key). Multiple values are
171      * referenced by the same path will be returned using a String array.
172      * Multiple values are searched only at leaf nodes.
173      *
174      * @param path the path (key) of the property
175      * @return the property values given the path (key)
176      */
177     public synchronized String[] getMultiple(String path) {
178         if (path == null) {
179             return null;
180         }
181 
182         Element curElement = doc.getRootElement();
183 
184         StringTokenizer st = new StringTokenizer(path, "/");
185         while (st.hasMoreElements()) {
186             String token = st.nextToken().trim();
187             if (token.equals("")) {
188                 continue;
189             }
190 
191             Element nextElement = getChildElement(curElement, token);
192             if (nextElement == null) {
193                 return null;
194             }
195             else {
196                 if (!st.hasMoreElements()) {
197                     // last level
198                     List children = curElement.getChildren(token);
199                     ArrayList values = new ArrayList();
200                     for (int i=0 ; i<children.size() ; i++) {
201                         values.add(((Element)children.get(i)).getText());
202                     }
203                     String [] ret = new String[values.size()];
204                     for (int i=0 ; i<values.size() ; i++) {
205                         ret[i] = (String) values.get(i);
206                     }
207                     return ret;
208                 }
209                 else {
210                     curElement = nextElement;
211                 }
212             }
213         }
214 
215         return null;
216     }
217 
218     /**
219      * Gets the property value given the path (key).
220      *
221      * @param path the path (key) of the property
222      * @return the property value given the path (key)
223      */
224     public synchronized String get(String path) {
225         return get(path, null);
226     }
227 
228     /**
229      * Gets the property value given the path (key). If the property value
230      * is not found, the default value passed in is returned.
231      *
232      * @param path the path (key) of the property
233      * @param defaultValue the value to be returned if the property value
234      *                     is not found
235      * @return the property value given the path (key)
236      */
237     public synchronized String get(String path, String defaultValue) {
238 
239         if (path == null) {
240             return null;
241         }
242         String ret = getFromCache(path);
243         if (ret == null) {
244             Element curElement = doc.getRootElement();
245     
246             StringTokenizer st = new StringTokenizer(path, "/");
247             while (st.hasMoreElements()) {
248                 String token = st.nextToken().trim();
249                 if (token.equals("")) {
250                     continue;
251                 }
252 
253                 Element nextElement = getChildElement(curElement, token);
254 
255                 if (nextElement == null) {
256                     ret = null;
257                     break;
258                 }
259                 else {
260                     curElement = nextElement;
261                 }
262     
263                 if (!st.hasMoreElements()) {
264                     // last level
265                     ret = curElement.getText();
266                     if (ret == null) {
267                         ret = defaultValue;
268                     }
269                 }
270             }
271 
272             if (ret == null && defaultValue != null) {
273                 ret = defaultValue;
274             }
275 
276             saveToCache(path, ret);
277         }
278         return ret;
279     }
280 
281     /**
282      * Sets the property value of the given path (key).
283      *
284      * @param path the path (key) of the property to be set
285      * @param value the property value to be set
286      */
287     public synchronized void set(String path, String value) {
288 
289         saveToCache(path, value);
290 
291         Element curElement = doc.getRootElement();
292 
293         StringTokenizer st = new StringTokenizer(path, "/");
294         while (st.hasMoreElements()) {
295             String token = st.nextToken().trim();
296             if (token.equals("")) {
297                 continue;
298             }
299 
300             Element nextElement = curElement.getChild(token);
301             if (nextElement == null) {
302                 nextElement = new Element(token);
303                 curElement.addContent(nextElement);
304             }
305             curElement = nextElement;
306 
307             if (!st.hasMoreElements()) {
308                 // last level
309                 curElement.setText(value);
310             }
311         }
312     }
313 
314     /**
315      * Saves the property object to the same location when loading.
316      *
317      * @throws IOException when error occurs when saving the property object
318      */
319     public synchronized void save() throws IOException {
320         save(fileName);
321     }
322 
323     /**
324      * Saves the property object to the specified location.
325      *
326      * @param location the location for saving the property object
327      * @throws IOException when error occurs when saving the property object
328      */
329     public synchronized void save(String fileName) throws IOException {
330         if (fileName == null) {
331             throw new IOException("Cannot save to <null>!");
332         }
333 
334         XMLOutputter outputter = new XMLOutputter("  ", true);
335         FileWriter fw = new FileWriter(fileName);
336         outputter.output(doc, fw);
337         fw.close();
338     }
339 
340     /**
341      * Gets the child element of the specified element, with the specified
342      * node name. The node name supports getting the nth child by using 
343      * node-name[n]. If there is anything wrong when parsing the node name,
344      * we will use abandon the parsing and fall back to assume the whole
345      * specified string as node name.
346      *
347      * @param parent the element to search for children
348      * @param token the node name, may be contains the square bracket to
349      *              specify which child to get
350      * @return the child element specified. If there is anything wrong,
351      *         null is returned.
352      */
353     protected Element getChildElement(Element parent, String token) {
354         if (token == null || parent == null) {
355             return null;
356         }
357 
358         token = token.trim();
359         String origToken = token;
360 
361         if (token.endsWith("]")) {
362             token = token.substring(0, token.length()-1);
363             int idx = token.indexOf("[");
364             if (idx > 0) {
365                 String idxStr = token.substring(idx+1).trim();
366                 token = token.substring(0, idx).trim();
367                 try {
368                     int elementIdx = Integer.parseInt(idxStr);
369                     if (elementIdx >= 0) {
370                         List children = parent.getChildren(token);
371                         if (elementIdx < children.size()) {
372                             return (Element) children.get(elementIdx);
373                         }
374                     }
375                 }
376                 catch (NumberFormatException e) {}
377             }
378         }
379         return parent.getChild(origToken);
380     }
381 }