/* @ITMillApache2LicenseForJavaFiles@ */ package com.vaadin.data.util; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.Property; /** *

* The QueryContainer is the specialized form of Container which is * Ordered and Indexed. This is used to represent the contents of relational * database tables accessed through the JDBC Connection in the Vaadin Table. * This creates Items based on the queryStatement provided to the container. *

* *

* The QueryContainer can be visualized as a representation of a * relational database table.Each Item in the container represents the row * fetched by the query.All cells in a column have same data type and the data * type information is retrieved from the metadata of the resultset. *

* *

* Note : If data in the tables gets modified, Container will not get reflected * with the updates, we have to explicity invoke QueryContainer.refresh method. * {@link com.vaadin.data.util.QueryContainer#refresh() refresh()} *

* * @see com.vaadin.data.Container * * @author IT Mill Ltd. * @version * @since 4.0 */ @SuppressWarnings("serial") public class QueryContainer implements Container, Container.Ordered, Container.Indexed { // default ResultSet type public static final int DEFAULT_RESULTSET_TYPE = ResultSet.TYPE_SCROLL_INSENSITIVE; // default ResultSet concurrency public static final int DEFAULT_RESULTSET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY; private int resultSetType = DEFAULT_RESULTSET_TYPE; private int resultSetConcurrency = DEFAULT_RESULTSET_CONCURRENCY; private final String queryStatement; private final Connection connection; private ResultSet result; private Collection propertyIds; private final HashMap propertyTypes = new HashMap(); private int size = -1; private Statement statement; /** * Constructs new QueryContainer with the specified * queryStatement. * * @param queryStatement * Database query * @param connection * Connection object * @param resultSetType * @param resultSetConcurrency * @throws SQLException * when database operation fails */ public QueryContainer(String queryStatement, Connection connection, int resultSetType, int resultSetConcurrency) throws SQLException { this.queryStatement = queryStatement; this.connection = connection; this.resultSetType = resultSetType; this.resultSetConcurrency = resultSetConcurrency; init(); } /** * Constructs new QueryContainer with the specified * queryStatement using the default resultset type and default resultset * concurrency. * * @param queryStatement * Database query * @param connection * Connection object * @see QueryContainer#DEFAULT_RESULTSET_TYPE * @see QueryContainer#DEFAULT_RESULTSET_CONCURRENCY * @throws SQLException * when database operation fails */ public QueryContainer(String queryStatement, Connection connection) throws SQLException { this(queryStatement, connection, DEFAULT_RESULTSET_TYPE, DEFAULT_RESULTSET_CONCURRENCY); } /** * Fills the Container with the items and properties. Invoked by the * constructor. * * @throws SQLException * when parameter initialization fails. * @see QueryContainer#QueryContainer(String, Connection, int, int). */ private void init() throws SQLException { refresh(); ResultSetMetaData metadata; metadata = result.getMetaData(); final int count = metadata.getColumnCount(); final ArrayList list = new ArrayList(count); for (int i = 1; i <= count; i++) { final String columnName = metadata.getColumnName(i); list.add(columnName); final Property p = getContainerProperty(new Integer(1), columnName); propertyTypes.put(columnName, p == null ? Object.class : p.getType()); } propertyIds = Collections.unmodifiableCollection(list); } /** *

* Restores items in the container. This method will update the latest data * to the container. *

* Note: This method should be used to update the container with the latest * items. * * @throws SQLException * when database operation fails * */ public void refresh() throws SQLException { close(); statement = connection.createStatement(resultSetType, resultSetConcurrency); result = statement.executeQuery(queryStatement); result.last(); size = result.getRow(); } /** * Releases and nullifies the statement. * * @throws SQLException * when database operation fails */ public void close() throws SQLException { if (statement != null) { statement.close(); } statement = null; } /** * Gets the Item with the given Item ID from the Container. * * @param id * ID of the Item to retrieve * @return Item Id. */ public Item getItem(Object id) { return new Row(id); } /** * Gets the collection of propertyId from the Container. * * @return Collection of Property ID. */ public Collection getContainerPropertyIds() { return propertyIds; } /** * Gets an collection of all the item IDs in the container. * * @return collection of Item IDs */ public Collection getItemIds() { final Collection c = new ArrayList(size); for (int i = 1; i <= size; i++) { c.add(new Integer(i)); } return c; } /** * Gets the property identified by the given itemId and propertyId from the * container. If the container does not contain the property * null is returned. * * @param itemId * ID of the Item which contains the Property * @param propertyId * ID of the Property to retrieve * * @return Property with the given ID if exists; null * otherwise. */ public synchronized Property getContainerProperty(Object itemId, Object propertyId) { if (!(itemId instanceof Integer && propertyId instanceof String)) { return null; } Object value; try { result.absolute(((Integer) itemId).intValue()); value = result.getObject((String) propertyId); } catch (final Exception e) { return null; } // Handle also null values from the database return new ObjectProperty(value != null ? value : new String("")); } /** * Gets the data type of all properties identified by the given type ID. * * @param id * ID identifying the Properties * * @return data type of the Properties */ public Class getType(Object id) { return (Class) propertyTypes.get(id); } /** * Gets the number of items in the container. * * @return the number of items in the container. */ public int size() { return size; } /** * Tests if the list contains the specified Item. * * @param id * ID the of Item to be tested. * @return true if given id is in the container; * false otherwise. */ public boolean containsId(Object id) { if (!(id instanceof Integer)) { return false; } final int i = ((Integer) id).intValue(); if (i < 1) { return false; } if (i > size) { return false; } return true; } /** * Creates new Item with the given ID into the Container. * * @param itemId * ID of the Item to be created. * * @return Created new Item, or null if it fails. * * @throws UnsupportedOperationException * if the addItem method is not supported. */ public Item addItem(Object itemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Creates a new Item into the Container, and assign it an ID. * * @return ID of the newly created Item, or null if it fails. * @throws UnsupportedOperationException * if the addItem method is not supported. */ public Object addItem() throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Removes the Item identified by ItemId from the Container. * * @param itemId * ID of the Item to remove. * @return true if the operation succeeded; false * otherwise. * @throws UnsupportedOperationException * if the removeItem method is not supported. */ public boolean removeItem(Object itemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Adds new Property to all Items in the Container. * * @param propertyId * ID of the Property * @param type * Data type of the new Property * @param defaultValue * The value all created Properties are initialized to. * @return true if the operation succeeded; false * otherwise. * @throws UnsupportedOperationException * if the addContainerProperty method is not supported. */ public boolean addContainerProperty(Object propertyId, Class type, Object defaultValue) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Removes a Property specified by the given Property ID from the Container. * * @param propertyId * ID of the Property to remove * @return true if the operation succeeded; false * otherwise. * @throws UnsupportedOperationException * if the removeContainerProperty method is not supported. */ public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Removes all Items from the Container. * * @return true if the operation succeeded; false * otherwise. * @throws UnsupportedOperationException * if the removeAllItems method is not supported. */ public boolean removeAllItems() throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Adds new item after the given item. * * @param previousItemId * Id of the previous item in ordered container. * @param newItemId * Id of the new item to be added. * @return Returns new item or null if the operation fails. * @throws UnsupportedOperationException * if the addItemAfter method is not supported. */ public Item addItemAfter(Object previousItemId, Object newItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Adds new item after the given item. * * @param previousItemId * Id of the previous item in ordered container. * @return Returns item id created new item or null if the * operation fails. * @throws UnsupportedOperationException * if the addItemAfter method is not supported. */ public Object addItemAfter(Object previousItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Returns id of first item in the Container. * * @return ID of the first Item in the list. */ public Object firstItemId() { if (size < 1) { return null; } return new Integer(1); } /** * Returns true if given id is first id at first index. * * @param id * ID of an Item in the Container. */ public boolean isFirstId(Object id) { return size > 0 && (id instanceof Integer) && ((Integer) id).intValue() == 1; } /** * Returns true if given id is last id at last index. * * @param id * ID of an Item in the Container * */ public boolean isLastId(Object id) { return size > 0 && (id instanceof Integer) && ((Integer) id).intValue() == size; } /** * Returns id of last item in the Container. * * @return ID of the last Item. */ public Object lastItemId() { if (size < 1) { return null; } return new Integer(size); } /** * Returns id of next item in container at next index. * * @param id * ID of an Item in the Container. * @return ID of the next Item or null. */ public Object nextItemId(Object id) { if (size < 1 || !(id instanceof Integer)) { return null; } final int i = ((Integer) id).intValue(); if (i >= size) { return null; } return new Integer(i + 1); } /** * Returns id of previous item in container at previous index. * * @param id * ID of an Item in the Container. * @return ID of the previous Item or null. */ public Object prevItemId(Object id) { if (size < 1 || !(id instanceof Integer)) { return null; } final int i = ((Integer) id).intValue(); if (i <= 1) { return null; } return new Integer(i - 1); } /** * The Row class implements methods of Item. * * @author IT Mill Ltd. * @version * @since 4.0 */ class Row implements Item { Object id; private Row(Object rowId) { id = rowId; } /** * Adds the item property. * * @param id * ID of the new Property. * @param property * Property to be added and associated with ID. * @return true if the operation succeeded; * false otherwise. * @throws UnsupportedOperationException * if the addItemProperty method is not supported. */ public boolean addItemProperty(Object id, Property property) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Gets the property corresponding to the given property ID stored in * the Item. * * @param propertyId * identifier of the Property to get * @return the Property with the given ID or null */ public Property getItemProperty(Object propertyId) { return getContainerProperty(id, propertyId); } /** * Gets the collection of property IDs stored in the Item. * * @return unmodifiable collection containing IDs of the Properties * stored the Item. */ public Collection getItemPropertyIds() { return propertyIds; } /** * Removes given item property. * * @param id * ID of the Property to be removed. * @return true if the item property is removed; * false otherwise. * @throws UnsupportedOperationException * if the removeItemProperty is not supported. */ public boolean removeItemProperty(Object id) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } } /** * Closes the statement. * * @see #close() */ @Override public void finalize() { try { close(); } catch (final SQLException ignored) { } } /** * Adds the given item at the position of given index. * * @param index * Index to add the new item. * @param newItemId * Id of the new item to be added. * @return new item or null if the operation fails. * @throws UnsupportedOperationException * if the addItemAt is not supported. */ public Item addItemAt(int index, Object newItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Adds item at the position of provided index in the container. * * @param index * Index to add the new item. * @return item id created new item or null if the operation * fails. * * @throws UnsupportedOperationException * if the addItemAt is not supported. */ public Object addItemAt(int index) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /** * Gets the Index id in the container. * * @param index * Index Id. * @return ID in the given index. */ public Object getIdByIndex(int index) { if (size < 1 || index < 0 || index >= size) { return null; } return new Integer(index + 1); } /** * Gets the index of the Item corresponding to id in the container. * * @param id * ID of an Item in the Container * @return index of the Item, or -1 if the Container does not include the * Item */ public int indexOfId(Object id) { if (size < 1 || !(id instanceof Integer)) { return -1; } final int i = ((Integer) id).intValue(); if (i >= size || i < 1) { return -1; } return i - 1; } }