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 &quot;faked&quot; <code>XMLReader</code> 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</code> 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 href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
041     * @version $Id: ConfigurationXMLReader.java 439648 2006-09-02 20:42:10Z oheger $
042     */
043    public abstract class ConfigurationXMLReader implements XMLReader
044    {
045        /** Constant for the namespace URI.*/
046        protected static final String NS_URI = "";
047    
048        /** Constant for the default name of the root element.*/
049        private static final String DEFAULT_ROOT_NAME = "config";
050    
051        /** An empty attributes object.*/
052        private static final Attributes EMPTY_ATTRS = new AttributesImpl();
053    
054        /** Stores the content handler.*/
055        private ContentHandler contentHandler;
056    
057        /** Stores an exception that occurred during parsing.*/
058        private SAXException exception;
059    
060        /** Stores the name for the root element.*/
061        private String rootName;
062    
063        /**
064         * Creates a new instance of <code>ConfigurationXMLReader</code>.
065         */
066        protected ConfigurationXMLReader()
067        {
068            super();
069            setRootName(DEFAULT_ROOT_NAME);
070        }
071    
072        /**
073         * Parses the acutal configuration object. The passed system ID will be
074         * ignored.
075         *
076         * @param systemId the system ID (ignored)
077         * @throws IOException if no configuration was specified
078         * @throws SAXException if an error occurs during parsing
079         */
080        public void parse(String systemId) throws IOException, SAXException
081        {
082            parseConfiguration();
083        }
084    
085        /**
086         * Parses the acutal configuration object. The passed input source will be
087         * ignored.
088         *
089         * @param input the input source (ignored)
090         * @throws IOException if no configuration was specified
091         * @throws SAXException if an error occurs during parsing
092         */
093        public void parse(InputSource input) throws IOException, SAXException
094        {
095            parseConfiguration();
096        }
097    
098        /**
099         * Dummy implementation of the interface method.
100         *
101         * @param name the name of the feature
102         * @return always <b>false</b> (no features are supported)
103         */
104        public boolean getFeature(String name)
105        {
106            return false;
107        }
108    
109        /**
110         * Dummy implementation of the interface method.
111         *
112         * @param name the name of the feature to be set
113         * @param value the value of the feature
114         */
115        public void setFeature(String name, boolean value)
116        {
117        }
118    
119        /**
120         * Returns the actually set content handler.
121         *
122         * @return the content handler
123         */
124        public ContentHandler getContentHandler()
125        {
126            return contentHandler;
127        }
128    
129        /**
130         * Sets the content handler. The object specified here will receive SAX
131         * events during parsing.
132         *
133         * @param handler the content handler
134         */
135        public void setContentHandler(ContentHandler handler)
136        {
137            contentHandler = handler;
138        }
139    
140        /**
141         * Returns the DTD handler. This class does not support DTD handlers,
142         * so this method always returns <b>null</b>.
143         *
144         * @return the DTD handler
145         */
146        public DTDHandler getDTDHandler()
147        {
148            return null;
149        }
150    
151        /**
152         * Sets the DTD handler. The passed value is ignored.
153         *
154         * @param handler the handler to be set
155         */
156        public void setDTDHandler(DTDHandler handler)
157        {
158        }
159    
160        /**
161         * Returns the entity resolver. This class does not support an entity
162         * resolver, so this method always returns <b>null</b>.
163         *
164         * @return the entity resolver
165         */
166        public EntityResolver getEntityResolver()
167        {
168            return null;
169        }
170    
171        /**
172         * Sets the entity resolver. The passed value is ignored.
173         *
174         * @param resolver the entity resolver
175         */
176        public void setEntityResolver(EntityResolver resolver)
177        {
178        }
179    
180        /**
181         * Returns the error handler. This class does not support an error handler,
182         * so this method always returns <b>null</b>.
183         *
184         * @return the error handler
185         */
186        public ErrorHandler getErrorHandler()
187        {
188            return null;
189        }
190    
191        /**
192         * Sets the error handler. The passed value is ignored.
193         *
194         * @param handler the error handler
195         */
196        public void setErrorHandler(ErrorHandler handler)
197        {
198        }
199    
200        /**
201         * Dummy implementation of the interface method. No properties are
202         * supported, so this method always returns <b>null</b>.
203         *
204         * @param name the name of the requested property
205         * @return the property value
206         */
207        public Object getProperty(String name)
208        {
209            return null;
210        }
211    
212        /**
213         * Dummy implementation of the interface method. No properties are
214         * supported, so a call of this method just has no effect.
215         *
216         * @param name the property name
217         * @param value the property value
218         */
219        public void setProperty(String name, Object value)
220        {
221        }
222    
223        /**
224         * Returns the name to be used for the root element.
225         *
226         * @return the name for the root element
227         */
228        public String getRootName()
229        {
230            return rootName;
231        }
232    
233        /**
234         * Sets the name for the root element.
235         *
236         * @param string the name for the root element.
237         */
238        public void setRootName(String string)
239        {
240            rootName = string;
241        }
242    
243        /**
244         * Fires a SAX element start event.
245         *
246         * @param name the name of the actual element
247         * @param attribs the attributes of this element (can be <b>null</b>)
248         */
249        protected void fireElementStart(String name, Attributes attribs)
250        {
251            if (getException() == null)
252            {
253                try
254                {
255                    Attributes at = (attribs == null) ? EMPTY_ATTRS : attribs;
256                    getContentHandler().startElement(NS_URI, name, name, at);
257                }
258                catch (SAXException ex)
259                {
260                    exception = ex;
261                }
262            }
263        }
264    
265        /**
266         * Fires a SAX element end event.
267         *
268         * @param name the name of the affected element
269         */
270        protected void fireElementEnd(String name)
271        {
272            if (getException() == null)
273            {
274                try
275                {
276                    getContentHandler().endElement(NS_URI, name, name);
277                }
278                catch (SAXException ex)
279                {
280                    exception = ex;
281                }
282            }
283        }
284    
285        /**
286         * Fires a SAX characters event.
287         *
288         * @param text the text
289         */
290        protected void fireCharacters(String text)
291        {
292            if (getException() == null)
293            {
294                try
295                {
296                    char[] ch = text.toCharArray();
297                    getContentHandler().characters(ch, 0, ch.length);
298                }
299                catch (SAXException ex)
300                {
301                    exception = ex;
302                }
303            }
304        }
305    
306        /**
307         * Returns a reference to an exception that occurred during parsing.
308         *
309         * @return a SAXExcpetion or <b>null</b> if none occurred
310         */
311        public SAXException getException()
312        {
313            return exception;
314        }
315    
316        /**
317         * Parses the configuration object and generates SAX events. This is the
318         * main processing method.
319         *
320         * @throws IOException if no configuration has been specified
321         * @throws SAXException if an error occurs during parsing
322         */
323        protected void parseConfiguration() throws IOException, SAXException
324        {
325            if (getParsedConfiguration() == null)
326            {
327                throw new IOException("No configuration specified!");
328            }
329    
330            if (getContentHandler() != null)
331            {
332                exception = null;
333                getContentHandler().startDocument();
334                processKeys();
335                if (getException() != null)
336                {
337                    throw getException();
338                }
339                getContentHandler().endDocument();
340            }
341        }
342    
343        /**
344         * Returns a reference to the configuration that is parsed by this object.
345         *
346         * @return the parsed configuration
347         */
348        public abstract Configuration getParsedConfiguration();
349    
350        /**
351         * Processes all keys stored in the actual configuration. This method is
352         * called by <code>parseConfiguration()</code> to start the main parsing
353         * process. <code>parseConfiguration()</code> calls the content handler's
354         * <code>startDocument()</code> and <code>endElement()</code> methods
355         * and cares for exception handling. The remaining actions are left to this
356         * method that must be implemented in a concrete sub class.
357         *
358         * @throws IOException if an IO error occurs
359         * @throws SAXException if a SAX error occurs
360         */
361        protected abstract void processKeys() throws IOException, SAXException;
362    }