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