1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.util;
  58. import com.sun.org.apache.xerces.internal.xni.Augmentations;
  59. import com.sun.org.apache.xerces.internal.xni.QName;
  60. import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  61. /**
  62. * The XMLAttributesImpl class is an implementation of the XMLAttributes
  63. * interface which defines a collection of attributes for an element.
  64. * In the parser, the document source would scan the entire start element
  65. * and collect the attributes. The attributes are communicated to the
  66. * document handler in the startElement method.
  67. * <p>
  68. * The attributes are read-write so that subsequent stages in the document
  69. * pipeline can modify the values or change the attributes that are
  70. * propogated to the next stage.
  71. *
  72. * @see com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler#startElement
  73. *
  74. * @author Andy Clark, IBM
  75. * @author Elena Litani, IBM
  76. * @author Michael Glavassevich, IBM
  77. *
  78. * @version $Id: XMLAttributesImpl.java,v 1.24 2004/01/19 22:12:10 mrglavas Exp $
  79. */
  80. public class XMLAttributesImpl
  81. implements XMLAttributes {
  82. //
  83. // Constants
  84. //
  85. /** Default table size. */
  86. protected static final int TABLE_SIZE = 101;
  87. /**
  88. * Threshold at which an instance is treated
  89. * as a large attribute list.
  90. */
  91. protected static final int SIZE_LIMIT = 20;
  92. //
  93. // Data
  94. //
  95. // features
  96. /** Namespaces. */
  97. protected boolean fNamespaces = true;
  98. // data
  99. /**
  100. * Usage count for the attribute table view.
  101. * Incremented each time all attributes are removed
  102. * when the attribute table view is in use.
  103. */
  104. protected int fLargeCount = 1;
  105. /** Attribute count. */
  106. protected int fLength;
  107. /** Attribute information. */
  108. protected Attribute[] fAttributes = new Attribute[4];
  109. /**
  110. * Hashtable of attribute information.
  111. * Provides an alternate view of the attribute specification.
  112. */
  113. protected Attribute[] fAttributeTableView;
  114. /**
  115. * Tracks whether each chain in the hash table is stale
  116. * with respect to the current state of this object.
  117. * A chain is stale if its state is not the same as the number
  118. * of times the attribute table view has been used.
  119. */
  120. protected int[] fAttributeTableViewChainState;
  121. /**
  122. * Actual number of buckets in the table view.
  123. */
  124. protected int fTableViewBuckets;
  125. /**
  126. * Indicates whether the table view contains consistent data.
  127. */
  128. protected boolean fIsTableViewConsistent;
  129. //
  130. // Constructors
  131. //
  132. /** Default constructor. */
  133. public XMLAttributesImpl() {
  134. this(TABLE_SIZE);
  135. }
  136. /**
  137. * @param tableSize initial size of table view
  138. */
  139. public XMLAttributesImpl(int tableSize) {
  140. fTableViewBuckets = tableSize;
  141. for (int i = 0; i < fAttributes.length; i++) {
  142. fAttributes[i] = new Attribute();
  143. }
  144. } // <init>()
  145. //
  146. // Public methods
  147. //
  148. /**
  149. * Sets whether namespace processing is being performed. This state
  150. * is needed to return the correct value from the getLocalName method.
  151. *
  152. * @param namespaces True if namespace processing is turned on.
  153. *
  154. * @see #getLocalName
  155. */
  156. public void setNamespaces(boolean namespaces) {
  157. fNamespaces = namespaces;
  158. } // setNamespaces(boolean)
  159. //
  160. // XMLAttributes methods
  161. //
  162. /**
  163. * Adds an attribute. The attribute's non-normalized value of the
  164. * attribute will have the same value as the attribute value until
  165. * set using the <code>setNonNormalizedValue</code> method. Also,
  166. * the added attribute will be marked as specified in the XML instance
  167. * document unless set otherwise using the <code>setSpecified</code>
  168. * method.
  169. * <p>
  170. * <strong>Note:</strong> If an attribute of the same name already
  171. * exists, the old values for the attribute are replaced by the new
  172. * values.
  173. *
  174. * @param name The attribute name.
  175. * @param type The attribute type. The type name is determined by
  176. * the type specified for this attribute in the DTD.
  177. * For example: "CDATA", "ID", "NMTOKEN", etc. However,
  178. * attributes of type enumeration will have the type
  179. * value specified as the pipe ('|') separated list of
  180. * the enumeration values prefixed by an open
  181. * parenthesis and suffixed by a close parenthesis.
  182. * For example: "(true|false)".
  183. * @param value The attribute value.
  184. *
  185. * @return Returns the attribute index.
  186. *
  187. * @see #setNonNormalizedValue
  188. * @see #setSpecified
  189. */
  190. public int addAttribute(QName name, String type, String value) {
  191. int index;
  192. if (fLength < SIZE_LIMIT) {
  193. index = name.uri != null && !name.uri.equals("")
  194. ? getIndexFast(name.uri, name.localpart)
  195. : getIndexFast(name.rawname);
  196. if (index == -1) {
  197. index = fLength;
  198. if (fLength++ == fAttributes.length) {
  199. Attribute[] attributes = new Attribute[fAttributes.length + 4];
  200. System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
  201. for (int i = fAttributes.length; i < attributes.length; i++) {
  202. attributes[i] = new Attribute();
  203. }
  204. fAttributes = attributes;
  205. }
  206. }
  207. }
  208. else if (name.uri == null ||
  209. name.uri.length() == 0 ||
  210. (index = getIndexFast(name.uri, name.localpart)) == -1) {
  211. /**
  212. * If attributes were removed from the list after the table
  213. * becomes in use this isn't reflected in the table view. It's
  214. * assumed that once a user starts removing attributes they're
  215. * not likely to add more. We only make the view consistent if
  216. * the user of this class adds attributes, removes them, and
  217. * then adds more.
  218. */
  219. if (!fIsTableViewConsistent || fLength == SIZE_LIMIT) {
  220. prepareAndPopulateTableView();
  221. fIsTableViewConsistent = true;
  222. }
  223. int bucket = getTableViewBucket(name.rawname);
  224. // The chain is stale.
  225. // This must be a unique attribute.
  226. if (fAttributeTableViewChainState[bucket] != fLargeCount) {
  227. index = fLength;
  228. if (fLength++ == fAttributes.length) {
  229. Attribute[] attributes = new Attribute[fAttributes.length << 1];
  230. System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
  231. for (int i = fAttributes.length; i < attributes.length; i++) {
  232. attributes[i] = new Attribute();
  233. }
  234. fAttributes = attributes;
  235. }
  236. // Update table view.
  237. fAttributeTableViewChainState[bucket] = fLargeCount;
  238. fAttributes[index].next = null;
  239. fAttributeTableView[bucket] = fAttributes[index];
  240. }
  241. // This chain is active.
  242. // We need to check if any of the attributes has the same rawname.
  243. else {
  244. // Search the table.
  245. Attribute found = fAttributeTableView[bucket];
  246. while (found != null) {
  247. if (found.name.rawname == name.rawname) {
  248. break;
  249. }
  250. found = found.next;
  251. }
  252. // This attribute is unique.
  253. if (found == null) {
  254. index = fLength;
  255. if (fLength++ == fAttributes.length) {
  256. Attribute[] attributes = new Attribute[fAttributes.length << 1];
  257. System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
  258. for (int i = fAttributes.length; i < attributes.length; i++) {
  259. attributes[i] = new Attribute();
  260. }
  261. fAttributes = attributes;
  262. }
  263. // Update table view
  264. fAttributes[index].next = fAttributeTableView[bucket];
  265. fAttributeTableView[bucket] = fAttributes[index];
  266. }
  267. // Duplicate. We still need to find the index.
  268. else {
  269. index = getIndexFast(name.rawname);
  270. }
  271. }
  272. }
  273. // set values
  274. Attribute attribute = fAttributes[index];
  275. attribute.name.setValues(name);
  276. attribute.type = type;
  277. attribute.value = value;
  278. attribute.nonNormalizedValue = value;
  279. attribute.specified = false;
  280. // return
  281. return index;
  282. } // addAttribute(QName,String,XMLString)
  283. /**
  284. * Removes all of the attributes. This method will also remove all
  285. * entities associated to the attributes.
  286. */
  287. public void removeAllAttributes() {
  288. fLength = 0;
  289. } // removeAllAttributes()
  290. /**
  291. * Removes the attribute at the specified index.
  292. * <p>
  293. * <strong>Note:</strong> This operation changes the indexes of all
  294. * attributes following the attribute at the specified index.
  295. *
  296. * @param attrIndex The attribute index.
  297. */
  298. public void removeAttributeAt(int attrIndex) {
  299. fIsTableViewConsistent = false;
  300. if (attrIndex < fLength - 1) {
  301. Attribute removedAttr = fAttributes[attrIndex];
  302. System.arraycopy(fAttributes, attrIndex + 1,
  303. fAttributes, attrIndex, fLength - attrIndex - 1);
  304. // Make the discarded Attribute object available for re-use
  305. // by tucking it after the Attributes that are still in use
  306. fAttributes[fLength-1] = removedAttr;
  307. }
  308. fLength--;
  309. } // removeAttributeAt(int)
  310. /**
  311. * Sets the name of the attribute at the specified index.
  312. *
  313. * @param attrIndex The attribute index.
  314. * @param attrName The new attribute name.
  315. */
  316. public void setName(int attrIndex, QName attrName) {
  317. fAttributes[attrIndex].name.setValues(attrName);
  318. } // setName(int,QName)
  319. /**
  320. * Sets the fields in the given QName structure with the values
  321. * of the attribute name at the specified index.
  322. *
  323. * @param attrIndex The attribute index.
  324. * @param attrName The attribute name structure to fill in.
  325. */
  326. public void getName(int attrIndex, QName attrName) {
  327. attrName.setValues(fAttributes[attrIndex].name);
  328. } // getName(int,QName)
  329. /**
  330. * Sets the type of the attribute at the specified index.
  331. *
  332. * @param attrIndex The attribute index.
  333. * @param attrType The attribute type. The type name is determined by
  334. * the type specified for this attribute in the DTD.
  335. * For example: "CDATA", "ID", "NMTOKEN", etc. However,
  336. * attributes of type enumeration will have the type
  337. * value specified as the pipe ('|') separated list of
  338. * the enumeration values prefixed by an open
  339. * parenthesis and suffixed by a close parenthesis.
  340. * For example: "(true|false)".
  341. */
  342. public void setType(int attrIndex, String attrType) {
  343. fAttributes[attrIndex].type = attrType;
  344. } // setType(int,String)
  345. /**
  346. * Sets the value of the attribute at the specified index. This
  347. * method will overwrite the non-normalized value of the attribute.
  348. *
  349. * @param attrIndex The attribute index.
  350. * @param attrValue The new attribute value.
  351. *
  352. * @see #setNonNormalizedValue
  353. */
  354. public void setValue(int attrIndex, String attrValue) {
  355. Attribute attribute = fAttributes[attrIndex];
  356. attribute.value = attrValue;
  357. attribute.nonNormalizedValue = attrValue;
  358. } // setValue(int,String)
  359. /**
  360. * Sets the non-normalized value of the attribute at the specified
  361. * index.
  362. *
  363. * @param attrIndex The attribute index.
  364. * @param attrValue The new non-normalized attribute value.
  365. */
  366. public void setNonNormalizedValue(int attrIndex, String attrValue) {
  367. if (attrValue == null) {
  368. attrValue = fAttributes[attrIndex].value;
  369. }
  370. fAttributes[attrIndex].nonNormalizedValue = attrValue;
  371. } // setNonNormalizedValue(int,String)
  372. /**
  373. * Returns the non-normalized value of the attribute at the specified
  374. * index. If no non-normalized value is set, this method will return
  375. * the same value as the <code>getValue(int)</code> method.
  376. *
  377. * @param attrIndex The attribute index.
  378. */
  379. public String getNonNormalizedValue(int attrIndex) {
  380. String value = fAttributes[attrIndex].nonNormalizedValue;
  381. return value;
  382. } // getNonNormalizedValue(int):String
  383. /**
  384. * Sets whether an attribute is specified in the instance document
  385. * or not.
  386. *
  387. * @param attrIndex The attribute index.
  388. * @param specified True if the attribute is specified in the instance
  389. * document.
  390. */
  391. public void setSpecified(int attrIndex, boolean specified) {
  392. fAttributes[attrIndex].specified = specified;
  393. } // setSpecified(int,boolean)
  394. /**
  395. * Returns true if the attribute is specified in the instance document.
  396. *
  397. * @param attrIndex The attribute index.
  398. */
  399. public boolean isSpecified(int attrIndex) {
  400. return fAttributes[attrIndex].specified;
  401. } // isSpecified(int):boolean
  402. //
  403. // AttributeList and Attributes methods
  404. //
  405. /**
  406. * Return the number of attributes in the list.
  407. *
  408. * <p>Once you know the number of attributes, you can iterate
  409. * through the list.</p>
  410. *
  411. * @return The number of attributes in the list.
  412. */
  413. public int getLength() {
  414. return fLength;
  415. } // getLength():int
  416. /**
  417. * Look up an attribute's type by index.
  418. *
  419. * <p>The attribute type is one of the strings "CDATA", "ID",
  420. * "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
  421. * or "NOTATION" (always in upper case).</p>
  422. *
  423. * <p>If the parser has not read a declaration for the attribute,
  424. * or if the parser does not report attribute types, then it must
  425. * return the value "CDATA" as stated in the XML 1.0 Recommentation
  426. * (clause 3.3.3, "Attribute-Value Normalization").</p>
  427. *
  428. * <p>For an enumerated attribute that is not a notation, the
  429. * parser will report the type as "NMTOKEN".</p>
  430. *
  431. * @param index The attribute index (zero-based).
  432. * @return The attribute's type as a string, or null if the
  433. * index is out of range.
  434. * @see #getLength
  435. */
  436. public String getType(int index) {
  437. if (index < 0 || index >= fLength) {
  438. return null;
  439. }
  440. return getReportableType(fAttributes[index].type);
  441. } // getType(int):String
  442. /**
  443. * Look up an attribute's type by XML 1.0 qualified name.
  444. *
  445. * <p>See {@link #getType(int) getType(int)} for a description
  446. * of the possible types.</p>
  447. *
  448. * @param qname The XML 1.0 qualified name.
  449. * @return The attribute type as a string, or null if the
  450. * attribute is not in the list or if qualified names
  451. * are not available.
  452. */
  453. public String getType(String qname) {
  454. int index = getIndex(qname);
  455. return index != -1 ? getReportableType(fAttributes[index].type) : null;
  456. } // getType(String):String
  457. /**
  458. * Look up an attribute's value by index.
  459. *
  460. * <p>If the attribute value is a list of tokens (IDREFS,
  461. * ENTITIES, or NMTOKENS), the tokens will be concatenated
  462. * into a single string with each token separated by a
  463. * single space.</p>
  464. *
  465. * @param index The attribute index (zero-based).
  466. * @return The attribute's value as a string, or null if the
  467. * index is out of range.
  468. * @see #getLength
  469. */
  470. public String getValue(int index) {
  471. if (index < 0 || index >= fLength) {
  472. return null;
  473. }
  474. return fAttributes[index].value;
  475. } // getValue(int):String
  476. /**
  477. * Look up an attribute's value by XML 1.0 qualified name.
  478. *
  479. * <p>See {@link #getValue(int) getValue(int)} for a description
  480. * of the possible values.</p>
  481. *
  482. * @param qname The XML 1.0 qualified name.
  483. * @return The attribute value as a string, or null if the
  484. * attribute is not in the list or if qualified names
  485. * are not available.
  486. */
  487. public String getValue(String qname) {
  488. int index = getIndex(qname);
  489. return index != -1 ? fAttributes[index].value : null;
  490. } // getValue(String):String
  491. //
  492. // AttributeList methods
  493. //
  494. /**
  495. * Return the name of an attribute in this list (by position).
  496. *
  497. * <p>The names must be unique: the SAX parser shall not include the
  498. * same attribute twice. Attributes without values (those declared
  499. * #IMPLIED without a value specified in the start tag) will be
  500. * omitted from the list.</p>
  501. *
  502. * <p>If the attribute name has a namespace prefix, the prefix
  503. * will still be attached.</p>
  504. *
  505. * @param i The index of the attribute in the list (starting at 0).
  506. * @return The name of the indexed attribute, or null
  507. * if the index is out of range.
  508. * @see #getLength
  509. */
  510. public String getName(int index) {
  511. if (index < 0 || index >= fLength) {
  512. return null;
  513. }
  514. return fAttributes[index].name.rawname;
  515. } // getName(int):String
  516. //
  517. // Attributes methods
  518. //
  519. /**
  520. * Look up the index of an attribute by XML 1.0 qualified name.
  521. *
  522. * @param qName The qualified (prefixed) name.
  523. * @return The index of the attribute, or -1 if it does not
  524. * appear in the list.
  525. */
  526. public int getIndex(String qName) {
  527. for (int i = 0; i < fLength; i++) {
  528. Attribute attribute = fAttributes[i];
  529. if (attribute.name.rawname != null &&
  530. attribute.name.rawname.equals(qName)) {
  531. return i;
  532. }
  533. }
  534. return -1;
  535. } // getIndex(String):int
  536. /**
  537. * Look up the index of an attribute by Namespace name.
  538. *
  539. * @param uri The Namespace URI, or null if
  540. * the name has no Namespace URI.
  541. * @param localName The attribute's local name.
  542. * @return The index of the attribute, or -1 if it does not
  543. * appear in the list.
  544. */
  545. public int getIndex(String uri, String localPart) {
  546. for (int i = 0; i < fLength; i++) {
  547. Attribute attribute = fAttributes[i];
  548. if (attribute.name.localpart != null &&
  549. attribute.name.localpart.equals(localPart) &&
  550. ((uri==attribute.name.uri) ||
  551. (uri!=null && attribute.name.uri!=null && attribute.name.uri.equals(uri))))
  552. {
  553. return i;
  554. }
  555. }
  556. return -1;
  557. } // getIndex(String,String):int
  558. /**
  559. * Look up an attribute's local name by index.
  560. *
  561. * @param index The attribute index (zero-based).
  562. * @return The local name, or the empty string if Namespace
  563. * processing is not being performed, or null
  564. * if the index is out of range.
  565. * @see #getLength
  566. */
  567. public String getLocalName(int index) {
  568. if (!fNamespaces) {
  569. return "";
  570. }
  571. if (index < 0 || index >= fLength) {
  572. return null;
  573. }
  574. return fAttributes[index].name.localpart;
  575. } // getLocalName(int):String
  576. /**
  577. * Look up an attribute's XML 1.0 qualified name by index.
  578. *
  579. * @param index The attribute index (zero-based).
  580. * @return The XML 1.0 qualified name, or the empty string
  581. * if none is available, or null if the index
  582. * is out of range.
  583. * @see #getLength
  584. */
  585. public String getQName(int index) {
  586. if (index < 0 || index >= fLength) {
  587. return null;
  588. }
  589. String rawname = fAttributes[index].name.rawname;
  590. return rawname != null ? rawname : "";
  591. } // getQName(int):String
  592. /**
  593. * Look up an attribute's type by Namespace name.
  594. *
  595. * <p>See {@link #getType(int) getType(int)} for a description
  596. * of the possible types.</p>
  597. *
  598. * @param uri The Namespace URI, or null if the
  599. * name has no Namespace URI.
  600. * @param localName The local name of the attribute.
  601. * @return The attribute type as a string, or null if the
  602. * attribute is not in the list or if Namespace
  603. * processing is not being performed.
  604. */
  605. public String getType(String uri, String localName) {
  606. if (!fNamespaces) {
  607. return null;
  608. }
  609. int index = getIndex(uri, localName);
  610. return index != -1 ? getType(index) : null;
  611. } // getType(String,String):String
  612. /**
  613. * Look up the index of an attribute by XML 1.0 qualified name.
  614. * <p>
  615. * <strong>Note:</strong>
  616. * This method uses reference comparison, and thus should
  617. * only be used internally. We cannot use this method in any
  618. * code exposed to users as they may not pass in unique strings.
  619. *
  620. * @param qName The qualified (prefixed) name.
  621. * @return The index of the attribute, or -1 if it does not
  622. * appear in the list.
  623. */
  624. public int getIndexFast(String qName) {
  625. for (int i = 0; i < fLength; ++i) {
  626. Attribute attribute = fAttributes[i];
  627. if (attribute.name.rawname == qName) {
  628. return i;
  629. }
  630. }
  631. return -1;
  632. } // getIndexFast(String):int
  633. /**
  634. * Adds an attribute. The attribute's non-normalized value of the
  635. * attribute will have the same value as the attribute value until
  636. * set using the <code>setNonNormalizedValue</code> method. Also,
  637. * the added attribute will be marked as specified in the XML instance
  638. * document unless set otherwise using the <code>setSpecified</code>
  639. * method.
  640. * <p>
  641. * This method differs from <code>addAttribute</code> in that it
  642. * does not check if an attribute of the same name already exists
  643. * in the list before adding it. In order to improve performance
  644. * of namespace processing, this method allows uniqueness checks
  645. * to be deferred until all the namespace information is available
  646. * after the entire attribute specification has been read.
  647. * <p>
  648. * <strong>Caution:</strong> If this method is called it should
  649. * not be mixed with calls to <code>addAttribute</code> unless
  650. * it has been determined that all the attribute names are unique.
  651. *
  652. * @param name the attribute name
  653. * @param type the attribute type
  654. * @param value the attribute value
  655. *
  656. * @see #setNonNormalizedValue
  657. * @see #setSpecified
  658. * @see #checkDuplicatesNS
  659. */
  660. public void addAttributeNS(QName name, String type, String value) {
  661. int index = fLength;
  662. if (fLength++ == fAttributes.length) {
  663. Attribute[] attributes;
  664. if (fLength < SIZE_LIMIT) {
  665. attributes = new Attribute[fAttributes.length + 4];
  666. }
  667. else {
  668. attributes = new Attribute[fAttributes.length << 1];
  669. }
  670. System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
  671. for (int i = fAttributes.length; i < attributes.length; i++) {
  672. attributes[i] = new Attribute();
  673. }
  674. fAttributes = attributes;
  675. }
  676. // set values
  677. Attribute attribute = fAttributes[index];
  678. attribute.name.setValues(name);
  679. attribute.type = type;
  680. attribute.value = value;
  681. attribute.nonNormalizedValue = value;
  682. attribute.specified = false;
  683. // clear augmentations
  684. attribute.augs.removeAllItems();
  685. }
  686. /**
  687. * Checks for duplicate expanded names (local part and namespace name
  688. * pairs) in the attribute specification. If a duplicate is found its
  689. * name is returned.
  690. * <p>
  691. * This should be called once all the in-scope namespaces for the element
  692. * enclosing these attributes is known, and after all the attributes
  693. * have gone through namespace binding.
  694. *
  695. * @return the name of a duplicate attribute found in the search,
  696. * otherwise null.
  697. */
  698. public QName checkDuplicatesNS() {
  699. // If the list is small check for duplicates using pairwise comparison.
  700. if (fLength <= SIZE_LIMIT) {
  701. for (int i = 0; i < fLength - 1; ++i) {
  702. Attribute att1 = fAttributes[i];
  703. for (int j = i + 1; j < fLength; ++j) {
  704. Attribute att2 = fAttributes[j];
  705. if (att1.name.localpart == att2.name.localpart &&
  706. att1.name.uri == att2.name.uri) {
  707. return att2.name;
  708. }
  709. }
  710. }
  711. }
  712. // If the list is large check duplicates using a hash table.
  713. else {
  714. // We don't want this table view to be read if someone calls
  715. // addAttribute so we invalidate it up front.
  716. fIsTableViewConsistent = false;
  717. prepareTableView();
  718. Attribute attr;
  719. int bucket;
  720. for (int i = fLength - 1; i >= 0; --i) {
  721. attr = fAttributes[i];
  722. bucket = getTableViewBucket(attr.name.localpart, attr.name.uri);
  723. // The chain is stale.
  724. // This must be a unique attribute.
  725. if (fAttributeTableViewChainState[bucket] != fLargeCount) {
  726. fAttributeTableViewChainState[bucket] = fLargeCount;
  727. attr.next = null;
  728. fAttributeTableView[bucket] = attr;
  729. }
  730. // This chain is active.
  731. // We need to check if any of the attributes has the same name.
  732. else {
  733. // Search the table.
  734. Attribute found = fAttributeTableView[bucket];
  735. while (found != null) {
  736. if (found.name.localpart == attr.name.localpart &&
  737. found.name.uri == attr.name.uri) {
  738. return attr.name;
  739. }
  740. found = found.next;
  741. }
  742. // Update table view
  743. attr.next = fAttributeTableView[bucket];
  744. fAttributeTableView[bucket] = attr;
  745. }
  746. }
  747. }
  748. return null;
  749. }
  750. /**
  751. * Look up the index of an attribute by Namespace name.
  752. * <p>
  753. * <strong>Note:</strong>
  754. * This method uses reference comparison, and thus should
  755. * only be used internally. We cannot use this method in any
  756. * code exposed to users as they may not pass in unique strings.
  757. *
  758. * @param uri The Namespace URI, or null if
  759. * the name has no Namespace URI.
  760. * @param localName The attribute's local name.
  761. * @return The index of the attribute, or -1 if it does not
  762. * appear in the list.
  763. */
  764. public int getIndexFast(String uri, String localPart) {
  765. for (int i = 0; i < fLength; ++i) {
  766. Attribute attribute = fAttributes[i];
  767. if (attribute.name.localpart == localPart &&
  768. attribute.name.uri == uri) {
  769. return i;
  770. }
  771. }
  772. return -1;
  773. } // getIndexFast(String,String):int
  774. /**
  775. * Returns the value passed in or NMTOKEN if it's an enumerated type.
  776. *
  777. * @param type attribute type
  778. * @return the value passed in or NMTOKEN if it's an enumerated type.
  779. */
  780. private String getReportableType(String type) {
  781. if (type.charAt(0) == '(') {
  782. return "NMTOKEN";
  783. }
  784. return type;
  785. }
  786. /**
  787. * Returns the position in the table view
  788. * where the given attribute name would be hashed.
  789. *
  790. * @param qname the attribute name
  791. * @return the position in the table view where the given attribute
  792. * would be hashed
  793. */
  794. protected int getTableViewBucket(String qname) {
  795. return (qname.hashCode() & 0x7FFFFFFF) % fTableViewBuckets;
  796. }
  797. /**
  798. * Returns the position in the table view
  799. * where the given attribute name would be hashed.
  800. *
  801. * @param localpart the local part of the attribute
  802. * @param uri the namespace name of the attribute
  803. * @return the position in the table view where the given attribute
  804. * would be hashed
  805. */
  806. protected int getTableViewBucket(String localpart, String uri) {
  807. if (uri == null) {
  808. return (localpart.hashCode() & 0x7FFFFFFF) % fTableViewBuckets;
  809. }
  810. else {
  811. return ((localpart.hashCode() + uri.hashCode())
  812. & 0x7FFFFFFF) % fTableViewBuckets;
  813. }
  814. }
  815. /**
  816. * Purges all elements from the table view.
  817. */
  818. protected void cleanTableView() {
  819. if (++fLargeCount < 0) {
  820. // Overflow. We actually need to visit the chain state array.
  821. if (fAttributeTableViewChainState != null) {
  822. for (int i = fTableViewBuckets - 1; i >= 0; --i) {
  823. fAttributeTableViewChainState[i] = 0;
  824. }
  825. }
  826. fLargeCount = 1;
  827. }
  828. }
  829. /**
  830. * Prepares the table view of the attributes list for use.
  831. */
  832. protected void prepareTableView() {
  833. if (fAttributeTableView == null) {
  834. fAttributeTableView = new Attribute[fTableViewBuckets];
  835. fAttributeTableViewChainState = new int[fTableViewBuckets];
  836. }
  837. else {
  838. cleanTableView();
  839. }
  840. }
  841. /**
  842. * Prepares the table view of the attributes list for use,
  843. * and populates it with the attributes which have been
  844. * previously read.
  845. */
  846. protected void prepareAndPopulateTableView() {
  847. prepareTableView();
  848. // Need to populate the hash table with the attributes we've scanned so far.
  849. Attribute attr;
  850. int bucket;
  851. for (int i = 0; i < fLength; ++i) {
  852. attr = fAttributes[i];
  853. bucket = getTableViewBucket(attr.name.rawname);
  854. if (fAttributeTableViewChainState[bucket] != fLargeCount) {
  855. fAttributeTableViewChainState[bucket] = fLargeCount;
  856. attr.next = null;
  857. fAttributeTableView[bucket] = attr;
  858. }
  859. else {
  860. // Update table view
  861. attr.next = fAttributeTableView[bucket];
  862. fAttributeTableView[bucket] = attr;
  863. }
  864. }
  865. }
  866. /**
  867. * Returns the prefix of the attribute at the specified index.
  868. *
  869. * @param index The index of the attribute.
  870. */
  871. public String getPrefix(int index) {
  872. if (index < 0 || index >= fLength) {
  873. return null;
  874. }
  875. String prefix = fAttributes[index].name.prefix;
  876. // REVISIT: The empty string is not entered in the symbol table!
  877. return prefix != null ? prefix : "";
  878. } // getPrefix(int):String
  879. /**
  880. * Look up an attribute's Namespace URI by index.
  881. *
  882. * @param index The attribute index (zero-based).
  883. * @return The Namespace URI
  884. * @see #getLength
  885. */
  886. public String getURI(int index) {
  887. if (index < 0 || index >= fLength) {
  888. return null;
  889. }
  890. String uri = fAttributes[index].name.uri;
  891. return uri;
  892. } // getURI(int):String
  893. /**
  894. * Look up an attribute's value by Namespace name.
  895. *
  896. * <p>See {@link #getValue(int) getValue(int)} for a description
  897. * of the possible values.</p>
  898. *
  899. * @param uri The Namespace URI, or null if the
  900. * @param localName The local name of the attribute.
  901. * @return The attribute value as a string, or null if the
  902. * attribute is not in the list.
  903. */
  904. public String getValue(String uri, String localName) {
  905. int index = getIndex(uri, localName);
  906. return index != -1 ? getValue(index) : null;
  907. } // getValue(String,String):String
  908. /**
  909. * Look up an augmentations by Namespace name.
  910. *
  911. * @param uri The Namespace URI, or null if the
  912. * @param localName The local name of the attribute.
  913. * @return Augmentations
  914. */
  915. public Augmentations getAugmentations (String uri, String localName) {
  916. int index = getIndex(uri, localName);
  917. return index != -1 ? fAttributes[index].augs : null;
  918. }
  919. /**
  920. * Look up an augmentation by XML 1.0 qualified name.
  921. * <p>
  922. *
  923. * @param qName The XML 1.0 qualified name.
  924. *
  925. * @return Augmentations
  926. *
  927. */
  928. public Augmentations getAugmentations(String qName){
  929. int index = getIndex(qName);
  930. return index != -1 ? fAttributes[index].augs : null;
  931. }
  932. /**
  933. * Look up an augmentations by attributes index.
  934. *
  935. * @param attributeIndex The attribute index.
  936. * @return Augmentations
  937. */
  938. public Augmentations getAugmentations (int attributeIndex){
  939. if (attributeIndex < 0 || attributeIndex >= fLength) {
  940. return null;
  941. }
  942. return fAttributes[attributeIndex].augs;
  943. }
  944. /**
  945. * Sets the augmentations of the attribute at the specified index.
  946. *
  947. * @param attrIndex The attribute index.
  948. * @param augs The augmentations.
  949. */
  950. public void setAugmentations(int attrIndex, Augmentations augs) {
  951. fAttributes[attrIndex].augs = augs;
  952. }
  953. /**
  954. * Sets the uri of the attribute at the specified index.
  955. *
  956. * @param attrIndex The attribute index.
  957. * @param uri Namespace uri
  958. */
  959. public void setURI(int attrIndex, String uri) {
  960. fAttributes[attrIndex].name.uri = uri;
  961. } // getURI(int,QName)
  962. //
  963. // Classes
  964. //
  965. /**
  966. * Attribute information.
  967. *
  968. * @author Andy Clark, IBM
  969. */
  970. static class Attribute {
  971. //
  972. // Data
  973. //
  974. // basic info
  975. /** Name. */
  976. public QName name = new QName();
  977. /** Type. */
  978. public String type;
  979. /** Value. */
  980. public String value;
  981. /** Non-normalized value. */
  982. public String nonNormalizedValue;
  983. /** Specified. */
  984. public boolean specified;
  985. /**
  986. * Augmentations information for this attribute.
  987. * XMLAttributes has no knowledge if any augmentations
  988. * were attached to Augmentations.
  989. */
  990. public Augmentations augs = new AugmentationsImpl();
  991. // Additional data for attribute table view
  992. /** Pointer to the next attribute in the chain. **/
  993. public Attribute next;
  994. } // class Attribute
  995. } // class XMLAttributesImpl