1
2
3
4
5
6
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 }