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} 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    }