• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // XMLWriter.java - serialize an XML document.
2 // Written by David Megginson, david@megginson.com
3 // and placed by him into the public domain.
4 // Extensively modified by John Cowan for TagSoup.
5 // TagSoup is licensed under the Apache License,
6 // Version 2.0.  You may obtain a copy of this license at
7 // http://www.apache.org/licenses/LICENSE-2.0 .  You may also have
8 // additional legal rights not granted by this license.
9 //
10 // TagSoup is distributed in the hope that it will be useful, but
11 // unless required by applicable law or agreed to in writing, TagSoup
12 // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
13 // OF ANY KIND, either express or implied; not even the implied warranty
14 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 package org.ccil.cowan.tagsoup;
17 import org.xml.sax.Attributes;
18 
19 
20 /**
21  * Default implementation of the Attributes interface.
22  *
23  * <blockquote>
24  * <em>This module, both source code and documentation, is in the
25  * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
26  * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
27  * for further information.
28  * </blockquote>
29  *
30  * <p>This class provides a default implementation of the SAX2
31  * {@link org.xml.sax.Attributes Attributes} interface, with the
32  * addition of manipulators so that the list can be modified or
33  * reused.</p>
34  *
35  * <p>There are two typical uses of this class:</p>
36  *
37  * <ol>
38  * <li>to take a persistent snapshot of an Attributes object
39  *  in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li>
40  * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li>
41  * </ol>
42  *
43  * <p>This class replaces the now-deprecated SAX1 {@link
44  * org.xml.sax.helpers.AttributeListImpl AttributeListImpl}
45  * class; in addition to supporting the updated Attributes
46  * interface rather than the deprecated {@link org.xml.sax.AttributeList
47  * AttributeList} interface, it also includes a much more efficient
48  * implementation using a single array rather than a set of Vectors.</p>
49  *
50  * @since SAX 2.0
51  * @author David Megginson
52  * @version 2.0.1 (sax2r2)
53  */
54 public class AttributesImpl implements Attributes
55 {
56 
57 
58     ////////////////////////////////////////////////////////////////////
59     // Constructors.
60     ////////////////////////////////////////////////////////////////////
61 
62 
63     /**
64      * Construct a new, empty AttributesImpl object.
65      */
AttributesImpl()66     public AttributesImpl ()
67     {
68 	length = 0;
69 	data = null;
70     }
71 
72 
73     /**
74      * Copy an existing Attributes object.
75      *
76      * <p>This constructor is especially useful inside a
77      * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
78      *
79      * @param atts The existing Attributes object.
80      */
AttributesImpl(Attributes atts)81     public AttributesImpl (Attributes atts)
82     {
83 	setAttributes(atts);
84     }
85 
86 
87 
88     ////////////////////////////////////////////////////////////////////
89     // Implementation of org.xml.sax.Attributes.
90     ////////////////////////////////////////////////////////////////////
91 
92 
93     /**
94      * Return the number of attributes in the list.
95      *
96      * @return The number of attributes in the list.
97      * @see org.xml.sax.Attributes#getLength
98      */
getLength()99     public int getLength ()
100     {
101 	return length;
102     }
103 
104 
105     /**
106      * Return an attribute's Namespace URI.
107      *
108      * @param index The attribute's index (zero-based).
109      * @return The Namespace URI, the empty string if none is
110      *         available, or null if the index is out of range.
111      * @see org.xml.sax.Attributes#getURI
112      */
getURI(int index)113     public String getURI (int index)
114     {
115 	if (index >= 0 && index < length) {
116 	    return data[index*5];
117 	} else {
118 	    return null;
119 	}
120     }
121 
122 
123     /**
124      * Return an attribute's local name.
125      *
126      * @param index The attribute's index (zero-based).
127      * @return The attribute's local name, the empty string if
128      *         none is available, or null if the index if out of range.
129      * @see org.xml.sax.Attributes#getLocalName
130      */
getLocalName(int index)131     public String getLocalName (int index)
132     {
133 	if (index >= 0 && index < length) {
134 	    return data[index*5+1];
135 	} else {
136 	    return null;
137 	}
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      */
getQName(int index)149     public String getQName (int index)
150     {
151 	if (index >= 0 && index < length) {
152 	    return data[index*5+2];
153 	} else {
154 	    return null;
155 	}
156     }
157 
158 
159     /**
160      * Return an attribute's type by index.
161      *
162      * @param index The attribute's index (zero-based).
163      * @return The attribute's type, "CDATA" if the type is unknown, or null
164      *         if the index is out of bounds.
165      * @see org.xml.sax.Attributes#getType(int)
166      */
getType(int index)167     public String getType (int index)
168     {
169 	if (index >= 0 && index < length) {
170 	    return data[index*5+3];
171 	} else {
172 	    return null;
173 	}
174     }
175 
176 
177     /**
178      * Return an attribute's value by index.
179      *
180      * @param index The attribute's index (zero-based).
181      * @return The attribute's value or null if the index is out of bounds.
182      * @see org.xml.sax.Attributes#getValue(int)
183      */
getValue(int index)184     public String getValue (int index)
185     {
186 	if (index >= 0 && index < length) {
187 	    return data[index*5+4];
188 	} else {
189 	    return null;
190 	}
191     }
192 
193 
194     /**
195      * Look up an attribute's index by Namespace name.
196      *
197      * <p>In many cases, it will be more efficient to look up the name once and
198      * use the index query methods rather than using the name query methods
199      * repeatedly.</p>
200      *
201      * @param uri The attribute's Namespace URI, or the empty
202      *        string if none is available.
203      * @param localName The attribute's local name.
204      * @return The attribute's index, or -1 if none matches.
205      * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
206      */
getIndex(String uri, String localName)207     public int getIndex (String uri, String localName)
208     {
209 	int max = length * 5;
210 	for (int i = 0; i < max; i += 5) {
211 	    if (data[i].equals(uri) && data[i+1].equals(localName)) {
212 		return i / 5;
213 	    }
214 	}
215 	return -1;
216     }
217 
218 
219     /**
220      * Look up an attribute's index by qualified (prefixed) name.
221      *
222      * @param qName The qualified name.
223      * @return The attribute's index, or -1 if none matches.
224      * @see org.xml.sax.Attributes#getIndex(java.lang.String)
225      */
getIndex(String qName)226     public int getIndex (String qName)
227     {
228 	int max = length * 5;
229 	for (int i = 0; i < max; i += 5) {
230 	    if (data[i+2].equals(qName)) {
231 		return i / 5;
232 	    }
233 	}
234 	return -1;
235     }
236 
237 
238     /**
239      * Look up an attribute's type by Namespace-qualified name.
240      *
241      * @param uri The Namespace URI, or the empty string for a name
242      *        with no explicit Namespace URI.
243      * @param localName The local name.
244      * @return The attribute's type, or null if there is no
245      *         matching attribute.
246      * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String)
247      */
getType(String uri, String localName)248     public String getType (String uri, String localName)
249     {
250 	int max = length * 5;
251 	for (int i = 0; i < max; i += 5) {
252 	    if (data[i].equals(uri) && data[i+1].equals(localName)) {
253 		return data[i+3];
254 	    }
255 	}
256 	return null;
257     }
258 
259 
260     /**
261      * Look up an attribute's type by qualified (prefixed) name.
262      *
263      * @param qName The qualified name.
264      * @return The attribute's type, or null if there is no
265      *         matching attribute.
266      * @see org.xml.sax.Attributes#getType(java.lang.String)
267      */
getType(String qName)268     public String getType (String qName)
269     {
270 	int max = length * 5;
271 	for (int i = 0; i < max; i += 5) {
272 	    if (data[i+2].equals(qName)) {
273 		return data[i+3];
274 	    }
275 	}
276 	return null;
277     }
278 
279 
280     /**
281      * Look up an attribute's value by Namespace-qualified name.
282      *
283      * @param uri The Namespace URI, or the empty string for a name
284      *        with no explicit Namespace URI.
285      * @param localName The local name.
286      * @return The attribute's value, or null if there is no
287      *         matching attribute.
288      * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String)
289      */
getValue(String uri, String localName)290     public String getValue (String uri, String localName)
291     {
292 	int max = length * 5;
293 	for (int i = 0; i < max; i += 5) {
294 	    if (data[i].equals(uri) && data[i+1].equals(localName)) {
295 		return data[i+4];
296 	    }
297 	}
298 	return null;
299     }
300 
301 
302     /**
303      * Look up an attribute's value by qualified (prefixed) name.
304      *
305      * @param qName The qualified name.
306      * @return The attribute's value, or null if there is no
307      *         matching attribute.
308      * @see org.xml.sax.Attributes#getValue(java.lang.String)
309      */
getValue(String qName)310     public String getValue (String qName)
311     {
312 	int max = length * 5;
313 	for (int i = 0; i < max; i += 5) {
314 	    if (data[i+2].equals(qName)) {
315 		return data[i+4];
316 	    }
317 	}
318 	return null;
319     }
320 
321 
322 
323     ////////////////////////////////////////////////////////////////////
324     // Manipulators.
325     ////////////////////////////////////////////////////////////////////
326 
327 
328     /**
329      * Clear the attribute list for reuse.
330      *
331      * <p>Note that little memory is freed by this call:
332      * the current array is kept so it can be
333      * reused.</p>
334      */
clear()335     public void clear ()
336     {
337 	if (data != null) {
338 	    for (int i = 0; i < (length * 5); i++)
339 		data [i] = null;
340 	}
341 	length = 0;
342     }
343 
344 
345     /**
346      * Copy an entire Attributes object.
347      *
348      * <p>It may be more efficient to reuse an existing object
349      * rather than constantly allocating new ones.</p>
350      *
351      * @param atts The attributes to copy.
352      */
setAttributes(Attributes atts)353     public void setAttributes (Attributes atts)
354     {
355         clear();
356         length = atts.getLength();
357         if (length > 0) {
358             data = new String[length*5];
359             for (int i = 0; i < length; i++) {
360                 data[i*5] = atts.getURI(i);
361                 data[i*5+1] = atts.getLocalName(i);
362                 data[i*5+2] = atts.getQName(i);
363                 data[i*5+3] = atts.getType(i);
364                 data[i*5+4] = atts.getValue(i);
365             }
366 	}
367     }
368 
369 
370     /**
371      * Add an attribute to the end of the list.
372      *
373      * <p>For the sake of speed, this method does no checking
374      * to see if the attribute is already in the list: that is
375      * the responsibility of the application.</p>
376      *
377      * @param uri The Namespace URI, or the empty string if
378      *        none is available or Namespace processing is not
379      *        being performed.
380      * @param localName The local name, or the empty string if
381      *        Namespace processing is not being performed.
382      * @param qName The qualified (prefixed) name, or the empty string
383      *        if qualified names are not available.
384      * @param type The attribute type as a string.
385      * @param value The attribute value.
386      */
addAttribute(String uri, String localName, String qName, String type, String value)387     public void addAttribute (String uri, String localName, String qName,
388 			      String type, String value)
389     {
390 	ensureCapacity(length+1);
391 	data[length*5] = uri;
392 	data[length*5+1] = localName;
393 	data[length*5+2] = qName;
394 	data[length*5+3] = type;
395 	data[length*5+4] = value;
396 	length++;
397     }
398 
399 
400     /**
401      * Set an attribute in the list.
402      *
403      * <p>For the sake of speed, this method does no checking
404      * for name conflicts or well-formedness: such checks are the
405      * responsibility of the application.</p>
406      *
407      * @param index The index of the attribute (zero-based).
408      * @param uri The Namespace URI, or the empty string if
409      *        none is available or Namespace processing is not
410      *        being performed.
411      * @param localName The local name, or the empty string if
412      *        Namespace processing is not being performed.
413      * @param qName The qualified name, or the empty string
414      *        if qualified names are not available.
415      * @param type The attribute type as a string.
416      * @param value The attribute value.
417      * @exception java.lang.ArrayIndexOutOfBoundsException When the
418      *            supplied index does not point to an attribute
419      *            in the list.
420      */
setAttribute(int index, String uri, String localName, String qName, String type, String value)421     public void setAttribute (int index, String uri, String localName,
422 			      String qName, String type, String value)
423     {
424 	if (index >= 0 && index < length) {
425 	    data[index*5] = uri;
426 	    data[index*5+1] = localName;
427 	    data[index*5+2] = qName;
428 	    data[index*5+3] = type;
429 	    data[index*5+4] = value;
430 	} else {
431 	    badIndex(index);
432 	}
433     }
434 
435 
436     /**
437      * Remove an attribute from the list.
438      *
439      * @param index The index of the attribute (zero-based).
440      * @exception java.lang.ArrayIndexOutOfBoundsException When the
441      *            supplied index does not point to an attribute
442      *            in the list.
443      */
removeAttribute(int index)444     public void removeAttribute (int index)
445     {
446 	if (index >= 0 && index < length) {
447 	    if (index < length - 1) {
448 		System.arraycopy(data, (index+1)*5, data, index*5,
449 				 (length-index-1)*5);
450 	    }
451 	    index = (length - 1) * 5;
452 	    data [index++] = null;
453 	    data [index++] = null;
454 	    data [index++] = null;
455 	    data [index++] = null;
456 	    data [index] = null;
457 	    length--;
458 	} else {
459 	    badIndex(index);
460 	}
461     }
462 
463 
464     /**
465      * Set the Namespace URI of a specific attribute.
466      *
467      * @param index The index of the attribute (zero-based).
468      * @param uri The attribute's Namespace URI, or the empty
469      *        string for none.
470      * @exception java.lang.ArrayIndexOutOfBoundsException When the
471      *            supplied index does not point to an attribute
472      *            in the list.
473      */
setURI(int index, String uri)474     public void setURI (int index, String uri)
475     {
476 	if (index >= 0 && index < length) {
477 	    data[index*5] = uri;
478 	} else {
479 	    badIndex(index);
480 	}
481     }
482 
483 
484     /**
485      * Set the local name of a specific attribute.
486      *
487      * @param index The index of the attribute (zero-based).
488      * @param localName The attribute's local name, or the empty
489      *        string for none.
490      * @exception java.lang.ArrayIndexOutOfBoundsException When the
491      *            supplied index does not point to an attribute
492      *            in the list.
493      */
setLocalName(int index, String localName)494     public void setLocalName (int index, String localName)
495     {
496 	if (index >= 0 && index < length) {
497 	    data[index*5+1] = localName;
498 	} else {
499 	    badIndex(index);
500 	}
501     }
502 
503 
504     /**
505      * Set the qualified name of a specific attribute.
506      *
507      * @param index The index of the attribute (zero-based).
508      * @param qName The attribute's qualified name, or the empty
509      *        string for none.
510      * @exception java.lang.ArrayIndexOutOfBoundsException When the
511      *            supplied index does not point to an attribute
512      *            in the list.
513      */
setQName(int index, String qName)514     public void setQName (int index, String qName)
515     {
516 	if (index >= 0 && index < length) {
517 	    data[index*5+2] = qName;
518 	} else {
519 	    badIndex(index);
520 	}
521     }
522 
523 
524     /**
525      * Set the type of a specific attribute.
526      *
527      * @param index The index of the attribute (zero-based).
528      * @param type The attribute's type.
529      * @exception java.lang.ArrayIndexOutOfBoundsException When the
530      *            supplied index does not point to an attribute
531      *            in the list.
532      */
setType(int index, String type)533     public void setType (int index, String type)
534     {
535 	if (index >= 0 && index < length) {
536 	    data[index*5+3] = type;
537 	} else {
538 	    badIndex(index);
539 	}
540     }
541 
542 
543     /**
544      * Set the value of a specific attribute.
545      *
546      * @param index The index of the attribute (zero-based).
547      * @param value The attribute's value.
548      * @exception java.lang.ArrayIndexOutOfBoundsException When the
549      *            supplied index does not point to an attribute
550      *            in the list.
551      */
setValue(int index, String value)552     public void setValue (int index, String value)
553     {
554 	if (index >= 0 && index < length) {
555 	    data[index*5+4] = value;
556 	} else {
557 	    badIndex(index);
558 	}
559     }
560 
561 
562 
563     ////////////////////////////////////////////////////////////////////
564     // Internal methods.
565     ////////////////////////////////////////////////////////////////////
566 
567 
568     /**
569      * Ensure the internal array's capacity.
570      *
571      * @param n The minimum number of attributes that the array must
572      *        be able to hold.
573      */
ensureCapacity(int n)574     private void ensureCapacity (int n)    {
575         if (n <= 0) {
576             return;
577         }
578         int max;
579         if (data == null || data.length == 0) {
580             max = 25;
581         }
582         else if (data.length >= n * 5) {
583             return;
584         }
585         else {
586             max = data.length;
587         }
588         while (max < n * 5) {
589             max *= 2;
590         }
591 
592         String newData[] = new String[max];
593         if (length > 0) {
594             System.arraycopy(data, 0, newData, 0, length*5);
595         }
596         data = newData;
597     }
598 
599 
600     /**
601      * Report a bad array index in a manipulator.
602      *
603      * @param index The index to report.
604      * @exception java.lang.ArrayIndexOutOfBoundsException Always.
605      */
badIndex(int index)606     private void badIndex (int index)
607 	throws ArrayIndexOutOfBoundsException
608     {
609 	String msg =
610 	    "Attempt to modify attribute at illegal index: " + index;
611 	throw new ArrayIndexOutOfBoundsException(msg);
612     }
613 
614 
615 
616     ////////////////////////////////////////////////////////////////////
617     // Internal state.
618     ////////////////////////////////////////////////////////////////////
619 
620     int length;
621     String data [];
622 
623 }
624 
625 // end of AttributesImpl.java
626 
627