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

Quick Search    Search Deep

Source code: com/gersonworks/xml/util/XMLPropertiesHandler.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.io.PrintWriter;
25  import java.io.File;
26  import java.io.InputStream;
27  
28  import org.xml.sax.*;
29  import org.xml.sax.helpers.DefaultHandler;
30  
31  import javax.xml.parsers.SAXParserFactory;
32  import javax.xml.parsers.ParserConfigurationException;
33  import javax.xml.parsers.SAXParser;
34  
35  import java.util.Map;
36  import java.util.HashMap;
37  
38  /**
39   * The XMLPropertiesHandler class acts as a handler to the XMLProperties file
40   * while it is being parsed by the SAX Parser. Most of the methods provided
41   * by this class only handles events wherein the SAX parser has entered and
42   * left the property and value elements.
43   * <p>
44   * This is not a public class because only the XMLProperties object directly
45   * accesses it.
46   *
47   * <p>Copyright: Copyright (c) 21 January 2004</p>
48   * @author Gerson Galang
49   * @version 1.1, 29 January 2004
50   */
51  class XMLPropertiesHandler extends DefaultHandler {
52  
53    // the map where the property list is going to be stored
54    private Map xmlProperties;
55  
56    // temporary holder of property values
57    private XMLPropertyValues propertyValues;
58  
59    // true if inside the value element
60    private boolean insideValueElement = false;
61  
62    // where the logs are going to written
63    private PrintWriter log = new PrintWriter(System.out);
64  
65    private String propertyKey = null;  // temporary property key
66    private String propertyValue = null;  // temporary property value
67  
68    private boolean verbose = false;  // used for debugging purposes
69  
70    /**
71     * Constructs an XMLPropertiesHandler for the given File instance of the XML
72     * document to be parsed.
73     *
74     * @param file the file going to be parsed by the SAXParser.
75     */
76    public XMLPropertiesHandler(File file) {
77      constructXMLPropertiesHandler(file);
78    }
79  
80    /**
81     * Constructs an XMLPropertiesHandler for the URI of the XML document to be
82     * parsed.
83     *
84     * @param uri the URI of the XML document which is going to be parsed by the
85     * SAXParser.
86     */
87    public XMLPropertiesHandler(String uri) {
88      constructXMLPropertiesHandler(uri);
89    }
90  
91    /**
92     * Constructs an XMLPropertiesHandler for the given InputStream as the XML
93     * document to be parsed.
94     *
95     * @param inStream the input stream containing the XML document to be parsed.
96     * @since XMLProperties version 1.1
97     */
98    public XMLPropertiesHandler(InputStream inStream) {
99      constructXMLPropertiesHandler(inStream);
100   }
101 
102   /**
103    * A special purpose method to lessen the number of lines I need to write
104    * especially if the only difference between the two contructors is the way
105    * the SAXParser.parse() method is called.
106    *
107    * @param o either a file or a string
108    */
109   private void constructXMLPropertiesHandler(Object o) {
110     // Use the default (non-validating) parser
111     SAXParserFactory factory = SAXParserFactory.newInstance();
112     xmlProperties = new HashMap();
113     try {
114       // Parse the input
115       SAXParser saxParser = factory.newSAXParser();
116 
117       // get the xmlReader wrapped by the saxParser object so that this class
118       // may be able to handle errors generated by the saxParser.
119       org.xml.sax.XMLReader xmlReader = saxParser.getXMLReader();
120       xmlReader.setErrorHandler(this);
121 
122       //xmlReader.setContentHandler(this);
123       //xmlReader.parse(
124       //    new org.xml.sax.InputSource(new java.io.FileReader(file)));
125 
126       // the code below are the only
127       if (o instanceof File) {
128         File file = (File) o;
129         verbosePrintln("Parsing file: " + file.getAbsolutePath());
130         saxParser.parse(file, this);
131 
132       }
133       else if (o instanceof String) {
134         String uri = o.toString(); // get the URI string
135         verbosePrintln("Parsing uri: " + uri);
136         saxParser.parse(uri, this);
137       }
138       else if (o instanceof InputStream) {
139         InputStream inStream = (InputStream) o; // get the input stream
140         verbosePrintln("Parsing inputStream... ");
141         saxParser.parse(inStream, this);
142       }
143       else {
144         // this should never be thrown because the user of this library doesn't
145         // have an access to this class.
146         throw new XMLPropertiesException(
147             "XMLPropertiesHandler.constructXMLPropertiesHandler()'s parameter "
148             + "is not of type File or String or InputStream");
149       }
150     }
151     catch (Throwable t) {
152       System.err.println(t.getMessage());
153       t.printStackTrace();
154     } finally {
155       log.flush();
156     }
157   }
158 
159   /**
160    * Returns the property list (key-values pair) generated by this handler.
161    *
162    * @return the property list (key-values pair) generated by this handler.
163    */
164   public Map getProperties() {
165     return xmlProperties;
166   }
167 
168   //===========================================================
169   // SAX DocumentHandler methods
170   //===========================================================
171 
172   /**
173    * Used only for debugging purposes
174    *
175    * @throws SAXException
176    */
177   public void startDocument() throws SAXException {
178     verbosePrintln("Entering properties document.");
179   }
180 
181   /**
182    * Used only for debugging purposes
183    *
184    * @throws SAXException
185    */
186   public void endDocument() throws SAXException {
187     verbosePrintln("Leaving properties document.");
188   }
189 
190   /**
191    * Overrides the startElement() method of the DefaultHandler class.
192    *
193    * @param namespaceURI
194    * @param sName
195    * @param qName
196    * @param attrs
197    * @throws XMLPropertiesFormatException thrown when an unrecognized element
198    * has been inserted into the XML properties document. Or, the key attribute
199    * is missing from the property element.
200    */
201   public void startElement(String namespaceURI,
202                            String sName, // simple name
203                            String qName, // qualified name
204                            Attributes attrs) throws SAXException {
205 
206     // if inside the property element, instantiate a new XMLPropertyValues
207     // object
208     if (sName.equals("property") || qName.equals("property")) {
209       propertyKey = attrs.getValue("key");
210       if (propertyKey == null) {
211         throw new XMLPropertiesFormatException("Key attribute missing in " +
212           "property element.", null);
213       }
214       verbosePrintln("Entering property element: " + propertyKey);
215       propertyValues = new XMLPropertyValues();
216     }
217     else if (sName.equals("value") || qName.equals("value")) {
218       insideValueElement = true;
219       verbosePrint("Entering value element: ");
220     }
221     else if (sName.equals("properties") || qName.equals("properties")) {
222       // do nothing
223     }
224     else {
225       // the checking of the XML document should be hard-coded in here to avoid
226       // the trouble of using a DTD or an XML Schema. the format of the XML
227       // document the XMLProperties class is very simple so there is no need to
228       // use a DTD or an XML schema.
229       throw new XMLPropertiesFormatException("Unrecognized <" +
230             (sName.length() > 1 ? sName : qName) +"> element " +
231             "found.", null);
232     }
233   }
234 
235   /**
236    * Overrides the endElement() method of the DefaultHandler class.
237    *
238    * @param namespaceURI
239    * @param sName
240    * @param qName
241    * @throws XMLPropertiesFormatException thrown when the value element
242    * has not been found inside the property element.
243    */
244   public void endElement(String namespaceURI,
245                          String sName, // simple name
246                          String qName // qualified name
247                          ) throws SAXException {
248 
249     // if leaving the property element, don't forget to reset the temporary
250     // property key and temporary property values
251     if (sName.equals("property") || qName.equals("property")) {
252 
253       // don't allow a property element not to have a property value element.
254       // it is important that the user, types-in the correct format of the
255       // properties file.
256       if (propertyKey != null && propertyValues.isEmpty()) {
257         throw new XMLPropertiesFormatException(
258             "Missing values for property element with propertyKey=\"" +
259             propertyKey + "\".", null);
260       }
261       xmlProperties.put(propertyKey, propertyValues);
262       verbosePrintln("Leaving property element: " + propertyKey);
263 
264       // just to be sure... set these variables' values to null
265       propertyKey = null;
266       propertyValues = null;
267     }
268 
269     if (sName.equals("value") || qName.equals("value")) {
270       verbosePrintln(propertyValue);
271 
272       // modified the following line in version 1.2. i was having some problems
273       // with xml property files having this format:
274       // <value>
275       //    value1
276       // </value>
277       // the old line did not remove the extra CR/LF and spaces so if value1 is
278       // converted to an integer or a double, a NumberFormatException will be
279       // thrown
280       propertyValues.addValue(propertyValue.trim());
281       
282       verbosePrintln("Leaving value element: " + propertyValue);
283 
284       // need to set this to false so that characters outside this element
285       // will not be allowed.
286       insideValueElement = false;
287 
288       propertyValue = null;  // reset the propertyValue to null just to be safe
289     }
290   }
291 
292   /**
293    * Creates a string from the text found inside the value element.
294    *
295    * @param ch
296    * @param start
297    * @param length
298    * @throws XMLPropertiesFormatException thrown if normal texts and not
299    * comments are found inside the property element.
300    */
301   public void characters(char ch[], int start, int length) throws SAXException {
302     propertyValue = new String(ch, start, length);
303     if (!insideValueElement && !propertyValue.trim().equals("")) {
304       String errorMsg = null;
305       if (propertyKey != null) {
306         errorMsg = "the property element with propertyKey=\""
307             + propertyKey + "\".";
308       }
309       else {
310         errorMsg = "the properties element.";
311       }
312 
313       throw new XMLPropertiesFormatException(
314           "Illegal \""+ propertyValue.trim() +"\" characters found inside the "
315           + errorMsg, null);
316     }
317   }
318 
319   /**
320    * Outputs a detailed warning message.
321    *
322    * @param ex
323    */
324   public void warning(SAXParseException ex)
325       throws XMLPropertiesFormatException {
326     throw new XMLPropertiesFormatException("Warning: " + ex.getMessage() +
327               " at line " + ex.getLineNumber() + " column " +
328               ex.getColumnNumber() + detailedMessage(), null);
329 
330   }
331 
332   /**
333    * Outputs a detailed error message.
334    *
335    * @param ex
336    */
337   public void error(SAXParseException ex)
338       throws XMLPropertiesFormatException {
339     throw new XMLPropertiesFormatException("Error: " + ex.getMessage() +
340               " at line " + ex.getLineNumber() + " column " +
341               ex.getColumnNumber() + detailedMessage(), null);
342   }
343 
344   /**
345    * Outputs a detailed fatal error message.
346    *
347    * @param ex
348    */
349   public void fatalError(SAXParseException ex)
350       throws XMLPropertiesFormatException {
351 
352     throw new XMLPropertiesFormatException("Fatal error: " + ex.getMessage() +
353               " at line " + ex.getLineNumber() + " column " +
354               ex.getColumnNumber() + detailedMessage(), null);
355   }
356 
357   /**
358    * Returns the key-value pair when the error occured.
359    *
360    * @return
361    */
362   public String detailedMessage() {
363     return "\nPropertyKey = " + propertyKey
364         + "\nPropertyValue = " + propertyValue;
365   }
366 
367   /**
368    * For debugging purposes.
369    *
370    * @param str
371    */
372   private void verbosePrint(String str) {
373     if (verbose) {
374       log.print(str);
375     }
376   }
377 
378   /**
379    * For debugging purposes.
380    *
381    * @param str
382    */
383   private void verbosePrintln(String str) {
384     if (verbose) {
385       log.println(str);
386     }
387   }
388 
389 }