001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl.dtd;
019:
020: import org.apache.xerces.impl.Constants;
021: import org.apache.xerces.impl.XMLDTDScannerImpl;
022: import org.apache.xerces.impl.XMLErrorReporter;
023: import org.apache.xerces.impl.XMLEntityManager;
024: import org.apache.xerces.impl.msg.XMLMessageFormatter;
025:
026: import org.apache.xerces.util.SymbolTable;
027: import org.apache.xerces.util.DefaultErrorHandler;
028:
029: import org.apache.xerces.xni.XNIException;
030: import org.apache.xerces.xni.grammars.XMLGrammarPool;
031: import org.apache.xerces.xni.grammars.XMLGrammarLoader;
032: import org.apache.xerces.xni.grammars.Grammar;
033: import org.apache.xerces.xni.parser.XMLConfigurationException;
034: import org.apache.xerces.xni.parser.XMLErrorHandler;
035: import org.apache.xerces.xni.parser.XMLEntityResolver;
036: import org.apache.xerces.xni.parser.XMLInputSource;
037:
038: import java.util.Locale;
039: import java.io.IOException;
040: import java.io.EOFException;
041: import java.io.StringReader;
042:
043: /**
044: * The DTD loader. The loader knows how to build grammars from XMLInputSources.
045: * It extends the DTD processor in order to do this; it's
046: * a separate class because DTD processors don't need to know how
047: * to talk to the outside world in their role as instance-document
048: * helpers.
049: * <p>
050: * This component requires the following features and properties. It
051: * know ho to set them if no one else does:from the
052: * <ul>
053: * <li>http://xml.org/sax/features/namespaces</li>
054: * <li>http://apache.org/xml/properties/internal/symbol-table</li>
055: * <li>http://apache.org/xml/properties/internal/error-reporter</li>
056: * <li>http://apache.org/xml/properties/internal/grammar-pool</li>
057: * <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
058: * </ul>
059: *
060: * @xerces.internal
061: *
062: * @author Neil Graham, IBM
063: * @author Michael Glavassevich, IBM
064: *
065: * @version $Id: XMLDTDLoader.java 446755 2006-09-15 21:56:27Z mrglavas $
066: */
067: public class XMLDTDLoader extends XMLDTDProcessor implements
068: XMLGrammarLoader {
069:
070: //
071: // Constants
072: //
073:
074: // feature identifiers
075:
076: /** Feature identifier: standard uri conformant feature. */
077: protected static final String STANDARD_URI_CONFORMANT_FEATURE = Constants.XERCES_FEATURE_PREFIX
078: + Constants.STANDARD_URI_CONFORMANT_FEATURE;
079:
080: /** Feature identifier: balance syntax trees. */
081: protected static final String BALANCE_SYNTAX_TREES = Constants.XERCES_FEATURE_PREFIX
082: + Constants.BALANCE_SYNTAX_TREES;
083:
084: // recognized features:
085: private static final String[] LOADER_RECOGNIZED_FEATURES = {
086: VALIDATION, WARN_ON_DUPLICATE_ATTDEF,
087: WARN_ON_UNDECLARED_ELEMDEF, NOTIFY_CHAR_REFS,
088: STANDARD_URI_CONFORMANT_FEATURE, BALANCE_SYNTAX_TREES };
089:
090: // property identifiers
091:
092: /** Property identifier: error handler. */
093: protected static final String ERROR_HANDLER = Constants.XERCES_PROPERTY_PREFIX
094: + Constants.ERROR_HANDLER_PROPERTY;
095:
096: /** Property identifier: entity resolver. */
097: public static final String ENTITY_RESOLVER = Constants.XERCES_PROPERTY_PREFIX
098: + Constants.ENTITY_RESOLVER_PROPERTY;
099:
100: /** Recognized properties. */
101: private static final String[] LOADER_RECOGNIZED_PROPERTIES = {
102: SYMBOL_TABLE, ERROR_REPORTER, ERROR_HANDLER,
103: ENTITY_RESOLVER, GRAMMAR_POOL, DTD_VALIDATOR, };
104:
105: // enforcing strict uri?
106: private boolean fStrictURI = false;
107:
108: /** Controls whether the DTD grammar produces balanced syntax trees. */
109: private boolean fBalanceSyntaxTrees = false;
110:
111: /** Entity resolver . */
112: protected XMLEntityResolver fEntityResolver;
113:
114: // the scanner we use to actually read the DTD
115: protected XMLDTDScannerImpl fDTDScanner;
116:
117: // the entity manager the scanner needs.
118: protected XMLEntityManager fEntityManager;
119:
120: // what's our Locale?
121: protected Locale fLocale;
122:
123: //
124: // Constructors
125: //
126:
127: /** Deny default construction; we need a SymtolTable! */
128: public XMLDTDLoader() {
129: this (new SymbolTable());
130: } // <init>()
131:
132: public XMLDTDLoader(SymbolTable symbolTable) {
133: this (symbolTable, null);
134: } // init(SymbolTable)
135:
136: public XMLDTDLoader(SymbolTable symbolTable,
137: XMLGrammarPool grammarPool) {
138: this (symbolTable, grammarPool, null, new XMLEntityManager());
139: } // init(SymbolTable, XMLGrammarPool)
140:
141: XMLDTDLoader(SymbolTable symbolTable, XMLGrammarPool grammarPool,
142: XMLErrorReporter errorReporter,
143: XMLEntityResolver entityResolver) {
144: fSymbolTable = symbolTable;
145: fGrammarPool = grammarPool;
146: if (errorReporter == null) {
147: errorReporter = new XMLErrorReporter();
148: errorReporter.setProperty(ERROR_HANDLER,
149: new DefaultErrorHandler());
150: }
151: fErrorReporter = errorReporter;
152: // Add XML message formatter if there isn't one.
153: if (fErrorReporter
154: .getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) {
155: XMLMessageFormatter xmft = new XMLMessageFormatter();
156: fErrorReporter.putMessageFormatter(
157: XMLMessageFormatter.XML_DOMAIN, xmft);
158: fErrorReporter.putMessageFormatter(
159: XMLMessageFormatter.XMLNS_DOMAIN, xmft);
160: }
161: fEntityResolver = entityResolver;
162: if (fEntityResolver instanceof XMLEntityManager) {
163: fEntityManager = (XMLEntityManager) fEntityResolver;
164: } else {
165: fEntityManager = new XMLEntityManager();
166: }
167: fEntityManager.setProperty(Constants.XERCES_PROPERTY_PREFIX
168: + Constants.ERROR_REPORTER_PROPERTY, errorReporter);
169: fDTDScanner = createDTDScanner(fSymbolTable, fErrorReporter,
170: fEntityManager);
171: fDTDScanner.setDTDHandler(this );
172: fDTDScanner.setDTDContentModelHandler(this );
173: reset();
174: } // init(SymbolTable, XMLGrammarPool, XMLErrorReporter, XMLEntityResolver)
175:
176: // XMLGrammarLoader methods
177:
178: /**
179: * Returns a list of feature identifiers that are recognized by
180: * this component. This method may return null if no features
181: * are recognized by this component.
182: */
183: public String[] getRecognizedFeatures() {
184: return (String[]) (LOADER_RECOGNIZED_FEATURES.clone());
185: } // getRecognizedFeatures():String[]
186:
187: /**
188: * Sets the state of a feature. This method is called by the component
189: * manager any time after reset when a feature changes state.
190: * <p>
191: * <strong>Note:</strong> Components should silently ignore features
192: * that do not affect the operation of the component.
193: *
194: * @param featureId The feature identifier.
195: * @param state The state of the feature.
196: *
197: * @throws SAXNotRecognizedException The component should not throw
198: * this exception.
199: * @throws SAXNotSupportedException The component should not throw
200: * this exception.
201: */
202: public void setFeature(String featureId, boolean state)
203: throws XMLConfigurationException {
204: if (featureId.equals(VALIDATION)) {
205: fValidation = state;
206: } else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) {
207: fWarnDuplicateAttdef = state;
208: } else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) {
209: fWarnOnUndeclaredElemdef = state;
210: } else if (featureId.equals(NOTIFY_CHAR_REFS)) {
211: fDTDScanner.setFeature(featureId, state);
212: } else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) {
213: fStrictURI = state;
214: } else if (featureId.equals(BALANCE_SYNTAX_TREES)) {
215: fBalanceSyntaxTrees = state;
216: } else {
217: throw new XMLConfigurationException(
218: XMLConfigurationException.NOT_RECOGNIZED, featureId);
219: }
220: } // setFeature(String,boolean)
221:
222: /**
223: * Returns a list of property identifiers that are recognized by
224: * this component. This method may return null if no properties
225: * are recognized by this component.
226: */
227: public String[] getRecognizedProperties() {
228: return (String[]) (LOADER_RECOGNIZED_PROPERTIES.clone());
229: } // getRecognizedProperties():String[]
230:
231: /**
232: * Returns the state of a property.
233: *
234: * @param propertyId The property identifier.
235: *
236: * @throws XMLConfigurationException Thrown on configuration error.
237: */
238: public Object getProperty(String propertyId)
239: throws XMLConfigurationException {
240: if (propertyId.equals(SYMBOL_TABLE)) {
241: return fSymbolTable;
242: } else if (propertyId.equals(ERROR_REPORTER)) {
243: return fErrorReporter;
244: } else if (propertyId.equals(ERROR_HANDLER)) {
245: return fErrorReporter.getErrorHandler();
246: } else if (propertyId.equals(ENTITY_RESOLVER)) {
247: return fEntityResolver;
248: } else if (propertyId.equals(GRAMMAR_POOL)) {
249: return fGrammarPool;
250: } else if (propertyId.equals(DTD_VALIDATOR)) {
251: return fValidator;
252: }
253: throw new XMLConfigurationException(
254: XMLConfigurationException.NOT_RECOGNIZED, propertyId);
255: } // getProperty(String): Object
256:
257: /**
258: * Sets the value of a property. This method is called by the component
259: * manager any time after reset when a property changes value.
260: * <p>
261: * <strong>Note:</strong> Components should silently ignore properties
262: * that do not affect the operation of the component.
263: *
264: * @param propertyId The property identifier.
265: * @param value The value of the property.
266: *
267: * @throws SAXNotRecognizedException The component should not throw
268: * this exception.
269: * @throws SAXNotSupportedException The component should not throw
270: * this exception.
271: */
272: public void setProperty(String propertyId, Object value)
273: throws XMLConfigurationException {
274: if (propertyId.equals(SYMBOL_TABLE)) {
275: fSymbolTable = (SymbolTable) value;
276: fDTDScanner.setProperty(propertyId, value);
277: fEntityManager.setProperty(propertyId, value);
278: } else if (propertyId.equals(ERROR_REPORTER)) {
279: fErrorReporter = (XMLErrorReporter) value;
280: // Add XML message formatter if there isn't one.
281: if (fErrorReporter
282: .getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) {
283: XMLMessageFormatter xmft = new XMLMessageFormatter();
284: fErrorReporter.putMessageFormatter(
285: XMLMessageFormatter.XML_DOMAIN, xmft);
286: fErrorReporter.putMessageFormatter(
287: XMLMessageFormatter.XMLNS_DOMAIN, xmft);
288: }
289: fDTDScanner.setProperty(propertyId, value);
290: fEntityManager.setProperty(propertyId, value);
291: } else if (propertyId.equals(ERROR_HANDLER)) {
292: fErrorReporter.setProperty(propertyId, value);
293: } else if (propertyId.equals(ENTITY_RESOLVER)) {
294: fEntityResolver = (XMLEntityResolver) value;
295: fEntityManager.setProperty(propertyId, value);
296: } else if (propertyId.equals(GRAMMAR_POOL)) {
297: fGrammarPool = (XMLGrammarPool) value;
298: } else {
299: throw new XMLConfigurationException(
300: XMLConfigurationException.NOT_RECOGNIZED,
301: propertyId);
302: }
303: } // setProperty(String,Object)
304:
305: /**
306: * Returns the state of a feature.
307: *
308: * @param featureId The feature identifier.
309: *
310: * @throws XMLConfigurationException Thrown on configuration error.
311: */
312: public boolean getFeature(String featureId)
313: throws XMLConfigurationException {
314: if (featureId.equals(VALIDATION)) {
315: return fValidation;
316: } else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) {
317: return fWarnDuplicateAttdef;
318: } else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) {
319: return fWarnOnUndeclaredElemdef;
320: } else if (featureId.equals(NOTIFY_CHAR_REFS)) {
321: return fDTDScanner.getFeature(featureId);
322: } else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) {
323: return fStrictURI;
324: } else if (featureId.equals(BALANCE_SYNTAX_TREES)) {
325: return fBalanceSyntaxTrees;
326: }
327: throw new XMLConfigurationException(
328: XMLConfigurationException.NOT_RECOGNIZED, featureId);
329: } //getFeature(String): boolean
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: /** Return the Locale the XMLGrammarLoader is using. */
344: public Locale getLocale() {
345: return fLocale;
346: } // getLocale(): Locale
347:
348: /**
349: * Sets the error handler.
350: *
351: * @param errorHandler The error handler.
352: */
353: public void setErrorHandler(XMLErrorHandler errorHandler) {
354: fErrorReporter.setProperty(ERROR_HANDLER, errorHandler);
355: } // setErrorHandler(XMLErrorHandler)
356:
357: /** Returns the registered error handler. */
358: public XMLErrorHandler getErrorHandler() {
359: return fErrorReporter.getErrorHandler();
360: } // getErrorHandler(): XMLErrorHandler
361:
362: /**
363: * Sets the entity resolver.
364: *
365: * @param entityResolver The new entity resolver.
366: */
367: public void setEntityResolver(XMLEntityResolver entityResolver) {
368: fEntityResolver = entityResolver;
369: fEntityManager.setProperty(ENTITY_RESOLVER, entityResolver);
370: } // setEntityResolver(XMLEntityResolver)
371:
372: /** Returns the registered entity resolver. */
373: public XMLEntityResolver getEntityResolver() {
374: return fEntityResolver;
375: } // getEntityResolver(): XMLEntityResolver
376:
377: /**
378: * Returns a Grammar object by parsing the contents of the
379: * entity pointed to by source.
380: *
381: * @param source the location of the entity which forms
382: * the starting point of the grammar to be constructed.
383: * @throws IOException When a problem is encountered reading the entity
384: * XNIException When a condition arises (such as a FatalError) that requires parsing
385: * of the entity be terminated.
386: */
387: public Grammar loadGrammar(XMLInputSource source)
388: throws IOException, XNIException {
389: reset();
390: // First chance checking strict URI
391: String eid = XMLEntityManager.expandSystemId(source
392: .getSystemId(), source.getBaseSystemId(), fStrictURI);
393: XMLDTDDescription desc = new XMLDTDDescription(source
394: .getPublicId(), source.getSystemId(), source
395: .getBaseSystemId(), eid, null);
396: if (!fBalanceSyntaxTrees) {
397: fDTDGrammar = new DTDGrammar(fSymbolTable, desc);
398: } else {
399: fDTDGrammar = new BalancedDTDGrammar(fSymbolTable, desc);
400: }
401: fGrammarBucket = new DTDGrammarBucket();
402: fGrammarBucket.setStandalone(false);
403: fGrammarBucket.setActiveGrammar(fDTDGrammar);
404: // no reason to use grammar bucket's "put" method--we
405: // know which grammar it is, and we don't know the root name anyway...
406:
407: // actually start the parsing!
408: try {
409: fDTDScanner.setInputSource(source);
410: fDTDScanner.scanDTDExternalSubset(true);
411: } catch (EOFException e) {
412: // expected behaviour...
413: } finally {
414: // Close all streams opened by the parser.
415: fEntityManager.closeReaders();
416: }
417: if (fDTDGrammar != null && fGrammarPool != null) {
418: fGrammarPool.cacheGrammars(XMLDTDDescription.XML_DTD,
419: new Grammar[] { fDTDGrammar });
420: }
421: return fDTDGrammar;
422: } // loadGrammar(XMLInputSource): Grammar
423:
424: /**
425: * Parse a DTD internal and/or external subset and insert the content
426: * into the existing DTD grammar owned by the given DTDValidator.
427: */
428: public void loadGrammarWithContext(XMLDTDValidator validator,
429: String rootName, String publicId, String systemId,
430: String baseSystemId, String internalSubset)
431: throws IOException, XNIException {
432: final DTDGrammarBucket grammarBucket = validator
433: .getGrammarBucket();
434: final DTDGrammar activeGrammar = grammarBucket
435: .getActiveGrammar();
436: if (activeGrammar != null && !activeGrammar.isImmutable()) {
437: fGrammarBucket = grammarBucket;
438: fEntityManager.setScannerVersion(getScannerVersion());
439: reset();
440: try {
441: // process internal subset
442: if (internalSubset != null) {
443: // To get the DTD scanner to end at the right place we have to fool
444: // it into thinking that it reached the end of the internal subset
445: // in a real document.
446: StringBuffer buffer = new StringBuffer(
447: internalSubset.length() + 2);
448: buffer.append(internalSubset).append("]>");
449: XMLInputSource is = new XMLInputSource(null,
450: baseSystemId, null, new StringReader(buffer
451: .toString()), null);
452: fEntityManager.startDocumentEntity(is);
453: fDTDScanner.scanDTDInternalSubset(true, false,
454: systemId != null);
455: }
456: // process external subset
457: if (systemId != null) {
458: XMLDTDDescription desc = new XMLDTDDescription(
459: publicId, systemId, baseSystemId, null,
460: rootName);
461: XMLInputSource source = fEntityManager
462: .resolveEntity(desc);
463: fDTDScanner.setInputSource(source);
464: fDTDScanner.scanDTDExternalSubset(true);
465: }
466: } catch (EOFException e) {
467: // expected behaviour...
468: } finally {
469: // Close all streams opened by the parser.
470: fEntityManager.closeReaders();
471: }
472: }
473: } // loadGrammarWithContext(XMLDTDValidator, String, String, String, String, String)
474:
475: // reset all the components that we rely upon
476: protected void reset() {
477: super .reset();
478: fDTDScanner.reset();
479: fEntityManager.reset();
480: fErrorReporter.setDocumentLocator(fEntityManager
481: .getEntityScanner());
482: }
483:
484: protected XMLDTDScannerImpl createDTDScanner(
485: SymbolTable symbolTable, XMLErrorReporter errorReporter,
486: XMLEntityManager entityManager) {
487: return new XMLDTDScannerImpl(symbolTable, errorReporter,
488: entityManager);
489: } // createDTDScanner(SymbolTable, XMLErrorReporter, XMLEntityManager) : XMLDTDScannerImpl
490:
491: protected short getScannerVersion() {
492: return Constants.XML_VERSION_1_0;
493: } // getScannerVersion() : short
494:
495: } // class XMLDTDLoader
|