neilg 2002/08/16 12:58:06 Modified: java/src/org/apache/xerces/impl/xs XMLSchemaValidator.java java/src/org/apache/xerces/impl/xs/identity Field.java FieldActivator.java Selector.java XPathMatcher.java Log: "fix" for bug 11571. The bug report was wrong, but this fixes an NPE that it provoked. Also, identity constraints now properly handle cases where an identity constraint is defined on a recursive element. I have also reworked some code to save some method calls. Revision Changes Path 1.83 +97 -60 xml-xerces/java/src/org/apache/xerces/impl/xs/XMLSchemaValidator.java Index: XMLSchemaValidator.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/xs/XMLSchemaValidator.java,v retrieving revision 1.82 retrieving revision 1.83 diff -u -r1.82 -r1.83 --- XMLSchemaValidator.java 16 Aug 2002 18:08:37 -0000 1.82 +++ XMLSchemaValidator.java 16 Aug 2002 19:58:05 -0000 1.83 @@ -236,6 +236,11 @@ JAXP_SCHEMA_SOURCE }; + // this is the number of valuestores of each kind + // we expect an element to have. It's almost + // never > 1; so leave it at that. + protected static final int ID_CONSTRAINT_NUM = 1; + // // Data // @@ -1368,14 +1373,12 @@ * * @param identityConstraint The identity constraint. */ - public void startValueScopeFor(IdentityConstraint identityConstraint) + public void startValueScopeFor(IdentityConstraint identityConstraint, + int initialDepth) throws XNIException { - for (int i=0; i<identityConstraint.getFieldCount(); i++) { - Field field = identityConstraint.getFieldAt(i); - ValueStoreBase valueStore = fValueStoreCache.getValueStoreFor(field); - valueStore.startValueScope(); - } + ValueStoreBase valueStore = fValueStoreCache.getValueStoreFor(identityConstraint, initialDepth); + valueStore.startValueScope(); } // startValueScopeFor(IdentityConstraint identityConstraint) @@ -1385,8 +1388,8 @@ * * @param field The field to activate. */ - public XPathMatcher activateField(Field field) throws XNIException { - ValueStore valueStore = fValueStoreCache.getValueStoreFor(field); + public XPathMatcher activateField(Field field, int initialDepth) { + ValueStore valueStore = fValueStoreCache.getValueStoreFor(field.getIdentityConstraint(), initialDepth); field.setMayMatch(true); XPathMatcher matcher = field.createMatcher(valueStore); fMatcherStack.addMatcher(matcher); @@ -1399,10 +1402,10 @@ * * @param identityConstraint The identity constraint. */ - public void endValueScopeFor(IdentityConstraint identityConstraint) + public void endValueScopeFor(IdentityConstraint identityConstraint, int initialDepth) throws XNIException { - ValueStoreBase valueStore = fValueStoreCache.getValueStoreFor(identityConstraint); + ValueStoreBase valueStore = fValueStoreCache.getValueStoreFor(identityConstraint, initialDepth); valueStore.endValueScope(); } // endValueScopeFor(IdentityConstraint) @@ -1413,7 +1416,7 @@ FieldActivator activator = this; if (selector == null) return; - XPathMatcher matcher = selector.createMatcher(activator); + XPathMatcher matcher = selector.createMatcher(activator, fElementDepth); fMatcherStack.addMatcher(matcher); matcher.startDocumentFragment(fSymbolTable); } @@ -2087,19 +2090,25 @@ // handle everything *but* keyref's. for (int i = oldCount - 1; i >= newCount; i--) { XPathMatcher matcher = fMatcherStack.getMatcherAt(i); - IdentityConstraint id; - if ((id = matcher.getIDConstraint()) != null && id.getCategory() != IdentityConstraint.IC_KEYREF) { - fValueStoreCache.transplant(id); + if(matcher instanceof Selector.Matcher) { + Selector.Matcher selMatcher = (Selector.Matcher)matcher; + IdentityConstraint id; + if ((id = selMatcher.getIdentityConstraint()) != null && id.getCategory() != IdentityConstraint.IC_KEYREF) { + fValueStoreCache.transplant(id, selMatcher.getInitialDepth()); + } } } // now handle keyref's/... for (int i = oldCount - 1; i >= newCount; i--) { XPathMatcher matcher = fMatcherStack.getMatcherAt(i); - IdentityConstraint id; - if ((id = matcher.getIDConstraint()) != null && id.getCategory() == IdentityConstraint.IC_KEYREF) { - ValueStoreBase values = fValueStoreCache.getValueStoreFor(id); - if (values != null) // nothing to do if nothing matched! - values.endDocumentFragment(); + if(matcher instanceof Selector.Matcher) { + Selector.Matcher selMatcher = (Selector.Matcher)matcher; + IdentityConstraint id; + if ((id = selMatcher.getIdentityConstraint()) != null && id.getCategory() == IdentityConstraint.IC_KEYREF) { + ValueStoreBase values = fValueStoreCache.getValueStoreFor(id, selMatcher.getInitialDepth()); + if (values != null) // nothing to do if nothing matched! + values.endDocumentFragment(); + } } } fValueStoreCache.endElement(); @@ -3098,11 +3107,11 @@ // destroys this ValueStore; useful when, for instance, a // locally-scoped ID constraint is involved. - public void destroy() { + public void clear() { fValuesCount = 0; fValues.clear(); fValueTuples.removeAllElements(); - } // end destroy():void + } // end clear():void // appends the contents of one ValueStore to those of us. public void append(ValueStoreBase newVal) { @@ -3498,7 +3507,15 @@ /** stores all global Values stores. */ protected final Vector fValueStores = new Vector(); - /** Values stores associated to specific identity constraints. */ + /** + * Values stores associated to specific identity constraints. + * This hashtable maps IdentityConstraints and + * the 0-based element on which their selectors first matched to + * a corresponding ValueStore. This should take care + * of all cases, including where ID constraints with + * descendant-or-self axes occur on recursively-defined + * elements. + */ protected final Hashtable fIdentityConstraint2ValueStoreMap = new Hashtable(); // sketch of algorithm: @@ -3574,8 +3591,7 @@ * Initializes the value stores for the specified element * declaration. */ - public void initValueStoresFor(XSElementDecl eDecl) - throws XNIException { + public void initValueStoresFor(XSElementDecl eDecl) { // initialize value stores for unique fields IdentityConstraint [] icArray = eDecl.fIDConstraints; int icCount = eDecl.fIDCPos; @@ -3584,56 +3600,52 @@ case (IdentityConstraint.IC_UNIQUE): // initialize value stores for unique fields UniqueOrKey unique = (UniqueOrKey)icArray[i]; - UniqueValueStore uniqueValueStore = (UniqueValueStore)fIdentityConstraint2ValueStoreMap.get(unique); - if (uniqueValueStore != null) { - // NOTE: If already initialized, don't need to - // do it again. -Ac - continue; + LocalIDKey toHash = new LocalIDKey (unique, fElementDepth); + UniqueValueStore uniqueValueStore = (UniqueValueStore)fIdentityConstraint2ValueStoreMap.get(toHash); + if (uniqueValueStore == null) { + uniqueValueStore = new UniqueValueStore(unique); + fIdentityConstraint2ValueStoreMap.put(toHash, uniqueValueStore); + } else { + uniqueValueStore.clear(); } - uniqueValueStore = new UniqueValueStore(unique); fValueStores.addElement(uniqueValueStore); - fIdentityConstraint2ValueStoreMap.put(unique, uniqueValueStore); break; case (IdentityConstraint.IC_KEY): // initialize value stores for key fields UniqueOrKey key = (UniqueOrKey)icArray[i]; - KeyValueStore keyValueStore = (KeyValueStore)fIdentityConstraint2ValueStoreMap.get(key); - if (keyValueStore != null) { - // NOTE: If already initialized, don't need to - // do it again. -Ac - continue; + toHash = new LocalIDKey(key, fElementDepth); + KeyValueStore keyValueStore = (KeyValueStore)fIdentityConstraint2ValueStoreMap.get(toHash); + if (keyValueStore == null) { + keyValueStore = new KeyValueStore(key); + fIdentityConstraint2ValueStoreMap.put(toHash, keyValueStore); + } else { + keyValueStore.clear(); } - keyValueStore = new KeyValueStore(key); fValueStores.addElement(keyValueStore); - fIdentityConstraint2ValueStoreMap.put(key, keyValueStore); break; case (IdentityConstraint.IC_KEYREF): - // initialize value stores for key reference fields + // initialize value stores for keyRef fields KeyRef keyRef = (KeyRef)icArray[i]; - KeyRefValueStore keyRefValueStore = (KeyRefValueStore)fIdentityConstraint2ValueStoreMap.get(keyRef); - if (keyRefValueStore != null) { - // NOTE: If already initialized, don't need to - // do it again. -Ac - continue; + toHash = new LocalIDKey(keyRef, fElementDepth); + KeyRefValueStore keyRefValueStore = (KeyRefValueStore)fIdentityConstraint2ValueStoreMap.get(toHash); + if (keyRefValueStore == null) { + keyRefValueStore = new KeyRefValueStore(keyRef, null); + fIdentityConstraint2ValueStoreMap.put(toHash, keyRefValueStore); + } else { + keyRefValueStore.clear(); } - keyRefValueStore = new KeyRefValueStore(keyRef, null); fValueStores.addElement(keyRefValueStore); - fIdentityConstraint2ValueStoreMap.put(keyRef, keyRefValueStore); break; } } } // initValueStoresFor(XSElementDecl) - /** Returns the value store associated to the specified field. */ - public ValueStoreBase getValueStoreFor(Field field) { - IdentityConstraint identityConstraint = field.getIdentityConstraint(); - return(ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(identityConstraint); - } // getValueStoreFor(Field):ValueStoreBase - /** Returns the value store associated to the specified IdentityConstraint. */ - public ValueStoreBase getValueStoreFor(IdentityConstraint id) { - return(ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(id); - } // getValueStoreFor(IdentityConstraint):ValueStoreBase + public ValueStoreBase getValueStoreFor(IdentityConstraint id, int initialDepth) { + ValueStoreBase vb = (ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(new LocalIDKey(id, initialDepth)); + // vb should *never* be null! + return vb; + } // getValueStoreFor(IdentityConstraint, int):ValueStoreBase /** Returns the global value store associated to the specified IdentityConstraint. */ public ValueStoreBase getGlobalValueStoreFor(IdentityConstraint id) { @@ -3643,10 +3655,9 @@ // associated with id and moves them into the global // hashtable, if id is a <unique> or a <key>. // If it's a <keyRef>, then we leave it for later. - public void transplant(IdentityConstraint id) { + public void transplant(IdentityConstraint id, int initialDepth) { + ValueStoreBase newVals = (ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(new LocalIDKey(id, initialDepth)); if (id.getCategory() == IdentityConstraint.IC_KEYREF) return; - ValueStoreBase newVals = (ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(id); - fIdentityConstraint2ValueStoreMap.remove(id); ValueStoreBase currVals = (ValueStoreBase)fGlobalIDConstraintMap.get(id); if (currVals != null) { currVals.append(newVals); @@ -3658,7 +3669,7 @@ } // transplant(id) /** Check identity constraints. */ - public void endDocument() throws XNIException { + public void endDocument() { int count = fValueStores.size(); for (int i = 0; i < count; i++) { @@ -3875,4 +3886,30 @@ } // class Entry } // class OrderedHashtable + + // the purpose of this class is to enable IdentityConstraint,int + // pairs to be used easily as keys in Hashtables. + protected class LocalIDKey { + private IdentityConstraint fId; + private int fDepth; + + public LocalIDKey (IdentityConstraint id, int depth) { + fId = id; + fDepth = depth; + } // init(IdentityConstraint, int) + + // object method + public int hashCode() { + return fId.hashCode()+fDepth; + } + + public boolean equals(Object localIDKey) { + if(localIDKey instanceof LocalIDKey) { + LocalIDKey lIDKey = (LocalIDKey)localIDKey; + return (lIDKey.fId == fId && lIDKey.fDepth == fDepth); + } + return false; + } + } // class LocalIDKey + } // class SchemaValidator 1.5 +2 -2 xml-xerces/java/src/org/apache/xerces/impl/xs/identity/Field.java Index: Field.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/xs/identity/Field.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- Field.java 13 Aug 2002 22:32:44 -0000 1.4 +++ Field.java 16 Aug 2002 19:58:06 -0000 1.5 @@ -197,7 +197,7 @@ /** Constructs a field matcher. */ public Matcher(Field.XPath xpath, ValueStore store) { - super(xpath, null); + super(xpath); fStore = store; } // <init>(Field.XPath,ValueStore) 1.3 +8 -4 xml-xerces/java/src/org/apache/xerces/impl/xs/identity/FieldActivator.java Index: FieldActivator.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/xs/identity/FieldActivator.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- FieldActivator.java 29 Jan 2002 01:15:15 -0000 1.2 +++ FieldActivator.java 16 Aug 2002 19:58:06 -0000 1.3 @@ -80,8 +80,10 @@ * the value store. * * @param identityConstraint The identity constraint. + * @param initialDepth the depth at which the selector began matching */ - public void startValueScopeFor(IdentityConstraint identityConstraint); + public void startValueScopeFor(IdentityConstraint identityConstraint, + int initialDepth); /** * Request to activate the specified field. This method returns the @@ -90,14 +92,16 @@ * it is permitted to match a value--that is, to call the field's setMayMatch(boolean) method. * * @param field The field to activate. + * @param initialDepth the 0-indexed depth in the instance document at which the Selector began to match. */ - public XPathMatcher activateField(Field field); + public XPathMatcher activateField(Field field, int initialDepth); /** * Ends the value scope for the specified identity constraint. * * @param identityConstraint The identity constraint. + * @param initialDepth the 0-indexed depth where the Selector began to match. */ - public void endValueScopeFor(IdentityConstraint identityConstraint); + public void endValueScopeFor(IdentityConstraint identityConstraint, int initialDepth); } // interface FieldActivator 1.8 +36 -13 xml-xerces/java/src/org/apache/xerces/impl/xs/identity/Selector.java Index: Selector.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/xs/identity/Selector.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- Selector.java 14 Aug 2002 16:02:22 -0000 1.7 +++ Selector.java 16 Aug 2002 19:58:06 -0000 1.8 @@ -88,6 +88,10 @@ /** Identity constraint. */ protected IdentityConstraint fIdentityConstraint; + // the Identity constraint we're the matcher for. Only + // used for selectors! + protected IdentityConstraint fIDConstraint; + // // Constructors // @@ -109,15 +113,19 @@ } // getXPath():org.apache.xerces.v1.schema.identity.XPath /** Returns the identity constraint. */ - public IdentityConstraint getIdentityConstraint() { + public IdentityConstraint getIDConstraint() { return fIdentityConstraint; - } // getIdentityConstraint():IdentityConstraint + } // getIDConstraint():IdentityConstraint // factory method - /** Creates a selector matcher. */ - public XPathMatcher createMatcher(FieldActivator activator) { - return new Selector.Matcher(fXPath, activator); + /** Creates a selector matcher. + * @param activator The activator for this selector's fields. + * @param initialDepth The depth in the document at which this matcher began its life; + * used in correctly handling recursive elements. + */ + public XPathMatcher createMatcher(FieldActivator activator, int initialDepth) { + return new Selector.Matcher(fXPath, activator, initialDepth); } // createMatcher(FieldActivator):XPathMatcher // @@ -176,7 +184,7 @@ * * @author Andy Clark, IBM */ - protected class Matcher + public class Matcher extends XPathMatcher { // @@ -186,6 +194,9 @@ /** Field activator. */ protected FieldActivator fFieldActivator; + /** Initial depth in the document at which this matcher was created. */ + protected int fInitialDepth; + /** Element depth. */ protected int fElementDepth; @@ -197,9 +208,11 @@ // /** Constructs a selector matcher. */ - public Matcher(Selector.XPath xpath, FieldActivator activator) { - super(xpath, Selector.this.fIdentityConstraint); + public Matcher(Selector.XPath xpath, FieldActivator activator, + int initialDepth) { + super(xpath); fFieldActivator = activator; + fInitialDepth = initialDepth; } // <init>(Selector.XPath,FieldActivator) // @@ -234,11 +247,11 @@ if ((fMatchedDepth == -1 && ((matched & MATCHED) == MATCHED)) || ((matched & MATCHED_DESCENDANT) == MATCHED_DESCENDANT)) { fMatchedDepth = fElementDepth; - fFieldActivator.startValueScopeFor(fIdentityConstraint); + fFieldActivator.startValueScopeFor(fIdentityConstraint, fInitialDepth); int count = fIdentityConstraint.getFieldCount(); for (int i = 0; i < count; i++) { Field field = fIdentityConstraint.getFieldAt(i); - XPathMatcher matcher = fFieldActivator.activateField(field); + XPathMatcher matcher = fFieldActivator.activateField(field, fInitialDepth); matcher.startElement(element, attributes, elementDecl); } } @@ -249,9 +262,19 @@ super.endElement(element, eDecl, ePSVI); if (fElementDepth-- == fMatchedDepth) { fMatchedDepth = -1; - fFieldActivator.endValueScopeFor(fIdentityConstraint); + fFieldActivator.endValueScopeFor(fIdentityConstraint, fInitialDepth); } } + + /** Returns the identity constraint. */ + public IdentityConstraint getIdentityConstraint() { + return fIdentityConstraint; + } // getIdentityConstraint():IdentityConstraint + + /** get the initial depth at which this selector matched. */ + public int getInitialDepth() { + return fInitialDepth; + } // getInitialDepth(): int // // Protected methods 1.10 +2 -32 xml-xerces/java/src/org/apache/xerces/impl/xs/identity/XPathMatcher.java Index: XPathMatcher.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/xs/identity/XPathMatcher.java,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- XPathMatcher.java 14 Aug 2002 16:02:22 -0000 1.9 +++ XPathMatcher.java 16 Aug 2002 19:58:06 -0000 1.10 @@ -147,10 +147,6 @@ */ private int [] fNoMatchDepth; - // the Identity constraint we're the matcher for. Only - // used for selectors! - protected IdentityConstraint fIDConstraint; - // the symbolTable for the XPath parser protected SymbolTable fSymbolTable; @@ -165,22 +161,7 @@ * @param xpath The xpath. */ public XPathMatcher(XPath xpath) { - this(xpath, null); - } // <init>(XPath) - - /** - * Constructs an XPath matcher that implements a document fragment - * handler. - * - * @param xpath The xpath. - * @param shouldBufferContent True if the matcher should buffer the - * matched content. - * @param idConstraint: the identity constraint we're matching for; - * null unless it's a Selector. - */ - public XPathMatcher(XPath xpath, IdentityConstraint idConstraint) { fLocationPaths = xpath.getLocationPaths(); - fIDConstraint = idConstraint; fStepIndexes = new IntStack[fLocationPaths.length]; for(int i=0; i<fStepIndexes.length; i++) fStepIndexes[i] = new IntStack(); fCurrentStep = new int[fLocationPaths.length]; @@ -189,7 +170,7 @@ if (DEBUG_METHODS) { System.out.println(toString()+"#<init>()"); } - } // <init>(XPath,boolean) + } // <init>(XPath) // // Public methods @@ -206,16 +187,6 @@ return 0; } // isMatched():int - // returns whether this XPathMatcher was matching a Selector - public boolean getIsSelector() { - return (fIDConstraint == null); - } // end getIsSelector():boolean - - // returns the ID constraint - public IdentityConstraint getIDConstraint() { - return fIDConstraint; - } // end getIDConstraint():IdentityConstraint - /** Returns the matched string. */ public String getMatchedString() { return fMatchedString; @@ -494,7 +465,6 @@ if (DEBUG_METHODS2) { System.out.println(toString()+"#endElement("+ "element={"+element+"},"+ - "ID constraint="+fIDConstraint+ ")"); } for(int i = 0; i<fLocationPaths.length; i++) {
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]