|  | 
 | |  |  |  |  |  |  I have a set of (DTD or XML Schema) grammars that I
    use a lot.  How can I make Xerces
    reuse the representations it builds for these grammars,
    instead of parsing them anew with every new document? |  |  |  |  |  | 
 |  |  | 
        Before answering this question, it will greatly help to
        understand how Xerces handles grammars internally.  To do
        this, here are some terms:
     
        Grammar:  defined in theorg.apache.xerces.xni.grammars.Grammarinterface; simply differentiates objects that are Xerces
        grammars from other objects, as well as providing a means
        to get at the location information (XMLGrammarDescription) for the grammar represented.XMLGrammarDescription:  defined by theorg.apache.xerces.xni.grammars.XMLGrammarDescriptioninterface, holds some basic location information common to all grammars.
        This can be used to distinguish oneGrammarobject from another, and also
        contains information about the type of the grammar.Validator:  A generic term used in Xerces to denote
        an object which compares the structure of an XML document
        with the expectations of a certain type of grammar.
        Currently, we have DTD and XML Schema validators.XMLGrammarPool:  Defined by theorg.apache.xerces.xni.grammars.XMLGrammarPoolinterface, this object is owned by the application and it
        is the means by which the application and Xerces pass
        complex grammars to one another.Grammar bucket:  An internal data structure owned by
        a Xerces validator in which grammars--and information
        related to grammars--to be used in a given validation
        episode is stored.XMLGrammarLoader:  defined in theorg.apache.xerces.xni.grammars.XMLGrammarLoaderinterface, this defines an object that "knows how" to
        read the XML representation of a particular kind of
        grammar and construct a Xerces-internal representation (aGrammarobject) out of it.  These objects
        may interact with validators during parsing of instance
        documents, or with external code during grammar
        preparsing. Now that the terminology is out of the way, it's possible
        to relate all these objects together.  At the commencement of
        a validation episode, a validator will call the
        retrieveInitialGrammarSet(String grammarType)method of theXMLGrammarPoolinstance to which it has access.  It
        will use theGrammarobjects it procures in this
        way to seed its grammar bucket. 
        When the validator determines that it needs a grammar, it
        will consult its grammar bucket.  If it finds a matching
        grammar, it will attempt to use it.  Otherwise, if it has
        access to an XMLGrammarPoolinstance, it
        will request a grammar from that object with theretrieveGrammar(XMLGrammarDescription desc)method.  Only if both of these steps fail will it fall
        back to attempting to resolve the grammar entity and
        calling the appropriateXMLGrammarLoaderto actually create a new Grammar object. 
        At the end of the validation episode, the validator will
        call the cacheGrammars(String grammarType,
        Grammar[] grammars)method of theXMLGrammarPool(if any) to which it has
        access.  There is no guarantee grammars that the grammar
        pool itself supplied to the validator will not be
        included in this set, so a grammar pool implementation
        cannot rely only on new grammars to be passed back in
        this situation. 
        At long last, it's now possible to answer the original
        question--how can one cache grammars?  Assuming one has a
        reasonable XMLGrammarPoolimplementation--such as that provided with Xerces--there are two
        answers: 
        The "passive" approach:  Don't do any preparsing,
        just register the grammar pool implementation with the
        parser, and as new grammars are requested by instance
        documents, simply let the validators add them to the
        pool.  This is very unobtrusive to the application, but
        doesn't provide that much control over what grammars are
        added; even if a custom EntityResolver is registered,
        it's still possible that unwanted grammars will make it
        into the pool.The "active" approach:  Preload a grammar pool
        implementation with all the grammars you'll need, then
        lock it so that no new grammars will be added.  Then
        registering this on the configuration will allow
        validators to make use of this set; registering a
        do-nothing EntityResolver will allow the application to
        deny validators from using any but the "approved" grammar
        set.  This will oblige the application to use more Xerces
        code, but provides a far more fine-grained approach to
        controlling what grammars may be used. 
        We discuss both these approaches in a bit more detail
        below, complete with some (broad) examples.  
        As a starting point, though, the
        XMLGrammarBuildersample, from thexnipackage, should provide a starting-point
        for implementing either the active or passive approach. | 
 
 | |  |  |  |  |  |  Exactly how does Xerces default implementation of things
  like the grammar pool work? |  |  |  |  |  | 
 |  |  | 
        Before proceeding further, let there be no doubt that, by default, Xerces
        does not cache grammars at all.  In order to trigger Xerces grammar caching, an XMLGrammarPoolmust be set, using thesetPropertymethod,
        on a Xerces configuration that supports grammar pools.  On the other hand,
        you could simply use theXMLGrammarCachingConfigurationas
        discussed briefly below. 
        When enabled, by default, Xerces's grammar pool implementation stores
        any grammar offered to it (provided it does not already
        have a reference matching that grammar).  It also makes
        available all grammars it has, of a particular type, on
        calls to retrieveInitialGrammarSet.  It will
        also try and retrieve a matching grammar on calls toretrieveGrammar. 
        Xerces uses hashing to distinguish different grammar
        objects, by hashing on the
        XMLGrammarDescriptionobjects that those
        grammars contain.  Thus, both of Xerces implementations
        of XMLGrammarDescription--for DTD's and XML
        Schemas--provide implementations ofhashCode():
        intandequals(Object):booleanthat
        are used by the hashing algorithm. 
        In XML Schemas, hashing is simply carried out on the
        target namespace of the schema.  Thus, two grammars are
        considered equal (by our default implementation) if and
        only if their XMLGrammarDescriptions are instances of
        org.apache.xerces.impl.xs.XSDDescription(our schema implementation of
        XMLGrammarDescription) and the targetNamespace fields of
        those objects are identical. 
        The case in DTD's is much more difficult.  Here is the
        algorithm, which describes the conditions under which two
        DTD grammars will be considered equal:
     
        Both grammars must have XMLGrammarDescriptions that
        are instances of
        org.apache.xerces.impl.dtd.XMLDTDDescription.If their publicId or expandedSystemId fields are
        non-null they must be identical.If one of the descriptions has a root element
        defined, it must be the same as the root element defined
        in the other description, or be in the list of global
        elements stored in that description.If neither has a root element defined, then they must
        share at least one global element declaration in
        common. 
        The DTD grammar caching also assumes that the entirety of
        the cached grammar will lie in an external subset.  i.e.,
        in the example below, Xerces will happily cache--or use a
        cached version of--the DTD in "my.dtd".  If the document
        contained an internal subset, the declarations would be
        ignored.  
     |  |  |  |  |  | <!DOCTYPE myDoc SYSTEM "my.dtd">
