0001: /*
0002: * The Apache Software License, Version 1.1
0003: *
0004: *
0005: * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
0006: * reserved.
0007: *
0008: * Redistribution and use in source and binary forms, with or without
0009: * modification, are permitted provided that the following conditions
0010: * are met:
0011: *
0012: * 1. Redistributions of source code must retain the above copyright
0013: * notice, this list of conditions and the following disclaimer.
0014: *
0015: * 2. Redistributions in binary form must reproduce the above copyright
0016: * notice, this list of conditions and the following disclaimer in
0017: * the documentation and/or other materials provided with the
0018: * distribution.
0019: *
0020: * 3. The end-user documentation included with the redistribution,
0021: * if any, must include the following acknowledgment:
0022: * "This product includes software developed by the
0023: * Apache Software Foundation (http://www.apache.org/)."
0024: * Alternately, this acknowledgment may appear in the software itself,
0025: * if and wherever such third-party acknowledgments normally appear.
0026: *
0027: * 4. The names "Xerces" and "Apache Software Foundation" must
0028: * not be used to endorse or promote products derived from this
0029: * software without prior written permission. For written
0030: * permission, please contact apache@apache.org.
0031: *
0032: * 5. Products derived from this software may not be called "Apache",
0033: * nor may "Apache" appear in their name, without prior written
0034: * permission of the Apache Software Foundation.
0035: *
0036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0047: * SUCH DAMAGE.
0048: * ====================================================================
0049: *
0050: * This software consists of voluntary contributions made by many
0051: * individuals on behalf of the Apache Software Foundation and was
0052: * originally based on software copyright (c) 1999, International
0053: * Business Machines, Inc., http://www.apache.org. For more
0054: * information on the Apache Software Foundation, please see
0055: * <http://www.apache.org/>.
0056: */
0057:
0058: package com.sun.xml.stream.xerces.util;
0059:
0060: import com.sun.xml.stream.XMLBufferListener;
0061: import com.sun.xml.stream.xerces.xni.Augmentations;
0062: import com.sun.xml.stream.xerces.xni.QName;
0063: import com.sun.xml.stream.xerces.xni.XMLAttributes;
0064: import com.sun.xml.stream.xerces.xni.XMLString;
0065:
0066: /**
0067: * The XMLAttributesImpl class is an implementation of the XMLAttributes
0068: * interface which defines a collection of attributes for an element.
0069: * In the parser, the document source would scan the entire start element
0070: * and collect the attributes. The attributes are communicated to the
0071: * document handler in the startElement method.
0072: * <p>
0073: * The attributes are read-write so that subsequent stages in the document
0074: * pipeline can modify the values or change the attributes that are
0075: * propogated to the next stage.
0076: *
0077: * @see com.sun.xml.stream.xerces.xni.XMLDocumentHandler#startElement
0078: *
0079: * @author Andy Clark, IBM
0080: * @author Elena Litani, IBM
0081: * @author Michael Glavassevich, IBM
0082: *
0083: * @version $Id: XMLAttributesImpl.java,v 1.2 2006/04/01 06:01:40 jeffsuttor Exp $
0084: */
0085: public class XMLAttributesImpl implements XMLAttributes,
0086: XMLBufferListener {
0087:
0088: //
0089: // Constants
0090: //
0091:
0092: /** Default table size. */
0093: protected static final int TABLE_SIZE = 101;
0094:
0095: /**
0096: * Threshold at which an instance is treated
0097: * as a large attribute list.
0098: */
0099: protected static final int SIZE_LIMIT = 20;
0100:
0101: //
0102: // Data
0103: //
0104:
0105: // features
0106:
0107: /** Namespaces. */
0108: protected boolean fNamespaces = true;
0109:
0110: // data
0111:
0112: /**
0113: * Usage count for the attribute table view.
0114: * Incremented each time all attributes are removed
0115: * when the attribute table view is in use.
0116: */
0117: protected int fLargeCount = 1;
0118:
0119: /** Attribute count. */
0120: protected int fLength;
0121:
0122: /** Attribute information. */
0123: protected Attribute[] fAttributes = new Attribute[4];
0124:
0125: /**
0126: * Hashtable of attribute information.
0127: * Provides an alternate view of the attribute specification.
0128: */
0129: protected Attribute[] fAttributeTableView;
0130:
0131: /**
0132: * Tracks whether each chain in the hash table is stale
0133: * with respect to the current state of this object.
0134: * A chain is stale if its state is not the same as the number
0135: * of times the attribute table view has been used.
0136: */
0137: protected int[] fAttributeTableViewChainState;
0138:
0139: /**
0140: * Actual number of buckets in the table view.
0141: */
0142: protected int fTableViewBuckets;
0143:
0144: /**
0145: * Indicates whether the table view contains consistent data.
0146: */
0147: protected boolean fIsTableViewConsistent;
0148:
0149: //
0150: // Constructors
0151: //
0152:
0153: /** Default constructor. */
0154: public XMLAttributesImpl() {
0155: this (TABLE_SIZE);
0156: }
0157:
0158: /**
0159: * @param tableSize initial size of table view
0160: */
0161: public XMLAttributesImpl(int tableSize) {
0162: fTableViewBuckets = tableSize;
0163: for (int i = 0; i < fAttributes.length; i++) {
0164: fAttributes[i] = new Attribute();
0165: }
0166: } // <init>()
0167:
0168: //
0169: // Public methods
0170: //
0171:
0172: /**
0173: * Sets whether namespace processing is being performed. This state
0174: * is needed to return the correct value from the getLocalName method.
0175: *
0176: * @param namespaces True if namespace processing is turned on.
0177: *
0178: * @see #getLocalName
0179: */
0180: public void setNamespaces(boolean namespaces) {
0181: fNamespaces = namespaces;
0182: } // setNamespaces(boolean)
0183:
0184: //
0185: // XMLAttributes methods
0186: //
0187:
0188: /**
0189: * Adds an attribute. The attribute's non-normalized value of the
0190: * attribute will have the same value as the attribute value until
0191: * set using the <code>setNonNormalizedValue</code> method. Also,
0192: * the added attribute will be marked as specified in the XML instance
0193: * document unless set otherwise using the <code>setSpecified</code>
0194: * method.
0195: * <p>
0196: * <strong>Note:</strong> If an attribute of the same name already
0197: * exists, the old values for the attribute are replaced by the new
0198: * values.
0199: *
0200: * @param name The attribute name.
0201: * @param type The attribute type. The type name is determined by
0202: * the type specified for this attribute in the DTD.
0203: * For example: "CDATA", "ID", "NMTOKEN", etc. However,
0204: * attributes of type enumeration will have the type
0205: * value specified as the pipe ('|') separated list of
0206: * the enumeration values prefixed by an open
0207: * parenthesis and suffixed by a close parenthesis.
0208: * For example: "(true|false)".
0209: * @param value The attribute value.
0210: *
0211: * @return Returns the attribute index.
0212: *
0213: * @see #setNonNormalizedValue
0214: * @see #setSpecified
0215: */
0216: public int addAttribute(QName name, String type, String value) {
0217: return addAttribute(name, type, value, null);
0218: }
0219:
0220: public int addAttribute(QName name, String type, String value,
0221: XMLString valueCache) {
0222:
0223: int index;
0224: if (fLength < SIZE_LIMIT) {
0225: index = name.uri != null && !name.uri.equals("") ? getIndexFast(
0226: name.uri, name.localpart)
0227: : getIndexFast(name.rawname);
0228:
0229: if (index == -1) {
0230: index = fLength;
0231: if (fLength++ == fAttributes.length) {
0232: Attribute[] attributes = new Attribute[fAttributes.length + 4];
0233: System.arraycopy(fAttributes, 0, attributes, 0,
0234: fAttributes.length);
0235: for (int i = fAttributes.length; i < attributes.length; i++) {
0236: attributes[i] = new Attribute();
0237: }
0238: fAttributes = attributes;
0239: }
0240: }
0241: } else if (name.uri == null
0242: || name.uri.length() == 0
0243: || (index = getIndexFast(name.uri, name.localpart)) == -1) {
0244:
0245: /**
0246: * If attributes were removed from the list after the table
0247: * becomes in use this isn't reflected in the table view. It's
0248: * assumed that once a user starts removing attributes they're
0249: * not likely to add more. We only make the view consistent if
0250: * the user of this class adds attributes, removes them, and
0251: * then adds more.
0252: */
0253: if (!fIsTableViewConsistent || fLength == SIZE_LIMIT) {
0254: prepareAndPopulateTableView();
0255: fIsTableViewConsistent = true;
0256: }
0257:
0258: int bucket = getTableViewBucket(name.rawname);
0259:
0260: // The chain is stale.
0261: // This must be a unique attribute.
0262: if (fAttributeTableViewChainState[bucket] != fLargeCount) {
0263: index = fLength;
0264: if (fLength++ == fAttributes.length) {
0265: Attribute[] attributes = new Attribute[fAttributes.length << 1];
0266: System.arraycopy(fAttributes, 0, attributes, 0,
0267: fAttributes.length);
0268: for (int i = fAttributes.length; i < attributes.length; i++) {
0269: attributes[i] = new Attribute();
0270: }
0271: fAttributes = attributes;
0272: }
0273:
0274: // Update table view.
0275: fAttributeTableViewChainState[bucket] = fLargeCount;
0276: fAttributes[index].next = null;
0277: fAttributeTableView[bucket] = fAttributes[index];
0278: }
0279: // This chain is active.
0280: // We need to check if any of the attributes has the same rawname.
0281: else {
0282: // Search the table.
0283: Attribute found = fAttributeTableView[bucket];
0284: while (found != null) {
0285: if (found.name.rawname == name.rawname) {
0286: break;
0287: }
0288: found = found.next;
0289: }
0290: // This attribute is unique.
0291: if (found == null) {
0292: index = fLength;
0293: if (fLength++ == fAttributes.length) {
0294: Attribute[] attributes = new Attribute[fAttributes.length << 1];
0295: System.arraycopy(fAttributes, 0, attributes, 0,
0296: fAttributes.length);
0297: for (int i = fAttributes.length; i < attributes.length; i++) {
0298: attributes[i] = new Attribute();
0299: }
0300: fAttributes = attributes;
0301: }
0302:
0303: // Update table view
0304: fAttributes[index].next = fAttributeTableView[bucket];
0305: fAttributeTableView[bucket] = fAttributes[index];
0306: }
0307: // Duplicate. We still need to find the index.
0308: else {
0309: index = getIndexFast(name.rawname);
0310: }
0311: }
0312: }
0313:
0314: // set values
0315: Attribute attribute = fAttributes[index];
0316: attribute.name.setValues(name);
0317: attribute.type = type;
0318: attribute.value = value;
0319: attribute.xmlValue = valueCache;
0320: attribute.nonNormalizedValue = value;
0321: attribute.specified = false;
0322:
0323: // clear augmentations
0324: if (attribute.augs != null)
0325: attribute.augs.removeAllItems();
0326:
0327: return index;
0328:
0329: } // addAttribute(QName,String,XMLString)
0330:
0331: /**
0332: * Removes all of the attributes. This method will also remove all
0333: * entities associated to the attributes.
0334: */
0335: public void removeAllAttributes() {
0336: fLength = 0;
0337: } // removeAllAttributes()
0338:
0339: /**
0340: * Removes the attribute at the specified index.
0341: * <p>
0342: * <strong>Note:</strong> This operation changes the indexes of all
0343: * attributes following the attribute at the specified index.
0344: *
0345: * @param attrIndex The attribute index.
0346: */
0347: public void removeAttributeAt(int attrIndex) {
0348: fIsTableViewConsistent = false;
0349: if (attrIndex < fLength - 1) {
0350: Attribute removedAttr = fAttributes[attrIndex];
0351: System.arraycopy(fAttributes, attrIndex + 1, fAttributes,
0352: attrIndex, fLength - attrIndex - 1);
0353: // Make the discarded Attribute object available for re-use
0354: // by tucking it after the Attributes that are still in use
0355: fAttributes[fLength - 1] = removedAttr;
0356: }
0357: fLength--;
0358: } // removeAttributeAt(int)
0359:
0360: /**
0361: * Sets the name of the attribute at the specified index.
0362: *
0363: * @param attrIndex The attribute index.
0364: * @param attrName The new attribute name.
0365: */
0366: public void setName(int attrIndex, QName attrName) {
0367: fAttributes[attrIndex].name.setValues(attrName);
0368: } // setName(int,QName)
0369:
0370: /**
0371: * Sets the fields in the given QName structure with the values
0372: * of the attribute name at the specified index.
0373: *
0374: * @param attrIndex The attribute index.
0375: * @param attrName The attribute name structure to fill in.
0376: */
0377: public void getName(int attrIndex, QName attrName) {
0378: attrName.setValues(fAttributes[attrIndex].name);
0379: } // getName(int,QName)
0380:
0381: /**
0382: * Sets the type of the attribute at the specified index.
0383: *
0384: * @param attrIndex The attribute index.
0385: * @param attrType The attribute type. The type name is determined by
0386: * the type specified for this attribute in the DTD.
0387: * For example: "CDATA", "ID", "NMTOKEN", etc. However,
0388: * attributes of type enumeration will have the type
0389: * value specified as the pipe ('|') separated list of
0390: * the enumeration values prefixed by an open
0391: * parenthesis and suffixed by a close parenthesis.
0392: * For example: "(true|false)".
0393: */
0394: public void setType(int attrIndex, String attrType) {
0395: fAttributes[attrIndex].type = attrType;
0396: } // setType(int,String)
0397:
0398: /**
0399: * Sets the value of the attribute at the specified index. This
0400: * method will overwrite the non-normalized value of the attribute.
0401: *
0402: * @param attrIndex The attribute index.
0403: * @param attrValue The new attribute value.
0404: *
0405: * @see #setNonNormalizedValue
0406: */
0407: public void setValue(int attrIndex, String attrValue) {
0408: setValue(attrIndex, attrValue, null);
0409: }
0410:
0411: public void setValue(int attrIndex, String attrValue,
0412: XMLString value) {
0413: Attribute attribute = fAttributes[attrIndex];
0414: attribute.value = attrValue;
0415: attribute.nonNormalizedValue = attrValue;
0416: attribute.xmlValue = value;
0417: } // setValue(int,String)
0418:
0419: /**
0420: * Sets the non-normalized value of the attribute at the specified
0421: * index.
0422: *
0423: * @param attrIndex The attribute index.
0424: * @param attrValue The new non-normalized attribute value.
0425: */
0426: public void setNonNormalizedValue(int attrIndex, String attrValue) {
0427: if (attrValue == null) {
0428: attrValue = fAttributes[attrIndex].value;
0429: }
0430: fAttributes[attrIndex].nonNormalizedValue = attrValue;
0431: } // setNonNormalizedValue(int,String)
0432:
0433: /**
0434: * Returns the non-normalized value of the attribute at the specified
0435: * index. If no non-normalized value is set, this method will return
0436: * the same value as the <code>getValue(int)</code> method.
0437: *
0438: * @param attrIndex The attribute index.
0439: */
0440: public String getNonNormalizedValue(int attrIndex) {
0441: String value = fAttributes[attrIndex].nonNormalizedValue;
0442: return value;
0443: } // getNonNormalizedValue(int):String
0444:
0445: /**
0446: * Sets whether an attribute is specified in the instance document
0447: * or not.
0448: *
0449: * @param attrIndex The attribute index.
0450: * @param specified True if the attribute is specified in the instance
0451: * document.
0452: */
0453: public void setSpecified(int attrIndex, boolean specified) {
0454: fAttributes[attrIndex].specified = specified;
0455: } // setSpecified(int,boolean)
0456:
0457: /**
0458: * Returns true if the attribute is specified in the instance document.
0459: *
0460: * @param attrIndex The attribute index.
0461: */
0462: public boolean isSpecified(int attrIndex) {
0463: return fAttributes[attrIndex].specified;
0464: } // isSpecified(int):boolean
0465:
0466: //
0467: // AttributeList and Attributes methods
0468: //
0469:
0470: /**
0471: * Return the number of attributes in the list.
0472: *
0473: * <p>Once you know the number of attributes, you can iterate
0474: * through the list.</p>
0475: *
0476: * @return The number of attributes in the list.
0477: */
0478: public int getLength() {
0479: return fLength;
0480: } // getLength():int
0481:
0482: /**
0483: * Look up an attribute's type by index.
0484: *
0485: * <p>The attribute type is one of the strings "CDATA", "ID",
0486: * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
0487: * or "NOTATION" (always in upper case).</p>
0488: *
0489: * <p>If the parser has not read a declaration for the attribute,
0490: * or if the parser does not report attribute types, then it must
0491: * return the value "CDATA" as stated in the XML 1.0 Recommentation
0492: * (clause 3.3.3, "Attribute-Value Normalization").</p>
0493: *
0494: * <p>For an enumerated attribute that is not a notation, the
0495: * parser will report the type as "NMTOKEN".</p>
0496: *
0497: * @param index The attribute index (zero-based).
0498: * @return The attribute's type as a string, or null if the
0499: * index is out of range.
0500: * @see #getLength
0501: */
0502: public String getType(int index) {
0503: if (index < 0 || index >= fLength) {
0504: return null;
0505: }
0506: return getReportableType(fAttributes[index].type);
0507: } // getType(int):String
0508:
0509: /**
0510: * Look up an attribute's type by XML 1.0 qualified name.
0511: *
0512: * <p>See {@link #getType(int) getType(int)} for a description
0513: * of the possible types.</p>
0514: *
0515: * @param qname The XML 1.0 qualified name.
0516: * @return The attribute type as a string, or null if the
0517: * attribute is not in the list or if qualified names
0518: * are not available.
0519: */
0520: public String getType(String qname) {
0521: int index = getIndex(qname);
0522: return index != -1 ? getReportableType(fAttributes[index].type)
0523: : null;
0524: } // getType(String):String
0525:
0526: /**
0527: * Look up an attribute's value by index.
0528: *
0529: * <p>If the attribute value is a list of tokens (IDREFS,
0530: * ENTITIES, or NMTOKENS), the tokens will be concatenated
0531: * into a single string with each token separated by a
0532: * single space.</p>
0533: *
0534: * @param index The attribute index (zero-based).
0535: * @return The attribute's value as a string, or null if the
0536: * index is out of range.
0537: * @see #getLength
0538: */
0539: public String getValue(int index) {
0540: if (index < 0 || index >= fLength) {
0541: return null;
0542: }
0543: if (fAttributes[index].value == null
0544: && fAttributes[index].xmlValue != null)
0545: fAttributes[index].value = fAttributes[index].xmlValue
0546: .toString();
0547: return fAttributes[index].value;
0548: } // getValue(int):String
0549:
0550: /**
0551: * Look up an attribute's value by XML 1.0 qualified name.
0552: *
0553: * <p>See {@link #getValue(int) getValue(int)} for a description
0554: * of the possible values.</p>
0555: *
0556: * @param qname The XML 1.0 qualified name.
0557: * @return The attribute value as a string, or null if the
0558: * attribute is not in the list or if qualified names
0559: * are not available.
0560: */
0561: public String getValue(String qname) {
0562: int index = getIndex(qname);
0563: if (index == -1)
0564: return null;
0565: if (fAttributes[index].value == null)
0566: fAttributes[index].value = fAttributes[index].xmlValue
0567: .toString();
0568: return fAttributes[index].value;
0569: } // getValue(String):String
0570:
0571: //
0572: // AttributeList methods
0573: //
0574:
0575: /**
0576: * Return the name of an attribute in this list (by position).
0577: *
0578: * <p>The names must be unique: the SAX parser shall not include the
0579: * same attribute twice. Attributes without values (those declared
0580: * #IMPLIED without a value specified in the start tag) will be
0581: * omitted from the list.</p>
0582: *
0583: * <p>If the attribute name has a namespace prefix, the prefix
0584: * will still be attached.</p>
0585: *
0586: * @param i The index of the attribute in the list (starting at 0).
0587: * @return The name of the indexed attribute, or null
0588: * if the index is out of range.
0589: * @see #getLength
0590: */
0591: public String getName(int index) {
0592: if (index < 0 || index >= fLength) {
0593: return null;
0594: }
0595: return fAttributes[index].name.rawname;
0596: } // getName(int):String
0597:
0598: //
0599: // Attributes methods
0600: //
0601:
0602: /**
0603: * Look up the index of an attribute by XML 1.0 qualified name.
0604: *
0605: * @param qName The qualified (prefixed) name.
0606: * @return The index of the attribute, or -1 if it does not
0607: * appear in the list.
0608: */
0609: public int getIndex(String qName) {
0610: for (int i = 0; i < fLength; i++) {
0611: Attribute attribute = fAttributes[i];
0612: if (attribute.name.rawname != null
0613: && attribute.name.rawname.equals(qName)) {
0614: return i;
0615: }
0616: }
0617: return -1;
0618: } // getIndex(String):int
0619:
0620: /**
0621: * Look up the index of an attribute by Namespace name.
0622: *
0623: * @param uri The Namespace URI, or null if
0624: * the name has no Namespace URI.
0625: * @param localName The attribute's local name.
0626: * @return The index of the attribute, or -1 if it does not
0627: * appear in the list.
0628: */
0629: public int getIndex(String uri, String localPart) {
0630: for (int i = 0; i < fLength; i++) {
0631: Attribute attribute = fAttributes[i];
0632: if (attribute.name.localpart != null
0633: && attribute.name.localpart.equals(localPart)
0634: && ((uri == attribute.name.uri) || (uri != null
0635: && attribute.name.uri != null && attribute.name.uri
0636: .equals(uri)))) {
0637: return i;
0638: }
0639: }
0640: return -1;
0641: } // getIndex(String,String):int
0642:
0643: /**
0644: * Look up an attribute's local name by index.
0645: *
0646: * @param index The attribute index (zero-based).
0647: * @return The local name, or the empty string if Namespace
0648: * processing is not being performed, or null
0649: * if the index is out of range.
0650: * @see #getLength
0651: */
0652: public String getLocalName(int index) {
0653: if (!fNamespaces) {
0654: return "";
0655: }
0656: if (index < 0 || index >= fLength) {
0657: return null;
0658: }
0659: return fAttributes[index].name.localpart;
0660: } // getLocalName(int):String
0661:
0662: /**
0663: * Look up an attribute's XML 1.0 qualified name by index.
0664: *
0665: * @param index The attribute index (zero-based).
0666: * @return The XML 1.0 qualified name, or the empty string
0667: * if none is available, or null if the index
0668: * is out of range.
0669: * @see #getLength
0670: */
0671: public String getQName(int index) {
0672: if (index < 0 || index >= fLength) {
0673: return null;
0674: }
0675: String rawname = fAttributes[index].name.rawname;
0676: return rawname != null ? rawname : "";
0677: } // getQName(int):String
0678:
0679: public QName getQualifiedName(int index) {
0680: if (index < 0 || index >= fLength) {
0681: return null;
0682: }
0683: return fAttributes[index].name;
0684: }
0685:
0686: /**
0687: * Look up an attribute's type by Namespace name.
0688: *
0689: * <p>See {@link #getType(int) getType(int)} for a description
0690: * of the possible types.</p>
0691: *
0692: * @param uri The Namespace URI, or null if the
0693: * name has no Namespace URI.
0694: * @param localName The local name of the attribute.
0695: * @return The attribute type as a string, or null if the
0696: * attribute is not in the list or if Namespace
0697: * processing is not being performed.
0698: */
0699: public String getType(String uri, String localName) {
0700: if (!fNamespaces) {
0701: return null;
0702: }
0703: int index = getIndex(uri, localName);
0704: return index != -1 ? getReportableType(fAttributes[index].type)
0705: : null;
0706: } // getType(String,String):String
0707:
0708: /**
0709: * Returns the prefix of the attribute at the specified index.
0710: *
0711: * @param index The index of the attribute.
0712: */
0713: public String getPrefix(int index) {
0714: if (index < 0 || index >= fLength) {
0715: return null;
0716: }
0717: String prefix = fAttributes[index].name.prefix;
0718: // REVISIT: The empty string is not entered in the symbol table!
0719: return prefix != null ? prefix : "";
0720: } // getPrefix(int):String
0721:
0722: /**
0723: * Look up an attribute's Namespace URI by index.
0724: *
0725: * @param index The attribute index (zero-based).
0726: * @return The Namespace URI
0727: * @see #getLength
0728: */
0729: public String getURI(int index) {
0730: if (index < 0 || index >= fLength) {
0731: return null;
0732: }
0733: String uri = fAttributes[index].name.uri;
0734: return uri;
0735: } // getURI(int):String
0736:
0737: /**
0738: * Look up an attribute's value by Namespace name.
0739: *
0740: * <p>See {@link #getValue(int) getValue(int)} for a description
0741: * of the possible values.</p>
0742: *
0743: * @param uri The Namespace URI, or null if the
0744: * @param localName The local name of the attribute.
0745: * @return The attribute value as a string, or null if the
0746: * attribute is not in the list.
0747: */
0748: public String getValue(String uri, String localName) {
0749: int index = getIndex(uri, localName);
0750: return index != -1 ? getValue(index) : null;
0751: } // getValue(String,String):String
0752:
0753: /**
0754: * Look up an augmentations by Namespace name.
0755: *
0756: * @param uri The Namespace URI, or null if the
0757: * @param localName The local name of the attribute.
0758: * @return Augmentations
0759: */
0760: public Augmentations getAugmentations(String uri, String localName) {
0761: int index = getIndex(uri, localName);
0762: return index != -1 ? fAttributes[index].augs : null;
0763: }
0764:
0765: /**
0766: * Look up an augmentation by XML 1.0 qualified name.
0767: * <p>
0768: *
0769: * @param qName The XML 1.0 qualified name.
0770: *
0771: * @return Augmentations
0772: *
0773: */
0774: public Augmentations getAugmentations(String qName) {
0775: int index = getIndex(qName);
0776: return index != -1 ? fAttributes[index].augs : null;
0777: }
0778:
0779: /**
0780: * Look up an augmentations by attributes index.
0781: *
0782: * @param attributeIndex The attribute index.
0783: * @return Augmentations
0784: */
0785: public Augmentations getAugmentations(int attributeIndex) {
0786: if (attributeIndex < 0 || attributeIndex >= fLength) {
0787: return null;
0788: }
0789: return fAttributes[attributeIndex].augs;
0790: }
0791:
0792: /**
0793: * Sets the augmentations of the attribute at the specified index.
0794: *
0795: * @param attrIndex The attribute index.
0796: * @param augs The augmentations.
0797: */
0798: public void setAugmentations(int attrIndex, Augmentations augs) {
0799: fAttributes[attrIndex].augs = augs;
0800: }
0801:
0802: /**
0803: * Sets the uri of the attribute at the specified index.
0804: *
0805: * @param attrIndex The attribute index.
0806: * @param uri Namespace uri
0807: */
0808: public void setURI(int attrIndex, String uri) {
0809: fAttributes[attrIndex].name.uri = uri;
0810: } // getURI(int,QName)
0811:
0812: // Implementation methods
0813: public void setSchemaId(int attrIndex, boolean schemaId) {
0814: fAttributes[attrIndex].schemaId = schemaId;
0815: }
0816:
0817: public boolean getSchemaId(int index) {
0818: if (index < 0 || index >= fLength) {
0819: return false;
0820: }
0821: return fAttributes[index].schemaId;
0822: }
0823:
0824: public boolean getSchemaId(String qname) {
0825: int index = getIndex(qname);
0826: return index != -1 ? fAttributes[index].schemaId : false;
0827: } // getType(String):String
0828:
0829: public boolean getSchemaId(String uri, String localName) {
0830: if (!fNamespaces) {
0831: return false;
0832: }
0833: int index = getIndex(uri, localName);
0834: return index != -1 ? fAttributes[index].schemaId : false;
0835: } // getType(String,String):String
0836:
0837: /**
0838: * Look up the index of an attribute by XML 1.0 qualified name.
0839: * <p>
0840: * <strong>Note:</strong>
0841: * This method uses reference comparison, and thus should
0842: * only be used internally. We cannot use this method in any
0843: * code exposed to users as they may not pass in unique strings.
0844: *
0845: * @param qName The qualified (prefixed) name.
0846: * @return The index of the attribute, or -1 if it does not
0847: * appear in the list.
0848: */
0849: public int getIndexFast(String qName) {
0850: for (int i = 0; i < fLength; ++i) {
0851: Attribute attribute = fAttributes[i];
0852: if (attribute.name.rawname == qName) {
0853: return i;
0854: }
0855: }
0856: return -1;
0857: } // getIndexFast(String):int
0858:
0859: /**
0860: * Adds an attribute. The attribute's non-normalized value of the
0861: * attribute will have the same value as the attribute value until
0862: * set using the <code>setNonNormalizedValue</code> method. Also,
0863: * the added attribute will be marked as specified in the XML instance
0864: * document unless set otherwise using the <code>setSpecified</code>
0865: * method.
0866: * <p>
0867: * This method differs from <code>addAttribute</code> in that it
0868: * does not check if an attribute of the same name already exists
0869: * in the list before adding it. In order to improve performance
0870: * of namespace processing, this method allows uniqueness checks
0871: * to be deferred until all the namespace information is available
0872: * after the entire attribute specification has been read.
0873: * <p>
0874: * <strong>Caution:</strong> If this method is called it should
0875: * not be mixed with calls to <code>addAttribute</code> unless
0876: * it has been determined that all the attribute names are unique.
0877: *
0878: * @param name the attribute name
0879: * @param type the attribute type
0880: * @param value the attribute value
0881: *
0882: * @see #setNonNormalizedValue
0883: * @see #setSpecified
0884: * @see #checkDuplicatesNS
0885: */
0886: public void addAttributeNS(QName name, String type, String value) {
0887: int index = fLength;
0888: if (fLength++ == fAttributes.length) {
0889: Attribute[] attributes;
0890: if (fLength < SIZE_LIMIT) {
0891: attributes = new Attribute[fAttributes.length + 4];
0892: } else {
0893: attributes = new Attribute[fAttributes.length << 1];
0894: }
0895: System.arraycopy(fAttributes, 0, attributes, 0,
0896: fAttributes.length);
0897: for (int i = fAttributes.length; i < attributes.length; i++) {
0898: attributes[i] = new Attribute();
0899: }
0900: fAttributes = attributes;
0901: }
0902:
0903: // set values
0904: Attribute attribute = fAttributes[index];
0905: attribute.name.setValues(name);
0906: attribute.type = type;
0907: attribute.value = value;
0908: attribute.nonNormalizedValue = value;
0909: attribute.specified = false;
0910:
0911: // clear augmentations
0912: if (attribute.augs != null)
0913: attribute.augs.removeAllItems();
0914: }
0915:
0916: /**
0917: * Checks for duplicate expanded names (local part and namespace name
0918: * pairs) in the attribute specification. If a duplicate is found its
0919: * name is returned.
0920: * <p>
0921: * This should be called once all the in-scope namespaces for the element
0922: * enclosing these attributes is known, and after all the attributes
0923: * have gone through namespace binding.
0924: *
0925: * @return the name of a duplicate attribute found in the search,
0926: * otherwise null.
0927: */
0928: public QName checkDuplicatesNS() {
0929: // If the list is small check for duplicates using pairwise comparison.
0930: if (fLength <= SIZE_LIMIT) {
0931: for (int i = 0; i < fLength - 1; ++i) {
0932: Attribute att1 = fAttributes[i];
0933: for (int j = i + 1; j < fLength; ++j) {
0934: Attribute att2 = fAttributes[j];
0935: if (att1.name.localpart == att2.name.localpart
0936: && att1.name.uri == att2.name.uri) {
0937: return att2.name;
0938: }
0939: }
0940: }
0941: }
0942: // If the list is large check duplicates using a hash table.
0943: else {
0944: // We don't want this table view to be read if someone calls
0945: // addAttribute so we invalidate it up front.
0946: fIsTableViewConsistent = false;
0947:
0948: prepareTableView();
0949:
0950: Attribute attr;
0951: int bucket;
0952:
0953: for (int i = fLength - 1; i >= 0; --i) {
0954: attr = fAttributes[i];
0955: bucket = getTableViewBucket(attr.name.localpart,
0956: attr.name.uri);
0957:
0958: // The chain is stale.
0959: // This must be a unique attribute.
0960: if (fAttributeTableViewChainState[bucket] != fLargeCount) {
0961: fAttributeTableViewChainState[bucket] = fLargeCount;
0962: attr.next = null;
0963: fAttributeTableView[bucket] = attr;
0964: }
0965: // This chain is active.
0966: // We need to check if any of the attributes has the same name.
0967: else {
0968: // Search the table.
0969: Attribute found = fAttributeTableView[bucket];
0970: while (found != null) {
0971: if (found.name.localpart == attr.name.localpart
0972: && found.name.uri == attr.name.uri) {
0973: return attr.name;
0974: }
0975: found = found.next;
0976: }
0977:
0978: // Update table view
0979: attr.next = fAttributeTableView[bucket];
0980: fAttributeTableView[bucket] = attr;
0981: }
0982: }
0983: }
0984: return null;
0985: }
0986:
0987: /**
0988: * Look up the index of an attribute by Namespace name.
0989: * <p>
0990: * <strong>Note:</strong>
0991: * This method uses reference comparison, and thus should
0992: * only be used internally. We cannot use this method in any
0993: * code exposed to users as they may not pass in unique strings.
0994: *
0995: * @param uri The Namespace URI, or null if
0996: * the name has no Namespace URI.
0997: * @param localName The attribute's local name.
0998: * @return The index of the attribute, or -1 if it does not
0999: * appear in the list.
1000: */
1001: public int getIndexFast(String uri, String localPart) {
1002: for (int i = 0; i < fLength; ++i) {
1003: Attribute attribute = fAttributes[i];
1004: if (attribute.name.localpart == localPart
1005: && attribute.name.uri == uri) {
1006: return i;
1007: }
1008: }
1009: return -1;
1010: } // getIndexFast(String,String):int
1011:
1012: /**
1013: * Returns the value passed in or NMTOKEN if it's an enumerated type.
1014: *
1015: * @param type attribute type
1016: * @return the value passed in or NMTOKEN if it's an enumerated type.
1017: */
1018: protected String getReportableType(String type) {
1019:
1020: if (type.indexOf('(') == 0
1021: && type.lastIndexOf(')') == type.length() - 1) {
1022: return "NMTOKEN";
1023: }
1024: return type;
1025: }
1026:
1027: /**
1028: * Returns the position in the table view
1029: * where the given attribute name would be hashed.
1030: *
1031: * @param qname the attribute name
1032: * @return the position in the table view where the given attribute
1033: * would be hashed
1034: */
1035: protected int getTableViewBucket(String qname) {
1036: return (qname.hashCode() & 0x7FFFFFFF) % fTableViewBuckets;
1037: }
1038:
1039: /**
1040: * Returns the position in the table view
1041: * where the given attribute name would be hashed.
1042: *
1043: * @param localpart the local part of the attribute
1044: * @param uri the namespace name of the attribute
1045: * @return the position in the table view where the given attribute
1046: * would be hashed
1047: */
1048: protected int getTableViewBucket(String localpart, String uri) {
1049: if (uri == null) {
1050: return (localpart.hashCode() & 0x7FFFFFFF)
1051: % fTableViewBuckets;
1052: } else {
1053: return ((localpart.hashCode() + uri.hashCode()) & 0x7FFFFFFF)
1054: % fTableViewBuckets;
1055: }
1056: }
1057:
1058: /**
1059: * Purges all elements from the table view.
1060: */
1061: protected void cleanTableView() {
1062: if (++fLargeCount < 0) {
1063: // Overflow. We actually need to visit the chain state array.
1064: if (fAttributeTableViewChainState != null) {
1065: for (int i = fTableViewBuckets - 1; i >= 0; --i) {
1066: fAttributeTableViewChainState[i] = 0;
1067: }
1068: }
1069: fLargeCount = 1;
1070: }
1071: }
1072:
1073: /**
1074: * Prepares the table view of the attributes list for use.
1075: */
1076: protected void prepareTableView() {
1077: if (fAttributeTableView == null) {
1078: fAttributeTableView = new Attribute[fTableViewBuckets];
1079: fAttributeTableViewChainState = new int[fTableViewBuckets];
1080: } else {
1081: cleanTableView();
1082: }
1083: }
1084:
1085: /**
1086: * Prepares the table view of the attributes list for use,
1087: * and populates it with the attributes which have been
1088: * previously read.
1089: */
1090: protected void prepareAndPopulateTableView() {
1091: prepareTableView();
1092: // Need to populate the hash table with the attributes we've scanned so far.
1093: Attribute attr;
1094: int bucket;
1095: for (int i = 0; i < fLength; ++i) {
1096: attr = fAttributes[i];
1097: bucket = getTableViewBucket(attr.name.rawname);
1098: if (fAttributeTableViewChainState[bucket] != fLargeCount) {
1099: fAttributeTableViewChainState[bucket] = fLargeCount;
1100: attr.next = null;
1101: fAttributeTableView[bucket] = attr;
1102: } else {
1103: // Update table view
1104: attr.next = fAttributeTableView[bucket];
1105: fAttributeTableView[bucket] = attr;
1106: }
1107: }
1108: }
1109:
1110: /**
1111: * This method will be invoked by XMLEntityReader before ScannedEntities buffer
1112: * is reloaded.
1113: */
1114: public void refresh() {
1115: if (fLength > 0) {
1116: for (int i = 0; i < fLength; i++) {
1117: getValue(i);
1118: }
1119: }
1120: }
1121:
1122: public void refresh(int pos) {
1123: }
1124:
1125: //
1126: // Classes
1127: //
1128:
1129: /**
1130: * Attribute information.
1131: *
1132: * @author Andy Clark, IBM
1133: */
1134: static class Attribute {
1135:
1136: //
1137: // Data
1138: //
1139:
1140: // basic info
1141:
1142: /** Name. */
1143: public QName name = new QName();
1144:
1145: /** Type. */
1146: public String type;
1147:
1148: /** Value. */
1149: public String value;
1150: /** This will point to the ScannedEntities buffer.*/
1151: public XMLString xmlValue;
1152:
1153: /** Non-normalized value. */
1154: public String nonNormalizedValue;
1155:
1156: /** Specified. */
1157: public boolean specified;
1158:
1159: /** Schema ID type. */
1160: public boolean schemaId;
1161:
1162: /**
1163: * Augmentations information for this attribute.
1164: * XMLAttributes has no knowledge if any augmentations
1165: * were attached to Augmentations.
1166: */
1167: public Augmentations augs = null;
1168: //new AugmentationsImpl();
1169:
1170: // Additional data for attribute table view
1171:
1172: /** Pointer to the next attribute in the chain. **/
1173: public Attribute next;
1174:
1175: } // class Attribute
1176:
1177: } // class XMLAttributesImpl
|