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.jaxp.validation;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.io.Reader;
023: import java.util.Locale;
024:
025: import javax.xml.XMLConstants;
026: import javax.xml.transform.Source;
027: import javax.xml.transform.dom.DOMSource;
028: import javax.xml.transform.sax.SAXSource;
029: import javax.xml.transform.stream.StreamSource;
030: import javax.xml.validation.Schema;
031: import javax.xml.validation.SchemaFactory;
032:
033: import org.apache.xerces.impl.Constants;
034: import org.apache.xerces.impl.xs.XMLSchemaLoader;
035: import org.apache.xerces.util.DOMEntityResolverWrapper;
036: import org.apache.xerces.util.DOMInputSource;
037: import org.apache.xerces.util.ErrorHandlerWrapper;
038: import org.apache.xerces.util.SAXInputSource;
039: import org.apache.xerces.util.SAXMessageFormatter;
040: import org.apache.xerces.util.SecurityManager;
041: import org.apache.xerces.util.XMLGrammarPoolImpl;
042: import org.apache.xerces.xni.XNIException;
043: import org.apache.xerces.xni.grammars.Grammar;
044: import org.apache.xerces.xni.grammars.XMLGrammarDescription;
045: import org.apache.xerces.xni.grammars.XMLGrammarPool;
046: import org.apache.xerces.xni.parser.XMLConfigurationException;
047: import org.apache.xerces.xni.parser.XMLInputSource;
048: import org.w3c.dom.Node;
049: import org.w3c.dom.ls.LSResourceResolver;
050: import org.xml.sax.ErrorHandler;
051: import org.xml.sax.InputSource;
052: import org.xml.sax.SAXException;
053: import org.xml.sax.SAXNotRecognizedException;
054: import org.xml.sax.SAXNotSupportedException;
055: import org.xml.sax.SAXParseException;
056:
057: /**
058: * {@link SchemaFactory} for XML Schema.
059: *
060: * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
061: * @version $Id: XMLSchemaFactory.java 542520 2007-05-29 13:55:53Z mrglavas $
062: */
063: public final class XMLSchemaFactory extends SchemaFactory {
064:
065: // feature identifiers
066:
067: /** Feature identifier: schema full checking. */
068: private static final String SCHEMA_FULL_CHECKING = Constants.XERCES_FEATURE_PREFIX
069: + Constants.SCHEMA_FULL_CHECKING;
070:
071: /** Feature identifier: use grammar pool only. */
072: private static final String USE_GRAMMAR_POOL_ONLY = Constants.XERCES_FEATURE_PREFIX
073: + Constants.USE_GRAMMAR_POOL_ONLY_FEATURE;
074:
075: // property identifiers
076:
077: /** Property identifier: grammar pool. */
078: private static final String XMLGRAMMAR_POOL = Constants.XERCES_PROPERTY_PREFIX
079: + Constants.XMLGRAMMAR_POOL_PROPERTY;
080:
081: /** Property identifier: SecurityManager. */
082: private static final String SECURITY_MANAGER = Constants.XERCES_PROPERTY_PREFIX
083: + Constants.SECURITY_MANAGER_PROPERTY;
084:
085: //
086: // Data
087: //
088:
089: /** The XMLSchemaLoader */
090: private final XMLSchemaLoader fXMLSchemaLoader = new XMLSchemaLoader();
091:
092: /** User-specified ErrorHandler; can be null. */
093: private ErrorHandler fErrorHandler;
094:
095: /** The LSResrouceResolver */
096: private LSResourceResolver fLSResourceResolver;
097:
098: /** The DOMEntityResolverWrapper */
099: private final DOMEntityResolverWrapper fDOMEntityResolverWrapper;
100:
101: /** The ErrorHandlerWrapper */
102: private final ErrorHandlerWrapper fErrorHandlerWrapper;
103:
104: /** The SecurityManager. */
105: private SecurityManager fSecurityManager;
106:
107: /** The container for the real grammar pool. */
108: private final XMLGrammarPoolWrapper fXMLGrammarPoolWrapper;
109:
110: /** Whether or not to allow new schemas to be added to the grammar pool */
111: private boolean fUseGrammarPoolOnly;
112:
113: public XMLSchemaFactory() {
114: fErrorHandlerWrapper = new ErrorHandlerWrapper(
115: DraconianErrorHandler.getInstance());
116: fDOMEntityResolverWrapper = new DOMEntityResolverWrapper();
117: fXMLGrammarPoolWrapper = new XMLGrammarPoolWrapper();
118: fXMLSchemaLoader.setFeature(SCHEMA_FULL_CHECKING, true);
119: fXMLSchemaLoader.setProperty(XMLGRAMMAR_POOL,
120: fXMLGrammarPoolWrapper);
121: fXMLSchemaLoader.setEntityResolver(fDOMEntityResolverWrapper);
122: fXMLSchemaLoader.setErrorHandler(fErrorHandlerWrapper);
123: fUseGrammarPoolOnly = true;
124: }
125:
126: /**
127: * <p>Is specified schema supported by this <code>SchemaFactory</code>?</p>
128: *
129: * @param schemaLanguage Specifies the schema language which the returned <code>SchemaFactory</code> will understand.
130: * <code>schemaLanguage</code> must specify a <a href="#schemaLanguage">valid</a> schema language.
131: *
132: * @return <code>true</code> if <code>SchemaFactory</code> supports <code>schemaLanguage</code>, else <code>false</code>.
133: *
134: * @throws NullPointerException If <code>schemaLanguage</code> is <code>null</code>.
135: * @throws IllegalArgumentException If <code>schemaLanguage.length() == 0</code>
136: * or <code>schemaLanguage</code> does not specify a <a href="#schemaLanguage">valid</a> schema language.
137: */
138: public boolean isSchemaLanguageSupported(String schemaLanguage) {
139: if (schemaLanguage == null) {
140: throw new NullPointerException(
141: JAXPValidationMessageFormatter.formatMessage(Locale
142: .getDefault(), "SchemaLanguageNull", null));
143: }
144: if (schemaLanguage.length() == 0) {
145: throw new IllegalArgumentException(
146: JAXPValidationMessageFormatter.formatMessage(Locale
147: .getDefault(), "SchemaLanguageLengthZero",
148: null));
149: }
150: // only W3C XML Schema 1.0 is supported
151: return schemaLanguage
152: .equals(XMLConstants.W3C_XML_SCHEMA_NS_URI);
153: }
154:
155: public LSResourceResolver getResourceResolver() {
156: return fLSResourceResolver;
157: }
158:
159: public void setResourceResolver(LSResourceResolver resourceResolver) {
160: fLSResourceResolver = resourceResolver;
161: fDOMEntityResolverWrapper.setEntityResolver(resourceResolver);
162: fXMLSchemaLoader.setEntityResolver(fDOMEntityResolverWrapper);
163: }
164:
165: public ErrorHandler getErrorHandler() {
166: return fErrorHandler;
167: }
168:
169: public void setErrorHandler(ErrorHandler errorHandler) {
170: fErrorHandler = errorHandler;
171: fErrorHandlerWrapper
172: .setErrorHandler(errorHandler != null ? errorHandler
173: : DraconianErrorHandler.getInstance());
174: fXMLSchemaLoader.setErrorHandler(fErrorHandlerWrapper);
175: }
176:
177: public Schema newSchema(Source[] schemas) throws SAXException {
178:
179: // this will let the loader store parsed Grammars into the pool.
180: XMLGrammarPoolImplExtension pool = new XMLGrammarPoolImplExtension();
181: fXMLGrammarPoolWrapper.setGrammarPool(pool);
182:
183: XMLInputSource[] xmlInputSources = new XMLInputSource[schemas.length];
184: InputStream inputStream;
185: Reader reader;
186: for (int i = 0; i < schemas.length; i++) {
187: Source source = schemas[i];
188: if (source instanceof StreamSource) {
189: StreamSource streamSource = (StreamSource) source;
190: String publicId = streamSource.getPublicId();
191: String systemId = streamSource.getSystemId();
192: inputStream = streamSource.getInputStream();
193: reader = streamSource.getReader();
194: xmlInputSources[i] = new XMLInputSource(publicId,
195: systemId, null);
196: xmlInputSources[i].setByteStream(inputStream);
197: xmlInputSources[i].setCharacterStream(reader);
198: } else if (source instanceof SAXSource) {
199: SAXSource saxSource = (SAXSource) source;
200: InputSource inputSource = saxSource.getInputSource();
201: if (inputSource == null) {
202: throw new SAXException(
203: JAXPValidationMessageFormatter
204: .formatMessage(Locale.getDefault(),
205: "SAXSourceNullInputSource",
206: null));
207: }
208: xmlInputSources[i] = new SAXInputSource(saxSource
209: .getXMLReader(), inputSource);
210: } else if (source instanceof DOMSource) {
211: DOMSource domSource = (DOMSource) source;
212: Node node = domSource.getNode();
213: String systemID = domSource.getSystemId();
214: xmlInputSources[i] = new DOMInputSource(node, systemID);
215: } else if (source == null) {
216: throw new NullPointerException(
217: JAXPValidationMessageFormatter.formatMessage(
218: Locale.getDefault(),
219: "SchemaSourceArrayMemberNull", null));
220: } else {
221: throw new IllegalArgumentException(
222: JAXPValidationMessageFormatter.formatMessage(
223: Locale.getDefault(),
224: "SchemaFactorySourceUnrecognized",
225: new Object[] { source.getClass()
226: .getName() }));
227: }
228: }
229:
230: try {
231: fXMLSchemaLoader.loadGrammar(xmlInputSources);
232: } catch (XNIException e) {
233: // this should have been reported to users already.
234: throw Util.toSAXException(e);
235: } catch (IOException e) {
236: // this hasn't been reported, so do so now.
237: SAXParseException se = new SAXParseException(
238: e.getMessage(), null, e);
239: if (fErrorHandler != null) {
240: fErrorHandler.error(se);
241: }
242: throw se; // and we must throw it.
243: }
244:
245: // Clear reference to grammar pool.
246: fXMLGrammarPoolWrapper.setGrammarPool(null);
247:
248: // Select Schema implementation based on grammar count.
249: final int grammarCount = pool.getGrammarCount();
250: AbstractXMLSchema schema = null;
251: if (fUseGrammarPoolOnly) {
252: if (grammarCount > 1) {
253: schema = new XMLSchema(new ReadOnlyGrammarPool(pool));
254: } else if (grammarCount == 1) {
255: Grammar[] grammars = pool
256: .retrieveInitialGrammarSet(XMLGrammarDescription.XML_SCHEMA);
257: schema = new SimpleXMLSchema(grammars[0]);
258: } else {
259: schema = new EmptyXMLSchema();
260: }
261: } else {
262: schema = new XMLSchema(new ReadOnlyGrammarPool(pool), false);
263: }
264: propagateFeatures(schema);
265: return schema;
266: }
267:
268: public Schema newSchema() throws SAXException {
269: /*
270: * It would make sense to return an EmptyXMLSchema object here, if
271: * fUseGrammarPoolOnly is set to true. However, because the default
272: * value of this feature is true, doing so would change the default
273: * behaviour of this method. Thus, we return a WeakReferenceXMLSchema
274: * regardless of the value of fUseGrammarPoolOnly. -PM
275: */
276:
277: // Use a Schema that uses the system id as the equality source.
278: AbstractXMLSchema schema = new WeakReferenceXMLSchema();
279: propagateFeatures(schema);
280: return schema;
281: }
282:
283: public boolean getFeature(String name)
284: throws SAXNotRecognizedException, SAXNotSupportedException {
285: if (name == null) {
286: throw new NullPointerException(
287: JAXPValidationMessageFormatter.formatMessage(Locale
288: .getDefault(), "FeatureNameNull", null));
289: }
290: if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
291: return (fSecurityManager != null);
292: } else if (name.equals(USE_GRAMMAR_POOL_ONLY)) {
293: return fUseGrammarPoolOnly;
294: }
295: try {
296: return fXMLSchemaLoader.getFeature(name);
297: } catch (XMLConfigurationException e) {
298: String identifier = e.getIdentifier();
299: if (e.getType() == XMLConfigurationException.NOT_RECOGNIZED) {
300: throw new SAXNotRecognizedException(SAXMessageFormatter
301: .formatMessage(Locale.getDefault(),
302: "feature-not-recognized",
303: new Object[] { identifier }));
304: } else {
305: throw new SAXNotSupportedException(SAXMessageFormatter
306: .formatMessage(Locale.getDefault(),
307: "feature-not-supported",
308: new Object[] { identifier }));
309: }
310: }
311: }
312:
313: public Object getProperty(String name)
314: throws SAXNotRecognizedException, SAXNotSupportedException {
315: if (name == null) {
316: throw new NullPointerException(
317: JAXPValidationMessageFormatter.formatMessage(Locale
318: .getDefault(), "ProperyNameNull", null));
319: }
320: if (name.equals(SECURITY_MANAGER)) {
321: return fSecurityManager;
322: } else if (name.equals(XMLGRAMMAR_POOL)) {
323: throw new SAXNotSupportedException(SAXMessageFormatter
324: .formatMessage(Locale.getDefault(),
325: "property-not-supported",
326: new Object[] { name }));
327: }
328: try {
329: return fXMLSchemaLoader.getProperty(name);
330: } catch (XMLConfigurationException e) {
331: String identifier = e.getIdentifier();
332: if (e.getType() == XMLConfigurationException.NOT_RECOGNIZED) {
333: throw new SAXNotRecognizedException(SAXMessageFormatter
334: .formatMessage(Locale.getDefault(),
335: "property-not-recognized",
336: new Object[] { identifier }));
337: } else {
338: throw new SAXNotSupportedException(SAXMessageFormatter
339: .formatMessage(Locale.getDefault(),
340: "property-not-supported",
341: new Object[] { identifier }));
342: }
343: }
344: }
345:
346: public void setFeature(String name, boolean value)
347: throws SAXNotRecognizedException, SAXNotSupportedException {
348: if (name == null) {
349: throw new NullPointerException(
350: JAXPValidationMessageFormatter.formatMessage(Locale
351: .getDefault(), "FeatureNameNull", null));
352: }
353: if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
354: fSecurityManager = value ? new SecurityManager() : null;
355: fXMLSchemaLoader.setProperty(SECURITY_MANAGER,
356: fSecurityManager);
357: return;
358: } else if (name.equals(USE_GRAMMAR_POOL_ONLY)) {
359: fUseGrammarPoolOnly = value;
360: return;
361: }
362: try {
363: fXMLSchemaLoader.setFeature(name, value);
364: } catch (XMLConfigurationException e) {
365: String identifier = e.getIdentifier();
366: if (e.getType() == XMLConfigurationException.NOT_RECOGNIZED) {
367: throw new SAXNotRecognizedException(SAXMessageFormatter
368: .formatMessage(Locale.getDefault(),
369: "feature-not-recognized",
370: new Object[] { identifier }));
371: } else {
372: throw new SAXNotSupportedException(SAXMessageFormatter
373: .formatMessage(Locale.getDefault(),
374: "feature-not-supported",
375: new Object[] { identifier }));
376: }
377: }
378: }
379:
380: public void setProperty(String name, Object object)
381: throws SAXNotRecognizedException, SAXNotSupportedException {
382: if (name == null) {
383: throw new NullPointerException(
384: JAXPValidationMessageFormatter.formatMessage(Locale
385: .getDefault(), "ProperyNameNull", null));
386: }
387: if (name.equals(SECURITY_MANAGER)) {
388: fSecurityManager = (SecurityManager) object;
389: fXMLSchemaLoader.setProperty(SECURITY_MANAGER,
390: fSecurityManager);
391: return;
392: } else if (name.equals(XMLGRAMMAR_POOL)) {
393: throw new SAXNotSupportedException(SAXMessageFormatter
394: .formatMessage(Locale.getDefault(),
395: "property-not-supported",
396: new Object[] { name }));
397: }
398: try {
399: fXMLSchemaLoader.setProperty(name, object);
400: } catch (XMLConfigurationException e) {
401: String identifier = e.getIdentifier();
402: if (e.getType() == XMLConfigurationException.NOT_RECOGNIZED) {
403: throw new SAXNotRecognizedException(SAXMessageFormatter
404: .formatMessage(Locale.getDefault(),
405: "property-not-recognized",
406: new Object[] { identifier }));
407: } else {
408: throw new SAXNotSupportedException(SAXMessageFormatter
409: .formatMessage(Locale.getDefault(),
410: "property-not-supported",
411: new Object[] { identifier }));
412: }
413: }
414: }
415:
416: private void propagateFeatures(AbstractXMLSchema schema) {
417: schema.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,
418: fSecurityManager != null);
419: String[] features = fXMLSchemaLoader.getRecognizedFeatures();
420: for (int i = 0; i < features.length; ++i) {
421: boolean state = fXMLSchemaLoader.getFeature(features[i]);
422: schema.setFeature(features[i], state);
423: }
424: }
425:
426: /**
427: * Extension of XMLGrammarPoolImpl which exposes the number of
428: * grammars stored in the grammar pool.
429: */
430: static class XMLGrammarPoolImplExtension extends XMLGrammarPoolImpl {
431:
432: /** Constructs a grammar pool with a default number of buckets. */
433: public XMLGrammarPoolImplExtension() {
434: super ();
435: }
436:
437: /** Constructs a grammar pool with a specified number of buckets. */
438: public XMLGrammarPoolImplExtension(int initialCapacity) {
439: super (initialCapacity);
440: }
441:
442: /** Returns the number of grammars contained in this pool. */
443: int getGrammarCount() {
444: return fGrammarCount;
445: }
446:
447: } // XMLSchemaFactory.XMLGrammarPoolImplExtension
448:
449: /**
450: * A grammar pool which wraps another.
451: */
452: static class XMLGrammarPoolWrapper implements XMLGrammarPool {
453:
454: private XMLGrammarPool fGrammarPool;
455:
456: /*
457: * XMLGrammarPool methods
458: */
459:
460: public Grammar[] retrieveInitialGrammarSet(String grammarType) {
461: return fGrammarPool.retrieveInitialGrammarSet(grammarType);
462: }
463:
464: public void cacheGrammars(String grammarType, Grammar[] grammars) {
465: fGrammarPool.cacheGrammars(grammarType, grammars);
466: }
467:
468: public Grammar retrieveGrammar(XMLGrammarDescription desc) {
469: return fGrammarPool.retrieveGrammar(desc);
470: }
471:
472: public void lockPool() {
473: fGrammarPool.lockPool();
474: }
475:
476: public void unlockPool() {
477: fGrammarPool.unlockPool();
478: }
479:
480: public void clear() {
481: fGrammarPool.clear();
482: }
483:
484: /*
485: * Other methods
486: */
487:
488: void setGrammarPool(XMLGrammarPool grammarPool) {
489: fGrammarPool = grammarPool;
490: }
491:
492: XMLGrammarPool getGrammarPool() {
493: return fGrammarPool;
494: }
495:
496: } // XMLSchemaFactory.XMLGrammarPoolWrapper
497:
498: } // XMLSchemaFactory
|