You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xerces.apache.org by er...@locus.apache.org on 2000/10/14 02:18:21 UTC

cvs commit: xml-xerces/java/src/org/apache/xerces/impl/validation/models CMAny.java DFAContentModel.java MixedContentModel.java SimpleContentModel.java

ericye      00/10/13 17:18:21

  Modified:    java/src/org/apache/xerces/impl/validation/models Tag:
                        xerces_j_2 CMAny.java DFAContentModel.java
                        MixedContentModel.java SimpleContentModel.java
  Log:
  Bring in All the content model validators
  
  Revision  Changes    Path
  No                   revision
  
  
  No                   revision
  
  
  1.1.2.2   +4 -4      xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/CMAny.java
  
  Index: CMAny.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/CMAny.java,v
  retrieving revision 1.1.2.1
  retrieving revision 1.1.2.2
  diff -u -r1.1.2.1 -r1.1.2.2
  --- CMAny.java	2000/10/13 21:58:18	1.1.2.1
  +++ CMAny.java	2000/10/14 00:18:15	1.1.2.2
  @@ -63,7 +63,7 @@
   /**
    * Content model any node.
    *
  - * @version $Id: CMAny.java,v 1.1.2.1 2000/10/13 21:58:18 ericye Exp $
  + * @version $Id: CMAny.java,v 1.1.2.2 2000/10/14 00:18:15 ericye Exp $
    */
   public class CMAny
       extends CMNode {
  @@ -86,7 +86,7 @@
        * XMLContentSpec.CONTENTSPECNODE_ANY, 
        * XMLContentSpec.CONTENTSPECNODE_ANY_OTHER.
        */
  -    private int fURI;
  +    private String fURI;
   
       /**
        * Part of the algorithm to convert a regex directly to a DFA
  @@ -100,7 +100,7 @@
       //
   
       /** Constructs a content model any. */
  -    public CMAny(int type, int uri, int position)  {
  +    public CMAny(int type, String uri, int position)  {
           super(type);
   
           // Store the information
  @@ -117,7 +117,7 @@
           return fType;
       }
   
  -    final int getURI() {
  +    final String getURI() {
           return fURI;
       }
   
  
  
  
  1.1.2.3   +892 -10   xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/DFAContentModel.java
  
  Index: DFAContentModel.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/DFAContentModel.java,v
  retrieving revision 1.1.2.2
  retrieving revision 1.1.2.3
  diff -u -r1.1.2.2 -r1.1.2.3
  --- DFAContentModel.java	2000/10/02 00:40:11	1.1.2.2
  +++ DFAContentModel.java	2000/10/14 00:18:16	1.1.2.3
  @@ -59,22 +59,205 @@
   
   import org.apache.xerces.xni.QName;
   import org.apache.xerces.impl.validation.ContentModelValidator;
  +import org.apache.xerces.impl.validation.XMLContentSpec;
   
   /**
    * @author Stubs generated by DesignDoc on Mon Sep 11 11:10:57 PDT 2000
  - * @version $Id: DFAContentModel.java,v 1.1.2.2 2000/10/02 00:40:11 lehors Exp $
  + * @version $Id: DFAContentModel.java,v 1.1.2.3 2000/10/14 00:18:16 ericye Exp $
  + *
  + * DFAContentModel is the derivative of ContentModel that does
  + * all of the non-trivial element content validation. This class does 
  + * the conversion from the regular expression to the DFA that 
  + * it then uses in its validation algorithm.
  + * <p>
  + * <b>Note:</b> Upstream work insures that this class will never see
  + * a content model with PCDATA in it. Any model with PCDATA is 'mixed' 
  + * and is handled via the MixedContentModel class since mixed models 
  + * are very constrained in form and easily handled via a special case. 
  + * This also makes implementation of this class much easier.
  + *
    */
   public class DFAContentModel
       implements ContentModelValidator {
   
       //
  +    // Constants
  +    //
  +    // special strings
  +
  +    /** Epsilon string. */
  +    private static final String fEpsilonString = "<<CMNODE_EPSILON>>";
  +
  +    /** End-of-content string. */
  +    private static final String fEOCString = "<<CMNODE_EOC>>";
  +
  +    /** initializing static members **/
  +    static{
  +        fEpsilonString.intern();
  +        fEOCString.intern();
  +    }
  +
  +    // debugging
  +
  +    /** Set to true to debug content model validation. */
  +    private static final boolean DEBUG_VALIDATE_CONTENT = false;
  +
  +    //
  +    // Data
  +    //
  +
  +    /* this is the EquivClassComparator object */
  +    //private EquivClassComparator comparator = null;
  +
  +    /**
  +     * This is the map of unique input symbol elements to indices into
  +     * each state's per-input symbol transition table entry. This is part
  +     * of the built DFA information that must be kept around to do the
  +     * actual validation.
  +     */
  +    private QName fElemMap[] = null;
  +
  +    /**
  +     * This is a map of whether the element map contains information 
  +     * related to ANY models.
  +     */
  +    private int fElemMapType[] = null;
  +
  +    /** The element map size. */
  +    private int fElemMapSize = 0;
  +
  +    /** Boolean to allow DTDs to validate even with namespace support. */
  +    private boolean fDTD;
  +
  +    /** Boolean to distinguish Schema Mixed Content */
  +    private boolean fMixed;
  +
  +    /**
  +     * The NFA position of the special EOC (end of content) node. This
  +     * is saved away since it's used during the DFA build.
  +     */
  +    private int fEOCPos = 0;
  +
  +
  +    /**
  +     * This is an array of booleans, one per state (there are
  +     * fTransTableSize states in the DFA) that indicates whether that
  +     * state is a final state.
  +     */
  +    private boolean fFinalStateFlags[] = null;
  +
  +    /**
  +     * The list of follow positions for each NFA position (i.e. for each
  +     * non-epsilon leaf node.) This is only used during the building of
  +     * the DFA, and is let go afterwards.
  +     */
  +    private CMStateSet fFollowList[] = null;
  +
  +    /**
  +     * This is the head node of our intermediate representation. It is
  +     * only non-null during the building of the DFA (just so that it
  +     * does not have to be passed all around.) Once the DFA is built,
  +     * this is no longer required so its nulled out.
  +     */
  +    private CMNode fHeadNode = null;
  +
  +    /**
  +     * The count of leaf nodes. This is an important number that set some
  +     * limits on the sizes of data structures in the DFA process.
  +     */
  +    private int fLeafCount = 0;
  +
  +    /**
  +     * An array of non-epsilon leaf nodes, which is used during the DFA
  +     * build operation, then dropped.
  +     */
  +    private CMLeaf fLeafList[] = null;
  +
  +    /** Array mapping ANY types to the leaf list. */
  +    private int fLeafListType[] = null;
  +
  +    //private ContentLeafNameTypeVector fLeafNameTypeVector = null;
  +
  +    /**
  +     * The string pool of our parser session. This is set during construction
  +     * and kept around.
  +     */
  +    //private StringPool fStringPool = null;
  +
  +    /**
  +     * This is the transition table that is the main by product of all
  +     * of the effort here. It is an array of arrays of ints. The first
  +     * dimension is the number of states we end up with in the DFA. The
  +     * second dimensions is the number of unique elements in the content
  +     * model (fElemMapSize). Each entry in the second dimension indicates
  +     * the new state given that input for the first dimension's start
  +     * state.
  +     * <p>
  +     * The fElemMap array handles mapping from element indexes to
  +     * positions in the second dimension of the transition table.
  +     */
  +    private int fTransTable[][] = null;
  +
  +    /**
  +     * The number of valid entries in the transition table, and in the other
  +     * related tables such as fFinalStateFlags.
  +     */
  +    private int fTransTableSize = 0;
  +
  +    /**
  +     * Flag that indicates that even though we have a "complicated"
  +     * content model, it is valid to have no content. In other words,
  +     * all parts of the content model are optional. For example:
  +     * <pre>
  +     *      &lt;!ELEMENT AllOptional (Optional*,NotRequired?)&gt;
  +     * </pre>
  +     */
  +    private boolean fEmptyContentIsValid = false;
  +
  +    // temp variables
  +
  +    /** Temporary qualified name. */
  +    private QName fQName = new QName();
  +
  +    //
       // Constructors
       //
   
  +
  +    //
  +    // Constructors
  +    //
  +
       /**
  -     * 
  +     * Constructs a DFA content model.
  +     *
  +     * @param syntaxTree    The syntax tree of the content model.
  +     * @param leafCount     The number of leaves.
  +     * @param dtd           if it is created for a DTDGrammar.
  +     *
        */
  -    public DFAContentModel() {
  +
  +    public DFAContentModel(CMNode syntaxTree, int leafCount, boolean dtd, boolean mixed) {
  +        // Store away our index and pools in members
  +        //fStringPool = stringPool;
  +        fLeafCount = leafCount;
  +
  +
  +        fDTD = dtd;
  +
  +        // this is for Schema Mixed Content
  +        fMixed = mixed;
  +
  +        //
  +        //  Ok, so lets grind through the building of the DFA. This method
  +        //  handles the high level logic of the algorithm, but it uses a
  +        //  number of helper classes to do its thing.
  +        //
  +        //  In order to avoid having hundreds of references to the error and
  +        //  string handlers around, this guy and all of his helper classes
  +        //  just throw a simple exception and we then pass it along.
  +        //
  +        buildDFA(syntaxTree);
       }
   
       //
  @@ -82,16 +265,715 @@
       //
   
       /**
  -     * validate
  -     * 
  -     * @param children 
  -     * @param offset 
  -     * @param length 
  -     * 
  -     * @return 
  +     * Check that the specified content is valid according to this
  +     * content model. This method can also be called to do 'what if' 
  +     * testing of content models just to see if they would be valid.
  +     * <p>
  +     * A value of -1 in the children array indicates a PCDATA node. All other 
  +     * indexes will be positive and represent child elements. The count can be
  +     * zero, since some elements have the EMPTY content model and that must be 
  +     * confirmed.
  +     *
  +     * @param children The children of this element.  Each integer is an index within
  +     *                 the <code>StringPool</code> of the child element name.  An index
  +     *                 of -1 is used to indicate an occurrence of non-whitespace character
  +     *                 data.
  +     * @param offset Offset into the array where the children starts.
  +     * @param length The number of entries in the <code>children</code> array.
  +     *
  +     * @return The value -1 if fully valid, else the 0 based index of the child
  +     *         that first failed. If the value returned is equal to the number
  +     *         of children, then the specified children are valid but additional
  +     *         content is required to reach a valid ending state.
  +     *
        */
       public int validate(QName[] children, int offset, int length) {
  +
  +        if (DEBUG_VALIDATE_CONTENT) 
  +            System.out.println("DFAContentModel#validateContent");
  +
  +        //
  +        // A DFA content model must *always* have at least 1 child
  +        // so a failure is given if no children present.
  +        // 
  +        // Defect 782: This is an incorrect statement because a DFA
  +        // content model is also used for constructions such as:
  +        //
  +        //     (Optional*,NotRequired?)
  +        //
  +        // where a perfectly valid content would be NO CHILDREN.
  +        // Therefore, if there are no children, we must check to
  +        // see if the CMNODE_EOC marker is a valid start state! -Ac
  +        //
  +        if (length == 0) {
  +            if (DEBUG_VALIDATE_CONTENT) {
  +                System.out.println("!!! no children");
  +                System.out.println("elemMap="+fElemMap);
  +                for (int i = 0; i < fElemMap.length; i++) {
  +                    String uri = fElemMap[i].uri;
  +                    String localpart = fElemMap[i].localpart;
  +                    
  +                    System.out.println("fElemMap["+i+"]="+uri+","+
  +                                       localpart+" ("+
  +                                       uri+", "+
  +                                       localpart+
  +                                       ')');
  +                                       
  +                }
  +                System.out.println("EOCIndex="+fEOCString);
  +            }
  +
  +            return fEmptyContentIsValid ? -1 : 0;
  +
  +        } // if child count == 0
  +
  +        //
  +        //  Lets loop through the children in the array and move our way
  +        //  through the states. Note that we use the fElemMap array to map
  +        //  an element index to a state index.
  +        //
  +        int curState = 0;
  +        for (int childIndex = 0; childIndex < length; childIndex++)
  +        {
  +            // Get the current element index out
  +            final QName curElem = children[offset + childIndex];
  +            // ignore mixed text
  +            if (fMixed && curElem.localpart == null) {
  +                continue;
  +            }
  +
  +            // Look up this child in our element map
  +            int elemIndex = 0;
  +            for (; elemIndex < fElemMapSize; elemIndex++)
  +            {
  +                int type = fElemMapType[elemIndex] & 0x0f ;
  +                if (type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
  +                    //System.out.println("fElemMap["+elemIndex+"]: "+fElemMap[elemIndex]);
  +                    if (fDTD) {
  +                        if (fElemMap[elemIndex].rawname == curElem.rawname) {
  +                            break;
  +                        }
  +                    }
  +                    else {
  +                        if (fElemMap[elemIndex].uri==curElem.uri
  +                             && fElemMap[elemIndex].localpart == curElem.localpart)
  +                            break;
  +                    }
  +                }
  +                else if (type == XMLContentSpec.CONTENTSPECNODE_ANY) {
  +                    String uri = fElemMap[elemIndex].uri;
  +                    if (uri == null || uri == curElem.uri) {
  +                        break;
  +                    }
  +                }
  +                else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL) {
  +                    if (curElem.uri == null) {
  +                        break;
  +                    }
  +                }
  +                else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER) {
  +                    if (fElemMap[elemIndex].uri != curElem.uri) {
  +                        break;
  +                    }
  +                }
  +            }
  +
  +            // If we didn't find it, then obviously not valid
  +            if (elemIndex == fElemMapSize) {
  +                if (DEBUG_VALIDATE_CONTENT) {
  +                    System.out.println("!!! didn't find it");
  +
  +                    System.out.println("curElem : " +curElem );
  +                    for (int i=0; i<fElemMapSize; i++) {
  +                        System.out.println("fElemMap["+i+"] = " +fElemMap[i] );
  +                        System.out.println("fElemMapType["+i+"] = " +fElemMapType[i] );
  +                    }
  +                }
  +
  +                return childIndex;
  +            }
  +
  +            //
  +            //  Look up the next state for this input symbol when in the
  +            //  current state.
  +            //
  +            curState = fTransTable[curState][elemIndex];
  +
  +            // If its not a legal transition, then invalid
  +            if (curState == -1) {
  +                if (DEBUG_VALIDATE_CONTENT) 
  +                    System.out.println("!!! not a legal transition");
  +                return childIndex;
  +            }
  +        }
  +
  +        //
  +        //  We transitioned all the way through the input list. However, that
  +        //  does not mean that we ended in a final state. So check whether
  +        //  our ending state is a final state.
  +        //
  +        if (DEBUG_VALIDATE_CONTENT) 
  +            System.out.println("curState="+curState+", childCount="+length);
  +        if (!fFinalStateFlags[curState])
  +            return length;
  +
  +        // success!
           return -1;
       } // validate
  +
  +
  +    //
  +    // Private methods
  +    //
  +
  +    /** 
  +     * Builds the internal DFA transition table from the given syntax tree.
  +     *
  +     * @param syntaxTree The syntax tree.
  +     *
  +     * @exception CMException Thrown if DFA cannot be built.
  +     */
  +    private void buildDFA(CMNode syntaxTree) 
  +    {
  +        //
  +        //  The first step we need to take is to rewrite the content model
  +        //  using our CMNode objects, and in the process get rid of any
  +        //  repetition short cuts, converting them into '*' style repetitions
  +        //  or getting rid of repetitions altogether.
  +        //
  +        //  The conversions done are:
  +        //
  +        //  x+ -> (x|x*)
  +        //  x? -> (x|epsilon)
  +        //
  +        //  This is a relatively complex scenario. What is happening is that
  +        //  we create a top level binary node of which the special EOC value
  +        //  is set as the right side node. The the left side is set to the
  +        //  rewritten syntax tree. The source is the original content model
  +        //  info from the decl pool. The rewrite is done by buildSyntaxTree()
  +        //  which recurses the decl pool's content of the element and builds
  +        //  a new tree in the process.
  +        //
  +        //  Note that, during this operation, we set each non-epsilon leaf
  +        //  node's DFA state position and count the number of such leafs, which
  +        //  is left in the fLeafCount member.
  +        //
  +        //  The nodeTmp object is passed in just as a temp node to use during
  +        //  the recursion. Otherwise, we'd have to create a new node on every
  +        //  level of recursion, which would be piggy in Java (as is everything
  +        //  for that matter.)
  +        //
  +        fQName.setValues(null, fEOCString, fEOCString, null);
  +        CMLeaf nodeEOC = new CMLeaf(fQName);
  +        fHeadNode = new CMBinOp
  +        (
  +            XMLContentSpec.CONTENTSPECNODE_SEQ
  +            , syntaxTree
  +            , nodeEOC
  +        );
  +
  +        //
  +        //  And handle specially the EOC node, which also must be numbered
  +        //  and counted as a non-epsilon leaf node. It could not be handled
  +        //  in the above tree build because it was created before all that
  +        //  started. We save the EOC position since its used during the DFA
  +        //  building loop.
  +        //
  +        fEOCPos = fLeafCount;
  +        nodeEOC.setPosition(fLeafCount++);
  +
  +        //
  +        //  Ok, so now we have to iterate the new tree and do a little more
  +        //  work now that we know the leaf count. One thing we need to do is
  +        //  to calculate the first and last position sets of each node. This
  +        //  is cached away in each of the nodes.
  +        //
  +        //  Along the way we also set the leaf count in each node as the
  +        //  maximum state count. They must know this in order to create their
  +        //  first/last pos sets.
  +        //
  +        //  We also need to build an array of references to the non-epsilon
  +        //  leaf nodes. Since we iterate it in the same way as before, this
  +        //  will put them in the array according to their position values.
  +        //
  +        fLeafList = new CMLeaf[fLeafCount];
  +        fLeafListType = new int[fLeafCount];
  +        postTreeBuildInit(fHeadNode, 0);
  +
  +        //
  +        //  And, moving onward... We now need to build the follow position
  +        //  sets for all the nodes. So we allocate an array of state sets,
  +        //  one for each leaf node (i.e. each DFA position.)
  +        //
  +        fFollowList = new CMStateSet[fLeafCount];
  +        for (int index = 0; index < fLeafCount; index++)
  +            fFollowList[index] = new CMStateSet(fLeafCount);
  +        calcFollowList(fHeadNode);
  +        //
  +        //  And finally the big push... Now we build the DFA using all the
  +        //  states and the tree we've built up. First we set up the various
  +        //  data structures we are going to use while we do this.
  +        //
  +        //  First of all we need an array of unique element names in our
  +        //  content model. For each transition table entry, we need a set of
  +        //  contiguous indices to represent the transitions for a particular
  +        //  input element. So we need to a zero based range of indexes that
  +        //  map to element types. This element map provides that mapping.
  +        //
  +        fElemMap = new QName[fLeafCount];
  +        fElemMapType = new int[fLeafCount];
  +        fElemMapSize = 0;
  +        for (int outIndex = 0; outIndex < fLeafCount; outIndex++)
  +        {
  +            fElemMap[outIndex] = new QName();
  +
  +            /****
  +            if ( (fLeafListType[outIndex] & 0x0f) != 0 ) {
  +                if (fLeafNameTypeVector == null) {
  +                    fLeafNameTypeVector = new ContentLeafNameTypeVector();
  +                }
  +            }
  +            /****/
  +
  +            // Get the current leaf's element index
  +            final QName element = fLeafList[outIndex].getElement();
  +
  +            // See if the current leaf node's element index is in the list
  +            int inIndex = 0;
  +            for (; inIndex < fElemMapSize; inIndex++)
  +            {
  +                if (fDTD) {
  +                    if (fElemMap[inIndex].rawname == element.rawname) {
  +                        break;
  +                    }
  +                }
  +                else {
  +                    if (fElemMap[inIndex].uri == element.uri &&
  +                        fElemMap[inIndex].localpart == element.localpart && 
  +                        fElemMapType[inIndex] == fLeafListType[outIndex] )
  +                        break;
  +                }
  +            }
  +
  +            // If it was not in the list, then add it, if not the EOC node
  +            if (inIndex == fElemMapSize) {
  +                //if (fDTD) {
  +                //    fElemMap[fElemMapSize].setValues(-1, element.rawname, element.rawname, -1);
  +                //}
  +                //else {
  +                    fElemMap[fElemMapSize].setValues(element);
  +                //}
  +                fElemMapType[fElemMapSize] = fLeafListType[outIndex];
  +                fElemMapSize++;
  +            }
  +        }
  +        // set up the fLeafNameTypeVector object if there is one.
  +        /*****
  +        if (fLeafNameTypeVector != null) {
  +            fLeafNameTypeVector.setValues(fElemMap, fElemMapType, fElemMapSize);
  +        }
  +        /*****/
  +
  +        //
  +        //  Next lets create some arrays, some that that hold transient
  +        //  information during the DFA build and some that are permament.
  +        //  These are kind of sticky since we cannot know how big they will
  +        //  get, but we don't want to use any Java collections because of
  +        //  performance.
  +        //
  +        //  Basically they will probably be about fLeafCount*2 on average,
  +        //  but can be as large as 2^(fLeafCount*2), worst case. So we start
  +        //  with fLeafCount*4 as a middle ground. This will be very unlikely
  +        //  to ever have to expand, though it if does, the overhead will be
  +        //  somewhat ugly.
  +        //
  +        int curArraySize = fLeafCount * 4;
  +        CMStateSet[] statesToDo = new CMStateSet[curArraySize];
  +        fFinalStateFlags = new boolean[curArraySize];
  +        fTransTable = new int[curArraySize][];
  +
  +        //
  +        //  Ok we start with the initial set as the first pos set of the
  +        //  head node (which is the seq node that holds the content model
  +        //  and the EOC node.)
  +        //
  +        CMStateSet setT = fHeadNode.firstPos();
  +
  +        //
  +        //  Init our two state flags. Basically the unmarked state counter
  +        //  is always chasing the current state counter. When it catches up,
  +        //  that means we made a pass through that did not add any new states
  +        //  to the lists, at which time we are done. We could have used a
  +        //  expanding array of flags which we used to mark off states as we
  +        //  complete them, but this is easier though less readable maybe.
  +        //
  +        int unmarkedState = 0;
  +        int curState = 0;
  +
  +        //
  +        //  Init the first transition table entry, and put the initial state
  +        //  into the states to do list, then bump the current state.
  +        //
  +        fTransTable[curState] = makeDefStateList();
  +        statesToDo[curState] = setT;
  +        curState++;
  +
  +        //
  +        //  Ok, almost done with the algorithm... We now enter the
  +        //  loop where we go until the states done counter catches up with
  +        //  the states to do counter.
  +        //
  +        while (unmarkedState < curState)
  +        {
  +            //
  +            //  Get the first unmarked state out of the list of states to do.
  +            //  And get the associated transition table entry.
  +            //
  +            setT = statesToDo[unmarkedState];
  +            int[] transEntry = fTransTable[unmarkedState];
  +
  +            // Mark this one final if it contains the EOC state
  +            fFinalStateFlags[unmarkedState] = setT.getBit(fEOCPos);
  +
  +            // Bump up the unmarked state count, marking this state done
  +            unmarkedState++;
  +
  +            // Loop through each possible input symbol in the element map
  +            CMStateSet newSet = null;
  +            for (int elemIndex = 0; elemIndex < fElemMapSize; elemIndex++)
  +            {
  +                //
  +                //  Build up a set of states which is the union of all of
  +                //  the follow sets of DFA positions that are in the current
  +                //  state. If we gave away the new set last time through then
  +                //  create a new one. Otherwise, zero out the existing one.
  +                //
  +                if (newSet == null)
  +                    newSet = new CMStateSet(fLeafCount);
  +                else
  +                    newSet.zeroBits();
  +
  +                for (int leafIndex = 0; leafIndex < fLeafCount; leafIndex++)
  +                {
  +                    // If this leaf index (DFA position) is in the current set...
  +                    if (setT.getBit(leafIndex))
  +                    {
  +                        //
  +                        //  If this leaf is the current input symbol, then we
  +                        //  want to add its follow list to the set of states to
  +                        //  transition to from the current state.
  +                        //
  +                        final QName leaf = fLeafList[leafIndex].getElement();
  +                        final QName element = fElemMap[elemIndex];
  +                        if (fDTD) {
  +                            if (leaf.rawname == element.rawname) {
  +                                newSet.union(fFollowList[leafIndex]);
  +                            }
  +                        }
  +                        else {
  +                            if (leaf.uri == element.uri &&
  +                                leaf.localpart == element.localpart)
  +                                newSet.union(fFollowList[leafIndex]);
  +                        }
  +                    }
  +                }
  +
  +                //
  +                //  If this new set is not empty, then see if its in the list
  +                //  of states to do. If not, then add it.
  +                //
  +                if (!newSet.isEmpty())
  +                {
  +                    //
  +                    //  Search the 'states to do' list to see if this new
  +                    //  state set is already in there.
  +                    //
  +                    int stateIndex = 0;
  +                    for (; stateIndex < curState; stateIndex++)
  +                    {
  +                        if (statesToDo[stateIndex].isSameSet(newSet))
  +                            break;
  +                    }
  +
  +                    // If we did not find it, then add it
  +                    if (stateIndex == curState)
  +                    {
  +                        //
  +                        //  Put this new state into the states to do and init
  +                        //  a new entry at the same index in the transition
  +                        //  table.
  +                        //
  +                        statesToDo[curState] = newSet;
  +                        fTransTable[curState] = makeDefStateList();
  +
  +                        // We now have a new state to do so bump the count
  +                        curState++;
  +
  +                        //
  +                        //  Null out the new set to indicate we adopted it.
  +                        //  This will cause the creation of a new set on the
  +                        //  next time around the loop.
  +                        //
  +                        newSet = null;
  +                    }
  +
  +                    //
  +                    //  Now set this state in the transition table's entry
  +                    //  for this element (using its index), with the DFA
  +                    //  state we will move to from the current state when we
  +                    //  see this input element.
  +                    //
  +                    transEntry[elemIndex] = stateIndex;
  +
  +                    // Expand the arrays if we're full
  +                    if (curState == curArraySize)
  +                    {
  +                        //
  +                        //  Yikes, we overflowed the initial array size, so
  +                        //  we've got to expand all of these arrays. So adjust
  +                        //  up the size by 50% and allocate new arrays.
  +                        //
  +                        final int newSize = (int)(curArraySize * 1.5);
  +                        CMStateSet[] newToDo = new CMStateSet[newSize];
  +                        boolean[] newFinalFlags = new boolean[newSize];
  +                        int[][] newTransTable = new int[newSize][];
  +
  +                        // Copy over all of the existing content
  +                        for (int expIndex = 0; expIndex < curArraySize; expIndex++)
  +                        {
  +                            newToDo[expIndex] = statesToDo[expIndex];
  +                            newFinalFlags[expIndex] = fFinalStateFlags[expIndex];
  +                            newTransTable[expIndex] = fTransTable[expIndex];
  +                        }
  +
  +                        // Store the new array size
  +                        curArraySize = newSize;
  +                        statesToDo = newToDo;
  +                        fFinalStateFlags = newFinalFlags;
  +                        fTransTable = newTransTable;
  +                    }
  +                }
  +            }
  +        }
  +
  +        // Check to see if we can set the fEmptyContentIsValid flag.
  +        fEmptyContentIsValid = ((CMBinOp)fHeadNode).getLeft().isNullable();
  +
  +        //
  +        //  And now we can say bye bye to the temp representation since we've
  +        //  built the DFA.
  +        //
  +        if (DEBUG_VALIDATE_CONTENT) 
  +            dumpTree(fHeadNode, 0);
  +        fHeadNode = null;
  +        fLeafList = null;
  +        fFollowList = null;
  +
  +    }
  +
  +    /**
  +     * Calculates the follow list of the current node.
  +     *
  +     * @param nodeCur The curent node.
  +     *
  +     * @exception CMException Thrown if follow list cannot be calculated.
  +     */
  +    private void calcFollowList(CMNode nodeCur) 
  +    {
  +        // Recurse as required
  +        if (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_CHOICE)
  +        {
  +            // Recurse only
  +            calcFollowList(((CMBinOp)nodeCur).getLeft());
  +            calcFollowList(((CMBinOp)nodeCur).getRight());
  +        }
  +         else if (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_SEQ)
  +        {
  +            // Recurse first
  +            calcFollowList(((CMBinOp)nodeCur).getLeft());
  +            calcFollowList(((CMBinOp)nodeCur).getRight());
  +
  +            //
  +            //  Now handle our level. We use our left child's last pos
  +            //  set and our right child's first pos set, so go ahead and
  +            //  get them ahead of time.
  +            //
  +            final CMStateSet last  = ((CMBinOp)nodeCur).getLeft().lastPos();
  +            final CMStateSet first = ((CMBinOp)nodeCur).getRight().firstPos();
  +
  +            //
  +            //  Now, for every position which is in our left child's last set
  +            //  add all of the states in our right child's first set to the
  +            //  follow set for that position.
  +            //
  +            for (int index = 0; index < fLeafCount; index++)
  +            {
  +                if (last.getBit(index))
  +                    fFollowList[index].union(first);
  +            }
  +        }
  +         else if (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE)
  +        {
  +            // Recurse first
  +            calcFollowList(((CMUniOp)nodeCur).getChild());
  +
  +            //
  +            //  Now handle our level. We use our own first and last position
  +            //  sets, so get them up front.
  +            //
  +            final CMStateSet first = nodeCur.firstPos();
  +            final CMStateSet last  = nodeCur.lastPos();
  +
  +            //
  +            //  For every position which is in our last position set, add all
  +            //  of our first position states to the follow set for that
  +            //  position.
  +            //
  +            for (int index = 0; index < fLeafCount; index++)
  +            {
  +                if (last.getBit(index))
  +                    fFollowList[index].union(first);
  +            }
  +        }
  +         else if ((nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE)
  +              ||  (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE))
  +        {
  +            throw new RuntimeException("ImplementationMessages.VAL_NIICM");
  +        }
  +    }
  +
  +    /**
  +     * Dumps the tree of the current node to standard output.
  +     *
  +     * @param nodeCur The current node.
  +     * @param level   The maximum levels to output.
  +     *
  +     * @exception CMException Thrown on error.
  +     */
  +    private void dumpTree(CMNode nodeCur, int level) 
  +    {
  +        for (int index = 0; index < level; index++)
  +            System.out.print("   ");
  +
  +        int type = nodeCur.type();
  +        if ((type == XMLContentSpec.CONTENTSPECNODE_CHOICE)
  +        ||  (type == XMLContentSpec.CONTENTSPECNODE_SEQ))
  +        {
  +            if (type == XMLContentSpec.CONTENTSPECNODE_CHOICE)
  +                System.out.print("Choice Node ");
  +            else
  +                System.out.print("Seq Node ");
  +
  +            if (nodeCur.isNullable())
  +                System.out.print("Nullable ");
  +
  +            System.out.print("firstPos=");
  +            System.out.print(nodeCur.firstPos().toString());
  +            System.out.print(" lastPos=");
  +            System.out.println(nodeCur.lastPos().toString());
  +
  +            dumpTree(((CMBinOp)nodeCur).getLeft(), level+1);
  +            dumpTree(((CMBinOp)nodeCur).getRight(), level+1);
  +        }
  +         else if (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE)
  +        {
  +            System.out.print("Rep Node ");
  +
  +            if (nodeCur.isNullable())
  +                System.out.print("Nullable ");
  +
  +            System.out.print("firstPos=");
  +            System.out.print(nodeCur.firstPos().toString());
  +            System.out.print(" lastPos=");
  +            System.out.println(nodeCur.lastPos().toString());
  +
  +            dumpTree(((CMUniOp)nodeCur).getChild(), level+1);
  +        }
  +         else if (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_LEAF)
  +        {
  +            System.out.print
  +            (
  +                "Leaf: (pos="
  +                + ((CMLeaf)nodeCur).getPosition()
  +                + "), "
  +                + ((CMLeaf)nodeCur).getElement()
  +                + "(elemIndex="
  +                + ((CMLeaf)nodeCur).getElement()
  +                + ") "
  +            );
  +
  +            if (nodeCur.isNullable())
  +                System.out.print(" Nullable ");
  +
  +            System.out.print("firstPos=");
  +            System.out.print(nodeCur.firstPos().toString());
  +            System.out.print(" lastPos=");
  +            System.out.println(nodeCur.lastPos().toString());
  +        }
  +         else
  +        {
  +            throw new RuntimeException("ImplementationMessages.VAL_NIICM");
  +        }
  +    }
  +
  +
  +    /**
  +     * -1 is used to represent bad transitions in the transition table
  +     * entry for each state. So each entry is initialized to an all -1
  +     * array. This method creates a new entry and initializes it.
  +     */
  +    private int[] makeDefStateList()
  +    {
  +        int[] retArray = new int[fElemMapSize];
  +        for (int index = 0; index < fElemMapSize; index++)
  +            retArray[index] = -1;
  +        return retArray;
  +    }
  +
  +    /** Post tree build initialization. */
  +    private int postTreeBuildInit(CMNode nodeCur, int curIndex) 
  +    {
  +        // Set the maximum states on this node
  +        nodeCur.setMaxStates(fLeafCount);
  +
  +        // Recurse as required
  +        if ((nodeCur.type() & 0x0f) == XMLContentSpec.CONTENTSPECNODE_ANY ||
  +            (nodeCur.type() & 0x0f) == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL ||
  +            (nodeCur.type() & 0x0f) == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER) {
  +            // REVISIT: Don't waste these structures.
  +            QName qname = new QName(null, null, null, ((CMAny)nodeCur).getURI());
  +            fLeafList[curIndex] = new CMLeaf(qname, ((CMAny)nodeCur).getPosition());
  +            fLeafListType[curIndex] = nodeCur.type();
  +            curIndex++;
  +        }
  +        else if ((nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_CHOICE)
  +        ||  (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_SEQ))
  +        {
  +            curIndex = postTreeBuildInit(((CMBinOp)nodeCur).getLeft(), curIndex);
  +            curIndex = postTreeBuildInit(((CMBinOp)nodeCur).getRight(), curIndex);
  +        }
  +         else if (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE)
  +        {
  +            curIndex = postTreeBuildInit(((CMUniOp)nodeCur).getChild(), curIndex);
  +        }
  +         else if (nodeCur.type() == XMLContentSpec.CONTENTSPECNODE_LEAF)
  +        {
  +            //
  +            //  Put this node in the leaf list at the current index if its
  +            //  a non-epsilon leaf.
  +            //
  +             final QName node = ((CMLeaf)nodeCur).getElement();
  +            if (node.localpart != fEpsilonString) {
  +                fLeafList[curIndex] = (CMLeaf)nodeCur;
  +                fLeafListType[curIndex] = XMLContentSpec.CONTENTSPECNODE_LEAF;
  +                curIndex++;
  +            }
  +        }
  +         else
  +        {
  +            throw new RuntimeException("ImplementationMessages.VAL_NIICM");
  +        }
  +        return curIndex;
  +    }
   
   } // class DFAContentModel
  
  
  
  1.1.2.4   +185 -13   xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/MixedContentModel.java
  
  Index: MixedContentModel.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/MixedContentModel.java,v
  retrieving revision 1.1.2.3
  retrieving revision 1.1.2.4
  diff -u -r1.1.2.3 -r1.1.2.4
  --- MixedContentModel.java	2000/10/13 01:31:46	1.1.2.3
  +++ MixedContentModel.java	2000/10/14 00:18:17	1.1.2.4
  @@ -59,10 +59,24 @@
   
   import org.apache.xerces.xni.QName;
   import org.apache.xerces.impl.validation.ContentModelValidator;
  +import org.apache.xerces.impl.validation.XMLContentSpec;
   
   /**
    * @author Stubs generated by DesignDoc on Mon Sep 11 11:10:57 PDT 2000
  - * @version $Id: MixedContentModel.java,v 1.1.2.3 2000/10/13 01:31:46 ericye Exp $
  + * @version $Id: MixedContentModel.java,v 1.1.2.4 2000/10/14 00:18:17 ericye Exp $
  + *
  + * MixedContentModel is a derivative of the abstract content model base
  + * class that handles the special case of mixed model elements. If an element
  + * is mixed model, it has PCDATA as its first possible content, followed
  + * by an alternation of the possible children. The children cannot have any
  + * numeration or order, so it must look like this:
  + * <pre>
  + *   &lt;!ELEMENT Foo ((#PCDATA|a|b|c|)*)&gt;
  + * </pre>
  + * So, all we have to do is to keep an array of the possible children and
  + * validate by just looking up each child being validated by looking it up
  + * in the list.
  + *
    */
   public class MixedContentModel
       implements ContentModelValidator {
  @@ -71,27 +85,55 @@
       // Data
       //
   
  -    /** fCount */
  -    protected int fCount;
  +    /** The count of possible children that we have to deal with. */
  +    private int fCount;
   
  -    /** fChildren */
  -    protected QName[] fChildren;
  +    /** The list of possible children that we have to accept. */
  +    private QName fChildren[];
   
  -    /** fOrdered */
  -    protected boolean fOrdered;
  +    /** The type of the children to support ANY. */
  +    private int fChildrenType[];
   
  +    /* this is the EquivClassComparator object */
  +    //private EquivClassComparator comparator = null;
  +    
  +    /** 
  +     * True if mixed content model is ordered. DTD mixed content models
  +     * are <em>always</em> unordered.
  +     */
  +    private boolean fOrdered;
  +
  +    /** Boolean to allow DTDs to validate even with namespace support. */
  +    private boolean fDTD;
  +
       //
       // Constructors
       //
   
       /**
  -     * 
  -     * 
  -     * @param count 
  -     * @param children 
  -     * @param ordered 
  +     * Constructs a mixed content model.
  +     *
  +     * @param children The list of allowed children.
  +     * @param type The list of the types of the children.
  +     * @param offset The start offset position in the children.
  +     * @param length The child count.
  +     * @param ordered True if content must be ordered.
  +     * @param dtd True if it is for a DTDGrammar.
  +     *
        */
  -    public MixedContentModel(QName[] children, int[] type, int offset, int count , boolean ordered, boolean isDTD) {
  +    public MixedContentModel(QName[] children, int[] type, int offset, int length , boolean ordered, boolean dtd) {
  +        // Make our own copy now, which is exactly the right size
  +        fCount = length;
  +        fChildren = new QName[fCount];
  +        fChildrenType = new int[fCount];
  +        for (int i = 0; i < fCount; i++) {
  +            fChildren[i] = new QName(children[offset + i]);
  +            fChildrenType[i] = type[offset + i];
  +        }
  +        fOrdered = ordered;
  +
  +        fDTD = dtd;
  +
       }
   
       //
  @@ -107,7 +149,137 @@
        * 
        * @return 
        */
  +    
  +    /**
  +     * Check that the specified content is valid according to this
  +     * content model. This method can also be called to do 'what if' 
  +     * testing of content models just to see if they would be valid.
  +     * <p>
  +     * A value of -1 in the children array indicates a PCDATA node. All other 
  +     * indexes will be positive and represent child elements. The count can be
  +     * zero, since some elements have the EMPTY content model and that must be 
  +     * confirmed.
  +     *
  +     * @param children The children of this element.  Each integer is an index within
  +     *                 the <code>StringPool</code> of the child element name.  An index
  +     *                 of -1 is used to indicate an occurrence of non-whitespace character
  +     *                 data.
  +     * @param offset Offset into the array where the children starts.
  +     * @param length The number of entries in the <code>children</code> array.
  +     *
  +     * @return The value -1 if fully valid, else the 0 based index of the child
  +     *         that first failed. If the value returned is equal to the number
  +     *         of children, then the specified children are valid but additional
  +     *         content is required to reach a valid ending state.
  +     *
  +     */
       public int validate(QName[] children, int offset, int length) {
  +        
  +        // must match order
  +        if (fOrdered) {
  +            int inIndex = 0;
  +            for (int outIndex = 0; outIndex < length; outIndex++) {
  +
  +                // ignore mixed text
  +                final QName curChild = children[offset + outIndex];
  +                if (curChild.localpart == null) {
  +                    continue;
  +                }
  +
  +                // element must match
  +                int type = fChildrenType[inIndex];
  +                if (type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
  +                    if (fDTD) {
  +                        if (fChildren[inIndex].rawname != children[offset + outIndex].rawname) {
  +                            return outIndex;
  +                        }
  +                    }
  +                    else {
  +                        if (fChildren[inIndex].uri != children[offset + outIndex].uri &&
  +                            fChildren[inIndex].localpart != children[offset + outIndex].localpart) {
  +                            return outIndex;
  +                        }
  +                    }
  +                }
  +                else if (type == XMLContentSpec.CONTENTSPECNODE_ANY) {
  +                    String uri = fChildren[inIndex].uri;
  +                    if (uri != null && uri != children[outIndex].uri) {
  +                        return outIndex;
  +                    }
  +                }
  +                else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL) {
  +                    if (children[outIndex].uri != null) {
  +                        return outIndex;
  +                    }
  +                }
  +                else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER) {
  +                    if (fChildren[inIndex].uri == children[outIndex].uri) {
  +                        return outIndex;
  +                    }
  +                }
  +                
  +                // advance index
  +                inIndex++;
  +            }
  +        }
  +
  +        // can appear in any order
  +        else {
  +            for (int outIndex = 0; outIndex < length; outIndex++)
  +            {
  +                // Get the current child out of the source index
  +                final QName curChild = children[offset + outIndex];
  +    
  +                // If its PCDATA, then we just accept that
  +                if (curChild.localpart == null)
  +                    continue;
  +    
  +                // And try to find it in our list
  +                int inIndex = 0;
  +                for (; inIndex < fCount; inIndex++)
  +                {
  +                    int type = fChildrenType[inIndex];
  +                    if (type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
  +                        if (fDTD) {
  +                            if (curChild.rawname == fChildren[inIndex].rawname) {
  +                                break;
  +                            }
  +                        }
  +                        else {
  +                            if (curChild.uri == fChildren[inIndex].uri &&
  +                                curChild.localpart == fChildren[inIndex].localpart)
  +                                break;
  +                        }
  +                    }
  +                    else if (type == XMLContentSpec.CONTENTSPECNODE_ANY) {
  +                        String uri = fChildren[inIndex].uri;
  +                        if (uri == null || uri == children[outIndex].uri) {
  +                            break;
  +                        }
  +                    }
  +                    else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL) {
  +                        if (children[outIndex].uri == null) {
  +                            break;
  +                        }
  +                    }
  +                    else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER) {
  +                        if (fChildren[inIndex].uri != children[outIndex].uri) {
  +                            break;
  +                        }
  +                    }
  +                    // REVISIT: What about checking for multiple ANY matches?
  +                    //          The content model ambiguity *could* be checked
  +                    //          by the caller before constructing the mixed
  +                    //          content model.
  +                }
  +
  +                // We did not find this one, so the validation failed
  +                if (inIndex == fCount)
  +                    return outIndex;
  +            }
  +        }
  +
  +        // Everything seems to be in order, so return success
           return -1;
       } // validate
   
  
  
  
  1.1.2.4   +268 -20   xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/SimpleContentModel.java
  
  Index: SimpleContentModel.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/validation/models/Attic/SimpleContentModel.java,v
  retrieving revision 1.1.2.3
  retrieving revision 1.1.2.4
  diff -u -r1.1.2.3 -r1.1.2.4
  --- SimpleContentModel.java	2000/10/13 01:31:46	1.1.2.3
  +++ SimpleContentModel.java	2000/10/14 00:18:17	1.1.2.4
  @@ -59,10 +59,31 @@
   
   import org.apache.xerces.xni.QName;
   import org.apache.xerces.impl.validation.ContentModelValidator;
  +import org.apache.xerces.impl.validation.XMLContentSpec;
   
   /**
    * @author Stubs generated by DesignDoc on Mon Sep 11 11:10:57 PDT 2000
  - * @version $Id: SimpleContentModel.java,v 1.1.2.3 2000/10/13 01:31:46 ericye Exp $
  + * @version $Id: SimpleContentModel.java,v 1.1.2.4 2000/10/14 00:18:17 ericye Exp $
  + *
  + * SimpleContentModel is a derivative of the abstract content model base
  + * class that handles a small set of simple content models that are just
  + * way overkill to give the DFA treatment.
  + * <p>
  + * This class handles the following scenarios:
  + * <ul>
  + * <li> a
  + * <li> a?
  + * <li> a*
  + * <li> a+
  + * <li> a,b
  + * <li> a|b
  + * </ul>
  + * <p>
  + * These all involve a unary operation with one element type, or a binary
  + * operation with two elements. These are very simple and can be checked
  + * in a simple way without a DFA and without the overhead of setting up a
  + * DFA for such a simple check.
  + *
    */
   public class SimpleContentModel
       implements ContentModelValidator {
  @@ -81,27 +102,65 @@
       // Data
       //
   
  -    /** fOperator */
  -    protected short fOperator;
   
  -    /** fFirstChild */
  -    protected QName fFirstChild;
  +    /**
  +     * The element decl pool indices of the first (and optional second)
  +     * child node. The operation code tells us whether the second child
  +     * is used or not.
  +     */
  +    private QName fFirstChild = new QName();
   
  -    /** fSecondChild */
  -    protected QName fSecondChild;
  +    /**
  +     * The element decl pool indices of the first (and optional second)
  +     * child node. The operation code tells us whether the second child
  +     * is used or not.
  +     */
  +    private QName fSecondChild = new QName();
   
  +    /**
  +     * The operation that this object represents. Since this class only
  +     * does simple contents, there is only ever a single operation
  +     * involved (i.e. the children of the operation are always one or
  +     * two leafs.) This is one of the XMLDTDParams.CONTENTSPECNODE_XXX values.
  +     */
  +    private int fOperator;
  +
  +    /** Boolean to allow DTDs to validate even with namespace support. */
  +    private boolean fDTD;
  +
  +    /* this is the EquivClassComparator object */
  +    //private EquivClassComparator comparator = null;
  +    
  +
       //
       // Constructors
       //
   
       /**
  -     * 
  -     * 
  -     * @param operator 
  -     * @param firstChild 
  -     * @param secondChild 
  +     * Constructs a simple content model.
  +     *
  +     * @param operator The content model operator.
  +     * @param firstChild The first child index.
  +     * @param secondChild The second child index.
  +     * @param dtd if it is for a DTDGrammar.
  +     *
        */
  -    public SimpleContentModel(short operator, QName firstChild, QName secondChild, boolean isDTD) {
  +    public SimpleContentModel(short operator, QName firstChild, QName secondChild, boolean dtd) {
  +        //
  +        //  Store away the children and operation. This is all we need to
  +        //  do the content model check.
  +        //
  +        //  The operation is one of the ContentSpecNode.NODE_XXX values!
  +        //
  +        fFirstChild.setValues(firstChild);
  +        if (secondChild != null) {
  +            fSecondChild.setValues(secondChild);
  +        }
  +        else {
  +            fSecondChild.clear();
  +        }
  +        fOperator = operator;
  +        fDTD = dtd;
       }
   
       //
  @@ -109,15 +168,204 @@
       //
   
       /**
  -     * validate
  -     * 
  -     * @param children 
  -     * @param offset 
  -     * @param length 
  -     * 
  -     * @return 
  +     * Check that the specified content is valid according to this
  +     * content model. This method can also be called to do 'what if' 
  +     * testing of content models just to see if they would be valid.
  +     * <p>
  +     * A value of -1 in the children array indicates a PCDATA node. All other 
  +     * indexes will be positive and represent child elements. The count can be
  +     * zero, since some elements have the EMPTY content model and that must be 
  +     * confirmed.
  +     *
  +     * @param children The children of this element.  Each integer is an index within
  +     *                 the <code>StringPool</code> of the child element name.  An index
  +     *                 of -1 is used to indicate an occurrence of non-whitespace character
  +     *                 data.
  +     * @param offset Offset into the array where the children starts.
  +     * @param length The number of entries in the <code>children</code> array.
  +     *
  +     * @return The value -1 if fully valid, else the 0 based index of the child
  +     *         that first failed. If the value returned is equal to the number
  +     *         of children, then the specified children are valid but additional
  +     *         content is required to reach a valid ending state.
  +     *
        */
       public int validate(QName[] children, int offset, int length) {
  +
  +        //
  +        //  According to the type of operation, we do the correct type of
  +        //  content check.
  +        //
  +        switch(fOperator)
  +        {
  +            case XMLContentSpec.CONTENTSPECNODE_LEAF :
  +                // If there is not a child, then report an error at index 0
  +                if (length == 0)
  +                    return 0;
  +
  +                // If the 0th child is not the right kind, report an error at 0
  +                if (fDTD) {
  +                    if (children[offset].rawname != fFirstChild.rawname) {
  +                        return 0;
  +                    }
  +                }
  +                else {
  +                    if (children[offset].uri != fFirstChild.uri || 
  +                        children[offset].localpart != fFirstChild.localpart)
  +                        return 0;
  +                }
  +
  +                // If more than one child, report an error at index 1
  +                if (length > 1)
  +                    return 1;
  +                break;
  +
  +            case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE :
  +                //
  +                //  If there is one child, make sure its the right type. If not,
  +                //  then its an error at index 0.
  +                //
  +                if (length == 1) {
  +                    if (fDTD) {
  +                        if (children[offset].rawname != fFirstChild.rawname) {
  +                            return 0;
  +                        }
  +                    }
  +                    else {
  +                        if (children[offset].uri != fFirstChild.uri || 
  +                         children[offset].localpart != fFirstChild.localpart)
  +                        return 0;
  +                    }
  +                }
  +
  +                //
  +                //  If the child count is greater than one, then obviously
  +                //  bad, so report an error at index 1.
  +                //
  +                if (length > 1)
  +                    return 1;
  +                break;
  +
  +            case XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE :
  +                //
  +                //  If the child count is zero, that's fine. If its more than
  +                //  zero, then make sure that all children are of the element
  +                //  type that we stored. If not, report the index of the first
  +                //  failed one.
  +                //
  +                if (length > 0)
  +                {
  +                    if (fDTD) {
  +                        for (int index = 0; index < length; index++) {
  +                            if (children[offset + index].rawname != fFirstChild.rawname) {
  +                                return index;
  +                            }
  +                        }
  +                    }
  +                    else {
  +                        for (int index = 0; index < length; index++)
  +                        {
  +                            if (children[offset + index].uri != fFirstChild.uri || 
  +                                children[offset + index].localpart != fFirstChild.localpart)
  +                                return index;
  +                        }
  +                    }
  +                }
  +                break;
  +
  +            case XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE :
  +                //
  +                //  If the child count is zero, that's an error so report
  +                //  an error at index 0.
  +                //
  +                if (length == 0)
  +                    return 0;
  +
  +                //
  +                //  Otherwise we have to check them all to make sure that they
  +                //  are of the correct child type. If not, then report the index
  +                //  of the first one that is not.
  +                //
  +                if (fDTD) {
  +                    for (int index = 0; index < length; index++) {
  +                        if (children[offset + index].rawname != fFirstChild.rawname) {
  +                            return index;
  +                        }
  +                    }
  +                }
  +                else {
  +                    for (int index = 0; index < length; index++)
  +                    {
  +                        if (children[offset + index].uri != fFirstChild.uri || 
  +                            children[offset + index].localpart != fFirstChild.localpart)
  +                            return index;
  +                    }
  +                }
  +                break;
  +
  +            case XMLContentSpec.CONTENTSPECNODE_CHOICE :
  +                //
  +                //  There must be one and only one child, so if the element count
  +                //  is zero, return an error at index 0.
  +                //
  +                if (length == 0)
  +                    return 0;
  +
  +                // If the zeroth element isn't one of our choices, error at 0
  +                if (fDTD) {
  +                    if ((children[offset].rawname != fFirstChild.rawname) &&
  +                        (children[offset].rawname != fSecondChild.rawname)) {
  +                        return 0;
  +                    }
  +                }
  +                else {
  +                    if ((children[offset].uri != fFirstChild.uri || children[offset].localpart != fFirstChild.localpart) &&
  +                        (children[offset].uri != fSecondChild.uri || children[offset].localpart != fSecondChild.localpart))
  +                        return 0;
  +                }
  +
  +                // If there is more than one element, then an error at 1
  +                if (length > 1)
  +                    return 1;
  +                break;
  +
  +            case XMLContentSpec.CONTENTSPECNODE_SEQ :
  +                //
  +                //  There must be two children and they must be the two values
  +                //  we stored, in the stored order.
  +                //
  +                if (length == 2) {
  +                    if (fDTD) {
  +                        if (children[offset].rawname != fFirstChild.rawname) {
  +                            return 0;
  +                        }
  +                        if (children[offset + 1].rawname != fSecondChild.rawname) {
  +                            return 1;
  +                        }
  +                    }
  +                    else {
  +                        if (children[offset].uri != fFirstChild.uri || children[offset].localpart != fFirstChild.localpart)
  +                            return 0;
  +
  +                        if (children[offset + 1].uri != fSecondChild.uri || children[offset + 1].localpart != fSecondChild.localpart)
  +                            return 1;
  +                    }
  +                }
  +                else {
  +                    if (length > 2) {
  +                        return 2;
  +                    }
  +
  +                    return length;
  +                }
  +
  +                break;
  +
  +            default :
  +                throw new RuntimeException("ImplementationMessages.VAL_CST");
  +        }
  +
  +        // We survived, so return success status
           return -1;
       } // validate