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