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