package client.net.sf.saxon.ce.om; import client.net.sf.saxon.ce.Configuration; /** * AttributeCollection represents the collection of attributes available on a particular element * node. It is modelled on the SAX2 Attributes interface, but is extended firstly to work with * Saxon NamePools, and secondly to provide type information as required by the XPath 2.0 data model. */ public class AttributeCollection { // Attribute values are maintained as an array of Strings. Everything else is maintained // in the form of integers. private Configuration config; private String[] values = null; private int[] codes = null; private int used = 0; // Empty attribute collection. The caller is trusted not to try and modify it. public static AttributeCollection EMPTY_ATTRIBUTE_COLLECTION = new AttributeCollection((Configuration)null); /** * Create an empty attribute list. * @param config the Saxon Configuration */ public AttributeCollection(Configuration config) { this.config = config; used = 0; } /** * Create an attribute list as a copy of an existing attribute list * @param atts the existing attribute list to be copied * @return the copied attribute list. Note that if the original attribute list * is empty, the method returns the singleton object {@link #EMPTY_ATTRIBUTE_COLLECTION}; * this case must therefore be handled specially if the returned attribute list is to * be modified. */ public static AttributeCollection copy(AttributeCollection atts) { if (atts.getLength() == 0) { return EMPTY_ATTRIBUTE_COLLECTION; } AttributeCollection t = new AttributeCollection(atts.config); t.used = atts.used; t.values = new String[atts.used]; t.codes = new int[atts.used]; System.arraycopy(atts.values, 0, t.values, 0, atts.used); System.arraycopy(atts.codes, 0, t.codes, 0, atts.used); return t; } /** * Add an attribute to an attribute list. The parameters correspond * to the parameters of the {@link client.net.sf.saxon.ce.event.Receiver#attribute(int, CharSequence)} * method. There is no check that the name of the attribute is distinct from other attributes * already in the collection: this check must be made by the caller. * * @param nameCode Integer representing the attribute name. * @param value The attribute value (must not be null) */ public void addAttribute(int nameCode, String value) { if (values == null) { values = new String[5]; codes = new int[5]; used = 0; } if (values.length == used) { int newsize = (used == 0 ? 5 : used * 2); String[] v2 = new String[newsize]; int[] c2 = new int[newsize]; System.arraycopy(values, 0, v2, 0, used); System.arraycopy(codes, 0, c2, 0, used); values = v2; codes = c2; } codes[used] = nameCode; values[used++] = value; } /** * Clear the attribute list. This removes the values but doesn't free the memory used. * free the memory, use clear() then compact(). */ public void clear() { used = 0; } /** * Compact the attribute list to avoid wasting memory */ public void compact() { if (used == 0) { codes = null; values = null; } else if (values.length > used) { String[] v2 = new String[used]; int[] c2 = new int[used]; System.arraycopy(values, 0, v2, 0, used); System.arraycopy(codes, 0, c2, 0, used); values = v2; codes = c2; } } /** * Return the number of attributes in the list. * * @return The number of attributes that have been created in this attribute collection. This is the number * of slots used in the list, including any slots allocated to attributes that have since been deleted. * Such slots are not reused, to preserve attribute identity. */ public int getLength() { return (values == null ? 0 : used); } /** * Get the namecode of an attribute (by position). * * @param index The position of the attribute in the list. * @return The display name of the attribute as a string, or null if there * is no attribute at that position. */ public int getNameCode(int index) { if (codes == null) { return -1; } if (index < 0 || index >= used) { return -1; } return codes[index]; } /** * Get the prefix of the name of an attribute (by position). * * @param index The position of the attribute in the list. * @return The prefix of the attribute name as a string, or null if there * is no attribute at that position. Returns "" for an attribute that * has no prefix. */ public String getPrefix(int index) { if (codes == null) { return null; } if (index < 0 || index >= used) { return null; } return config.getNamePool().getPrefix(getNameCode(index)); } /** * Get the local name of an attribute (by position). * * @param index The position of the attribute in the list. * @return The local name of the attribute as a string, or null if there * is no attribute at that position. */ public String getLocalName(int index) { if (codes == null) { return null; } if (index < 0 || index >= used) { return null; } return config.getNamePool().getLocalName(getNameCode(index)); } /** * Get the namespace URI of an attribute (by position). * * @param index The position of the attribute in the list. * @return The local name of the attribute as a string, or null if there * is no attribute at that position. */ public String getURI(int index) { if (codes == null) { return null; } if (index < 0 || index >= used) { return null; } return config.getNamePool().getURI(getNameCode(index)); } /** * Get the value of an attribute (by position). * * @param index The position of the attribute in the list. * @return The attribute value as a string, or null if * there is no attribute at that position. */ public String getValue(int index) { if (values == null) { return null; } if (index < 0 || index >= used) { return null; } return values[index]; } /** * Get the value of an attribute (by name). * * @param uri The namespace uri of the attribute. * @param localname The local name of the attribute. * @return The index position of the attribute */ public String getValue(String uri, String localname) { int index = findByName(uri, localname); return (index < 0 ? null : getValue(index)); } /** * Get the attribute value using its fingerprint */ public String getValueByFingerprint(int fingerprint) { int index = findByFingerprint(fingerprint); return (index < 0 ? null : getValue(index)); } /** * Get the index of an attribute, from its lexical QName * * @param qname The lexical QName of the attribute. The prefix must match. * @return The index position of the attribute */ public int getIndex(String qname) { if (codes == null) { return -1; } if (qname.indexOf(':') < 0) { return findByName("", qname); } // Searching using prefix+localname is not recommended, but SAX allows it... String[] parts; try { parts = NameChecker.getQNameParts(qname); } catch (QNameException err) { return -1; } String prefix = parts[0]; if (prefix.length() == 0) { return findByName("", qname); } else { String localName = parts[1]; for (int i = 0; i < used; i++) { String lname = config.getNamePool().getLocalName(getNameCode(i)); String ppref = config.getNamePool().getPrefix(getNameCode(i)); if (localName.equals(lname) && prefix.equals(ppref)) { return i; } } return -1; } } /** * Get the index, given the fingerprint. * Return -1 if not found. */ public int getIndexByFingerprint(int fingerprint) { return findByFingerprint(fingerprint); } /** * Get the value of an attribute (by lexical QName). * * @param name The attribute name (a lexical QName). * The prefix must match the prefix originally used. This method is defined in SAX, but is * not recommended except where the prefix is null. */ public String getValue(String name) { int index = getIndex(name); return getValue(index); } /** * Find an attribute by expanded name * @param uri the namespace uri * @param localName the local name * @return the index of the attribute, or -1 if absent */ private int findByName(String uri, String localName) { if (config == null) { return -1; // indicates an empty attribute set } NamePool namePool = config.getNamePool(); int f = namePool.getFingerprint(uri, localName); if (f == -1) { return -1; } return findByFingerprint(f); } /** * Find an attribute by fingerprint * @param fingerprint the fingerprint representing the name of the required attribute * @return the index of the attribute, or -1 if absent */ private int findByFingerprint(int fingerprint) { if (codes == null) { return -1; } for (int i = 0; i < used; i++) { if (fingerprint == (codes[i] & NamePool.FP_MASK)) { return i; } } return -1; } /** * Determine whether a given attribute has the is-ID property set */ public boolean isId(int index) { return (codes[index] & NamePool.FP_MASK) == StandardNames.XML_ID; } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.