1/*
2 * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xerces.internal.util;
22
23import com.sun.xml.internal.stream.XMLBufferListener;
24import com.sun.org.apache.xerces.internal.xni.Augmentations;
25import com.sun.org.apache.xerces.internal.xni.QName;
26import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
27import com.sun.org.apache.xerces.internal.xni.XMLString;
28/**
29 * The XMLAttributesImpl class is an implementation of the XMLAttributes
30 * interface which defines a collection of attributes for an element.
31 * In the parser, the document source would scan the entire start element
32 * and collect the attributes. The attributes are communicated to the
33 * document handler in the startElement method.
34 * <p>
35 * The attributes are read-write so that subsequent stages in the document
36 * pipeline can modify the values or change the attributes that are
37 * propogated to the next stage.
38 *
39 * @see com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler#startElement
40 *
41 * @author Andy Clark, IBM
42 * @author Elena Litani, IBM
43 * @author Michael Glavassevich, IBM
44 *
45 */
46public class XMLAttributesImpl
47implements XMLAttributes, XMLBufferListener {
48
49    //
50    // Constants
51    //
52
53    /** Default table size. */
54    protected static final int TABLE_SIZE = 101;
55
56    /** Maximum hash collisions per bucket. */
57    protected static final int MAX_HASH_COLLISIONS = 40;
58
59    protected static final int MULTIPLIERS_SIZE = 1 << 5;
60    protected static final int MULTIPLIERS_MASK = MULTIPLIERS_SIZE - 1;
61
62    /**
63     * Threshold at which an instance is treated
64     * as a large attribute list.
65     */
66    protected static final int SIZE_LIMIT = 20;
67
68    //
69    // Data
70    //
71
72    // features
73
74    /** Namespaces. */
75    protected boolean fNamespaces = true;
76
77    // data
78
79    /**
80     * Usage count for the attribute table view.
81     * Incremented each time all attributes are removed
82     * when the attribute table view is in use.
83     */
84    protected int fLargeCount = 1;
85
86    /** Attribute count. */
87    protected int fLength;
88
89    /** Attribute information. */
90    protected Attribute[] fAttributes = new Attribute[4];
91
92    /**
93     * Provides an alternate view of the attribute specification.
94     */
95    protected Attribute[] fAttributeTableView;
96
97    /**
98     * Tracks whether each chain in the hash table is stale
99     * with respect to the current state of this object.
100     * A chain is stale if its state is not the same as the number
101     * of times the attribute table view has been used.
102     */
103    protected int[] fAttributeTableViewChainState;
104
105    /**
106     * Actual number of buckets in the table view.
107     */
108    protected int fTableViewBuckets;
109
110    /**
111     * Indicates whether the table view contains consistent data.
112     */
113    protected boolean fIsTableViewConsistent;
114
115    /**
116     * Array of randomly selected hash function multipliers or <code>null</code>
117     * if the default String.hashCode() function should be used.
118     */
119    protected int[] fHashMultipliers;
120
121    //
122    // Constructors
123    //
124
125    /** Default constructor. */
126    public XMLAttributesImpl() {
127        this(TABLE_SIZE);
128    }
129
130    /**
131     * @param tableSize initial size of table view
132     */
133    public XMLAttributesImpl(int tableSize) {
134        fTableViewBuckets = tableSize;
135        for (int i = 0; i < fAttributes.length; i++) {
136            fAttributes[i] = new Attribute();
137        }
138    } // <init>()
139
140    //
141    // Public methods
142    //
143
144    /**
145     * Sets whether namespace processing is being performed. This state
146     * is needed to return the correct value from the getLocalName method.
147     *
148     * @param namespaces True if namespace processing is turned on.
149     *
150     * @see #getLocalName
151     */
152    public void setNamespaces(boolean namespaces) {
153        fNamespaces = namespaces;
154    } // setNamespaces(boolean)
155
156    //
157    // XMLAttributes methods
158    //
159
160    /**
161     * Adds an attribute. The attribute's non-normalized value of the
162     * attribute will have the same value as the attribute value until
163     * set using the <code>setNonNormalizedValue</code> method. Also,
164     * the added attribute will be marked as specified in the XML instance
165     * document unless set otherwise using the <code>setSpecified</code>
166     * method.
167     * <p>
168     * <strong>Note:</strong> If an attribute of the same name already
169     * exists, the old values for the attribute are replaced by the new
170     * values.
171     *
172     * @param name  The attribute name.
173     * @param type  The attribute type. The type name is determined by
174     *                  the type specified for this attribute in the DTD.
175     *                  For example: "CDATA", "ID", "NMTOKEN", etc. However,
176     *                  attributes of type enumeration will have the type
177     *                  value specified as the pipe ('|') separated list of
178     *                  the enumeration values prefixed by an open
179     *                  parenthesis and suffixed by a close parenthesis.
180     *                  For example: "(true|false)".
181     * @param value The attribute value.
182     *
183     * @return Returns the attribute index.
184     *
185     * @see #setNonNormalizedValue
186     * @see #setSpecified
187     */
188    public int addAttribute(QName name, String type, String value) {
189      return addAttribute(name,type,value,null);
190    }
191    public int addAttribute(QName name, String type, String value,XMLString valueCache) {
192
193        int index;
194        if (fLength < SIZE_LIMIT) {
195            index = name.uri != null && name.uri.length() != 0
196                ? getIndexFast(name.uri, name.localpart)
197                : getIndexFast(name.rawname);
198
199            if (index == -1) {
200                index = fLength;
201                if (fLength++ == fAttributes.length) {
202                    Attribute[] attributes = new Attribute[fAttributes.length + 4];
203                    System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
204                    for (int i = fAttributes.length; i < attributes.length; i++) {
205                        attributes[i] = new Attribute();
206                    }
207                    fAttributes = attributes;
208                }
209            }
210        }
211        else if (name.uri == null ||
212            name.uri.length() == 0 ||
213            (index = getIndexFast(name.uri, name.localpart)) == -1) {
214
215            /**
216             * If attributes were removed from the list after the table
217             * becomes in use this isn't reflected in the table view. It's
218             * assumed that once a user starts removing attributes they're
219             * not likely to add more. We only make the view consistent if
220             * the user of this class adds attributes, removes them, and
221             * then adds more.
222             */
223            if (!fIsTableViewConsistent || fLength == SIZE_LIMIT ||
224                (fLength > SIZE_LIMIT && fLength > fTableViewBuckets)) {
225                prepareAndPopulateTableView();
226                fIsTableViewConsistent = true;
227            }
228
229            int bucket = getTableViewBucket(name.rawname);
230
231            // The chain is stale.
232            // This must be a unique attribute.
233            if (fAttributeTableViewChainState[bucket] != fLargeCount) {
234                index = fLength;
235                if (fLength++ == fAttributes.length) {
236                    Attribute[] attributes = new Attribute[fAttributes.length << 1];
237                    System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
238                    for (int i = fAttributes.length; i < attributes.length; i++) {
239                        attributes[i] = new Attribute();
240                    }
241                    fAttributes = attributes;
242                }
243
244                // Update table view.
245                fAttributeTableViewChainState[bucket] = fLargeCount;
246                fAttributes[index].next = null;
247                fAttributeTableView[bucket] = fAttributes[index];
248            }
249            // This chain is active.
250            // We need to check if any of the attributes has the same rawname.
251            else {
252                // Search the table.
253                int collisionCount = 0;
254                Attribute found = fAttributeTableView[bucket];
255                while (found != null) {
256                    if (found.name.rawname == name.rawname) {
257                        break;
258                    }
259                    found = found.next;
260                    ++collisionCount;
261                }
262                // This attribute is unique.
263                if (found == null) {
264                    index = fLength;
265                    if (fLength++ == fAttributes.length) {
266                        Attribute[] attributes = new Attribute[fAttributes.length << 1];
267                        System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
268                        for (int i = fAttributes.length; i < attributes.length; i++) {
269                            attributes[i] = new Attribute();
270                        }
271                        fAttributes = attributes;
272                    }
273
274                    // Select a new hash function and rehash the table view
275                    // if the collision threshold is exceeded.
276                    if (collisionCount >= MAX_HASH_COLLISIONS) {
277                        // The current attribute will be processed in the rehash.
278                        // Need to set its name first.
279                        fAttributes[index].name.setValues(name);
280                        rebalanceTableView(fLength);
281                    }
282                    else {
283                        // Update table view
284                        fAttributes[index].next = fAttributeTableView[bucket];
285                        fAttributeTableView[bucket] = fAttributes[index];
286                    }
287                }
288                // Duplicate. We still need to find the index.
289                else {
290                    index = getIndexFast(name.rawname);
291                }
292            }
293        }
294
295        // set values
296        Attribute attribute = fAttributes[index];
297        attribute.name.setValues(name);
298        attribute.type = type;
299        attribute.value = value;
300        attribute.xmlValue = valueCache;
301        attribute.nonNormalizedValue = value;
302        attribute.specified = false;
303
304        // clear augmentations
305        if(attribute.augs != null)
306            attribute.augs.removeAllItems();
307
308        return index;
309
310    } // addAttribute(QName,String,XMLString)
311
312    /**
313     * Removes all of the attributes. This method will also remove all
314     * entities associated to the attributes.
315     */
316    public void removeAllAttributes() {
317        fLength = 0;
318    } // removeAllAttributes()
319
320    /**
321     * Removes the attribute at the specified index.
322     * <p>
323     * <strong>Note:</strong> This operation changes the indexes of all
324     * attributes following the attribute at the specified index.
325     *
326     * @param attrIndex The attribute index.
327     */
328    public void removeAttributeAt(int attrIndex) {
329        fIsTableViewConsistent = false;
330        if (attrIndex < fLength - 1) {
331            Attribute removedAttr = fAttributes[attrIndex];
332            System.arraycopy(fAttributes, attrIndex + 1,
333                             fAttributes, attrIndex, fLength - attrIndex - 1);
334            // Make the discarded Attribute object available for re-use
335            // by tucking it after the Attributes that are still in use
336            fAttributes[fLength-1] = removedAttr;
337        }
338        fLength--;
339    } // removeAttributeAt(int)
340
341    /**
342     * Sets the name of the attribute at the specified index.
343     *
344     * @param attrIndex The attribute index.
345     * @param attrName  The new attribute name.
346     */
347    public void setName(int attrIndex, QName attrName) {
348        fAttributes[attrIndex].name.setValues(attrName);
349    } // setName(int,QName)
350
351    /**
352     * Sets the fields in the given QName structure with the values
353     * of the attribute name at the specified index.
354     *
355     * @param attrIndex The attribute index.
356     * @param attrName  The attribute name structure to fill in.
357     */
358    public void getName(int attrIndex, QName attrName) {
359        attrName.setValues(fAttributes[attrIndex].name);
360    } // getName(int,QName)
361
362    /**
363     * Sets the type of the attribute at the specified index.
364     *
365     * @param attrIndex The attribute index.
366     * @param attrType  The attribute type. The type name is determined by
367     *                  the type specified for this attribute in the DTD.
368     *                  For example: "CDATA", "ID", "NMTOKEN", etc. However,
369     *                  attributes of type enumeration will have the type
370     *                  value specified as the pipe ('|') separated list of
371     *                  the enumeration values prefixed by an open
372     *                  parenthesis and suffixed by a close parenthesis.
373     *                  For example: "(true|false)".
374     */
375    public void setType(int attrIndex, String attrType) {
376        fAttributes[attrIndex].type = attrType;
377    } // setType(int,String)
378
379    /**
380     * Sets the value of the attribute at the specified index. This
381     * method will overwrite the non-normalized value of the attribute.
382     *
383     * @param attrIndex The attribute index.
384     * @param attrValue The new attribute value.
385     *
386     * @see #setNonNormalizedValue
387     */
388    public void setValue(int attrIndex, String attrValue) {
389        setValue(attrIndex,attrValue,null);
390    }
391
392    public void setValue(int attrIndex, String attrValue,XMLString value) {
393        Attribute attribute = fAttributes[attrIndex];
394        attribute.value = attrValue;
395        attribute.nonNormalizedValue = attrValue;
396        attribute.xmlValue = value;
397    } // setValue(int,String)
398
399    /**
400     * Sets the non-normalized value of the attribute at the specified
401     * index.
402     *
403     * @param attrIndex The attribute index.
404     * @param attrValue The new non-normalized attribute value.
405     */
406    public void setNonNormalizedValue(int attrIndex, String attrValue) {
407        if (attrValue == null) {
408            attrValue = fAttributes[attrIndex].value;
409        }
410        fAttributes[attrIndex].nonNormalizedValue = attrValue;
411    } // setNonNormalizedValue(int,String)
412
413    /**
414     * Returns the non-normalized value of the attribute at the specified
415     * index. If no non-normalized value is set, this method will return
416     * the same value as the <code>getValue(int)</code> method.
417     *
418     * @param attrIndex The attribute index.
419     */
420    public String getNonNormalizedValue(int attrIndex) {
421        String value = fAttributes[attrIndex].nonNormalizedValue;
422        return value;
423    } // getNonNormalizedValue(int):String
424
425    /**
426     * Sets whether an attribute is specified in the instance document
427     * or not.
428     *
429     * @param attrIndex The attribute index.
430     * @param specified True if the attribute is specified in the instance
431     *                  document.
432     */
433    public void setSpecified(int attrIndex, boolean specified) {
434        fAttributes[attrIndex].specified = specified;
435    } // setSpecified(int,boolean)
436
437    /**
438     * Returns true if the attribute is specified in the instance document.
439     *
440     * @param attrIndex The attribute index.
441     */
442    public boolean isSpecified(int attrIndex) {
443        return fAttributes[attrIndex].specified;
444    } // isSpecified(int):boolean
445
446    //
447    // AttributeList and Attributes methods
448    //
449
450    /**
451     * Return the number of attributes in the list.
452     *
453     * <p>Once you know the number of attributes, you can iterate
454     * through the list.</p>
455     *
456     * @return The number of attributes in the list.
457     */
458    public int getLength() {
459        return fLength;
460    } // getLength():int
461
462    /**
463     * Look up an attribute's type by index.
464     *
465     * <p>The attribute type is one of the strings "CDATA", "ID",
466     * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
467     * or "NOTATION" (always in upper case).</p>
468     *
469     * <p>If the parser has not read a declaration for the attribute,
470     * or if the parser does not report attribute types, then it must
471     * return the value "CDATA" as stated in the XML 1.0 Recommentation
472     * (clause 3.3.3, "Attribute-Value Normalization").</p>
473     *
474     * <p>For an enumerated attribute that is not a notation, the
475     * parser will report the type as "NMTOKEN".</p>
476     *
477     * @param index The attribute index (zero-based).
478     * @return The attribute's type as a string, or null if the
479     *         index is out of range.
480     * @see #getLength
481     */
482    public String getType(int index) {
483        if (index < 0 || index >= fLength) {
484            return null;
485        }
486        return getReportableType(fAttributes[index].type);
487    } // getType(int):String
488
489    /**
490     * Look up an attribute's type by XML 1.0 qualified name.
491     *
492     * <p>See {@link #getType(int) getType(int)} for a description
493     * of the possible types.</p>
494     *
495     * @param qname The XML 1.0 qualified name.
496     * @return The attribute type as a string, or null if the
497     *         attribute is not in the list or if qualified names
498     *         are not available.
499     */
500    public String getType(String qname) {
501        int index = getIndex(qname);
502        return index != -1 ? getReportableType(fAttributes[index].type) : null;
503    } // getType(String):String
504
505    /**
506     * Look up an attribute's value by index.
507     *
508     * <p>If the attribute value is a list of tokens (IDREFS,
509     * ENTITIES, or NMTOKENS), the tokens will be concatenated
510     * into a single string with each token separated by a
511     * single space.</p>
512     *
513     * @param index The attribute index (zero-based).
514     * @return The attribute's value as a string, or null if the
515     *         index is out of range.
516     * @see #getLength
517     */
518    public String getValue(int index) {
519        if (index < 0 || index >= fLength) {
520            return null;
521        }
522        if(fAttributes[index].value == null && fAttributes[index].xmlValue != null)
523            fAttributes[index].value = fAttributes[index].xmlValue.toString();
524        return fAttributes[index].value;
525    } // getValue(int):String
526
527    /**
528     * Look up an attribute's value by XML 1.0 qualified name.
529     *
530     * <p>See {@link #getValue(int) getValue(int)} for a description
531     * of the possible values.</p>
532     *
533     * @param qname The XML 1.0 qualified name.
534     * @return The attribute value as a string, or null if the
535     *         attribute is not in the list or if qualified names
536     *         are not available.
537     */
538    public String getValue(String qname) {
539        int index = getIndex(qname);
540        if(index == -1 )
541            return null;
542        if(fAttributes[index].value == null)
543            fAttributes[index].value = fAttributes[index].xmlValue.toString();
544        return fAttributes[index].value;
545    } // getValue(String):String
546
547    //
548    // AttributeList methods
549    //
550
551    /**
552     * Return the name of an attribute in this list (by position).
553     *
554     * <p>The names must be unique: the SAX parser shall not include the
555     * same attribute twice.  Attributes without values (those declared
556     * #IMPLIED without a value specified in the start tag) will be
557     * omitted from the list.</p>
558     *
559     * <p>If the attribute name has a namespace prefix, the prefix
560     * will still be attached.</p>
561     *
562     * @param i The index of the attribute in the list (starting at 0).
563     * @return The name of the indexed attribute, or null
564     *         if the index is out of range.
565     * @see #getLength
566     */
567    public String getName(int index) {
568        if (index < 0 || index >= fLength) {
569            return null;
570        }
571        return fAttributes[index].name.rawname;
572    } // getName(int):String
573
574    //
575    // Attributes methods
576    //
577
578    /**
579     * Look up the index of an attribute by XML 1.0 qualified name.
580     *
581     * @param qName The qualified (prefixed) name.
582     * @return The index of the attribute, or -1 if it does not
583     *         appear in the list.
584     */
585    public int getIndex(String qName) {
586        for (int i = 0; i < fLength; i++) {
587            Attribute attribute = fAttributes[i];
588            if (attribute.name.rawname != null &&
589                attribute.name.rawname.equals(qName)) {
590                return i;
591            }
592        }
593        return -1;
594    } // getIndex(String):int
595
596    /**
597     * Look up the index of an attribute by Namespace name.
598     *
599     * @param uri The Namespace URI, or null if
600     *        the name has no Namespace URI.
601     * @param localName The attribute's local name.
602     * @return The index of the attribute, or -1 if it does not
603     *         appear in the list.
604     */
605    public int getIndex(String uri, String localPart) {
606        for (int i = 0; i < fLength; i++) {
607            Attribute attribute = fAttributes[i];
608            if (attribute.name.localpart != null &&
609                attribute.name.localpart.equals(localPart) &&
610                ((uri==attribute.name.uri) ||
611            (uri!=null && attribute.name.uri!=null && attribute.name.uri.equals(uri)))) {
612                return i;
613            }
614        }
615        return -1;
616    } // getIndex(String,String):int
617
618    /**
619     * Look up the index of an attribute by local name only,
620     * ignoring its namespace.
621     *
622     * @param localName The attribute's local name.
623     * @return The index of the attribute, or -1 if it does not
624     *         appear in the list.
625     */
626    public int getIndexByLocalName(String localPart) {
627        for (int i = 0; i < fLength; i++) {
628            Attribute attribute = fAttributes[i];
629            if (attribute.name.localpart != null &&
630                attribute.name.localpart.equals(localPart)) {
631                return i;
632            }
633        }
634        return -1;
635    } // getIndex(String):int
636
637    /**
638     * Look up an attribute's local name by index.
639     *
640     * @param index The attribute index (zero-based).
641     * @return The local name, or the empty string if Namespace
642     *         processing is not being performed, or null
643     *         if the index is out of range.
644     * @see #getLength
645     */
646    public String getLocalName(int index) {
647        if (!fNamespaces) {
648            return "";
649        }
650        if (index < 0 || index >= fLength) {
651            return null;
652        }
653        return fAttributes[index].name.localpart;
654    } // getLocalName(int):String
655
656    /**
657     * Look up an attribute's XML 1.0 qualified name by index.
658     *
659     * @param index The attribute index (zero-based).
660     * @return The XML 1.0 qualified name, or the empty string
661     *         if none is available, or null if the index
662     *         is out of range.
663     * @see #getLength
664     */
665    public String getQName(int index) {
666        if (index < 0 || index >= fLength) {
667            return null;
668        }
669        String rawname = fAttributes[index].name.rawname;
670        return rawname != null ? rawname : "";
671    } // getQName(int):String
672
673    public QName getQualifiedName(int index){
674        if (index < 0 || index >= fLength) {
675            return null;
676        }
677        return fAttributes[index].name;
678    }
679
680    /**
681     * Look up an attribute's type by Namespace name.
682     *
683     * <p>See {@link #getType(int) getType(int)} for a description
684     * of the possible types.</p>
685     *
686     * @param uri The Namespace URI, or null if the
687     *        name has no Namespace URI.
688     * @param localName The local name of the attribute.
689     * @return The attribute type as a string, or null if the
690     *         attribute is not in the list or if Namespace
691     *         processing is not being performed.
692     */
693    public String getType(String uri, String localName) {
694        if (!fNamespaces) {
695            return null;
696        }
697        int index = getIndex(uri, localName);
698        return index != -1 ? getType(index) : null;
699    } // getType(String,String):String
700    /**
701     * Look up the index of an attribute by XML 1.0 qualified name.
702     * <p>
703     * <strong>Note:</strong>
704     * This method uses reference comparison, and thus should
705     * only be used internally. We cannot use this method in any
706     * code exposed to users as they may not pass in unique strings.
707     *
708     * @param qName The qualified (prefixed) name.
709     * @return The index of the attribute, or -1 if it does not
710     *         appear in the list.
711     */
712    public int getIndexFast(String qName) {
713        for (int i = 0; i < fLength; ++i) {
714            Attribute attribute = fAttributes[i];
715            if (attribute.name.rawname == qName) {
716                return i;
717            }
718        }
719        return -1;
720    } // getIndexFast(String):int
721
722    /**
723     * Adds an attribute. The attribute's non-normalized value of the
724     * attribute will have the same value as the attribute value until
725     * set using the <code>setNonNormalizedValue</code> method. Also,
726     * the added attribute will be marked as specified in the XML instance
727     * document unless set otherwise using the <code>setSpecified</code>
728     * method.
729     * <p>
730     * This method differs from <code>addAttribute</code> in that it
731     * does not check if an attribute of the same name already exists
732     * in the list before adding it. In order to improve performance
733     * of namespace processing, this method allows uniqueness checks
734     * to be deferred until all the namespace information is available
735     * after the entire attribute specification has been read.
736     * <p>
737     * <strong>Caution:</strong> If this method is called it should
738     * not be mixed with calls to <code>addAttribute</code> unless
739     * it has been determined that all the attribute names are unique.
740     *
741     * @param name the attribute name
742     * @param type the attribute type
743     * @param value the attribute value
744     *
745     * @see #setNonNormalizedValue
746     * @see #setSpecified
747     * @see #checkDuplicatesNS
748     */
749    public void addAttributeNS(QName name, String type, String value) {
750        int index = fLength;
751        if (fLength++ == fAttributes.length) {
752            Attribute[] attributes;
753            if (fLength < SIZE_LIMIT) {
754                attributes = new Attribute[fAttributes.length + 4];
755            }
756            else {
757                attributes = new Attribute[fAttributes.length << 1];
758            }
759            System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
760            for (int i = fAttributes.length; i < attributes.length; i++) {
761                attributes[i] = new Attribute();
762            }
763            fAttributes = attributes;
764        }
765
766        // set values
767        Attribute attribute = fAttributes[index];
768        attribute.name.setValues(name);
769        attribute.type = type;
770        attribute.value = value;
771        attribute.nonNormalizedValue = value;
772        attribute.specified = false;
773
774        // clear augmentations
775        attribute.augs.removeAllItems();
776    }
777
778    /**
779     * Checks for duplicate expanded names (local part and namespace name
780     * pairs) in the attribute specification. If a duplicate is found its
781     * name is returned.
782     * <p>
783     * This should be called once all the in-scope namespaces for the element
784     * enclosing these attributes is known, and after all the attributes
785     * have gone through namespace binding.
786     *
787     * @return the name of a duplicate attribute found in the search,
788     * otherwise null.
789     */
790    public QName checkDuplicatesNS() {
791        // If the list is small check for duplicates using pairwise comparison.
792        final int length = fLength;
793        if (length <= SIZE_LIMIT) {
794            final Attribute[] attributes = fAttributes;
795            for (int i = 0; i < length - 1; ++i) {
796                Attribute att1 = attributes[i];
797                for (int j = i + 1; j < length; ++j) {
798                    Attribute att2 = attributes[j];
799                    if (att1.name.localpart == att2.name.localpart &&
800                        att1.name.uri == att2.name.uri) {
801                        return att2.name;
802                    }
803                }
804            }
805            return null;
806        }
807        // If the list is large check duplicates using a hash table.
808        else {
809            return checkManyDuplicatesNS();
810        }
811    }
812
813    private QName checkManyDuplicatesNS() {
814        // We don't want this table view to be read if someone calls
815        // addAttribute so we invalidate it up front.
816        fIsTableViewConsistent = false;
817
818        prepareTableView();
819
820        Attribute attr;
821        int bucket;
822
823        final int length = fLength;
824        final Attribute[] attributes = fAttributes;
825        final Attribute[] attributeTableView = fAttributeTableView;
826        final int[] attributeTableViewChainState = fAttributeTableViewChainState;
827        int largeCount = fLargeCount;
828
829        for (int i = 0; i < length; ++i) {
830            attr = attributes[i];
831            bucket = getTableViewBucket(attr.name.localpart, attr.name.uri);
832
833            // The chain is stale.
834            // This must be a unique attribute.
835            if (attributeTableViewChainState[bucket] != largeCount) {
836                attributeTableViewChainState[bucket] = largeCount;
837                attr.next = null;
838                attributeTableView[bucket] = attr;
839            }
840            // This chain is active.
841            // We need to check if any of the attributes has the same name.
842            else {
843                // Search the table.
844                int collisionCount = 0;
845                Attribute found = attributeTableView[bucket];
846                while (found != null) {
847                    if (found.name.localpart == attr.name.localpart &&
848                        found.name.uri == attr.name.uri) {
849                        return attr.name;
850                    }
851                    found = found.next;
852                    ++collisionCount;
853                }
854                // Select a new hash function and rehash the table view
855                // if the collision threshold is exceeded.
856                if (collisionCount >= MAX_HASH_COLLISIONS) {
857                    // The current attribute will be processed in the rehash.
858                    rebalanceTableViewNS(i+1);
859                    largeCount = fLargeCount;
860                }
861                else {
862                    // Update table view
863                    attr.next = attributeTableView[bucket];
864                    attributeTableView[bucket] = attr;
865                }
866            }
867        }
868        return null;
869    }
870
871    /**
872     * Look up the index of an attribute by Namespace name.
873     * <p>
874     * <strong>Note:</strong>
875     * This method uses reference comparison, and thus should
876     * only be used internally. We cannot use this method in any
877     * code exposed to users as they may not pass in unique strings.
878     *
879     * @param uri The Namespace URI, or null if
880     *        the name has no Namespace URI.
881     * @param localName The attribute's local name.
882     * @return The index of the attribute, or -1 if it does not
883     *         appear in the list.
884     */
885    public int getIndexFast(String uri, String localPart) {
886        for (int i = 0; i < fLength; ++i) {
887            Attribute attribute = fAttributes[i];
888            if (attribute.name.localpart == localPart &&
889                attribute.name.uri == uri) {
890                return i;
891            }
892        }
893        return -1;
894    } // getIndexFast(String,String):int
895
896    /**
897     * Returns the value passed in or NMTOKEN if it's an enumerated type.
898     *
899     * @param type attribute type
900     * @return the value passed in or NMTOKEN if it's an enumerated type.
901     */
902    private String getReportableType(String type) {
903
904        if (type.charAt(0) == '(') {
905            return "NMTOKEN";
906        }
907        return type;
908    }
909
910    /**
911     * Returns the position in the table view
912     * where the given attribute name would be hashed.
913     *
914     * @param qname the attribute name
915     * @return the position in the table view where the given attribute
916     * would be hashed
917     */
918    protected int getTableViewBucket(String qname) {
919        return (hash(qname) & 0x7FFFFFFF) % fTableViewBuckets;
920    }
921
922    /**
923     * Returns the position in the table view
924     * where the given attribute name would be hashed.
925     *
926     * @param localpart the local part of the attribute
927     * @param uri the namespace name of the attribute
928     * @return the position in the table view where the given attribute
929     * would be hashed
930     */
931    protected int getTableViewBucket(String localpart, String uri) {
932        if (uri == null) {
933            return (hash(localpart) & 0x7FFFFFFF) % fTableViewBuckets;
934        }
935        else {
936            return (hash(localpart, uri) & 0x7FFFFFFF) % fTableViewBuckets;
937        }
938    }
939
940    private int hash(String localpart) {
941        if (fHashMultipliers == null) {
942            return localpart.hashCode();
943        }
944        return hash0(localpart);
945    } // hash(String):int
946
947    private int hash(String localpart, String uri) {
948        if (fHashMultipliers == null) {
949            return localpart.hashCode() + uri.hashCode() * 31;
950        }
951        return hash0(localpart) + hash0(uri) * fHashMultipliers[MULTIPLIERS_SIZE];
952    } // hash(String,String):int
953
954    private int hash0(String symbol) {
955        int code = 0;
956        final int length = symbol.length();
957        final int[] multipliers = fHashMultipliers;
958        for (int i = 0; i < length; ++i) {
959            code = code * multipliers[i & MULTIPLIERS_MASK] + symbol.charAt(i);
960        }
961        return code;
962    } // hash0(String):int
963
964    /**
965     * Purges all elements from the table view.
966     */
967    protected void cleanTableView() {
968        if (++fLargeCount < 0) {
969            // Overflow. We actually need to visit the chain state array.
970            if (fAttributeTableViewChainState != null) {
971                for (int i = fTableViewBuckets - 1; i >= 0; --i) {
972                    fAttributeTableViewChainState[i] = 0;
973                }
974            }
975            fLargeCount = 1;
976        }
977    }
978
979     /**
980     * Increases the capacity of the table view.
981     */
982    private void growTableView() {
983        final int length = fLength;
984        int tableViewBuckets = fTableViewBuckets;
985        do {
986            tableViewBuckets = (tableViewBuckets << 1) + 1;
987            if (tableViewBuckets < 0) {
988                tableViewBuckets = Integer.MAX_VALUE;
989                break;
990            }
991        }
992       while (length > tableViewBuckets);
993        fTableViewBuckets = tableViewBuckets;
994        fAttributeTableView = null;
995        fLargeCount = 1;
996    }
997
998    /**
999     * Prepares the table view of the attributes list for use.
1000     */
1001    protected void prepareTableView() {
1002        if (fLength > fTableViewBuckets) {
1003            growTableView();
1004        }
1005        if (fAttributeTableView == null) {
1006            fAttributeTableView = new Attribute[fTableViewBuckets];
1007            fAttributeTableViewChainState = new int[fTableViewBuckets];
1008        }
1009        else {
1010            cleanTableView();
1011        }
1012    }
1013
1014    /**
1015     * Prepares the table view of the attributes list for use,
1016     * and populates it with the attributes which have been
1017     * previously read.
1018     */
1019    protected void prepareAndPopulateTableView() {
1020        prepareAndPopulateTableView(fLength);
1021    }
1022
1023    private void prepareAndPopulateTableView(final int count) {
1024        prepareTableView();
1025        // Need to populate the hash table with the attributes we've processed so far.
1026        Attribute attr;
1027        int bucket;
1028        for (int i = 0; i < count; ++i) {
1029            attr = fAttributes[i];
1030            bucket = getTableViewBucket(attr.name.rawname);
1031            if (fAttributeTableViewChainState[bucket] != fLargeCount) {
1032                fAttributeTableViewChainState[bucket] = fLargeCount;
1033                attr.next = null;
1034                fAttributeTableView[bucket] = attr;
1035            }
1036            else {
1037                // Update table view
1038                attr.next = fAttributeTableView[bucket];
1039                fAttributeTableView[bucket] = attr;
1040            }
1041        }
1042    }
1043
1044
1045    /**
1046     * Returns the prefix of the attribute at the specified index.
1047     *
1048     * @param index The index of the attribute.
1049     */
1050    public String getPrefix(int index) {
1051        if (index < 0 || index >= fLength) {
1052            return null;
1053        }
1054        String prefix = fAttributes[index].name.prefix;
1055        // REVISIT: The empty string is not entered in the symbol table!
1056        return prefix != null ? prefix : "";
1057    } // getPrefix(int):String
1058
1059    /**
1060     * Look up an attribute's Namespace URI by index.
1061     *
1062     * @param index The attribute index (zero-based).
1063     * @return The Namespace URI
1064     * @see #getLength
1065     */
1066    public String getURI(int index) {
1067        if (index < 0 || index >= fLength) {
1068            return null;
1069        }
1070        String uri = fAttributes[index].name.uri;
1071        return uri;
1072    } // getURI(int):String
1073
1074    /**
1075     * Look up an attribute's value by Namespace name and
1076     * Local name. If Namespace is null, ignore namespace
1077     * comparison. If Namespace is "", treat the name as
1078     * having no Namespace URI.
1079     *
1080     * <p>See {@link #getValue(int) getValue(int)} for a description
1081     * of the possible values.</p>
1082     *
1083     * @param uri The Namespace URI, or null namespaces are ignored.
1084     * @param localName The local name of the attribute.
1085     * @return The attribute value as a string, or null if the
1086     *         attribute is not in the list.
1087     */
1088    public String getValue(String uri, String localName) {
1089        int index = getIndex(uri, localName);
1090        return index != -1 ? getValue(index) : null;
1091    } // getValue(String,String):String
1092
1093    /**
1094     * Look up an augmentations by Namespace name.
1095     *
1096     * @param uri The Namespace URI, or null if the
1097     * @param localName The local name of the attribute.
1098     * @return Augmentations
1099     */
1100    public Augmentations getAugmentations (String uri, String localName) {
1101        int index = getIndex(uri, localName);
1102        return index != -1 ? fAttributes[index].augs : null;
1103    }
1104
1105    /**
1106     * Look up an augmentation by XML 1.0 qualified name.
1107     * <p>
1108     *
1109     * @param qName The XML 1.0 qualified name.
1110     *
1111     * @return Augmentations
1112     *
1113     */
1114    public Augmentations getAugmentations(String qName){
1115        int index = getIndex(qName);
1116        return index != -1 ? fAttributes[index].augs : null;
1117    }
1118
1119
1120
1121    /**
1122     * Look up an augmentations by attributes index.
1123     *
1124     * @param attributeIndex The attribute index.
1125     * @return Augmentations
1126     */
1127    public Augmentations getAugmentations (int attributeIndex){
1128        if (attributeIndex < 0 || attributeIndex >= fLength) {
1129            return null;
1130        }
1131        return fAttributes[attributeIndex].augs;
1132    }
1133
1134    /**
1135     * Sets the augmentations of the attribute at the specified index.
1136     *
1137     * @param attrIndex The attribute index.
1138     * @param augs      The augmentations.
1139     */
1140    public void setAugmentations(int attrIndex, Augmentations augs) {
1141        fAttributes[attrIndex].augs = augs;
1142    }
1143
1144    /**
1145     * Sets the uri of the attribute at the specified index.
1146     *
1147     * @param attrIndex The attribute index.
1148     * @param uri       Namespace uri
1149     */
1150    public void setURI(int attrIndex, String uri) {
1151        fAttributes[attrIndex].name.uri = uri;
1152    } // getURI(int,QName)
1153
1154    // Implementation methods
1155
1156    //XMLBufferListener methods
1157    /**
1158     * This method will be invoked by XMLEntityReader before ScannedEntities buffer
1159     * is reloaded.
1160     */
1161    public void refresh() {
1162        if(fLength > 0){
1163            for(int i = 0 ; i < fLength ; i++){
1164                getValue(i);
1165            }
1166        }
1167    }
1168    public void refresh(int pos) {
1169    }
1170
1171    private void prepareAndPopulateTableViewNS(final int count) {
1172        prepareTableView();
1173        // Need to populate the hash table with the attributes we've processed so far.
1174        Attribute attr;
1175        int bucket;
1176        for (int i = 0; i < count; ++i) {
1177            attr = fAttributes[i];
1178            bucket = getTableViewBucket(attr.name.localpart, attr.name.uri);
1179            if (fAttributeTableViewChainState[bucket] != fLargeCount) {
1180                fAttributeTableViewChainState[bucket] = fLargeCount;
1181                attr.next = null;
1182                fAttributeTableView[bucket] = attr;
1183            }
1184            else {
1185                // Update table view
1186                attr.next = fAttributeTableView[bucket];
1187                fAttributeTableView[bucket] = attr;
1188            }
1189        }
1190    }
1191
1192    /**
1193     * Randomly selects a new hash function and reorganizes the table view
1194     * in order to more evenly distribute its entries. This method is called
1195     * automatically when the number of attributes in one bucket exceeds
1196     * MAX_HASH_COLLISIONS.
1197     */
1198    private void rebalanceTableView(final int count) {
1199        if (fHashMultipliers == null) {
1200            fHashMultipliers = new int[MULTIPLIERS_SIZE + 1];
1201        }
1202        PrimeNumberSequenceGenerator.generateSequence(fHashMultipliers);
1203        prepareAndPopulateTableView(count);
1204    }
1205
1206    /**
1207     * Randomly selects a new hash function and reorganizes the table view
1208     * in order to more evenly distribute its entries. This method is called
1209     * automatically when the number of attributes in one bucket exceeds
1210     * MAX_HASH_COLLISIONS.
1211     */
1212    private void rebalanceTableViewNS(final int count) {
1213        if (fHashMultipliers == null) {
1214            fHashMultipliers = new int[MULTIPLIERS_SIZE + 1];
1215        }
1216        PrimeNumberSequenceGenerator.generateSequence(fHashMultipliers);
1217        prepareAndPopulateTableViewNS(count);
1218    }
1219
1220    //
1221    // Classes
1222    //
1223
1224    /**
1225     * Attribute information.
1226     *
1227     * @author Andy Clark, IBM
1228     */
1229    static class Attribute {
1230
1231        //
1232        // Data
1233        //
1234
1235        // basic info
1236
1237        /** Name. */
1238        public final QName name = new QName();
1239
1240        /** Type. */
1241        public String type;
1242
1243        /** Value. */
1244        public String value;
1245
1246        /** This will point to the ScannedEntities buffer.*/
1247        public XMLString xmlValue;
1248
1249        /** Non-normalized value. */
1250        public String nonNormalizedValue;
1251
1252        /** Specified. */
1253        public boolean specified;
1254
1255
1256        /**
1257         * Augmentations information for this attribute.
1258         * XMLAttributes has no knowledge if any augmentations
1259         * were attached to Augmentations.
1260         */
1261        public Augmentations augs = new AugmentationsImpl();
1262
1263        // Additional data for attribute table view
1264
1265        /** Pointer to the next attribute in the chain. **/
1266        public Attribute next;
1267
1268    } // class Attribute
1269
1270} // class XMLAttributesImpl
1271