View Javadoc

1   /*
2    * Copyright (C) The DNA Group. All rights reserved.
3    *
4    * This software is published under the terms of the DNA
5    * Software License version 1.1, a copy of which has been included
6    * with this distribution in the LICENSE.txt file.
7    */
8   package org.codehaus.dna.impl;
9   
10  import java.util.ArrayList;
11  import java.util.List;
12  
13  import org.codehaus.dna.Configuration;
14  import org.xml.sax.Attributes;
15  import org.xml.sax.Locator;
16  import org.xml.sax.SAXException;
17  import org.xml.sax.SAXParseException;
18  import org.xml.sax.helpers.DefaultHandler;
19  
20  /***
21   * The SAXConfigurationHandler builds a Configuration tree
22   * from SAX events.
23   *
24   * @author Peter Donald
25   * @version $Revision: 1.2 $ $Date: 2004/05/01 09:51:48 $
26   */
27  public class SAXConfigurationHandler
28      extends DefaultHandler
29  {
30      /***
31       * Empty string used for padding out contents array.
32       */
33      private static final String EMPTY_STRING = "";
34  
35      /***
36       * Constant to indicate location of
37       * element when parser does not support Locator
38       * interface.
39       */
40      private static final String UNKNOWN = "";
41  
42      /***
43       * Stack of configuration elements currently being
44       * constructed.
45       */
46      private final List m_elements = new ArrayList();
47  
48      /***
49       * Stakc of content text for elements currently being
50       * constructed.
51       */
52      private final ArrayList m_values = new ArrayList();
53  
54      /***
55       * The configuration element created.
56       */
57      private Configuration m_configuration;
58  
59      /***
60       * The Locator specified by XML parser.
61       */
62      private Locator m_locator;
63  
64      /***
65       * Let the XML parser specify locator for when
66       * events arrive at handler.
67       *
68       * @param locator the locator
69       */
70      public void setDocumentLocator( final Locator locator )
71      {
72          m_locator = locator;
73      }
74  
75      /***
76       * Reset internal state of handler in preapration for reuse.
77       */
78      public void clear()
79      {
80          m_elements.clear();
81          m_values.clear();
82          m_locator = null;
83      }
84  
85      /***
86       * Return the configuration created by handler.
87       *
88       * @return the configuration created by handler.
89       */
90      public Configuration getConfiguration()
91      {
92          return m_configuration;
93      }
94  
95      /***
96       * Start an element and thus a Configuration object.
97       *
98       * @param uri the uri (ignored)
99       * @param localName the localName (ignored)
100      * @param qName the qualified name (used for name of configuration)
101      * @param attributes the attributes of XML element
102      * @throws SAXException if unable to parse element
103      */
104     public void startElement( final String uri,
105                               final String localName,
106                               final String qName,
107                               final Attributes attributes )
108         throws SAXException
109     {
110         DefaultConfiguration parent = null;
111         String path = ConfigurationUtil.ROOT_PATH;
112         if( m_elements.size() > 0 )
113         {
114             final int index = m_elements.size() - 1;
115             parent =
116                 (DefaultConfiguration)m_elements.get( index );
117             path = ConfigurationUtil.
118                 generatePathName( parent.getPath(),
119                                   parent.getName() );
120         }
121         final DefaultConfiguration configuration =
122             new DefaultConfiguration( qName, getLocationDescription(), path );
123         if( null != parent )
124         {
125             parent.addChild( configuration );
126         }
127         final int length = attributes.getLength();
128         for( int i = 0; i < length; i++ )
129         {
130             final String key = attributes.getQName( i );
131             final String value = attributes.getValue( i );
132             final String newValue =
133                 processAttributeText( configuration, key, value );
134             configuration.setAttribute( key, newValue );
135         }
136 
137         m_elements.add( configuration );
138     }
139 
140     /***
141      * End an element and thus a Configuration object.
142      * Will pop of configuration and value of object from
143      * stack. If the handler detects that element has both
144      * child elements and a text value then it will throw
145      * a SAXException.
146      *
147      * @param uri the uri (ignored)
148      * @param localName the localName (ignored)
149      * @param qName the qualified name (used for name of configuration)
150      * @throws SAXException if element had mixed content
151      */
152     public void endElement( final String uri,
153                             final String localName,
154                             final String qName )
155         throws SAXException
156     {
157         final int index = m_elements.size() - 1;
158         final DefaultConfiguration configuration =
159             (DefaultConfiguration)m_elements.remove( index );
160         if( index < m_values.size() )
161         {
162             final String value = m_values.remove( index ).toString();
163             if( 0 != value.trim().length() )
164             {
165                 if( 0 == configuration.getChildren().length )
166                 {
167                     final String newValue =
168                         processValueText( configuration, value );
169                     configuration.setValue( newValue );
170                 }
171                 else
172                 {
173                     final String message =
174                         "Mixed content (" + value.trim() + ") " +
175                         "not supported @ " + getLocationDescription();
176                     throw new SAXException( message );
177                 }
178             }
179         }
180         m_configuration = configuration;
181     }
182 
183     /***
184      * Receive text data for current element.
185      *
186      * @param ch the char array
187      * @param start the start index
188      * @param length the length of data
189      * @throws SAXException if unable ot parse data
190      */
191     public void characters( final char[] ch,
192                             final int start,
193                             final int length )
194         throws SAXException
195     {
196         final int index = m_elements.size() - 1;
197         StringBuffer sb = null;
198         if( index < m_values.size() )
199         {
200             sb = (StringBuffer)m_values.get( index );
201         }
202         if( null == sb )
203         {
204             sb = new StringBuffer();
205             final int minCapacity = index + 1;
206             m_values.ensureCapacity( minCapacity );
207             final int size = m_values.size();
208             for( int i = size; i < minCapacity; i++ )
209             {
210                 m_values.add( EMPTY_STRING );
211             }
212             m_values.set( index, sb );
213         }
214         sb.append( ch, start, length );
215     }
216 
217     /***
218      * Rethrow exception and dont attempt to do
219      * any error handling.
220      *
221      * @param spe the input exception
222      * @throws SAXException always thrown
223      */
224     public void warning( final SAXParseException spe )
225         throws SAXException
226     {
227         throw spe;
228     }
229 
230     /***
231      * Rethrow exception and dont attempt to do
232      * any error handling.
233      *
234      * @param spe the input exception
235      * @throws SAXException always thrown
236      */
237     public void error( final SAXParseException spe )
238         throws SAXException
239     {
240         throw spe;
241     }
242 
243     /***
244      * Rethrow exception and dont attempt to do
245      * any error handling.
246      *
247      * @param spe the input exception
248      * @throws SAXException always thrown
249      */
250     public void fatalError( final SAXParseException spe )
251         throws SAXException
252     {
253         throw spe;
254     }
255 
256     /***
257      * Utility method to derive current location of
258      * XML parser. Attempts to build up a string containing
259      * systemID:lineNumber:columnNumber such as
260      * "file.xml:20:3" if parser supports all fields.
261      *
262      * @return the location description
263      */
264     protected final String getLocationDescription()
265     {
266         if( null == m_locator ||
267             null == m_locator.getSystemId() )
268         {
269             return UNKNOWN;
270         }
271         else if( -1 == m_locator.getLineNumber() )
272         {
273             return m_locator.getSystemId();
274         }
275         else if( -1 == m_locator.getColumnNumber() )
276         {
277             return m_locator.getSystemId() + ":" +
278                 m_locator.getLineNumber();
279         }
280         else
281         {
282             return m_locator.getSystemId() + ':' +
283                 m_locator.getLineNumber() + ':' +
284                 m_locator.getColumnNumber();
285         }
286     }
287 
288     /***
289      * Users may subclass this method to process attribute
290      * prior to it being set.
291      *
292      * @param configuration the associated configuration
293      * @param name the attribute name
294      * @param value the attribute value
295      * @return the attribute value
296      */
297     protected String processAttributeText( final Configuration configuration,
298                                            final String name,
299                                            final String value )
300     {
301         return value;
302     }
303 
304     /***
305      * Users may subclass this method to process content
306      * prior to it being set.
307      *
308      * @param configuration the associated configuration
309      * @param value the value
310      * @return the value
311      */
312     protected String processValueText( final Configuration configuration,
313                                        final String value )
314     {
315         return value;
316     }
317 }