001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.configuration;
019
020 import java.io.IOException;
021
022 import org.xml.sax.Attributes;
023 import org.xml.sax.ContentHandler;
024 import org.xml.sax.DTDHandler;
025 import org.xml.sax.EntityResolver;
026 import org.xml.sax.ErrorHandler;
027 import org.xml.sax.InputSource;
028 import org.xml.sax.SAXException;
029 import org.xml.sax.XMLReader;
030 import org.xml.sax.helpers.AttributesImpl;
031
032 /**
033 * <p>A base class for "faked" {@code XMLReader} classes
034 * that transform a configuration object in a set of SAX parsing events.</p>
035 * <p>This class provides dummy implementations for most of the methods
036 * defined in the {@code XMLReader} interface that are not used for this
037 * special purpose. There will be concrete sub classes that process specific
038 * configuration classes.</p>
039 *
040 * @author <a
041 * href="http://commons.apache.org/configuration/team-list.html">Commons
042 * Configuration team</a>
043 * @version $Id: ConfigurationXMLReader.java 1208805 2011-11-30 21:33:33Z oheger $
044 */
045 public abstract class ConfigurationXMLReader implements XMLReader
046 {
047 /** Constant for the namespace URI.*/
048 protected static final String NS_URI = "";
049
050 /** Constant for the default name of the root element.*/
051 private static final String DEFAULT_ROOT_NAME = "config";
052
053 /** An empty attributes object.*/
054 private static final Attributes EMPTY_ATTRS = new AttributesImpl();
055
056 /** Stores the content handler.*/
057 private ContentHandler contentHandler;
058
059 /** Stores an exception that occurred during parsing.*/
060 private SAXException exception;
061
062 /** Stores the name for the root element.*/
063 private String rootName;
064
065 /**
066 * Creates a new instance of {@code ConfigurationXMLReader}.
067 */
068 protected ConfigurationXMLReader()
069 {
070 super();
071 setRootName(DEFAULT_ROOT_NAME);
072 }
073
074 /**
075 * Parses the acutal configuration object. The passed system ID will be
076 * ignored.
077 *
078 * @param systemId the system ID (ignored)
079 * @throws IOException if no configuration was specified
080 * @throws SAXException if an error occurs during parsing
081 */
082 public void parse(String systemId) throws IOException, SAXException
083 {
084 parseConfiguration();
085 }
086
087 /**
088 * Parses the actual configuration object. The passed input source will be
089 * ignored.
090 *
091 * @param input the input source (ignored)
092 * @throws IOException if no configuration was specified
093 * @throws SAXException if an error occurs during parsing
094 */
095 public void parse(InputSource input) throws IOException, SAXException
096 {
097 parseConfiguration();
098 }
099
100 /**
101 * Dummy implementation of the interface method.
102 *
103 * @param name the name of the feature
104 * @return always <b>false</b> (no features are supported)
105 */
106 public boolean getFeature(String name)
107 {
108 return false;
109 }
110
111 /**
112 * Dummy implementation of the interface method.
113 *
114 * @param name the name of the feature to be set
115 * @param value the value of the feature
116 */
117 public void setFeature(String name, boolean value)
118 {
119 }
120
121 /**
122 * Returns the actually set content handler.
123 *
124 * @return the content handler
125 */
126 public ContentHandler getContentHandler()
127 {
128 return contentHandler;
129 }
130
131 /**
132 * Sets the content handler. The object specified here will receive SAX
133 * events during parsing.
134 *
135 * @param handler the content handler
136 */
137 public void setContentHandler(ContentHandler handler)
138 {
139 contentHandler = handler;
140 }
141
142 /**
143 * Returns the DTD handler. This class does not support DTD handlers,
144 * so this method always returns <b>null</b>.
145 *
146 * @return the DTD handler
147 */
148 public DTDHandler getDTDHandler()
149 {
150 return null;
151 }
152
153 /**
154 * Sets the DTD handler. The passed value is ignored.
155 *
156 * @param handler the handler to be set
157 */
158 public void setDTDHandler(DTDHandler handler)
159 {
160 }
161
162 /**
163 * Returns the entity resolver. This class does not support an entity
164 * resolver, so this method always returns <b>null</b>.
165 *
166 * @return the entity resolver
167 */
168 public EntityResolver getEntityResolver()
169 {
170 return null;
171 }
172
173 /**
174 * Sets the entity resolver. The passed value is ignored.
175 *
176 * @param resolver the entity resolver
177 */
178 public void setEntityResolver(EntityResolver resolver)
179 {
180 }
181
182 /**
183 * Returns the error handler. This class does not support an error handler,
184 * so this method always returns <b>null</b>.
185 *
186 * @return the error handler
187 */
188 public ErrorHandler getErrorHandler()
189 {
190 return null;
191 }
192
193 /**
194 * Sets the error handler. The passed value is ignored.
195 *
196 * @param handler the error handler
197 */
198 public void setErrorHandler(ErrorHandler handler)
199 {
200 }
201
202 /**
203 * Dummy implementation of the interface method. No properties are
204 * supported, so this method always returns <b>null</b>.
205 *
206 * @param name the name of the requested property
207 * @return the property value
208 */
209 public Object getProperty(String name)
210 {
211 return null;
212 }
213
214 /**
215 * Dummy implementation of the interface method. No properties are
216 * supported, so a call of this method just has no effect.
217 *
218 * @param name the property name
219 * @param value the property value
220 */
221 public void setProperty(String name, Object value)
222 {
223 }
224
225 /**
226 * Returns the name to be used for the root element.
227 *
228 * @return the name for the root element
229 */
230 public String getRootName()
231 {
232 return rootName;
233 }
234
235 /**
236 * Sets the name for the root element.
237 *
238 * @param string the name for the root element.
239 */
240 public void setRootName(String string)
241 {
242 rootName = string;
243 }
244
245 /**
246 * Fires a SAX element start event.
247 *
248 * @param name the name of the actual element
249 * @param attribs the attributes of this element (can be <b>null</b>)
250 */
251 protected void fireElementStart(String name, Attributes attribs)
252 {
253 if (getException() == null)
254 {
255 try
256 {
257 Attributes at = (attribs == null) ? EMPTY_ATTRS : attribs;
258 getContentHandler().startElement(NS_URI, name, name, at);
259 }
260 catch (SAXException ex)
261 {
262 exception = ex;
263 }
264 }
265 }
266
267 /**
268 * Fires a SAX element end event.
269 *
270 * @param name the name of the affected element
271 */
272 protected void fireElementEnd(String name)
273 {
274 if (getException() == null)
275 {
276 try
277 {
278 getContentHandler().endElement(NS_URI, name, name);
279 }
280 catch (SAXException ex)
281 {
282 exception = ex;
283 }
284 }
285 }
286
287 /**
288 * Fires a SAX characters event.
289 *
290 * @param text the text
291 */
292 protected void fireCharacters(String text)
293 {
294 if (getException() == null)
295 {
296 try
297 {
298 char[] ch = text.toCharArray();
299 getContentHandler().characters(ch, 0, ch.length);
300 }
301 catch (SAXException ex)
302 {
303 exception = ex;
304 }
305 }
306 }
307
308 /**
309 * Returns a reference to an exception that occurred during parsing.
310 *
311 * @return a SAXExcpetion or <b>null</b> if none occurred
312 */
313 public SAXException getException()
314 {
315 return exception;
316 }
317
318 /**
319 * Parses the configuration object and generates SAX events. This is the
320 * main processing method.
321 *
322 * @throws IOException if no configuration has been specified
323 * @throws SAXException if an error occurs during parsing
324 */
325 protected void parseConfiguration() throws IOException, SAXException
326 {
327 if (getParsedConfiguration() == null)
328 {
329 throw new IOException("No configuration specified!");
330 }
331
332 if (getContentHandler() != null)
333 {
334 exception = null;
335 getContentHandler().startDocument();
336 processKeys();
337 if (getException() != null)
338 {
339 throw getException();
340 }
341 getContentHandler().endDocument();
342 }
343 }
344
345 /**
346 * Returns a reference to the configuration that is parsed by this object.
347 *
348 * @return the parsed configuration
349 */
350 public abstract Configuration getParsedConfiguration();
351
352 /**
353 * Processes all keys stored in the actual configuration. This method is
354 * called by {@code parseConfiguration()} to start the main parsing
355 * process. {@code parseConfiguration()} calls the content handler's
356 * {@code startDocument()} and {@code endElement()} methods
357 * and cares for exception handling. The remaining actions are left to this
358 * method that must be implemented in a concrete sub class.
359 *
360 * @throws IOException if an IO error occurs
361 * @throws SAXException if a SAX error occurs
362 */
363 protected abstract void processKeys() throws IOException, SAXException;
364 }