1. // AttributesImpl.java - default implementation of Attributes.
  2. // http://www.saxproject.org
  3. // Written by David Megginson
  4. // NO WARRANTY! This class is in the public domain.
  5. // $Id: AttributesImpl.java,v 1.1.24.1 2004/05/01 08:34:45 jsuttor Exp $
  6. package org.xml.sax.helpers;
  7. import org.xml.sax.Attributes;
  8. /**
  9. * Default implementation of the Attributes interface.
  10. *
  11. * <blockquote>
  12. * <em>This module, both source code and documentation, is in the
  13. * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
  14. * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
  15. * for further information.
  16. * </blockquote>
  17. *
  18. * <p>This class provides a default implementation of the SAX2
  19. * {@link org.xml.sax.Attributes Attributes} interface, with the
  20. * addition of manipulators so that the list can be modified or
  21. * reused.</p>
  22. *
  23. * <p>There are two typical uses of this class:</p>
  24. *
  25. * <ol>
  26. * <li>to take a persistent snapshot of an Attributes object
  27. * in a {@link org.xml.sax.ContentHandler#startElement startElement} event; or</li>
  28. * <li>to construct or modify an Attributes object in a SAX2 driver or filter.</li>
  29. * </ol>
  30. *
  31. * <p>This class replaces the now-deprecated SAX1 {@link
  32. * org.xml.sax.helpers.AttributeListImpl AttributeListImpl}
  33. * class; in addition to supporting the updated Attributes
  34. * interface rather than the deprecated {@link org.xml.sax.AttributeList
  35. * AttributeList} interface, it also includes a much more efficient
  36. * implementation using a single array rather than a set of Vectors.</p>
  37. *
  38. * @since SAX 2.0
  39. * @author David Megginson
  40. * @version 2.0.1 (sax2r2)
  41. */
  42. public class AttributesImpl implements Attributes
  43. {
  44. ////////////////////////////////////////////////////////////////////
  45. // Constructors.
  46. ////////////////////////////////////////////////////////////////////
  47. /**
  48. * Construct a new, empty AttributesImpl object.
  49. */
  50. public AttributesImpl ()
  51. {
  52. length = 0;
  53. data = null;
  54. }
  55. /**
  56. * Copy an existing Attributes object.
  57. *
  58. * <p>This constructor is especially useful inside a
  59. * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
  60. *
  61. * @param atts The existing Attributes object.
  62. */
  63. public AttributesImpl (Attributes atts)
  64. {
  65. setAttributes(atts);
  66. }
  67. ////////////////////////////////////////////////////////////////////
  68. // Implementation of org.xml.sax.Attributes.
  69. ////////////////////////////////////////////////////////////////////
  70. /**
  71. * Return the number of attributes in the list.
  72. *
  73. * @return The number of attributes in the list.
  74. * @see org.xml.sax.Attributes#getLength
  75. */
  76. public int getLength ()
  77. {
  78. return length;
  79. }
  80. /**
  81. * Return an attribute's Namespace URI.
  82. *
  83. * @param index The attribute's index (zero-based).
  84. * @return The Namespace URI, the empty string if none is
  85. * available, or null if the index is out of range.
  86. * @see org.xml.sax.Attributes#getURI
  87. */
  88. public String getURI (int index)
  89. {
  90. if (index >= 0 && index < length) {
  91. return data[index*5];
  92. } else {
  93. return null;
  94. }
  95. }
  96. /**
  97. * Return an attribute's local name.
  98. *
  99. * @param index The attribute's index (zero-based).
  100. * @return The attribute's local name, the empty string if
  101. * none is available, or null if the index if out of range.
  102. * @see org.xml.sax.Attributes#getLocalName
  103. */
  104. public String getLocalName (int index)
  105. {
  106. if (index >= 0 && index < length) {
  107. return data[index*5+1];
  108. } else {
  109. return null;
  110. }
  111. }
  112. /**
  113. * Return an attribute's qualified (prefixed) name.
  114. *
  115. * @param index The attribute's index (zero-based).
  116. * @return The attribute's qualified name, the empty string if
  117. * none is available, or null if the index is out of bounds.
  118. * @see org.xml.sax.Attributes#getQName
  119. */
  120. public String getQName (int index)
  121. {
  122. if (index >= 0 && index < length) {
  123. return data[index*5+2];
  124. } else {
  125. return null;
  126. }
  127. }
  128. /**
  129. * Return an attribute's type by index.
  130. *
  131. * @param index The attribute's index (zero-based).
  132. * @return The attribute's type, "CDATA" if the type is unknown, or null
  133. * if the index is out of bounds.
  134. * @see org.xml.sax.Attributes#getType(int)
  135. */
  136. public String getType (int index)
  137. {
  138. if (index >= 0 && index < length) {
  139. return data[index*5+3];
  140. } else {
  141. return null;
  142. }
  143. }
  144. /**
  145. * Return an attribute's value by index.
  146. *
  147. * @param index The attribute's index (zero-based).
  148. * @return The attribute's value or null if the index is out of bounds.
  149. * @see org.xml.sax.Attributes#getValue(int)
  150. */
  151. public String getValue (int index)
  152. {
  153. if (index >= 0 && index < length) {
  154. return data[index*5+4];
  155. } else {
  156. return null;
  157. }
  158. }
  159. /**
  160. * Look up an attribute's index by Namespace name.
  161. *
  162. * <p>In many cases, it will be more efficient to look up the name once and
  163. * use the index query methods rather than using the name query methods
  164. * repeatedly.</p>
  165. *
  166. * @param uri The attribute's Namespace URI, or the empty
  167. * string if none is available.
  168. * @param localName The attribute's local name.
  169. * @return The attribute's index, or -1 if none matches.
  170. * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
  171. */
  172. public int getIndex (String uri, String localName)
  173. {
  174. int max = length * 5;
  175. for (int i = 0; i < max; i += 5) {
  176. if (data[i].equals(uri) && data[i+1].equals(localName)) {
  177. return i / 5;
  178. }
  179. }
  180. return -1;
  181. }
  182. /**
  183. * Look up an attribute's index by qualified (prefixed) name.
  184. *
  185. * @param qName The qualified name.
  186. * @return The attribute's index, or -1 if none matches.
  187. * @see org.xml.sax.Attributes#getIndex(java.lang.String)
  188. */
  189. public int getIndex (String qName)
  190. {
  191. int max = length * 5;
  192. for (int i = 0; i < max; i += 5) {
  193. if (data[i+2].equals(qName)) {
  194. return i / 5;
  195. }
  196. }
  197. return -1;
  198. }
  199. /**
  200. * Look up an attribute's type by Namespace-qualified name.
  201. *
  202. * @param uri The Namespace URI, or the empty string for a name
  203. * with no explicit Namespace URI.
  204. * @param localName The local name.
  205. * @return The attribute's type, or null if there is no
  206. * matching attribute.
  207. * @see org.xml.sax.Attributes#getType(java.lang.String,java.lang.String)
  208. */
  209. public String getType (String uri, String localName)
  210. {
  211. int max = length * 5;
  212. for (int i = 0; i < max; i += 5) {
  213. if (data[i].equals(uri) && data[i+1].equals(localName)) {
  214. return data[i+3];
  215. }
  216. }
  217. return null;
  218. }
  219. /**
  220. * Look up an attribute's type by qualified (prefixed) name.
  221. *
  222. * @param qName The qualified name.
  223. * @return The attribute's type, or null if there is no
  224. * matching attribute.
  225. * @see org.xml.sax.Attributes#getType(java.lang.String)
  226. */
  227. public String getType (String qName)
  228. {
  229. int max = length * 5;
  230. for (int i = 0; i < max; i += 5) {
  231. if (data[i+2].equals(qName)) {
  232. return data[i+3];
  233. }
  234. }
  235. return null;
  236. }
  237. /**
  238. * Look up an attribute's value by Namespace-qualified name.
  239. *
  240. * @param uri The Namespace URI, or the empty string for a name
  241. * with no explicit Namespace URI.
  242. * @param localName The local name.
  243. * @return The attribute's value, or null if there is no
  244. * matching attribute.
  245. * @see org.xml.sax.Attributes#getValue(java.lang.String,java.lang.String)
  246. */
  247. public String getValue (String uri, String localName)
  248. {
  249. int max = length * 5;
  250. for (int i = 0; i < max; i += 5) {
  251. if (data[i].equals(uri) && data[i+1].equals(localName)) {
  252. return data[i+4];
  253. }
  254. }
  255. return null;
  256. }
  257. /**
  258. * Look up an attribute's value by qualified (prefixed) name.
  259. *
  260. * @param qName The qualified name.
  261. * @return The attribute's value, or null if there is no
  262. * matching attribute.
  263. * @see org.xml.sax.Attributes#getValue(java.lang.String)
  264. */
  265. public String getValue (String qName)
  266. {
  267. int max = length * 5;
  268. for (int i = 0; i < max; i += 5) {
  269. if (data[i+2].equals(qName)) {
  270. return data[i+4];
  271. }
  272. }
  273. return null;
  274. }
  275. ////////////////////////////////////////////////////////////////////
  276. // Manipulators.
  277. ////////////////////////////////////////////////////////////////////
  278. /**
  279. * Clear the attribute list for reuse.
  280. *
  281. * <p>Note that little memory is freed by this call:
  282. * the current array is kept so it can be
  283. * reused.</p>
  284. */
  285. public void clear ()
  286. {
  287. if (data != null) {
  288. for (int i = 0; i < (length * 5); i++)
  289. data [i] = null;
  290. }
  291. length = 0;
  292. }
  293. /**
  294. * Copy an entire Attributes object.
  295. *
  296. * <p>It may be more efficient to reuse an existing object
  297. * rather than constantly allocating new ones.</p>
  298. *
  299. * @param atts The attributes to copy.
  300. */
  301. public void setAttributes (Attributes atts)
  302. {
  303. clear();
  304. length = atts.getLength();
  305. if (length > 0) {
  306. data = new String[length*5];
  307. for (int i = 0; i < length; i++) {
  308. data[i*5] = atts.getURI(i);
  309. data[i*5+1] = atts.getLocalName(i);
  310. data[i*5+2] = atts.getQName(i);
  311. data[i*5+3] = atts.getType(i);
  312. data[i*5+4] = atts.getValue(i);
  313. }
  314. }
  315. }
  316. /**
  317. * Add an attribute to the end of the list.
  318. *
  319. * <p>For the sake of speed, this method does no checking
  320. * to see if the attribute is already in the list: that is
  321. * the responsibility of the application.</p>
  322. *
  323. * @param uri The Namespace URI, or the empty string if
  324. * none is available or Namespace processing is not
  325. * being performed.
  326. * @param localName The local name, or the empty string if
  327. * Namespace processing is not being performed.
  328. * @param qName The qualified (prefixed) name, or the empty string
  329. * if qualified names are not available.
  330. * @param type The attribute type as a string.
  331. * @param value The attribute value.
  332. */
  333. public void addAttribute (String uri, String localName, String qName,
  334. String type, String value)
  335. {
  336. ensureCapacity(length+1);
  337. data[length*5] = uri;
  338. data[length*5+1] = localName;
  339. data[length*5+2] = qName;
  340. data[length*5+3] = type;
  341. data[length*5+4] = value;
  342. length++;
  343. }
  344. /**
  345. * Set an attribute in the list.
  346. *
  347. * <p>For the sake of speed, this method does no checking
  348. * for name conflicts or well-formedness: such checks are the
  349. * responsibility of the application.</p>
  350. *
  351. * @param index The index of the attribute (zero-based).
  352. * @param uri The Namespace URI, or the empty string if
  353. * none is available or Namespace processing is not
  354. * being performed.
  355. * @param localName The local name, or the empty string if
  356. * Namespace processing is not being performed.
  357. * @param qName The qualified name, or the empty string
  358. * if qualified names are not available.
  359. * @param type The attribute type as a string.
  360. * @param value The attribute value.
  361. * @exception java.lang.ArrayIndexOutOfBoundsException When the
  362. * supplied index does not point to an attribute
  363. * in the list.
  364. */
  365. public void setAttribute (int index, String uri, String localName,
  366. String qName, String type, String value)
  367. {
  368. if (index >= 0 && index < length) {
  369. data[index*5] = uri;
  370. data[index*5+1] = localName;
  371. data[index*5+2] = qName;
  372. data[index*5+3] = type;
  373. data[index*5+4] = value;
  374. } else {
  375. badIndex(index);
  376. }
  377. }
  378. /**
  379. * Remove an attribute from the list.
  380. *
  381. * @param index The index of the attribute (zero-based).
  382. * @exception java.lang.ArrayIndexOutOfBoundsException When the
  383. * supplied index does not point to an attribute
  384. * in the list.
  385. */
  386. public void removeAttribute (int index)
  387. {
  388. if (index >= 0 && index < length) {
  389. if (index < length - 1) {
  390. System.arraycopy(data, (index+1)*5, data, index*5,
  391. (length-index-1)*5);
  392. }
  393. index = (length - 1) * 5;
  394. data [index++] = null;
  395. data [index++] = null;
  396. data [index++] = null;
  397. data [index++] = null;
  398. data [index] = null;
  399. length--;
  400. } else {
  401. badIndex(index);
  402. }
  403. }
  404. /**
  405. * Set the Namespace URI of a specific attribute.
  406. *
  407. * @param index The index of the attribute (zero-based).
  408. * @param uri The attribute's Namespace URI, or the empty
  409. * string for none.
  410. * @exception java.lang.ArrayIndexOutOfBoundsException When the
  411. * supplied index does not point to an attribute
  412. * in the list.
  413. */
  414. public void setURI (int index, String uri)
  415. {
  416. if (index >= 0 && index < length) {
  417. data[index*5] = uri;
  418. } else {
  419. badIndex(index);
  420. }
  421. }
  422. /**
  423. * Set the local name of a specific attribute.
  424. *
  425. * @param index The index of the attribute (zero-based).
  426. * @param localName The attribute's local name, or the empty
  427. * string for none.
  428. * @exception java.lang.ArrayIndexOutOfBoundsException When the
  429. * supplied index does not point to an attribute
  430. * in the list.
  431. */
  432. public void setLocalName (int index, String localName)
  433. {
  434. if (index >= 0 && index < length) {
  435. data[index*5+1] = localName;
  436. } else {
  437. badIndex(index);
  438. }
  439. }
  440. /**
  441. * Set the qualified name of a specific attribute.
  442. *
  443. * @param index The index of the attribute (zero-based).
  444. * @param qName The attribute's qualified name, or the empty
  445. * string for none.
  446. * @exception java.lang.ArrayIndexOutOfBoundsException When the
  447. * supplied index does not point to an attribute
  448. * in the list.
  449. */
  450. public void setQName (int index, String qName)
  451. {
  452. if (index >= 0 && index < length) {
  453. data[index*5+2] = qName;
  454. } else {
  455. badIndex(index);
  456. }
  457. }
  458. /**
  459. * Set the type of a specific attribute.
  460. *
  461. * @param index The index of the attribute (zero-based).
  462. * @param type The attribute's type.
  463. * @exception java.lang.ArrayIndexOutOfBoundsException When the
  464. * supplied index does not point to an attribute
  465. * in the list.
  466. */
  467. public void setType (int index, String type)
  468. {
  469. if (index >= 0 && index < length) {
  470. data[index*5+3] = type;
  471. } else {
  472. badIndex(index);
  473. }
  474. }
  475. /**
  476. * Set the value of a specific attribute.
  477. *
  478. * @param index The index of the attribute (zero-based).
  479. * @param value The attribute's value.
  480. * @exception java.lang.ArrayIndexOutOfBoundsException When the
  481. * supplied index does not point to an attribute
  482. * in the list.
  483. */
  484. public void setValue (int index, String value)
  485. {
  486. if (index >= 0 && index < length) {
  487. data[index*5+4] = value;
  488. } else {
  489. badIndex(index);
  490. }
  491. }
  492. ////////////////////////////////////////////////////////////////////
  493. // Internal methods.
  494. ////////////////////////////////////////////////////////////////////
  495. /**
  496. * Ensure the internal array's capacity.
  497. *
  498. * @param n The minimum number of attributes that the array must
  499. * be able to hold.
  500. */
  501. private void ensureCapacity (int n) {
  502. if (n <= 0) {
  503. return;
  504. }
  505. int max;
  506. if (data == null || data.length == 0) {
  507. max = 25;
  508. }
  509. else if (data.length >= n * 5) {
  510. return;
  511. }
  512. else {
  513. max = data.length;
  514. }
  515. while (max < n * 5) {
  516. max *= 2;
  517. }
  518. String newData[] = new String[max];
  519. if (length > 0) {
  520. System.arraycopy(data, 0, newData, 0, length*5);
  521. }
  522. data = newData;
  523. }
  524. /**
  525. * Report a bad array index in a manipulator.
  526. *
  527. * @param index The index to report.
  528. * @exception java.lang.ArrayIndexOutOfBoundsException Always.
  529. */
  530. private void badIndex (int index)
  531. throws ArrayIndexOutOfBoundsException
  532. {
  533. String msg =
  534. "Attempt to modify attribute at illegal index: " + index;
  535. throw new ArrayIndexOutOfBoundsException(msg);
  536. }
  537. ////////////////////////////////////////////////////////////////////
  538. // Internal state.
  539. ////////////////////////////////////////////////////////////////////
  540. int length;
  541. String data [];
  542. }
  543. // end of AttributesImpl.java