~vcs-imports/xena/trunk

« back to all changes in this revision

Viewing changes to ext/src/xerces-2_9_1/src/org/apache/xerces/util/XMLAttributesImpl.java

  • Committer: matthewoliver
  • Date: 2009-12-10 03:18:07 UTC
  • Revision ID: vcs-imports@canonical.com-20091210031807-l086qguzdlljtkl9
Merged Xena Testing into Xena Stable for the Xena 5 release.

Show diffs side-by-side

added added

removed removed

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