001: /*
002: * $Id: AttributesHolder.java,v 1.4 2004/07/08 08:03:04 yuvalo Exp $
003: *
004: * 2003-06-10 - MODIFIED to throw FatalParsingException on line 51
005: * instead of SAXParsingException
006: *
007: * (C) Copyright 2002-2004 by Yuval Oren. All rights reserved.
008: *
009: * Licensed under the Apache License, Version 2.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.apache.org/licenses/LICENSE-2.0
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: */
021:
022: package com.bluecast.xml;
023:
024: import org.xml.sax.*;
025: import org.xml.sax.helpers.AttributesImpl;
026: import java.util.*;
027:
028: /** This is an extension to Megginson's AttributesImpl to make it
029: * compatible with SAX 1. It also adds a method to check for uniqueness
030: * while adding attributes.
031: *
032: * @author Yuval Oren, yuval@bluecast.com
033: * @version $Revision: 1.4 $
034: */
035: final public class AttributesHolder implements Attributes,
036: AttributeList {
037:
038: /**
039: * Add an attribute while checking if it is unique.
040: */
041: public void addAndCheckAttribute(String uri, String localName,
042: String qName, String type, String value)
043: throws SAXException {
044: // Check the QName for duplicates
045: for (int i = 0; i < length; i++) {
046: if (data[i * 5 + 2] == qName)
047: throw new FatalParsingException("duplicate attribute '"
048: + qName + "'");
049: }
050: addAttribute(uri, localName, qName, type, value);
051: }
052:
053: ////////////////////////////////////////////////////////////////////
054: // Constructors.
055: ////////////////////////////////////////////////////////////////////
056: /**
057: * Construct a new, empty AttributesHolder object.
058: */
059: public AttributesHolder() {
060: length = 0;
061: data = null;
062: }
063:
064: /**
065: * Copy an existing Attributes object.
066: *
067: * <p>This constructor is especially useful inside a
068: * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
069: *
070: * @param atts The existing Attributes object.
071: */
072: public AttributesHolder(Attributes atts) {
073: setAttributes(atts);
074: }
075:
076: ////////////////////////////////////////////////////////////////////
077: // Implementation of org.xml.sax.Attributes.
078: ////////////////////////////////////////////////////////////////////
079: /**
080: * Return the number of attributes in the list.
081: *
082: * @return The number of attributes in the list.
083: * @see org.xml.sax.Attributes#getLength
084: */
085: public int getLength() {
086: return length;
087: }
088:
089: /**
090: * Return an attribute's Namespace URI.
091: *
092: * @param index The attribute's index (zero-based).
093: * @return The Namespace URI, the empty string if none is
094: * available, or null if the index is out of range.
095: * @see org.xml.sax.Attributes#getURI
096: */
097: public String getURI(int index) {
098: if (index >= 0 && index < length) {
099: return data[index * 5];
100: } else {
101: return null;
102: }
103: }
104:
105: /**
106: * Return an attribute's local name.
107: *
108: * @param index The attribute's index (zero-based).
109: * @return The attribute's local name, the empty string if
110: * none is available, or null if the index if out of range.
111: * @see org.xml.sax.Attributes#getLocalName
112: */
113: public String getLocalName(int index) {
114: if (index >= 0 && index < length) {
115: return data[index * 5 + 1];
116: } else {
117: return null;
118: }
119: }
120:
121: /**
122: * Return an attributes XML 1.0 name. This returns the same
123: * result as getQName().
124: *
125: * @see #getQName
126: *
127: */
128: public String getName(int i) {
129: return getQName(i);
130: }
131:
132: /**
133: * Return an attribute's qualified (prefixed) name.
134: *
135: * @param index The attribute's index (zero-based).
136: * @return The attribute's qualified name, the empty string if
137: * none is available, or null if the index is out of bounds.
138: */
139: public String getQName(int index) {
140: if (index >= 0 && index < length) {
141: return data[index * 5 + 2];
142: } else {
143: return null;
144: }
145: }
146:
147: /**
148: * Return an attribute's type by index.
149: *
150: * @param index The attribute's index (zero-based).
151: * @return The attribute's type, "CDATA" if the type is unknown, or null
152: * if the index is out of bounds.
153: * @see org.xml.sax.Attributes#getType(int)
154: */
155: public String getType(int index) {
156: if (index >= 0 && index < length) {
157: return data[index * 5 + 3];
158: } else {
159: return null;
160: }
161: }
162:
163: /**
164: * Return an attribute's value by index.
165: *
166: * @param index The attribute's index (zero-based).
167: * @return The attribute's value or null if the index is out of bounds.
168: * @see org.xml.sax.Attributes#getValue(int)
169: */
170: public String getValue(int index) {
171: if (index >= 0 && index < length) {
172: return data[index * 5 + 4];
173: } else {
174: return null;
175: }
176: }
177:
178: /**
179: * Look up an attribute's index by Namespace name.
180: *
181: * <p>In many cases, it will be more efficient to look up the name once and
182: * use the index query methods rather than using the name query methods
183: * repeatedly.</p>
184: *
185: * @param uri The attribute's Namespace URI, or the empty
186: * string if none is available.
187: * @param localName The attribute's local name.
188: * @return The attribute's index, or -1 if none matches.
189: * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
190: */
191: public int getIndex(String uri, String localName) {
192: int max = length * 5;
193: for (int i = 0; i < max; i += 5) {
194: if (data[i].equals(uri) && data[i + 1].equals(localName)) {
195: return i / 5;
196: }
197: }
198: return -1;
199: }
200:
201: /**
202: * Look up an attribute's index by qualified (prefixed) name.
203: *
204: * @param qName The qualified name.
205: * @return The attribute's index, or -1 if none matches.
206: * @see org.xml.sax.Attributes#getIndex(java.lang.String)
207: */
208: public int getIndex(String qName) {
209: int max = length * 5;
210: for (int i = 0; i < max; i += 5) {
211: if (data[i + 2].equals(qName)) {
212: return i / 5;
213: }
214: }
215: return -1;
216: }
217:
218: /**
219: * Look up an attribute's type by Namespace-qualified name.
220: *
221: * @param uri The Namespace URI, or the empty string for a name
222: * with no explicit Namespace URI.
223: * @param localName The local name.
224: * @return The attribute's type, or null if there is no
225: * matching attribute.
226: * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String)
227: */
228: public String getType(String uri, String localName) {
229: int max = length * 5;
230: for (int i = 0; i < max; i += 5) {
231: if (data[i].equals(uri) && data[i + 1].equals(localName)) {
232: return data[i + 3];
233: }
234: }
235: return null;
236: }
237:
238: /**
239: * Look up an attribute's type by qualified (prefixed) name.
240: *
241: * @param qName The qualified name.
242: * @return The attribute's type, or null if there is no
243: * matching attribute.
244: * @see org.xml.sax.Attributes#getType(java.lang.String)
245: */
246: public String getType(String qName) {
247: int max = length * 5;
248: for (int i = 0; i < max; i += 5) {
249: if (data[i + 2].equals(qName)) {
250: return data[i + 3];
251: }
252: }
253: return null;
254: }
255:
256: /**
257: * Look up an attribute's value by Namespace-qualified name.
258: *
259: * @param uri The Namespace URI, or the empty string for a name
260: * with no explicit Namespace URI.
261: * @param localName The local name.
262: * @return The attribute's value, or null if there is no
263: * matching attribute.
264: * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String)
265: */
266: public String getValue(String uri, String localName) {
267: int max = length * 5;
268: for (int i = 0; i < max; i += 5) {
269: if (data[i].equals(uri) && data[i + 1].equals(localName)) {
270: return data[i + 4];
271: }
272: }
273: return null;
274: }
275:
276: /**
277: * Look up an attribute's value by qualified (prefixed) name.
278: *
279: * @param qName The qualified name.
280: * @return The attribute's value, or null if there is no
281: * matching attribute.
282: * @see org.xml.sax.Attributes#getValue(java.lang.String)
283: */
284: public String getValue(String qName) {
285: int max = length * 5;
286: for (int i = 0; i < max; i += 5) {
287: if (data[i + 2].equals(qName)) {
288: return data[i + 4];
289: }
290: }
291: return null;
292: }
293:
294: ////////////////////////////////////////////////////////////////////
295: // Manipulators.
296: ////////////////////////////////////////////////////////////////////
297: /**
298: * Clear the attribute list for reuse.
299: *
300: * <p>Note that no memory is actually freed by this call:
301: * the current arrays are kept so that they can be
302: * reused.</p>
303: */
304: public void clear() {
305: length = 0;
306: }
307:
308: /**
309: * Copy an entire Attributes object.
310: *
311: * <p>It may be more efficient to reuse an existing object
312: * rather than constantly allocating new ones.</p>
313: *
314: * @param atts The attributes to copy.
315: */
316: public void setAttributes(Attributes atts) {
317: clear();
318: length = atts.getLength();
319: if (length > 0) {
320: data = new String[length * 5];
321: for (int i = 0; i < length; i++) {
322: data[i * 5] = atts.getURI(i);
323: data[i * 5 + 1] = atts.getLocalName(i);
324: data[i * 5 + 2] = atts.getQName(i);
325: data[i * 5 + 3] = atts.getType(i);
326: data[i * 5 + 4] = atts.getValue(i);
327: }
328: }
329: }
330:
331: /**
332: * Add an attribute to the end of the list.
333: *
334: * <p>For the sake of speed, this method does no checking
335: * to see if the attribute is already in the list: that is
336: * the responsibility of the application.</p>
337: *
338: * @param uri The Namespace URI, or the empty string if
339: * none is available or Namespace processing is not
340: * being performed.
341: * @param localName The local name, or the empty string if
342: * Namespace processing is not being performed.
343: * @param qName The qualified (prefixed) name, or the empty string
344: * if qualified names are not available.
345: * @param type The attribute type as a string.
346: * @param value The attribute value.
347: */
348: public void addAttribute(String uri, String localName,
349: String qName, String type, String value) {
350: ensureCapacity(length + 1);
351: data[length * 5] = uri;
352: data[length * 5 + 1] = localName;
353: data[length * 5 + 2] = qName;
354: data[length * 5 + 3] = type;
355: data[length * 5 + 4] = value;
356: length++;
357: }
358:
359: /**
360: * Set an attribute in the list.
361: *
362: * <p>For the sake of speed, this method does no checking
363: * for name conflicts or well-formedness: such checks are the
364: * responsibility of the application.</p>
365: *
366: * @param index The index of the attribute (zero-based).
367: * @param uri The Namespace URI, or the empty string if
368: * none is available or Namespace processing is not
369: * being performed.
370: * @param localName The local name, or the empty string if
371: * Namespace processing is not being performed.
372: * @param qName The qualified name, or the empty string
373: * if qualified names are not available.
374: * @param type The attribute type as a string.
375: * @param value The attribute value.
376: * @exception java.lang.ArrayIndexOutOfBoundsException When the
377: * supplied index does not point to an attribute
378: * in the list.
379: */
380: public void setAttribute(int index, String uri, String localName,
381: String qName, String type, String value) {
382: if (index >= 0 && index < length) {
383: data[index * 5] = uri;
384: data[index * 5 + 1] = localName;
385: data[index * 5 + 2] = qName;
386: data[index * 5 + 3] = type;
387: data[index * 5 + 4] = value;
388: } else {
389: badIndex(index);
390: }
391: }
392:
393: /**
394: * Remove an attribute from the list.
395: *
396: * @param index The index of the attribute (zero-based).
397: * @exception java.lang.ArrayIndexOutOfBoundsException When the
398: * supplied index does not point to an attribute
399: * in the list.
400: */
401: public void removeAttribute(int index) {
402: if (index >= 0 && index < length) {
403: data[index * 5] = null;
404: data[index * 5 + 1] = null;
405: data[index * 5 + 2] = null;
406: data[index * 5 + 3] = null;
407: data[index * 5 + 4] = null;
408: if (index < length - 1) {
409: System.arraycopy(data, (index + 1) * 5, data,
410: index * 5, (length - index - 1) * 5);
411: }
412: length--;
413: } else {
414: badIndex(index);
415: }
416: }
417:
418: /**
419: * Set the Namespace URI of a specific attribute.
420: *
421: * @param index The index of the attribute (zero-based).
422: * @param uri The attribute's Namespace URI, or the empty
423: * string for none.
424: * @exception java.lang.ArrayIndexOutOfBoundsException When the
425: * supplied index does not point to an attribute
426: * in the list.
427: */
428: public void setURI(int index, String uri) {
429: if (index >= 0 && index < length) {
430: data[index * 5] = uri;
431: } else {
432: badIndex(index);
433: }
434: }
435:
436: /**
437: * Set the local name of a specific attribute.
438: *
439: * @param index The index of the attribute (zero-based).
440: * @param localName The attribute's local name, or the empty
441: * string for none.
442: * @exception java.lang.ArrayIndexOutOfBoundsException When the
443: * supplied index does not point to an attribute
444: * in the list.
445: */
446: public void setLocalName(int index, String localName) {
447: if (index >= 0 && index < length) {
448: data[index * 5 + 1] = localName;
449: } else {
450: badIndex(index);
451: }
452: }
453:
454: /**
455: * Set the qualified name of a specific attribute.
456: *
457: * @param index The index of the attribute (zero-based).
458: * @param qName The attribute's qualified name, or the empty
459: * string for none.
460: * @exception java.lang.ArrayIndexOutOfBoundsException When the
461: * supplied index does not point to an attribute
462: * in the list.
463: */
464: public void setQName(int index, String qName) {
465: if (index >= 0 && index < length) {
466: data[index * 5 + 2] = qName;
467: } else {
468: badIndex(index);
469: }
470: }
471:
472: /**
473: * Set the type of a specific attribute.
474: *
475: * @param index The index of the attribute (zero-based).
476: * @param type The attribute's type.
477: * @exception java.lang.ArrayIndexOutOfBoundsException When the
478: * supplied index does not point to an attribute
479: * in the list.
480: */
481: public void setType(int index, String type) {
482: if (index >= 0 && index < length) {
483: data[index * 5 + 3] = type;
484: } else {
485: badIndex(index);
486: }
487: }
488:
489: /**
490: * Set the value of a specific attribute.
491: *
492: * @param index The index of the attribute (zero-based).
493: * @param value The attribute's value.
494: * @exception java.lang.ArrayIndexOutOfBoundsException When the
495: * supplied index does not point to an attribute
496: * in the list.
497: */
498: public void setValue(int index, String value) {
499: if (index >= 0 && index < length) {
500: data[index * 5 + 4] = value;
501: } else {
502: badIndex(index);
503: }
504: }
505:
506: ////////////////////////////////////////////////////////////////////
507: // Internal methods.
508: ////////////////////////////////////////////////////////////////////
509: /**
510: * Ensure the internal array's capacity.
511: *
512: * @param n The minimum number of attributes that the array must
513: * be able to hold.
514: */
515: private void ensureCapacity(int n) {
516: if (n <= 0) {
517: return;
518: }
519: int max;
520: if (data == null || data.length == 0) {
521: max = 25;
522: } else if (data.length >= n * 5) {
523: return;
524: } else {
525: max = data.length;
526: }
527: while (max < n * 5) {
528: max *= 2;
529: }
530: String newData[] = new String[max];
531: if (length > 0) {
532: System.arraycopy(data, 0, newData, 0, length * 5);
533: }
534: data = newData;
535: }
536:
537: /**
538: * Report a bad array index in a manipulator.
539: *
540: * @param index The index to report.
541: * @exception java.lang.ArrayIndexOutOfBoundsException Always.
542: */
543: private void badIndex(int index)
544: throws ArrayIndexOutOfBoundsException {
545: String msg = "Attempt to modify attribute at illegal index: "
546: + index;
547: throw new ArrayIndexOutOfBoundsException(msg);
548: }
549:
550: ////////////////////////////////////////////////////////////////////
551: // Internal state.
552: ////////////////////////////////////////////////////////////////////
553: protected int length;
554: protected String data[];
555: }
|