001 /*
002 * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 // AttributesImpl.java - default implementation of Attributes.
027 // http://www.saxproject.org
028 // Written by David Megginson
029 // NO WARRANTY! This class is in the public domain.
030 // $Id: AttributesImpl.java,v 1.2 2004/11/03 22:53:08 jsuttor Exp $
031 package org.xml.sax.helpers;
032
033 import org.xml.sax.Attributes;
034
035 /**
036 * Default implementation of the Attributes interface.
037 *
038 * <blockquote>
039 * <em>This module, both source code and documentation, is in the
040 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
041 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
042 * for further information.
043 * </blockquote>
044 *
045 * <p>This class provides a default implementation of the SAX2
046 * {@link org.xml.sax.Attributes Attributes} interface, with the
047 * addition of manipulators so that the list can be modified or
048 * reused.</p>
049 *
050 * <p>There are two typical uses of this class:</p>
051 *
052 * <ol>
053 * <li>to take a persistent snapshot of an Attributes object
054 * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li>
055 * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li>
056 * </ol>
057 *
058 * <p>This class replaces the now-deprecated SAX1 {@link
059 * org.xml.sax.helpers.AttributeListImpl AttributeListImpl}
060 * class; in addition to supporting the updated Attributes
061 * interface rather than the deprecated {@link org.xml.sax.AttributeList
062 * AttributeList} interface, it also includes a much more efficient
063 * implementation using a single array rather than a set of Vectors.</p>
064 *
065 * @since SAX 2.0
066 * @author David Megginson
067 * @version 2.0.1 (sax2r2)
068 */
069 public class AttributesImpl implements Attributes {
070
071 ////////////////////////////////////////////////////////////////////
072 // Constructors.
073 ////////////////////////////////////////////////////////////////////
074
075 /**
076 * Construct a new, empty AttributesImpl object.
077 */
078 public AttributesImpl() {
079 length = 0;
080 data = null;
081 }
082
083 /**
084 * Copy an existing Attributes object.
085 *
086 * <p>This constructor is especially useful inside a
087 * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
088 *
089 * @param atts The existing Attributes object.
090 */
091 public AttributesImpl(Attributes atts) {
092 setAttributes(atts);
093 }
094
095 ////////////////////////////////////////////////////////////////////
096 // Implementation of org.xml.sax.Attributes.
097 ////////////////////////////////////////////////////////////////////
098
099 /**
100 * Return the number of attributes in the list.
101 *
102 * @return The number of attributes in the list.
103 * @see org.xml.sax.Attributes#getLength
104 */
105 public int getLength() {
106 return length;
107 }
108
109 /**
110 * Return an attribute's Namespace URI.
111 *
112 * @param index The attribute's index (zero-based).
113 * @return The Namespace URI, the empty string if none is
114 * available, or null if the index is out of range.
115 * @see org.xml.sax.Attributes#getURI
116 */
117 public String getURI(int index) {
118 if (index >= 0 && index < length) {
119 return data[index * 5];
120 } else {
121 return null;
122 }
123 }
124
125 /**
126 * Return an attribute's local name.
127 *
128 * @param index The attribute's index (zero-based).
129 * @return The attribute's local name, the empty string if
130 * none is available, or null if the index if out of range.
131 * @see org.xml.sax.Attributes#getLocalName
132 */
133 public String getLocalName(int index) {
134 if (index >= 0 && index < length) {
135 return data[index * 5 + 1];
136 } else {
137 return null;
138 }
139 }
140
141 /**
142 * Return an attribute's qualified (prefixed) name.
143 *
144 * @param index The attribute's index (zero-based).
145 * @return The attribute's qualified name, the empty string if
146 * none is available, or null if the index is out of bounds.
147 * @see org.xml.sax.Attributes#getQName
148 */
149 public String getQName(int index) {
150 if (index >= 0 && index < length) {
151 return data[index * 5 + 2];
152 } else {
153 return null;
154 }
155 }
156
157 /**
158 * Return an attribute's type by index.
159 *
160 * @param index The attribute's index (zero-based).
161 * @return The attribute's type, "CDATA" if the type is unknown, or null
162 * if the index is out of bounds.
163 * @see org.xml.sax.Attributes#getType(int)
164 */
165 public String getType(int index) {
166 if (index >= 0 && index < length) {
167 return data[index * 5 + 3];
168 } else {
169 return null;
170 }
171 }
172
173 /**
174 * Return an attribute's value by index.
175 *
176 * @param index The attribute's index (zero-based).
177 * @return The attribute's value or null if the index is out of bounds.
178 * @see org.xml.sax.Attributes#getValue(int)
179 */
180 public String getValue(int index) {
181 if (index >= 0 && index < length) {
182 return data[index * 5 + 4];
183 } else {
184 return null;
185 }
186 }
187
188 /**
189 * Look up an attribute's index by Namespace name.
190 *
191 * <p>In many cases, it will be more efficient to look up the name once and
192 * use the index query methods rather than using the name query methods
193 * repeatedly.</p>
194 *
195 * @param uri The attribute's Namespace URI, or the empty
196 * string if none is available.
197 * @param localName The attribute's local name.
198 * @return The attribute's index, or -1 if none matches.
199 * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
200 */
201 public int getIndex(String uri, String localName) {
202 int max = length * 5;
203 for (int i = 0; i < max; i += 5) {
204 if (data[i].equals(uri) && data[i + 1].equals(localName)) {
205 return i / 5;
206 }
207 }
208 return -1;
209 }
210
211 /**
212 * Look up an attribute's index by qualified (prefixed) name.
213 *
214 * @param qName The qualified name.
215 * @return The attribute's index, or -1 if none matches.
216 * @see org.xml.sax.Attributes#getIndex(java.lang.String)
217 */
218 public int getIndex(String qName) {
219 int max = length * 5;
220 for (int i = 0; i < max; i += 5) {
221 if (data[i + 2].equals(qName)) {
222 return i / 5;
223 }
224 }
225 return -1;
226 }
227
228 /**
229 * Look up an attribute's type by Namespace-qualified name.
230 *
231 * @param uri The Namespace URI, or the empty string for a name
232 * with no explicit Namespace URI.
233 * @param localName The local name.
234 * @return The attribute's type, or null if there is no
235 * matching attribute.
236 * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String)
237 */
238 public String getType(String uri, String localName) {
239 int max = length * 5;
240 for (int i = 0; i < max; i += 5) {
241 if (data[i].equals(uri) && data[i + 1].equals(localName)) {
242 return data[i + 3];
243 }
244 }
245 return null;
246 }
247
248 /**
249 * Look up an attribute's type by qualified (prefixed) name.
250 *
251 * @param qName The qualified name.
252 * @return The attribute's type, or null if there is no
253 * matching attribute.
254 * @see org.xml.sax.Attributes#getType(java.lang.String)
255 */
256 public String getType(String qName) {
257 int max = length * 5;
258 for (int i = 0; i < max; i += 5) {
259 if (data[i + 2].equals(qName)) {
260 return data[i + 3];
261 }
262 }
263 return null;
264 }
265
266 /**
267 * Look up an attribute's value by Namespace-qualified name.
268 *
269 * @param uri The Namespace URI, or the empty string for a name
270 * with no explicit Namespace URI.
271 * @param localName The local name.
272 * @return The attribute's value, or null if there is no
273 * matching attribute.
274 * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String)
275 */
276 public String getValue(String uri, String localName) {
277 int max = length * 5;
278 for (int i = 0; i < max; i += 5) {
279 if (data[i].equals(uri) && data[i + 1].equals(localName)) {
280 return data[i + 4];
281 }
282 }
283 return null;
284 }
285
286 /**
287 * Look up an attribute's value by qualified (prefixed) name.
288 *
289 * @param qName The qualified name.
290 * @return The attribute's value, or null if there is no
291 * matching attribute.
292 * @see org.xml.sax.Attributes#getValue(java.lang.String)
293 */
294 public String getValue(String qName) {
295 int max = length * 5;
296 for (int i = 0; i < max; i += 5) {
297 if (data[i + 2].equals(qName)) {
298 return data[i + 4];
299 }
300 }
301 return null;
302 }
303
304 ////////////////////////////////////////////////////////////////////
305 // Manipulators.
306 ////////////////////////////////////////////////////////////////////
307
308 /**
309 * Clear the attribute list for reuse.
310 *
311 * <p>Note that little memory is freed by this call:
312 * the current array is kept so it can be
313 * reused.</p>
314 */
315 public void clear() {
316 if (data != null) {
317 for (int i = 0; i < (length * 5); i++)
318 data[i] = null;
319 }
320 length = 0;
321 }
322
323 /**
324 * Copy an entire Attributes object.
325 *
326 * <p>It may be more efficient to reuse an existing object
327 * rather than constantly allocating new ones.</p>
328 *
329 * @param atts The attributes to copy.
330 */
331 public void setAttributes(Attributes atts) {
332 clear();
333 length = atts.getLength();
334 if (length > 0) {
335 data = new String[length * 5];
336 for (int i = 0; i < length; i++) {
337 data[i * 5] = atts.getURI(i);
338 data[i * 5 + 1] = atts.getLocalName(i);
339 data[i * 5 + 2] = atts.getQName(i);
340 data[i * 5 + 3] = atts.getType(i);
341 data[i * 5 + 4] = atts.getValue(i);
342 }
343 }
344 }
345
346 /**
347 * Add an attribute to the end of the list.
348 *
349 * <p>For the sake of speed, this method does no checking
350 * to see if the attribute is already in the list: that is
351 * the responsibility of the application.</p>
352 *
353 * @param uri The Namespace URI, or the empty string if
354 * none is available or Namespace processing is not
355 * being performed.
356 * @param localName The local name, or the empty string if
357 * Namespace processing is not being performed.
358 * @param qName The qualified (prefixed) name, or the empty string
359 * if qualified names are not available.
360 * @param type The attribute type as a string.
361 * @param value The attribute value.
362 */
363 public void addAttribute(String uri, String localName,
364 String qName, String type, String value) {
365 ensureCapacity(length + 1);
366 data[length * 5] = uri;
367 data[length * 5 + 1] = localName;
368 data[length * 5 + 2] = qName;
369 data[length * 5 + 3] = type;
370 data[length * 5 + 4] = value;
371 length++;
372 }
373
374 /**
375 * Set an attribute in the list.
376 *
377 * <p>For the sake of speed, this method does no checking
378 * for name conflicts or well-formedness: such checks are the
379 * responsibility of the application.</p>
380 *
381 * @param index The index of the attribute (zero-based).
382 * @param uri The Namespace URI, or the empty string if
383 * none is available or Namespace processing is not
384 * being performed.
385 * @param localName The local name, or the empty string if
386 * Namespace processing is not being performed.
387 * @param qName The qualified name, or the empty string
388 * if qualified names are not available.
389 * @param type The attribute type as a string.
390 * @param value The attribute value.
391 * @exception java.lang.ArrayIndexOutOfBoundsException When the
392 * supplied index does not point to an attribute
393 * in the list.
394 */
395 public void setAttribute(int index, String uri, String localName,
396 String qName, String type, String value) {
397 if (index >= 0 && index < length) {
398 data[index * 5] = uri;
399 data[index * 5 + 1] = localName;
400 data[index * 5 + 2] = qName;
401 data[index * 5 + 3] = type;
402 data[index * 5 + 4] = value;
403 } else {
404 badIndex(index);
405 }
406 }
407
408 /**
409 * Remove an attribute from the list.
410 *
411 * @param index The index of the attribute (zero-based).
412 * @exception java.lang.ArrayIndexOutOfBoundsException When the
413 * supplied index does not point to an attribute
414 * in the list.
415 */
416 public void removeAttribute(int index) {
417 if (index >= 0 && index < length) {
418 if (index < length - 1) {
419 System.arraycopy(data, (index + 1) * 5, data,
420 index * 5, (length - index - 1) * 5);
421 }
422 index = (length - 1) * 5;
423 data[index++] = null;
424 data[index++] = null;
425 data[index++] = null;
426 data[index++] = null;
427 data[index] = null;
428 length--;
429 } else {
430 badIndex(index);
431 }
432 }
433
434 /**
435 * Set the Namespace URI of a specific attribute.
436 *
437 * @param index The index of the attribute (zero-based).
438 * @param uri The attribute's Namespace URI, or the empty
439 * string for none.
440 * @exception java.lang.ArrayIndexOutOfBoundsException When the
441 * supplied index does not point to an attribute
442 * in the list.
443 */
444 public void setURI(int index, String uri) {
445 if (index >= 0 && index < length) {
446 data[index * 5] = uri;
447 } else {
448 badIndex(index);
449 }
450 }
451
452 /**
453 * Set the local name of a specific attribute.
454 *
455 * @param index The index of the attribute (zero-based).
456 * @param localName The attribute's local name, or the empty
457 * string for none.
458 * @exception java.lang.ArrayIndexOutOfBoundsException When the
459 * supplied index does not point to an attribute
460 * in the list.
461 */
462 public void setLocalName(int index, String localName) {
463 if (index >= 0 && index < length) {
464 data[index * 5 + 1] = localName;
465 } else {
466 badIndex(index);
467 }
468 }
469
470 /**
471 * Set the qualified name of a specific attribute.
472 *
473 * @param index The index of the attribute (zero-based).
474 * @param qName The attribute's qualified name, or the empty
475 * string for none.
476 * @exception java.lang.ArrayIndexOutOfBoundsException When the
477 * supplied index does not point to an attribute
478 * in the list.
479 */
480 public void setQName(int index, String qName) {
481 if (index >= 0 && index < length) {
482 data[index * 5 + 2] = qName;
483 } else {
484 badIndex(index);
485 }
486 }
487
488 /**
489 * Set the type of a specific attribute.
490 *
491 * @param index The index of the attribute (zero-based).
492 * @param type The attribute's type.
493 * @exception java.lang.ArrayIndexOutOfBoundsException When the
494 * supplied index does not point to an attribute
495 * in the list.
496 */
497 public void setType(int index, String type) {
498 if (index >= 0 && index < length) {
499 data[index * 5 + 3] = type;
500 } else {
501 badIndex(index);
502 }
503 }
504
505 /**
506 * Set the value of a specific attribute.
507 *
508 * @param index The index of the attribute (zero-based).
509 * @param value The attribute's value.
510 * @exception java.lang.ArrayIndexOutOfBoundsException When the
511 * supplied index does not point to an attribute
512 * in the list.
513 */
514 public void setValue(int index, String value) {
515 if (index >= 0 && index < length) {
516 data[index * 5 + 4] = value;
517 } else {
518 badIndex(index);
519 }
520 }
521
522 ////////////////////////////////////////////////////////////////////
523 // Internal methods.
524 ////////////////////////////////////////////////////////////////////
525
526 /**
527 * Ensure the internal array's capacity.
528 *
529 * @param n The minimum number of attributes that the array must
530 * be able to hold.
531 */
532 private void ensureCapacity(int n) {
533 if (n <= 0) {
534 return;
535 }
536 int max;
537 if (data == null || data.length == 0) {
538 max = 25;
539 } else if (data.length >= n * 5) {
540 return;
541 } else {
542 max = data.length;
543 }
544 while (max < n * 5) {
545 max *= 2;
546 }
547
548 String newData[] = new String[max];
549 if (length > 0) {
550 System.arraycopy(data, 0, newData, 0, length * 5);
551 }
552 data = newData;
553 }
554
555 /**
556 * Report a bad array index in a manipulator.
557 *
558 * @param index The index to report.
559 * @exception java.lang.ArrayIndexOutOfBoundsException Always.
560 */
561 private void badIndex(int index)
562 throws ArrayIndexOutOfBoundsException {
563 String msg = "Attempt to modify attribute at illegal index: "
564 + index;
565 throw new ArrayIndexOutOfBoundsException(msg);
566 }
567
568 ////////////////////////////////////////////////////////////////////
569 // Internal state.
570 ////////////////////////////////////////////////////////////////////
571
572 int length;
573 String data[];
574
575 }
576
577 // end of AttributesImpl.java
|