1 /*
   2  * Licensed to the Apache Software Foundation (ASF) under one or more
   3  * contributor license agreements.  See the NOTICE file distributed with
   4  * this work for additional information regarding copyright ownership.
   5  * The ASF licenses this file to You under the Apache License, Version 2.0
   6  * (the "License"); you may not use this file except in compliance with
   7  * the License.  You may obtain a copy of the License at
   8  *
   9  *      http://www.apache.org/licenses/LICENSE-2.0
  10  *
  11  * Unless required by applicable law or agreed to in writing, software
  12  * distributed under the License is distributed on an "AS IS" BASIS,
  13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14  * See the License for the specific language governing permissions and
  15  * limitations under the License.
  16  */
  17 
  18 package com.sun.org.apache.xerces.internal.jaxp.validation;
  19 
  20 import java.io.IOException;
  21 import java.io.InputStream;
  22 import java.io.Reader;
  23 
  24 import javax.xml.XMLConstants;
  25 import javax.xml.stream.XMLEventReader;
  26 import javax.xml.transform.Source;
  27 import javax.xml.transform.dom.DOMSource;
  28 import javax.xml.transform.sax.SAXSource;
  29 import javax.xml.transform.stax.StAXSource;
  30 import javax.xml.transform.stream.StreamSource;
  31 import javax.xml.validation.Schema;
  32 import javax.xml.validation.SchemaFactory;
  33 
  34 import com.sun.org.apache.xerces.internal.impl.Constants;
  35 import com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader;
  36 import com.sun.org.apache.xerces.internal.util.DOMEntityResolverWrapper;
  37 import com.sun.org.apache.xerces.internal.util.DOMInputSource;
  38 import com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper;
  39 import com.sun.org.apache.xerces.internal.util.SAXInputSource;
  40 import com.sun.org.apache.xerces.internal.util.SAXMessageFormatter;
  41 import com.sun.org.apache.xerces.internal.util.StAXInputSource;
  42 import com.sun.org.apache.xerces.internal.util.Status;
  43 import com.sun.org.apache.xerces.internal.util.XMLGrammarPoolImpl;
  44 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
  45 import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
  46 import com.sun.org.apache.xerces.internal.xni.XNIException;
  47 import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
  48 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
  49 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
  50 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  51 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  52 import org.w3c.dom.Node;
  53 import org.w3c.dom.ls.LSResourceResolver;
  54 import org.xml.sax.ErrorHandler;
  55 import org.xml.sax.InputSource;
  56 import org.xml.sax.SAXException;
  57 import org.xml.sax.SAXNotRecognizedException;
  58 import org.xml.sax.SAXNotSupportedException;
  59 import org.xml.sax.SAXParseException;
  60 
  61 /**
  62  * {@link SchemaFactory} for XML Schema.
  63  *
  64  * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
  65  */
  66 public final class XMLSchemaFactory extends SchemaFactory {
  67 
  68     // feature identifiers
  69 
  70     /** JAXP Source feature prefix. */
  71     private static final String JAXP_SOURCE_FEATURE_PREFIX = "http://javax.xml.transform";
  72 
  73     /** Feature identifier: schema full checking. */
  74     private static final String SCHEMA_FULL_CHECKING =
  75         Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_FULL_CHECKING;
  76 
  77     /** Feature identifier: use grammar pool only. */
  78     private static final String USE_GRAMMAR_POOL_ONLY =
  79         Constants.XERCES_FEATURE_PREFIX + Constants.USE_GRAMMAR_POOL_ONLY_FEATURE;
  80 
  81     // property identifiers
  82 
  83     /** Property identifier: grammar pool. */
  84     private static final String XMLGRAMMAR_POOL =
  85         Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
  86 
  87     /** Property identifier: XMLSecurityManager. */
  88     private static final String SECURITY_MANAGER =
  89         Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
  90 
  91     /** Property identifier: Security property manager. */
  92     private static final String XML_SECURITY_PROPERTY_MANAGER =
  93             Constants.XML_SECURITY_PROPERTY_MANAGER;
  94 
  95 
  96     //
  97     // Data
  98     //
  99 
 100     /** The XMLSchemaLoader */
 101     private final XMLSchemaLoader fXMLSchemaLoader = new XMLSchemaLoader();
 102 
 103     /** User-specified ErrorHandler; can be null. */
 104     private ErrorHandler fErrorHandler;
 105 
 106     /** The LSResrouceResolver */
 107     private LSResourceResolver fLSResourceResolver;
 108 
 109     /** The DOMEntityResolverWrapper */
 110     private final DOMEntityResolverWrapper fDOMEntityResolverWrapper;
 111 
 112     /** The ErrorHandlerWrapper */
 113     private final ErrorHandlerWrapper fErrorHandlerWrapper;
 114 
 115     /** The SecurityManager. */
 116     private XMLSecurityManager fSecurityManager;
 117 
 118     /** The Security property manager. */
 119     private XMLSecurityPropertyManager fSecurityPropertyMgr;
 120 
 121     /** The container for the real grammar pool. */
 122     private final XMLGrammarPoolWrapper fXMLGrammarPoolWrapper;
 123 
 124     /** Whether or not to allow new schemas to be added to the grammar pool */
 125     private boolean fUseGrammarPoolOnly;
 126 
 127     /**
 128      * Indicates whether implementation parts should use
 129      *   service loader (or similar).
 130      * Note the default value (false) is the safe option..
 131      */
 132     private final boolean fUseServicesMechanism;
 133 
 134 
 135     public XMLSchemaFactory() {
 136         this(true);
 137     }
 138     public static XMLSchemaFactory newXMLSchemaFactoryNoServiceLoader() {
 139         return new XMLSchemaFactory(false);
 140     }
 141     private XMLSchemaFactory(boolean useServicesMechanism) {
 142         fUseServicesMechanism = useServicesMechanism;
 143         fErrorHandlerWrapper = new ErrorHandlerWrapper(DraconianErrorHandler.getInstance());
 144         fDOMEntityResolverWrapper = new DOMEntityResolverWrapper();
 145         fXMLGrammarPoolWrapper = new XMLGrammarPoolWrapper();
 146         fXMLSchemaLoader.setFeature(SCHEMA_FULL_CHECKING, true);
 147         fXMLSchemaLoader.setProperty(XMLGRAMMAR_POOL, fXMLGrammarPoolWrapper);
 148         fXMLSchemaLoader.setEntityResolver(fDOMEntityResolverWrapper);
 149         fXMLSchemaLoader.setErrorHandler(fErrorHandlerWrapper);
 150         fUseGrammarPoolOnly = true;
 151 
 152         // Enable secure processing feature by default
 153         fSecurityManager = new XMLSecurityManager(true);
 154         fXMLSchemaLoader.setProperty(SECURITY_MANAGER, fSecurityManager);
 155 
 156         fSecurityPropertyMgr = new XMLSecurityPropertyManager();
 157         fXMLSchemaLoader.setProperty(XML_SECURITY_PROPERTY_MANAGER,
 158                 fSecurityPropertyMgr);
 159     }
 160 
 161     /**
 162      * <p>Is specified schema supported by this <code>SchemaFactory</code>?</p>
 163      *
 164      * @param schemaLanguage Specifies the schema language which the returned <code>SchemaFactory</code> will understand.
 165      *    <code>schemaLanguage</code> must specify a <a href="#schemaLanguage">valid</a> schema language.
 166      *
 167      * @return <code>true</code> if <code>SchemaFactory</code> supports <code>schemaLanguage</code>, else <code>false</code>.
 168      *
 169      * @throws NullPointerException If <code>schemaLanguage</code> is <code>null</code>.
 170      * @throws IllegalArgumentException If <code>schemaLanguage.length() == 0</code>
 171      *   or <code>schemaLanguage</code> does not specify a <a href="#schemaLanguage">valid</a> schema language.
 172      */
 173     public boolean isSchemaLanguageSupported(String schemaLanguage) {
 174         if (schemaLanguage == null) {
 175             throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 176                     "SchemaLanguageNull", null));
 177         }
 178         if (schemaLanguage.length() == 0) {
 179             throw new IllegalArgumentException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 180                     "SchemaLanguageLengthZero", null));
 181         }
 182         // only W3C XML Schema 1.0 is supported
 183         return schemaLanguage.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI) ||
 184                 schemaLanguage.equals(Constants.W3C_XML_SCHEMA10_NS_URI);
 185     }
 186 
 187     public LSResourceResolver getResourceResolver() {
 188         return fLSResourceResolver;
 189     }
 190 
 191     public void setResourceResolver(LSResourceResolver resourceResolver) {
 192         fLSResourceResolver = resourceResolver;
 193         fDOMEntityResolverWrapper.setEntityResolver(resourceResolver);
 194         fXMLSchemaLoader.setEntityResolver(fDOMEntityResolverWrapper);
 195     }
 196 
 197     public ErrorHandler getErrorHandler() {
 198         return fErrorHandler;
 199     }
 200 
 201     public void setErrorHandler(ErrorHandler errorHandler) {
 202         fErrorHandler = errorHandler;
 203         fErrorHandlerWrapper.setErrorHandler(errorHandler != null ? errorHandler : DraconianErrorHandler.getInstance());
 204         fXMLSchemaLoader.setErrorHandler(fErrorHandlerWrapper);
 205     }
 206 
 207     public Schema newSchema( Source[] schemas ) throws SAXException {
 208 
 209         // this will let the loader store parsed Grammars into the pool.
 210         XMLGrammarPoolImplExtension pool = new XMLGrammarPoolImplExtension();
 211         fXMLGrammarPoolWrapper.setGrammarPool(pool);
 212 
 213         XMLInputSource[] xmlInputSources = new XMLInputSource[schemas.length];
 214         InputStream inputStream;
 215         Reader reader;
 216         for (int i = 0; i < schemas.length; ++i) {
 217             Source source = schemas[i];
 218             if (source instanceof StreamSource) {
 219                 StreamSource streamSource = (StreamSource) source;
 220                 String publicId = streamSource.getPublicId();
 221                 String systemId = streamSource.getSystemId();
 222                 inputStream = streamSource.getInputStream();
 223                 reader = streamSource.getReader();
 224                 XMLInputSource xmlInputSource = new XMLInputSource(publicId, systemId, null, false);
 225                 xmlInputSource.setByteStream(inputStream);
 226                 xmlInputSource.setCharacterStream(reader);
 227                 xmlInputSources[i] = xmlInputSource;
 228             }
 229             else if (source instanceof SAXSource) {
 230                 SAXSource saxSource = (SAXSource) source;
 231                 InputSource inputSource = saxSource.getInputSource();
 232                 if (inputSource == null) {
 233                     throw new SAXException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 234                             "SAXSourceNullInputSource", null));
 235                 }
 236                 xmlInputSources[i] = new SAXInputSource(saxSource.getXMLReader(), inputSource);
 237             }
 238             else if (source instanceof DOMSource) {
 239                 DOMSource domSource = (DOMSource) source;
 240                 Node node = domSource.getNode();
 241                 String systemID = domSource.getSystemId();
 242                 xmlInputSources[i] = new DOMInputSource(node, systemID);
 243             }
 244              else if (source instanceof StAXSource) {
 245                 StAXSource staxSource = (StAXSource) source;
 246                 XMLEventReader eventReader = staxSource.getXMLEventReader();
 247                 if (eventReader != null) {
 248                     xmlInputSources[i] = new StAXInputSource(eventReader);
 249                 }
 250                 else {
 251                     xmlInputSources[i] = new StAXInputSource(staxSource.getXMLStreamReader());
 252                 }
 253             }
 254             else if (source == null) {
 255                 throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 256                         "SchemaSourceArrayMemberNull", null));
 257             }
 258             else {
 259                 throw new IllegalArgumentException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 260                         "SchemaFactorySourceUnrecognized",
 261                         new Object [] {source.getClass().getName()}));
 262             }
 263         }
 264 
 265         try {
 266             fXMLSchemaLoader.loadGrammar(xmlInputSources);
 267         }
 268         catch (XNIException e) {
 269             // this should have been reported to users already.
 270             throw Util.toSAXException(e);
 271         }
 272         catch (IOException e) {
 273             // this hasn't been reported, so do so now.
 274             SAXParseException se = new SAXParseException(e.getMessage(),null,e);
 275             if (fErrorHandler != null) {
 276                 fErrorHandler.error(se);
 277             }
 278             throw se; // and we must throw it.
 279         }
 280 
 281         // Clear reference to grammar pool.
 282         fXMLGrammarPoolWrapper.setGrammarPool(null);
 283 
 284         // Select Schema implementation based on grammar count.
 285         final int grammarCount = pool.getGrammarCount();
 286         AbstractXMLSchema schema = null;
 287         if (fUseGrammarPoolOnly) {
 288             if (grammarCount > 1) {
 289                 schema = new XMLSchema(new ReadOnlyGrammarPool(pool));
 290             }
 291             else if (grammarCount == 1) {
 292                 Grammar[] grammars = pool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_SCHEMA);
 293                 schema = new SimpleXMLSchema(grammars[0]);
 294             }
 295             else {
 296                 schema = new EmptyXMLSchema();
 297             }
 298         }
 299         else {
 300             schema = new XMLSchema(new ReadOnlyGrammarPool(pool), false);
 301         }
 302         propagateFeatures(schema);
 303         propagateProperties(schema);
 304         return schema;
 305     }
 306 
 307     public Schema newSchema() throws SAXException {
 308         /*
 309          * It would make sense to return an EmptyXMLSchema object here, if
 310          * fUseGrammarPoolOnly is set to true. However, because the default
 311          * value of this feature is true, doing so would change the default
 312          * behaviour of this method. Thus, we return a WeakReferenceXMLSchema
 313          * regardless of the value of fUseGrammarPoolOnly. -PM
 314          */
 315 
 316         // Use a Schema that uses the system id as the equality source.
 317         AbstractXMLSchema schema = new WeakReferenceXMLSchema();
 318         propagateFeatures(schema);
 319         propagateProperties(schema);
 320         return schema;
 321     }
 322 
 323     public Schema newSchema(XMLGrammarPool pool) throws SAXException {
 324         // If the "use-grammar-pool-only" feature is set to true
 325         // prevent the application's grammar pool from being mutated
 326         // by wrapping it in a ReadOnlyGrammarPool.
 327         final AbstractXMLSchema schema = (fUseGrammarPoolOnly) ?
 328             new XMLSchema(new ReadOnlyGrammarPool(pool)) :
 329             new XMLSchema(pool, false);
 330         propagateFeatures(schema);
 331         return schema;
 332     }
 333 
 334     public boolean getFeature(String name)
 335         throws SAXNotRecognizedException, SAXNotSupportedException {
 336         if (name == null) {
 337             throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 338                     "FeatureNameNull", null));
 339         }
 340         if (name.startsWith(JAXP_SOURCE_FEATURE_PREFIX)) {
 341             // Indicates to the caller that this SchemaFactory supports a specific JAXP Source.
 342             if (name.equals(StreamSource.FEATURE) ||
 343                 name.equals(SAXSource.FEATURE) ||
 344                 name.equals(DOMSource.FEATURE) ||
 345                 name.equals(StAXSource.FEATURE)) {
 346                 return true;
 347             }
 348         }
 349         if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
 350             return (fSecurityManager != null && fSecurityManager.isSecureProcessing());
 351         }
 352         else if (name.equals(USE_GRAMMAR_POOL_ONLY)) {
 353             return fUseGrammarPoolOnly;
 354         }
 355         try {
 356             return fXMLSchemaLoader.getFeature(name);
 357         }
 358         catch (XMLConfigurationException e) {
 359             String identifier = e.getIdentifier();
 360             if (e.getType() == Status.NOT_RECOGNIZED) {
 361                 throw new SAXNotRecognizedException(
 362                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 363                         "feature-not-recognized", new Object [] {identifier}));
 364             }
 365             else {
 366                 throw new SAXNotSupportedException(
 367                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 368                         "feature-not-supported", new Object [] {identifier}));
 369             }
 370         }
 371     }
 372 
 373     public Object getProperty(String name)
 374         throws SAXNotRecognizedException, SAXNotSupportedException {
 375         if (name == null) {
 376             throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 377                     "ProperyNameNull", null));
 378         }
 379         if (name.equals(SECURITY_MANAGER)) {
 380             return fSecurityManager;
 381         }
 382         else if (name.equals(XMLGRAMMAR_POOL)) {
 383             throw new SAXNotSupportedException(
 384                     SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 385                     "property-not-supported", new Object [] {name}));
 386         }
 387         try {
 388             return fXMLSchemaLoader.getProperty(name);
 389         }
 390         catch (XMLConfigurationException e) {
 391             String identifier = e.getIdentifier();
 392             if (e.getType() == Status.NOT_RECOGNIZED) {
 393                 throw new SAXNotRecognizedException(
 394                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 395                         "property-not-recognized", new Object [] {identifier}));
 396             }
 397             else {
 398                 throw new SAXNotSupportedException(
 399                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 400                         "property-not-supported", new Object [] {identifier}));
 401             }
 402         }
 403     }
 404 
 405     public void setFeature(String name, boolean value)
 406         throws SAXNotRecognizedException, SAXNotSupportedException {
 407         if (name == null) {
 408             throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 409                     "FeatureNameNull", null));
 410         }
 411         if (name.startsWith(JAXP_SOURCE_FEATURE_PREFIX)) {
 412             if (name.equals(StreamSource.FEATURE) ||
 413                 name.equals(SAXSource.FEATURE) ||
 414                 name.equals(DOMSource.FEATURE) ||
 415                 name.equals(StAXSource.FEATURE)) {
 416                 throw new SAXNotSupportedException(
 417                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 418                         "feature-read-only", new Object [] {name}));
 419             }
 420         }
 421         if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
 422             if (System.getSecurityManager() != null && (!value)) {
 423                 throw new SAXNotSupportedException(
 424                         SAXMessageFormatter.formatMessage(null,
 425                         "jaxp-secureprocessing-feature", null));
 426             }
 427 
 428             fSecurityManager.setSecureProcessing(value);
 429             if (value) {
 430                 fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD,
 431                         XMLSecurityPropertyManager.State.FSP, Constants.EXTERNAL_ACCESS_DEFAULT_FSP);
 432                 fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA,
 433                         XMLSecurityPropertyManager.State.FSP, Constants.EXTERNAL_ACCESS_DEFAULT_FSP);
 434             }
 435 
 436             fXMLSchemaLoader.setProperty(SECURITY_MANAGER, fSecurityManager);
 437             return;
 438         }
 439         else if (name.equals(USE_GRAMMAR_POOL_ONLY)) {
 440             fUseGrammarPoolOnly = value;
 441             return;
 442         }
 443         else if (name.equals(Constants.ORACLE_FEATURE_SERVICE_MECHANISM)) {
 444             //in secure mode, let _useServicesMechanism be determined by the constructor
 445             if (System.getSecurityManager() != null)
 446                 return;
 447         }
 448         try {
 449             fXMLSchemaLoader.setFeature(name, value);
 450         }
 451         catch (XMLConfigurationException e) {
 452             String identifier = e.getIdentifier();
 453             if (e.getType() == Status.NOT_RECOGNIZED) {
 454                 throw new SAXNotRecognizedException(
 455                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 456                         "feature-not-recognized", new Object [] {identifier}));
 457             }
 458             else {
 459                 throw new SAXNotSupportedException(
 460                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 461                         "feature-not-supported", new Object [] {identifier}));
 462             }
 463         }
 464     }
 465 
 466     public void setProperty(String name, Object object)
 467         throws SAXNotRecognizedException, SAXNotSupportedException {
 468         if (name == null) {
 469             throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 470                     "ProperyNameNull", null));
 471         }
 472         if (name.equals(SECURITY_MANAGER)) {
 473             fSecurityManager = XMLSecurityManager.convert(object, fSecurityManager);
 474             fXMLSchemaLoader.setProperty(SECURITY_MANAGER, fSecurityManager);
 475             return;
 476         } else if (name.equals(Constants.XML_SECURITY_PROPERTY_MANAGER)) {
 477             if (object == null) {
 478                 fSecurityPropertyMgr = new XMLSecurityPropertyManager();
 479             } else {
 480                 fSecurityPropertyMgr = (XMLSecurityPropertyManager)object;
 481             }
 482             fXMLSchemaLoader.setProperty(Constants.XML_SECURITY_PROPERTY_MANAGER, fSecurityPropertyMgr);
 483             return;
 484         }
 485         else if (name.equals(XMLGRAMMAR_POOL)) {
 486             throw new SAXNotSupportedException(
 487                     SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 488                     "property-not-supported", new Object [] {name}));
 489         }
 490         try {
 491             //check if the property is managed by security manager
 492             if (fSecurityManager == null ||
 493                     !fSecurityManager.setLimit(name, XMLSecurityManager.State.APIPROPERTY, object)) {
 494                 //check if the property is managed by security property manager
 495                 if (fSecurityPropertyMgr == null ||
 496                         !fSecurityPropertyMgr.setValue(name, XMLSecurityPropertyManager.State.APIPROPERTY, object)) {
 497                     //fall back to the existing property manager
 498                     fXMLSchemaLoader.setProperty(name, object);
 499                 }
 500             }
 501         }
 502         catch (XMLConfigurationException e) {
 503             String identifier = e.getIdentifier();
 504             if (e.getType() == Status.NOT_RECOGNIZED) {
 505                 throw new SAXNotRecognizedException(
 506                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 507                         "property-not-recognized", new Object [] {identifier}));
 508             }
 509             else {
 510                 throw new SAXNotSupportedException(
 511                         SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(),
 512                         "property-not-supported", new Object [] {identifier}));
 513             }
 514         }
 515     }
 516 
 517     private void propagateFeatures(AbstractXMLSchema schema) {
 518         schema.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,
 519                 (fSecurityManager != null && fSecurityManager.isSecureProcessing()));
 520         schema.setFeature(Constants.ORACLE_FEATURE_SERVICE_MECHANISM, fUseServicesMechanism);
 521         String[] features = fXMLSchemaLoader.getRecognizedFeatures();
 522         for (int i = 0; i < features.length; ++i) {
 523             boolean state = fXMLSchemaLoader.getFeature(features[i]);
 524             schema.setFeature(features[i], state);
 525         }
 526     }
 527 
 528     private void propagateProperties(AbstractXMLSchema schema) {
 529         String[] properties = fXMLSchemaLoader.getRecognizedProperties();
 530         for (int i = 0; i < properties.length; ++i) {
 531             Object state = fXMLSchemaLoader.getProperty(properties[i]);
 532             schema.setProperty(properties[i], state);
 533         }
 534     }
 535 
 536 
 537     /**
 538      * Extension of XMLGrammarPoolImpl which exposes the number of
 539      * grammars stored in the grammar pool.
 540      */
 541     static class XMLGrammarPoolImplExtension extends XMLGrammarPoolImpl {
 542 
 543         /** Constructs a grammar pool with a default number of buckets. */
 544         public XMLGrammarPoolImplExtension() {
 545             super();
 546         }
 547 
 548         /** Constructs a grammar pool with a specified number of buckets. */
 549         public XMLGrammarPoolImplExtension(int initialCapacity) {
 550             super(initialCapacity);
 551         }
 552 
 553         /** Returns the number of grammars contained in this pool. */
 554         int getGrammarCount() {
 555             return fGrammarCount;
 556         }
 557 
 558     } // XMLSchemaFactory.XMLGrammarPoolImplExtension
 559 
 560     /**
 561      * A grammar pool which wraps another.
 562      */
 563     static class XMLGrammarPoolWrapper implements XMLGrammarPool {
 564 
 565         private XMLGrammarPool fGrammarPool;
 566 
 567         /*
 568          * XMLGrammarPool methods
 569          */
 570 
 571         public Grammar[] retrieveInitialGrammarSet(String grammarType) {
 572             return fGrammarPool.retrieveInitialGrammarSet(grammarType);
 573         }
 574 
 575         public void cacheGrammars(String grammarType, Grammar[] grammars) {
 576             fGrammarPool.cacheGrammars(grammarType, grammars);
 577         }
 578 
 579         public Grammar retrieveGrammar(XMLGrammarDescription desc) {
 580             return fGrammarPool.retrieveGrammar(desc);
 581         }
 582 
 583         public void lockPool() {
 584             fGrammarPool.lockPool();
 585         }
 586 
 587         public void unlockPool() {
 588             fGrammarPool.unlockPool();
 589         }
 590 
 591         public void clear() {
 592             fGrammarPool.clear();
 593         }
 594 
 595         /*
 596          * Other methods
 597          */
 598 
 599         void setGrammarPool(XMLGrammarPool grammarPool) {
 600             fGrammarPool = grammarPool;
 601         }
 602 
 603         XMLGrammarPool getGrammarPool() {
 604             return fGrammarPool;
 605         }
 606 
 607     } // XMLSchemaFactory.XMLGrammarPoolWrapper
 608 
 609 } // XMLSchemaFactory