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