mrglavas 2003/10/10 11:25:40 Modified: java/src/org/apache/xerces/util XMLAttributesImpl.java java/src/org/apache/xerces/impl/dtd XMLNSDTDValidator.java XML11NSDTDValidator.java java/src/org/apache/xerces/impl XML11NSDocumentScannerImpl.java XMLNSDocumentScannerImpl.java XMLNamespaceBinder.java Log: Improved processing of attribute lists both small and large in many ways. Take advantage of the SymbolTable and always use reference comparison when checking names. Revision Changes Path 1.21 +438 -55 xml-xerces/java/src/org/apache/xerces/util/XMLAttributesImpl.java Index: XMLAttributesImpl.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/util/XMLAttributesImpl.java,v retrieving revision 1.20 retrieving revision 1.21 diff -u -r1.20 -r1.21 --- XMLAttributesImpl.java 8 May 2003 20:12:00 -0000 1.20 +++ XMLAttributesImpl.java 10 Oct 2003 18:25:40 -0000 1.21 @@ -76,6 +76,7 @@ * * @author Andy Clark, IBM * @author Elena Litani, IBM + * @author Michael Glavassevich, IBM * * @version $Id$ */ @@ -83,6 +84,19 @@ implements XMLAttributes { // + // Constants + // + + /** Default table size. */ + protected static final int TABLE_SIZE = 101; + + /** + * Threshold at which an instance is treated + * as a large attribute list. + */ + protected static final int SIZE_LIMIT = 20; + + // // Data // @@ -93,18 +107,42 @@ // data + /** + * Usage count for the attribute table view. + * Incremented each time all attributes are removed + * when the attribute table view is in use. + */ + protected int fLargeCount = 1; + /** Attribute count. */ protected int fLength; /** Attribute information. */ protected Attribute[] fAttributes = new Attribute[4]; - - /** Augmentations information for each attribute - number of augmentations is equal to the number of attributes - */ - // XMLAttributes has no knowledge if any augmentations - // we attached to Augmentations. - protected Augmentations[] fAugmentations = new AugmentationsImpl[4]; + + /** + * Hashtable of attribute information. + * Provides an alternate view of the attribute specification. + */ + protected Attribute[] fAttributeTableView; + + /** + * Tracks whether each chain in the hash table is stale + * with respect to the current state of this object. + * A chain is stale if its state is not the same as the number + * of times the attribute table view has been used. + */ + protected int[] fAttributeTableViewChainState; + + /** + * Actual number of buckets in the table view. + */ + protected int fTableViewBuckets; + + /** + * Indicates whether the table view contains consistent data. + */ + protected boolean fIsTableViewConsistent; // // Constructors @@ -112,9 +150,16 @@ /** Default constructor. */ public XMLAttributesImpl() { + this(TABLE_SIZE); + } + + /** + * @param tableSize initial size of table view + */ + public XMLAttributesImpl(int tableSize) { + fTableViewBuckets = tableSize; for (int i = 0; i < fAttributes.length; i++) { fAttributes[i] = new Attribute(); - fAugmentations[i] = new AugmentationsImpl(); } } // <init>() @@ -150,8 +195,8 @@ * exists, the old values for the attribute are replaced by the new * values. * - * @param attrName The attribute name. - * @param attrType The attribute type. The type name is determined by + * @param name The attribute name. + * @param type The attribute type. The type name is determined by * the type specified for this attribute in the DTD. * For example: "CDATA", "ID", "NMTOKEN", etc. However, * attributes of type enumeration will have the type @@ -159,7 +204,7 @@ * the enumeration values prefixed by an open * parenthesis and suffixed by a close parenthesis. * For example: "(true|false)". - * @param attrValue The attribute value. + * @param value The attribute value. * * @return Returns the attribute index. * @@ -168,29 +213,95 @@ */ public int addAttribute(QName name, String type, String value) { - // find attribute; create, if necessary - int index = name.uri != null && !name.uri.equals("") - ? getIndex(name.uri, name.localpart) - : getIndex(name.rawname); - if (index == -1) { - index = fLength; - if (fLength++ == fAttributes.length) { - Attribute[] attributes = new Attribute[fAttributes.length + 4]; - Augmentations[] augs = new AugmentationsImpl[fAttributes.length +4]; - System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length); - System.arraycopy(fAugmentations, 0, augs, 0, fAttributes.length); - for (int i = fAttributes.length; i < attributes.length; i++) { - attributes[i] = new Attribute(); - augs[i] = new AugmentationsImpl(); + int index; + if (fLength < SIZE_LIMIT) { + index = name.uri != null && !name.uri.equals("") + ? getIndexFast(name.uri, name.localpart) + : getIndexFast(name.rawname); + + if (index == -1) { + index = fLength; + if (fLength++ == fAttributes.length) { + Attribute[] attributes = new Attribute[fAttributes.length + 4]; + System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length); + for (int i = fAttributes.length; i < attributes.length; i++) { + attributes[i] = new Attribute(); + } + fAttributes = attributes; } - fAttributes = attributes; - fAugmentations = augs; } } + else if (name.uri == null || + name.uri.length() == 0 || + (index = getIndexFast(name.uri, name.localpart)) == -1) { + + /** + * If attributes were removed from the list after the table + * becomes in use this isn't reflected in the table view. It's + * assumed that once a user starts removing attributes they're + * not likely to add more. We only make the view consistent if + * the user of this class adds attributes, removes them, and + * then adds more. + */ + if (!fIsTableViewConsistent || fLength == SIZE_LIMIT) { + prepareAndPopulateTableView(); + fIsTableViewConsistent = true; + } - // clear augmentations - fAugmentations[index].removeAllItems(); - + int bucket = getTableViewBucket(name.rawname); + + // The chain is stale. + // This must be a unique attribute. + if (fAttributeTableViewChainState[bucket] != fLargeCount) { + index = fLength; + if (fLength++ == fAttributes.length) { + Attribute[] attributes = new Attribute[fAttributes.length << 1]; + System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length); + for (int i = fAttributes.length; i < attributes.length; i++) { + attributes[i] = new Attribute(); + } + fAttributes = attributes; + } + + // Update table view. + fAttributeTableViewChainState[bucket] = fLargeCount; + fAttributes[index].next = null; + fAttributeTableView[bucket] = fAttributes[index]; + } + // This chain is active. + // We need to check if any of the attributes has the same rawname. + else { + // Search the table. + Attribute found = fAttributeTableView[bucket]; + while (found != null) { + if (found.name.rawname == name.rawname) { + break; + } + found = found.next; + } + // This attribute is unique. + if (found == null) { + index = fLength; + if (fLength++ == fAttributes.length) { + Attribute[] attributes = new Attribute[fAttributes.length << 1]; + System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length); + for (int i = fAttributes.length; i < attributes.length; i++) { + attributes[i] = new Attribute(); + } + fAttributes = attributes; + } + + // Update table view + fAttributes[index].next = fAttributeTableView[bucket]; + fAttributeTableView[bucket] = fAttributes[index]; + } + // Duplicate. We still need to find the index. + else { + index = getIndexFast(name.rawname); + } + } + } + // set values Attribute attribute = fAttributes[index]; attribute.name.setValues(name); @@ -198,8 +309,10 @@ attribute.value = value; attribute.nonNormalizedValue = value; attribute.specified = false; + + // clear augmentations + attribute.augs.removeAllItems(); - // return return index; } // addAttribute(QName,String,XMLString) @@ -221,20 +334,14 @@ * @param attrIndex The attribute index. */ public void removeAttributeAt(int attrIndex) { + fIsTableViewConsistent = false; if (attrIndex < fLength - 1) { Attribute removedAttr = fAttributes[attrIndex]; - Augmentations removedAug = fAugmentations[attrIndex]; System.arraycopy(fAttributes, attrIndex + 1, - fAttributes, attrIndex, fLength - attrIndex - 1); - - System.arraycopy(fAugmentations, attrIndex + 1, - fAugmentations, attrIndex, fLength - attrIndex - 1); - + fAttributes, attrIndex, fLength - attrIndex - 1); // Make the discarded Attribute object available for re-use // by tucking it after the Attributes that are still in use fAttributes[fLength-1] = removedAttr; - - fAugmentations[fLength-1] = removedAug; } fLength--; } // removeAttributeAt(int) @@ -379,11 +486,7 @@ if (index < 0 || index >= fLength) { return null; } - String type = fAttributes[index].type; - if(type.indexOf('(') == 0 && type.lastIndexOf(')') == type.length()-1) { - return "NMTOKEN"; - } - return type; + return getReportableType(fAttributes[index].type); } // getType(int):String /** @@ -399,7 +502,7 @@ */ public String getType(String qname) { int index = getIndex(qname); - return index != -1 ? getType(index): null; + return index != -1 ? getReportableType(fAttributes[index].type) : null; } // getType(String):String /** @@ -564,7 +667,7 @@ return null; } int index = getIndex(uri, localName); - return index != -1 ? getType(index) : null; + return index != -1 ? getReportableType(fAttributes[index].type) : null; } // getType(String,String):String /** @@ -622,8 +725,7 @@ */ public Augmentations getAugmentations (String uri, String localName) { int index = getIndex(uri, localName); - - return index != -1 ? fAugmentations[index] : null; + return index != -1 ? fAttributes[index].augs : null; } /** @@ -637,11 +739,9 @@ */ public Augmentations getAugmentations(String qName){ int index = getIndex(qName); - return index != -1 ? fAugmentations[index] : null; + return index != -1 ? fAttributes[index].augs : null; } - - /** * Look up an augmentations by attributes index. * @@ -652,7 +752,7 @@ if (attributeIndex < 0 || attributeIndex >= fLength) { return null; } - return fAugmentations[attributeIndex]; + return fAttributes[attributeIndex].augs; } /** @@ -662,7 +762,7 @@ * @param augs The augmentations. */ public void setAugmentations(int attrIndex, Augmentations augs) { - fAugmentations[attrIndex] = augs; + fAttributes[attrIndex].augs = augs; } /** @@ -687,7 +787,7 @@ } public boolean getSchemaId(String qname) { int index = getIndex(qname); - return index != -1 ? fAttributes[index].schemaId : false; + return index != -1 ? fAttributes[index].schemaId : false; } // getType(String):String public boolean getSchemaId(String uri, String localName) { if (!fNamespaces) { @@ -696,6 +796,277 @@ int index = getIndex(uri, localName); return index != -1 ? fAttributes[index].schemaId : false; } // getType(String,String):String + + /** + * Look up the index of an attribute by XML 1.0 qualified name. + * <p> + * <strong>Note:</strong> + * This method uses reference comparison, and thus should + * only be used internally. We cannot use this method in any + * code exposed to users as they may not pass in unique strings. + * + * @param qName The qualified (prefixed) name. + * @return The index of the attribute, or -1 if it does not + * appear in the list. + */ + public int getIndexFast(String qName) { + for (int i = 0; i < fLength; ++i) { + Attribute attribute = fAttributes[i]; + if (attribute.name.rawname == qName) { + return i; + } + } + return -1; + } // getIndexFast(String):int + + /** + * Adds an attribute. The attribute's non-normalized value of the + * attribute will have the same value as the attribute value until + * set using the <code>setNonNormalizedValue</code> method. Also, + * the added attribute will be marked as specified in the XML instance + * document unless set otherwise using the <code>setSpecified</code> + * method. + * <p> + * This method differs from <code>addAttribute</code> in that it + * does not check if an attribute of the same name already exists + * in the list before adding it. In order to improve performance + * of namespace processing, this method allows uniqueness checks + * to be deferred until all the namespace information is available + * after the entire attribute specification has been read. + * <p> + * <strong>Caution:</strong> If this method is called it should + * not be mixed with calls to <code>addAttribute</code> unless + * it has been determined that all the attribute names are unique. + * + * @param name the attribute name + * @param type the attribute type + * @param value the attribute value + * + * @see #setNonNormalizedValue + * @see #setSpecified + * @see #checkDuplicatesNS + */ + public void addAttributeNS(QName name, String type, String value) { + int index = fLength; + if (fLength++ == fAttributes.length) { + Attribute[] attributes; + if (fLength < SIZE_LIMIT) { + attributes = new Attribute[fAttributes.length + 4]; + } + else { + attributes = new Attribute[fAttributes.length << 1]; + } + System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length); + for (int i = fAttributes.length; i < attributes.length; i++) { + attributes[i] = new Attribute(); + } + fAttributes = attributes; + } + + // set values + Attribute attribute = fAttributes[index]; + attribute.name.setValues(name); + attribute.type = type; + attribute.value = value; + attribute.nonNormalizedValue = value; + attribute.specified = false; + + // clear augmentations + attribute.augs.removeAllItems(); + } + + /** + * Checks for duplicate expanded names (local part and namespace name + * pairs) in the attribute specification. If a duplicate is found its + * name is returned. + * <p> + * This should be called once all the in-scope namespaces for the element + * enclosing these attributes is known, and after all the attributes + * have gone through namespace binding. + * + * @return the name of a duplicate attribute found in the search, + * otherwise null. + */ + public QName checkDuplicatesNS() { + // If the list is small check for duplicates using pairwise comparison. + if (fLength <= SIZE_LIMIT) { + for (int i = 0; i < fLength - 1; ++i) { + Attribute att1 = fAttributes[i]; + for (int j = i + 1; j < fLength; ++j) { + Attribute att2 = fAttributes[j]; + if (att1.name.localpart == att2.name.localpart && + att1.name.uri == att2.name.uri) { + return att2.name; + } + } + } + } + // If the list is large check duplicates using a hash table. + else { + // We don't want this table view to be read if someone calls + // addAttribute so we invalidate it up front. + fIsTableViewConsistent = false; + + prepareTableView(); + + Attribute attr; + int bucket; + + for (int i = fLength - 1; i >= 0; --i) { + attr = fAttributes[i]; + bucket = getTableViewBucket(attr.name.localpart, attr.name.uri); + + // The chain is stale. + // This must be a unique attribute. + if (fAttributeTableViewChainState[bucket] != fLargeCount) { + fAttributeTableViewChainState[bucket] = fLargeCount; + attr.next = null; + fAttributeTableView[bucket] = attr; + } + // This chain is active. + // We need to check if any of the attributes has the same name. + else { + // Search the table. + Attribute found = fAttributeTableView[bucket]; + while (found != null) { + if (found.name.localpart == attr.name.localpart && + found.name.uri == attr.name.uri) { + return attr.name; + } + found = found.next; + } + + // Update table view + attr.next = fAttributeTableView[bucket]; + fAttributeTableView[bucket] = attr; + } + } + } + return null; + } + + /** + * Look up the index of an attribute by Namespace name. + * <p> + * <strong>Note:</strong> + * This method uses reference comparison, and thus should + * only be used internally. We cannot use this method in any + * code exposed to users as they may not pass in unique strings. + * + * @param uri The Namespace URI, or null if + * the name has no Namespace URI. + * @param localName The attribute's local name. + * @return The index of the attribute, or -1 if it does not + * appear in the list. + */ + public int getIndexFast(String uri, String localPart) { + for (int i = 0; i < fLength; ++i) { + Attribute attribute = fAttributes[i]; + if (attribute.name.localpart == localPart && + attribute.name.uri == uri) { + return i; + } + } + return -1; + } // getIndexFast(String,String):int + + /** + * Returns the value passed in or NMTOKEN if it's an enumerated type. + * + * @param type attribute type + * @return the value passed in or NMTOKEN if it's an enumerated type. + */ + protected String getReportableType(String type) { + if (type.indexOf('(') == 0 && type.lastIndexOf(')') == type.length()-1) { + return "NMTOKEN"; + } + return type; + } + + /** + * Returns the position in the table view + * where the given attribute name would be hashed. + * + * @param qname the attribute name + * @return the position in the table view where the given attribute + * would be hashed + */ + protected int getTableViewBucket(String qname) { + return (qname.hashCode() & 0x7FFFFFFF) % fTableViewBuckets; + } + + /** + * Returns the position in the table view + * where the given attribute name would be hashed. + * + * @param localpart the local part of the attribute + * @param uri the namespace name of the attribute + * @return the position in the table view where the given attribute + * would be hashed + */ + protected int getTableViewBucket(String localpart, String uri) { + if (uri == null) { + return (localpart.hashCode() & 0x7FFFFFFF) % fTableViewBuckets; + } + else { + return ((localpart.hashCode() + uri.hashCode()) + & 0x7FFFFFFF) % fTableViewBuckets; + } + } + + /** + * Purges all elements from the table view. + */ + protected void cleanTableView() { + if (++fLargeCount < 0) { + // Overflow. We actually need to visit the chain state array. + if (fAttributeTableViewChainState != null) { + for (int i = fTableViewBuckets - 1; i >= 0; --i) { + fAttributeTableViewChainState[i] = 0; + } + } + fLargeCount = 1; + } + } + + /** + * Prepares the table view of the attributes list for use. + */ + protected void prepareTableView() { + if (fAttributeTableView == null) { + fAttributeTableView = new Attribute[fTableViewBuckets]; + fAttributeTableViewChainState = new int[fTableViewBuckets]; + } + else { + cleanTableView(); + } + } + + /** + * Prepares the table view of the attributes list for use, + * and populates it with the attributes which have been + * previously read. + */ + protected void prepareAndPopulateTableView() { + prepareTableView(); + // Need to populate the hash table with the attributes we've scanned so far. + Attribute attr; + int bucket; + for (int i = 0; i < fLength; ++i) { + attr = fAttributes[i]; + bucket = getTableViewBucket(attr.name.rawname); + if (fAttributeTableViewChainState[bucket] != fLargeCount) { + fAttributeTableViewChainState[bucket] = fLargeCount; + attr.next = null; + fAttributeTableView[bucket] = attr; + } + else { + // Update table view + attr.next = fAttributeTableView[bucket]; + fAttributeTableView[bucket] = attr; + } + } + } // // Classes @@ -731,6 +1102,18 @@ /** Schema ID type. */ public boolean schemaId; + + /** + * Augmentations information for this attribute. + * XMLAttributes has no knowledge if any augmentations + * were attached to Augmentations. + */ + public Augmentations augs = new AugmentationsImpl(); + + // Additional data for attribute table view + + /** Pointer to the next attribute in the chain. **/ + public Attribute next; } // class Attribute 1.7 +5 -2 xml-xerces/java/src/org/apache/xerces/impl/dtd/XMLNSDTDValidator.java Index: XMLNSDTDValidator.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/dtd/XMLNSDTDValidator.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- XMLNSDTDValidator.java 29 May 2003 13:25:42 -0000 1.6 +++ XMLNSDTDValidator.java 10 Oct 2003 18:25:40 -0000 1.7 @@ -222,8 +222,11 @@ // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/> int attrCount = attributes.getLength(); for (int i = 0; i < attrCount - 1; i++) { - String alocalpart = attributes.getLocalName(i); String auri = attributes.getURI(i); + if (auri == null || auri == NamespaceContext.XMLNS_URI) { + continue; + } + String alocalpart = attributes.getLocalName(i); for (int j = i + 1; j < attrCount; j++) { String blocalpart = attributes.getLocalName(j); String buri = attributes.getURI(j); 1.2 +5 -2 xml-xerces/java/src/org/apache/xerces/impl/dtd/XML11NSDTDValidator.java Index: XML11NSDTDValidator.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/dtd/XML11NSDTDValidator.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- XML11NSDTDValidator.java 2 Oct 2003 18:18:15 -0000 1.1 +++ XML11NSDTDValidator.java 10 Oct 2003 18:25:40 -0000 1.2 @@ -215,8 +215,11 @@ // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/> int attrCount = attributes.getLength(); for (int i = 0; i < attrCount - 1; i++) { - String alocalpart = attributes.getLocalName(i); String auri = attributes.getURI(i); + if (auri == null || auri == NamespaceContext.XMLNS_URI) { + continue; + } + String alocalpart = attributes.getLocalName(i); for (int j = i + 1; j < attrCount; j++) { String blocalpart = attributes.getLocalName(j); String buri = attributes.getURI(j); 1.3 +43 -49 xml-xerces/java/src/org/apache/xerces/impl/XML11NSDocumentScannerImpl.java Index: XML11NSDocumentScannerImpl.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/XML11NSDocumentScannerImpl.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- XML11NSDocumentScannerImpl.java 2 Oct 2003 18:24:15 -0000 1.2 +++ XML11NSDocumentScannerImpl.java 10 Oct 2003 18:25:40 -0000 1.3 @@ -116,10 +116,6 @@ */ protected boolean fPerformValidation; - protected String[] fUri = new String[4]; - protected String[] fLocalpart = new String[4]; - protected int fLength = 0; - // private data // @@ -257,7 +253,6 @@ // bind attributes (xmlns are already bound bellow) int length = fAttributes.getLength(); - fLength = 0; //initialize structure for (int i = 0; i < length; i++) { fAttributes.getName(i, fAttributeQName); @@ -270,7 +265,6 @@ // if (fAttributeQName.uri != null && fAttributeQName.uri == uri) { - checkDuplicates(fAttributeQName, fAttributes); continue; } if (aprefix != XMLSymbols.EMPTY_STRING) { @@ -286,7 +280,30 @@ XMLErrorReporter.SEVERITY_FATAL_ERROR); } fAttributes.setURI(i, uri); - checkDuplicates(fAttributeQName, fAttributes); + } + } + + if (length > 1) { + QName name = fAttributes.checkDuplicatesNS(); + if (name != null) { + if (name.uri != null) { + fErrorReporter.reportError( + XMLMessageFormatter.XMLNS_DOMAIN, + "AttributeNSNotUnique", + new Object[] { + fElementQName.rawname, + name.localpart, + name.uri }, + XMLErrorReporter.SEVERITY_FATAL_ERROR); + } else { + fErrorReporter.reportError( + XMLMessageFormatter.XMLNS_DOMAIN, + "AttributeNotUnique", + new Object[] { + fElementQName.rawname, + name.rawname }, + XMLErrorReporter.SEVERITY_FATAL_ERROR); + } } } } @@ -323,40 +340,6 @@ } // scanStartElement():boolean - private final void checkDuplicates( - QName qname, - XMLAttributesImpl attributes) { - - // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/> - // search if such attribute already exists - for (int i = 0; i < fLength; i++) { - if (qname.uri == fUri[i] - && fLocalpart[i].equals(qname.localpart)) { - fErrorReporter.reportError( - XMLMessageFormatter.XMLNS_DOMAIN, - "AttributeNSNotUnique", - new Object[] { - fElementQName.rawname, - qname.rawname, - qname.uri }, - XMLErrorReporter.SEVERITY_FATAL_ERROR); - } - } - int index = fLength; - if (fLength++ == fUri.length) { - String[] uris = new String[fUri.length + 4]; - String[] lps = new String[fUri.length + 4]; - System.arraycopy(fUri, 0, uris, 0, fUri.length); - System.arraycopy(fLocalpart, 0, lps, 0, fLocalpart.length); - fUri = uris; - fLocalpart = lps; - - } - - fUri[index] = qname.uri; - fLocalpart[index] = qname.localpart; - } - /** * Scans an attribute. * <p> @@ -395,15 +378,26 @@ // content int oldLen = attributes.getLength(); - attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null); - // WFC: Unique Att Spec - if (oldLen == attributes.getLength()) { - reportFatalError( - "AttributeNotUnique", - new Object[] { - fCurrentElement.rawname, - fAttributeQName.rawname }); + if (fBindNamespaces) { + attributes.addAttributeNS( + fAttributeQName, + XMLSymbols.fCDATASymbol, + null); + } else { + attributes.addAttribute( + fAttributeQName, + XMLSymbols.fCDATASymbol, + null); + + // WFC: Unique Att Spec + if (oldLen == attributes.getLength()) { + reportFatalError( + "AttributeNotUnique", + new Object[] { + fCurrentElement.rawname, + fAttributeQName.rawname }); + } } //REVISIT: one more case needs to be included: external PE and standalone is no 1.17 +40 -16 xml-xerces/java/src/org/apache/xerces/impl/XMLNSDocumentScannerImpl.java Index: XMLNSDocumentScannerImpl.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/XMLNSDocumentScannerImpl.java,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- XMLNSDocumentScannerImpl.java 2 Sep 2003 07:06:14 -0000 1.16 +++ XMLNSDocumentScannerImpl.java 10 Oct 2003 18:25:40 -0000 1.17 @@ -112,9 +112,9 @@ * scanner if DTD grammar is missing.*/ protected boolean fPerformValidation; - protected String[] fUri= new String[4]; - protected String[] fLocalpart = new String[4]; - protected int fLength = 0; + // protected String[] fUri= new String[4]; + // protected String[] fLocalpart = new String[4]; + // protected int fLength = 0; // private data @@ -248,7 +248,7 @@ // bind attributes (xmlns are already bound bellow) int length = fAttributes.getLength(); - fLength = 0; //initialize structure + // fLength = 0; //initialize structure for (int i = 0; i < length; i++) { fAttributes.getName(i, fAttributeQName); @@ -258,7 +258,7 @@ // REVISIT: try removing the first "if" and see if it is faster. // if (fAttributeQName.uri != null && fAttributeQName.uri == uri) { - checkDuplicates(fAttributeQName, fAttributes); + // checkDuplicates(fAttributeQName, fAttributes); continue; } if (aprefix != XMLSymbols.EMPTY_STRING) { @@ -270,7 +270,25 @@ XMLErrorReporter.SEVERITY_FATAL_ERROR); } fAttributes.setURI(i, uri); - checkDuplicates(fAttributeQName, fAttributes); + // checkDuplicates(fAttributeQName, fAttributes); + } + } + + if (length > 1) { + QName name = fAttributes.checkDuplicatesNS(); + if (name != null) { + if (name.uri != null) { + fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, + "AttributeNSNotUnique", + new Object[]{fElementQName.rawname, name.localpart, name.uri}, + XMLErrorReporter.SEVERITY_FATAL_ERROR); + } + else { + fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN, + "AttributeNotUnique", + new Object[]{fElementQName.rawname, name.rawname}, + XMLErrorReporter.SEVERITY_FATAL_ERROR); + } } } } @@ -306,7 +324,7 @@ } // scanStartElement():boolean - private final void checkDuplicates(QName qname, XMLAttributesImpl attributes){ + /** private final void checkDuplicates(QName qname, XMLAttributesImpl attributes){ // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/> // search if such attribute already exists @@ -331,7 +349,7 @@ fUri[index] = qname.uri; fLocalpart[index] = qname.localpart; - } + } **/ @@ -370,13 +388,19 @@ // content int oldLen = attributes.getLength(); - attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null); - - // WFC: Unique Att Spec - if (oldLen == attributes.getLength()) { - reportFatalError("AttributeNotUnique", - new Object[]{fCurrentElement.rawname, + + if (fBindNamespaces) { + attributes.addAttributeNS(fAttributeQName, XMLSymbols.fCDATASymbol, null); + } + else { + attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null); + + // WFC: Unique Att Spec + if (oldLen == attributes.getLength()) { + reportFatalError("AttributeNotUnique", + new Object[]{fCurrentElement.rawname, fAttributeQName.rawname}); + } } //REVISIT: one more case needs to be included: external PE and standalone is no @@ -393,7 +417,7 @@ // record namespace declarations if any. if (fBindNamespaces) { - + String localpart = fAttributeQName.localpart; String prefix = fAttributeQName.prefix != null ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING; 1.30 +5 -2 xml-xerces/java/src/org/apache/xerces/impl/XMLNamespaceBinder.java Index: XMLNamespaceBinder.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/impl/XMLNamespaceBinder.java,v retrieving revision 1.29 retrieving revision 1.30 diff -u -r1.29 -r1.30 --- XMLNamespaceBinder.java 29 May 2003 13:25:41 -0000 1.29 +++ XMLNamespaceBinder.java 10 Oct 2003 18:25:40 -0000 1.30 @@ -824,8 +824,11 @@ // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/> int attrCount = attributes.getLength(); for (int i = 0; i < attrCount - 1; i++) { - String alocalpart = attributes.getLocalName(i); String auri = attributes.getURI(i); + if (auri == null || auri == NamespaceContext.XMLNS_URI) { + continue; + } + String alocalpart = attributes.getLocalName(i); for (int j = i + 1; j < attrCount; j++) { String blocalpart = attributes.getLocalName(j); String buri = attributes.getURI(j);
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]