<myDoc ...>...</myDoc> |  |  |  |  |  | 
        Using these heuristics, Xerces's default grammar caching
        implementation appears to do a reasonable job at matching
        grammars up with appropriate instance documents.  This
        functionality is very new, so in addition to bug reports
        we'd very much appreciate, especially on the DTD front,
        feedback on whether this form of caching is indeed useful or
        whether--for instance--it would be better if internal
        declarations were somehow incorporated into the grammar
        that's been cached.
     | 
 
 | |  |  |  |  |  |  I like the idea of "active" caching (or I want the grammar
  object for some purpose); how do I go about parsing a grammar
  independent of an instance document? |  |  |  |  |  | 
 |  |  | 
        First, if you haven't read the first FAQ on this page and
        have trouble with terminology, hopefully answers
        lie there.  
       
        Preparsing of grammars in Xerces is accomplished with
        implementations of the XMLGrammarLoaderinterface.  Each implementation needs to know how to
        parse a particular type of grammar and how to build a
        data structure representing that grammar that Xerces can
        efficiently make use of in validation.  Since most
        application programs won't want to deal with Xerces
        implementations per se, we have provided a handy utility
        class to handle grammar preparsing generally:org.apache.xerces.parsers.XMLGrammarPreparser.
        This FAQ describes the use of this class.
        For a live example, check out theXMLGrammarBuildersample in thesamples/xnidirectory of the binary
        distribution. 
        XMLGrammarPreparserhas methods for
        installing XNI error handlers, entity resolvers, setting
        the Locale, and generally doing similar things as an XNI
        configuration.  Any object passed to XMLGrammarPreparser
        by any of these methods will be passed on to allXMLGrammarLoaders registered with
        XMLGrammarPreparser. 
        Before XMLGrammarPreparsercan be used, itsregisterPreparser(String, XMLGrammarLoader):
        booleanmethod must be called.  This allows a
        String identifying an arbitrary grammar type to be
        associated with a loader for that type.  To make peoples'
        lives easier, if you want DTD grammars or XML Schema
        grammar support, you can passnullfor the
        second parameter andXMLGrammarPreparserwill try and instantiate the appropriate default grammar
        loader.  For DTD's, for instance, just callregisterPreparserlike: |  |  |  |  |  | grammarPreparser("http://www.w3.org/TR/REC-xml", null) |  |  |  |  |  | 
        Schema grammars correspond to the URI
        "http://www.w3.org/2001/XMLSchema"; both these constants
        can be found in the
        org.apache.xerces.xni.grammars.XMLGrammarDescriptioninterface.  The method returns true if an
        XMLGrammarLoader was successfully associated with the
        given grammar String, false otherwise. 
        XMLGrammarPreparser also contains methods for setting
        features and properties on particular loaders--keyed on
        with the same string that was used to register the
        loader.  It also allows features and properties the
        application believes to be general to all loaders to be
        set; it transmits such features and properties to each
        loader that is registered.  These methods also silently consume any
        notRecognized/notSupported exceptions that the loaders throw.  Particularly useful here is
        registering an XMLGrammarPoolimplementation, such as that found inorg.apache.xerces.util.XMLGrammarPoolImpl. 
        To actually parse a grammar, one simply calls the
        preparseGrammar(String grammarType, XMLInputSource
        source):  Grammarmethod.  As above, the String
        represents the type of the grammar to be parsed, and the
        XMLInputSource is the location of the grammar to be
        parsed; this will not be subjected to entity expansion. 
        It's worth noting that Xerces default grammar loaders
        will attempt to cache the resulting grammar(s) if a
        grammar pool implementation is registered with them.
        This is particularly useful in the case of schema
        grammars:  If a schema grammar imports another grammar,
        the Grammar object returned will be the schema doing the
        importing, not the one being imported.  For caching,
        this means that if this grammar is cached by itself, the grammars
        that it imports won't be available to the grammar pool
        implementation.  Since our Schema Loader knows about this
        idiosyncrasy, if a grammar pool is registered with it,
        it will cache all schema grammars it encounters,
        including the one which it was specifically called to
        parse.  In general, it is probably advisable to register
        grammar pool implementations with grammar loaders for
        this reason; generally, one would want to cache--and make
        available to the grammar pool implementation--imported
        grammars as well as specific schema grammars, since the
        specific schemas cannot be used without those that they
        import.
     | 
 
 
 
 
 |