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