1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * The Apache Software License, Version 1.1 7 * 8 * 9 * Copyright (c) 1999-2003 The Apache Software Foundation. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in 21 * the documentation and/or other materials provided with the 22 * distribution. 23 * 24 * 3. The end-user documentation included with the redistribution, 25 * if any, must include the following acknowledgment: 26 * "This product includes software developed by the 27 * Apache Software Foundation (http://www.apache.org/)." 28 * Alternately, this acknowledgment may appear in the software itself, 29 * if and wherever such third-party acknowledgments normally appear. 30 * 31 * 4. The names "Xerces" and "Apache Software Foundation" must 32 * not be used to endorse or promote products derived from this 33 * software without prior written permission. For written 34 * permission, please contact apache@apache.org. 35 * 36 * 5. Products derived from this software may not be called "Apache", 37 * nor may "Apache" appear in their name, without prior written 38 * permission of the Apache Software Foundation. 39 * 40 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 41 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 42 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 43 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 46 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 47 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 48 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 49 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 50 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * ==================================================================== 53 * 54 * This software consists of voluntary contributions made by many 55 * individuals on behalf of the Apache Software Foundation and was 56 * originally based on software copyright (c) 1999, International 57 * Business Machines, Inc., http://www.apache.org. For more 58 * information on the Apache Software Foundation, please see 59 * <http://www.apache.org/>. 60 */ 61 62 package com.sun.org.apache.xerces.internal.impl.dtd; 63 64 import java.io.EOFException; 65 import java.io.IOException; 66 import java.io.StringReader; 67 import java.util.Locale; 68 69 import com.sun.org.apache.xerces.internal.impl.Constants; 70 import com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl; 71 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; 72 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager; 73 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; 74 75 import com.sun.org.apache.xerces.internal.util.Status; 76 import com.sun.org.apache.xerces.internal.util.SymbolTable; 77 import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler; 78 79 import com.sun.org.apache.xerces.internal.xni.XNIException; 80 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool; 81 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarLoader; 82 import com.sun.org.apache.xerces.internal.xni.grammars.Grammar; 83 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 84 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler; 85 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver; 86 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; 87 88 89 /** 90 * The DTD loader. The loader knows how to build grammars from XMLInputSources. 91 * It extends the DTD processor in order to do this; it's 92 * a separate class because DTD processors don't need to know how 93 * to talk to the outside world in their role as instance-document 94 * helpers. 95 * <p> 96 * This component requires the following features and properties. It 97 * know ho to set them if no one else does:from the 98 * <ul> 99 * <li>http://xml.org/sax/features/namespaces</li> 100 * <li>http://apache.org/xml/properties/internal/symbol-table</li> 101 * <li>http://apache.org/xml/properties/internal/error-reporter</li> 102 * <li>http://apache.org/xml/properties/internal/grammar-pool</li> 103 * <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li> 104 * </ul> 105 * 106 * @xerces.internal 107 * 108 * @author Neil Graham, IBM 109 * @author Michael Glavassevich, IBM 110 * 111 */ 112 public class XMLDTDLoader 113 extends XMLDTDProcessor 114 implements XMLGrammarLoader { 115 116 // 117 // Constants 118 // 119 120 // feature identifiers 121 122 /** Feature identifier: standard uri conformant feature. */ 123 protected static final String STANDARD_URI_CONFORMANT_FEATURE = 124 Constants.XERCES_FEATURE_PREFIX + Constants.STANDARD_URI_CONFORMANT_FEATURE; 125 126 /** Feature identifier: balance syntax trees. */ 127 protected static final String BALANCE_SYNTAX_TREES = 128 Constants.XERCES_FEATURE_PREFIX + Constants.BALANCE_SYNTAX_TREES; 129 130 // recognized features: 131 private static final String[] LOADER_RECOGNIZED_FEATURES = { 132 VALIDATION, 133 WARN_ON_DUPLICATE_ATTDEF, 134 WARN_ON_UNDECLARED_ELEMDEF, 135 NOTIFY_CHAR_REFS, 136 STANDARD_URI_CONFORMANT_FEATURE, 137 BALANCE_SYNTAX_TREES 138 }; 139 140 // property identifiers 141 142 /** Property identifier: error handler. */ 143 protected static final String ERROR_HANDLER = 144 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY; 145 146 /** Property identifier: entity resolver. */ 147 public static final String ENTITY_RESOLVER = 148 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; 149 150 /** Property identifier: locale. */ 151 public static final String LOCALE = 152 Constants.XERCES_PROPERTY_PREFIX + Constants.LOCALE_PROPERTY; 153 154 /** Recognized properties. */ 155 private static final String[] LOADER_RECOGNIZED_PROPERTIES = { 156 SYMBOL_TABLE, 157 ERROR_REPORTER, 158 ERROR_HANDLER, 159 ENTITY_RESOLVER, 160 GRAMMAR_POOL, 161 DTD_VALIDATOR, 162 LOCALE 163 }; 164 165 // enforcing strict uri? 166 private boolean fStrictURI = false; 167 168 /** Controls whether the DTD grammar produces balanced syntax trees. */ 169 private boolean fBalanceSyntaxTrees = false; 170 171 /** Entity resolver . */ 172 protected XMLEntityResolver fEntityResolver; 173 174 // the scanner we use to actually read the DTD 175 protected XMLDTDScannerImpl fDTDScanner; 176 177 // the entity manager the scanner needs. 178 protected XMLEntityManager fEntityManager; 179 180 // what's our Locale? 181 protected Locale fLocale; 182 183 // 184 // Constructors 185 // 186 187 /** Deny default construction; we need a SymtolTable! */ 188 public XMLDTDLoader() { 189 this(new SymbolTable()); 190 } // <init>() 191 192 public XMLDTDLoader(SymbolTable symbolTable) { 193 this(symbolTable, null); 194 } // init(SymbolTable) 195 196 public XMLDTDLoader(SymbolTable symbolTable, 197 XMLGrammarPool grammarPool) { 198 this(symbolTable, grammarPool, null, new XMLEntityManager()); 199 } // init(SymbolTable, XMLGrammarPool) 200 201 XMLDTDLoader(SymbolTable symbolTable, 202 XMLGrammarPool grammarPool, XMLErrorReporter errorReporter, 203 XMLEntityResolver entityResolver) { 204 fSymbolTable = symbolTable; 205 fGrammarPool = grammarPool; 206 if(errorReporter == null) { 207 errorReporter = new XMLErrorReporter(); 208 errorReporter.setProperty(ERROR_HANDLER, new DefaultErrorHandler()); 209 } 210 fErrorReporter = errorReporter; 211 // Add XML message formatter if there isn't one. 212 if (fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) { 213 XMLMessageFormatter xmft = new XMLMessageFormatter(); 214 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft); 215 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft); 216 } 217 fEntityResolver = entityResolver; 218 if(fEntityResolver instanceof XMLEntityManager) { 219 fEntityManager = (XMLEntityManager)fEntityResolver; 220 } else { 221 fEntityManager = new XMLEntityManager(); 222 } 223 fEntityManager.setProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY, errorReporter); 224 fDTDScanner = createDTDScanner(fSymbolTable, fErrorReporter, fEntityManager); 225 fDTDScanner.setDTDHandler(this); 226 fDTDScanner.setDTDContentModelHandler(this); 227 reset(); 228 } // init(SymbolTable, XMLGrammarPool, XMLErrorReporter, XMLEntityResolver) 229 230 // XMLGrammarLoader methods 231 232 /** 233 * Returns a list of feature identifiers that are recognized by 234 * this component. This method may return null if no features 235 * are recognized by this component. 236 */ 237 public String[] getRecognizedFeatures() { 238 return (String[])(LOADER_RECOGNIZED_FEATURES.clone()); 239 } // getRecognizedFeatures():String[] 240 241 /** 242 * Sets the state of a feature. This method is called by the component 243 * manager any time after reset when a feature changes state. 244 * <p> 245 * <strong>Note:</strong> Components should silently ignore features 246 * that do not affect the operation of the component. 247 * 248 * @param featureId The feature identifier. 249 * @param state The state of the feature. 250 * 251 * @throws SAXNotRecognizedException The component should not throw 252 * this exception. 253 * @throws SAXNotSupportedException The component should not throw 254 * this exception. 255 */ 256 public void setFeature(String featureId, boolean state) 257 throws XMLConfigurationException { 258 if (featureId.equals(VALIDATION)) { 259 fValidation = state; 260 } 261 else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) { 262 fWarnDuplicateAttdef = state; 263 } 264 else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) { 265 fWarnOnUndeclaredElemdef = state; 266 } 267 else if (featureId.equals(NOTIFY_CHAR_REFS)) { 268 fDTDScanner.setFeature(featureId, state); 269 } 270 else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) { 271 fStrictURI = state; 272 } 273 else if (featureId.equals(BALANCE_SYNTAX_TREES)) { 274 fBalanceSyntaxTrees = state; 275 } 276 else { 277 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId); 278 } 279 } // setFeature(String,boolean) 280 281 /** 282 * Returns a list of property identifiers that are recognized by 283 * this component. This method may return null if no properties 284 * are recognized by this component. 285 */ 286 public String[] getRecognizedProperties() { 287 return (String[])(LOADER_RECOGNIZED_PROPERTIES.clone()); 288 } // getRecognizedProperties():String[] 289 290 /** 291 * Returns the state of a property. 292 * 293 * @param propertyId The property identifier. 294 * 295 * @throws XMLConfigurationException Thrown on configuration error. 296 */ 297 public Object getProperty(String propertyId) 298 throws XMLConfigurationException { 299 if (propertyId.equals(SYMBOL_TABLE)) { 300 return fSymbolTable; 301 } 302 else if (propertyId.equals(ERROR_REPORTER)) { 303 return fErrorReporter; 304 } 305 else if (propertyId.equals(ERROR_HANDLER)) { 306 return fErrorReporter.getErrorHandler(); 307 } 308 else if (propertyId.equals(ENTITY_RESOLVER)) { 309 return fEntityResolver; 310 } 311 else if (propertyId.equals(LOCALE)) { 312 return getLocale(); 313 } 314 else if (propertyId.equals(GRAMMAR_POOL)) { 315 return fGrammarPool; 316 } 317 else if (propertyId.equals(DTD_VALIDATOR)) { 318 return fValidator; 319 } 320 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, propertyId); 321 } // getProperty(String): Object 322 323 /** 324 * Sets the value of a property. This method is called by the component 325 * manager any time after reset when a property changes value. 326 * <p> 327 * <strong>Note:</strong> Components should silently ignore properties 328 * that do not affect the operation of the component. 329 * 330 * @param propertyId The property identifier. 331 * @param value The value of the property. 332 * 333 * @throws SAXNotRecognizedException The component should not throw 334 * this exception. 335 * @throws SAXNotSupportedException The component should not throw 336 * this exception. 337 */ 338 public void setProperty(String propertyId, Object value) 339 throws XMLConfigurationException { 340 if (propertyId.equals(SYMBOL_TABLE)) { 341 fSymbolTable = (SymbolTable)value; 342 fDTDScanner.setProperty(propertyId, value); 343 fEntityManager.setProperty(propertyId, value); 344 } 345 else if(propertyId.equals(ERROR_REPORTER)) { 346 fErrorReporter = (XMLErrorReporter)value; 347 // Add XML message formatter if there isn't one. 348 if (fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) { 349 XMLMessageFormatter xmft = new XMLMessageFormatter(); 350 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft); 351 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft); 352 } 353 fDTDScanner.setProperty(propertyId, value); 354 fEntityManager.setProperty(propertyId, value); 355 } 356 else if (propertyId.equals(ERROR_HANDLER)) { 357 fErrorReporter.setProperty(propertyId, value); 358 } 359 else if (propertyId.equals(ENTITY_RESOLVER)) { 360 fEntityResolver = (XMLEntityResolver)value; 361 fEntityManager.setProperty(propertyId, value); 362 } 363 else if (propertyId.equals(LOCALE)) { 364 setLocale((Locale) value); 365 } 366 else if(propertyId.equals(GRAMMAR_POOL)) { 367 fGrammarPool = (XMLGrammarPool)value; 368 } 369 else { 370 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, propertyId); 371 } 372 } // setProperty(String,Object) 373 374 /** 375 * Returns the state of a feature. 376 * 377 * @param featureId The feature identifier. 378 * 379 * @throws XMLConfigurationException Thrown on configuration error. 380 */ 381 public boolean getFeature(String featureId) 382 throws XMLConfigurationException { 383 if (featureId.equals(VALIDATION)) { 384 return fValidation; 385 } 386 else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) { 387 return fWarnDuplicateAttdef; 388 } 389 else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) { 390 return fWarnOnUndeclaredElemdef; 391 } 392 else if (featureId.equals(NOTIFY_CHAR_REFS)) { 393 return fDTDScanner.getFeature(featureId); 394 } 395 else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) { 396 return fStrictURI; 397 } 398 else if (featureId.equals(BALANCE_SYNTAX_TREES)) { 399 return fBalanceSyntaxTrees; 400 } 401 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId); 402 } //getFeature(String): boolean 403 404 /** 405 * Set the locale to use for messages. 406 * 407 * @param locale The locale object to use for localization of messages. 408 * 409 * @exception XNIException Thrown if the parser does not support the 410 * specified locale. 411 */ 412 public void setLocale(Locale locale) { 413 fLocale = locale; 414 fErrorReporter.setLocale(locale); 415 } // setLocale(Locale) 416 417 /** Return the Locale the XMLGrammarLoader is using. */ 418 public Locale getLocale() { 419 return fLocale; 420 } // getLocale(): Locale 421 422 423 /** 424 * Sets the error handler. 425 * 426 * @param errorHandler The error handler. 427 */ 428 public void setErrorHandler(XMLErrorHandler errorHandler) { 429 fErrorReporter.setProperty(ERROR_HANDLER, errorHandler); 430 } // setErrorHandler(XMLErrorHandler) 431 432 /** Returns the registered error handler. */ 433 public XMLErrorHandler getErrorHandler() { 434 return fErrorReporter.getErrorHandler(); 435 } // getErrorHandler(): XMLErrorHandler 436 437 /** 438 * Sets the entity resolver. 439 * 440 * @param entityResolver The new entity resolver. 441 */ 442 public void setEntityResolver(XMLEntityResolver entityResolver) { 443 fEntityResolver = entityResolver; 444 fEntityManager.setProperty(ENTITY_RESOLVER, entityResolver); 445 } // setEntityResolver(XMLEntityResolver) 446 447 /** Returns the registered entity resolver. */ 448 public XMLEntityResolver getEntityResolver() { 449 return fEntityResolver; 450 } // getEntityResolver(): XMLEntityResolver 451 452 /** 453 * Returns a Grammar object by parsing the contents of the 454 * entity pointed to by source. 455 * 456 * @param source the location of the entity which forms 457 * the starting point of the grammar to be constructed. 458 * @throws IOException When a problem is encountered reading the entity 459 * XNIException When a condition arises (such as a FatalError) that requires parsing 460 * of the entity be terminated. 461 */ 462 public Grammar loadGrammar(XMLInputSource source) 463 throws IOException, XNIException { 464 reset(); 465 // First chance checking strict URI 466 String eid = XMLEntityManager.expandSystemId(source.getSystemId(), source.getBaseSystemId(), fStrictURI); 467 XMLDTDDescription desc = new XMLDTDDescription(source.getPublicId(), source.getSystemId(), source.getBaseSystemId(), eid, null); 468 if (!fBalanceSyntaxTrees) { 469 fDTDGrammar = new DTDGrammar(fSymbolTable, desc); 470 } 471 else { 472 fDTDGrammar = new BalancedDTDGrammar(fSymbolTable, desc); 473 } 474 fGrammarBucket = new DTDGrammarBucket(); 475 fGrammarBucket.setStandalone(false); 476 fGrammarBucket.setActiveGrammar(fDTDGrammar); 477 // no reason to use grammar bucket's "put" method--we 478 // know which grammar it is, and we don't know the root name anyway... 479 480 // actually start the parsing! 481 try { 482 fDTDScanner.setInputSource(source); 483 fDTDScanner.scanDTDExternalSubset(true); 484 } catch (EOFException e) { 485 // expected behaviour... 486 } 487 finally { 488 // Close all streams opened by the parser. 489 fEntityManager.closeReaders(); 490 } 491 if(fDTDGrammar != null && fGrammarPool != null) { 492 fGrammarPool.cacheGrammars(XMLDTDDescription.XML_DTD, new Grammar[] {fDTDGrammar}); 493 } 494 return fDTDGrammar; 495 } // loadGrammar(XMLInputSource): Grammar 496 497 /** 498 * Parse a DTD internal and/or external subset and insert the content 499 * into the existing DTD grammar owned by the given DTDValidator. 500 */ 501 public void loadGrammarWithContext(XMLDTDValidator validator, String rootName, 502 String publicId, String systemId, String baseSystemId, String internalSubset) 503 throws IOException, XNIException { 504 final DTDGrammarBucket grammarBucket = validator.getGrammarBucket(); 505 final DTDGrammar activeGrammar = grammarBucket.getActiveGrammar(); 506 if (activeGrammar != null && !activeGrammar.isImmutable()) { 507 fGrammarBucket = grammarBucket; 508 fEntityManager.setScannerVersion(getScannerVersion()); 509 reset(); 510 try { 511 // process internal subset 512 if (internalSubset != null) { 513 // To get the DTD scanner to end at the right place we have to fool 514 // it into thinking that it reached the end of the internal subset 515 // in a real document. 516 StringBuffer buffer = new StringBuffer(internalSubset.length() + 2); 517 buffer.append(internalSubset).append("]>"); 518 XMLInputSource is = new XMLInputSource(null, baseSystemId, 519 null, new StringReader(buffer.toString()), null); 520 fEntityManager.startDocumentEntity(is); 521 fDTDScanner.scanDTDInternalSubset(true, false, systemId != null); 522 } 523 // process external subset 524 if (systemId != null) { 525 XMLDTDDescription desc = new XMLDTDDescription(publicId, systemId, baseSystemId, null, rootName); 526 XMLInputSource source = fEntityManager.resolveEntity(desc); 527 fDTDScanner.setInputSource(source); 528 fDTDScanner.scanDTDExternalSubset(true); 529 } 530 } 531 catch (EOFException e) { 532 // expected behaviour... 533 } 534 finally { 535 // Close all streams opened by the parser. 536 fEntityManager.closeReaders(); 537 } 538 } 539 } // loadGrammarWithContext(XMLDTDValidator, String, String, String, String, String) 540 541 // reset all the components that we rely upon 542 protected void reset() { 543 super.reset(); 544 fDTDScanner.reset(); 545 fEntityManager.reset(); 546 fErrorReporter.setDocumentLocator(fEntityManager.getEntityScanner()); 547 } 548 549 protected XMLDTDScannerImpl createDTDScanner(SymbolTable symbolTable, 550 XMLErrorReporter errorReporter, XMLEntityManager entityManager) { 551 return new XMLDTDScannerImpl(symbolTable, errorReporter, entityManager); 552 } // createDTDScanner(SymbolTable, XMLErrorReporter, XMLEntityManager) : XMLDTDScannerImpl 553 554 protected short getScannerVersion() { 555 return Constants.XML_VERSION_1_0; 556 } // getScannerVersion() : short 557 558 } // class XMLDTDLoader