You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

QueryContainer.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.data.util;
  5. import java.sql.Connection;
  6. import java.sql.ResultSet;
  7. import java.sql.ResultSetMetaData;
  8. import java.sql.SQLException;
  9. import java.sql.Statement;
  10. import java.util.ArrayList;
  11. import java.util.Collection;
  12. import java.util.Collections;
  13. import java.util.HashMap;
  14. import com.itmill.toolkit.data.Container;
  15. import com.itmill.toolkit.data.Item;
  16. import com.itmill.toolkit.data.Property;
  17. /**
  18. * <p>
  19. * The <code>QueryContainer</code> is the specialized form of Container which
  20. * is Ordered and Indexed. This is used to represent the contents of relational
  21. * database tables accessed through the JDBC Connection in the Toolkit Table.
  22. * This creates Items based on the queryStatement provided to the container.
  23. * </p>
  24. *
  25. * <p>
  26. * The <code>QueryContainer</code> can be visualized as a representation of a
  27. * relational database table.Each Item in the container represents the row
  28. * fetched by the query.All cells in a column have same data type and the data
  29. * type information is retrieved from the metadata of the resultset.
  30. * </p>
  31. *
  32. * <p>
  33. * Note : If data in the tables gets modified, Container will not get reflected
  34. * with the updates, we have to explicity invoke QueryContainer.refresh method.
  35. * {@link com.itmill.toolkit.data.util.QueryContainer#refresh() refresh()}
  36. * </p>
  37. *
  38. * @see com.itmill.toolkit.data.Container
  39. *
  40. * @author IT Mill Ltd.
  41. * @version
  42. * @since 4.0
  43. */
  44. public class QueryContainer implements Container, Container.Ordered,
  45. Container.Indexed {
  46. // default ResultSet type
  47. public static final int DEFAULT_RESULTSET_TYPE = ResultSet.TYPE_SCROLL_INSENSITIVE;
  48. // default ResultSet concurrency
  49. public static final int DEFAULT_RESULTSET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY;
  50. private int resultSetType = DEFAULT_RESULTSET_TYPE;
  51. private int resultSetConcurrency = DEFAULT_RESULTSET_CONCURRENCY;
  52. private final String queryStatement;
  53. private final Connection connection;
  54. private ResultSet result;
  55. private Collection propertyIds;
  56. private final HashMap propertyTypes = new HashMap();
  57. private int size = -1;
  58. private Statement statement;
  59. /**
  60. * Constructs new <code>QueryContainer</code> with the specified
  61. * <code>queryStatement</code>.
  62. *
  63. * @param queryStatement
  64. * Database query
  65. * @param connection
  66. * Connection object
  67. * @param resultSetType
  68. * @param resultSetConcurrency
  69. * @throws SQLException
  70. * when database operation fails
  71. */
  72. public QueryContainer(String queryStatement, Connection connection,
  73. int resultSetType, int resultSetConcurrency) throws SQLException {
  74. this.queryStatement = queryStatement;
  75. this.connection = connection;
  76. this.resultSetType = resultSetType;
  77. this.resultSetConcurrency = resultSetConcurrency;
  78. init();
  79. }
  80. /**
  81. * Constructs new <code>QueryContainer</code> with the specified
  82. * queryStatement using the default resultset type and default resultset
  83. * concurrency.
  84. *
  85. * @param queryStatement
  86. * Database query
  87. * @param connection
  88. * Connection object
  89. * @see QueryContainer#DEFAULT_RESULTSET_TYPE
  90. * @see QueryContainer#DEFAULT_RESULTSET_CONCURRENCY
  91. * @throws SQLException
  92. * when database operation fails
  93. */
  94. public QueryContainer(String queryStatement, Connection connection)
  95. throws SQLException {
  96. this(queryStatement, connection, DEFAULT_RESULTSET_TYPE,
  97. DEFAULT_RESULTSET_CONCURRENCY);
  98. }
  99. /**
  100. * Fills the Container with the items and properties. Invoked by the
  101. * constructor.
  102. *
  103. * @throws SQLException
  104. * when parameter initialization fails.
  105. * @see QueryContainer#QueryContainer(String, Connection, int, int).
  106. */
  107. private void init() throws SQLException {
  108. refresh();
  109. ResultSetMetaData metadata;
  110. metadata = result.getMetaData();
  111. final int count = metadata.getColumnCount();
  112. final ArrayList list = new ArrayList(count);
  113. for (int i = 1; i <= count; i++) {
  114. final String columnName = metadata.getColumnName(i);
  115. list.add(columnName);
  116. final Property p = getContainerProperty(new Integer(1), columnName);
  117. propertyTypes.put(columnName, p == null ? Object.class : p
  118. .getType());
  119. }
  120. propertyIds = Collections.unmodifiableCollection(list);
  121. }
  122. /**
  123. * <p>
  124. * Restores items in the container. This method will update the latest data
  125. * to the container.
  126. * </p>
  127. * Note: This method should be used to update the container with the latest
  128. * items.
  129. *
  130. * @throws SQLException
  131. * when database operation fails
  132. *
  133. */
  134. public void refresh() throws SQLException {
  135. close();
  136. statement = connection.createStatement(resultSetType,
  137. resultSetConcurrency);
  138. result = statement.executeQuery(queryStatement);
  139. result.last();
  140. size = result.getRow();
  141. }
  142. /**
  143. * Releases and nullifies the <code>statement</code>.
  144. *
  145. * @throws SQLException
  146. * when database operation fails
  147. */
  148. public void close() throws SQLException {
  149. if (statement != null) {
  150. statement.close();
  151. }
  152. statement = null;
  153. }
  154. /**
  155. * Gets the Item with the given Item ID from the Container.
  156. *
  157. * @param id
  158. * ID of the Item to retrieve
  159. * @return Item Id.
  160. */
  161. public Item getItem(Object id) {
  162. return new Row(id);
  163. }
  164. /**
  165. * Gets the collection of propertyId from the Container.
  166. *
  167. * @return Collection of Property ID.
  168. */
  169. public Collection getContainerPropertyIds() {
  170. return propertyIds;
  171. }
  172. /**
  173. * Gets an collection of all the item IDs in the container.
  174. *
  175. * @return collection of Item IDs
  176. */
  177. public Collection getItemIds() {
  178. final Collection c = new ArrayList(size);
  179. for (int i = 1; i <= size; i++) {
  180. c.add(new Integer(i));
  181. }
  182. return c;
  183. }
  184. /**
  185. * Gets the property identified by the given itemId and propertyId from the
  186. * container. If the container does not contain the property
  187. * <code>null</code> is returned.
  188. *
  189. * @param itemId
  190. * ID of the Item which contains the Property
  191. * @param propertyId
  192. * ID of the Property to retrieve
  193. *
  194. * @return Property with the given ID if exists; <code>null</code>
  195. * otherwise.
  196. */
  197. public synchronized Property getContainerProperty(Object itemId,
  198. Object propertyId) {
  199. if (!(itemId instanceof Integer && propertyId instanceof String)) {
  200. return null;
  201. }
  202. Object value;
  203. try {
  204. result.absolute(((Integer) itemId).intValue());
  205. value = result.getObject((String) propertyId);
  206. } catch (final Exception e) {
  207. return null;
  208. }
  209. // Handle also null values from the database
  210. return new ObjectProperty(value != null ? value : new String(""));
  211. }
  212. /**
  213. * Gets the data type of all properties identified by the given type ID.
  214. *
  215. * @param id
  216. * ID identifying the Properties
  217. *
  218. * @return data type of the Properties
  219. */
  220. public Class getType(Object id) {
  221. return (Class) propertyTypes.get(id);
  222. }
  223. /**
  224. * Gets the number of items in the container.
  225. *
  226. * @return the number of items in the container.
  227. */
  228. public int size() {
  229. return size;
  230. }
  231. /**
  232. * Tests if the list contains the specified Item.
  233. *
  234. * @param id
  235. * ID the of Item to be tested.
  236. * @return <code>true</code> if given id is in the container;
  237. * <code>false</code> otherwise.
  238. */
  239. public boolean containsId(Object id) {
  240. if (!(id instanceof Integer)) {
  241. return false;
  242. }
  243. final int i = ((Integer) id).intValue();
  244. if (i < 1) {
  245. return false;
  246. }
  247. if (i > size) {
  248. return false;
  249. }
  250. return true;
  251. }
  252. /**
  253. * Creates new Item with the given ID into the Container.
  254. *
  255. * @param itemId
  256. * ID of the Item to be created.
  257. *
  258. * @return Created new Item, or <code>null</code> if it fails.
  259. *
  260. * @throws UnsupportedOperationException
  261. * if the addItem method is not supported.
  262. */
  263. public Item addItem(Object itemId) throws UnsupportedOperationException {
  264. throw new UnsupportedOperationException();
  265. }
  266. /**
  267. * Creates a new Item into the Container, and assign it an ID.
  268. *
  269. * @return ID of the newly created Item, or <code>null</code> if it fails.
  270. * @throws UnsupportedOperationException
  271. * if the addItem method is not supported.
  272. */
  273. public Object addItem() throws UnsupportedOperationException {
  274. throw new UnsupportedOperationException();
  275. }
  276. /**
  277. * Removes the Item identified by ItemId from the Container.
  278. *
  279. * @param itemId
  280. * ID of the Item to remove.
  281. * @return <code>true</code> if the operation succeeded;
  282. * <code>false</code> otherwise.
  283. * @throws UnsupportedOperationException
  284. * if the removeItem method is not supported.
  285. */
  286. public boolean removeItem(Object itemId)
  287. throws UnsupportedOperationException {
  288. throw new UnsupportedOperationException();
  289. }
  290. /**
  291. * Adds new Property to all Items in the Container.
  292. *
  293. * @param propertyId
  294. * ID of the Property
  295. * @param type
  296. * Data type of the new Property
  297. * @param defaultValue
  298. * The value all created Properties are initialized to.
  299. * @return <code>true</code> if the operation succeeded;
  300. * <code>false</code> otherwise.
  301. * @throws UnsupportedOperationException
  302. * if the addContainerProperty method is not supported.
  303. */
  304. public boolean addContainerProperty(Object propertyId, Class type,
  305. Object defaultValue) throws UnsupportedOperationException {
  306. throw new UnsupportedOperationException();
  307. }
  308. /**
  309. * Removes a Property specified by the given Property ID from the Container.
  310. *
  311. * @param propertyId
  312. * ID of the Property to remove
  313. * @return <code>true</code> if the operation succeeded;
  314. * <code>false</code> otherwise.
  315. * @throws UnsupportedOperationException
  316. * if the removeContainerProperty method is not supported.
  317. */
  318. public boolean removeContainerProperty(Object propertyId)
  319. throws UnsupportedOperationException {
  320. throw new UnsupportedOperationException();
  321. }
  322. /**
  323. * Removes all Items from the Container.
  324. *
  325. * @return <code>true</code> if the operation succeeded;
  326. * <code>false</code> otherwise.
  327. * @throws UnsupportedOperationException
  328. * if the removeAllItems method is not supported.
  329. */
  330. public boolean removeAllItems() throws UnsupportedOperationException {
  331. throw new UnsupportedOperationException();
  332. }
  333. /**
  334. * Adds new item after the given item.
  335. *
  336. * @param previousItemId
  337. * Id of the previous item in ordered container.
  338. * @param newItemId
  339. * Id of the new item to be added.
  340. * @return Returns new item or <code>null</code> if the operation fails.
  341. * @throws UnsupportedOperationException
  342. * if the addItemAfter method is not supported.
  343. */
  344. public Item addItemAfter(Object previousItemId, Object newItemId)
  345. throws UnsupportedOperationException {
  346. throw new UnsupportedOperationException();
  347. }
  348. /**
  349. * Adds new item after the given item.
  350. *
  351. * @param previousItemId
  352. * Id of the previous item in ordered container.
  353. * @return Returns item id created new item or <code>null</code> if the
  354. * operation fails.
  355. * @throws UnsupportedOperationException
  356. * if the addItemAfter method is not supported.
  357. */
  358. public Object addItemAfter(Object previousItemId)
  359. throws UnsupportedOperationException {
  360. throw new UnsupportedOperationException();
  361. }
  362. /**
  363. * Returns id of first item in the Container.
  364. *
  365. * @return ID of the first Item in the list.
  366. */
  367. public Object firstItemId() {
  368. if (size < 1) {
  369. return null;
  370. }
  371. return new Integer(1);
  372. }
  373. /**
  374. * Returns <code>true</code> if given id is first id at first index.
  375. *
  376. * @param id
  377. * ID of an Item in the Container.
  378. */
  379. public boolean isFirstId(Object id) {
  380. return size > 0 && (id instanceof Integer)
  381. && ((Integer) id).intValue() == 1;
  382. }
  383. /**
  384. * Returns <code>true</code> if given id is last id at last index.
  385. *
  386. * @param id
  387. * ID of an Item in the Container
  388. *
  389. */
  390. public boolean isLastId(Object id) {
  391. return size > 0 && (id instanceof Integer)
  392. && ((Integer) id).intValue() == size;
  393. }
  394. /**
  395. * Returns id of last item in the Container.
  396. *
  397. * @return ID of the last Item.
  398. */
  399. public Object lastItemId() {
  400. if (size < 1) {
  401. return null;
  402. }
  403. return new Integer(size);
  404. }
  405. /**
  406. * Returns id of next item in container at next index.
  407. *
  408. * @param id
  409. * ID of an Item in the Container.
  410. * @return ID of the next Item or null.
  411. */
  412. public Object nextItemId(Object id) {
  413. if (size < 1 || !(id instanceof Integer)) {
  414. return null;
  415. }
  416. final int i = ((Integer) id).intValue();
  417. if (i >= size) {
  418. return null;
  419. }
  420. return new Integer(i + 1);
  421. }
  422. /**
  423. * Returns id of previous item in container at previous index.
  424. *
  425. * @param id
  426. * ID of an Item in the Container.
  427. * @return ID of the previous Item or null.
  428. */
  429. public Object prevItemId(Object id) {
  430. if (size < 1 || !(id instanceof Integer)) {
  431. return null;
  432. }
  433. final int i = ((Integer) id).intValue();
  434. if (i <= 1) {
  435. return null;
  436. }
  437. return new Integer(i - 1);
  438. }
  439. /**
  440. * The <code>Row</code> class implements methods of Item.
  441. *
  442. * @author IT Mill Ltd.
  443. * @version
  444. * @since 4.0
  445. */
  446. class Row implements Item {
  447. Object id;
  448. private Row(Object rowId) {
  449. id = rowId;
  450. }
  451. /**
  452. * Adds the item property.
  453. *
  454. * @param id
  455. * ID of the new Property.
  456. * @param property
  457. * Property to be added and associated with ID.
  458. * @return <code>true</code> if the operation succeeded;
  459. * <code>false</code> otherwise.
  460. * @throws UnsupportedOperationException
  461. * if the addItemProperty method is not supported.
  462. */
  463. public boolean addItemProperty(Object id, Property property)
  464. throws UnsupportedOperationException {
  465. throw new UnsupportedOperationException();
  466. }
  467. /**
  468. * Gets the property corresponding to the given property ID stored in
  469. * the Item.
  470. *
  471. * @param propertyId
  472. * identifier of the Property to get
  473. * @return the Property with the given ID or <code>null</code>
  474. */
  475. public Property getItemProperty(Object propertyId) {
  476. return getContainerProperty(id, propertyId);
  477. }
  478. /**
  479. * Gets the collection of property IDs stored in the Item.
  480. *
  481. * @return unmodifiable collection containing IDs of the Properties
  482. * stored the Item.
  483. */
  484. public Collection getItemPropertyIds() {
  485. return propertyIds;
  486. }
  487. /**
  488. * Removes given item property.
  489. *
  490. * @param id
  491. * ID of the Property to be removed.
  492. * @return <code>true</code> if the item property is removed;
  493. * <code>false</code> otherwise.
  494. * @throws UnsupportedOperationException
  495. * if the removeItemProperty is not supported.
  496. */
  497. public boolean removeItemProperty(Object id)
  498. throws UnsupportedOperationException {
  499. throw new UnsupportedOperationException();
  500. }
  501. }
  502. /**
  503. * Closes the statement.
  504. *
  505. * @see #close()
  506. */
  507. public void finalize() {
  508. try {
  509. close();
  510. } catch (final SQLException ignored) {
  511. }
  512. }
  513. /**
  514. * Adds the given item at the position of given index.
  515. *
  516. * @param index
  517. * Index to add the new item.
  518. * @param newItemId
  519. * Id of the new item to be added.
  520. * @return new item or <code>null</code> if the operation fails.
  521. * @throws UnsupportedOperationException
  522. * if the addItemAt is not supported.
  523. */
  524. public Item addItemAt(int index, Object newItemId)
  525. throws UnsupportedOperationException {
  526. throw new UnsupportedOperationException();
  527. }
  528. /**
  529. * Adds item at the position of provided index in the container.
  530. *
  531. * @param index
  532. * Index to add the new item.
  533. * @return item id created new item or <code>null</code> if the operation
  534. * fails.
  535. *
  536. * @throws UnsupportedOperationException
  537. * if the addItemAt is not supported.
  538. */
  539. public Object addItemAt(int index) throws UnsupportedOperationException {
  540. throw new UnsupportedOperationException();
  541. }
  542. /**
  543. * Gets the Index id in the container.
  544. *
  545. * @param index
  546. * Index Id.
  547. * @return ID in the given index.
  548. */
  549. public Object getIdByIndex(int index) {
  550. if (size < 1 || index < 0 || index >= size) {
  551. return null;
  552. }
  553. return new Integer(index + 1);
  554. }
  555. /**
  556. * Gets the index of the Item corresponding to id in the container.
  557. *
  558. * @param id
  559. * ID of an Item in the Container
  560. * @return index of the Item, or -1 if the Container does not include the
  561. * Item
  562. */
  563. public int indexOfId(Object id) {
  564. if (size < 1 || !(id instanceof Integer)) {
  565. return -1;
  566. }
  567. final int i = ((Integer) id).intValue();
  568. if (i >= size || i < 1) {
  569. return -1;
  570. }
  571. return i - 1;
  572. }
  573. }