001: /**
002: * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
003: * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
004: */package com.sun.xml.stream.xerces.util;
005:
006: import com.sun.xml.stream.xerces.xni.Augmentations;
007: import com.sun.xml.stream.xerces.xni.QName;
008: import com.sun.xml.stream.xerces.xni.XMLAttributes;
009: import java.util.*;
010:
011: /**
012: * K.venugopal@sun.com
013: */
014:
015: public class STAXAttributesImpl implements XMLAttributes {
016:
017: int attr_index = 0;
018: int MAGIC_NUMBER = 2;
019: /** Namespaces. */
020: protected boolean fNamespaces = true;
021: protected ArrayList attrList = null;
022: protected ArrayList dupList = null;
023: private boolean init = false;
024: //protected TreeMap attrMap = null;
025: protected HashMap attrMap = null;
026:
027: //protected SymbolHash attrMap = null;
028: /** Default constructor. */
029: public STAXAttributesImpl() {
030: attrList = new ArrayList(2);
031: //attrMap = new TreeMap();
032: for (int i = 0; i < 2; i++) {
033: Attribute attr = new Attribute();
034: attr.name = new QName();
035: attrList.add(i, attr);
036: }
037: //attrMap = new SymbolHash();
038: }
039:
040: /**
041: * @param tableSize initial size of table view
042: */
043: public STAXAttributesImpl(int tableSize) {
044: attrList = new ArrayList(tableSize);
045: //attrMap = new TreeMap();
046: attrMap = new HashMap();
047: //attrMap = new SymbolHash();
048: }
049:
050: /**
051: * Sets whether namespace processing is being performed. This state
052: * is needed to return the correct value from the getLocalName method.
053: *
054: * @param namespaces True if namespace processing is turned on.
055: *
056: * @see #getLocalName
057: */
058:
059: public void setNamespaces(boolean namespaces) {
060: fNamespaces = namespaces;
061: }
062:
063: /**
064: * Adds an attribute. The attribute's non-normalized value of the
065: * attribute will have the same value as the attribute value until
066: * set using the <code>setNonNormalizedValue</code> method. Also,
067: * the added attribute will be marked as specified in the XML instance
068: * document unless set otherwise using the <code>setSpecified</code>
069: * method.
070: * <p>
071: * <strong>Note:</strong> If an attribute of the same name already
072: * exists, the old values for the attribute are replaced by the new
073: * values.
074: *
075: * @param name The attribute name.
076: * @param type The attribute type. The type name is determined by
077: * the type specified for this attribute in the DTD.
078: * For example: "CDATA", "ID", "NMTOKEN", etc. However,
079: * attributes of type enumeration will have the type
080: * value specified as the pipe ('|') separated list of
081: * the enumeration values prefixed by an open
082: * parenthesis and suffixed by a close parenthesis.
083: * For example: "(true|false)".
084: * @param value The attribute value.
085: *
086: * @return Returns the attribute index.
087: *
088: * @see #setNonNormalizedValue
089: * @see #setSpecified
090: */
091: public int addAttribute(QName name, String type, String value) {
092: Attribute attr = null;
093: if (attr_index >= attrList.size()) {
094: attr = new Attribute();
095: attr.name = new QName();
096: attrList.add(attr);
097: attr.next = null;
098: } else {
099: attr = (Attribute) attrList.get(attr_index);
100: attr.next = null;
101: }
102:
103: attr.name.setValues(name);
104: attr.type = type;
105: attr.value = value;
106:
107: if (attr_index < 5) {
108: Attribute tmp = null;
109: for (int i = 0; i < attr_index; i++) {
110: tmp = (Attribute) attrList.get(i);
111: if (tmp.name.rawname == name.rawname) {
112: tmp.value = value;
113: return i;
114: }
115: }
116: } else {
117: Attribute tmp = null;
118: if (!init) {
119: if (attrMap == null)
120: attrMap = new HashMap(2, 2);
121: for (int i = 0; i < attr_index; i++) {
122: tmp = (Attribute) attrList.get(i);
123: attrMap.put(tmp.name.rawname, tmp);
124: }
125: init = true;
126: }
127: /*Attribute obj = (Attribute)attrMap.get(name.rawname);
128: if( obj != null ){
129: obj.value = value;
130: return getLength();
131: */
132: if (attrMap.containsKey(name.rawname)) {
133: return getLength();
134: } else {
135: attrMap.put(name.rawname, attr);
136: }
137: }
138: attr_index++;
139: return getLength() - 1;
140: }
141:
142: /**
143: * Removes all of the attributes. This method will also remove all
144: * entities associated to the attributes.
145: */
146: public void removeAllAttributes() {
147: attr_index = 0;
148: if (attrMap != null)
149: attrMap.clear();
150: if (dupList != null)
151: dupList.clear();
152: init = false;
153: }
154:
155: /**
156: * Removes the attribute at the specified index.
157: * <p>
158: * <strong>Note:</strong> This operation changes the indexes of all
159: * attributes following the attribute at the specified index.
160: *
161: * @param attrIndex The attribute index.
162: */
163: public void removeAttributeAt(int attrIndex) {
164: Attribute attr = (Attribute) attrList.remove(attrIndex);
165: }
166:
167: /**
168: * Sets the name of the attribute at the specified index.
169: *
170: * @param attrIndex The attribute index.
171: * @param attrName The new attribute name.
172: */
173: public void setName(int attrIndex, QName attrName) {
174: ((Attribute) attrList.get(attrIndex)).name.setValues(attrName);
175: }
176:
177: /**
178: * Sets the fields in the given QName structure with the values
179: * of the attribute name at the specified index.
180: *
181: * @param attrIndex The attribute index.
182: * @param attrName The attribute name structure to fill in.
183: */
184: public void getName(int attrIndex, QName attrName) {
185: attrName.setValues(((Attribute) attrList.get(attrIndex)).name);
186: }
187:
188: /**
189: * Sets the type of the attribute at the specified index.
190: *
191: * @param attrIndex The attribute index.
192: * @param attrType The attribute type. The type name is determined by
193: * the type specified for this attribute in the DTD.
194: * For example: "CDATA", "ID", "NMTOKEN", etc. However,
195: * attributes of type enumeration will have the type
196: * value specified as the pipe ('|') separated list of
197: * the enumeration values prefixed by an open
198: * parenthesis and suffixed by a close parenthesis.
199: * For example: "(true|false)".
200: */
201: public void setType(int attrIndex, String attrType) {
202: ((Attribute) attrList.get(attrIndex)).type = attrType;
203: }
204:
205: /**
206: * Sets the value of the attribute at the specified index. This
207: * method will overwrite the non-normalized value of the attribute.
208: *
209: * @param attrIndex The attribute index.
210: * @param attrValue The new attribute value.
211: *
212: * @see #setNonNormalizedValue
213: */
214: public void setValue(int attrIndex, String attrValue) {
215: if (attrIndex > attr_index)
216: return;
217: Attribute attribute = (Attribute) attrList.get(attrIndex);
218: attribute.value = attrValue;
219: attribute.nonNormalizedValue = attrValue;
220: }
221:
222: /**
223: * Sets the non-normalized value of the attribute at the specified
224: * index.
225: *
226: * @param attrIndex The attribute index.
227: * @param attrValue The new non-normalized attribute value.
228: */
229: public void setNonNormalizedValue(int attrIndex, String attrValue) {
230: Attribute attribute = (Attribute) attrList.get(attrIndex);
231: attribute.nonNormalizedValue = attrValue;
232: }
233:
234: /**
235: * Returns the non-normalized value of the attribute at the specified
236: * index. If no non-normalized value is set, this method will return
237: * the same value as the <code>getValue(int)</code> method.
238: *
239: * @param attrIndex The attribute index.
240: */
241: public String getNonNormalizedValue(int attrIndex) {
242: Attribute attribute = (Attribute) attrList.get(attrIndex);
243: return attribute.nonNormalizedValue;
244: }
245:
246: /**
247: * Sets whether an attribute is specified in the instance document
248: * or not.
249: *
250: * @param attrIndex The attribute index.
251: * @param specified True if the attribute is specified in the instance
252: * document.
253: */
254: public void setSpecified(int attrIndex, boolean specified) {
255: ((Attribute) attrList.get(attrIndex)).specified = specified;
256: }
257:
258: /**
259: * Returns true if the attribute is specified in the instance document.
260: *
261: * @param attrIndex The attribute index.
262: */
263: public boolean isSpecified(int attrIndex) {
264: return ((Attribute) attrList.get(attrIndex)).specified;
265: }
266:
267: /**
268: * Return the number of attributes in the list.
269: *
270: * <p>Once you know the number of attributes, you can iterate
271: * through the list.</p>
272: *
273: * @return The number of attributes in the list.
274: */
275: public int getLength() {
276: return attr_index;
277: }
278:
279: /**
280: * Look up an attribute's type by index.
281: *
282: * <p>The attribute type is one of the strings "CDATA", "ID",
283: * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
284: * or "NOTATION" (always in upper case).</p>
285: *
286: * <p>If the parser has not read a declaration for the attribute,
287: * or if the parser does not report attribute types, then it must
288: * return the value "CDATA" as stated in the XML 1.0 Recommentation
289: * (clause 3.3.3, "Attribute-Value Normalization").</p>
290: *
291: * <p>For an enumerated attribute that is not a notation, the
292: * parser will report the type as "NMTOKEN".</p>
293: *
294: * @param index The attribute index (zero-based).
295: * @return The attribute's type as a string, or null if the
296: * index is out of range.
297: * @see #getLength
298: */
299: public String getType(int index) {
300: if (index < 0 || index >= attrList.size()) {
301: return null;
302: }
303: return getReportableType(((Attribute) attrList.get(index)).type);
304: }
305:
306: /**
307: * Look up an attribute's type by XML 1.0 qualified name.
308: *
309: * <p>See {@link #getType(int) getType(int)} for a description
310: * of the possible types.</p>
311: *
312: * @param qname The XML 1.0 qualified name.
313: * @return The attribute type as a string, or null if the
314: * attribute is not in the list or if qualified names
315: * are not available.
316: */
317: public String getType(String qname) {
318: return null;
319: }
320:
321: /**
322: * Look up an attribute's value by index.
323: *
324: * <p>If the attribute value is a list of tokens (IDREFS,
325: * ENTITIES, or NMTOKENS), the tokens will be concatenated
326: * into a single string with each token separated by a
327: * single space.</p>
328: *
329: * @param index The attribute index (zero-based).
330: * @return The attribute's value as a string, or null if the
331: * index is out of range.
332: * @see #getLength
333: */
334: public String getValue(int index) {
335: return ((Attribute) attrList.get(index)).value;
336: }
337:
338: /**
339: * Look up an attribute's value by XML 1.0 qualified name.
340: *
341: * <p>See {@link #getValue(int) getValue(int)} for a description
342: * of the possible values.</p>
343: *
344: * @param qname The XML 1.0 qualified name.
345: * @return The attribute value as a string, or null if the
346: * attribute is not in the list or if qualified names
347: * are not available.
348: */
349: public String getValue(String qname) {
350: return null;
351: }
352:
353: /**
354: * Return the name of an attribute in this list (by position).
355: *
356: * <p>The names must be unique: the SAX parser shall not include the
357: * same attribute twice. Attributes without values (those declared
358: * #IMPLIED without a value specified in the start tag) will be
359: * omitted from the list.</p>
360: *
361: * <p>If the attribute name has a namespace prefix, the prefix
362: * will still be attached.</p>
363: *
364: * @param i The index of the attribute in the list (starting at 0).
365: * @return The name of the indexed attribute, or null
366: * if the index is out of range.
367: * @see #getLength
368: */
369: public String getName(int index) {
370: if (index < 0 || index >= attrList.size()) {
371: return null;
372: }
373: return ((Attribute) attrList.get(index)).name.rawname;
374: }
375:
376: /**
377: * Look up the index of an attribute by XML 1.0 qualified name.
378: *
379: * @param qName The qualified (prefixed) name.
380: * @return The index of the attribute, or -1 if it does not
381: * appear in the list.
382: */
383: public int getIndex(String qName) {
384: for (int i = 0; i < attr_index; i++) {
385: Attribute attribute = (Attribute) attrList.get(i);
386: if (attribute.name.rawname != null
387: && attribute.name.rawname.equals(qName)) {
388: return i;
389: }
390: }
391: return -1;
392: }
393:
394: /**
395: * Look up the index of an attribute by Namespace name.
396: *
397: * @param uri The Namespace URI, or null if
398: * the name has no Namespace URI.
399: * @param localName The attribute's local name.
400: * @return The index of the attribute, or -1 if it does not
401: * appear in the list.
402: */
403: public int getIndex(String uri, String localPart) {
404: for (int i = 0; i < attr_index; i++) {
405: Attribute attribute = (Attribute) attrList.get(i);
406: if (attribute.name.localpart != null
407: && attribute.name.localpart.equals(localPart)
408: && ((uri == attribute.name.uri) || (uri != null
409: && attribute.name.uri != null && attribute.name.uri
410: .equals(uri)))) {
411: return i;
412: }
413: }
414: return -1;
415: }
416:
417: /**
418: * Look up an attribute's local name by index.
419: *
420: * @param index The attribute index (zero-based).
421: * @return The local name, or the empty string if Namespace
422: * processing is not being performed, or null
423: * if the index is out of range.
424: * @see #getLength
425: */
426: public String getLocalName(int index) {
427: if (!fNamespaces) {
428: return "";
429: }
430: return ((Attribute) attrList.get(index)).name.localpart;
431: }
432:
433: /**
434: * Look up an attribute's XML 1.0 qualified name by index.
435: *
436: * @param index The attribute index (zero-based).
437: * @return The XML 1.0 qualified name, or the empty string
438: * if none is available, or null if the index
439: * is out of range.
440: * @see #getLength
441: */
442: public String getQName(int index) {
443: return ((Attribute) attrList.get(index)).name.rawname;
444: }
445:
446: public QName getQualifiedName(int index) {
447: return ((Attribute) attrList.get(index)).name;
448: }
449:
450: /**
451: * Look up an attribute's type by Namespace name.
452: *
453: * <p>See {@link #getType(int) getType(int)} for a description
454: * of the possible types.</p>
455: *
456: * @param uri The Namespace URI, or null if the
457: * name has no Namespace URI.
458: * @param localName The local name of the attribute.
459: * @return The attribute type as a string, or null if the
460: * attribute is not in the list or if Namespace
461: * processing is not being performed.
462: */
463: public String getType(String uri, String localName) {
464: if (!fNamespaces) {
465: return null;
466: }
467: int index = getIndex(uri, localName);
468: return index != -1 ? getReportableType(((Attribute) attrList
469: .get(index)).type) : null;
470: }
471:
472: /**
473: * Returns the prefix of the attribute at the specified index.
474: *
475: * @param index The index of the attribute.
476: */
477: public String getPrefix(int index) {
478: return ((Attribute) attrList.get(index)).name.prefix;
479: }
480:
481: /**
482: * Look up an attribute's Namespace URI by index.
483: *
484: * @param index The attribute index (zero-based).
485: * @return The Namespace URI
486: * @see #getLength
487: */
488: public String getURI(int index) {
489: return ((Attribute) attrList.get(index)).name.uri;
490: }
491:
492: /**
493: * Look up an attribute's value by Namespace name.
494: *
495: * <p>See {@link #getValue(int) getValue(int)} for a description
496: * of the possible values.</p>
497: *
498: * @param uri The Namespace URI, or null if the
499: * @param localName The local name of the attribute.
500: * @return The attribute value as a string, or null if the
501: * attribute is not in the list.
502: */
503: public String getValue(String uri, String localName) {
504: int index = getIndex(uri, localName);
505: return index != -1 ? getValue(index) : null;
506: }
507:
508: /**
509: * Look up an augmentations by Namespace name.
510: *
511: * @param uri The Namespace URI, or null if the
512: * @param localName The local name of the attribute.
513: * @return Augmentations
514: */
515: public Augmentations getAugmentations(String uri, String localName) {
516: return null;
517: }
518:
519: /**
520: * Look up an augmentation by XML 1.0 qualified name.
521: * <p>
522: *
523: * @param qName The XML 1.0 qualified name.
524: *
525: * @return Augmentations
526: *
527: */
528: public Augmentations getAugmentations(String qName) {
529: return null;
530: }
531:
532: /**
533: * Look up an augmentations by attributes index.
534: *
535: * @param attributeIndex The attribute index.
536: * @return Augmentations
537: */
538: public Augmentations getAugmentations(int attributeIndex) {
539: return null;
540: }
541:
542: /**
543: * Sets the augmentations of the attribute at the specified index.
544: *
545: * @param attrIndex The attribute index.
546: * @param augs The augmentations.
547: */
548: public void setAugmentations(int attrIndex, Augmentations augs) {
549: }
550:
551: /**
552: * Sets the uri of the attribute at the specified index.
553: *
554: * @param attrIndex The attribute index.
555: * @param uri Namespace uri
556: */
557: public void setURI(int attrIndex, String uri) {
558: Attribute attribute = (Attribute) attrList.get(attrIndex);
559: attribute.name.uri = uri;
560: }
561:
562: public void setSchemaId(int attrIndex, boolean schemaId) {
563: //noop
564: }
565:
566: public boolean getSchemaId(int index) {
567: return false;
568: }
569:
570: public boolean getSchemaId(String qname) {
571: return false;
572: }
573:
574: public boolean getSchemaId(String uri, String localName) {
575: return false;
576: }
577:
578: public void addAttributeNS(QName name, String type, String value) {
579: Attribute attr = null;
580: if (attr_index >= attrList.size()) {
581: attr = new Attribute();
582: attr.name = new QName();
583: attrList.add(attr);
584: attr.next = null;
585: } else {
586: attr = (Attribute) attrList.get(attr_index);
587: attr.next = null;
588: }
589: attr.name.setValues(name);
590: attr.type = type;
591: attr.value = value;
592: /* System.out.println("Add local"+name.localpart);
593: System.out.println("Add uri"+name.uri);
594: System.out.println("Add rawname"+name.rawname);
595: */
596: if (attr_index > MAGIC_NUMBER) {
597: if (!init) {
598: if (attrMap == null)
599: attrMap = new HashMap(2, 2);
600: for (int i = 0; i < attr_index; i++) {
601: Attribute tmp = (Attribute) attrList.get(i);
602: attrMap.put(tmp.name.localpart, tmp);
603: }
604: init = true;
605: }
606: if (attrMap.containsKey(name.localpart)) {
607: Attribute obj = (Attribute) attrMap.get(name.localpart);
608: attr.next = obj.next;
609: obj.next = attr;
610: attr_index++;
611: if (!obj.dup) {
612: if (dupList == null)
613: dupList = new ArrayList();
614: dupList.add(attr);
615: obj.dup = true;
616: }
617: } else {
618: attrMap.put(name.localpart, attr);
619: attr_index++;
620: }
621: } else
622: attr_index++;
623: }
624:
625: /**
626: * Checks for duplicate expanded names (local part and namespace name
627: * pairs) in the attribute specification. If a duplicate is found its
628: * name is returned.
629: * <p>
630: * This should be called once all the in-scope namespaces for the element
631: * enclosing these attributes is known, and after all the attributes
632: * have gone through namespace binding.
633: *
634: * @return the name of a duplicate attribute found in the search,
635: * otherwise null.
636: */
637: public QName checkDuplicatesNS() {
638: if (attr_index <= MAGIC_NUMBER) {
639: for (int i = 0; i < attr_index - 1; ++i) {
640: Attribute att1 = (Attribute) attrList.get(i);
641: for (int j = i + 1; j < attr_index; ++j) {
642: Attribute att2 = (Attribute) attrList.get(j);
643: if (att1.name.localpart == att2.name.localpart
644: && att1.name.uri == att2.name.uri) {
645: return att2.name;
646: }
647: }
648: }
649: } else {
650: if (dupList == null)
651: return null;
652: for (int i = 0; i < dupList.size(); ++i) {
653: Attribute att1 = (Attribute) dupList.get(i);
654: Attribute att2 = att1.next;
655: while (att2 != null) {
656: if (att1.name.localpart == att2.name.localpart
657: && att1.name.uri == att2.name.uri) {
658: return att2.name;
659: }
660: att2 = att1.next;
661: }
662: }
663: }
664: return null;
665: }
666:
667: protected String getReportableType(String type) {
668: if (type.indexOf('(') == 0
669: && type.lastIndexOf(')') == type.length() - 1) {
670: return "NMTOKEN";
671: }
672: return type;
673: }
674:
675: protected Attribute getDuplicate(Attribute attr1, QName qname) {
676: Attribute att1 = attr1;
677: if (att1.name.prefix == qname.prefix && attr1.next == null)
678: return att1;
679: while (att1 != null) {
680: if (att1.name.rawname == qname.rawname)
681: return att1;
682: att1 = att1.next;
683: }
684: return null;
685: }
686:
687: class Attribute {
688:
689: /** Name. */
690: public QName name = new QName();
691:
692: /** Type. */
693: public String type;
694:
695: /** Value. */
696: public String value;
697:
698: /** Non-normalized value. */
699: public String nonNormalizedValue;
700:
701: /** Specified. */
702: public boolean specified;
703:
704: /** Schema ID type. */
705: public boolean schemaId;
706: public boolean dup = false;
707: Attribute next;
708: }
709:
710: }
|