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 * @version $Id: XMLDTDLoader.java,v 1.6 2010-11-01 04:39:42 joehw Exp $ 112 */ 113 public class XMLDTDLoader 114 extends XMLDTDProcessor 115 implements XMLGrammarLoader { 116 117 // 118 // Constants 119 // 120 121 // feature identifiers 122 123 /** Feature identifier: standard uri conformant feature. */ 124 protected static final String STANDARD_URI_CONFORMANT_FEATURE = 125 Constants.XERCES_FEATURE_PREFIX + Constants.STANDARD_URI_CONFORMANT_FEATURE; 126 127 /** Feature identifier: balance syntax trees. */ 128 protected static final String BALANCE_SYNTAX_TREES = 129 Constants.XERCES_FEATURE_PREFIX + Constants.BALANCE_SYNTAX_TREES; 130 131 // recognized features: 132 private static final String[] LOADER_RECOGNIZED_FEATURES = { 133 VALIDATION, 134 WARN_ON_DUPLICATE_ATTDEF, 135 WARN_ON_UNDECLARED_ELEMDEF, 136 NOTIFY_CHAR_REFS, 137 STANDARD_URI_CONFORMANT_FEATURE, 138 BALANCE_SYNTAX_TREES 139 }; 140 141 // property identifiers 142 143 /** Property identifier: error handler. */ 144 protected static final String ERROR_HANDLER = 145 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY; 146 147 /** Property identifier: entity resolver. */ 148 public static final String ENTITY_RESOLVER = 149 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; 150 151 /** Property identifier: locale. */ 152 public static final String LOCALE = 153 Constants.XERCES_PROPERTY_PREFIX + Constants.LOCALE_PROPERTY; 154 155 /** Recognized properties. */ 156 private static final String[] LOADER_RECOGNIZED_PROPERTIES = { 157 SYMBOL_TABLE, 158 ERROR_REPORTER, 159 ERROR_HANDLER, 160 ENTITY_RESOLVER, 161 GRAMMAR_POOL, 162 DTD_VALIDATOR, 163 LOCALE 164 }; 165 166 // enforcing strict uri? 167 private boolean fStrictURI = false; 168 169 /** Controls whether the DTD grammar produces balanced syntax trees. */ 170 private boolean fBalanceSyntaxTrees = false; 171 172 /** Entity resolver . */ 173 protected XMLEntityResolver fEntityResolver; 174 175 // the scanner we use to actually read the DTD 176 protected XMLDTDScannerImpl fDTDScanner; 177 178 // the entity manager the scanner needs. 179 protected XMLEntityManager fEntityManager; 180 181 // what's our Locale? 182 protected Locale fLocale; 183 184 // 185 // Constructors 186 // 187 188 /** Deny default construction; we need a SymtolTable! */ 189 public XMLDTDLoader() { 190 this(new SymbolTable()); 191 } // <init>() 192 193 public XMLDTDLoader(SymbolTable symbolTable) { 194 this(symbolTable, null); 195 } // init(SymbolTable) 196 197 public XMLDTDLoader(SymbolTable symbolTable, 198 XMLGrammarPool grammarPool) { 199 this(symbolTable, grammarPool, null, new XMLEntityManager()); 200 } // init(SymbolTable, XMLGrammarPool) 201 202 XMLDTDLoader(SymbolTable symbolTable, 203 XMLGrammarPool grammarPool, XMLErrorReporter errorReporter, 204 XMLEntityResolver entityResolver) { 205 fSymbolTable = symbolTable; 206 fGrammarPool = grammarPool; 207 if(errorReporter == null) { 208 errorReporter = new XMLErrorReporter(); 209 errorReporter.setProperty(ERROR_HANDLER, new DefaultErrorHandler()); 210 } 211 fErrorReporter = errorReporter; 212 // Add XML message formatter if there isn't one. 213 if (fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) { 214 XMLMessageFormatter xmft = new XMLMessageFormatter(); 215 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft); 216 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft); 217 } 218 fEntityResolver = entityResolver; 219 if(fEntityResolver instanceof XMLEntityManager) { 220 fEntityManager = (XMLEntityManager)fEntityResolver; 221 } else { 222 fEntityManager = new XMLEntityManager(); 223 } 224 fEntityManager.setProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY, errorReporter); 225 fDTDScanner = createDTDScanner(fSymbolTable, fErrorReporter, fEntityManager); 226 fDTDScanner.setDTDHandler(this); 227 fDTDScanner.setDTDContentModelHandler(this); 228 reset(); 229 } // init(SymbolTable, XMLGrammarPool, XMLErrorReporter, XMLEntityResolver) 230 231 // XMLGrammarLoader methods 232 233 /** 234 * Returns a list of feature identifiers that are recognized by 235 * this component. This method may return null if no features 236 * are recognized by this component. 237 */ 238 public String[] getRecognizedFeatures() { 239 return (String[])(LOADER_RECOGNIZED_FEATURES.clone()); 240 } // getRecognizedFeatures():String[] 241 242 /** 243 * Sets the state of a feature. This method is called by the component 244 * manager any time after reset when a feature changes state. 245 * <p> 246 * <strong>Note:</strong> Components should silently ignore features 247 * that do not affect the operation of the component. 248 * 249 * @param featureId The feature identifier. 250 * @param state The state of the feature. 251 * 252 * @throws SAXNotRecognizedException The component should not throw 253 * this exception. 254 * @throws SAXNotSupportedException The component should not throw 255 * this exception. 256 */ 257 public void setFeature(String featureId, boolean state) 258 throws XMLConfigurationException { 259 if (featureId.equals(VALIDATION)) { 260 fValidation = state; 261 } 262 else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) { 263 fWarnDuplicateAttdef = state; 264 } 265 else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) { 266 fWarnOnUndeclaredElemdef = state; 267 } 268 else if (featureId.equals(NOTIFY_CHAR_REFS)) { 269 fDTDScanner.setFeature(featureId, state); 270 } 271 else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) { 272 fStrictURI = state; 273 } 274 else if (featureId.equals(BALANCE_SYNTAX_TREES)) { 275 fBalanceSyntaxTrees = state; 276 } 277 else { 278 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId); 279 } 280 } // setFeature(String,boolean) 281 282 /** 283 * Returns a list of property identifiers that are recognized by 284 * this component. This method may return null if no properties 285 * are recognized by this component. 286 */ 287 public String[] getRecognizedProperties() { 288 return (String[])(LOADER_RECOGNIZED_PROPERTIES.clone()); 289 } // getRecognizedProperties():String[] 290 291 /** 292 * Returns the state of a property. 293 * 294 * @param propertyId The property identifier. 295 * 296 * @throws XMLConfigurationException Thrown on configuration error. 297 */ 298 public Object getProperty(String propertyId) 299 throws XMLConfigurationException { 300 if (propertyId.equals(SYMBOL_TABLE)) { 301 return fSymbolTable; 302 } 303 else if (propertyId.equals(ERROR_REPORTER)) { 304 return fErrorReporter; 305 } 306 else if (propertyId.equals(ERROR_HANDLER)) { 307 return fErrorReporter.getErrorHandler(); 308 } 309 else if (propertyId.equals(ENTITY_RESOLVER)) { 310 return fEntityResolver; 311 } 312 else if (propertyId.equals(LOCALE)) { 313 return getLocale(); 314 } 315 else if (propertyId.equals(GRAMMAR_POOL)) { 316 return fGrammarPool; 317 } 318 else if (propertyId.equals(DTD_VALIDATOR)) { 319 return fValidator; 320 } 321 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, propertyId); 322 } // getProperty(String): Object 323 324 /** 325 * Sets the value of a property. This method is called by the component 326 * manager any time after reset when a property changes value. 327 * <p> 328 * <strong>Note:</strong> Components should silently ignore properties 329 * that do not affect the operation of the component. 330 * 331 * @param propertyId The property identifier. 332 * @param value The value of the property. 333 * 334 * @throws SAXNotRecognizedException The component should not throw 335 * this exception. 336 * @throws SAXNotSupportedException The component should not throw 337 * this exception. 338 */ 339 public void setProperty(String propertyId, Object value) 340 throws XMLConfigurationException { 341 if (propertyId.equals(SYMBOL_TABLE)) { 342 fSymbolTable = (SymbolTable)value; 343 fDTDScanner.setProperty(propertyId, value); 344 fEntityManager.setProperty(propertyId, value); 345 } 346 else if(propertyId.equals(ERROR_REPORTER)) { 347 fErrorReporter = (XMLErrorReporter)value; 348 // Add XML message formatter if there isn't one. 349 if (fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) { 350 XMLMessageFormatter xmft = new XMLMessageFormatter(); 351 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft); 352 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft); 353 } 354 fDTDScanner.setProperty(propertyId, value); 355 fEntityManager.setProperty(propertyId, value); 356 } 357 else if (propertyId.equals(ERROR_HANDLER)) { 358 fErrorReporter.setProperty(propertyId, value); 359 } 360 else if (propertyId.equals(ENTITY_RESOLVER)) { 361 fEntityResolver = (XMLEntityResolver)value; 362 fEntityManager.setProperty(propertyId, value); 363 } 364 else if (propertyId.equals(LOCALE)) { 365 setLocale((Locale) value); 366 } 367 else if(propertyId.equals(GRAMMAR_POOL)) { 368 fGrammarPool = (XMLGrammarPool)value; 369 } 370 else { 371 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, propertyId); 372 } 373 } // setProperty(String,Object) 374 375 /** 376 * Returns the state of a feature. 377 * 378 * @param featureId The feature identifier. 379 * 380 * @throws XMLConfigurationException Thrown on configuration error. 381 */ 382 public boolean getFeature(String featureId) 383 throws XMLConfigurationException { 384 if (featureId.equals(VALIDATION)) { 385 return fValidation; 386 } 387 else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) { 388 return fWarnDuplicateAttdef; 389 } 390 else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) { 391 return fWarnOnUndeclaredElemdef; 392 } 393 else if (featureId.equals(NOTIFY_CHAR_REFS)) { 394 return fDTDScanner.getFeature(featureId); 395 } 396 else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) { 397 return fStrictURI; 398 } 399 else if (featureId.equals(BALANCE_SYNTAX_TREES)) { 400 return fBalanceSyntaxTrees; 401 } 402 throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId); 403 } //getFeature(String): boolean 404 405 /** 406 * Set the locale to use for messages. 407 * 408 * @param locale The locale object to use for localization of messages. 409 * 410 * @exception XNIException Thrown if the parser does not support the 411 * specified locale. 412 */ 413 public void setLocale(Locale locale) { 414 fLocale = locale; 415 fErrorReporter.setLocale(locale); 416 } // setLocale(Locale) 417 418 /** Return the Locale the XMLGrammarLoader is using. */ 419 public Locale getLocale() { 420 return fLocale; 421 } // getLocale(): Locale 422 423 424 /** 425 * Sets the error handler. 426 * 427 * @param errorHandler The error handler. 428 */ 429 public void setErrorHandler(XMLErrorHandler errorHandler) { 430 fErrorReporter.setProperty(ERROR_HANDLER, errorHandler); 431 } // setErrorHandler(XMLErrorHandler) 432 433 /** Returns the registered error handler. */ 434 public XMLErrorHandler getErrorHandler() { 435 return fErrorReporter.getErrorHandler(); 436 } // getErrorHandler(): XMLErrorHandler 437 438 /** 439 * Sets the entity resolver. 440 * 441 * @param entityResolver The new entity resolver. 442 */ 443 public void setEntityResolver(XMLEntityResolver entityResolver) { 444 fEntityResolver = entityResolver; 445 fEntityManager.setProperty(ENTITY_RESOLVER, entityResolver); 446 } // setEntityResolver(XMLEntityResolver) 447 448 /** Returns the registered entity resolver. */ 449 public XMLEntityResolver getEntityResolver() { 450 return fEntityResolver; 451 } // getEntityResolver(): XMLEntityResolver 452 453 /** 454 * Returns a Grammar object by parsing the contents of the 455 * entity pointed to by source. 456 * 457 * @param source the location of the entity which forms 458 * the starting point of the grammar to be constructed. 459 * @throws IOException When a problem is encountered reading the entity 460 * XNIException When a condition arises (such as a FatalError) that requires parsing 461 * of the entity be terminated. 462 */ 463 public Grammar loadGrammar(XMLInputSource source) 464 throws IOException, XNIException { 465 reset(); 466 // First chance checking strict URI 467 String eid = XMLEntityManager.expandSystemId(source.getSystemId(), source.getBaseSystemId(), fStrictURI); 468 XMLDTDDescription desc = new XMLDTDDescription(source.getPublicId(), source.getSystemId(), source.getBaseSystemId(), eid, null); 469 if (!fBalanceSyntaxTrees) { 470 fDTDGrammar = new DTDGrammar(fSymbolTable, desc); 471 } 472 else { 473 fDTDGrammar = new BalancedDTDGrammar(fSymbolTable, desc); 474 } 475 fGrammarBucket = new DTDGrammarBucket(); 476 fGrammarBucket.setStandalone(false); 477 fGrammarBucket.setActiveGrammar(fDTDGrammar); 478 // no reason to use grammar bucket's "put" method--we 479 // know which grammar it is, and we don't know the root name anyway... 480 481 // actually start the parsing! 482 try { 483 fDTDScanner.setInputSource(source); 484 fDTDScanner.scanDTDExternalSubset(true); 485 } catch (EOFException e) { 486 // expected behaviour... 487 } 488 finally { 489 // Close all streams opened by the parser. 490 fEntityManager.closeReaders(); 491 } 492 if(fDTDGrammar != null && fGrammarPool != null) { 493 fGrammarPool.cacheGrammars(XMLDTDDescription.XML_DTD, new Grammar[] {fDTDGrammar}); 494 } 495 return fDTDGrammar; 496 } // loadGrammar(XMLInputSource): Grammar 497 498 /** 499 * Parse a DTD internal and/or external subset and insert the content 500 * into the existing DTD grammar owned by the given DTDValidator. 501 */ 502 public void loadGrammarWithContext(XMLDTDValidator validator, String rootName, 503 String publicId, String systemId, String baseSystemId, String internalSubset) 504 throws IOException, XNIException { 505 final DTDGrammarBucket grammarBucket = validator.getGrammarBucket(); 506 final DTDGrammar activeGrammar = grammarBucket.getActiveGrammar(); 507 if (activeGrammar != null && !activeGrammar.isImmutable()) { 508 fGrammarBucket = grammarBucket; 509 fEntityManager.setScannerVersion(getScannerVersion()); 510 reset(); 511 try { 512 // process internal subset 513 if (internalSubset != null) { 514 // To get the DTD scanner to end at the right place we have to fool 515 // it into thinking that it reached the end of the internal subset 516 // in a real document. 517 StringBuffer buffer = new StringBuffer(internalSubset.length() + 2); 518 buffer.append(internalSubset).append("]>"); 519 XMLInputSource is = new XMLInputSource(null, baseSystemId, 520 null, new StringReader(buffer.toString()), null); 521 fEntityManager.startDocumentEntity(is); 522 fDTDScanner.scanDTDInternalSubset(true, false, systemId != null); 523 } 524 // process external subset 525 if (systemId != null) { 526 XMLDTDDescription desc = new XMLDTDDescription(publicId, systemId, baseSystemId, null, rootName); 527 XMLInputSource source = fEntityManager.resolveEntity(desc); 528 fDTDScanner.setInputSource(source); 529 fDTDScanner.scanDTDExternalSubset(true); 530 } 531 } 532 catch (EOFException e) { 533 // expected behaviour... 534 } 535 finally { 536 // Close all streams opened by the parser. 537 fEntityManager.closeReaders(); 538 } 539 } 540 } // loadGrammarWithContext(XMLDTDValidator, String, String, String, String, String) 541 542 // reset all the components that we rely upon 543 protected void reset() { 544 super.reset(); 545 fDTDScanner.reset(); 546 fEntityManager.reset(); 547 fErrorReporter.setDocumentLocator(fEntityManager.getEntityScanner()); 548 } 549 550 protected XMLDTDScannerImpl createDTDScanner(SymbolTable symbolTable, 551 XMLErrorReporter errorReporter, XMLEntityManager entityManager) { 552 return new XMLDTDScannerImpl(symbolTable, errorReporter, entityManager); 553 } // createDTDScanner(SymbolTable, XMLErrorReporter, XMLEntityManager) : XMLDTDScannerImpl 554 555 protected short getScannerVersion() { 556 return Constants.XML_VERSION_1_0; 557 } // getScannerVersion() : short 558 559 } // class XMLDTDLoader