KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xerces > internal > util > XMLAttributesImpl


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package com.sun.org.apache.xerces.internal.util;
59
60 import com.sun.org.apache.xerces.internal.xni.Augmentations;
61 import com.sun.org.apache.xerces.internal.xni.QName;
62 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
63
64 /**
65  * The XMLAttributesImpl class is an implementation of the XMLAttributes
66  * interface which defines a collection of attributes for an element.
67  * In the parser, the document source would scan the entire start element
68  * and collect the attributes. The attributes are communicated to the
69  * document handler in the startElement method.
70  * <p>
71  * The attributes are read-write so that subsequent stages in the document
72  * pipeline can modify the values or change the attributes that are
73  * propogated to the next stage.
74  *
75  * @see com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler#startElement
76  *
77  * @author Andy Clark, IBM
78  * @author Elena Litani, IBM
79  * @author Michael Glavassevich, IBM
80  *
81  * @version $Id: XMLAttributesImpl.java,v 1.24 2004/01/19 22:12:10 mrglavas Exp $
82  */

83 public class XMLAttributesImpl
84     implements XMLAttributes {
85
86     //
87
// Constants
88
//
89

90     /** Default table size. */
91     protected static final int TABLE_SIZE = 101;
92     
93     /**
94      * Threshold at which an instance is treated
95      * as a large attribute list.
96      */

97     protected static final int SIZE_LIMIT = 20;
98     
99     //
100
// Data
101
//
102

103     // features
104

105     /** Namespaces. */
106     protected boolean fNamespaces = true;
107
108     // data
109

110     /**
111      * Usage count for the attribute table view.
112      * Incremented each time all attributes are removed
113      * when the attribute table view is in use.
114      */

115     protected int fLargeCount = 1;
116     
117     /** Attribute count. */
118     protected int fLength;
119
120     /** Attribute information. */
121     protected Attribute[] fAttributes = new Attribute[4];
122
123     /**
124      * Hashtable of attribute information.
125      * Provides an alternate view of the attribute specification.
126      */

127     protected Attribute[] fAttributeTableView;
128     
129     /**
130      * Tracks whether each chain in the hash table is stale
131      * with respect to the current state of this object.
132      * A chain is stale if its state is not the same as the number
133      * of times the attribute table view has been used.
134      */

135     protected int[] fAttributeTableViewChainState;
136     
137     /**
138      * Actual number of buckets in the table view.
139      */

140     protected int fTableViewBuckets;
141     
142     /**
143      * Indicates whether the table view contains consistent data.
144      */

145     protected boolean fIsTableViewConsistent;
146
147     //
148
// Constructors
149
//
150

151     /** Default constructor. */
152     public XMLAttributesImpl() {
153         this(TABLE_SIZE);
154     }
155     
156     /**
157      * @param tableSize initial size of table view
158      */

159     public XMLAttributesImpl(int tableSize) {
160         fTableViewBuckets = tableSize;
161         for (int i = 0; i < fAttributes.length; i++) {
162             fAttributes[i] = new Attribute();
163         }
164     } // <init>()
165

166     //
167
// Public methods
168
//
169

170     /**
171      * Sets whether namespace processing is being performed. This state
172      * is needed to return the correct value from the getLocalName method.
173      *
174      * @param namespaces True if namespace processing is turned on.
175      *
176      * @see #getLocalName
177      */

178     public void setNamespaces(boolean namespaces) {
179         fNamespaces = namespaces;
180     } // setNamespaces(boolean)
181

182     //
183
// XMLAttributes methods
184
//
185

186     /**
187      * Adds an attribute. The attribute's non-normalized value of the
188      * attribute will have the same value as the attribute value until
189      * set using the <code>setNonNormalizedValue</code> method. Also,
190      * the added attribute will be marked as specified in the XML instance
191      * document unless set otherwise using the <code>setSpecified</code>
192      * method.
193      * <p>
194      * <strong>Note:</strong> If an attribute of the same name already
195      * exists, the old values for the attribute are replaced by the new
196      * values.
197      *
198      * @param name The attribute name.
199      * @param type The attribute type. The type name is determined by
200      * the type specified for this attribute in the DTD.
201      * For example: "CDATA", "ID", "NMTOKEN", etc. However,
202      * attributes of type enumeration will have the type
203      * value specified as the pipe ('|') separated list of
204      * the enumeration values prefixed by an open
205      * parenthesis and suffixed by a close parenthesis.
206      * For example: "(true|false)".
207      * @param value The attribute value.
208      *
209      * @return Returns the attribute index.
210      *
211      * @see #setNonNormalizedValue
212      * @see #setSpecified
213      */

214     public int addAttribute(QName name, String JavaDoc type, String JavaDoc value) {
215
216         int index;
217         if (fLength < SIZE_LIMIT) {
218             index = name.uri != null && !name.uri.equals("")
219                 ? getIndexFast(name.uri, name.localpart)
220                 : getIndexFast(name.rawname);
221     
222             if (index == -1) {
223                 index = fLength;
224                 if (fLength++ == fAttributes.length) {
225                     Attribute[] attributes = new Attribute[fAttributes.length + 4];
226                     System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
227                     for (int i = fAttributes.length; i < attributes.length; i++) {
228                         attributes[i] = new Attribute();
229                     }
230                     fAttributes = attributes;
231                 }
232             }
233         }
234         else if (name.uri == null ||
235             name.uri.length() == 0 ||
236             (index = getIndexFast(name.uri, name.localpart)) == -1) {
237             
238             /**
239              * If attributes were removed from the list after the table
240              * becomes in use this isn't reflected in the table view. It's
241              * assumed that once a user starts removing attributes they're
242              * not likely to add more. We only make the view consistent if
243              * the user of this class adds attributes, removes them, and
244              * then adds more.
245              */

246             if (!fIsTableViewConsistent || fLength == SIZE_LIMIT) {
247                 prepareAndPopulateTableView();
248                 fIsTableViewConsistent = true;
249             }
250
251             int bucket = getTableViewBucket(name.rawname);
252         
253             // The chain is stale.
254
// This must be a unique attribute.
255
if (fAttributeTableViewChainState[bucket] != fLargeCount) {
256                 index = fLength;
257                 if (fLength++ == fAttributes.length) {
258                     Attribute[] attributes = new Attribute[fAttributes.length << 1];
259                     System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
260                     for (int i = fAttributes.length; i < attributes.length; i++) {
261                         attributes[i] = new Attribute();
262                     }
263                     fAttributes = attributes;
264                 }
265             
266                 // Update table view.
267
fAttributeTableViewChainState[bucket] = fLargeCount;
268                 fAttributes[index].next = null;
269                 fAttributeTableView[bucket] = fAttributes[index];
270             }
271             // This chain is active.
272
// We need to check if any of the attributes has the same rawname.
273
else {
274                 // Search the table.
275
Attribute found = fAttributeTableView[bucket];
276                 while (found != null) {
277                     if (found.name.rawname == name.rawname) {
278                         break;
279                     }
280                     found = found.next;
281                 }
282                 // This attribute is unique.
283
if (found == null) {
284                     index = fLength;
285                     if (fLength++ == fAttributes.length) {
286                         Attribute[] attributes = new Attribute[fAttributes.length << 1];
287                         System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
288                         for (int i = fAttributes.length; i < attributes.length; i++) {
289                             attributes[i] = new Attribute();
290                         }
291                         fAttributes = attributes;
292                     }
293                  
294                     // Update table view
295
fAttributes[index].next = fAttributeTableView[bucket];
296                     fAttributeTableView[bucket] = fAttributes[index];
297                 }
298                 // Duplicate. We still need to find the index.
299
else {
300                     index = getIndexFast(name.rawname);
301                 }
302             }
303         }
304
305         // set values
306
Attribute attribute = fAttributes[index];
307         attribute.name.setValues(name);
308         attribute.type = type;
309         attribute.value = value;
310         attribute.nonNormalizedValue = value;
311         attribute.specified = false;
312
313         // return
314
return index;
315
316     } // addAttribute(QName,String,XMLString)
317

318     /**
319      * Removes all of the attributes. This method will also remove all
320      * entities associated to the attributes.
321      */

322     public void removeAllAttributes() {
323         fLength = 0;
324     } // removeAllAttributes()
325

326     /**
327      * Removes the attribute at the specified index.
328      * <p>
329      * <strong>Note:</strong> This operation changes the indexes of all
330      * attributes following the attribute at the specified index.
331      *
332      * @param attrIndex The attribute index.
333      */

334     public void removeAttributeAt(int attrIndex) {
335         fIsTableViewConsistent = false;
336         if (attrIndex < fLength - 1) {
337             Attribute removedAttr = fAttributes[attrIndex];
338             System.arraycopy(fAttributes, attrIndex + 1,
339                              fAttributes, attrIndex, fLength - attrIndex - 1);
340             // Make the discarded Attribute object available for re-use
341
// by tucking it after the Attributes that are still in use
342
fAttributes[fLength-1] = removedAttr;
343         }
344         fLength--;
345     } // removeAttributeAt(int)
346

347     /**
348      * Sets the name of the attribute at the specified index.
349      *
350      * @param attrIndex The attribute index.
351      * @param attrName The new attribute name.
352      */

353     public void setName(int attrIndex, QName attrName) {
354         fAttributes[attrIndex].name.setValues(attrName);
355     } // setName(int,QName)
356

357     /**
358      * Sets the fields in the given QName structure with the values
359      * of the attribute name at the specified index.
360      *
361      * @param attrIndex The attribute index.
362      * @param attrName The attribute name structure to fill in.
363      */

364     public void getName(int attrIndex, QName attrName) {
365         attrName.setValues(fAttributes[attrIndex].name);
366     } // getName(int,QName)
367

368     /**
369      * Sets the type of the attribute at the specified index.
370      *
371      * @param attrIndex The attribute index.
372      * @param attrType The attribute type. The type name is determined by
373      * the type specified for this attribute in the DTD.
374      * For example: "CDATA", "ID", "NMTOKEN", etc. However,
375      * attributes of type enumeration will have the type
376      * value specified as the pipe ('|') separated list of
377      * the enumeration values prefixed by an open
378      * parenthesis and suffixed by a close parenthesis.
379      * For example: "(true|false)".
380      */

381     public void setType(int attrIndex, String JavaDoc attrType) {
382         fAttributes[attrIndex].type = attrType;
383     } // setType(int,String)
384

385     /**
386      * Sets the value of the attribute at the specified index. This
387      * method will overwrite the non-normalized value of the attribute.
388      *
389      * @param attrIndex The attribute index.
390      * @param attrValue The new attribute value.
391      *
392      * @see #setNonNormalizedValue
393      */

394     public void setValue(int attrIndex, String JavaDoc attrValue) {
395         Attribute attribute = fAttributes[attrIndex];
396         attribute.value = attrValue;
397         attribute.nonNormalizedValue = attrValue;
398     } // setValue(int,String)
399

400     /**
401      * Sets the non-normalized value of the attribute at the specified
402      * index.
403      *
404      * @param attrIndex The attribute index.
405      * @param attrValue The new non-normalized attribute value.
406      */

407     public void setNonNormalizedValue(int attrIndex, String JavaDoc attrValue) {
408         if (attrValue == null) {
409             attrValue = fAttributes[attrIndex].value;
410         }
411         fAttributes[attrIndex].nonNormalizedValue = attrValue;
412     } // setNonNormalizedValue(int,String)
413

414     /**
415      * Returns the non-normalized value of the attribute at the specified
416      * index. If no non-normalized value is set, this method will return
417      * the same value as the <code>getValue(int)</code> method.
418      *
419      * @param attrIndex The attribute index.
420      */

421     public String JavaDoc getNonNormalizedValue(int attrIndex) {
422         String JavaDoc value = fAttributes[attrIndex].nonNormalizedValue;
423         return value;
424     } // getNonNormalizedValue(int):String
425

426     /**
427      * Sets whether an attribute is specified in the instance document
428      * or not.
429      *
430      * @param attrIndex The attribute index.
431      * @param specified True if the attribute is specified in the instance
432      * document.
433      */

434     public void setSpecified(int attrIndex, boolean specified) {
435         fAttributes[attrIndex].specified = specified;
436     } // setSpecified(int,boolean)
437

438     /**
439      * Returns true if the attribute is specified in the instance document.
440      *
441      * @param attrIndex The attribute index.
442      */

443     public boolean isSpecified(int attrIndex) {
444         return fAttributes[attrIndex].specified;
445     } // isSpecified(int):boolean
446

447     //
448
// AttributeList and Attributes methods
449
//
450

451     /**
452      * Return the number of attributes in the list.
453      *
454      * <p>Once you know the number of attributes, you can iterate
455      * through the list.</p>
456      *
457      * @return The number of attributes in the list.
458      */

459     public int getLength() {
460         return fLength;
461     } // getLength():int
462

463     /**
464      * Look up an attribute's type by index.
465      *
466      * <p>The attribute type is one of the strings "CDATA", "ID",
467      * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
468      * or "NOTATION" (always in upper case).</p>
469      *
470      * <p>If the parser has not read a declaration for the attribute,
471      * or if the parser does not report attribute types, then it must
472      * return the value "CDATA" as stated in the XML 1.0 Recommentation
473      * (clause 3.3.3, "Attribute-Value Normalization").</p>
474      *
475      * <p>For an enumerated attribute that is not a notation, the
476      * parser will report the type as "NMTOKEN".</p>
477      *
478      * @param index The attribute index (zero-based).
479      * @return The attribute's type as a string, or null if the
480      * index is out of range.
481      * @see #getLength
482      */

483     public String JavaDoc getType(int index) {
484         if (index < 0 || index >= fLength) {
485             return null;
486         }
487         return getReportableType(fAttributes[index].type);
488     } // getType(int):String
489

490     /**
491      * Look up an attribute's type by XML 1.0 qualified name.
492      *
493      * <p>See {@link #getType(int) getType(int)} for a description
494      * of the possible types.</p>
495      *
496      * @param qname The XML 1.0 qualified name.
497      * @return The attribute type as a string, or null if the
498      * attribute is not in the list or if qualified names
499      * are not available.
500      */

501     public String JavaDoc getType(String JavaDoc qname) {
502         int index = getIndex(qname);
503         return index != -1 ? getReportableType(fAttributes[index].type) : null;
504     } // getType(String):String
505

506     /**
507      * Look up an attribute's value by index.
508      *
509      * <p>If the attribute value is a list of tokens (IDREFS,
510      * ENTITIES, or NMTOKENS), the tokens will be concatenated
511      * into a single string with each token separated by a
512      * single space.</p>
513      *
514      * @param index The attribute index (zero-based).
515      * @return The attribute's value as a string, or null if the
516      * index is out of range.
517      * @see #getLength
518      */

519     public String JavaDoc getValue(int index) {
520         if (index < 0 || index >= fLength) {
521             return null;
522         }
523         return fAttributes[index].value;
524     } // getValue(int):String
525

526     /**
527      * Look up an attribute's value by XML 1.0 qualified name.
528      *
529      * <p>See {@link #getValue(int) getValue(int)} for a description
530      * of the possible values.</p>
531      *
532      * @param qname The XML 1.0 qualified name.
533      * @return The attribute value as a string, or null if the
534      * attribute is not in the list or if qualified names
535      * are not available.
536      */

537     public String JavaDoc getValue(String JavaDoc qname) {
538         int index = getIndex(qname);
539         return index != -1 ? fAttributes[index].value : null;
540     } // getValue(String):String
541

542     //
543
// AttributeList methods
544
//
545

546     /**
547      * Return the name of an attribute in this list (by position).
548      *
549      * <p>The names must be unique: the SAX parser shall not include the
550      * same attribute twice. Attributes without values (those declared
551      * #IMPLIED without a value specified in the start tag) will be
552      * omitted from the list.</p>
553      *
554      * <p>If the attribute name has a namespace prefix, the prefix
555      * will still be attached.</p>
556      *
557      * @param i The index of the attribute in the list (starting at 0).
558      * @return The name of the indexed attribute, or null
559      * if the index is out of range.
560      * @see #getLength
561      */

562     public String JavaDoc getName(int index) {
563         if (index < 0 || index >= fLength) {
564             return null;
565         }
566         return fAttributes[index].name.rawname;
567     } // getName(int):String
568

569     //
570
// Attributes methods
571
//
572

573     /**
574      * Look up the index of an attribute by XML 1.0 qualified name.
575      *
576      * @param qName The qualified (prefixed) name.
577      * @return The index of the attribute, or -1 if it does not
578      * appear in the list.
579      */

580     public int getIndex(String JavaDoc qName) {
581         for (int i = 0; i < fLength; i++) {
582             Attribute attribute = fAttributes[i];
583             if (attribute.name.rawname != null &&
584                 attribute.name.rawname.equals(qName)) {
585                 return i;
586             }
587         }
588         return -1;
589     } // getIndex(String):int
590

591     /**
592      * Look up the index of an attribute by Namespace name.
593      *
594      * @param uri The Namespace URI, or null if
595      * the name has no Namespace URI.
596      * @param localName The attribute's local name.
597      * @return The index of the attribute, or -1 if it does not
598      * appear in the list.
599      */

600     public int getIndex(String JavaDoc uri, String JavaDoc localPart) {
601         for (int i = 0; i < fLength; i++) {
602             Attribute attribute = fAttributes[i];
603             if (attribute.name.localpart != null &&
604                 attribute.name.localpart.equals(localPart) &&
605                 ((uri==attribute.name.uri) ||
606                 (uri!=null && attribute.name.uri!=null && attribute.name.uri.equals(uri))))
607             {
608                 return i;
609             }
610         }
611         return -1;
612     } // getIndex(String,String):int
613

614     /**
615      * Look up an attribute's local name by index.
616      *
617      * @param index The attribute index (zero-based).
618      * @return The local name, or the empty string if Namespace
619      * processing is not being performed, or null
620      * if the index is out of range.
621      * @see #getLength
622      */

623     public String JavaDoc getLocalName(int index) {
624         if (!fNamespaces) {
625             return "";
626         }
627         if (index < 0 || index >= fLength) {
628             return null;
629         }
630         return fAttributes[index].name.localpart;
631     } // getLocalName(int):String
632

633     /**
634      * Look up an attribute's XML 1.0 qualified name by index.
635      *
636      * @param index The attribute index (zero-based).
637      * @return The XML 1.0 qualified name, or the empty string
638      * if none is available, or null if the index
639      * is out of range.
640      * @see #getLength
641      */

642     public String JavaDoc getQName(int index) {
643         if (index < 0 || index >= fLength) {
644             return null;
645         }
646         String JavaDoc rawname = fAttributes[index].name.rawname;
647         return rawname != null ? rawname : "";
648     } // getQName(int):String
649

650     /**
651      * Look up an attribute's type by Namespace name.
652      *
653      * <p>See {@link #getType(int) getType(int)} for a description
654      * of the possible types.</p>
655      *
656      * @param uri The Namespace URI, or null if the
657      * name has no Namespace URI.
658      * @param localName The local name of the attribute.
659      * @return The attribute type as a string, or null if the
660      * attribute is not in the list or if Namespace
661      * processing is not being performed.
662      */

663     public String JavaDoc getType(String JavaDoc uri, String JavaDoc localName) {
664         if (!fNamespaces) {
665             return null;
666         }
667         int index = getIndex(uri, localName);
668         return index != -1 ? getType(index) : null;
669     } // getType(String,String):String
670
/**
671      * Look up the index of an attribute by XML 1.0 qualified name.
672      * <p>
673      * <strong>Note:</strong>
674      * This method uses reference comparison, and thus should
675      * only be used internally. We cannot use this method in any
676      * code exposed to users as they may not pass in unique strings.
677      *
678      * @param qName The qualified (prefixed) name.
679      * @return The index of the attribute, or -1 if it does not
680      * appear in the list.
681      */

682     public int getIndexFast(String JavaDoc qName) {
683         for (int i = 0; i < fLength; ++i) {
684             Attribute attribute = fAttributes[i];
685             if (attribute.name.rawname == qName) {
686                 return i;
687             }
688         }
689         return -1;
690     } // getIndexFast(String):int
691

692     /**
693      * Adds an attribute. The attribute's non-normalized value of the
694      * attribute will have the same value as the attribute value until
695      * set using the <code>setNonNormalizedValue</code> method. Also,
696      * the added attribute will be marked as specified in the XML instance
697      * document unless set otherwise using the <code>setSpecified</code>
698      * method.
699      * <p>
700      * This method differs from <code>addAttribute</code> in that it
701      * does not check if an attribute of the same name already exists
702      * in the list before adding it. In order to improve performance
703      * of namespace processing, this method allows uniqueness checks
704      * to be deferred until all the namespace information is available
705      * after the entire attribute specification has been read.
706      * <p>
707      * <strong>Caution:</strong> If this method is called it should
708      * not be mixed with calls to <code>addAttribute</code> unless
709      * it has been determined that all the attribute names are unique.
710      *
711      * @param name the attribute name
712      * @param type the attribute type
713      * @param value the attribute value
714      *
715      * @see #setNonNormalizedValue
716      * @see #setSpecified
717      * @see #checkDuplicatesNS
718      */

719     public void addAttributeNS(QName name, String JavaDoc type, String JavaDoc value) {
720         int index = fLength;
721         if (fLength++ == fAttributes.length) {
722             Attribute[] attributes;
723             if (fLength < SIZE_LIMIT) {
724                 attributes = new Attribute[fAttributes.length + 4];
725             }
726             else {
727                 attributes = new Attribute[fAttributes.length << 1];
728             }
729             System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
730             for (int i = fAttributes.length; i < attributes.length; i++) {
731                 attributes[i] = new Attribute();
732             }
733             fAttributes = attributes;
734         }
735         
736         // set values
737
Attribute attribute = fAttributes[index];
738         attribute.name.setValues(name);
739         attribute.type = type;
740         attribute.value = value;
741         attribute.nonNormalizedValue = value;
742         attribute.specified = false;
743             
744         // clear augmentations
745
attribute.augs.removeAllItems();
746     }
747     
748     /**
749      * Checks for duplicate expanded names (local part and namespace name
750      * pairs) in the attribute specification. If a duplicate is found its
751      * name is returned.
752      * <p>
753      * This should be called once all the in-scope namespaces for the element
754      * enclosing these attributes is known, and after all the attributes
755      * have gone through namespace binding.
756      *
757      * @return the name of a duplicate attribute found in the search,
758      * otherwise null.
759      */

760     public QName checkDuplicatesNS() {
761         // If the list is small check for duplicates using pairwise comparison.
762
if (fLength <= SIZE_LIMIT) {
763             for (int i = 0; i < fLength - 1; ++i) {
764                 Attribute att1 = fAttributes[i];
765                 for (int j = i + 1; j < fLength; ++j) {
766                     Attribute att2 = fAttributes[j];
767                     if (att1.name.localpart == att2.name.localpart &&
768                         att1.name.uri == att2.name.uri) {
769                         return att2.name;
770                     }
771                 }
772             }
773         }
774         // If the list is large check duplicates using a hash table.
775
else {
776             // We don't want this table view to be read if someone calls
777
// addAttribute so we invalidate it up front.
778
fIsTableViewConsistent = false;
779
780             prepareTableView();
781
782             Attribute attr;
783             int bucket;
784
785             for (int i = fLength - 1; i >= 0; --i) {
786                 attr = fAttributes[i];
787                 bucket = getTableViewBucket(attr.name.localpart, attr.name.uri);
788                 
789                 // The chain is stale.
790
// This must be a unique attribute.
791
if (fAttributeTableViewChainState[bucket] != fLargeCount) {
792                     fAttributeTableViewChainState[bucket] = fLargeCount;
793                     attr.next = null;
794                     fAttributeTableView[bucket] = attr;
795                 }
796                 // This chain is active.
797
// We need to check if any of the attributes has the same name.
798
else {
799                     // Search the table.
800
Attribute found = fAttributeTableView[bucket];
801                     while (found != null) {
802                         if (found.name.localpart == attr.name.localpart &&
803                             found.name.uri == attr.name.uri) {
804                             return attr.name;
805                         }
806                         found = found.next;
807                     }
808                     
809                     // Update table view
810
attr.next = fAttributeTableView[bucket];
811                     fAttributeTableView[bucket] = attr;
812                 }
813             }
814         }
815         return null;
816     }
817     
818     /**
819      * Look up the index of an attribute by Namespace name.
820      * <p>
821      * <strong>Note:</strong>
822      * This method uses reference comparison, and thus should
823      * only be used internally. We cannot use this method in any
824      * code exposed to users as they may not pass in unique strings.
825      *
826      * @param uri The Namespace URI, or null if
827      * the name has no Namespace URI.
828      * @param localName The attribute's local name.
829      * @return The index of the attribute, or -1 if it does not
830      * appear in the list.
831      */

832     public int getIndexFast(String JavaDoc uri, String JavaDoc localPart) {
833         for (int i = 0; i < fLength; ++i) {
834             Attribute attribute = fAttributes[i];
835             if (attribute.name.localpart == localPart &&
836                 attribute.name.uri == uri) {
837                 return i;
838             }
839         }
840         return -1;
841     } // getIndexFast(String,String):int
842

843     /**
844      * Returns the value passed in or NMTOKEN if it's an enumerated type.
845      *
846      * @param type attribute type
847      * @return the value passed in or NMTOKEN if it's an enumerated type.
848      */

849     private String JavaDoc getReportableType(String JavaDoc type) {
850
851         if (type.charAt(0) == '(') {
852             return "NMTOKEN";
853         }
854         return type;
855     }
856     
857     /**
858      * Returns the position in the table view
859      * where the given attribute name would be hashed.
860      *
861      * @param qname the attribute name
862      * @return the position in the table view where the given attribute
863      * would be hashed
864      */

865     protected int getTableViewBucket(String JavaDoc qname) {
866         return (qname.hashCode() & 0x7FFFFFFF) % fTableViewBuckets;
867     }
868     
869     /**
870      * Returns the position in the table view
871      * where the given attribute name would be hashed.
872      *
873      * @param localpart the local part of the attribute
874      * @param uri the namespace name of the attribute
875      * @return the position in the table view where the given attribute
876      * would be hashed
877      */

878     protected int getTableViewBucket(String JavaDoc localpart, String JavaDoc uri) {
879         if (uri == null) {
880             return (localpart.hashCode() & 0x7FFFFFFF) % fTableViewBuckets;
881         }
882         else {
883             return ((localpart.hashCode() + uri.hashCode())
884                & 0x7FFFFFFF) % fTableViewBuckets;
885         }
886     }
887     
888     /**
889      * Purges all elements from the table view.
890      */

891     protected void cleanTableView() {
892         if (++fLargeCount < 0) {
893             // Overflow. We actually need to visit the chain state array.
894
if (fAttributeTableViewChainState != null) {
895                 for (int i = fTableViewBuckets - 1; i >= 0; --i) {
896                     fAttributeTableViewChainState[i] = 0;
897                 }
898             }
899             fLargeCount = 1;
900         }
901     }
902     
903     /**
904      * Prepares the table view of the attributes list for use.
905      */

906     protected void prepareTableView() {
907         if (fAttributeTableView == null) {
908             fAttributeTableView = new Attribute[fTableViewBuckets];
909             fAttributeTableViewChainState = new int[fTableViewBuckets];
910         }
911         else {
912             cleanTableView();
913         }
914     }
915     
916     /**
917      * Prepares the table view of the attributes list for use,
918      * and populates it with the attributes which have been
919      * previously read.
920      */

921     protected void prepareAndPopulateTableView() {
922         prepareTableView();
923         // Need to populate the hash table with the attributes we've scanned so far.
924
Attribute attr;
925         int bucket;
926         for (int i = 0; i < fLength; ++i) {
927             attr = fAttributes[i];
928             bucket = getTableViewBucket(attr.name.rawname);
929             if (fAttributeTableViewChainState[bucket] != fLargeCount) {
930                 fAttributeTableViewChainState[bucket] = fLargeCount;
931                 attr.next = null;
932                 fAttributeTableView[bucket] = attr;
933             }
934             else {
935                 // Update table view
936
attr.next = fAttributeTableView[bucket];
937                 fAttributeTableView[bucket] = attr;
938             }
939         }
940     }
941
942
943     /**
944      * Returns the prefix of the attribute at the specified index.
945      *
946      * @param index The index of the attribute.
947      */

948     public String JavaDoc getPrefix(int index) {
949         if (index < 0 || index >= fLength) {
950             return null;
951         }
952         String JavaDoc prefix = fAttributes[index].name.prefix;
953         // REVISIT: The empty string is not entered in the symbol table!
954
return prefix != null ? prefix : "";
955     } // getPrefix(int):String
956

957     /**
958      * Look up an attribute's Namespace URI by index.
959      *
960      * @param index The attribute index (zero-based).
961      * @return The Namespace URI
962      * @see #getLength
963      */

964     public String JavaDoc getURI(int index) {
965         if (index < 0 || index >= fLength) {
966             return null;
967         }
968         String JavaDoc uri = fAttributes[index].name.uri;
969         return uri;
970     } // getURI(int):String
971

972     /**
973      * Look up an attribute's value by Namespace name.
974      *
975      * <p>See {@link #getValue(int) getValue(int)} for a description
976      * of the possible values.</p>
977      *
978      * @param uri The Namespace URI, or null if the
979      * @param localName The local name of the attribute.
980      * @return The attribute value as a string, or null if the
981      * attribute is not in the list.
982      */

983     public String JavaDoc getValue(String JavaDoc uri, String JavaDoc localName) {
984         int index = getIndex(uri, localName);
985         return index != -1 ? getValue(index) : null;
986     } // getValue(String,String):String
987

988
989     /**
990      * Look up an augmentations by Namespace name.
991      *
992      * @param uri The Namespace URI, or null if the
993      * @param localName The local name of the attribute.
994      * @return Augmentations
995      */

996     public Augmentations getAugmentations (String JavaDoc uri, String JavaDoc localName) {
997         int index = getIndex(uri, localName);
998         return index != -1 ? fAttributes[index].augs : null;
999     }
1000
1001    /**
1002     * Look up an augmentation by XML 1.0 qualified name.
1003     * <p>
1004     *
1005     * @param qName The XML 1.0 qualified name.
1006     *
1007     * @return Augmentations
1008     *
1009     */

1010    public Augmentations getAugmentations(String JavaDoc qName){
1011        int index = getIndex(qName);
1012        return index != -1 ? fAttributes[index].augs : null;
1013    }
1014
1015
1016
1017    /**
1018     * Look up an augmentations by attributes index.
1019     *
1020     * @param attributeIndex The attribute index.
1021     * @return Augmentations
1022     */

1023    public Augmentations getAugmentations (int attributeIndex){
1024        if (attributeIndex < 0 || attributeIndex >= fLength) {
1025            return null;
1026        }
1027        return fAttributes[attributeIndex].augs;
1028    }
1029
1030    /**
1031     * Sets the augmentations of the attribute at the specified index.
1032     *
1033     * @param attrIndex The attribute index.
1034     * @param augs The augmentations.
1035     */

1036    public void setAugmentations(int attrIndex, Augmentations augs) {
1037        fAttributes[attrIndex].augs = augs;
1038    }
1039
1040    /**
1041     * Sets the uri of the attribute at the specified index.
1042     *
1043     * @param attrIndex The attribute index.
1044     * @param uri Namespace uri
1045     */

1046    public void setURI(int attrIndex, String JavaDoc uri) {
1047        fAttributes[attrIndex].name.uri = uri;
1048    } // getURI(int,QName)
1049

1050    //
1051
// Classes
1052
//
1053

1054    /**
1055     * Attribute information.
1056     *
1057     * @author Andy Clark, IBM
1058     */

1059    static class Attribute {
1060        
1061        //
1062
// Data
1063
//
1064

1065        // basic info
1066

1067        /** Name. */
1068        public QName name = new QName();
1069
1070        /** Type. */
1071        public String JavaDoc type;
1072
1073        /** Value. */
1074        public String JavaDoc value;
1075
1076        /** Non-normalized value. */
1077        public String JavaDoc nonNormalizedValue;
1078
1079        /** Specified. */
1080        public boolean specified;
1081        
1082        /**
1083         * Augmentations information for this attribute.
1084         * XMLAttributes has no knowledge if any augmentations
1085         * were attached to Augmentations.
1086         */

1087        public Augmentations augs = new AugmentationsImpl();
1088        
1089        // Additional data for attribute table view
1090

1091        /** Pointer to the next attribute in the chain. **/
1092        public Attribute next;
1093        
1094    } // class Attribute
1095

1096} // class XMLAttributesImpl
1097
Popular Tags