1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package xni.parser; 19 20 import java.io.FileInputStream; 21 import java.io.InputStream; 22 import java.io.IOException; 23 import java.net.MalformedURLException; 24 import java.net.URL; 25 import java.util.Hashtable; 26 import java.util.Locale; 27 import java.util.Vector; 28 29 import org.apache.xerces.xni.XMLDocumentHandler; 30 import org.apache.xerces.xni.XMLDTDHandler; 31 import org.apache.xerces.xni.XMLDTDContentModelHandler; 32 import org.apache.xerces.xni.XNIException; 33 34 import org.apache.xerces.xni.parser.XMLComponent; 35 import org.apache.xerces.xni.parser.XMLConfigurationException; 36 import org.apache.xerces.xni.parser.XMLEntityResolver; 37 import org.apache.xerces.xni.parser.XMLErrorHandler; 38 import org.apache.xerces.xni.parser.XMLInputSource; 39 import org.apache.xerces.xni.parser.XMLParserConfiguration; 40 41 /** 42 * This abstract parser configuration simply helps manage components, 43 * features and properties, and other tasks common to all parser 44 * configurations. In order to subclass this configuration and use 45 * it effectively, the subclass is required to do the following: 46 * <ul> 47 * <li> 48 * Add all configurable components using the <code>addComponent</code> 49 * method,</li> 50 * <li>Implement the <code>parse</code> method, and</li> 51 * <li>Call the <code>resetComponents</code> before parsing.</li> 52 * </ul> 53 * 54 * @author Andy Clark, IBM 55 * 56 * @version $Id: AbstractConfiguration.java 447690 2006-09-19 02:41:53Z mrglavas $ 57 */ 58 public abstract class AbstractConfiguration 59 implements XMLParserConfiguration { 60 61 // 62 // Data 63 // 64 65 // features and properties 66 67 /** Recognized features. */ 68 protected final Vector fRecognizedFeatures = new Vector(); 69 70 /** Recognized properties. */ 71 protected final Vector fRecognizedProperties = new Vector(); 72 73 /** Features. */ 74 protected final Hashtable fFeatures = new Hashtable(); 75 76 /** Properties. */ 77 protected final Hashtable fProperties = new Hashtable(); 78 79 // other parser configuration fields 80 81 /** The registered entity resolver. */ 82 protected XMLEntityResolver fEntityResolver; 83 84 /** The registered error handler. */ 85 protected XMLErrorHandler fErrorHandler; 86 87 /** The registered document handler. */ 88 protected XMLDocumentHandler fDocumentHandler; 89 90 /** The registered DTD handler. */ 91 protected XMLDTDHandler fDTDHandler; 92 93 /** The registered DTD content model handler. */ 94 protected XMLDTDContentModelHandler fDTDContentModelHandler; 95 96 /** Locale for error messages. */ 97 protected Locale fLocale; 98 99 // components 100 101 /** List of configurable components. */ 102 protected final Vector fComponents = new Vector(); 103 104 // 105 // XMLParserConfiguration methods 106 // 107 108 /** 109 * Allows a parser to add parser specific features to be recognized 110 * and managed by the parser configuration. 111 * 112 * @param featureIds An array of the additional feature identifiers 113 * to be recognized. 114 */ 115 public void addRecognizedFeatures(String[] featureIds) { 116 int length = featureIds != null ? featureIds.length : 0; 117 for (int i = 0; i < length; i++) { 118 String featureId = featureIds[i]; 119 if (!fRecognizedFeatures.contains(featureId)) { 120 fRecognizedFeatures.addElement(featureId); 121 } 122 } 123 } // addRecognizedFeatures(String[]) 124 125 /** 126 * Sets the state of a feature. This method is called by the parser 127 * and gets propagated to components in this parser configuration. 128 * 129 * @param featureId The feature identifier. 130 * @param state The state of the feature. 131 * 132 * @throws XMLConfigurationException Thrown if there is a configuration 133 * error. 134 */ 135 public void setFeature(String featureId, boolean state) 136 throws XMLConfigurationException { 137 if (!fRecognizedFeatures.contains(featureId)) { 138 short type = XMLConfigurationException.NOT_RECOGNIZED; 139 throw new XMLConfigurationException(type, featureId); 140 } 141 fFeatures.put(featureId, state ? Boolean.TRUE : Boolean.FALSE); 142 int length = fComponents.size(); 143 for (int i = 0; i < length; i++) { 144 XMLComponent component = (XMLComponent)fComponents.elementAt(i); 145 component.setFeature(featureId, state); 146 } 147 } // setFeature(String,boolean) 148 149 /** 150 * Returns the state of a feature. 151 * 152 * @param featureId The feature identifier. 153 * 154 * @throws XMLConfigurationException Thrown if there is a configuration 155 * error. 156 */ 157 public boolean getFeature(String featureId) 158 throws XMLConfigurationException { 159 if (!fRecognizedFeatures.contains(featureId)) { 160 short type = XMLConfigurationException.NOT_RECOGNIZED; 161 throw new XMLConfigurationException(type, featureId); 162 } 163 Boolean state = (Boolean)fFeatures.get(featureId); 164 return state != null ? state.booleanValue() : false; 165 } // getFeature(String):boolean 166 167 /** 168 * Allows a parser to add parser specific properties to be recognized 169 * and managed by the parser configuration. 170 * 171 * @param propertyIds An array of the additional property identifiers 172 * to be recognized. 173 */ 174 public void addRecognizedProperties(String[] propertyIds) { 175 int length = propertyIds != null ? propertyIds.length : 0; 176 for (int i = 0; i < length; i++) { 177 String propertyId = propertyIds[i]; 178 if (!fRecognizedProperties.contains(propertyId)) { 179 fRecognizedProperties.addElement(propertyId); 180 } 181 } 182 } // addRecognizedProperties(String[]) 183 184 /** 185 * Sets the value of a property. This method is called by the parser 186 * and gets propagated to components in this parser configuration. 187 * 188 * @param propertyId The property identifier. 189 * @param value The value of the property. 190 * 191 * @throws XMLConfigurationException Thrown if there is a configuration 192 * error. 193 */ 194 public void setProperty(String propertyId, Object value) 195 throws XMLConfigurationException { 196 if (!fRecognizedProperties.contains(propertyId)) { 197 short type = XMLConfigurationException.NOT_RECOGNIZED; 198 throw new XMLConfigurationException(type, propertyId); 199 } 200 if (value != null) { 201 fProperties.put(propertyId, value); 202 } 203 else { 204 fProperties.remove(propertyId); 205 } 206 int length = fComponents.size(); 207 for (int i = 0; i < length; i++) { 208 XMLComponent component = (XMLComponent)fComponents.elementAt(i); 209 component.setProperty(propertyId, value); 210 } 211 } // setProperty(String,Object) 212 213 /** 214 * Returns the value of a property. 215 * 216 * @param propertyId The property identifier. 217 * 218 * @throws XMLConfigurationException Thrown if there is a configuration 219 * error. 220 */ 221 public Object getProperty(String propertyId) 222 throws XMLConfigurationException { 223 if (!fRecognizedProperties.contains(propertyId)) { 224 short type = XMLConfigurationException.NOT_RECOGNIZED; 225 throw new XMLConfigurationException(type, propertyId); 226 } 227 Object value = fProperties.get(propertyId); 228 return value; 229 } // getProperty(String):Object 230 231 /** 232 * Sets the entity resolver. 233 * 234 * @param resolver The new entity resolver. 235 */ 236 public void setEntityResolver(XMLEntityResolver resolver) { 237 fEntityResolver = resolver; 238 } // setEntityResolver(XMLEntityResolver) 239 240 /** Returns the registered entity resolver. */ 241 public XMLEntityResolver getEntityResolver() { 242 return fEntityResolver; 243 } // getEntityResolver():XMLEntityResolver 244 245 /** 246 * Sets the error handler. 247 * 248 * @param handler The error resolver. 249 */ 250 public void setErrorHandler(XMLErrorHandler handler) { 251 fErrorHandler = handler; 252 } // setErrorHandler(XMLErrorHandler) 253 254 /** Returns the registered error handler. */ 255 public XMLErrorHandler getErrorHandler() { 256 return fErrorHandler; 257 } // getErrorHandler():XMLErrorHandler 258 259 /** 260 * Sets the document handler to receive information about the document. 261 * 262 * @param handler The document handler. 263 */ 264 public void setDocumentHandler(XMLDocumentHandler handler) { 265 fDocumentHandler = handler; 266 } // setDocumentHandler(XMLDocumentHandler) 267 268 /** Returns the registered document handler. */ 269 public XMLDocumentHandler getDocumentHandler() { 270 return fDocumentHandler; 271 } // getDocumentHandler():XMLDocumentHandler 272 273 /** 274 * Sets the DTD handler. 275 * 276 * @param handler The DTD handler. 277 */ 278 public void setDTDHandler(XMLDTDHandler handler) { 279 fDTDHandler = handler; 280 } // setDTDHandler(XMLDTDHandler) 281 282 /** Returns the registered DTD handler. */ 283 public XMLDTDHandler getDTDHandler() { 284 return fDTDHandler; 285 } // getDTDHandler():XMLDTDHandler 286 287 /** 288 * Sets the DTD content model handler. 289 * 290 * @param handler The DTD content model handler. 291 */ 292 public void setDTDContentModelHandler(XMLDTDContentModelHandler handler) { 293 fDTDContentModelHandler = handler; 294 } // setDTDContentModelHandler(XMLDTDContentModelHandler) 295 296 /** Returns the registered DTD content model handler. */ 297 public XMLDTDContentModelHandler getDTDContentModelHandler() { 298 return fDTDContentModelHandler; 299 } // getDTDContentModelHandler():XMLDTDContentModelHandler 300 301 /** 302 * Parse an XML document. 303 * <p> 304 * The parser can use this method to instruct this configuration 305 * to begin parsing an XML document from any valid input source 306 * (a character stream, a byte stream, or a URI). 307 * <p> 308 * Parsers may not invoke this method while a parse is in progress. 309 * Once a parse is complete, the parser may then parse another XML 310 * document. 311 * <p> 312 * This method is synchronous: it will not return until parsing 313 * has ended. If a client application wants to terminate 314 * parsing early, it should throw an exception. 315 * <p> 316 * <strong>Note:</strong> This method needs to be implemented 317 * by the subclass. 318 * 319 * @param source The input source for the top-level of the 320 * XML document. 321 * 322 * @exception XNIException Any XNI exception, possibly wrapping 323 * another exception. 324 * @exception IOException An IO exception from the parser, possibly 325 * from a byte stream or character stream 326 * supplied by the parser. 327 */ 328 public abstract void parse(XMLInputSource inputSource) 329 throws IOException, XNIException; 330 331 /** 332 * Set the locale to use for messages. 333 * 334 * @param locale The locale object to use for localization of messages. 335 * 336 * @exception XNIException Thrown if the parser does not support the 337 * specified locale. 338 */ 339 public void setLocale(Locale locale) { 340 fLocale = locale; 341 } // setLocale(Locale) 342 343 344 /** Returns the locale. */ 345 public Locale getLocale() { 346 return fLocale; 347 } // getLocale():Locale 348 349 // 350 // Protected methods 351 // 352 353 /** 354 * Adds a component to list of configurable components. If the 355 * same component is added multiple times, the component is 356 * added only the first time. 357 * <p> 358 * This method helps manage the components in the configuration. 359 * Therefore, all subclasses should call this method to add the 360 * components specific to the configuration. 361 * 362 * @param component The component to add. 363 * 364 * @see #resetComponents 365 */ 366 protected void addComponent(XMLComponent component) { 367 if (!fComponents.contains(component)) { 368 fComponents.addElement(component); 369 addRecognizedFeatures(component.getRecognizedFeatures()); 370 addRecognizedProperties(component.getRecognizedProperties()); 371 } 372 } // addComponent(XMLComponent) 373 374 /** 375 * Resets all of the registered components. Before the subclassed 376 * configuration begins parsing, it should call this method to 377 * reset the components. 378 * 379 * @see #addComponent 380 */ 381 protected void resetComponents() 382 throws XMLConfigurationException { 383 int length = fComponents.size(); 384 for (int i = 0; i < length; i++) { 385 XMLComponent component = (XMLComponent)fComponents.elementAt(i); 386 component.reset(this); 387 } 388 } // resetComponents() 389 390 /** 391 * This method tries to open the necessary stream for the given 392 * XMLInputSource. If the input source already has a character 393 * stream (java.io.Reader) or a byte stream (java.io.InputStream) 394 * set, this method returns immediately. However, if no character 395 * or byte stream is already open, this method attempts to open 396 * an input stream using the source's system identifier. 397 * 398 * @param source The input source to open. 399 */ 400 protected void openInputSourceStream(XMLInputSource source) 401 throws IOException { 402 if (source.getCharacterStream() != null) { 403 return; 404 } 405 InputStream stream = source.getByteStream(); 406 if (stream == null) { 407 String systemId = source.getSystemId(); 408 try { 409 URL url = new URL(systemId); 410 stream = url.openStream(); 411 } 412 catch (MalformedURLException e) { 413 stream = new FileInputStream(systemId); 414 } 415 source.setByteStream(stream); 416 } 417 } // openInputSourceStream(XMLInputSource) 418 419 } // class AbstractConfiguration