]> source.dussan.org Git - vaadin-framework.git/commitdiff
Moved SQLContainer from util package into its own util.sqlcontainer package. #5712
authorJohn Alhroos <john.ahlroos@itmill.com>
Wed, 10 Aug 2011 08:31:14 +0000 (08:31 +0000)
committerJohn Alhroos <john.ahlroos@itmill.com>
Wed, 10 Aug 2011 08:31:14 +0000 (08:31 +0000)
svn changeset:20254/svn branch:6.7

90 files changed:
src/com/vaadin/data/util/CacheFlushNotifier.java [deleted file]
src/com/vaadin/data/util/CacheMap.java [deleted file]
src/com/vaadin/data/util/ColumnProperty.java [deleted file]
src/com/vaadin/data/util/OptimisticLockException.java [deleted file]
src/com/vaadin/data/util/ReadOnlyRowId.java [deleted file]
src/com/vaadin/data/util/Reference.java [deleted file]
src/com/vaadin/data/util/RowId.java [deleted file]
src/com/vaadin/data/util/RowItem.java [deleted file]
src/com/vaadin/data/util/SQLContainer.java [deleted file]
src/com/vaadin/data/util/SQLUtil.java [deleted file]
src/com/vaadin/data/util/TemporaryRowId.java [deleted file]
src/com/vaadin/data/util/connection/J2EEConnectionPool.java [deleted file]
src/com/vaadin/data/util/connection/JDBCConnectionPool.java [deleted file]
src/com/vaadin/data/util/connection/SimpleJDBCConnectionPool.java [deleted file]
src/com/vaadin/data/util/query/FreeformQuery.java [deleted file]
src/com/vaadin/data/util/query/FreeformQueryDelegate.java [deleted file]
src/com/vaadin/data/util/query/FreeformStatementDelegate.java [deleted file]
src/com/vaadin/data/util/query/OrderBy.java [deleted file]
src/com/vaadin/data/util/query/QueryDelegate.java [deleted file]
src/com/vaadin/data/util/query/TableQuery.java [deleted file]
src/com/vaadin/data/util/query/generator/DefaultSQLGenerator.java [deleted file]
src/com/vaadin/data/util/query/generator/MSSQLGenerator.java [deleted file]
src/com/vaadin/data/util/query/generator/OracleGenerator.java [deleted file]
src/com/vaadin/data/util/query/generator/SQLGenerator.java [deleted file]
src/com/vaadin/data/util/query/generator/StatementHelper.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/AndTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/BetweenTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/CompareTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/FilterTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/IsNullTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/LikeTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/NotTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/OrTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/QueryBuilder.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/SimpleStringTranslator.java [deleted file]
src/com/vaadin/data/util/query/generator/filter/StringDecorator.java [deleted file]
src/com/vaadin/data/util/sqlcontainer/CacheFlushNotifier.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/CacheMap.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/ReadOnlyRowId.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/Reference.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/RowId.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/RowItem.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/SQLContainer.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/SQLUtil.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/TemporaryRowId.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/connection/JDBCConnectionPool.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/FreeformQueryDelegate.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/FreeformStatementDelegate.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/OrderBy.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/QueryDelegate.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/MSSQLGenerator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/OracleGenerator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/SQLGenerator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/StatementHelper.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/AndTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/NotTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/OrTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java [new file with mode: 0644]
src/com/vaadin/data/util/sqlcontainer/query/generator/filter/StringDecorator.java [new file with mode: 0644]
tests/src/com/vaadin/tests/containers/sqlcontainer/CheckboxUpdateProblem.java
tests/src/com/vaadin/tests/containers/sqlcontainer/MassInsertMemoryLeakTestApp.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/AllTests.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/ColumnPropertyTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/DataGenerator.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/FreeformQueryUtil.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/ReadOnlyRowIdTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/RowIdTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/SQLContainerTableQueryTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/SQLContainerTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/TicketTests.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/UtilTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/J2EEConnectionPoolTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/generator/SQLGeneratorsTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/query/FreeformQueryTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/query/QueryBuilderTest.java
tests/src/com/vaadin/tests/server/container/sqlcontainer/query/TableQueryTest.java

diff --git a/src/com/vaadin/data/util/CacheFlushNotifier.java b/src/com/vaadin/data/util/CacheFlushNotifier.java
deleted file mode 100644 (file)
index c953785..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.vaadin.data.util;\r
-\r
-import java.lang.ref.ReferenceQueue;\r
-import java.lang.ref.WeakReference;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import com.vaadin.data.util.query.FreeformQuery;\r
-import com.vaadin.data.util.query.QueryDelegate;\r
-import com.vaadin.data.util.query.TableQuery;\r
-\r
-/**\r
- * CacheFlushNotifier is a simple static notification mechanism to inform other\r
- * SQLContainers that the contents of their caches may have become stale.\r
- */\r
-class CacheFlushNotifier {\r
-    /*\r
-     * SQLContainer instance reference list and dead reference queue. Used for\r
-     * the cache flush notification feature.\r
-     */\r
-    private static List<WeakReference<SQLContainer>> allInstances = new ArrayList<WeakReference<SQLContainer>>();\r
-    private static ReferenceQueue<SQLContainer> deadInstances = new ReferenceQueue<SQLContainer>();\r
-\r
-    /**\r
-     * Adds the given SQLContainer to the cache flush notification receiver list\r
-     * \r
-     * @param c\r
-     *            Container to add\r
-     */\r
-    public static void addInstance(SQLContainer c) {\r
-        removeDeadReferences();\r
-        if (c != null) {\r
-            allInstances.add(new WeakReference<SQLContainer>(c, deadInstances));\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Removes dead references from instance list\r
-     */\r
-    private static void removeDeadReferences() {\r
-        java.lang.ref.Reference<? extends SQLContainer> dead = deadInstances\r
-                .poll();\r
-        while (dead != null) {\r
-            allInstances.remove(dead);\r
-            dead = deadInstances.poll();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Iterates through the instances and notifies containers which are\r
-     * connected to the same table or are using the same query string.\r
-     * \r
-     * @param c\r
-     *            SQLContainer that issued the cache flush notification\r
-     */\r
-    public static void notifyOfCacheFlush(SQLContainer c) {\r
-        removeDeadReferences();\r
-        for (WeakReference<SQLContainer> wr : allInstances) {\r
-            if (wr.get() != null) {\r
-                SQLContainer wrc = wr.get();\r
-                if (wrc == null) {\r
-                    continue;\r
-                }\r
-                /*\r
-                 * If the reference points to the container sending the\r
-                 * notification, do nothing.\r
-                 */\r
-                if (wrc.equals(c)) {\r
-                    continue;\r
-                }\r
-                /* Compare QueryDelegate types and tableName/queryString */\r
-                QueryDelegate wrQd = wrc.getQueryDelegate();\r
-                QueryDelegate qd = c.getQueryDelegate();\r
-                if (wrQd instanceof TableQuery\r
-                        && qd instanceof TableQuery\r
-                        && ((TableQuery) wrQd).getTableName().equals(\r
-                                ((TableQuery) qd).getTableName())) {\r
-                    wrc.refresh();\r
-                } else if (wrQd instanceof FreeformQuery\r
-                        && qd instanceof FreeformQuery\r
-                        && ((FreeformQuery) wrQd).getQueryString().equals(\r
-                                ((FreeformQuery) qd).getQueryString())) {\r
-                    wrc.refresh();\r
-                }\r
-            }\r
-        }\r
-    }\r
-}\r
diff --git a/src/com/vaadin/data/util/CacheMap.java b/src/com/vaadin/data/util/CacheMap.java
deleted file mode 100644 (file)
index a7a17c6..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.vaadin.data.util;\r
-\r
-import java.util.LinkedHashMap;\r
-import java.util.Map;\r
-\r
-/**\r
- * CacheMap extends LinkedHashMap, adding the possibility to adjust maximum\r
- * number of items. In SQLContainer this is used for RowItem -cache. Cache size\r
- * will be two times the page length parameter of the container.\r
- */\r
-class CacheMap<K, V> extends LinkedHashMap<K, V> {\r
-    private static final long serialVersionUID = 679999766473555231L;\r
-    private int cacheLimit = SQLContainer.CACHE_RATIO\r
-            * SQLContainer.DEFAULT_PAGE_LENGTH;\r
-\r
-    @Override\r
-    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {\r
-        return size() > cacheLimit;\r
-    }\r
-\r
-    void setCacheLimit(int limit) {\r
-        cacheLimit = limit > 0 ? limit : SQLContainer.DEFAULT_PAGE_LENGTH;\r
-    }\r
-\r
-    int getCacheLimit() {\r
-        return cacheLimit;\r
-    }\r
-}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/ColumnProperty.java b/src/com/vaadin/data/util/ColumnProperty.java
deleted file mode 100644 (file)
index 0deb1a7..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-package com.vaadin.data.util;
-
-import java.lang.reflect.Constructor;
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
-
-import com.vaadin.data.Property;
-
-/**
- * ColumnProperty represents the value of one column in a RowItem. In addition
- * to the value, ColumnProperty also contains some basic column attributes such
- * as nullability status, read-only status and data type.
- * 
- * Note that depending on the QueryDelegate in use this does not necessarily map
- * into an actual column in a database table.
- */
-final public class ColumnProperty implements Property {
-    private static final long serialVersionUID = -3694463129581802457L;
-
-    private RowItem owner;
-
-    private String propertyId;
-
-    private boolean readOnly;
-    private boolean allowReadOnlyChange = true;
-    private boolean nullable = true;
-
-    private Object value;
-    private Object changedValue;
-    private Class<?> type;
-
-    private boolean modified;
-
-    private boolean versionColumn;
-
-    /**
-     * Prevent instantiation without required parameters.
-     */
-    @SuppressWarnings("unused")
-    private ColumnProperty() {
-    }
-
-    public ColumnProperty(String propertyId, boolean readOnly,
-            boolean allowReadOnlyChange, boolean nullable, Object value,
-            Class<?> type) {
-        if (propertyId == null) {
-            throw new IllegalArgumentException("Properties must be named.");
-        }
-        if (type == null) {
-            throw new IllegalArgumentException("Property type must be set.");
-        }
-        this.propertyId = propertyId;
-        this.type = type;
-        this.value = value;
-
-        this.allowReadOnlyChange = allowReadOnlyChange;
-        this.nullable = nullable;
-        this.readOnly = readOnly;
-    }
-
-    public Object getValue() {
-        if (isModified()) {
-            return changedValue;
-        }
-        return value;
-    }
-
-    public void setValue(Object newValue) throws ReadOnlyException,
-            ConversionException {
-        if (newValue == null && !nullable) {
-            throw new NotNullableException(
-                    "Null values are not allowed for this property.");
-        }
-        if (readOnly) {
-            throw new ReadOnlyException(
-                    "Cannot set value for read-only property.");
-        }
-
-        /* Check if this property is a date property. */
-        boolean isDateProperty = Time.class.equals(getType())
-                || Date.class.equals(getType())
-                || Timestamp.class.equals(getType());
-
-        if (newValue != null) {
-            /* Handle SQL dates, times and Timestamps given as java.util.Date */
-            if (isDateProperty) {
-                /*
-                 * Try to get the millisecond value from the new value of this
-                 * property. Possible type to convert from is java.util.Date.
-                 */
-                long millis = 0;
-                if (newValue instanceof java.util.Date) {
-                    millis = ((java.util.Date) newValue).getTime();
-                    /*
-                     * Create the new object based on the millisecond value,
-                     * according to the type of this property.
-                     */
-                    if (Time.class.equals(getType())) {
-                        newValue = new Time(millis);
-                    } else if (Date.class.equals(getType())) {
-                        newValue = new Date(millis);
-                    } else if (Timestamp.class.equals(getType())) {
-                        newValue = new Timestamp(millis);
-                    }
-                }
-            }
-
-            /*
-             * If the type is not correct, try to generate it through a possibly
-             * existing String constructor.
-             */
-            if (!getType().isAssignableFrom(newValue.getClass())) {
-                try {
-                    final Constructor<?> constr = getType().getConstructor(
-                            new Class[] { String.class });
-                    newValue = constr.newInstance(new Object[] { newValue
-                            .toString() });
-                } catch (Exception e) {
-                    throw new ConversionException(e);
-                }
-            }
-
-            /*
-             * If the value to be set is the same that has already been set, do
-             * not set it again.
-             */
-            if (newValue.equals(value)) {
-                return;
-            }
-        }
-
-        /* Set the new value and notify container of the change. */
-        changedValue = newValue;
-        owner.getContainer().itemChangeNotification(owner);
-        modified = true;
-    }
-
-    public Class<?> getType() {
-        return type;
-    }
-
-    public boolean isReadOnly() {
-        return readOnly;
-    }
-
-    public boolean isReadOnlyChangeAllowed() {
-        return allowReadOnlyChange;
-    }
-
-    public void setReadOnly(boolean newStatus) {
-        if (allowReadOnlyChange) {
-            readOnly = newStatus;
-        }
-    }
-
-    public String getPropertyId() {
-        return propertyId;
-    }
-
-    @Override
-    public String toString() {
-        Object val = getValue();
-        if (val == null) {
-            return null;
-        }
-        return val.toString();
-    }
-
-    public void setOwner(RowItem owner) {
-        if (owner == null) {
-            throw new IllegalArgumentException("Owner can not be set to null.");
-        }
-        if (this.owner != null) {
-            throw new IllegalStateException(
-                    "ColumnProperties can only be bound once.");
-        }
-        this.owner = owner;
-    }
-
-    public boolean isModified() {
-        return modified;
-    }
-
-    public boolean isVersionColumn() {
-        return versionColumn;
-    }
-
-    public void setVersionColumn(boolean versionColumn) {
-        this.versionColumn = versionColumn;
-    }
-
-    public boolean isNullable() {
-        return nullable;
-    }
-
-    /**
-     * An exception that signals that a <code>null</code> value was passed to
-     * the <code>setValue</code> method, but the value of this property can not
-     * be set to <code>null</code>.
-     */
-    @SuppressWarnings("serial")
-    public class NotNullableException extends RuntimeException {
-
-        /**
-         * Constructs a new <code>NotNullableException</code> without a detail
-         * message.
-         */
-        public NotNullableException() {
-        }
-
-        /**
-         * Constructs a new <code>NotNullableException</code> with the specified
-         * detail message.
-         * 
-         * @param msg
-         *            the detail message
-         */
-        public NotNullableException(String msg) {
-            super(msg);
-        }
-
-        /**
-         * Constructs a new <code>NotNullableException</code> from another
-         * exception.
-         * 
-         * @param cause
-         *            The cause of the failure
-         */
-        public NotNullableException(Throwable cause) {
-            super(cause);
-        }
-    }
-
-    public void commit() {
-        if (isModified()) {
-            modified = false;
-            value = changedValue;
-        }
-    }
-}
diff --git a/src/com/vaadin/data/util/OptimisticLockException.java b/src/com/vaadin/data/util/OptimisticLockException.java
deleted file mode 100644 (file)
index a32994a..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.vaadin.data.util;
-
-/**
- * An OptimisticLockException is thrown when trying to update or delete a row
- * that has been changed since last read from the database.
- * 
- * OptimisticLockException is a runtime exception because optimistic locking is
- * turned off by default, and as such will never be thrown in a default
- * configuration. In order to turn on optimistic locking, you need to specify
- * the version column in your TableQuery instance.
- * 
- * @see com.vaadin.addon.sqlcontainer.query.TableQuery#setVersionColumn(String)
- * 
- * @author Jonatan Kronqvist / Vaadin Ltd
- */
-public class OptimisticLockException extends RuntimeException {
-
-    private final RowId rowId;
-
-    public OptimisticLockException(RowId rowId) {
-        super();
-        this.rowId = rowId;
-    }
-
-    public OptimisticLockException(String msg, RowId rowId) {
-        super(msg);
-        this.rowId = rowId;
-    }
-
-    public RowId getRowId() {
-        return rowId;
-    }
-}
diff --git a/src/com/vaadin/data/util/ReadOnlyRowId.java b/src/com/vaadin/data/util/ReadOnlyRowId.java
deleted file mode 100644 (file)
index a3c559c..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.vaadin.data.util;
-
-public class ReadOnlyRowId extends RowId {
-    private static final long serialVersionUID = -2626764781642012467L;
-    private final Integer rowNum;
-
-    public ReadOnlyRowId(int rowNum) {
-        super();
-        this.rowNum = rowNum;
-    }
-
-    @Override
-    public int hashCode() {
-        return rowNum.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || !(obj instanceof ReadOnlyRowId)) {
-            return false;
-        }
-        return rowNum.equals(((ReadOnlyRowId) obj).rowNum);
-    }
-
-    public int getRowNum() {
-        return rowNum;
-    }
-}
diff --git a/src/com/vaadin/data/util/Reference.java b/src/com/vaadin/data/util/Reference.java
deleted file mode 100644 (file)
index 9e174b4..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.vaadin.data.util;\r
-\r
-import java.io.Serializable;\r
-\r
-/**\r
- * The reference class represents a simple [usually foreign key] reference to\r
- * another SQLContainer. Actual foreign key reference in the database is not\r
- * required, but it is recommended to make sure that certain constraints are\r
- * followed.\r
- */\r
-@SuppressWarnings("serial")\r
-class Reference implements Serializable {\r
-\r
-    /**\r
-     * The SQLContainer that this reference points to.\r
-     */\r
-    private SQLContainer referencedContainer;\r
-\r
-    /**\r
-     * The column ID/name in the referencing SQLContainer that contains the key\r
-     * used for the reference.\r
-     */\r
-    private String referencingColumn;\r
-\r
-    /**\r
-     * The column ID/name in the referenced SQLContainer that contains the key\r
-     * used for the reference.\r
-     */\r
-    private String referencedColumn;\r
-\r
-    /**\r
-     * Constructs a new reference to be used within the SQLContainer to\r
-     * reference another SQLContainer.\r
-     */\r
-    Reference(SQLContainer referencedContainer, String referencingColumn,\r
-            String referencedColumn) {\r
-        this.referencedContainer = referencedContainer;\r
-        this.referencingColumn = referencingColumn;\r
-        this.referencedColumn = referencedColumn;\r
-    }\r
-\r
-    SQLContainer getReferencedContainer() {\r
-        return referencedContainer;\r
-    }\r
-\r
-    String getReferencingColumn() {\r
-        return referencingColumn;\r
-    }\r
-\r
-    String getReferencedColumn() {\r
-        return referencedColumn;\r
-    }\r
-}\r
diff --git a/src/com/vaadin/data/util/RowId.java b/src/com/vaadin/data/util/RowId.java
deleted file mode 100644 (file)
index 565161b..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.vaadin.data.util;\r
-\r
-import java.io.Serializable;\r
-\r
-/**\r
- * RowId represents identifiers of a single database result set row.\r
- * \r
- * The data structure of a RowId is an Object array which contains the values of\r
- * the primary key columns of the identified row. This allows easy equals()\r
- * -comparison of RowItems.\r
- */\r
-public class RowId implements Serializable {\r
-    private static final long serialVersionUID = -3161778404698901258L;\r
-    protected Object[] id;\r
-\r
-    /**\r
-     * Prevent instantiation without required parameters.\r
-     */\r
-    protected RowId() {\r
-    }\r
-\r
-    public RowId(Object[] id) {\r
-        if (id == null) {\r
-            throw new IllegalArgumentException("id parameter must not be null!");\r
-        }\r
-        this.id = id;\r
-    }\r
-\r
-    public Object[] getId() {\r
-        return id;\r
-    }\r
-\r
-    @Override\r
-    public int hashCode() {\r
-        int result = 31;\r
-        if (id != null) {\r
-            for (Object o : id) {\r
-                if (o != null) {\r
-                    result += o.hashCode();\r
-                }\r
-            }\r
-        }\r
-        return result;\r
-    }\r
-\r
-    @Override\r
-    public boolean equals(Object obj) {\r
-        if (obj == null || !(obj instanceof RowId)) {\r
-            return false;\r
-        }\r
-        Object[] compId = ((RowId) obj).getId();\r
-        if (id == null && compId == null) {\r
-            return true;\r
-        }\r
-        if (id.length != compId.length) {\r
-            return false;\r
-        }\r
-        for (int i = 0; i < id.length; i++) {\r
-            if ((id[i] == null && compId[i] != null)\r
-                    || (id[i] != null && !id[i].equals(compId[i]))) {\r
-                return false;\r
-            }\r
-        }\r
-        return true;\r
-    }\r
-\r
-    @Override\r
-    public String toString() {\r
-        StringBuffer s = new StringBuffer();\r
-        for (int i = 0; i < id.length; i++) {\r
-            s.append(id[i]);\r
-            if (i < id.length - 1) {\r
-                s.append("/");\r
-            }\r
-        }\r
-        return s.toString();\r
-    }\r
-}\r
diff --git a/src/com/vaadin/data/util/RowItem.java b/src/com/vaadin/data/util/RowItem.java
deleted file mode 100644 (file)
index 5fae278..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.vaadin.data.util;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-
-import com.vaadin.data.Item;
-import com.vaadin.data.Property;
-
-/**
- * RowItem represents one row of a result set obtained from a QueryDelegate.
- * 
- * Note that depending on the QueryDelegate in use this does not necessarily map
- * into an actual row in a database table.
- */
-public final class RowItem implements Item {
-    private static final long serialVersionUID = -6228966439127951408L;
-    private SQLContainer container;
-    private RowId id;
-    private Collection<ColumnProperty> properties;
-
-    /**
-     * Prevent instantiation without required parameters.
-     */
-    @SuppressWarnings("unused")
-    private RowItem() {
-    }
-
-    public RowItem(SQLContainer container, RowId id,
-            Collection<ColumnProperty> properties) {
-        if (container == null) {
-            throw new IllegalArgumentException("Container cannot be null.");
-        }
-        if (id == null) {
-            throw new IllegalArgumentException("Row ID cannot be null.");
-        }
-        this.container = container;
-        this.properties = properties;
-        /* Set this RowItem as owner to the properties */
-        if (properties != null) {
-            for (ColumnProperty p : properties) {
-                p.setOwner(this);
-            }
-        }
-        this.id = id;
-    }
-
-    public Property getItemProperty(Object id) {
-        if (id instanceof String && id != null) {
-            for (ColumnProperty cp : properties) {
-                if (id.equals(cp.getPropertyId())) {
-                    return cp;
-                }
-            }
-        }
-        return null;
-    }
-
-    public Collection<?> getItemPropertyIds() {
-        Collection<String> ids = new ArrayList<String>(properties.size());
-        for (ColumnProperty cp : properties) {
-            ids.add(cp.getPropertyId());
-        }
-        return Collections.unmodifiableCollection(ids);
-    }
-
-    /**
-     * Adding properties is not supported. Properties are generated by
-     * SQLContainer.
-     */
-    public boolean addItemProperty(Object id, Property property)
-            throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Removing properties is not supported. Properties are generated by
-     * SQLContainer.
-     */
-    public boolean removeItemProperty(Object id)
-            throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    public RowId getId() {
-        return id;
-    }
-
-    public SQLContainer getContainer() {
-        return container;
-    }
-
-    public boolean isModified() {
-        if (properties != null) {
-            for (ColumnProperty p : properties) {
-                if (p.isModified()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        StringBuffer s = new StringBuffer();
-        s.append("ID:");
-        s.append(getId().toString());
-        for (Object propId : getItemPropertyIds()) {
-            s.append("|");
-            s.append(propId.toString());
-            s.append(":");
-            s.append(getItemProperty(propId).toString());
-        }
-        return s.toString();
-    }
-
-    public void commit() {
-        if (properties != null) {
-            for (ColumnProperty p : properties) {
-                p.commit();
-            }
-        }
-    }
-}
diff --git a/src/com/vaadin/data/util/SQLContainer.java b/src/com/vaadin/data/util/SQLContainer.java
deleted file mode 100644 (file)
index 9189727..0000000
+++ /dev/null
@@ -1,1587 +0,0 @@
-package com.vaadin.data.util;
-
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.Date;
-import java.util.EventObject;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import com.vaadin.data.Container;
-import com.vaadin.data.Item;
-import com.vaadin.data.Property;
-import com.vaadin.data.util.filter.Compare.Equal;
-import com.vaadin.data.util.filter.Like;
-import com.vaadin.data.util.filter.UnsupportedFilterException;
-import com.vaadin.data.util.query.OrderBy;
-import com.vaadin.data.util.query.QueryDelegate;
-import com.vaadin.data.util.query.QueryDelegate.RowIdChangeListener;
-import com.vaadin.data.util.query.TableQuery;
-import com.vaadin.data.util.query.generator.MSSQLGenerator;
-import com.vaadin.data.util.query.generator.OracleGenerator;
-
-public class SQLContainer implements Container, Container.Filterable,
-        Container.Indexed, Container.Sortable, Container.ItemSetChangeNotifier {
-    private static final long serialVersionUID = -3863564310693712511L;
-
-    /** Query delegate */
-    private QueryDelegate delegate;
-    /** Auto commit mode, default = false */
-    private boolean autoCommit = false;
-
-    /** Page length = number of items contained in one page */
-    private int pageLength = DEFAULT_PAGE_LENGTH;
-    public static final int DEFAULT_PAGE_LENGTH = 100;
-
-    /** Number of items to cache = CACHE_RATIO x pageLength */
-    public static final int CACHE_RATIO = 2;
-
-    /** Item and index caches */
-    private final Map<Integer, RowId> itemIndexes = new HashMap<Integer, RowId>();
-    private final CacheMap<RowId, RowItem> cachedItems = new CacheMap<RowId, RowItem>();
-
-    /** Container properties = column names, data types and statuses */
-    private final List<String> propertyIds = new ArrayList<String>();
-    private final Map<String, Class<?>> propertyTypes = new HashMap<String, Class<?>>();
-    private final Map<String, Boolean> propertyReadOnly = new HashMap<String, Boolean>();
-    private final Map<String, Boolean> propertyNullable = new HashMap<String, Boolean>();
-
-    /** Filters (WHERE) and sorters (ORDER BY) */
-    private final List<Filter> filters = new ArrayList<Filter>();
-    private final List<OrderBy> sorters = new ArrayList<OrderBy>();
-
-    /**
-     * Total number of items available in the data source using the current
-     * query, filters and sorters.
-     */
-    private int size;
-
-    /**
-     * Size updating logic. Do not update size from data source if it has been
-     * updated in the last sizeValidMilliSeconds milliseconds.
-     */
-    private final int sizeValidMilliSeconds = 10000;
-    private boolean sizeDirty = true;
-    private Date sizeUpdated = new Date();
-
-    /** Starting row number of the currently fetched page */
-    private int currentOffset;
-
-    /** ItemSetChangeListeners */
-    private LinkedList<Container.ItemSetChangeListener> itemSetChangeListeners;
-
-    /** Temporary storage for modified items and items to be removed and added */
-    private final Map<RowId, RowItem> removedItems = new HashMap<RowId, RowItem>();
-    private final List<RowItem> addedItems = new ArrayList<RowItem>();
-    private final List<RowItem> modifiedItems = new ArrayList<RowItem>();
-
-    /** List of references to other SQLContainers */
-    private final Map<SQLContainer, Reference> references = new HashMap<SQLContainer, Reference>();
-
-    /** Cache flush notification system enabled. Disabled by default. */
-    private boolean notificationsEnabled;
-
-    /** Enable to output possible stack traces and diagnostic information */
-    private boolean debugMode;
-
-    /**
-     * Prevent instantiation without a QueryDelegate.
-     */
-    @SuppressWarnings("unused")
-    private SQLContainer() {
-    }
-
-    /**
-     * Creates and initializes SQLContainer using the given QueryDelegate
-     * 
-     * @param delegate
-     *            QueryDelegate implementation
-     * @throws SQLException
-     */
-    public SQLContainer(QueryDelegate delegate) throws SQLException {
-        if (delegate == null) {
-            throw new IllegalArgumentException(
-                    "QueryDelegate must not be null.");
-        }
-        this.delegate = delegate;
-        getPropertyIds();
-        cachedItems.setCacheLimit(CACHE_RATIO * getPageLength());
-    }
-
-    /**************************************/
-    /** Methods from interface Container **/
-    /**************************************/
-
-    /**
-     * Note! If auto commit mode is enabled, this method will still return the
-     * temporary row ID assigned for the item. Implement
-     * QueryDelegate.RowIdChangeListener to receive the actual Row ID value
-     * after the addition has been committed.
-     * 
-     * {@inheritDoc}
-     */
-    public Object addItem() throws UnsupportedOperationException {
-        Object emptyKey[] = new Object[delegate.getPrimaryKeyColumns().size()];
-        RowId itemId = new TemporaryRowId(emptyKey);
-        // Create new empty column properties for the row item.
-        List<ColumnProperty> itemProperties = new ArrayList<ColumnProperty>();
-        for (String propertyId : propertyIds) {
-            /* Default settings for new item properties. */
-            itemProperties
-                    .add(new ColumnProperty(propertyId, propertyReadOnly
-                            .get(propertyId),
-                            !propertyReadOnly.get(propertyId), propertyNullable
-                                    .get(propertyId), null, getType(propertyId)));
-        }
-        RowItem newRowItem = new RowItem(this, itemId, itemProperties);
-
-        if (autoCommit) {
-            /* Add and commit instantly */
-            try {
-                if (delegate instanceof TableQuery) {
-                    itemId = ((TableQuery) delegate)
-                            .storeRowImmediately(newRowItem);
-                } else {
-                    delegate.beginTransaction();
-                    delegate.storeRow(newRowItem);
-                    delegate.commit();
-                }
-                refresh();
-                if (notificationsEnabled) {
-                    CacheFlushNotifier.notifyOfCacheFlush(this);
-                }
-                debug(null, "Row added to DB...");
-                return itemId;
-            } catch (SQLException e) {
-                debug(e, null);
-                try {
-                    delegate.rollback();
-                } catch (SQLException ee) {
-                    debug(ee, null);
-                }
-                return null;
-            }
-        } else {
-            addedItems.add(newRowItem);
-            fireContentsChange();
-            return itemId;
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#containsId(java.lang.Object)
-     */
-    public boolean containsId(Object itemId) {
-        if (itemId == null) {
-            return false;
-        }
-
-        if (cachedItems.containsKey(itemId)) {
-            return true;
-        } else {
-            for (RowItem item : addedItems) {
-                if (item.getId().equals(itemId)) {
-                    return itemPassesFilters(item);
-                }
-            }
-        }
-        if (removedItems.containsKey(itemId)) {
-            return false;
-        }
-
-        if (itemId instanceof ReadOnlyRowId) {
-            int rowNum = ((ReadOnlyRowId) itemId).getRowNum();
-            return rowNum >= 0 && rowNum < size;
-        }
-
-        if (!(itemId instanceof TemporaryRowId)) {
-            try {
-                return delegate.containsRowWithKey(((RowId) itemId).getId());
-            } catch (Exception e) {
-                /* Query failed, just return false. */
-                debug(e, null);
-            }
-        }
-        return false;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object,
-     * java.lang.Object)
-     */
-    public Property getContainerProperty(Object itemId, Object propertyId) {
-        Item item = getItem(itemId);
-        if (item == null) {
-            return null;
-        }
-        return item.getItemProperty(propertyId);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#getContainerPropertyIds()
-     */
-    public Collection<?> getContainerPropertyIds() {
-        return Collections.unmodifiableCollection(propertyIds);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#getItem(java.lang.Object)
-     */
-    public Item getItem(Object itemId) {
-        if (!cachedItems.containsKey(itemId)) {
-            int index = indexOfId(itemId);
-            if (index >= size) {
-                // The index is in the added items
-                int offset = index - size;
-                RowItem item = addedItems.get(offset);
-                if (itemPassesFilters(item)) {
-                    return item;
-                } else {
-                    return null;
-                }
-            } else {
-                // load the item into cache
-                updateOffsetAndCache(index);
-            }
-        }
-        return cachedItems.get(itemId);
-    }
-
-    /**
-     * Bypasses in-memory filtering to return items that are cached in memory.
-     * <em>NOTE</em>: This does not bypass database-level filtering.
-     * 
-     * @param itemId
-     *            the id of the item to retrieve.
-     * @return the item represented by itemId.
-     */
-    public Item getItemUnfiltered(Object itemId) {
-        if (!cachedItems.containsKey(itemId)) {
-            for (RowItem item : addedItems) {
-                if (item.getId().equals(itemId)) {
-                    return item;
-                }
-            }
-        }
-        return cachedItems.get(itemId);
-    }
-
-    /**
-     * NOTE! Do not use this method if in any way avoidable. This method doesn't
-     * (and cannot) use lazy loading, which means that all rows in the database
-     * will be loaded into memory.
-     * 
-     * {@inheritDoc}
-     */
-    public Collection<?> getItemIds() {
-        updateCount();
-        ArrayList<RowId> ids = new ArrayList<RowId>();
-        ResultSet rs = null;
-        try {
-            // Load ALL rows :(
-            delegate.beginTransaction();
-            rs = delegate.getResults(0, 0);
-            List<String> pKeys = delegate.getPrimaryKeyColumns();
-            while (rs.next()) {
-                RowId id = null;
-                if (pKeys.isEmpty()) {
-                    /* Create a read only itemId */
-                    id = new ReadOnlyRowId(rs.getRow());
-                } else {
-                    /* Generate itemId for the row based on primary key(s) */
-                    Object[] itemId = new Object[pKeys.size()];
-                    for (int i = 0; i < pKeys.size(); i++) {
-                        itemId[i] = rs.getObject(pKeys.get(i));
-                    }
-                    id = new RowId(itemId);
-                }
-                if (id != null && !removedItems.containsKey(id)) {
-                    ids.add(id);
-                }
-            }
-            rs.getStatement().close();
-            rs.close();
-            delegate.commit();
-        } catch (SQLException e) {
-            debug(e, null);
-            try {
-                delegate.rollback();
-            } catch (SQLException e1) {
-                debug(e1, null);
-            }
-            try {
-                rs.getStatement().close();
-                rs.close();
-            } catch (SQLException e1) {
-                debug(e1, null);
-            }
-            throw new RuntimeException("Failed to fetch item indexes.", e);
-        }
-        for (RowItem item : getFilteredAddedItems()) {
-            ids.add(item.getId());
-        }
-        return Collections.unmodifiableCollection(ids);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#getType(java.lang.Object)
-     */
-    public Class<?> getType(Object propertyId) {
-        if (!propertyIds.contains(propertyId)) {
-            return null;
-        }
-        return propertyTypes.get(propertyId);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#size()
-     */
-    public int size() {
-        updateCount();
-        return size + sizeOfAddedItems() - removedItems.size();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#removeItem(java.lang.Object)
-     */
-    public boolean removeItem(Object itemId)
-            throws UnsupportedOperationException {
-        if (!containsId(itemId)) {
-            return false;
-        }
-        for (RowItem item : addedItems) {
-            if (item.getId().equals(itemId)) {
-                addedItems.remove(item);
-                fireContentsChange();
-                return true;
-            }
-        }
-
-        if (autoCommit) {
-            /* Remove and commit instantly. */
-            Item i = getItem(itemId);
-            if (i == null) {
-                return false;
-            }
-            try {
-                delegate.beginTransaction();
-                boolean success = delegate.removeRow((RowItem) i);
-                delegate.commit();
-                refresh();
-                if (notificationsEnabled) {
-                    CacheFlushNotifier.notifyOfCacheFlush(this);
-                }
-                if (success) {
-                    debug(null, "Row removed from DB...");
-                }
-                return success;
-            } catch (SQLException e) {
-                debug(e, null);
-                try {
-                    delegate.rollback();
-                } catch (SQLException ee) {
-                    /* Nothing can be done here */
-                    debug(ee, null);
-                }
-                return false;
-            }
-        } else {
-            removedItems.put((RowId) itemId, (RowItem) getItem(itemId));
-            cachedItems.remove(itemId);
-            refresh();
-            return true;
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#removeAllItems()
-     */
-    public boolean removeAllItems() throws UnsupportedOperationException {
-        if (autoCommit) {
-            /* Remove and commit instantly. */
-            try {
-                delegate.beginTransaction();
-                boolean success = true;
-                for (Object id : getItemIds()) {
-                    if (!delegate.removeRow((RowItem) getItem(id))) {
-                        success = false;
-                    }
-                }
-                if (success) {
-                    delegate.commit();
-                    debug(null, "All rows removed from DB...");
-                    refresh();
-                    if (notificationsEnabled) {
-                        CacheFlushNotifier.notifyOfCacheFlush(this);
-                    }
-                } else {
-                    delegate.rollback();
-                }
-                return success;
-            } catch (SQLException e) {
-                debug(e, null);
-                try {
-                    delegate.rollback();
-                } catch (SQLException ee) {
-                    /* Nothing can be done here */
-                    debug(ee, null);
-                }
-                return false;
-            }
-        } else {
-            for (Object id : getItemIds()) {
-                removedItems.put((RowId) id, (RowItem) getItem(id));
-                cachedItems.remove(id);
-            }
-            refresh();
-            return true;
-        }
-    }
-
-    /*************************************************/
-    /** Methods from interface Container.Filterable **/
-    /*************************************************/
-
-    /**
-     * {@inheritDoc}
-     */
-    public void addContainerFilter(Filter filter)
-            throws UnsupportedFilterException {
-        // filter.setCaseSensitive(!ignoreCase);
-
-        filters.add(filter);
-        refresh();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void removeContainerFilter(Filter filter) {
-        filters.remove(filter);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void addContainerFilter(Object propertyId, String filterString,
-            boolean ignoreCase, boolean onlyMatchPrefix) {
-        if (propertyId == null || !propertyIds.contains(propertyId)) {
-            return;
-        }
-
-        /* Generate Filter -object */
-        String likeStr = onlyMatchPrefix ? filterString + "%" : "%"
-                + filterString + "%";
-        Like like = new Like(propertyId.toString(), likeStr);
-        like.setCaseSensitive(!ignoreCase);
-        filters.add(like);
-        refresh();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void removeContainerFilters(Object propertyId) {
-        ArrayList<Filter> toRemove = new ArrayList<Filter>();
-        for (Filter f : filters) {
-            if (f.appliesToProperty(propertyId)) {
-                toRemove.add(f);
-            }
-        }
-        filters.removeAll(toRemove);
-        refresh();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void removeAllContainerFilters() {
-        filters.clear();
-        refresh();
-    }
-
-    /**********************************************/
-    /** Methods from interface Container.Indexed **/
-    /**********************************************/
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Indexed#indexOfId(java.lang.Object)
-     */
-    public int indexOfId(Object itemId) {
-        // First check if the id is in the added items
-        for (int ix = 0; ix < addedItems.size(); ix++) {
-            RowItem item = addedItems.get(ix);
-            if (item.getId().equals(itemId)) {
-                if (itemPassesFilters(item)) {
-                    updateCount();
-                    return size + ix;
-                } else {
-                    return -1;
-                }
-            }
-        }
-
-        if (!containsId(itemId)) {
-            return -1;
-        }
-        if (cachedItems.isEmpty()) {
-            getPage();
-        }
-        int size = size();
-        boolean wrappedAround = false;
-        while (!wrappedAround) {
-            for (Integer i : itemIndexes.keySet()) {
-                if (itemIndexes.get(i).equals(itemId)) {
-                    return i;
-                }
-            }
-            // load in the next page.
-            int nextIndex = (currentOffset / (pageLength * CACHE_RATIO) + 1)
-                    * (pageLength * CACHE_RATIO);
-            if (nextIndex >= size) {
-                // Container wrapped around, start from index 0.
-                wrappedAround = true;
-                nextIndex = 0;
-            }
-            updateOffsetAndCache(nextIndex);
-        }
-        return -1;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Indexed#getIdByIndex(int)
-     */
-    public Object getIdByIndex(int index) {
-        if (index < 0 || index > size() - 1) {
-            return null;
-        }
-        if (index < size) {
-            if (itemIndexes.keySet().contains(index)) {
-                return itemIndexes.get(index);
-            }
-            updateOffsetAndCache(index);
-            return itemIndexes.get(index);
-        } else {
-            // The index is in the added items
-            int offset = index - size;
-            return addedItems.get(offset).getId();
-        }
-    }
-
-    /**********************************************/
-    /** Methods from interface Container.Ordered **/
-    /**********************************************/
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
-     */
-    public Object nextItemId(Object itemId) {
-        return getIdByIndex(indexOfId(itemId) + 1);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
-     */
-    public Object prevItemId(Object itemId) {
-        return getIdByIndex(indexOfId(itemId) - 1);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#firstItemId()
-     */
-    public Object firstItemId() {
-        updateCount();
-        if (size == 0) {
-            if (addedItems.isEmpty()) {
-                return null;
-            } else {
-                int ix = -1;
-                do {
-                    ix++;
-                } while (!itemPassesFilters(addedItems.get(ix))
-                        && ix < addedItems.size());
-                if (ix < addedItems.size()) {
-                    return addedItems.get(ix).getId();
-                }
-            }
-        }
-        if (!itemIndexes.containsKey(0)) {
-            updateOffsetAndCache(0);
-        }
-        return itemIndexes.get(0);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#lastItemId()
-     */
-    public Object lastItemId() {
-        if (addedItems.isEmpty()) {
-            int lastIx = size() - 1;
-            if (!itemIndexes.containsKey(lastIx)) {
-                updateOffsetAndCache(size - 1);
-            }
-            return itemIndexes.get(lastIx);
-        } else {
-            int ix = addedItems.size();
-            do {
-                ix--;
-            } while (!itemPassesFilters(addedItems.get(ix)) && ix >= 0);
-            if (ix >= 0) {
-                return addedItems.get(ix).getId();
-            } else {
-                return null;
-            }
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
-     */
-    public boolean isFirstId(Object itemId) {
-        return firstItemId().equals(itemId);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
-     */
-    public boolean isLastId(Object itemId) {
-        return lastItemId().equals(itemId);
-    }
-
-    /***********************************************/
-    /** Methods from interface Container.Sortable **/
-    /***********************************************/
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
-     * boolean[])
-     */
-    public void sort(Object[] propertyId, boolean[] ascending) {
-        sorters.clear();
-        if (propertyId == null || propertyId.length == 0) {
-            refresh();
-            return;
-        }
-        /* Generate OrderBy -objects */
-        boolean asc = true;
-        for (int i = 0; i < propertyId.length; i++) {
-            /* Check that the property id is valid */
-            if (propertyId[i] instanceof String
-                    && propertyIds.contains(propertyId[i])) {
-                try {
-                    asc = ascending[i];
-                } catch (Exception e) {
-                    debug(e, null);
-                }
-                sorters.add(new OrderBy((String) propertyId[i], asc));
-            }
-        }
-        refresh();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
-     */
-    public Collection<?> getSortableContainerPropertyIds() {
-        return getContainerPropertyIds();
-    }
-
-    /**************************************/
-    /** Methods specific to SQLContainer **/
-    /**************************************/
-
-    /**
-     * Refreshes the container - clears all caches and resets size and offset.
-     * Does NOT remove sorting or filtering rules!
-     */
-    public void refresh() {
-        sizeDirty = true;
-        currentOffset = 0;
-        cachedItems.clear();
-        itemIndexes.clear();
-        fireContentsChange();
-    }
-
-    /**
-     * Returns modify state of the container.
-     * 
-     * @return true if contents of this container have been modified
-     */
-    public boolean isModified() {
-        return !removedItems.isEmpty() || !addedItems.isEmpty()
-                || !modifiedItems.isEmpty();
-    }
-
-    /**
-     * Set auto commit mode enabled or disabled. Auto commit mode means that all
-     * changes made to items of this container will be immediately written to
-     * the underlying data source.
-     * 
-     * @param autoCommitEnabled
-     *            true to enable auto commit mode
-     */
-    public void setAutoCommit(boolean autoCommitEnabled) {
-        autoCommit = autoCommitEnabled;
-    }
-
-    /**
-     * Returns status of the auto commit mode.
-     * 
-     * @return true if auto commit mode is enabled
-     */
-    public boolean isAutoCommit() {
-        return autoCommit;
-    }
-
-    /**
-     * Returns the currently set page length.
-     * 
-     * @return current page length
-     */
-    public int getPageLength() {
-        return pageLength;
-    }
-
-    /**
-     * Sets the page length used in lazy fetching of items from the data source.
-     * Also resets the cache size to match the new page length.
-     * 
-     * As a side effect the container will be refreshed.
-     * 
-     * @param pageLength
-     *            new page length
-     */
-    public void setPageLength(int pageLength) {
-        setPageLengthInternal(pageLength);
-        refresh();
-    }
-
-    /**
-     * Sets the page length internally, without refreshing the container.
-     * 
-     * @param pageLength
-     *            the new page length
-     */
-    private void setPageLengthInternal(int pageLength) {
-        this.pageLength = pageLength > 0 ? pageLength : DEFAULT_PAGE_LENGTH;
-        cachedItems.setCacheLimit(CACHE_RATIO * getPageLength());
-    }
-
-    /**
-     * Adds the given OrderBy to this container and refreshes the container
-     * contents with the new sorting rules.
-     * 
-     * Note that orderBy.getColumn() must return a column name that exists in
-     * this container.
-     * 
-     * @param orderBy
-     *            OrderBy to be added to the container sorting rules
-     */
-    public void addOrderBy(OrderBy orderBy) {
-        if (orderBy == null) {
-            return;
-        }
-        if (!propertyIds.contains(orderBy.getColumn())) {
-            throw new IllegalArgumentException(
-                    "The column given for sorting does not exist in this container.");
-        }
-        sorters.add(orderBy);
-        refresh();
-    }
-
-    /**
-     * Commits all the changes, additions and removals made to the items of this
-     * container.
-     * 
-     * @throws UnsupportedOperationException
-     * @throws SQLException
-     */
-    public void commit() throws UnsupportedOperationException, SQLException {
-        try {
-            debug(null, "Commiting changes through delegate...");
-            delegate.beginTransaction();
-            /* Perform buffered deletions */
-            for (RowItem item : removedItems.values()) {
-                if (!delegate.removeRow(item)) {
-                    throw new SQLException("Removal failed for row with ID: "
-                            + item.getId());
-                }
-            }
-            /* Perform buffered modifications */
-            for (RowItem item : modifiedItems) {
-                if (delegate.storeRow(item) > 0) {
-                    /*
-                     * Also reset the modified state in the item in case it is
-                     * reused e.g. in a form.
-                     */
-                    item.commit();
-                } else {
-                    delegate.rollback();
-                    refresh();
-                    throw new ConcurrentModificationException(
-                            "Item with the ID '" + item.getId()
-                                    + "' has been externally modified.");
-                }
-            }
-            /* Perform buffered additions */
-            for (RowItem item : addedItems) {
-                delegate.storeRow(item);
-            }
-            delegate.commit();
-            removedItems.clear();
-            addedItems.clear();
-            modifiedItems.clear();
-            refresh();
-            if (notificationsEnabled) {
-                CacheFlushNotifier.notifyOfCacheFlush(this);
-            }
-        } catch (SQLException e) {
-            delegate.rollback();
-            throw e;
-        }
-    }
-
-    /**
-     * Rolls back all the changes, additions and removals made to the items of
-     * this container.
-     * 
-     * @throws UnsupportedOperationException
-     * @throws SQLException
-     */
-    public void rollback() throws UnsupportedOperationException, SQLException {
-        debug(null, "Rolling back changes...");
-        removedItems.clear();
-        addedItems.clear();
-        modifiedItems.clear();
-        refresh();
-    }
-
-    /**
-     * Notifies this container that a property in the given item has been
-     * modified. The change will be buffered or made instantaneously depending
-     * on auto commit mode.
-     * 
-     * @param changedItem
-     *            item that has a modified property
-     */
-    void itemChangeNotification(RowItem changedItem) {
-        if (autoCommit) {
-            try {
-                delegate.beginTransaction();
-                if (delegate.storeRow(changedItem) == 0) {
-                    delegate.rollback();
-                    refresh();
-                    throw new ConcurrentModificationException(
-                            "Item with the ID '" + changedItem.getId()
-                                    + "' has been externally modified.");
-                }
-                delegate.commit();
-                if (notificationsEnabled) {
-                    CacheFlushNotifier.notifyOfCacheFlush(this);
-                }
-                debug(null, "Row updated to DB...");
-            } catch (SQLException e) {
-                debug(e, null);
-                try {
-                    delegate.rollback();
-                } catch (SQLException ee) {
-                    /* Nothing can be done here */
-                    debug(e, null);
-                }
-                throw new RuntimeException(e);
-            }
-        } else {
-            if (!(changedItem.getId() instanceof TemporaryRowId)
-                    && !modifiedItems.contains(changedItem)) {
-                modifiedItems.add(changedItem);
-            }
-        }
-    }
-
-    /**
-     * Determines a new offset for updating the row cache. The offset is
-     * calculated from the given index, and will be fixed to match the start of
-     * a page, based on the value of pageLength.
-     * 
-     * @param index
-     *            Index of the item that was requested, but not found in cache
-     */
-    private void updateOffsetAndCache(int index) {
-        if (itemIndexes.containsKey(index)) {
-            return;
-        }
-        currentOffset = (index / (pageLength * CACHE_RATIO))
-                * (pageLength * CACHE_RATIO);
-        if (currentOffset < 0) {
-            currentOffset = 0;
-        }
-        getPage();
-    }
-
-    /**
-     * Fetches new count of rows from the data source, if needed.
-     */
-    private void updateCount() {
-        if (!sizeDirty
-                && new Date().getTime() < sizeUpdated.getTime()
-                        + sizeValidMilliSeconds) {
-            return;
-        }
-        try {
-            try {
-                delegate.setFilters(filters);
-            } catch (UnsupportedOperationException e) {
-                /* The query delegate doesn't support filtering. */
-                debug(e, null);
-            }
-            try {
-                delegate.setOrderBy(sorters);
-            } catch (UnsupportedOperationException e) {
-                /* The query delegate doesn't support filtering. */
-                debug(e, null);
-            }
-            int newSize = delegate.getCount();
-            if (newSize != size) {
-                size = newSize;
-                refresh();
-            }
-            sizeUpdated = new Date();
-            sizeDirty = false;
-            debug(null, "Updated row count. New count is: " + size);
-        } catch (SQLException e) {
-            throw new RuntimeException("Failed to update item set size.", e);
-        }
-    }
-
-    /**
-     * Fetches property id's (column names and their types) from the data
-     * source.
-     * 
-     * @throws SQLException
-     */
-    private void getPropertyIds() throws SQLException {
-        propertyIds.clear();
-        propertyTypes.clear();
-        delegate.setFilters(null);
-        delegate.setOrderBy(null);
-        ResultSet rs = null;
-        ResultSetMetaData rsmd = null;
-        try {
-            delegate.beginTransaction();
-            rs = delegate.getResults(0, 1);
-            boolean resultExists = rs.next();
-            rsmd = rs.getMetaData();
-            Class<?> type = null;
-            for (int i = 1; i <= rsmd.getColumnCount(); i++) {
-                if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) {
-                    continue;
-                }
-                String colName = rsmd.getColumnLabel(i);
-                /*
-                 * Make sure not to add the same colName twice. This can easily
-                 * happen if the SQL query joins many tables with an ID column.
-                 */
-                if (!propertyIds.contains(colName)) {
-                    propertyIds.add(colName);
-                }
-                /* Try to determine the column's JDBC class by all means. */
-                if (resultExists && rs.getObject(i) != null) {
-                    type = rs.getObject(i).getClass();
-                } else {
-                    try {
-                        type = Class.forName(rsmd.getColumnClassName(i));
-                    } catch (Exception e) {
-                        debug(e, null);
-                        /* On failure revert to Object and hope for the best. */
-                        type = Object.class;
-                    }
-                }
-                /*
-                 * Determine read only and nullability status of the column. A
-                 * column is read only if it is reported as either read only or
-                 * auto increment by the database, and also it is set as the
-                 * version column in a TableQuery delegate.
-                 */
-                boolean readOnly = rsmd.isAutoIncrement(i)
-                        || rsmd.isReadOnly(i);
-                if (delegate instanceof TableQuery
-                        && rsmd.getColumnLabel(i).equals(
-                                ((TableQuery) delegate).getVersionColumn())) {
-                    readOnly = true;
-                }
-                propertyReadOnly.put(colName, readOnly);
-                propertyNullable.put(colName,
-                        rsmd.isNullable(i) == ResultSetMetaData.columnNullable);
-                propertyTypes.put(colName, type);
-            }
-            rs.getStatement().close();
-            rs.close();
-            delegate.commit();
-            debug(null, "Property IDs fetched.");
-        } catch (SQLException e) {
-            debug(e, null);
-            try {
-                delegate.rollback();
-            } catch (SQLException e1) {
-                debug(e1, null);
-            }
-            try {
-                if (rs != null) {
-                    if (rs.getStatement() != null) {
-                        rs.getStatement().close();
-                    }
-                    rs.close();
-                }
-            } catch (SQLException e1) {
-                debug(e1, null);
-            }
-            throw e;
-        }
-    }
-
-    /**
-     * Fetches a page from the data source based on the values of pageLenght and
-     * currentOffset. Also updates the set of primary keys, used in
-     * identification of RowItems.
-     */
-    private void getPage() {
-        updateCount();
-        ResultSet rs = null;
-        ResultSetMetaData rsmd = null;
-        cachedItems.clear();
-        itemIndexes.clear();
-        try {
-            try {
-                delegate.setOrderBy(sorters);
-            } catch (UnsupportedOperationException e) {
-                /* The query delegate doesn't support sorting. */
-                /* No need to do anything. */
-                debug(e, null);
-            }
-            delegate.beginTransaction();
-            rs = delegate.getResults(currentOffset, pageLength * CACHE_RATIO);
-            rsmd = rs.getMetaData();
-            List<String> pKeys = delegate.getPrimaryKeyColumns();
-            // }
-            /* Create new items and column properties */
-            ColumnProperty cp = null;
-            int rowCount = currentOffset;
-            if (!delegate.implementationRespectsPagingLimits()) {
-                rowCount = currentOffset = 0;
-                setPageLengthInternal(size);
-            }
-            while (rs.next()) {
-                List<ColumnProperty> itemProperties = new ArrayList<ColumnProperty>();
-                /* Generate row itemId based on primary key(s) */
-                Object[] itemId = new Object[pKeys.size()];
-                for (int i = 0; i < pKeys.size(); i++) {
-                    itemId[i] = rs.getObject(pKeys.get(i));
-                }
-                RowId id = null;
-                if (pKeys.isEmpty()) {
-                    id = new ReadOnlyRowId(rs.getRow());
-                } else {
-                    id = new RowId(itemId);
-                }
-                List<String> propertiesToAdd = new ArrayList<String>(
-                        propertyIds);
-                if (!removedItems.containsKey(id)) {
-                    for (int i = 1; i <= rsmd.getColumnCount(); i++) {
-                        if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) {
-                            continue;
-                        }
-                        String colName = rsmd.getColumnLabel(i);
-                        Object value = rs.getObject(i);
-                        Class<?> type = value != null ? value.getClass()
-                                : Object.class;
-                        if (value == null) {
-                            for (String propName : propertyTypes.keySet()) {
-                                if (propName.equals(rsmd.getColumnLabel(i))) {
-                                    type = propertyTypes.get(propName);
-                                    break;
-                                }
-                            }
-                        }
-                        /*
-                         * In case there are more than one column with the same
-                         * name, add only the first one. This can easily happen
-                         * if you join many tables where each table has an ID
-                         * column.
-                         */
-                        if (propertiesToAdd.contains(colName)) {
-                            cp = new ColumnProperty(colName,
-                                    propertyReadOnly.get(colName),
-                                    !propertyReadOnly.get(colName),
-                                    propertyNullable.get(colName), value, type);
-                            itemProperties.add(cp);
-                            propertiesToAdd.remove(colName);
-                        }
-                    }
-                    /* Cache item */
-                    itemIndexes.put(rowCount, id);
-                    cachedItems.put(id, new RowItem(this, id, itemProperties));
-                    rowCount++;
-                }
-            }
-            rs.getStatement().close();
-            rs.close();
-            delegate.commit();
-            debug(null, "Fetched " + pageLength * CACHE_RATIO
-                    + " rows starting from " + currentOffset);
-        } catch (SQLException e) {
-            debug(e, null);
-            try {
-                delegate.rollback();
-            } catch (SQLException e1) {
-                debug(e1, null);
-            }
-            try {
-                if (rs != null) {
-                    if (rs.getStatement() != null) {
-                        rs.getStatement().close();
-                        rs.close();
-                    }
-                }
-            } catch (SQLException e1) {
-                debug(e1, null);
-            }
-            throw new RuntimeException("Failed to fetch page.", e);
-        }
-    }
-
-    private int sizeOfAddedItems() {
-        return getFilteredAddedItems().size();
-    }
-
-    private List<RowItem> getFilteredAddedItems() {
-        ArrayList<RowItem> filtered = new ArrayList<RowItem>(addedItems);
-        if (filters != null && !filters.isEmpty()) {
-            for (RowItem item : addedItems) {
-                if (!itemPassesFilters(item)) {
-                    filtered.remove(item);
-                }
-            }
-        }
-        return filtered;
-    }
-
-    private boolean itemPassesFilters(RowItem item) {
-        for (Filter filter : filters) {
-            if (!filter.passesFilter(item.getId(), item)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Checks is the given column identifier valid to be used with SQLContainer.
-     * Currently the only non-valid identifier is "rownum" when MSSQL or Oracle
-     * is used. This is due to the way the SELECT queries are constructed in
-     * order to implement paging in these databases.
-     * 
-     * @param identifier
-     *            Column identifier
-     * @return true if the identifier is valid
-     */
-    private boolean isColumnIdentifierValid(String identifier) {
-        if (identifier.equalsIgnoreCase("rownum")
-                && delegate instanceof TableQuery) {
-            TableQuery tq = (TableQuery) delegate;
-            if (tq.getSqlGenerator() instanceof MSSQLGenerator
-                    || tq.getSqlGenerator() instanceof OracleGenerator) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns the QueryDelegate set for this SQLContainer.
-     * 
-     * @return current querydelegate
-     */
-    protected QueryDelegate getQueryDelegate() {
-        return delegate;
-    }
-
-    /************************************/
-    /** UNSUPPORTED CONTAINER FEATURES **/
-    /************************************/
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object,
-     * java.lang.Class, java.lang.Object)
-     */
-    public boolean addContainerProperty(Object propertyId, Class<?> type,
-            Object defaultValue) throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object)
-     */
-    public boolean removeContainerProperty(Object propertyId)
-            throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container#addItem(java.lang.Object)
-     */
-    public Item addItem(Object itemId) throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
-     * java.lang.Object)
-     */
-    public Item addItemAfter(Object previousItemId, Object newItemId)
-            throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object)
-     */
-    public Item addItemAt(int index, Object newItemId)
-            throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Indexed#addItemAt(int)
-     */
-    public Object addItemAt(int index) throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
-     */
-    public Object addItemAfter(Object previousItemId)
-            throws UnsupportedOperationException {
-        throw new UnsupportedOperationException();
-    }
-
-    /******************************************/
-    /** ITEMSETCHANGENOTIFIER IMPLEMENTATION **/
-    /******************************************/
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin
-     * .data.Container.ItemSetChangeListener)
-     */
-    public void addListener(Container.ItemSetChangeListener listener) {
-        if (itemSetChangeListeners == null) {
-            itemSetChangeListeners = new LinkedList<Container.ItemSetChangeListener>();
-        }
-        itemSetChangeListeners.add(listener);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin
-     * .data.Container.ItemSetChangeListener)
-     */
-    public void removeListener(Container.ItemSetChangeListener listener) {
-        if (itemSetChangeListeners != null) {
-            itemSetChangeListeners.remove(listener);
-        }
-    }
-
-    protected void fireContentsChange() {
-        if (itemSetChangeListeners != null) {
-            final Object[] l = itemSetChangeListeners.toArray();
-            final Container.ItemSetChangeEvent event = new SQLContainer.ItemSetChangeEvent(
-                    this);
-            for (int i = 0; i < l.length; i++) {
-                ((Container.ItemSetChangeListener) l[i])
-                        .containerItemSetChange(event);
-            }
-        }
-    }
-
-    /**
-     * Simple ItemSetChangeEvent implementation.
-     */
-    @SuppressWarnings("serial")
-    public class ItemSetChangeEvent extends EventObject implements
-            Container.ItemSetChangeEvent {
-
-        private ItemSetChangeEvent(SQLContainer source) {
-            super(source);
-        }
-
-        public Container getContainer() {
-            return (Container) getSource();
-        }
-    }
-
-    /**************************************************/
-    /** ROWIDCHANGELISTENER PASSING TO QUERYDELEGATE **/
-    /**************************************************/
-
-    /**
-     * Adds a RowIdChangeListener to the QueryDelegate
-     * 
-     * @param listener
-     */
-    public void addListener(RowIdChangeListener listener) {
-        if (delegate instanceof QueryDelegate.RowIdChangeNotifier) {
-            ((QueryDelegate.RowIdChangeNotifier) delegate)
-                    .addListener(listener);
-        }
-    }
-
-    /**
-     * Removes a RowIdChangeListener from the QueryDelegate
-     * 
-     * @param listener
-     */
-    public void removeListener(RowIdChangeListener listener) {
-        if (delegate instanceof QueryDelegate.RowIdChangeNotifier) {
-            ((QueryDelegate.RowIdChangeNotifier) delegate)
-                    .removeListener(listener);
-        }
-    }
-
-    public boolean isDebugMode() {
-        return debugMode;
-    }
-
-    public void setDebugMode(boolean debugMode) {
-        this.debugMode = debugMode;
-    }
-
-    /**
-     * Output a debug message or a stack trace of an exception
-     * 
-     * @param message
-     */
-    private void debug(Exception e, String message) {
-        if (debugMode) {
-            // TODO: Replace with the common Vaadin logging system once it is
-            // available.
-            if (message != null) {
-                System.err.println(message);
-            }
-            if (e != null) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    /**
-     * Calling this will enable this SQLContainer to send and receive cache
-     * flush notifications for its lifetime.
-     */
-    public void enableCacheFlushNotifications() {
-        if (!notificationsEnabled) {
-            notificationsEnabled = true;
-            CacheFlushNotifier.addInstance(this);
-        }
-    }
-
-    /******************************************/
-    /** Referencing mechanism implementation **/
-    /******************************************/
-
-    /**
-     * Adds a new reference to the given SQLContainer. In addition to the
-     * container you must provide the column (property) names used for the
-     * reference in both this and the referenced SQLContainer.
-     * 
-     * Note that multiple references pointing to the same SQLContainer are not
-     * supported.
-     * 
-     * @param refdCont
-     *            Target SQLContainer of the new reference
-     * @param refingCol
-     *            Column (property) name in this container storing the (foreign
-     *            key) reference
-     * @param refdCol
-     *            Column (property) name in the referenced container storing the
-     *            referenced key
-     */
-    public void addReference(SQLContainer refdCont, String refingCol,
-            String refdCol) {
-        if (refdCont == null) {
-            throw new IllegalArgumentException(
-                    "Referenced SQLContainer can not be null.");
-        }
-        if (!getContainerPropertyIds().contains(refingCol)) {
-            throw new IllegalArgumentException(
-                    "Given referencing column name is invalid."
-                            + " Please ensure that this container"
-                            + " contains a property ID named: " + refingCol);
-        }
-        if (!refdCont.getContainerPropertyIds().contains(refdCol)) {
-            throw new IllegalArgumentException(
-                    "Given referenced column name is invalid."
-                            + " Please ensure that the referenced container"
-                            + " contains a property ID named: " + refdCol);
-        }
-        if (references.keySet().contains(refdCont)) {
-            throw new IllegalArgumentException(
-                    "An SQLContainer instance can only be referenced once.");
-        }
-        references.put(refdCont, new Reference(refdCont, refingCol, refdCol));
-    }
-
-    /**
-     * Removes the reference pointing to the given SQLContainer.
-     * 
-     * @param refdCont
-     *            Target SQLContainer of the reference
-     * @return true if successful, false if the reference did not exist
-     */
-    public boolean removeReference(SQLContainer refdCont) {
-        if (refdCont == null) {
-            throw new IllegalArgumentException(
-                    "Referenced SQLContainer can not be null.");
-        }
-        return references.remove(refdCont) == null ? false : true;
-    }
-
-    /**
-     * Sets the referenced item. The referencing column of the item in this
-     * container is updated accordingly.
-     * 
-     * @param itemId
-     *            Item Id of the reference source (from this container)
-     * @param refdItemId
-     *            Item Id of the reference target (from referenced container)
-     * @param refdCont
-     *            Target SQLContainer of the reference
-     * @return true if the referenced item was successfully set, false on
-     *         failure
-     */
-    public boolean setReferencedItem(Object itemId, Object refdItemId,
-            SQLContainer refdCont) {
-        if (refdCont == null) {
-            throw new IllegalArgumentException(
-                    "Referenced SQLContainer can not be null.");
-        }
-        Reference r = references.get(refdCont);
-        if (r == null) {
-            throw new IllegalArgumentException(
-                    "Reference to the given SQLContainer not defined.");
-        }
-        try {
-            getContainerProperty(itemId, r.getReferencingColumn()).setValue(
-                    refdCont.getContainerProperty(refdItemId,
-                            r.getReferencedColumn()));
-            return true;
-        } catch (Exception e) {
-            debug(e, "Setting referenced item failed.");
-            return false;
-        }
-    }
-
-    /**
-     * Fetches the Item Id of the referenced item from the target SQLContainer.
-     * 
-     * @param itemId
-     *            Item Id of the reference source (from this container)
-     * @param refdCont
-     *            Target SQLContainer of the reference
-     * @return Item Id of the referenced item, or null if not found
-     */
-    public Object getReferencedItemId(Object itemId, SQLContainer refdCont) {
-        if (refdCont == null) {
-            throw new IllegalArgumentException(
-                    "Referenced SQLContainer can not be null.");
-        }
-        Reference r = references.get(refdCont);
-        if (r == null) {
-            throw new IllegalArgumentException(
-                    "Reference to the given SQLContainer not defined.");
-        }
-        Object refKey = getContainerProperty(itemId, r.getReferencingColumn())
-                .getValue();
-
-        refdCont.removeAllContainerFilters();
-        refdCont.addContainerFilter(new Equal(r.getReferencedColumn(), refKey));
-        Object toReturn = refdCont.firstItemId();
-        refdCont.removeAllContainerFilters();
-        return toReturn;
-    }
-
-    /**
-     * Fetches the referenced item from the target SQLContainer.
-     * 
-     * @param itemId
-     *            Item Id of the reference source (from this container)
-     * @param refdCont
-     *            Target SQLContainer of the reference
-     * @return The referenced item, or null if not found
-     */
-    public Item getReferencedItem(Object itemId, SQLContainer refdCont) {
-        return refdCont.getItem(getReferencedItemId(itemId, refdCont));
-    }
-
-}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/SQLUtil.java b/src/com/vaadin/data/util/SQLUtil.java
deleted file mode 100644 (file)
index 5426d8d..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.vaadin.data.util;
-
-public class SQLUtil {
-       /**
-     * Escapes different special characters in strings that are passed to SQL.
-     * Replaces the following:
-     * 
-     * <list> <li>' is replaced with ''</li> <li>\x00 is removed</li> <li>\ is
-     * replaced with \\</li> <li>" is replaced with \"</li> <li>
-     * \x1a is removed</li> </list>
-     * 
-     * Also note! The escaping done here may or may not be enough to prevent any
-     * and all SQL injections so it is recommended to check user input before
-     * giving it to the SQLContainer/TableQuery.
-     * 
-     * @param constant
-     * @return \\\'\'
-     */
-    public static String escapeSQL(String constant) {
-        if (constant == null) {
-            return null;
-        }
-        String fixedConstant = constant;
-        fixedConstant = fixedConstant.replaceAll("\\\\x00", "");
-        fixedConstant = fixedConstant.replaceAll("\\\\x1a", "");
-        fixedConstant = fixedConstant.replaceAll("'", "''");
-        fixedConstant = fixedConstant.replaceAll("\\\\", "\\\\\\\\");
-        fixedConstant = fixedConstant.replaceAll("\\\"", "\\\\\"");
-        return fixedConstant;
-    }
-}
diff --git a/src/com/vaadin/data/util/TemporaryRowId.java b/src/com/vaadin/data/util/TemporaryRowId.java
deleted file mode 100644 (file)
index ec5ca7f..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.vaadin.data.util;
-
-public class TemporaryRowId extends RowId {
-    private static final long serialVersionUID = -641983830469018329L;
-
-    public TemporaryRowId(Object[] id) {
-        super(id);
-    }
-
-    @Override
-    public int hashCode() {
-        return id.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || !(obj instanceof TemporaryRowId)) {
-            return false;
-        }
-        Object[] compId = ((TemporaryRowId) obj).getId();
-        return id.equals(compId);
-    }
-
-    @Override
-    public String toString() {
-        return "Temporary row id";
-    }
-
-}
diff --git a/src/com/vaadin/data/util/connection/J2EEConnectionPool.java b/src/com/vaadin/data/util/connection/J2EEConnectionPool.java
deleted file mode 100644 (file)
index 789fcb9..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.vaadin.data.util.connection;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.sql.DataSource;
-
-public class J2EEConnectionPool implements JDBCConnectionPool {
-
-    private String dataSourceJndiName;
-
-    private DataSource dataSource = null;
-
-    public J2EEConnectionPool(DataSource dataSource) {
-        this.dataSource = dataSource;
-    }
-
-    public J2EEConnectionPool(String dataSourceJndiName) {
-        this.dataSourceJndiName = dataSourceJndiName;
-    }
-
-    public Connection reserveConnection() throws SQLException {
-        Connection conn = getDataSource().getConnection();
-        conn.setAutoCommit(false);
-
-        return conn;
-    }
-
-    private DataSource getDataSource() throws SQLException {
-        if (dataSource == null) {
-            dataSource = lookupDataSource();
-        }
-        return dataSource;
-    }
-
-    private DataSource lookupDataSource() throws SQLException {
-        try {
-            InitialContext ic = new InitialContext();
-            return (DataSource) ic.lookup(dataSourceJndiName);
-        } catch (NamingException e) {
-            throw new SQLException(
-                    "NamingException - Cannot connect to the database. Cause: "
-                            + e.getMessage());
-        }
-    }
-
-    public void releaseConnection(Connection conn) {
-        try {
-            conn.close();
-        } catch (SQLException e) {
-            e.printStackTrace();
-        }
-    }
-
-    public void destroy() {
-        dataSource = null;
-    }
-
-}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/connection/JDBCConnectionPool.java b/src/com/vaadin/data/util/connection/JDBCConnectionPool.java
deleted file mode 100644 (file)
index 8f10d77..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.vaadin.data.util.connection;
-
-import java.io.Serializable;
-import java.sql.Connection;
-import java.sql.SQLException;
-
-/**
- * Interface for implementing connection pools to be used with SQLContainer.
- */
-public interface JDBCConnectionPool extends Serializable {
-    /**
-     * Retrieves a connection.
-     * 
-     * @return a usable connection to the database
-     * @throws SQLException
-     */
-    public Connection reserveConnection() throws SQLException;
-
-    /**
-     * Releases a connection that was retrieved earlier.
-     * 
-     * Note that depending on implementation, the transaction possibly open in
-     * the connection may or may not be rolled back.
-     * 
-     * @param conn
-     *            Connection to be released
-     */
-    public void releaseConnection(Connection conn);
-
-    /**
-     * Destroys the connection pool: close() is called an all the connections in
-     * the pool, whether available or reserved.
-     * 
-     * This method was added to fix PostgreSQL -related issues with connections
-     * that were left hanging 'idle'.
-     */
-    public void destroy();
-}
diff --git a/src/com/vaadin/data/util/connection/SimpleJDBCConnectionPool.java b/src/com/vaadin/data/util/connection/SimpleJDBCConnectionPool.java
deleted file mode 100644 (file)
index 91da3f1..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-package com.vaadin.data.util.connection;\r
-\r
-import java.io.IOException;\r
-import java.sql.Connection;\r
-import java.sql.DriverManager;\r
-import java.sql.SQLException;\r
-import java.sql.Statement;\r
-import java.util.HashSet;\r
-import java.util.Set;\r
-\r
-/**\r
- * Simple implementation of the JDBCConnectionPool interface. Handles loading\r
- * the JDBC driver, setting up the connections and ensuring they are still\r
- * usable upon release.\r
- */\r
-@SuppressWarnings("serial")\r
-public class SimpleJDBCConnectionPool implements JDBCConnectionPool {\r
-\r
-    private int initialConnections = 5;\r
-    private int maxConnections = 20;\r
-\r
-    private String driverName;\r
-    private String connectionUri;\r
-    private String userName;\r
-    private String password;\r
-\r
-    private transient Set<Connection> availableConnections;\r
-    private transient Set<Connection> reservedConnections;\r
-\r
-    private boolean initialized;\r
-\r
-    public SimpleJDBCConnectionPool(String driverName, String connectionUri,\r
-            String userName, String password) throws SQLException {\r
-        if (driverName == null) {\r
-            throw new IllegalArgumentException(\r
-                    "JDBC driver class name must be given.");\r
-        }\r
-        if (connectionUri == null) {\r
-            throw new IllegalArgumentException(\r
-                    "Database connection URI must be given.");\r
-        }\r
-        if (userName == null) {\r
-            throw new IllegalArgumentException(\r
-                    "Database username must be given.");\r
-        }\r
-        if (password == null) {\r
-            throw new IllegalArgumentException(\r
-                    "Database password must be given.");\r
-        }\r
-        this.driverName = driverName;\r
-        this.connectionUri = connectionUri;\r
-        this.userName = userName;\r
-        this.password = password;\r
-\r
-        /* Initialize JDBC driver */\r
-        try {\r
-            Class.forName(driverName).newInstance();\r
-        } catch (Exception ex) {\r
-            throw new RuntimeException("Specified JDBC Driver: " + driverName\r
-                    + " - initialization failed.", ex);\r
-        }\r
-    }\r
-\r
-    public SimpleJDBCConnectionPool(String driverName, String connectionUri,\r
-            String userName, String password, int initialConnections,\r
-            int maxConnections) throws SQLException {\r
-        this(driverName, connectionUri, userName, password);\r
-        this.initialConnections = initialConnections;\r
-        this.maxConnections = maxConnections;\r
-    }\r
-\r
-    private void initializeConnections() throws SQLException {\r
-        availableConnections = new HashSet<Connection>(initialConnections);\r
-        reservedConnections = new HashSet<Connection>(initialConnections);\r
-        for (int i = 0; i < initialConnections; i++) {\r
-            availableConnections.add(createConnection());\r
-        }\r
-        initialized = true;\r
-    }\r
-\r
-    public synchronized Connection reserveConnection() throws SQLException {\r
-        if (!initialized) {\r
-            initializeConnections();\r
-        }\r
-        if (availableConnections.isEmpty()) {\r
-            if (reservedConnections.size() < maxConnections) {\r
-                availableConnections.add(createConnection());\r
-            } else {\r
-                throw new SQLException("Connection limit has been reached.");\r
-            }\r
-        }\r
-\r
-        Connection c = availableConnections.iterator().next();\r
-        availableConnections.remove(c);\r
-        reservedConnections.add(c);\r
-\r
-        return c;\r
-    }\r
-\r
-    public synchronized void releaseConnection(Connection conn) {\r
-        if (conn == null || !initialized) {\r
-            return;\r
-        }\r
-        /* Try to roll back if necessary */\r
-        try {\r
-            if (!conn.getAutoCommit()) {\r
-                conn.rollback();\r
-            }\r
-        } catch (SQLException e) {\r
-            /* Roll back failed, close and discard connection */\r
-            try {\r
-                conn.close();\r
-            } catch (SQLException e1) {\r
-                /* Nothing needs to be done */\r
-            }\r
-            reservedConnections.remove(conn);\r
-            return;\r
-        }\r
-        reservedConnections.remove(conn);\r
-        availableConnections.add(conn);\r
-    }\r
-\r
-    private Connection createConnection() throws SQLException {\r
-        Connection c = DriverManager.getConnection(connectionUri, userName,\r
-                password);\r
-        c.setAutoCommit(false);\r
-        if (driverName.toLowerCase().contains("mysql")) {\r
-            try {\r
-                Statement s = c.createStatement();\r
-                s.execute("SET SESSION sql_mode = 'ANSI'");\r
-                s.close();\r
-            } catch (Exception e) {\r
-                // Failed to set ansi mode; continue\r
-            }\r
-        }\r
-        return c;\r
-    }\r
-\r
-    public void destroy() {\r
-        for (Connection c : availableConnections) {\r
-            try {\r
-                c.close();\r
-            } catch (SQLException e) {\r
-                // No need to do anything\r
-            }\r
-        }\r
-        for (Connection c : reservedConnections) {\r
-            try {\r
-                c.close();\r
-            } catch (SQLException e) {\r
-                // No need to do anything\r
-            }\r
-        }\r
-\r
-    }\r
-\r
-    private void writeObject(java.io.ObjectOutputStream out) throws IOException {\r
-        initialized = false;\r
-        out.defaultWriteObject();\r
-    }\r
-\r
-}\r
diff --git a/src/com/vaadin/data/util/query/FreeformQuery.java b/src/com/vaadin/data/util/query/FreeformQuery.java
deleted file mode 100644 (file)
index 2ac67c9..0000000
+++ /dev/null
@@ -1,450 +0,0 @@
-package com.vaadin.data.util.query;
-
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.RowItem;
-import com.vaadin.data.util.SQLContainer;
-import com.vaadin.data.util.connection.JDBCConnectionPool;
-import com.vaadin.data.util.query.generator.StatementHelper;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
-
-@SuppressWarnings("serial")
-public class FreeformQuery implements QueryDelegate {
-
-    FreeformQueryDelegate delegate = null;
-    private String queryString;
-    private List<String> primaryKeyColumns;
-    private JDBCConnectionPool connectionPool;
-    private transient Connection activeConnection = null;
-
-    /**
-     * Prevent no-parameters instantiation of FreeformQuery
-     */
-    @SuppressWarnings("unused")
-    private FreeformQuery() {
-    }
-
-    /**
-     * Creates a new freeform query delegate to be used with the
-     * {@link SQLContainer}.
-     * 
-     * @param queryString
-     *            The actual query to perform.
-     * @param primaryKeyColumns
-     *            The primary key columns. Read-only mode is forced if this
-     *            parameter is null or empty.
-     * @param connectionPool
-     *            the JDBCConnectionPool to use to open connections to the SQL
-     *            database.
-     * @deprecated @see
-     *             {@link FreeformQuery#FreeformQuery(String, JDBCConnectionPool, String...)}
-     */
-    @Deprecated
-    public FreeformQuery(String queryString, List<String> primaryKeyColumns,
-            JDBCConnectionPool connectionPool) {
-        if (primaryKeyColumns == null) {
-            primaryKeyColumns = new ArrayList<String>();
-        }
-        if (primaryKeyColumns.contains("")) {
-            throw new IllegalArgumentException(
-                    "The primary key columns contain an empty string!");
-        } else if (queryString == null || "".equals(queryString)) {
-            throw new IllegalArgumentException(
-                    "The query string may not be empty or null!");
-        } else if (connectionPool == null) {
-            throw new IllegalArgumentException(
-                    "The connectionPool may not be null!");
-        }
-        this.queryString = queryString;
-        this.primaryKeyColumns = Collections
-                .unmodifiableList(primaryKeyColumns);
-        this.connectionPool = connectionPool;
-    }
-
-    /**
-     * Creates a new freeform query delegate to be used with the
-     * {@link SQLContainer}.
-     * 
-     * @param queryString
-     *            The actual query to perform.
-     * @param connectionPool
-     *            the JDBCConnectionPool to use to open connections to the SQL
-     *            database.
-     * @param primaryKeyColumns
-     *            The primary key columns. Read-only mode is forced if none are
-     *            provided. (optional)
-     */
-    public FreeformQuery(String queryString, JDBCConnectionPool connectionPool,
-            String... primaryKeyColumns) {
-        this(queryString, Arrays.asList(primaryKeyColumns), connectionPool);
-    }
-
-    /**
-     * This implementation of getCount() actually fetches all records from the
-     * database, which might be a performance issue. Override this method with a
-     * SELECT COUNT(*) ... query if this is too slow for your needs.
-     * 
-     * {@inheritDoc}
-     */
-    public int getCount() throws SQLException {
-        // First try the delegate
-        int count = countByDelegate();
-        if (count < 0) {
-            // Couldn't use the delegate, use the bad way.
-            Connection conn = getConnection();
-            Statement statement = conn.createStatement(
-                    ResultSet.TYPE_SCROLL_INSENSITIVE,
-                    ResultSet.CONCUR_READ_ONLY);
-
-            ResultSet rs = statement.executeQuery(queryString);
-            if (rs.last()) {
-                count = rs.getRow();
-            } else {
-                count = 0;
-            }
-            rs.close();
-            statement.close();
-            releaseConnection(conn);
-        }
-        return count;
-    }
-
-    @SuppressWarnings("deprecation")
-    private int countByDelegate() throws SQLException {
-        int count = -1;
-        if (delegate == null) {
-            return count;
-        }
-        /* First try using prepared statement */
-        if (delegate instanceof FreeformStatementDelegate) {
-            try {
-                StatementHelper sh = ((FreeformStatementDelegate) delegate)
-                        .getCountStatement();
-                Connection c = getConnection();
-                PreparedStatement pstmt = c.prepareStatement(sh
-                        .getQueryString());
-                sh.setParameterValuesToStatement(pstmt);
-                ResultSet rs = pstmt.executeQuery();
-                rs.next();
-                count = rs.getInt(1);
-                rs.close();
-                pstmt.clearParameters();
-                pstmt.close();
-                releaseConnection(c);
-                return count;
-            } catch (UnsupportedOperationException e) {
-                // Count statement generation not supported
-            }
-        }
-        /* Try using regular statement */
-        try {
-            String countQuery = delegate.getCountQuery();
-            if (countQuery != null) {
-                Connection conn = getConnection();
-                Statement statement = conn.createStatement();
-                ResultSet rs = statement.executeQuery(countQuery);
-                rs.next();
-                count = rs.getInt(1);
-                rs.close();
-                statement.close();
-                releaseConnection(conn);
-                return count;
-            }
-        } catch (UnsupportedOperationException e) {
-            // Count query generation not supported
-        }
-        return count;
-    }
-
-    private Connection getConnection() throws SQLException {
-        if (activeConnection != null) {
-            return activeConnection;
-        }
-        return connectionPool.reserveConnection();
-    }
-
-    /**
-     * Fetches the results for the query. This implementation always fetches the
-     * entire record set, ignoring the offset and pagelength parameters. In
-     * order to support lazy loading of records, you must supply a
-     * FreeformQueryDelegate that implements the
-     * FreeformQueryDelegate.getQueryString(int,int) method.
-     * 
-     * @throws SQLException
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getQueryString(int,
-     *      int) {@inheritDoc}
-     */
-    @SuppressWarnings("deprecation")
-    public ResultSet getResults(int offset, int pagelength) throws SQLException {
-        if (activeConnection == null) {
-            throw new SQLException("No active transaction!");
-        }
-        String query = queryString;
-        if (delegate != null) {
-            /* First try using prepared statement */
-            if (delegate instanceof FreeformStatementDelegate) {
-                try {
-                    StatementHelper sh = ((FreeformStatementDelegate) delegate)
-                            .getQueryStatement(offset, pagelength);
-                    PreparedStatement pstmt = activeConnection
-                            .prepareStatement(sh.getQueryString());
-                    sh.setParameterValuesToStatement(pstmt);
-                    return pstmt.executeQuery();
-                } catch (UnsupportedOperationException e) {
-                    // Statement generation not supported, continue...
-                }
-            }
-            try {
-                query = delegate.getQueryString(offset, pagelength);
-            } catch (UnsupportedOperationException e) {
-                // This is fine, we'll just use the default queryString.
-            }
-        }
-        Statement statement = activeConnection.createStatement();
-        ResultSet rs = statement.executeQuery(query);
-        return rs;
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean implementationRespectsPagingLimits() {
-        if (delegate == null) {
-            return false;
-        }
-        /* First try using prepared statement */
-        if (delegate instanceof FreeformStatementDelegate) {
-            try {
-                StatementHelper sh = ((FreeformStatementDelegate) delegate)
-                        .getCountStatement();
-                if (sh != null && sh.getQueryString() != null
-                        && sh.getQueryString().length() > 0) {
-                    return true;
-                }
-            } catch (UnsupportedOperationException e) {
-                // Statement generation not supported, continue...
-            }
-        }
-        try {
-            String queryString = delegate.getQueryString(0, 50);
-            return queryString != null && queryString.length() > 0;
-        } catch (UnsupportedOperationException e) {
-            return false;
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util
-     * .List)
-     */
-    public void setFilters(List<Filter> filters)
-            throws UnsupportedOperationException {
-        if (delegate != null) {
-            delegate.setFilters(filters);
-        } else if (filters != null) {
-            throw new UnsupportedOperationException(
-                    "FreeFormQueryDelegate not set!");
-        }
-    }
-
-    public void setOrderBy(List<OrderBy> orderBys)
-            throws UnsupportedOperationException {
-        if (delegate != null) {
-            delegate.setOrderBy(orderBys);
-        } else if (orderBys != null) {
-            throw new UnsupportedOperationException(
-                    "FreeFormQueryDelegate not set!");
-        }
-    }
-
-    public int storeRow(RowItem row) throws SQLException {
-        if (activeConnection == null) {
-            throw new IllegalStateException("No transaction is active!");
-        } else if (primaryKeyColumns.isEmpty()) {
-            throw new UnsupportedOperationException(
-                    "Cannot store items fetched with a read-only freeform query!");
-        }
-        if (delegate != null) {
-            return delegate.storeRow(activeConnection, row);
-        } else {
-            throw new UnsupportedOperationException(
-                    "FreeFormQueryDelegate not set!");
-        }
-    }
-
-    public boolean removeRow(RowItem row) throws SQLException {
-        if (activeConnection == null) {
-            throw new IllegalStateException("No transaction is active!");
-        } else if (primaryKeyColumns.isEmpty()) {
-            throw new UnsupportedOperationException(
-                    "Cannot remove items fetched with a read-only freeform query!");
-        }
-        if (delegate != null) {
-            return delegate.removeRow(activeConnection, row);
-        } else {
-            throw new UnsupportedOperationException(
-                    "FreeFormQueryDelegate not set!");
-        }
-    }
-
-    public synchronized void beginTransaction()
-            throws UnsupportedOperationException, SQLException {
-        if (activeConnection != null) {
-            throw new IllegalStateException("A transaction is already active!");
-        }
-        activeConnection = connectionPool.reserveConnection();
-        activeConnection.setAutoCommit(false);
-    }
-
-    public synchronized void commit() throws UnsupportedOperationException,
-            SQLException {
-        if (activeConnection == null) {
-            throw new SQLException("No active transaction");
-        }
-        if (!activeConnection.getAutoCommit()) {
-            activeConnection.commit();
-        }
-        connectionPool.releaseConnection(activeConnection);
-        activeConnection = null;
-    }
-
-    public synchronized void rollback() throws UnsupportedOperationException,
-            SQLException {
-        if (activeConnection == null) {
-            throw new SQLException("No active transaction");
-        }
-        activeConnection.rollback();
-        connectionPool.releaseConnection(activeConnection);
-        activeConnection = null;
-    }
-
-    public List<String> getPrimaryKeyColumns() {
-        return primaryKeyColumns;
-    }
-
-    public String getQueryString() {
-        return queryString;
-    }
-
-    public FreeformQueryDelegate getDelegate() {
-        return delegate;
-    }
-
-    public void setDelegate(FreeformQueryDelegate delegate) {
-        this.delegate = delegate;
-    }
-
-    /**
-     * This implementation of the containsRowWithKey method rewrites existing
-     * WHERE clauses in the query string. The logic is, however, not very
-     * complex and some times can do the Wrong Thing<sup>TM</sup>. For the
-     * situations where this logic is not enough, you can implement the
-     * getContainsRowQueryString method in FreeformQueryDelegate and this will
-     * be used instead of the logic.
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getContainsRowQueryString(Object...)
-     * 
-     *      {@inheritDoc}
-     */
-    @SuppressWarnings("deprecation")
-    public boolean containsRowWithKey(Object... keys) throws SQLException {
-        String query = null;
-        boolean contains = false;
-        if (delegate != null) {
-            if (delegate instanceof FreeformStatementDelegate) {
-                try {
-                    StatementHelper sh = ((FreeformStatementDelegate) delegate)
-                            .getContainsRowQueryStatement(keys);
-                    Connection c = getConnection();
-                    PreparedStatement pstmt = c.prepareStatement(sh
-                            .getQueryString());
-                    sh.setParameterValuesToStatement(pstmt);
-                    ResultSet rs = pstmt.executeQuery();
-                    contains = rs.next();
-                    rs.close();
-                    pstmt.clearParameters();
-                    pstmt.close();
-                    releaseConnection(c);
-                    return contains;
-                } catch (UnsupportedOperationException e) {
-                    // Statement generation not supported, continue...
-                }
-            }
-            try {
-                query = delegate.getContainsRowQueryString(keys);
-            } catch (UnsupportedOperationException e) {
-                query = modifyWhereClause(keys);
-            }
-        } else {
-            query = modifyWhereClause(keys);
-        }
-        Connection conn = getConnection();
-        try {
-            Statement statement = conn.createStatement();
-            ResultSet rs = statement.executeQuery(query);
-            contains = rs.next();
-            rs.close();
-            statement.close();
-        } finally {
-            releaseConnection(conn);
-        }
-        return contains;
-    }
-
-    /**
-     * Releases the connection if it is not part of an active transaction.
-     * 
-     * @param conn
-     *            the connection to release
-     */
-    private void releaseConnection(Connection conn) {
-        if (conn != activeConnection) {
-            connectionPool.releaseConnection(conn);
-        }
-    }
-
-    private String modifyWhereClause(Object... keys) {
-        // Build the where rules for the provided keys
-        StringBuffer where = new StringBuffer();
-        for (int ix = 0; ix < primaryKeyColumns.size(); ix++) {
-            where.append(QueryBuilder.quote(primaryKeyColumns.get(ix)));
-            if (keys[ix] == null) {
-                where.append(" IS NULL");
-            } else {
-                where.append(" = '").append(keys[ix]).append("'");
-            }
-            if (ix < primaryKeyColumns.size() - 1) {
-                where.append(" AND ");
-            }
-        }
-        // Is there already a WHERE clause in the query string?
-        int index = queryString.toLowerCase().indexOf("where ");
-        if (index > -1) {
-            // Rewrite the where clause
-            return queryString.substring(0, index) + "WHERE " + where + " AND "
-                    + queryString.substring(index + 6);
-        }
-        // Append a where clause
-        return queryString + " WHERE " + where;
-    }
-
-    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
-        try {
-            rollback();
-        } catch (SQLException ignored) {
-        }
-        out.defaultWriteObject();
-    }
-}
diff --git a/src/com/vaadin/data/util/query/FreeformQueryDelegate.java b/src/com/vaadin/data/util/query/FreeformQueryDelegate.java
deleted file mode 100644 (file)
index c87acec..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.vaadin.data.util.query;
-
-import java.io.Serializable;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.List;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.RowItem;
-
-public interface FreeformQueryDelegate extends Serializable {
-    /**
-     * Should return the SQL query string to be performed. This method is
-     * responsible for gluing together the select query from the filters and the
-     * order by conditions if these are supported.
-     * 
-     * @param offset
-     *            the first record (row) to fetch.
-     * @param pagelength
-     *            the number of records (rows) to fetch. 0 means all records
-     *            starting from offset.
-     * @deprecated Implement {@link FreeformStatementDelegate} instead of
-     *             {@link FreeformQueryDelegate}
-     */
-    @Deprecated
-    public String getQueryString(int offset, int limit)
-            throws UnsupportedOperationException;
-
-    /**
-     * Generates and executes a query to determine the current row count from
-     * the DB. Row count will be fetched using filters that are currently set to
-     * the QueryDelegate.
-     * 
-     * @return row count
-     * @throws SQLException
-     * @deprecated Implement {@link FreeformStatementDelegate} instead of
-     *             {@link FreeformQueryDelegate}
-     */
-    @Deprecated
-    public String getCountQuery() throws UnsupportedOperationException;
-
-    /**
-     * Sets the filters to apply when performing the SQL query. These are
-     * translated into a WHERE clause. Default filtering mode will be used.
-     * 
-     * @param filters
-     *            The filters to apply.
-     * @throws UnsupportedOperationException
-     *             if the implementation doesn't support filtering.
-     */
-    public void setFilters(List<Filter> filters)
-            throws UnsupportedOperationException;
-
-    /**
-     * Sets the order in which to retrieve rows from the database. The result
-     * can be ordered by zero or more columns and each column can be in
-     * ascending or descending order. These are translated into an ORDER BY
-     * clause in the SQL query.
-     * 
-     * @param orderBys
-     *            A list of the OrderBy conditions.
-     * @throws UnsupportedOperationException
-     *             if the implementation doesn't support ordering.
-     */
-    public void setOrderBy(List<OrderBy> orderBys)
-            throws UnsupportedOperationException;
-
-    /**
-     * Stores a row in the database. The implementation of this interface
-     * decides how to identify whether to store a new row or update an existing
-     * one.
-     * 
-     * @param conn
-     *            the JDBC connection to use
-     * @param row
-     *            RowItem to be stored or updated.
-     * @throws UnsupportedOperationException
-     *             if the implementation is read only.
-     * @throws SQLException
-     */
-    public int storeRow(Connection conn, RowItem row)
-            throws UnsupportedOperationException, SQLException;
-
-    /**
-     * Removes the given RowItem from the database.
-     * 
-     * @param conn
-     *            the JDBC connection to use
-     * @param row
-     *            RowItem to be removed
-     * @return true on success
-     * @throws UnsupportedOperationException
-     * @throws SQLException
-     */
-    public boolean removeRow(Connection conn, RowItem row)
-            throws UnsupportedOperationException, SQLException;
-
-    /**
-     * Generates an SQL Query string that allows the user of the FreeformQuery
-     * class to customize the query string used by the
-     * FreeformQuery.containsRowWithKeys() method. This is useful for cases when
-     * the logic in the containsRowWithKeys method is not enough to support more
-     * complex free form queries.
-     * 
-     * @param keys
-     *            the values of the primary keys
-     * @throws UnsupportedOperationException
-     *             to use the default logic in FreeformQuery
-     * @deprecated Implement {@link FreeformStatementDelegate} instead of
-     *             {@link FreeformQueryDelegate}
-     */
-    @Deprecated
-    public String getContainsRowQueryString(Object... keys)
-            throws UnsupportedOperationException;
-}
diff --git a/src/com/vaadin/data/util/query/FreeformStatementDelegate.java b/src/com/vaadin/data/util/query/FreeformStatementDelegate.java
deleted file mode 100644 (file)
index 0de8677..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.vaadin.data.util.query;\r
-\r
-import com.vaadin.data.util.query.generator.StatementHelper;\r
-\r
-/**\r
- * FreeformStatementDelegate is an extension to FreeformQueryDelegate that\r
- * provides definitions for methods that produce StatementHelper objects instead\r
- * of basic query strings. This allows the FreeformQuery query delegate to use\r
- * PreparedStatements instead of regular Statement when accessing the database.\r
- * \r
- * Due to the injection protection and other benefits of prepared statements, it\r
- * is advisable to implement this interface instead of the FreeformQueryDelegate\r
- * whenever possible.\r
- */\r
-public interface FreeformStatementDelegate extends FreeformQueryDelegate {\r
-    /**\r
-     * Should return a new instance of StatementHelper that contains the query\r
-     * string and parameter values required to create a PreparedStatement. This\r
-     * method is responsible for gluing together the select query from the\r
-     * filters and the order by conditions if these are supported.\r
-     * \r
-     * @param offset\r
-     *            the first record (row) to fetch.\r
-     * @param pagelength\r
-     *            the number of records (rows) to fetch. 0 means all records\r
-     *            starting from offset.\r
-     */\r
-    public StatementHelper getQueryStatement(int offset, int limit)\r
-            throws UnsupportedOperationException;\r
-\r
-    /**\r
-     * Should return a new instance of StatementHelper that contains the query\r
-     * string and parameter values required to create a PreparedStatement that\r
-     * will fetch the row count from the DB. Row count should be fetched using\r
-     * filters that are currently set to the QueryDelegate.\r
-     */\r
-    public StatementHelper getCountStatement()\r
-            throws UnsupportedOperationException;\r
-\r
-    /**\r
-     * Should return a new instance of StatementHelper that contains the query\r
-     * string and parameter values required to create a PreparedStatement used\r
-     * by the FreeformQuery.containsRowWithKeys() method. This is useful for\r
-     * cases when the default logic in said method is not enough to support more\r
-     * complex free form queries.\r
-     * \r
-     * @param keys\r
-     *            the values of the primary keys\r
-     * @throws UnsupportedOperationException\r
-     *             to use the default logic in FreeformQuery\r
-     */\r
-    public StatementHelper getContainsRowQueryStatement(Object... keys)\r
-            throws UnsupportedOperationException;\r
-}\r
diff --git a/src/com/vaadin/data/util/query/OrderBy.java b/src/com/vaadin/data/util/query/OrderBy.java
deleted file mode 100644 (file)
index d83d322..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.vaadin.data.util.query;
-
-import java.io.Serializable;
-
-/**
- * OrderBy represents a sorting rule to be applied to a query made by the
- * SQLContainer's QueryDelegate.
- * 
- * The sorting rule is simple and contains only the affected column's name and
- * the direction of the sort.
- */
-public class OrderBy implements Serializable {
-    private String column;
-    private boolean isAscending;
-
-    /**
-     * Prevent instantiation without required parameters.
-     */
-    @SuppressWarnings("unused")
-    private OrderBy() {
-    }
-
-    public OrderBy(String column, boolean isAscending) {
-        setColumn(column);
-        setAscending(isAscending);
-    }
-
-    public void setColumn(String column) {
-        this.column = column;
-    }
-
-    public String getColumn() {
-        return column;
-    }
-
-    public void setAscending(boolean isAscending) {
-        this.isAscending = isAscending;
-    }
-
-    public boolean isAscending() {
-        return isAscending;
-    }
-}
diff --git a/src/com/vaadin/data/util/query/QueryDelegate.java b/src/com/vaadin/data/util/query/QueryDelegate.java
deleted file mode 100644 (file)
index 95ec89f..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-package com.vaadin.data.util.query;
-
-import java.io.Serializable;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.RowId;
-import com.vaadin.data.util.RowItem;
-
-public interface QueryDelegate extends Serializable {
-    /**
-     * Generates and executes a query to determine the current row count from
-     * the DB. Row count will be fetched using filters that are currently set to
-     * the QueryDelegate.
-     * 
-     * @return row count
-     * @throws SQLException
-     */
-    public int getCount() throws SQLException;
-
-    /**
-     * Executes a paged SQL query and returns the ResultSet. The query is
-     * defined through implementations of this QueryDelegate interface.
-     * 
-     * @param offset
-     *            the first item of the page to load
-     * @param pagelength
-     *            the length of the page to load
-     * @return a ResultSet containing the rows of the page
-     * @throws SQLException
-     *             if the database access fails.
-     */
-    public ResultSet getResults(int offset, int pagelength) throws SQLException;
-
-    /**
-     * Allows the SQLContainer implementation to check whether the QueryDelegate
-     * implementation implements paging in the getResults method.
-     * 
-     * @see QueryDelegate#getResults(int, int)
-     * 
-     * @return true if the delegate implements paging
-     */
-    public boolean implementationRespectsPagingLimits();
-
-    /**
-     * Sets the filters to apply when performing the SQL query. These are
-     * translated into a WHERE clause. Default filtering mode will be used.
-     * 
-     * @param filters
-     *            The filters to apply.
-     * @throws UnsupportedOperationException
-     *             if the implementation doesn't support filtering.
-     */
-    public void setFilters(List<Filter> filters)
-            throws UnsupportedOperationException;
-
-    /**
-     * Sets the order in which to retrieve rows from the database. The result
-     * can be ordered by zero or more columns and each column can be in
-     * ascending or descending order. These are translated into an ORDER BY
-     * clause in the SQL query.
-     * 
-     * @param orderBys
-     *            A list of the OrderBy conditions.
-     * @throws UnsupportedOperationException
-     *             if the implementation doesn't support ordering.
-     */
-    public void setOrderBy(List<OrderBy> orderBys)
-            throws UnsupportedOperationException;
-
-    /**
-     * Stores a row in the database. The implementation of this interface
-     * decides how to identify whether to store a new row or update an existing
-     * one.
-     * 
-     * @param columnToValueMap
-     *            A map containing the values for all columns to be stored or
-     *            updated.
-     * @return the number of affected rows in the database table
-     * @throws UnsupportedOperationException
-     *             if the implementation is read only.
-     */
-    public int storeRow(RowItem row) throws UnsupportedOperationException,
-            SQLException;
-
-    /**
-     * Removes the given RowItem from the database.
-     * 
-     * @param row
-     *            RowItem to be removed
-     * @return true on success
-     * @throws UnsupportedOperationException
-     * @throws SQLException
-     */
-    public boolean removeRow(RowItem row) throws UnsupportedOperationException,
-            SQLException;
-
-    /**
-     * Starts a new database transaction. Used when storing multiple changes.
-     * 
-     * Note that if a transaction is already open, it will be rolled back when a
-     * new transaction is started.
-     * 
-     * @throws SQLException
-     *             if the database access fails.
-     */
-    public void beginTransaction() throws SQLException;
-
-    /**
-     * Commits a transaction. If a transaction is not open nothing should
-     * happen.
-     * 
-     * @throws SQLException
-     *             if the database access fails.
-     */
-    public void commit() throws SQLException;
-
-    /**
-     * Rolls a transaction back. If a transaction is not open nothing should
-     * happen.
-     * 
-     * @throws SQLException
-     *             if the database access fails.
-     */
-    public void rollback() throws SQLException;
-
-    /**
-     * Returns a list of primary key column names. The list is either fetched
-     * from the database (TableQuery) or given as an argument depending on
-     * implementation.
-     * 
-     * @return
-     */
-    public List<String> getPrimaryKeyColumns();
-
-    /**
-     * Performs a query to find out whether the SQL table contains a row with
-     * the given set of primary keys.
-     * 
-     * @param keys
-     *            the primary keys
-     * @return true if the SQL table contains a row with the provided keys
-     * @throws SQLException
-     */
-    public boolean containsRowWithKey(Object... keys) throws SQLException;
-
-    /************************/
-    /** ROWID CHANGE EVENT **/
-    /************************/
-
-    /**
-     * An <code>Event</code> object specifying the old and new RowId of an added
-     * item after the addition has been successfully committed.
-     */
-    public interface RowIdChangeEvent extends Serializable {
-        /**
-         * Gets the old (temporary) RowId of the added row that raised this
-         * event.
-         * 
-         * @return old RowId
-         */
-        public RowId getOldRowId();
-
-        /**
-         * Gets the new, possibly database assigned RowId of the added row that
-         * raised this event.
-         * 
-         * @return new RowId
-         */
-        public RowId getNewRowId();
-    }
-
-    /** RowId change listener interface. */
-    public interface RowIdChangeListener extends Serializable {
-        /**
-         * Lets the listener know that a RowId has been changed.
-         * 
-         * @param event
-         */
-        public void rowIdChange(QueryDelegate.RowIdChangeEvent event);
-    }
-
-    /**
-     * The interface for adding and removing <code>RowIdChangeEvent</code>
-     * listeners. By implementing this interface a class explicitly announces
-     * that it will generate a <code>RowIdChangeEvent</code> when it performs a
-     * database commit that may change the RowId.
-     */
-    public interface RowIdChangeNotifier extends Serializable {
-        /**
-         * Adds a RowIdChangeListener for the object.
-         * 
-         * @param listener
-         *            listener to be added
-         */
-        public void addListener(QueryDelegate.RowIdChangeListener listener);
-
-        /**
-         * Removes the specified RowIdChangeListener from the object.
-         * 
-         * @param listener
-         *            listener to be removed
-         */
-        public void removeListener(QueryDelegate.RowIdChangeListener listener);
-    }
-}
diff --git a/src/com/vaadin/data/util/query/TableQuery.java b/src/com/vaadin/data/util/query/TableQuery.java
deleted file mode 100644 (file)
index c44a748..0000000
+++ /dev/null
@@ -1,707 +0,0 @@
-package com.vaadin.data.util.query;
-
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EventObject;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.ColumnProperty;
-import com.vaadin.data.util.OptimisticLockException;
-import com.vaadin.data.util.RowId;
-import com.vaadin.data.util.RowItem;
-import com.vaadin.data.util.SQLUtil;
-import com.vaadin.data.util.TemporaryRowId;
-import com.vaadin.data.util.connection.JDBCConnectionPool;
-import com.vaadin.data.util.filter.Compare.Equal;
-import com.vaadin.data.util.query.generator.DefaultSQLGenerator;
-import com.vaadin.data.util.query.generator.MSSQLGenerator;
-import com.vaadin.data.util.query.generator.SQLGenerator;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-@SuppressWarnings("serial")
-public class TableQuery implements QueryDelegate,
-        QueryDelegate.RowIdChangeNotifier {
-
-    /** Table name, primary key column name(s) and version column name */
-    private String tableName;
-    private List<String> primaryKeyColumns;
-    private String versionColumn;
-
-    /** Currently set Filters and OrderBys */
-    private List<Filter> filters;
-    private List<OrderBy> orderBys;
-
-    /** SQLGenerator instance to use for generating queries */
-    private SQLGenerator sqlGenerator;
-
-    /** Fields related to Connection and Transaction handling */
-    private JDBCConnectionPool connectionPool;
-    private transient Connection activeConnection;
-    private boolean transactionOpen;
-
-    /** Row ID change listeners */
-    private LinkedList<RowIdChangeListener> rowIdChangeListeners;
-    /** Row ID change events, stored until commit() is called */
-    private final List<RowIdChangeEvent> bufferedEvents = new ArrayList<RowIdChangeEvent>();
-
-    /** Set to true to output generated SQL Queries to System.out */
-    private boolean debug = false;
-
-    /** Prevent no-parameters instantiation of TableQuery */
-    @SuppressWarnings("unused")
-    private TableQuery() {
-    }
-
-    /**
-     * Creates a new TableQuery using the given connection pool, SQL generator
-     * and table name to fetch the data from. All parameters must be non-null.
-     * 
-     * @param tableName
-     *            Name of the database table to connect to
-     * @param connectionPool
-     *            Connection pool for accessing the database
-     * @param sqlGenerator
-     *            SQL query generator implementation
-     */
-    public TableQuery(String tableName, JDBCConnectionPool connectionPool,
-            SQLGenerator sqlGenerator) {
-        if (tableName == null || tableName.trim().length() < 1
-                || connectionPool == null || sqlGenerator == null) {
-            throw new IllegalArgumentException(
-                    "All parameters must be non-null and a table name must be given.");
-        }
-        this.tableName = tableName;
-        this.sqlGenerator = sqlGenerator;
-        this.connectionPool = connectionPool;
-        fetchMetaData();
-    }
-
-    /**
-     * Creates a new TableQuery using the given connection pool and table name
-     * to fetch the data from. All parameters must be non-null. The default SQL
-     * generator will be used for queries.
-     * 
-     * @param tableName
-     *            Name of the database table to connect to
-     * @param connectionPool
-     *            Connection pool for accessing the database
-     */
-    public TableQuery(String tableName, JDBCConnectionPool connectionPool) {
-        this(tableName, connectionPool, new DefaultSQLGenerator());
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getCount()
-     */
-    public int getCount() throws SQLException {
-        debug("Fetching count...");
-        StatementHelper sh = sqlGenerator.generateSelectQuery(tableName,
-                filters, null, 0, 0, "COUNT(*)");
-        boolean shouldCloseTransaction = false;
-        if (!transactionOpen) {
-            shouldCloseTransaction = true;
-            beginTransaction();
-        }
-        ResultSet r = executeQuery(sh);
-        r.next();
-        int count = r.getInt(1);
-        r.getStatement().close();
-        r.close();
-        if (shouldCloseTransaction) {
-            commit();
-        }
-        return count;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getResults(int,
-     * int)
-     */
-    public ResultSet getResults(int offset, int pagelength) throws SQLException {
-        StatementHelper sh;
-        /*
-         * If no ordering is explicitly set, results will be ordered by the
-         * first primary key column.
-         */
-        if (orderBys == null || orderBys.isEmpty()) {
-            List<OrderBy> ob = new ArrayList<OrderBy>();
-            ob.add(new OrderBy(primaryKeyColumns.get(0), true));
-            sh = sqlGenerator.generateSelectQuery(tableName, filters, ob,
-                    offset, pagelength, null);
-        } else {
-            sh = sqlGenerator.generateSelectQuery(tableName, filters, orderBys,
-                    offset, pagelength, null);
-        }
-        return executeQuery(sh);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#
-     * implementationRespectsPagingLimits()
-     */
-    public boolean implementationRespectsPagingLimits() {
-        return true;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin
-     * .addon.sqlcontainer.RowItem)
-     */
-    public int storeRow(RowItem row) throws UnsupportedOperationException,
-            SQLException {
-        if (row == null) {
-            throw new IllegalArgumentException("Row argument must be non-null.");
-        }
-        StatementHelper sh;
-        int result = 0;
-        if (row.getId() instanceof TemporaryRowId) {
-            setVersionColumnFlagInProperty(row);
-            sh = sqlGenerator.generateInsertQuery(tableName, row);
-            result = executeUpdateReturnKeys(sh, row);
-        } else {
-            setVersionColumnFlagInProperty(row);
-            sh = sqlGenerator.generateUpdateQuery(tableName, row);
-            result = executeUpdate(sh);
-        }
-        if (versionColumn != null && result == 0) {
-            throw new OptimisticLockException(
-                    "Someone else changed the row that was being updated.",
-                    row.getId());
-        }
-        return result;
-    }
-
-    private void setVersionColumnFlagInProperty(RowItem row) {
-        ColumnProperty versionProperty = (ColumnProperty) row
-                .getItemProperty(versionColumn);
-        if (versionProperty != null) {
-            versionProperty.setVersionColumn(true);
-        }
-    }
-
-    /**
-     * Inserts the given row in the database table immediately. Begins and
-     * commits the transaction needed. This method was added specifically to
-     * solve the problem of returning the final RowId immediately on the
-     * SQLContainer.addItem() call when auto commit mode is enabled in the
-     * SQLContainer.
-     * 
-     * @param row
-     *            RowItem to add to the database
-     * @return Final RowId of the added row
-     * @throws SQLException
-     */
-    public RowId storeRowImmediately(RowItem row) throws SQLException {
-        beginTransaction();
-        /* Set version column, if one is provided */
-        setVersionColumnFlagInProperty(row);
-        /* Generate query */
-        StatementHelper sh = sqlGenerator.generateInsertQuery(tableName, row);
-        PreparedStatement pstmt = activeConnection.prepareStatement(
-                sh.getQueryString(), primaryKeyColumns.toArray(new String[0]));
-        sh.setParameterValuesToStatement(pstmt);
-        debug("DB -> " + sh.getQueryString());
-        int result = pstmt.executeUpdate();
-        if (result > 0) {
-            /*
-             * If affected rows exist, we'll get the new RowId, commit the
-             * transaction and return the new RowId.
-             */
-            ResultSet generatedKeys = pstmt.getGeneratedKeys();
-            RowId newId = getNewRowId(row, generatedKeys);
-            generatedKeys.close();
-            pstmt.clearParameters();
-            pstmt.close();
-            commit();
-            return newId;
-        } else {
-            pstmt.clearParameters();
-            pstmt.close();
-            /* On failure return null */
-            return null;
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util
-     * .List)
-     */
-    public void setFilters(List<Filter> filters)
-            throws UnsupportedOperationException {
-        if (filters == null) {
-            this.filters = null;
-            return;
-        }
-        this.filters = Collections.unmodifiableList(filters);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setOrderBy(java.util
-     * .List)
-     */
-    public void setOrderBy(List<OrderBy> orderBys)
-            throws UnsupportedOperationException {
-        if (orderBys == null) {
-            this.orderBys = null;
-            return;
-        }
-        this.orderBys = Collections.unmodifiableList(orderBys);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#beginTransaction()
-     */
-    public void beginTransaction() throws UnsupportedOperationException,
-            SQLException {
-        if (transactionOpen && activeConnection != null) {
-            throw new IllegalStateException();
-        }
-        debug("DB -> begin transaction");
-        activeConnection = connectionPool.reserveConnection();
-        activeConnection.setAutoCommit(false);
-        transactionOpen = true;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#commit()
-     */
-    public void commit() throws UnsupportedOperationException, SQLException {
-        if (transactionOpen && activeConnection != null) {
-            debug("DB -> commit");
-            activeConnection.commit();
-            connectionPool.releaseConnection(activeConnection);
-        } else {
-            throw new SQLException("No active transaction");
-        }
-        transactionOpen = false;
-
-        /* Handle firing row ID change events */
-        RowIdChangeEvent[] unFiredEvents = bufferedEvents
-                .toArray(new RowIdChangeEvent[] {});
-        bufferedEvents.clear();
-        if (rowIdChangeListeners != null && !rowIdChangeListeners.isEmpty()) {
-            for (RowIdChangeListener r : rowIdChangeListeners) {
-                for (RowIdChangeEvent e : unFiredEvents) {
-                    r.rowIdChange(e);
-                }
-            }
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#rollback()
-     */
-    public void rollback() throws UnsupportedOperationException, SQLException {
-        if (transactionOpen && activeConnection != null) {
-            debug("DB -> rollback");
-            activeConnection.rollback();
-            connectionPool.releaseConnection(activeConnection);
-        } else {
-            throw new SQLException("No active transaction");
-        }
-        transactionOpen = false;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#getPrimaryKeyColumns()
-     */
-    public List<String> getPrimaryKeyColumns() {
-        return Collections.unmodifiableList(primaryKeyColumns);
-    }
-
-    public String getVersionColumn() {
-        return versionColumn;
-    }
-
-    public void setVersionColumn(String column) {
-        versionColumn = column;
-    }
-
-    public String getTableName() {
-        return tableName;
-    }
-
-    public SQLGenerator getSqlGenerator() {
-        return sqlGenerator;
-    }
-
-    /**
-     * Executes the given query string using either the active connection if a
-     * transaction is already open, or a new connection from this query's
-     * connection pool.
-     * 
-     * @param sh
-     *            an instance of StatementHelper, containing the query string
-     *            and parameter values.
-     * @return ResultSet of the query
-     * @throws SQLException
-     */
-    private ResultSet executeQuery(StatementHelper sh) throws SQLException {
-        Connection c = null;
-        if (transactionOpen && activeConnection != null) {
-            c = activeConnection;
-        } else {
-            throw new SQLException("No active transaction!");
-        }
-        PreparedStatement pstmt = c.prepareStatement(sh.getQueryString());
-        sh.setParameterValuesToStatement(pstmt);
-        debug("DB -> " + sh.getQueryString());
-        return pstmt.executeQuery();
-    }
-
-    /**
-     * Executes the given update query string using either the active connection
-     * if a transaction is already open, or a new connection from this query's
-     * connection pool.
-     * 
-     * @param sh
-     *            an instance of StatementHelper, containing the query string
-     *            and parameter values.
-     * @return Number of affected rows
-     * @throws SQLException
-     */
-    private int executeUpdate(StatementHelper sh) throws SQLException {
-        Connection c = null;
-        PreparedStatement pstmt = null;
-        try {
-            if (transactionOpen && activeConnection != null) {
-                c = activeConnection;
-            } else {
-                c = connectionPool.reserveConnection();
-            }
-            pstmt = c.prepareStatement(sh.getQueryString());
-            sh.setParameterValuesToStatement(pstmt);
-            debug("DB -> " + sh.getQueryString());
-            int retval = pstmt.executeUpdate();
-            return retval;
-        } finally {
-            if (pstmt != null) {
-                pstmt.clearParameters();
-                pstmt.close();
-            }
-            if (!transactionOpen) {
-                connectionPool.releaseConnection(c);
-            }
-        }
-    }
-
-    /**
-     * Executes the given update query string using either the active connection
-     * if a transaction is already open, or a new connection from this query's
-     * connection pool.
-     * 
-     * Additionally adds a new RowIdChangeEvent to the event buffer.
-     * 
-     * @param sh
-     *            an instance of StatementHelper, containing the query string
-     *            and parameter values.
-     * @param row
-     *            the row item to update
-     * @return Number of affected rows
-     * @throws SQLException
-     */
-    private int executeUpdateReturnKeys(StatementHelper sh, RowItem row)
-            throws SQLException {
-        Connection c = null;
-        PreparedStatement pstmt = null;
-        ResultSet genKeys = null;
-        try {
-            if (transactionOpen && activeConnection != null) {
-                c = activeConnection;
-            } else {
-                c = connectionPool.reserveConnection();
-            }
-            pstmt = c.prepareStatement(sh.getQueryString(),
-                    primaryKeyColumns.toArray(new String[0]));
-            sh.setParameterValuesToStatement(pstmt);
-            debug("DB -> " + sh.getQueryString());
-            int result = pstmt.executeUpdate();
-            genKeys = pstmt.getGeneratedKeys();
-            RowId newId = getNewRowId(row, genKeys);
-            bufferedEvents.add(new RowIdChangeEvent(row.getId(), newId));
-            return result;
-        } finally {
-            if (genKeys != null) {
-                genKeys.close();
-            }
-            if (pstmt != null) {
-                pstmt.clearParameters();
-                pstmt.close();
-            }
-            if (!transactionOpen) {
-                connectionPool.releaseConnection(c);
-            }
-        }
-    }
-
-    /**
-     * Fetches name(s) of primary key column(s) from DB metadata.
-     * 
-     * Also tries to get the escape string to be used in search strings.
-     */
-    private void fetchMetaData() {
-        Connection c = null;
-        try {
-            c = connectionPool.reserveConnection();
-            DatabaseMetaData dbmd = c.getMetaData();
-            if (dbmd != null) {
-                tableName = SQLUtil.escapeSQL(tableName);
-                ResultSet tables = dbmd.getTables(null, null, tableName, null);
-                if (!tables.next()) {
-                    tables = dbmd.getTables(null, null,
-                            tableName.toUpperCase(), null);
-                    if (!tables.next()) {
-                        throw new IllegalArgumentException(
-                                "Table with the name \""
-                                        + tableName
-                                        + "\" was not found. Check your database contents.");
-                    } else {
-                        tableName = tableName.toUpperCase();
-                    }
-                }
-                tables.close();
-                ResultSet rs = dbmd.getPrimaryKeys(null, null, tableName);
-                List<String> names = new ArrayList<String>();
-                while (rs.next()) {
-                    names.add(rs.getString("COLUMN_NAME"));
-                }
-                rs.close();
-                if (!names.isEmpty()) {
-                    primaryKeyColumns = names;
-                }
-                if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
-                    throw new IllegalArgumentException(
-                            "Primary key constraints have not been defined for the table \""
-                                    + tableName
-                                    + "\". Use FreeFormQuery to access this table.");
-                }
-                for (String colName : primaryKeyColumns) {
-                    if (colName.equalsIgnoreCase("rownum")) {
-                        if (getSqlGenerator() instanceof MSSQLGenerator
-                                || getSqlGenerator() instanceof MSSQLGenerator) {
-                            throw new IllegalArgumentException(
-                                    "When using Oracle or MSSQL, a primary key column"
-                                            + " named \'rownum\' is not allowed!");
-                        }
-                    }
-                }
-            }
-        } catch (SQLException e) {
-            throw new RuntimeException(e);
-        } finally {
-            connectionPool.releaseConnection(c);
-        }
-    }
-
-    private RowId getNewRowId(RowItem row, ResultSet genKeys) {
-        try {
-            /* Fetch primary key values and generate a map out of them. */
-            Map<String, Object> values = new HashMap<String, Object>();
-            ResultSetMetaData rsmd = genKeys.getMetaData();
-            int colCount = rsmd.getColumnCount();
-            if (genKeys.next()) {
-                for (int i = 1; i <= colCount; i++) {
-                    values.put(rsmd.getColumnName(i), genKeys.getObject(i));
-                }
-            }
-            /* Generate new RowId */
-            List<Object> newRowId = new ArrayList<Object>();
-            if (values.size() == 1) {
-                if (primaryKeyColumns.size() == 1) {
-                    newRowId.add(values.get(values.keySet().iterator().next()));
-                } else {
-                    for (String s : primaryKeyColumns) {
-                        if (!((ColumnProperty) row.getItemProperty(s))
-                                .isReadOnlyChangeAllowed()) {
-                            newRowId.add(values.get(values.keySet().iterator()
-                                    .next()));
-                        } else {
-                            newRowId.add(values.get(s));
-                        }
-                    }
-                }
-            } else {
-                for (String s : primaryKeyColumns) {
-                    newRowId.add(values.get(s));
-                }
-            }
-            return new RowId(newRowId.toArray());
-        } catch (Exception e) {
-            debug("Failed to fetch key values on insert: " + e.getMessage());
-            return null;
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#removeRow(com.vaadin
-     * .addon.sqlcontainer.RowItem)
-     */
-    public boolean removeRow(RowItem row) throws UnsupportedOperationException,
-            SQLException {
-        debug("Removing row with id: " + row.getId().getId()[0].toString());
-        if (executeUpdate(sqlGenerator.generateDeleteQuery(getTableName(),
-                primaryKeyColumns, versionColumn, row)) == 1) {
-            return true;
-        }
-        if (versionColumn != null) {
-            throw new OptimisticLockException(
-                    "Someone else changed the row that was being deleted.",
-                    row.getId());
-        }
-        return false;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see
-     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#containsRowWithKey(
-     * java.lang.Object[])
-     */
-    public boolean containsRowWithKey(Object... keys) throws SQLException {
-        ArrayList<Filter> filtersAndKeys = new ArrayList<Filter>();
-        if (filters != null) {
-            filtersAndKeys.addAll(filters);
-        }
-        int ix = 0;
-        for (String colName : primaryKeyColumns) {
-            filtersAndKeys.add(new Equal(colName, keys[ix]));
-            ix++;
-        }
-        StatementHelper sh = sqlGenerator.generateSelectQuery(tableName,
-                filtersAndKeys, orderBys, 0, 0, "*");
-
-        boolean shouldCloseTransaction = false;
-        if (!transactionOpen) {
-            shouldCloseTransaction = true;
-            beginTransaction();
-        }
-        ResultSet rs = null;
-        try {
-            rs = executeQuery(sh);
-            boolean contains = rs.next();
-            return contains;
-        } finally {
-            if (rs != null) {
-                if (rs.getStatement() != null) {
-                    rs.getStatement().close();
-                }
-                rs.close();
-            }
-            if (shouldCloseTransaction) {
-                commit();
-            }
-        }
-    }
-
-    /**
-     * Output a debug message
-     * 
-     * @param message
-     */
-    private void debug(String message) {
-        if (debug) {
-            System.out.println(message);
-        }
-    }
-
-    /**
-     * Enable or disable debug mode.
-     * 
-     * @param debug
-     */
-    public void setDebug(boolean debug) {
-        this.debug = debug;
-    }
-
-    /**
-     * Custom writeObject to call rollback() if object is serialized.
-     */
-    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
-        try {
-            rollback();
-        } catch (SQLException ignored) {
-        }
-        out.defaultWriteObject();
-    }
-
-    /**
-     * Simple RowIdChangeEvent implementation.
-     */
-    public class RowIdChangeEvent extends EventObject implements
-            QueryDelegate.RowIdChangeEvent {
-        private final RowId oldId;
-        private final RowId newId;
-
-        private RowIdChangeEvent(RowId oldId, RowId newId) {
-            super(oldId);
-            this.oldId = oldId;
-            this.newId = newId;
-        }
-
-        public RowId getNewRowId() {
-            return newId;
-        }
-
-        public RowId getOldRowId() {
-            return oldId;
-        }
-    }
-
-    /**
-     * Adds RowIdChangeListener to this query
-     */
-    public void addListener(RowIdChangeListener listener) {
-        if (rowIdChangeListeners == null) {
-            rowIdChangeListeners = new LinkedList<QueryDelegate.RowIdChangeListener>();
-        }
-        rowIdChangeListeners.add(listener);
-    }
-
-    /**
-     * Removes the given RowIdChangeListener from this query
-     */
-    public void removeListener(RowIdChangeListener listener) {
-        if (rowIdChangeListeners != null) {
-            rowIdChangeListeners.remove(listener);
-        }
-    }
-}
diff --git a/src/com/vaadin/data/util/query/generator/DefaultSQLGenerator.java b/src/com/vaadin/data/util/query/generator/DefaultSQLGenerator.java
deleted file mode 100644 (file)
index b904141..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-package com.vaadin.data.util.query.generator;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.ColumnProperty;
-import com.vaadin.data.util.RowItem;
-import com.vaadin.data.util.SQLUtil;
-import com.vaadin.data.util.TemporaryRowId;
-import com.vaadin.data.util.query.OrderBy;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
-import com.vaadin.data.util.query.generator.filter.StringDecorator;
-
-/**
- * Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL.
- * 
- * @author Jonatan Kronqvist / IT Mill Ltd
- */
-@SuppressWarnings("serial")
-public class DefaultSQLGenerator implements SQLGenerator {
-
-    public DefaultSQLGenerator() {
-
-    }
-
-    /**
-     * Construct a DefaultSQLGenerator with the specified identifiers for start
-     * and end of quoted strings. The identifiers may be different depending on
-     * the database engine and it's settings.
-     * 
-     * @param quoteStart
-     *            the identifier (character) denoting the start of a quoted
-     *            string
-     * @param quoteEnd
-     *            the identifier (character) denoting the end of a quoted string
-     */
-    public DefaultSQLGenerator(String quoteStart, String quoteEnd) {
-        QueryBuilder.setStringDecorator(new StringDecorator(quoteStart,
-                quoteEnd));
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
-     * generateSelectQuery(java.lang.String, java.util.List, java.util.List,
-     * int, int, java.lang.String)
-     */
-    public StatementHelper generateSelectQuery(String tableName,
-            List<Filter> filters, List<OrderBy> orderBys, int offset,
-            int pagelength, String toSelect) {
-        if (tableName == null || tableName.trim().equals("")) {
-            throw new IllegalArgumentException("Table name must be given.");
-        }
-        toSelect = toSelect == null ? "*" : toSelect;
-        StatementHelper sh = new StatementHelper();
-        StringBuffer query = new StringBuffer();
-        query.append("SELECT " + toSelect + " FROM ").append(
-                SQLUtil.escapeSQL(tableName));
-        if (filters != null) {
-            query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
-        }
-        if (orderBys != null) {
-            for (OrderBy o : orderBys) {
-                generateOrderBy(query, o, orderBys.indexOf(o) == 0);
-            }
-        }
-        if (pagelength != 0) {
-            generateLimits(query, offset, pagelength);
-        }
-        sh.setQueryString(query.toString());
-        return sh;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
-     * generateUpdateQuery(java.lang.String,
-     * com.vaadin.addon.sqlcontainer.RowItem)
-     */
-    public StatementHelper generateUpdateQuery(String tableName, RowItem item) {
-        if (tableName == null || tableName.trim().equals("")) {
-            throw new IllegalArgumentException("Table name must be given.");
-        }
-        if (item == null) {
-            throw new IllegalArgumentException("Updated item must be given.");
-        }
-        StatementHelper sh = new StatementHelper();
-        StringBuffer query = new StringBuffer();
-        query.append("UPDATE ").append(tableName).append(" SET");
-
-        /* Generate column<->value and rowidentifiers map */
-        Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
-        Map<String, Object> rowIdentifiers = generateRowIdentifiers(item);
-        /* Generate columns and values to update */
-        boolean first = true;
-        for (String column : columnToValueMap.keySet()) {
-            if (first) {
-                query.append(" " + QueryBuilder.quote(column) + " = ?");
-            } else {
-                query.append(", " + QueryBuilder.quote(column) + " = ?");
-            }
-            sh.addParameterValue(columnToValueMap.get(column), item
-                    .getItemProperty(column).getType());
-            first = false;
-        }
-        /* Generate identifiers for the row to be updated */
-        first = true;
-        for (String column : rowIdentifiers.keySet()) {
-            if (first) {
-                query.append(" WHERE " + QueryBuilder.quote(column) + " = ?");
-            } else {
-                query.append(" AND " + QueryBuilder.quote(column) + " = ?");
-            }
-            sh.addParameterValue(rowIdentifiers.get(column), item
-                    .getItemProperty(column).getType());
-            first = false;
-        }
-        sh.setQueryString(query.toString());
-        return sh;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
-     * generateInsertQuery(java.lang.String,
-     * com.vaadin.addon.sqlcontainer.RowItem)
-     */
-    public StatementHelper generateInsertQuery(String tableName, RowItem item) {
-        if (tableName == null || tableName.trim().equals("")) {
-            throw new IllegalArgumentException("Table name must be given.");
-        }
-        if (item == null) {
-            throw new IllegalArgumentException("New item must be given.");
-        }
-        if (!(item.getId() instanceof TemporaryRowId)) {
-            throw new IllegalArgumentException(
-                    "Cannot generate an insert query for item already in database.");
-        }
-        StatementHelper sh = new StatementHelper();
-        StringBuffer query = new StringBuffer();
-        query.append("INSERT INTO ").append(tableName).append(" (");
-
-        /* Generate column<->value map */
-        Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
-        /* Generate column names for insert query */
-        boolean first = true;
-        for (String column : columnToValueMap.keySet()) {
-            if (!first) {
-                query.append(", ");
-            }
-            query.append(QueryBuilder.quote(column));
-            first = false;
-        }
-
-        /* Generate values for insert query */
-        query.append(") VALUES (");
-        first = true;
-        for (String column : columnToValueMap.keySet()) {
-            if (!first) {
-                query.append(", ");
-            }
-            query.append("?");
-            sh.addParameterValue(columnToValueMap.get(column), item
-                    .getItemProperty(column).getType());
-            first = false;
-        }
-        query.append(")");
-        sh.setQueryString(query.toString());
-        return sh;
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
-     * generateDeleteQuery(java.lang.String,
-     * com.vaadin.addon.sqlcontainer.RowItem)
-     */
-    public StatementHelper generateDeleteQuery(String tableName,
-            List<String> primaryKeyColumns, String versionColumn, RowItem item) {
-        if (tableName == null || tableName.trim().equals("")) {
-            throw new IllegalArgumentException("Table name must be given.");
-        }
-        if (item == null) {
-            throw new IllegalArgumentException(
-                    "Item to be deleted must be given.");
-        }
-        if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "Valid keyColumnNames must be provided.");
-        }
-        StatementHelper sh = new StatementHelper();
-        StringBuffer query = new StringBuffer();
-        query.append("DELETE FROM ").append(tableName).append(" WHERE ");
-        int count = 1;
-        for (String keyColName : primaryKeyColumns) {
-            if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
-                    && keyColName.equalsIgnoreCase("rownum")) {
-                count++;
-                continue;
-            }
-            if (count > 1) {
-                query.append(" AND ");
-            }
-            if (item.getItemProperty(keyColName).getValue() != null) {
-                query.append(QueryBuilder.quote(keyColName) + " = ?");
-                sh.addParameterValue(item.getItemProperty(keyColName)
-                        .getValue(), item.getItemProperty(keyColName).getType());
-            }
-            count++;
-        }
-        if (versionColumn != null) {
-            query.append(String.format(" AND %s = ?",
-                    QueryBuilder.quote(versionColumn)));
-            sh.addParameterValue(
-                    item.getItemProperty(versionColumn).getValue(), item
-                            .getItemProperty(versionColumn).getType());
-        }
-
-        sh.setQueryString(query.toString());
-        return sh;
-    }
-
-    /**
-     * Generates sorting rules as an ORDER BY -clause
-     * 
-     * @param sb
-     *            StringBuffer to which the clause is appended.
-     * @param o
-     *            OrderBy object to be added into the sb.
-     * @param firstOrderBy
-     *            If true, this is the first OrderBy.
-     * @return
-     */
-    protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o,
-            boolean firstOrderBy) {
-        if (firstOrderBy) {
-            sb.append(" ORDER BY ");
-        } else {
-            sb.append(", ");
-        }
-        sb.append(QueryBuilder.quote(o.getColumn()));
-        if (o.isAscending()) {
-            sb.append(" ASC");
-        } else {
-            sb.append(" DESC");
-        }
-        return sb;
-    }
-
-    /**
-     * Generates the LIMIT and OFFSET clause.
-     * 
-     * @param sb
-     *            StringBuffer to which the clause is appended.
-     * @param offset
-     *            Value for offset.
-     * @param pagelength
-     *            Value for pagelength.
-     * @return StringBuffer with LIMIT and OFFSET clause added.
-     */
-    protected StringBuffer generateLimits(StringBuffer sb, int offset,
-            int pagelength) {
-        sb.append(" LIMIT ").append(pagelength).append(" OFFSET ")
-                .append(offset);
-        return sb;
-    }
-
-    protected Map<String, Object> generateColumnToValueMap(RowItem item) {
-        Map<String, Object> columnToValueMap = new HashMap<String, Object>();
-        for (Object id : item.getItemPropertyIds()) {
-            ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
-            /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
-            if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
-                    && cp.getPropertyId().equalsIgnoreCase("rownum")) {
-                continue;
-            }
-            Object value = cp.getValue() == null ? null : cp.getValue();
-            /* Only include properties whose read-only status can be altered */
-            if (cp.isReadOnlyChangeAllowed() && !cp.isVersionColumn()) {
-                columnToValueMap.put(cp.getPropertyId(), value);
-            }
-        }
-        return columnToValueMap;
-    }
-
-    protected Map<String, Object> generateRowIdentifiers(RowItem item) {
-        Map<String, Object> rowIdentifiers = new HashMap<String, Object>();
-        for (Object id : item.getItemPropertyIds()) {
-            ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
-            /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
-            if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
-                    && cp.getPropertyId().equalsIgnoreCase("rownum")) {
-                continue;
-            }
-            Object value = cp.getValue() == null ? null : cp.getValue();
-            if (!cp.isReadOnlyChangeAllowed() || cp.isVersionColumn()) {
-                rowIdentifiers.put(cp.getPropertyId(), value);
-            }
-        }
-        return rowIdentifiers;
-    }
-}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/query/generator/MSSQLGenerator.java b/src/com/vaadin/data/util/query/generator/MSSQLGenerator.java
deleted file mode 100644 (file)
index da98e67..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.vaadin.data.util.query.generator;
-
-import java.util.List;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.query.OrderBy;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
-
-@SuppressWarnings("serial")
-public class MSSQLGenerator extends DefaultSQLGenerator {
-
-    public MSSQLGenerator() {
-
-    }
-
-    /**
-     * Construct a MSSQLGenerator with the specified identifiers for start and
-     * end of quoted strings. The identifiers may be different depending on the
-     * database engine and it's settings.
-     * 
-     * @param quoteStart
-     *            the identifier (character) denoting the start of a quoted
-     *            string
-     * @param quoteEnd
-     *            the identifier (character) denoting the end of a quoted string
-     */
-    public MSSQLGenerator(String quoteStart, String quoteEnd) {
-        super(quoteStart, quoteEnd);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator#
-     * generateSelectQuery(java.lang.String, java.util.List,
-     * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int,
-     * int, java.lang.String)
-     */
-    @Override
-    public StatementHelper generateSelectQuery(String tableName,
-            List<Filter> filters, List<OrderBy> orderBys, int offset,
-            int pagelength, String toSelect) {
-        if (tableName == null || tableName.trim().equals("")) {
-            throw new IllegalArgumentException("Table name must be given.");
-        }
-        /* Adjust offset and page length parameters to match "row numbers" */
-        offset = pagelength > 1 ? ++offset : offset;
-        pagelength = pagelength > 1 ? --pagelength : pagelength;
-        toSelect = toSelect == null ? "*" : toSelect;
-        StatementHelper sh = new StatementHelper();
-        StringBuffer query = new StringBuffer();
-
-        /* Row count request is handled here */
-        if ("COUNT(*)".equalsIgnoreCase(toSelect)) {
-            query.append(String.format(
-                    "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s",
-                    QueryBuilder.quote("rowcount"), tableName));
-            if (filters != null && !filters.isEmpty()) {
-                query.append(QueryBuilder.getWhereStringForFilters(
-                        filters, sh));
-            }
-            query.append(") AS t");
-            sh.setQueryString(query.toString());
-            return sh;
-        }
-
-        /* SELECT without row number constraints */
-        if (offset == 0 && pagelength == 0) {
-            query.append("SELECT ").append(toSelect).append(" FROM ")
-                    .append(tableName);
-            if (filters != null) {
-                query.append(QueryBuilder.getWhereStringForFilters(
-                        filters, sh));
-            }
-            if (orderBys != null) {
-                for (OrderBy o : orderBys) {
-                    generateOrderBy(query, o, orderBys.indexOf(o) == 0);
-                }
-            }
-            sh.setQueryString(query.toString());
-            return sh;
-        }
-
-        /* Remaining SELECT cases are handled here */
-        query.append("SELECT * FROM (SELECT row_number() OVER (");
-        if (orderBys != null) {
-            for (OrderBy o : orderBys) {
-                generateOrderBy(query, o, orderBys.indexOf(o) == 0);
-            }
-        }
-        query.append(") AS rownum, " + toSelect + " FROM ").append(tableName);
-        if (filters != null) {
-            query.append(QueryBuilder.getWhereStringForFilters(
-                    filters, sh));
-        }
-        query.append(") AS a WHERE a.rownum BETWEEN ").append(offset)
-                .append(" AND ").append(Integer.toString(offset + pagelength));
-        sh.setQueryString(query.toString());
-        return sh;
-    }
-}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/query/generator/OracleGenerator.java b/src/com/vaadin/data/util/query/generator/OracleGenerator.java
deleted file mode 100644 (file)
index 519d182..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.vaadin.data.util.query.generator;
-
-import java.util.List;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.query.OrderBy;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
-
-@SuppressWarnings("serial")
-public class OracleGenerator extends DefaultSQLGenerator {
-
-    public OracleGenerator() {
-
-    }
-
-    /**
-     * Construct an OracleSQLGenerator with the specified identifiers for start
-     * and end of quoted strings. The identifiers may be different depending on
-     * the database engine and it's settings.
-     * 
-     * @param quoteStart
-     *            the identifier (character) denoting the start of a quoted
-     *            string
-     * @param quoteEnd
-     *            the identifier (character) denoting the end of a quoted string
-     */
-    public OracleGenerator(String quoteStart, String quoteEnd) {
-        super(quoteStart, quoteEnd);
-    }
-
-    /*
-     * (non-Javadoc)
-     * 
-     * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator#
-     * generateSelectQuery(java.lang.String, java.util.List,
-     * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int,
-     * int, java.lang.String)
-     */
-    @Override
-    public StatementHelper generateSelectQuery(String tableName,
-            List<Filter> filters, List<OrderBy> orderBys, int offset,
-            int pagelength, String toSelect) {
-        if (tableName == null || tableName.trim().equals("")) {
-            throw new IllegalArgumentException("Table name must be given.");
-        }
-        /* Adjust offset and page length parameters to match "row numbers" */
-        offset = pagelength > 1 ? ++offset : offset;
-        pagelength = pagelength > 1 ? --pagelength : pagelength;
-        toSelect = toSelect == null ? "*" : toSelect;
-        StatementHelper sh = new StatementHelper();
-        StringBuffer query = new StringBuffer();
-
-        /* Row count request is handled here */
-        if ("COUNT(*)".equalsIgnoreCase(toSelect)) {
-            query.append(String.format(
-                    "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s",
-                    QueryBuilder.quote("rowcount"), tableName));
-            if (filters != null && !filters.isEmpty()) {
-                query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
-            }
-            query.append(")");
-            sh.setQueryString(query.toString());
-            return sh;
-        }
-
-        /* SELECT without row number constraints */
-        if (offset == 0 && pagelength == 0) {
-            query.append("SELECT ").append(toSelect).append(" FROM ")
-                    .append(tableName);
-            if (filters != null) {
-                query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
-            }
-            if (orderBys != null) {
-                for (OrderBy o : orderBys) {
-                    generateOrderBy(query, o, orderBys.indexOf(o) == 0);
-                }
-            }
-            sh.setQueryString(query.toString());
-            return sh;
-        }
-
-        /* Remaining SELECT cases are handled here */
-        query.append(String
-                .format("SELECT * FROM (SELECT x.*, ROWNUM AS %s FROM (SELECT %s FROM %s",
-                        QueryBuilder.quote("rownum"), toSelect, tableName));
-        if (filters != null) {
-            query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
-        }
-        if (orderBys != null) {
-            for (OrderBy o : orderBys) {
-                generateOrderBy(query, o, orderBys.indexOf(o) == 0);
-            }
-        }
-        query.append(String.format(") x) WHERE %s BETWEEN %d AND %d",
-                QueryBuilder.quote("rownum"), offset, offset + pagelength));
-        sh.setQueryString(query.toString());
-        return sh;
-    }
-}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/query/generator/SQLGenerator.java b/src/com/vaadin/data/util/query/generator/SQLGenerator.java
deleted file mode 100644 (file)
index dfdecae..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.vaadin.data.util.query.generator;
-
-import java.io.Serializable;
-import java.util.List;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.RowItem;
-import com.vaadin.data.util.query.OrderBy;
-
-/**
- * The SQLGenerator interface is meant to be implemented for each different SQL
- * syntax that is to be supported. By default there are implementations for
- * HSQLDB, MySQL, PostgreSQL, MSSQL and Oracle syntaxes.
- * 
- * @author Jonatan Kronqvist / IT Mill Ltd
- */
-public interface SQLGenerator extends Serializable {
-    /**
-     * Generates a SELECT query with the provided parameters. Uses default
-     * filtering mode (INCLUSIVE).
-     * 
-     * @param tableName
-     *            Name of the table queried
-     * @param filters
-     *            The filters, converted into a WHERE clause
-     * @param orderBys
-     *            The the ordering conditions, converted into an ORDER BY clause
-     * @param offset
-     *            The offset of the first row to be included
-     * @param pagelength
-     *            The number of rows to be returned when the query executes
-     * @param toSelect
-     *            String containing what to select, e.g. "*", "COUNT(*)"
-     * @return StatementHelper instance containing the query string for a
-     *         PreparedStatement and the values required for the parameters
-     */
-    public StatementHelper generateSelectQuery(String tableName,
-            List<Filter> filters, List<OrderBy> orderBys, int offset,
-            int pagelength, String toSelect);
-
-    /**
-     * Generates an UPDATE query with the provided parameters.
-     * 
-     * @param tableName
-     *            Name of the table queried
-     * @param item
-     *            RowItem containing the updated values update.
-     * @return StatementHelper instance containing the query string for a
-     *         PreparedStatement and the values required for the parameters
-     */
-    public StatementHelper generateUpdateQuery(String tableName, RowItem item);
-
-    /**
-     * Generates an INSERT query for inserting a new row with the provided
-     * values.
-     * 
-     * @param tableName
-     *            Name of the table queried
-     * @param item
-     *            New RowItem to be inserted into the database.
-     * @return StatementHelper instance containing the query string for a
-     *         PreparedStatement and the values required for the parameters
-     */
-    public StatementHelper generateInsertQuery(String tableName, RowItem item);
-
-    /**
-     * Generates a DELETE query for deleting data related to the given RowItem
-     * from the database.
-     * 
-     * @param tableName
-     *            Name of the table queried
-     * @param primaryKeyColumns
-     *            the names of the columns holding the primary key. Usually just
-     *            one column, but might be several.
-     * @param versionColumn
-     *            the column containing the version number of the row, null if
-     *            versioning (optimistic locking) not enabled.
-     * @param item
-     *            Item to be deleted from the database
-     * @return StatementHelper instance containing the query string for a
-     *         PreparedStatement and the values required for the parameters
-     */
-    public StatementHelper generateDeleteQuery(String tableName,
-            List<String> primaryKeyColumns, String versionColumn, RowItem item);
-}
diff --git a/src/com/vaadin/data/util/query/generator/StatementHelper.java b/src/com/vaadin/data/util/query/generator/StatementHelper.java
deleted file mode 100644 (file)
index b230f58..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-package com.vaadin.data.util.query.generator;\r
-\r
-import java.math.BigDecimal;\r
-import java.sql.Date;\r
-import java.sql.PreparedStatement;\r
-import java.sql.SQLException;\r
-import java.sql.Time;\r
-import java.sql.Timestamp;\r
-import java.sql.Types;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-/**\r
- * StatementHelper is a simple helper class that assists TableQuery and the\r
- * query generators in filling a PreparedStatement. The actual statement is\r
- * generated by the query generator methods, but the resulting statement and all\r
- * the parameter values are stored in an instance of StatementHelper.\r
- * \r
- * This class will also fill the values with correct setters into the\r
- * PreparedStatement on request.\r
- */\r
-public class StatementHelper {\r
-\r
-    private String queryString;\r
-\r
-    private List<Object> parameters = new ArrayList<Object>();\r
-    private Map<Integer, Class<?>> dataTypes = new HashMap<Integer, Class<?>>();\r
-\r
-    public StatementHelper() {\r
-    }\r
-\r
-    public void setQueryString(String queryString) {\r
-        this.queryString = queryString;\r
-    }\r
-\r
-    public String getQueryString() {\r
-        return queryString;\r
-    }\r
-\r
-    public void addParameterValue(Object parameter) {\r
-        if (parameter != null) {\r
-            parameters.add(parameter);\r
-            dataTypes.put(parameters.size() - 1, parameter.getClass());\r
-        }\r
-    }\r
-\r
-    public void addParameterValue(Object parameter, Class<?> type) {\r
-        parameters.add(parameter);\r
-        dataTypes.put(parameters.size() - 1, type);\r
-    }\r
-\r
-    public void setParameterValuesToStatement(PreparedStatement pstmt)\r
-            throws SQLException {\r
-        for (int i = 0; i < parameters.size(); i++) {\r
-            if (parameters.get(i) == null) {\r
-                handleNullValue(i, pstmt);\r
-            } else {\r
-                pstmt.setObject(i + 1, parameters.get(i));\r
-            }\r
-        }\r
-\r
-        /*\r
-         * The following list contains the data types supported by\r
-         * PreparedStatement but not supported by SQLContainer:\r
-         * \r
-         * [The list is provided as PreparedStatement method signatures]\r
-         * \r
-         * setNCharacterStream(int parameterIndex, Reader value)\r
-         * \r
-         * setNClob(int parameterIndex, NClob value)\r
-         * \r
-         * setNString(int parameterIndex, String value)\r
-         * \r
-         * setRef(int parameterIndex, Ref x)\r
-         * \r
-         * setRowId(int parameterIndex, RowId x)\r
-         * \r
-         * setSQLXML(int parameterIndex, SQLXML xmlObject)\r
-         * \r
-         * setBytes(int parameterIndex, byte[] x)\r
-         * \r
-         * setCharacterStream(int parameterIndex, Reader reader)\r
-         * \r
-         * setClob(int parameterIndex, Clob x)\r
-         * \r
-         * setURL(int parameterIndex, URL x)\r
-         * \r
-         * setArray(int parameterIndex, Array x)\r
-         * \r
-         * setAsciiStream(int parameterIndex, InputStream x)\r
-         * \r
-         * setBinaryStream(int parameterIndex, InputStream x)\r
-         * \r
-         * setBlob(int parameterIndex, Blob x)\r
-         */\r
-    }\r
-\r
-    private void handleNullValue(int i, PreparedStatement pstmt)\r
-            throws SQLException {\r
-        if (BigDecimal.class.equals(dataTypes.get(i))) {\r
-            pstmt.setBigDecimal(i + 1, null);\r
-        } else if (Boolean.class.equals(dataTypes.get(i))) {\r
-            pstmt.setNull(i + 1, Types.BOOLEAN);\r
-        } else if (Byte.class.equals(dataTypes.get(i))) {\r
-            pstmt.setNull(i + 1, Types.SMALLINT);\r
-        } else if (Date.class.equals(dataTypes.get(i))) {\r
-            pstmt.setDate(i + 1, null);\r
-        } else if (Double.class.equals(dataTypes.get(i))) {\r
-            pstmt.setNull(i + 1, Types.DOUBLE);\r
-        } else if (Float.class.equals(dataTypes.get(i))) {\r
-            pstmt.setNull(i + 1, Types.FLOAT);\r
-        } else if (Integer.class.equals(dataTypes.get(i))) {\r
-            pstmt.setNull(i + 1, Types.INTEGER);\r
-        } else if (Long.class.equals(dataTypes.get(i))) {\r
-            pstmt.setNull(i + 1, Types.BIGINT);\r
-        } else if (Short.class.equals(dataTypes.get(i))) {\r
-            pstmt.setNull(i + 1, Types.SMALLINT);\r
-        } else if (String.class.equals(dataTypes.get(i))) {\r
-            pstmt.setString(i + 1, null);\r
-        } else if (Time.class.equals(dataTypes.get(i))) {\r
-            pstmt.setTime(i + 1, null);\r
-        } else if (Timestamp.class.equals(dataTypes.get(i))) {\r
-            pstmt.setTimestamp(i + 1, null);\r
-        } else {\r
-            throw new SQLException("Data type not supported by SQLContainer: "\r
-                    + parameters.get(i).getClass().toString());\r
-        }\r
-    }\r
-}\r
diff --git a/src/com/vaadin/data/util/query/generator/filter/AndTranslator.java b/src/com/vaadin/data/util/query/generator/filter/AndTranslator.java
deleted file mode 100644 (file)
index 19f1ce5..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.And;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class AndTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof And;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        return QueryBuilder.group(QueryBuilder
-                .getJoinedFilterString(((And) filter).getFilters(), "AND", sh));
-    }
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/BetweenTranslator.java b/src/com/vaadin/data/util/query/generator/filter/BetweenTranslator.java
deleted file mode 100644 (file)
index 0492741..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.Between;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class BetweenTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof Between;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        Between between = (Between) filter;
-        sh.addParameterValue(between.getStartValue());
-        sh.addParameterValue(between.getEndValue());
-        return QueryBuilder.quote(between.getPropertyId())
-                + " BETWEEN ? AND ?";
-    }
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/CompareTranslator.java b/src/com/vaadin/data/util/query/generator/filter/CompareTranslator.java
deleted file mode 100644 (file)
index c35baf0..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.Compare;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class CompareTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof Compare;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        Compare compare = (Compare) filter;
-        sh.addParameterValue(compare.getValue());
-        String prop = QueryBuilder.quote(compare.getPropertyId());
-        switch (compare.getOperation()) {
-        case EQUAL:
-            return prop + " = ?";
-        case GREATER:
-            return prop + " > ?";
-        case GREATER_OR_EQUAL:
-            return prop + " >= ?";
-        case LESS:
-            return prop + " < ?";
-        case LESS_OR_EQUAL:
-            return prop + " <= ?";
-        default:
-            return "";
-        }
-    }
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/FilterTranslator.java b/src/com/vaadin/data/util/query/generator/filter/FilterTranslator.java
deleted file mode 100644 (file)
index 75ac488..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public interface FilterTranslator {
-    public boolean translatesFilter(Filter filter);
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh);
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/IsNullTranslator.java b/src/com/vaadin/data/util/query/generator/filter/IsNullTranslator.java
deleted file mode 100644 (file)
index fc44d65..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.IsNull;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class IsNullTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof IsNull;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        IsNull in = (IsNull) filter;
-        return QueryBuilder.quote(in.getPropertyId()) + " IS NULL";
-    }
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/LikeTranslator.java b/src/com/vaadin/data/util/query/generator/filter/LikeTranslator.java
deleted file mode 100644 (file)
index e4d1d72..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.Like;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class LikeTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof Like;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        Like like = (Like) filter;
-        if (like.isCaseSensitive()) {
-            sh.addParameterValue(like.getValue());
-            return QueryBuilder.quote(like.getPropertyId())
-                    + " LIKE ?";
-        } else {
-            sh.addParameterValue(like.getValue().toUpperCase());
-            return "UPPER("
-                    + QueryBuilder.quote(like.getPropertyId())
-                    + ") LIKE ?";
-        }
-    }
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/NotTranslator.java b/src/com/vaadin/data/util/query/generator/filter/NotTranslator.java
deleted file mode 100644 (file)
index 1cb7321..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.IsNull;
-import com.vaadin.data.util.filter.Not;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class NotTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof Not;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        Not not = (Not) filter;
-        if (not.getFilter() instanceof IsNull) {
-            IsNull in = (IsNull) not.getFilter();
-            return QueryBuilder.quote(in.getPropertyId())
-                    + " IS NOT NULL";
-        }
-        return "NOT "
-                + QueryBuilder.getWhereStringForFilter(
-                        not.getFilter(), sh);
-    }
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/OrTranslator.java b/src/com/vaadin/data/util/query/generator/filter/OrTranslator.java
deleted file mode 100644 (file)
index 1ba2db6..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.Or;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class OrTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof Or;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        return QueryBuilder.group(QueryBuilder
-                .getJoinedFilterString(((Or) filter).getFilters(), "OR", sh));
-    }
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/QueryBuilder.java b/src/com/vaadin/data/util/query/generator/filter/QueryBuilder.java
deleted file mode 100644 (file)
index 75405d8..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class QueryBuilder {
-
-    private static ArrayList<FilterTranslator> filterTranslators = new ArrayList<FilterTranslator>();
-    private static StringDecorator stringDecorator = new StringDecorator("\"",
-            "\"");
-
-    static {
-        /* Register all default filter translators */
-        addFilterTranslator(new AndTranslator());
-        addFilterTranslator(new OrTranslator());
-        addFilterTranslator(new LikeTranslator());
-        addFilterTranslator(new BetweenTranslator());
-        addFilterTranslator(new CompareTranslator());
-        addFilterTranslator(new NotTranslator());
-        addFilterTranslator(new IsNullTranslator());
-        addFilterTranslator(new SimpleStringTranslator());
-    }
-
-    public synchronized static void addFilterTranslator(
-            FilterTranslator translator) {
-        filterTranslators.add(translator);
-    }
-
-    /**
-     * Allows specification of a custom ColumnQuoter instance that handles
-     * quoting of column names for the current DB dialect.
-     * 
-     * @param decorator
-     *            the ColumnQuoter instance to use.
-     */
-    public static void setStringDecorator(StringDecorator decorator) {
-        stringDecorator = decorator;
-    }
-
-    public static String quote(Object str) {
-        return stringDecorator.quote(str);
-    }
-
-    public static String group(String str) {
-        return stringDecorator.group(str);
-    }
-
-    /**
-     * Constructs and returns a string representing the filter that can be used
-     * in a WHERE clause.
-     * 
-     * @param filter
-     *            the filter to translate
-     * @param sh
-     *            the statement helper to update with the value(s) of the filter
-     * @return a string representing the filter.
-     */
-    public synchronized static String getWhereStringForFilter(Filter filter,
-            StatementHelper sh) {
-        for (FilterTranslator ft : filterTranslators) {
-            if (ft.translatesFilter(filter)) {
-                return ft.getWhereStringForFilter(filter, sh);
-            }
-        }
-        return "";
-    }
-
-    public static String getJoinedFilterString(Collection<Filter> filters,
-            String joinString, StatementHelper sh) {
-        StringBuilder result = new StringBuilder();
-        for (Filter f : filters) {
-            result.append(getWhereStringForFilter(f, sh));
-            result.append(" ").append(joinString).append(" ");
-        }
-        // Remove the last instance of joinString
-        result.delete(result.length() - joinString.length() - 2,
-                result.length());
-        return result.toString();
-    }
-
-    public static String getWhereStringForFilters(List<Filter> filters,
-            StatementHelper sh) {
-        if (filters == null || filters.isEmpty()) {
-            return "";
-        }
-        StringBuilder where = new StringBuilder(" WHERE ");
-        where.append(getJoinedFilterString(filters, "AND", sh));
-        return where.toString();
-    }
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/SimpleStringTranslator.java b/src/com/vaadin/data/util/query/generator/filter/SimpleStringTranslator.java
deleted file mode 100644 (file)
index 6be678c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.filter.Like;
-import com.vaadin.data.util.filter.SimpleStringFilter;
-import com.vaadin.data.util.query.generator.StatementHelper;
-
-public class SimpleStringTranslator implements FilterTranslator {
-
-    public boolean translatesFilter(Filter filter) {
-        return filter instanceof SimpleStringFilter;
-    }
-
-    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
-        SimpleStringFilter ssf = (SimpleStringFilter) filter;
-        // Create a Like filter based on the SimpleStringFilter and execute the
-        // LikeTranslator
-        String likeStr = ssf.isOnlyMatchPrefix() ? ssf.getFilterString() + "%"
-                : "%" + ssf.getFilterString() + "%";
-        Like like = new Like(ssf.getPropertyId().toString(), likeStr);
-        like.setCaseSensitive(!ssf.isIgnoreCase());
-        return new LikeTranslator().getWhereStringForFilter(like, sh);
-    }
-
-}
diff --git a/src/com/vaadin/data/util/query/generator/filter/StringDecorator.java b/src/com/vaadin/data/util/query/generator/filter/StringDecorator.java
deleted file mode 100644 (file)
index 0fdd9a1..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.vaadin.data.util.query.generator.filter;
-
-/**
- * The StringDecorator knows how to produce a quoted string using the specified
- * quote start and quote end characters. It also handles grouping of a string
- * (surrounding it in parenthesis).
- * 
- * Extend this class if you need to support special characters for grouping
- * (parenthesis).
- * 
- * @author Vaadin Ltd
- */
-public class StringDecorator {
-
-    private final String quoteStart;
-    private final String quoteEnd;
-
-    /**
-     * Constructs a StringDecorator that uses the quoteStart and quoteEnd
-     * characters to create quoted strings.
-     * 
-     * @param quoteStart
-     *            the character denoting the start of a quote.
-     * @param quoteEnd
-     *            the character denoting the end of a quote.
-     */
-    public StringDecorator(String quoteStart, String quoteEnd) {
-        this.quoteStart = quoteStart;
-        this.quoteEnd = quoteEnd;
-    }
-
-    /**
-     * Surround a string with quote characters.
-     * 
-     * @param str
-     *            the string to quote
-     * @return the quoted string
-     */
-    public String quote(Object str) {
-        return quoteStart + str + quoteEnd;
-    }
-
-    /**
-     * Groups a string by surrounding it in parenthesis
-     * 
-     * @param str
-     *            the string to group
-     * @return the grouped string
-     */
-    public String group(String str) {
-        return "(" + str + ")";
-    }
-}
diff --git a/src/com/vaadin/data/util/sqlcontainer/CacheFlushNotifier.java b/src/com/vaadin/data/util/sqlcontainer/CacheFlushNotifier.java
new file mode 100644 (file)
index 0000000..bb61fef
--- /dev/null
@@ -0,0 +1,88 @@
+package com.vaadin.data.util.sqlcontainer;\r
+\r
+import java.lang.ref.ReferenceQueue;\r
+import java.lang.ref.WeakReference;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import com.vaadin.data.util.sqlcontainer.query.FreeformQuery;\r
+import com.vaadin.data.util.sqlcontainer.query.QueryDelegate;\r
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;\r
+\r
+/**\r
+ * CacheFlushNotifier is a simple static notification mechanism to inform other\r
+ * SQLContainers that the contents of their caches may have become stale.\r
+ */\r
+class CacheFlushNotifier {\r
+    /*\r
+     * SQLContainer instance reference list and dead reference queue. Used for\r
+     * the cache flush notification feature.\r
+     */\r
+    private static List<WeakReference<SQLContainer>> allInstances = new ArrayList<WeakReference<SQLContainer>>();\r
+    private static ReferenceQueue<SQLContainer> deadInstances = new ReferenceQueue<SQLContainer>();\r
+\r
+    /**\r
+     * Adds the given SQLContainer to the cache flush notification receiver list\r
+     * \r
+     * @param c\r
+     *            Container to add\r
+     */\r
+    public static void addInstance(SQLContainer c) {\r
+        removeDeadReferences();\r
+        if (c != null) {\r
+            allInstances.add(new WeakReference<SQLContainer>(c, deadInstances));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Removes dead references from instance list\r
+     */\r
+    private static void removeDeadReferences() {\r
+        java.lang.ref.Reference<? extends SQLContainer> dead = deadInstances\r
+                .poll();\r
+        while (dead != null) {\r
+            allInstances.remove(dead);\r
+            dead = deadInstances.poll();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Iterates through the instances and notifies containers which are\r
+     * connected to the same table or are using the same query string.\r
+     * \r
+     * @param c\r
+     *            SQLContainer that issued the cache flush notification\r
+     */\r
+    public static void notifyOfCacheFlush(SQLContainer c) {\r
+        removeDeadReferences();\r
+        for (WeakReference<SQLContainer> wr : allInstances) {\r
+            if (wr.get() != null) {\r
+                SQLContainer wrc = wr.get();\r
+                if (wrc == null) {\r
+                    continue;\r
+                }\r
+                /*\r
+                 * If the reference points to the container sending the\r
+                 * notification, do nothing.\r
+                 */\r
+                if (wrc.equals(c)) {\r
+                    continue;\r
+                }\r
+                /* Compare QueryDelegate types and tableName/queryString */\r
+                QueryDelegate wrQd = wrc.getQueryDelegate();\r
+                QueryDelegate qd = c.getQueryDelegate();\r
+                if (wrQd instanceof TableQuery\r
+                        && qd instanceof TableQuery\r
+                        && ((TableQuery) wrQd).getTableName().equals(\r
+                                ((TableQuery) qd).getTableName())) {\r
+                    wrc.refresh();\r
+                } else if (wrQd instanceof FreeformQuery\r
+                        && qd instanceof FreeformQuery\r
+                        && ((FreeformQuery) wrQd).getQueryString().equals(\r
+                                ((FreeformQuery) qd).getQueryString())) {\r
+                    wrc.refresh();\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/src/com/vaadin/data/util/sqlcontainer/CacheMap.java b/src/com/vaadin/data/util/sqlcontainer/CacheMap.java
new file mode 100644 (file)
index 0000000..ef173c7
--- /dev/null
@@ -0,0 +1,28 @@
+package com.vaadin.data.util.sqlcontainer;\r
+\r
+import java.util.LinkedHashMap;\r
+import java.util.Map;\r
+\r
+/**\r
+ * CacheMap extends LinkedHashMap, adding the possibility to adjust maximum\r
+ * number of items. In SQLContainer this is used for RowItem -cache. Cache size\r
+ * will be two times the page length parameter of the container.\r
+ */\r
+class CacheMap<K, V> extends LinkedHashMap<K, V> {\r
+    private static final long serialVersionUID = 679999766473555231L;\r
+    private int cacheLimit = SQLContainer.CACHE_RATIO\r
+            * SQLContainer.DEFAULT_PAGE_LENGTH;\r
+\r
+    @Override\r
+    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {\r
+        return size() > cacheLimit;\r
+    }\r
+\r
+    void setCacheLimit(int limit) {\r
+        cacheLimit = limit > 0 ? limit : SQLContainer.DEFAULT_PAGE_LENGTH;\r
+    }\r
+\r
+    int getCacheLimit() {\r
+        return cacheLimit;\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java b/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java
new file mode 100644 (file)
index 0000000..02246ef
--- /dev/null
@@ -0,0 +1,241 @@
+package com.vaadin.data.util.sqlcontainer;
+
+import java.lang.reflect.Constructor;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import com.vaadin.data.Property;
+
+/**
+ * ColumnProperty represents the value of one column in a RowItem. In addition
+ * to the value, ColumnProperty also contains some basic column attributes such
+ * as nullability status, read-only status and data type.
+ * 
+ * Note that depending on the QueryDelegate in use this does not necessarily map
+ * into an actual column in a database table.
+ */
+final public class ColumnProperty implements Property {
+    private static final long serialVersionUID = -3694463129581802457L;
+
+    private RowItem owner;
+
+    private String propertyId;
+
+    private boolean readOnly;
+    private boolean allowReadOnlyChange = true;
+    private boolean nullable = true;
+
+    private Object value;
+    private Object changedValue;
+    private Class<?> type;
+
+    private boolean modified;
+
+    private boolean versionColumn;
+
+    /**
+     * Prevent instantiation without required parameters.
+     */
+    @SuppressWarnings("unused")
+    private ColumnProperty() {
+    }
+
+    public ColumnProperty(String propertyId, boolean readOnly,
+            boolean allowReadOnlyChange, boolean nullable, Object value,
+            Class<?> type) {
+        if (propertyId == null) {
+            throw new IllegalArgumentException("Properties must be named.");
+        }
+        if (type == null) {
+            throw new IllegalArgumentException("Property type must be set.");
+        }
+        this.propertyId = propertyId;
+        this.type = type;
+        this.value = value;
+
+        this.allowReadOnlyChange = allowReadOnlyChange;
+        this.nullable = nullable;
+        this.readOnly = readOnly;
+    }
+
+    public Object getValue() {
+        if (isModified()) {
+            return changedValue;
+        }
+        return value;
+    }
+
+    public void setValue(Object newValue) throws ReadOnlyException,
+            ConversionException {
+        if (newValue == null && !nullable) {
+            throw new NotNullableException(
+                    "Null values are not allowed for this property.");
+        }
+        if (readOnly) {
+            throw new ReadOnlyException(
+                    "Cannot set value for read-only property.");
+        }
+
+        /* Check if this property is a date property. */
+        boolean isDateProperty = Time.class.equals(getType())
+                || Date.class.equals(getType())
+                || Timestamp.class.equals(getType());
+
+        if (newValue != null) {
+            /* Handle SQL dates, times and Timestamps given as java.util.Date */
+            if (isDateProperty) {
+                /*
+                 * Try to get the millisecond value from the new value of this
+                 * property. Possible type to convert from is java.util.Date.
+                 */
+                long millis = 0;
+                if (newValue instanceof java.util.Date) {
+                    millis = ((java.util.Date) newValue).getTime();
+                    /*
+                     * Create the new object based on the millisecond value,
+                     * according to the type of this property.
+                     */
+                    if (Time.class.equals(getType())) {
+                        newValue = new Time(millis);
+                    } else if (Date.class.equals(getType())) {
+                        newValue = new Date(millis);
+                    } else if (Timestamp.class.equals(getType())) {
+                        newValue = new Timestamp(millis);
+                    }
+                }
+            }
+
+            /*
+             * If the type is not correct, try to generate it through a possibly
+             * existing String constructor.
+             */
+            if (!getType().isAssignableFrom(newValue.getClass())) {
+                try {
+                    final Constructor<?> constr = getType().getConstructor(
+                            new Class[] { String.class });
+                    newValue = constr.newInstance(new Object[] { newValue
+                            .toString() });
+                } catch (Exception e) {
+                    throw new ConversionException(e);
+                }
+            }
+
+            /*
+             * If the value to be set is the same that has already been set, do
+             * not set it again.
+             */
+            if (newValue.equals(value)) {
+                return;
+            }
+        }
+
+        /* Set the new value and notify container of the change. */
+        changedValue = newValue;
+        owner.getContainer().itemChangeNotification(owner);
+        modified = true;
+    }
+
+    public Class<?> getType() {
+        return type;
+    }
+
+    public boolean isReadOnly() {
+        return readOnly;
+    }
+
+    public boolean isReadOnlyChangeAllowed() {
+        return allowReadOnlyChange;
+    }
+
+    public void setReadOnly(boolean newStatus) {
+        if (allowReadOnlyChange) {
+            readOnly = newStatus;
+        }
+    }
+
+    public String getPropertyId() {
+        return propertyId;
+    }
+
+    @Override
+    public String toString() {
+        Object val = getValue();
+        if (val == null) {
+            return null;
+        }
+        return val.toString();
+    }
+
+    public void setOwner(RowItem owner) {
+        if (owner == null) {
+            throw new IllegalArgumentException("Owner can not be set to null.");
+        }
+        if (this.owner != null) {
+            throw new IllegalStateException(
+                    "ColumnProperties can only be bound once.");
+        }
+        this.owner = owner;
+    }
+
+    public boolean isModified() {
+        return modified;
+    }
+
+    public boolean isVersionColumn() {
+        return versionColumn;
+    }
+
+    public void setVersionColumn(boolean versionColumn) {
+        this.versionColumn = versionColumn;
+    }
+
+    public boolean isNullable() {
+        return nullable;
+    }
+
+    /**
+     * An exception that signals that a <code>null</code> value was passed to
+     * the <code>setValue</code> method, but the value of this property can not
+     * be set to <code>null</code>.
+     */
+    @SuppressWarnings("serial")
+    public class NotNullableException extends RuntimeException {
+
+        /**
+         * Constructs a new <code>NotNullableException</code> without a detail
+         * message.
+         */
+        public NotNullableException() {
+        }
+
+        /**
+         * Constructs a new <code>NotNullableException</code> with the specified
+         * detail message.
+         * 
+         * @param msg
+         *            the detail message
+         */
+        public NotNullableException(String msg) {
+            super(msg);
+        }
+
+        /**
+         * Constructs a new <code>NotNullableException</code> from another
+         * exception.
+         * 
+         * @param cause
+         *            The cause of the failure
+         */
+        public NotNullableException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    public void commit() {
+        if (isModified()) {
+            modified = false;
+            value = changedValue;
+        }
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java b/src/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java
new file mode 100644 (file)
index 0000000..9c6f805
--- /dev/null
@@ -0,0 +1,33 @@
+package com.vaadin.data.util.sqlcontainer;
+
+/**
+ * An OptimisticLockException is thrown when trying to update or delete a row
+ * that has been changed since last read from the database.
+ * 
+ * OptimisticLockException is a runtime exception because optimistic locking is
+ * turned off by default, and as such will never be thrown in a default
+ * configuration. In order to turn on optimistic locking, you need to specify
+ * the version column in your TableQuery instance.
+ * 
+ * @see com.vaadin.addon.sqlcontainer.query.TableQuery#setVersionColumn(String)
+ * 
+ * @author Jonatan Kronqvist / Vaadin Ltd
+ */
+public class OptimisticLockException extends RuntimeException {
+
+    private final RowId rowId;
+
+    public OptimisticLockException(RowId rowId) {
+        super();
+        this.rowId = rowId;
+    }
+
+    public OptimisticLockException(String msg, RowId rowId) {
+        super(msg);
+        this.rowId = rowId;
+    }
+
+    public RowId getRowId() {
+        return rowId;
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/ReadOnlyRowId.java b/src/com/vaadin/data/util/sqlcontainer/ReadOnlyRowId.java
new file mode 100644 (file)
index 0000000..4604d27
--- /dev/null
@@ -0,0 +1,28 @@
+package com.vaadin.data.util.sqlcontainer;
+
+public class ReadOnlyRowId extends RowId {
+    private static final long serialVersionUID = -2626764781642012467L;
+    private final Integer rowNum;
+
+    public ReadOnlyRowId(int rowNum) {
+        super();
+        this.rowNum = rowNum;
+    }
+
+    @Override
+    public int hashCode() {
+        return rowNum.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj instanceof ReadOnlyRowId)) {
+            return false;
+        }
+        return rowNum.equals(((ReadOnlyRowId) obj).rowNum);
+    }
+
+    public int getRowNum() {
+        return rowNum;
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/Reference.java b/src/com/vaadin/data/util/sqlcontainer/Reference.java
new file mode 100644 (file)
index 0000000..5b5744f
--- /dev/null
@@ -0,0 +1,53 @@
+package com.vaadin.data.util.sqlcontainer;\r
+\r
+import java.io.Serializable;\r
+\r
+/**\r
+ * The reference class represents a simple [usually foreign key] reference to\r
+ * another SQLContainer. Actual foreign key reference in the database is not\r
+ * required, but it is recommended to make sure that certain constraints are\r
+ * followed.\r
+ */\r
+@SuppressWarnings("serial")\r
+class Reference implements Serializable {\r
+\r
+    /**\r
+     * The SQLContainer that this reference points to.\r
+     */\r
+    private SQLContainer referencedContainer;\r
+\r
+    /**\r
+     * The column ID/name in the referencing SQLContainer that contains the key\r
+     * used for the reference.\r
+     */\r
+    private String referencingColumn;\r
+\r
+    /**\r
+     * The column ID/name in the referenced SQLContainer that contains the key\r
+     * used for the reference.\r
+     */\r
+    private String referencedColumn;\r
+\r
+    /**\r
+     * Constructs a new reference to be used within the SQLContainer to\r
+     * reference another SQLContainer.\r
+     */\r
+    Reference(SQLContainer referencedContainer, String referencingColumn,\r
+            String referencedColumn) {\r
+        this.referencedContainer = referencedContainer;\r
+        this.referencingColumn = referencingColumn;\r
+        this.referencedColumn = referencedColumn;\r
+    }\r
+\r
+    SQLContainer getReferencedContainer() {\r
+        return referencedContainer;\r
+    }\r
+\r
+    String getReferencingColumn() {\r
+        return referencingColumn;\r
+    }\r
+\r
+    String getReferencedColumn() {\r
+        return referencedColumn;\r
+    }\r
+}\r
diff --git a/src/com/vaadin/data/util/sqlcontainer/RowId.java b/src/com/vaadin/data/util/sqlcontainer/RowId.java
new file mode 100644 (file)
index 0000000..33ef273
--- /dev/null
@@ -0,0 +1,78 @@
+package com.vaadin.data.util.sqlcontainer;\r
+\r
+import java.io.Serializable;\r
+\r
+/**\r
+ * RowId represents identifiers of a single database result set row.\r
+ * \r
+ * The data structure of a RowId is an Object array which contains the values of\r
+ * the primary key columns of the identified row. This allows easy equals()\r
+ * -comparison of RowItems.\r
+ */\r
+public class RowId implements Serializable {\r
+    private static final long serialVersionUID = -3161778404698901258L;\r
+    protected Object[] id;\r
+\r
+    /**\r
+     * Prevent instantiation without required parameters.\r
+     */\r
+    protected RowId() {\r
+    }\r
+\r
+    public RowId(Object[] id) {\r
+        if (id == null) {\r
+            throw new IllegalArgumentException("id parameter must not be null!");\r
+        }\r
+        this.id = id;\r
+    }\r
+\r
+    public Object[] getId() {\r
+        return id;\r
+    }\r
+\r
+    @Override\r
+    public int hashCode() {\r
+        int result = 31;\r
+        if (id != null) {\r
+            for (Object o : id) {\r
+                if (o != null) {\r
+                    result += o.hashCode();\r
+                }\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    @Override\r
+    public boolean equals(Object obj) {\r
+        if (obj == null || !(obj instanceof RowId)) {\r
+            return false;\r
+        }\r
+        Object[] compId = ((RowId) obj).getId();\r
+        if (id == null && compId == null) {\r
+            return true;\r
+        }\r
+        if (id.length != compId.length) {\r
+            return false;\r
+        }\r
+        for (int i = 0; i < id.length; i++) {\r
+            if ((id[i] == null && compId[i] != null)\r
+                    || (id[i] != null && !id[i].equals(compId[i]))) {\r
+                return false;\r
+            }\r
+        }\r
+        return true;\r
+    }\r
+\r
+    @Override\r
+    public String toString() {\r
+        StringBuffer s = new StringBuffer();\r
+        for (int i = 0; i < id.length; i++) {\r
+            s.append(id[i]);\r
+            if (i < id.length - 1) {\r
+                s.append("/");\r
+            }\r
+        }\r
+        return s.toString();\r
+    }\r
+}\r
diff --git a/src/com/vaadin/data/util/sqlcontainer/RowItem.java b/src/com/vaadin/data/util/sqlcontainer/RowItem.java
new file mode 100644 (file)
index 0000000..46aefd2
--- /dev/null
@@ -0,0 +1,125 @@
+package com.vaadin.data.util.sqlcontainer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+
+/**
+ * RowItem represents one row of a result set obtained from a QueryDelegate.
+ * 
+ * Note that depending on the QueryDelegate in use this does not necessarily map
+ * into an actual row in a database table.
+ */
+public final class RowItem implements Item {
+    private static final long serialVersionUID = -6228966439127951408L;
+    private SQLContainer container;
+    private RowId id;
+    private Collection<ColumnProperty> properties;
+
+    /**
+     * Prevent instantiation without required parameters.
+     */
+    @SuppressWarnings("unused")
+    private RowItem() {
+    }
+
+    public RowItem(SQLContainer container, RowId id,
+            Collection<ColumnProperty> properties) {
+        if (container == null) {
+            throw new IllegalArgumentException("Container cannot be null.");
+        }
+        if (id == null) {
+            throw new IllegalArgumentException("Row ID cannot be null.");
+        }
+        this.container = container;
+        this.properties = properties;
+        /* Set this RowItem as owner to the properties */
+        if (properties != null) {
+            for (ColumnProperty p : properties) {
+                p.setOwner(this);
+            }
+        }
+        this.id = id;
+    }
+
+    public Property getItemProperty(Object id) {
+        if (id instanceof String && id != null) {
+            for (ColumnProperty cp : properties) {
+                if (id.equals(cp.getPropertyId())) {
+                    return cp;
+                }
+            }
+        }
+        return null;
+    }
+
+    public Collection<?> getItemPropertyIds() {
+        Collection<String> ids = new ArrayList<String>(properties.size());
+        for (ColumnProperty cp : properties) {
+            ids.add(cp.getPropertyId());
+        }
+        return Collections.unmodifiableCollection(ids);
+    }
+
+    /**
+     * Adding properties is not supported. Properties are generated by
+     * SQLContainer.
+     */
+    public boolean addItemProperty(Object id, Property property)
+            throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Removing properties is not supported. Properties are generated by
+     * SQLContainer.
+     */
+    public boolean removeItemProperty(Object id)
+            throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    public RowId getId() {
+        return id;
+    }
+
+    public SQLContainer getContainer() {
+        return container;
+    }
+
+    public boolean isModified() {
+        if (properties != null) {
+            for (ColumnProperty p : properties) {
+                if (p.isModified()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer s = new StringBuffer();
+        s.append("ID:");
+        s.append(getId().toString());
+        for (Object propId : getItemPropertyIds()) {
+            s.append("|");
+            s.append(propId.toString());
+            s.append(":");
+            s.append(getItemProperty(propId).toString());
+        }
+        return s.toString();
+    }
+
+    public void commit() {
+        if (properties != null) {
+            for (ColumnProperty p : properties) {
+                p.commit();
+            }
+        }
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java
new file mode 100644 (file)
index 0000000..f077029
--- /dev/null
@@ -0,0 +1,1587 @@
+package com.vaadin.data.util.sqlcontainer;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Date;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.data.util.filter.Compare.Equal;
+import com.vaadin.data.util.filter.Like;
+import com.vaadin.data.util.filter.UnsupportedFilterException;
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;
+import com.vaadin.data.util.sqlcontainer.query.QueryDelegate;
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;
+import com.vaadin.data.util.sqlcontainer.query.QueryDelegate.RowIdChangeListener;
+import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator;
+
+public class SQLContainer implements Container, Container.Filterable,
+        Container.Indexed, Container.Sortable, Container.ItemSetChangeNotifier {
+    private static final long serialVersionUID = -3863564310693712511L;
+
+    /** Query delegate */
+    private QueryDelegate delegate;
+    /** Auto commit mode, default = false */
+    private boolean autoCommit = false;
+
+    /** Page length = number of items contained in one page */
+    private int pageLength = DEFAULT_PAGE_LENGTH;
+    public static final int DEFAULT_PAGE_LENGTH = 100;
+
+    /** Number of items to cache = CACHE_RATIO x pageLength */
+    public static final int CACHE_RATIO = 2;
+
+    /** Item and index caches */
+    private final Map<Integer, RowId> itemIndexes = new HashMap<Integer, RowId>();
+    private final CacheMap<RowId, RowItem> cachedItems = new CacheMap<RowId, RowItem>();
+
+    /** Container properties = column names, data types and statuses */
+    private final List<String> propertyIds = new ArrayList<String>();
+    private final Map<String, Class<?>> propertyTypes = new HashMap<String, Class<?>>();
+    private final Map<String, Boolean> propertyReadOnly = new HashMap<String, Boolean>();
+    private final Map<String, Boolean> propertyNullable = new HashMap<String, Boolean>();
+
+    /** Filters (WHERE) and sorters (ORDER BY) */
+    private final List<Filter> filters = new ArrayList<Filter>();
+    private final List<OrderBy> sorters = new ArrayList<OrderBy>();
+
+    /**
+     * Total number of items available in the data source using the current
+     * query, filters and sorters.
+     */
+    private int size;
+
+    /**
+     * Size updating logic. Do not update size from data source if it has been
+     * updated in the last sizeValidMilliSeconds milliseconds.
+     */
+    private final int sizeValidMilliSeconds = 10000;
+    private boolean sizeDirty = true;
+    private Date sizeUpdated = new Date();
+
+    /** Starting row number of the currently fetched page */
+    private int currentOffset;
+
+    /** ItemSetChangeListeners */
+    private LinkedList<Container.ItemSetChangeListener> itemSetChangeListeners;
+
+    /** Temporary storage for modified items and items to be removed and added */
+    private final Map<RowId, RowItem> removedItems = new HashMap<RowId, RowItem>();
+    private final List<RowItem> addedItems = new ArrayList<RowItem>();
+    private final List<RowItem> modifiedItems = new ArrayList<RowItem>();
+
+    /** List of references to other SQLContainers */
+    private final Map<SQLContainer, Reference> references = new HashMap<SQLContainer, Reference>();
+
+    /** Cache flush notification system enabled. Disabled by default. */
+    private boolean notificationsEnabled;
+
+    /** Enable to output possible stack traces and diagnostic information */
+    private boolean debugMode;
+
+    /**
+     * Prevent instantiation without a QueryDelegate.
+     */
+    @SuppressWarnings("unused")
+    private SQLContainer() {
+    }
+
+    /**
+     * Creates and initializes SQLContainer using the given QueryDelegate
+     * 
+     * @param delegate
+     *            QueryDelegate implementation
+     * @throws SQLException
+     */
+    public SQLContainer(QueryDelegate delegate) throws SQLException {
+        if (delegate == null) {
+            throw new IllegalArgumentException(
+                    "QueryDelegate must not be null.");
+        }
+        this.delegate = delegate;
+        getPropertyIds();
+        cachedItems.setCacheLimit(CACHE_RATIO * getPageLength());
+    }
+
+    /**************************************/
+    /** Methods from interface Container **/
+    /**************************************/
+
+    /**
+     * Note! If auto commit mode is enabled, this method will still return the
+     * temporary row ID assigned for the item. Implement
+     * QueryDelegate.RowIdChangeListener to receive the actual Row ID value
+     * after the addition has been committed.
+     * 
+     * {@inheritDoc}
+     */
+    public Object addItem() throws UnsupportedOperationException {
+        Object emptyKey[] = new Object[delegate.getPrimaryKeyColumns().size()];
+        RowId itemId = new TemporaryRowId(emptyKey);
+        // Create new empty column properties for the row item.
+        List<ColumnProperty> itemProperties = new ArrayList<ColumnProperty>();
+        for (String propertyId : propertyIds) {
+            /* Default settings for new item properties. */
+            itemProperties
+                    .add(new ColumnProperty(propertyId, propertyReadOnly
+                            .get(propertyId),
+                            !propertyReadOnly.get(propertyId), propertyNullable
+                                    .get(propertyId), null, getType(propertyId)));
+        }
+        RowItem newRowItem = new RowItem(this, itemId, itemProperties);
+
+        if (autoCommit) {
+            /* Add and commit instantly */
+            try {
+                if (delegate instanceof TableQuery) {
+                    itemId = ((TableQuery) delegate)
+                            .storeRowImmediately(newRowItem);
+                } else {
+                    delegate.beginTransaction();
+                    delegate.storeRow(newRowItem);
+                    delegate.commit();
+                }
+                refresh();
+                if (notificationsEnabled) {
+                    CacheFlushNotifier.notifyOfCacheFlush(this);
+                }
+                debug(null, "Row added to DB...");
+                return itemId;
+            } catch (SQLException e) {
+                debug(e, null);
+                try {
+                    delegate.rollback();
+                } catch (SQLException ee) {
+                    debug(ee, null);
+                }
+                return null;
+            }
+        } else {
+            addedItems.add(newRowItem);
+            fireContentsChange();
+            return itemId;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#containsId(java.lang.Object)
+     */
+    public boolean containsId(Object itemId) {
+        if (itemId == null) {
+            return false;
+        }
+
+        if (cachedItems.containsKey(itemId)) {
+            return true;
+        } else {
+            for (RowItem item : addedItems) {
+                if (item.getId().equals(itemId)) {
+                    return itemPassesFilters(item);
+                }
+            }
+        }
+        if (removedItems.containsKey(itemId)) {
+            return false;
+        }
+
+        if (itemId instanceof ReadOnlyRowId) {
+            int rowNum = ((ReadOnlyRowId) itemId).getRowNum();
+            return rowNum >= 0 && rowNum < size;
+        }
+
+        if (!(itemId instanceof TemporaryRowId)) {
+            try {
+                return delegate.containsRowWithKey(((RowId) itemId).getId());
+            } catch (Exception e) {
+                /* Query failed, just return false. */
+                debug(e, null);
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object,
+     * java.lang.Object)
+     */
+    public Property getContainerProperty(Object itemId, Object propertyId) {
+        Item item = getItem(itemId);
+        if (item == null) {
+            return null;
+        }
+        return item.getItemProperty(propertyId);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#getContainerPropertyIds()
+     */
+    public Collection<?> getContainerPropertyIds() {
+        return Collections.unmodifiableCollection(propertyIds);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#getItem(java.lang.Object)
+     */
+    public Item getItem(Object itemId) {
+        if (!cachedItems.containsKey(itemId)) {
+            int index = indexOfId(itemId);
+            if (index >= size) {
+                // The index is in the added items
+                int offset = index - size;
+                RowItem item = addedItems.get(offset);
+                if (itemPassesFilters(item)) {
+                    return item;
+                } else {
+                    return null;
+                }
+            } else {
+                // load the item into cache
+                updateOffsetAndCache(index);
+            }
+        }
+        return cachedItems.get(itemId);
+    }
+
+    /**
+     * Bypasses in-memory filtering to return items that are cached in memory.
+     * <em>NOTE</em>: This does not bypass database-level filtering.
+     * 
+     * @param itemId
+     *            the id of the item to retrieve.
+     * @return the item represented by itemId.
+     */
+    public Item getItemUnfiltered(Object itemId) {
+        if (!cachedItems.containsKey(itemId)) {
+            for (RowItem item : addedItems) {
+                if (item.getId().equals(itemId)) {
+                    return item;
+                }
+            }
+        }
+        return cachedItems.get(itemId);
+    }
+
+    /**
+     * NOTE! Do not use this method if in any way avoidable. This method doesn't
+     * (and cannot) use lazy loading, which means that all rows in the database
+     * will be loaded into memory.
+     * 
+     * {@inheritDoc}
+     */
+    public Collection<?> getItemIds() {
+        updateCount();
+        ArrayList<RowId> ids = new ArrayList<RowId>();
+        ResultSet rs = null;
+        try {
+            // Load ALL rows :(
+            delegate.beginTransaction();
+            rs = delegate.getResults(0, 0);
+            List<String> pKeys = delegate.getPrimaryKeyColumns();
+            while (rs.next()) {
+                RowId id = null;
+                if (pKeys.isEmpty()) {
+                    /* Create a read only itemId */
+                    id = new ReadOnlyRowId(rs.getRow());
+                } else {
+                    /* Generate itemId for the row based on primary key(s) */
+                    Object[] itemId = new Object[pKeys.size()];
+                    for (int i = 0; i < pKeys.size(); i++) {
+                        itemId[i] = rs.getObject(pKeys.get(i));
+                    }
+                    id = new RowId(itemId);
+                }
+                if (id != null && !removedItems.containsKey(id)) {
+                    ids.add(id);
+                }
+            }
+            rs.getStatement().close();
+            rs.close();
+            delegate.commit();
+        } catch (SQLException e) {
+            debug(e, null);
+            try {
+                delegate.rollback();
+            } catch (SQLException e1) {
+                debug(e1, null);
+            }
+            try {
+                rs.getStatement().close();
+                rs.close();
+            } catch (SQLException e1) {
+                debug(e1, null);
+            }
+            throw new RuntimeException("Failed to fetch item indexes.", e);
+        }
+        for (RowItem item : getFilteredAddedItems()) {
+            ids.add(item.getId());
+        }
+        return Collections.unmodifiableCollection(ids);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#getType(java.lang.Object)
+     */
+    public Class<?> getType(Object propertyId) {
+        if (!propertyIds.contains(propertyId)) {
+            return null;
+        }
+        return propertyTypes.get(propertyId);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#size()
+     */
+    public int size() {
+        updateCount();
+        return size + sizeOfAddedItems() - removedItems.size();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#removeItem(java.lang.Object)
+     */
+    public boolean removeItem(Object itemId)
+            throws UnsupportedOperationException {
+        if (!containsId(itemId)) {
+            return false;
+        }
+        for (RowItem item : addedItems) {
+            if (item.getId().equals(itemId)) {
+                addedItems.remove(item);
+                fireContentsChange();
+                return true;
+            }
+        }
+
+        if (autoCommit) {
+            /* Remove and commit instantly. */
+            Item i = getItem(itemId);
+            if (i == null) {
+                return false;
+            }
+            try {
+                delegate.beginTransaction();
+                boolean success = delegate.removeRow((RowItem) i);
+                delegate.commit();
+                refresh();
+                if (notificationsEnabled) {
+                    CacheFlushNotifier.notifyOfCacheFlush(this);
+                }
+                if (success) {
+                    debug(null, "Row removed from DB...");
+                }
+                return success;
+            } catch (SQLException e) {
+                debug(e, null);
+                try {
+                    delegate.rollback();
+                } catch (SQLException ee) {
+                    /* Nothing can be done here */
+                    debug(ee, null);
+                }
+                return false;
+            }
+        } else {
+            removedItems.put((RowId) itemId, (RowItem) getItem(itemId));
+            cachedItems.remove(itemId);
+            refresh();
+            return true;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#removeAllItems()
+     */
+    public boolean removeAllItems() throws UnsupportedOperationException {
+        if (autoCommit) {
+            /* Remove and commit instantly. */
+            try {
+                delegate.beginTransaction();
+                boolean success = true;
+                for (Object id : getItemIds()) {
+                    if (!delegate.removeRow((RowItem) getItem(id))) {
+                        success = false;
+                    }
+                }
+                if (success) {
+                    delegate.commit();
+                    debug(null, "All rows removed from DB...");
+                    refresh();
+                    if (notificationsEnabled) {
+                        CacheFlushNotifier.notifyOfCacheFlush(this);
+                    }
+                } else {
+                    delegate.rollback();
+                }
+                return success;
+            } catch (SQLException e) {
+                debug(e, null);
+                try {
+                    delegate.rollback();
+                } catch (SQLException ee) {
+                    /* Nothing can be done here */
+                    debug(ee, null);
+                }
+                return false;
+            }
+        } else {
+            for (Object id : getItemIds()) {
+                removedItems.put((RowId) id, (RowItem) getItem(id));
+                cachedItems.remove(id);
+            }
+            refresh();
+            return true;
+        }
+    }
+
+    /*************************************************/
+    /** Methods from interface Container.Filterable **/
+    /*************************************************/
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addContainerFilter(Filter filter)
+            throws UnsupportedFilterException {
+        // filter.setCaseSensitive(!ignoreCase);
+
+        filters.add(filter);
+        refresh();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removeContainerFilter(Filter filter) {
+        filters.remove(filter);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addContainerFilter(Object propertyId, String filterString,
+            boolean ignoreCase, boolean onlyMatchPrefix) {
+        if (propertyId == null || !propertyIds.contains(propertyId)) {
+            return;
+        }
+
+        /* Generate Filter -object */
+        String likeStr = onlyMatchPrefix ? filterString + "%" : "%"
+                + filterString + "%";
+        Like like = new Like(propertyId.toString(), likeStr);
+        like.setCaseSensitive(!ignoreCase);
+        filters.add(like);
+        refresh();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removeContainerFilters(Object propertyId) {
+        ArrayList<Filter> toRemove = new ArrayList<Filter>();
+        for (Filter f : filters) {
+            if (f.appliesToProperty(propertyId)) {
+                toRemove.add(f);
+            }
+        }
+        filters.removeAll(toRemove);
+        refresh();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removeAllContainerFilters() {
+        filters.clear();
+        refresh();
+    }
+
+    /**********************************************/
+    /** Methods from interface Container.Indexed **/
+    /**********************************************/
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Indexed#indexOfId(java.lang.Object)
+     */
+    public int indexOfId(Object itemId) {
+        // First check if the id is in the added items
+        for (int ix = 0; ix < addedItems.size(); ix++) {
+            RowItem item = addedItems.get(ix);
+            if (item.getId().equals(itemId)) {
+                if (itemPassesFilters(item)) {
+                    updateCount();
+                    return size + ix;
+                } else {
+                    return -1;
+                }
+            }
+        }
+
+        if (!containsId(itemId)) {
+            return -1;
+        }
+        if (cachedItems.isEmpty()) {
+            getPage();
+        }
+        int size = size();
+        boolean wrappedAround = false;
+        while (!wrappedAround) {
+            for (Integer i : itemIndexes.keySet()) {
+                if (itemIndexes.get(i).equals(itemId)) {
+                    return i;
+                }
+            }
+            // load in the next page.
+            int nextIndex = (currentOffset / (pageLength * CACHE_RATIO) + 1)
+                    * (pageLength * CACHE_RATIO);
+            if (nextIndex >= size) {
+                // Container wrapped around, start from index 0.
+                wrappedAround = true;
+                nextIndex = 0;
+            }
+            updateOffsetAndCache(nextIndex);
+        }
+        return -1;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Indexed#getIdByIndex(int)
+     */
+    public Object getIdByIndex(int index) {
+        if (index < 0 || index > size() - 1) {
+            return null;
+        }
+        if (index < size) {
+            if (itemIndexes.keySet().contains(index)) {
+                return itemIndexes.get(index);
+            }
+            updateOffsetAndCache(index);
+            return itemIndexes.get(index);
+        } else {
+            // The index is in the added items
+            int offset = index - size;
+            return addedItems.get(offset).getId();
+        }
+    }
+
+    /**********************************************/
+    /** Methods from interface Container.Ordered **/
+    /**********************************************/
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
+     */
+    public Object nextItemId(Object itemId) {
+        return getIdByIndex(indexOfId(itemId) + 1);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
+     */
+    public Object prevItemId(Object itemId) {
+        return getIdByIndex(indexOfId(itemId) - 1);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#firstItemId()
+     */
+    public Object firstItemId() {
+        updateCount();
+        if (size == 0) {
+            if (addedItems.isEmpty()) {
+                return null;
+            } else {
+                int ix = -1;
+                do {
+                    ix++;
+                } while (!itemPassesFilters(addedItems.get(ix))
+                        && ix < addedItems.size());
+                if (ix < addedItems.size()) {
+                    return addedItems.get(ix).getId();
+                }
+            }
+        }
+        if (!itemIndexes.containsKey(0)) {
+            updateOffsetAndCache(0);
+        }
+        return itemIndexes.get(0);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#lastItemId()
+     */
+    public Object lastItemId() {
+        if (addedItems.isEmpty()) {
+            int lastIx = size() - 1;
+            if (!itemIndexes.containsKey(lastIx)) {
+                updateOffsetAndCache(size - 1);
+            }
+            return itemIndexes.get(lastIx);
+        } else {
+            int ix = addedItems.size();
+            do {
+                ix--;
+            } while (!itemPassesFilters(addedItems.get(ix)) && ix >= 0);
+            if (ix >= 0) {
+                return addedItems.get(ix).getId();
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
+     */
+    public boolean isFirstId(Object itemId) {
+        return firstItemId().equals(itemId);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
+     */
+    public boolean isLastId(Object itemId) {
+        return lastItemId().equals(itemId);
+    }
+
+    /***********************************************/
+    /** Methods from interface Container.Sortable **/
+    /***********************************************/
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
+     * boolean[])
+     */
+    public void sort(Object[] propertyId, boolean[] ascending) {
+        sorters.clear();
+        if (propertyId == null || propertyId.length == 0) {
+            refresh();
+            return;
+        }
+        /* Generate OrderBy -objects */
+        boolean asc = true;
+        for (int i = 0; i < propertyId.length; i++) {
+            /* Check that the property id is valid */
+            if (propertyId[i] instanceof String
+                    && propertyIds.contains(propertyId[i])) {
+                try {
+                    asc = ascending[i];
+                } catch (Exception e) {
+                    debug(e, null);
+                }
+                sorters.add(new OrderBy((String) propertyId[i], asc));
+            }
+        }
+        refresh();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
+     */
+    public Collection<?> getSortableContainerPropertyIds() {
+        return getContainerPropertyIds();
+    }
+
+    /**************************************/
+    /** Methods specific to SQLContainer **/
+    /**************************************/
+
+    /**
+     * Refreshes the container - clears all caches and resets size and offset.
+     * Does NOT remove sorting or filtering rules!
+     */
+    public void refresh() {
+        sizeDirty = true;
+        currentOffset = 0;
+        cachedItems.clear();
+        itemIndexes.clear();
+        fireContentsChange();
+    }
+
+    /**
+     * Returns modify state of the container.
+     * 
+     * @return true if contents of this container have been modified
+     */
+    public boolean isModified() {
+        return !removedItems.isEmpty() || !addedItems.isEmpty()
+                || !modifiedItems.isEmpty();
+    }
+
+    /**
+     * Set auto commit mode enabled or disabled. Auto commit mode means that all
+     * changes made to items of this container will be immediately written to
+     * the underlying data source.
+     * 
+     * @param autoCommitEnabled
+     *            true to enable auto commit mode
+     */
+    public void setAutoCommit(boolean autoCommitEnabled) {
+        autoCommit = autoCommitEnabled;
+    }
+
+    /**
+     * Returns status of the auto commit mode.
+     * 
+     * @return true if auto commit mode is enabled
+     */
+    public boolean isAutoCommit() {
+        return autoCommit;
+    }
+
+    /**
+     * Returns the currently set page length.
+     * 
+     * @return current page length
+     */
+    public int getPageLength() {
+        return pageLength;
+    }
+
+    /**
+     * Sets the page length used in lazy fetching of items from the data source.
+     * Also resets the cache size to match the new page length.
+     * 
+     * As a side effect the container will be refreshed.
+     * 
+     * @param pageLength
+     *            new page length
+     */
+    public void setPageLength(int pageLength) {
+        setPageLengthInternal(pageLength);
+        refresh();
+    }
+
+    /**
+     * Sets the page length internally, without refreshing the container.
+     * 
+     * @param pageLength
+     *            the new page length
+     */
+    private void setPageLengthInternal(int pageLength) {
+        this.pageLength = pageLength > 0 ? pageLength : DEFAULT_PAGE_LENGTH;
+        cachedItems.setCacheLimit(CACHE_RATIO * getPageLength());
+    }
+
+    /**
+     * Adds the given OrderBy to this container and refreshes the container
+     * contents with the new sorting rules.
+     * 
+     * Note that orderBy.getColumn() must return a column name that exists in
+     * this container.
+     * 
+     * @param orderBy
+     *            OrderBy to be added to the container sorting rules
+     */
+    public void addOrderBy(OrderBy orderBy) {
+        if (orderBy == null) {
+            return;
+        }
+        if (!propertyIds.contains(orderBy.getColumn())) {
+            throw new IllegalArgumentException(
+                    "The column given for sorting does not exist in this container.");
+        }
+        sorters.add(orderBy);
+        refresh();
+    }
+
+    /**
+     * Commits all the changes, additions and removals made to the items of this
+     * container.
+     * 
+     * @throws UnsupportedOperationException
+     * @throws SQLException
+     */
+    public void commit() throws UnsupportedOperationException, SQLException {
+        try {
+            debug(null, "Commiting changes through delegate...");
+            delegate.beginTransaction();
+            /* Perform buffered deletions */
+            for (RowItem item : removedItems.values()) {
+                if (!delegate.removeRow(item)) {
+                    throw new SQLException("Removal failed for row with ID: "
+                            + item.getId());
+                }
+            }
+            /* Perform buffered modifications */
+            for (RowItem item : modifiedItems) {
+                if (delegate.storeRow(item) > 0) {
+                    /*
+                     * Also reset the modified state in the item in case it is
+                     * reused e.g. in a form.
+                     */
+                    item.commit();
+                } else {
+                    delegate.rollback();
+                    refresh();
+                    throw new ConcurrentModificationException(
+                            "Item with the ID '" + item.getId()
+                                    + "' has been externally modified.");
+                }
+            }
+            /* Perform buffered additions */
+            for (RowItem item : addedItems) {
+                delegate.storeRow(item);
+            }
+            delegate.commit();
+            removedItems.clear();
+            addedItems.clear();
+            modifiedItems.clear();
+            refresh();
+            if (notificationsEnabled) {
+                CacheFlushNotifier.notifyOfCacheFlush(this);
+            }
+        } catch (SQLException e) {
+            delegate.rollback();
+            throw e;
+        }
+    }
+
+    /**
+     * Rolls back all the changes, additions and removals made to the items of
+     * this container.
+     * 
+     * @throws UnsupportedOperationException
+     * @throws SQLException
+     */
+    public void rollback() throws UnsupportedOperationException, SQLException {
+        debug(null, "Rolling back changes...");
+        removedItems.clear();
+        addedItems.clear();
+        modifiedItems.clear();
+        refresh();
+    }
+
+    /**
+     * Notifies this container that a property in the given item has been
+     * modified. The change will be buffered or made instantaneously depending
+     * on auto commit mode.
+     * 
+     * @param changedItem
+     *            item that has a modified property
+     */
+    void itemChangeNotification(RowItem changedItem) {
+        if (autoCommit) {
+            try {
+                delegate.beginTransaction();
+                if (delegate.storeRow(changedItem) == 0) {
+                    delegate.rollback();
+                    refresh();
+                    throw new ConcurrentModificationException(
+                            "Item with the ID '" + changedItem.getId()
+                                    + "' has been externally modified.");
+                }
+                delegate.commit();
+                if (notificationsEnabled) {
+                    CacheFlushNotifier.notifyOfCacheFlush(this);
+                }
+                debug(null, "Row updated to DB...");
+            } catch (SQLException e) {
+                debug(e, null);
+                try {
+                    delegate.rollback();
+                } catch (SQLException ee) {
+                    /* Nothing can be done here */
+                    debug(e, null);
+                }
+                throw new RuntimeException(e);
+            }
+        } else {
+            if (!(changedItem.getId() instanceof TemporaryRowId)
+                    && !modifiedItems.contains(changedItem)) {
+                modifiedItems.add(changedItem);
+            }
+        }
+    }
+
+    /**
+     * Determines a new offset for updating the row cache. The offset is
+     * calculated from the given index, and will be fixed to match the start of
+     * a page, based on the value of pageLength.
+     * 
+     * @param index
+     *            Index of the item that was requested, but not found in cache
+     */
+    private void updateOffsetAndCache(int index) {
+        if (itemIndexes.containsKey(index)) {
+            return;
+        }
+        currentOffset = (index / (pageLength * CACHE_RATIO))
+                * (pageLength * CACHE_RATIO);
+        if (currentOffset < 0) {
+            currentOffset = 0;
+        }
+        getPage();
+    }
+
+    /**
+     * Fetches new count of rows from the data source, if needed.
+     */
+    private void updateCount() {
+        if (!sizeDirty
+                && new Date().getTime() < sizeUpdated.getTime()
+                        + sizeValidMilliSeconds) {
+            return;
+        }
+        try {
+            try {
+                delegate.setFilters(filters);
+            } catch (UnsupportedOperationException e) {
+                /* The query delegate doesn't support filtering. */
+                debug(e, null);
+            }
+            try {
+                delegate.setOrderBy(sorters);
+            } catch (UnsupportedOperationException e) {
+                /* The query delegate doesn't support filtering. */
+                debug(e, null);
+            }
+            int newSize = delegate.getCount();
+            if (newSize != size) {
+                size = newSize;
+                refresh();
+            }
+            sizeUpdated = new Date();
+            sizeDirty = false;
+            debug(null, "Updated row count. New count is: " + size);
+        } catch (SQLException e) {
+            throw new RuntimeException("Failed to update item set size.", e);
+        }
+    }
+
+    /**
+     * Fetches property id's (column names and their types) from the data
+     * source.
+     * 
+     * @throws SQLException
+     */
+    private void getPropertyIds() throws SQLException {
+        propertyIds.clear();
+        propertyTypes.clear();
+        delegate.setFilters(null);
+        delegate.setOrderBy(null);
+        ResultSet rs = null;
+        ResultSetMetaData rsmd = null;
+        try {
+            delegate.beginTransaction();
+            rs = delegate.getResults(0, 1);
+            boolean resultExists = rs.next();
+            rsmd = rs.getMetaData();
+            Class<?> type = null;
+            for (int i = 1; i <= rsmd.getColumnCount(); i++) {
+                if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) {
+                    continue;
+                }
+                String colName = rsmd.getColumnLabel(i);
+                /*
+                 * Make sure not to add the same colName twice. This can easily
+                 * happen if the SQL query joins many tables with an ID column.
+                 */
+                if (!propertyIds.contains(colName)) {
+                    propertyIds.add(colName);
+                }
+                /* Try to determine the column's JDBC class by all means. */
+                if (resultExists && rs.getObject(i) != null) {
+                    type = rs.getObject(i).getClass();
+                } else {
+                    try {
+                        type = Class.forName(rsmd.getColumnClassName(i));
+                    } catch (Exception e) {
+                        debug(e, null);
+                        /* On failure revert to Object and hope for the best. */
+                        type = Object.class;
+                    }
+                }
+                /*
+                 * Determine read only and nullability status of the column. A
+                 * column is read only if it is reported as either read only or
+                 * auto increment by the database, and also it is set as the
+                 * version column in a TableQuery delegate.
+                 */
+                boolean readOnly = rsmd.isAutoIncrement(i)
+                        || rsmd.isReadOnly(i);
+                if (delegate instanceof TableQuery
+                        && rsmd.getColumnLabel(i).equals(
+                                ((TableQuery) delegate).getVersionColumn())) {
+                    readOnly = true;
+                }
+                propertyReadOnly.put(colName, readOnly);
+                propertyNullable.put(colName,
+                        rsmd.isNullable(i) == ResultSetMetaData.columnNullable);
+                propertyTypes.put(colName, type);
+            }
+            rs.getStatement().close();
+            rs.close();
+            delegate.commit();
+            debug(null, "Property IDs fetched.");
+        } catch (SQLException e) {
+            debug(e, null);
+            try {
+                delegate.rollback();
+            } catch (SQLException e1) {
+                debug(e1, null);
+            }
+            try {
+                if (rs != null) {
+                    if (rs.getStatement() != null) {
+                        rs.getStatement().close();
+                    }
+                    rs.close();
+                }
+            } catch (SQLException e1) {
+                debug(e1, null);
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Fetches a page from the data source based on the values of pageLenght and
+     * currentOffset. Also updates the set of primary keys, used in
+     * identification of RowItems.
+     */
+    private void getPage() {
+        updateCount();
+        ResultSet rs = null;
+        ResultSetMetaData rsmd = null;
+        cachedItems.clear();
+        itemIndexes.clear();
+        try {
+            try {
+                delegate.setOrderBy(sorters);
+            } catch (UnsupportedOperationException e) {
+                /* The query delegate doesn't support sorting. */
+                /* No need to do anything. */
+                debug(e, null);
+            }
+            delegate.beginTransaction();
+            rs = delegate.getResults(currentOffset, pageLength * CACHE_RATIO);
+            rsmd = rs.getMetaData();
+            List<String> pKeys = delegate.getPrimaryKeyColumns();
+            // }
+            /* Create new items and column properties */
+            ColumnProperty cp = null;
+            int rowCount = currentOffset;
+            if (!delegate.implementationRespectsPagingLimits()) {
+                rowCount = currentOffset = 0;
+                setPageLengthInternal(size);
+            }
+            while (rs.next()) {
+                List<ColumnProperty> itemProperties = new ArrayList<ColumnProperty>();
+                /* Generate row itemId based on primary key(s) */
+                Object[] itemId = new Object[pKeys.size()];
+                for (int i = 0; i < pKeys.size(); i++) {
+                    itemId[i] = rs.getObject(pKeys.get(i));
+                }
+                RowId id = null;
+                if (pKeys.isEmpty()) {
+                    id = new ReadOnlyRowId(rs.getRow());
+                } else {
+                    id = new RowId(itemId);
+                }
+                List<String> propertiesToAdd = new ArrayList<String>(
+                        propertyIds);
+                if (!removedItems.containsKey(id)) {
+                    for (int i = 1; i <= rsmd.getColumnCount(); i++) {
+                        if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) {
+                            continue;
+                        }
+                        String colName = rsmd.getColumnLabel(i);
+                        Object value = rs.getObject(i);
+                        Class<?> type = value != null ? value.getClass()
+                                : Object.class;
+                        if (value == null) {
+                            for (String propName : propertyTypes.keySet()) {
+                                if (propName.equals(rsmd.getColumnLabel(i))) {
+                                    type = propertyTypes.get(propName);
+                                    break;
+                                }
+                            }
+                        }
+                        /*
+                         * In case there are more than one column with the same
+                         * name, add only the first one. This can easily happen
+                         * if you join many tables where each table has an ID
+                         * column.
+                         */
+                        if (propertiesToAdd.contains(colName)) {
+                            cp = new ColumnProperty(colName,
+                                    propertyReadOnly.get(colName),
+                                    !propertyReadOnly.get(colName),
+                                    propertyNullable.get(colName), value, type);
+                            itemProperties.add(cp);
+                            propertiesToAdd.remove(colName);
+                        }
+                    }
+                    /* Cache item */
+                    itemIndexes.put(rowCount, id);
+                    cachedItems.put(id, new RowItem(this, id, itemProperties));
+                    rowCount++;
+                }
+            }
+            rs.getStatement().close();
+            rs.close();
+            delegate.commit();
+            debug(null, "Fetched " + pageLength * CACHE_RATIO
+                    + " rows starting from " + currentOffset);
+        } catch (SQLException e) {
+            debug(e, null);
+            try {
+                delegate.rollback();
+            } catch (SQLException e1) {
+                debug(e1, null);
+            }
+            try {
+                if (rs != null) {
+                    if (rs.getStatement() != null) {
+                        rs.getStatement().close();
+                        rs.close();
+                    }
+                }
+            } catch (SQLException e1) {
+                debug(e1, null);
+            }
+            throw new RuntimeException("Failed to fetch page.", e);
+        }
+    }
+
+    private int sizeOfAddedItems() {
+        return getFilteredAddedItems().size();
+    }
+
+    private List<RowItem> getFilteredAddedItems() {
+        ArrayList<RowItem> filtered = new ArrayList<RowItem>(addedItems);
+        if (filters != null && !filters.isEmpty()) {
+            for (RowItem item : addedItems) {
+                if (!itemPassesFilters(item)) {
+                    filtered.remove(item);
+                }
+            }
+        }
+        return filtered;
+    }
+
+    private boolean itemPassesFilters(RowItem item) {
+        for (Filter filter : filters) {
+            if (!filter.passesFilter(item.getId(), item)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Checks is the given column identifier valid to be used with SQLContainer.
+     * Currently the only non-valid identifier is "rownum" when MSSQL or Oracle
+     * is used. This is due to the way the SELECT queries are constructed in
+     * order to implement paging in these databases.
+     * 
+     * @param identifier
+     *            Column identifier
+     * @return true if the identifier is valid
+     */
+    private boolean isColumnIdentifierValid(String identifier) {
+        if (identifier.equalsIgnoreCase("rownum")
+                && delegate instanceof TableQuery) {
+            TableQuery tq = (TableQuery) delegate;
+            if (tq.getSqlGenerator() instanceof MSSQLGenerator
+                    || tq.getSqlGenerator() instanceof OracleGenerator) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns the QueryDelegate set for this SQLContainer.
+     * 
+     * @return current querydelegate
+     */
+    protected QueryDelegate getQueryDelegate() {
+        return delegate;
+    }
+
+    /************************************/
+    /** UNSUPPORTED CONTAINER FEATURES **/
+    /************************************/
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object,
+     * java.lang.Class, java.lang.Object)
+     */
+    public boolean addContainerProperty(Object propertyId, Class<?> type,
+            Object defaultValue) throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object)
+     */
+    public boolean removeContainerProperty(Object propertyId)
+            throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container#addItem(java.lang.Object)
+     */
+    public Item addItem(Object itemId) throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
+     * java.lang.Object)
+     */
+    public Item addItemAfter(Object previousItemId, Object newItemId)
+            throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object)
+     */
+    public Item addItemAt(int index, Object newItemId)
+            throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Indexed#addItemAt(int)
+     */
+    public Object addItemAt(int index) throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
+     */
+    public Object addItemAfter(Object previousItemId)
+            throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /******************************************/
+    /** ITEMSETCHANGENOTIFIER IMPLEMENTATION **/
+    /******************************************/
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin
+     * .data.Container.ItemSetChangeListener)
+     */
+    public void addListener(Container.ItemSetChangeListener listener) {
+        if (itemSetChangeListeners == null) {
+            itemSetChangeListeners = new LinkedList<Container.ItemSetChangeListener>();
+        }
+        itemSetChangeListeners.add(listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin
+     * .data.Container.ItemSetChangeListener)
+     */
+    public void removeListener(Container.ItemSetChangeListener listener) {
+        if (itemSetChangeListeners != null) {
+            itemSetChangeListeners.remove(listener);
+        }
+    }
+
+    protected void fireContentsChange() {
+        if (itemSetChangeListeners != null) {
+            final Object[] l = itemSetChangeListeners.toArray();
+            final Container.ItemSetChangeEvent event = new SQLContainer.ItemSetChangeEvent(
+                    this);
+            for (int i = 0; i < l.length; i++) {
+                ((Container.ItemSetChangeListener) l[i])
+                        .containerItemSetChange(event);
+            }
+        }
+    }
+
+    /**
+     * Simple ItemSetChangeEvent implementation.
+     */
+    @SuppressWarnings("serial")
+    public class ItemSetChangeEvent extends EventObject implements
+            Container.ItemSetChangeEvent {
+
+        private ItemSetChangeEvent(SQLContainer source) {
+            super(source);
+        }
+
+        public Container getContainer() {
+            return (Container) getSource();
+        }
+    }
+
+    /**************************************************/
+    /** ROWIDCHANGELISTENER PASSING TO QUERYDELEGATE **/
+    /**************************************************/
+
+    /**
+     * Adds a RowIdChangeListener to the QueryDelegate
+     * 
+     * @param listener
+     */
+    public void addListener(RowIdChangeListener listener) {
+        if (delegate instanceof QueryDelegate.RowIdChangeNotifier) {
+            ((QueryDelegate.RowIdChangeNotifier) delegate)
+                    .addListener(listener);
+        }
+    }
+
+    /**
+     * Removes a RowIdChangeListener from the QueryDelegate
+     * 
+     * @param listener
+     */
+    public void removeListener(RowIdChangeListener listener) {
+        if (delegate instanceof QueryDelegate.RowIdChangeNotifier) {
+            ((QueryDelegate.RowIdChangeNotifier) delegate)
+                    .removeListener(listener);
+        }
+    }
+
+    public boolean isDebugMode() {
+        return debugMode;
+    }
+
+    public void setDebugMode(boolean debugMode) {
+        this.debugMode = debugMode;
+    }
+
+    /**
+     * Output a debug message or a stack trace of an exception
+     * 
+     * @param message
+     */
+    private void debug(Exception e, String message) {
+        if (debugMode) {
+            // TODO: Replace with the common Vaadin logging system once it is
+            // available.
+            if (message != null) {
+                System.err.println(message);
+            }
+            if (e != null) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Calling this will enable this SQLContainer to send and receive cache
+     * flush notifications for its lifetime.
+     */
+    public void enableCacheFlushNotifications() {
+        if (!notificationsEnabled) {
+            notificationsEnabled = true;
+            CacheFlushNotifier.addInstance(this);
+        }
+    }
+
+    /******************************************/
+    /** Referencing mechanism implementation **/
+    /******************************************/
+
+    /**
+     * Adds a new reference to the given SQLContainer. In addition to the
+     * container you must provide the column (property) names used for the
+     * reference in both this and the referenced SQLContainer.
+     * 
+     * Note that multiple references pointing to the same SQLContainer are not
+     * supported.
+     * 
+     * @param refdCont
+     *            Target SQLContainer of the new reference
+     * @param refingCol
+     *            Column (property) name in this container storing the (foreign
+     *            key) reference
+     * @param refdCol
+     *            Column (property) name in the referenced container storing the
+     *            referenced key
+     */
+    public void addReference(SQLContainer refdCont, String refingCol,
+            String refdCol) {
+        if (refdCont == null) {
+            throw new IllegalArgumentException(
+                    "Referenced SQLContainer can not be null.");
+        }
+        if (!getContainerPropertyIds().contains(refingCol)) {
+            throw new IllegalArgumentException(
+                    "Given referencing column name is invalid."
+                            + " Please ensure that this container"
+                            + " contains a property ID named: " + refingCol);
+        }
+        if (!refdCont.getContainerPropertyIds().contains(refdCol)) {
+            throw new IllegalArgumentException(
+                    "Given referenced column name is invalid."
+                            + " Please ensure that the referenced container"
+                            + " contains a property ID named: " + refdCol);
+        }
+        if (references.keySet().contains(refdCont)) {
+            throw new IllegalArgumentException(
+                    "An SQLContainer instance can only be referenced once.");
+        }
+        references.put(refdCont, new Reference(refdCont, refingCol, refdCol));
+    }
+
+    /**
+     * Removes the reference pointing to the given SQLContainer.
+     * 
+     * @param refdCont
+     *            Target SQLContainer of the reference
+     * @return true if successful, false if the reference did not exist
+     */
+    public boolean removeReference(SQLContainer refdCont) {
+        if (refdCont == null) {
+            throw new IllegalArgumentException(
+                    "Referenced SQLContainer can not be null.");
+        }
+        return references.remove(refdCont) == null ? false : true;
+    }
+
+    /**
+     * Sets the referenced item. The referencing column of the item in this
+     * container is updated accordingly.
+     * 
+     * @param itemId
+     *            Item Id of the reference source (from this container)
+     * @param refdItemId
+     *            Item Id of the reference target (from referenced container)
+     * @param refdCont
+     *            Target SQLContainer of the reference
+     * @return true if the referenced item was successfully set, false on
+     *         failure
+     */
+    public boolean setReferencedItem(Object itemId, Object refdItemId,
+            SQLContainer refdCont) {
+        if (refdCont == null) {
+            throw new IllegalArgumentException(
+                    "Referenced SQLContainer can not be null.");
+        }
+        Reference r = references.get(refdCont);
+        if (r == null) {
+            throw new IllegalArgumentException(
+                    "Reference to the given SQLContainer not defined.");
+        }
+        try {
+            getContainerProperty(itemId, r.getReferencingColumn()).setValue(
+                    refdCont.getContainerProperty(refdItemId,
+                            r.getReferencedColumn()));
+            return true;
+        } catch (Exception e) {
+            debug(e, "Setting referenced item failed.");
+            return false;
+        }
+    }
+
+    /**
+     * Fetches the Item Id of the referenced item from the target SQLContainer.
+     * 
+     * @param itemId
+     *            Item Id of the reference source (from this container)
+     * @param refdCont
+     *            Target SQLContainer of the reference
+     * @return Item Id of the referenced item, or null if not found
+     */
+    public Object getReferencedItemId(Object itemId, SQLContainer refdCont) {
+        if (refdCont == null) {
+            throw new IllegalArgumentException(
+                    "Referenced SQLContainer can not be null.");
+        }
+        Reference r = references.get(refdCont);
+        if (r == null) {
+            throw new IllegalArgumentException(
+                    "Reference to the given SQLContainer not defined.");
+        }
+        Object refKey = getContainerProperty(itemId, r.getReferencingColumn())
+                .getValue();
+
+        refdCont.removeAllContainerFilters();
+        refdCont.addContainerFilter(new Equal(r.getReferencedColumn(), refKey));
+        Object toReturn = refdCont.firstItemId();
+        refdCont.removeAllContainerFilters();
+        return toReturn;
+    }
+
+    /**
+     * Fetches the referenced item from the target SQLContainer.
+     * 
+     * @param itemId
+     *            Item Id of the reference source (from this container)
+     * @param refdCont
+     *            Target SQLContainer of the reference
+     * @return The referenced item, or null if not found
+     */
+    public Item getReferencedItem(Object itemId, SQLContainer refdCont) {
+        return refdCont.getItem(getReferencedItemId(itemId, refdCont));
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/sqlcontainer/SQLUtil.java b/src/com/vaadin/data/util/sqlcontainer/SQLUtil.java
new file mode 100644 (file)
index 0000000..bed9e67
--- /dev/null
@@ -0,0 +1,31 @@
+package com.vaadin.data.util.sqlcontainer;
+
+public class SQLUtil {
+       /**
+     * Escapes different special characters in strings that are passed to SQL.
+     * Replaces the following:
+     * 
+     * <list> <li>' is replaced with ''</li> <li>\x00 is removed</li> <li>\ is
+     * replaced with \\</li> <li>" is replaced with \"</li> <li>
+     * \x1a is removed</li> </list>
+     * 
+     * Also note! The escaping done here may or may not be enough to prevent any
+     * and all SQL injections so it is recommended to check user input before
+     * giving it to the SQLContainer/TableQuery.
+     * 
+     * @param constant
+     * @return \\\'\'
+     */
+    public static String escapeSQL(String constant) {
+        if (constant == null) {
+            return null;
+        }
+        String fixedConstant = constant;
+        fixedConstant = fixedConstant.replaceAll("\\\\x00", "");
+        fixedConstant = fixedConstant.replaceAll("\\\\x1a", "");
+        fixedConstant = fixedConstant.replaceAll("'", "''");
+        fixedConstant = fixedConstant.replaceAll("\\\\", "\\\\\\\\");
+        fixedConstant = fixedConstant.replaceAll("\\\"", "\\\\\"");
+        return fixedConstant;
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/TemporaryRowId.java b/src/com/vaadin/data/util/sqlcontainer/TemporaryRowId.java
new file mode 100644 (file)
index 0000000..2ff187e
--- /dev/null
@@ -0,0 +1,29 @@
+package com.vaadin.data.util.sqlcontainer;
+
+public class TemporaryRowId extends RowId {
+    private static final long serialVersionUID = -641983830469018329L;
+
+    public TemporaryRowId(Object[] id) {
+        super(id);
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj instanceof TemporaryRowId)) {
+            return false;
+        }
+        Object[] compId = ((TemporaryRowId) obj).getId();
+        return id.equals(compId);
+    }
+
+    @Override
+    public String toString() {
+        return "Temporary row id";
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java b/src/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java
new file mode 100644 (file)
index 0000000..65a6bc6
--- /dev/null
@@ -0,0 +1,61 @@
+package com.vaadin.data.util.sqlcontainer.connection;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+public class J2EEConnectionPool implements JDBCConnectionPool {
+
+    private String dataSourceJndiName;
+
+    private DataSource dataSource = null;
+
+    public J2EEConnectionPool(DataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    public J2EEConnectionPool(String dataSourceJndiName) {
+        this.dataSourceJndiName = dataSourceJndiName;
+    }
+
+    public Connection reserveConnection() throws SQLException {
+        Connection conn = getDataSource().getConnection();
+        conn.setAutoCommit(false);
+
+        return conn;
+    }
+
+    private DataSource getDataSource() throws SQLException {
+        if (dataSource == null) {
+            dataSource = lookupDataSource();
+        }
+        return dataSource;
+    }
+
+    private DataSource lookupDataSource() throws SQLException {
+        try {
+            InitialContext ic = new InitialContext();
+            return (DataSource) ic.lookup(dataSourceJndiName);
+        } catch (NamingException e) {
+            throw new SQLException(
+                    "NamingException - Cannot connect to the database. Cause: "
+                            + e.getMessage());
+        }
+    }
+
+    public void releaseConnection(Connection conn) {
+        try {
+            conn.close();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void destroy() {
+        dataSource = null;
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/sqlcontainer/connection/JDBCConnectionPool.java b/src/com/vaadin/data/util/sqlcontainer/connection/JDBCConnectionPool.java
new file mode 100644 (file)
index 0000000..cc7916a
--- /dev/null
@@ -0,0 +1,38 @@
+package com.vaadin.data.util.sqlcontainer.connection;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * Interface for implementing connection pools to be used with SQLContainer.
+ */
+public interface JDBCConnectionPool extends Serializable {
+    /**
+     * Retrieves a connection.
+     * 
+     * @return a usable connection to the database
+     * @throws SQLException
+     */
+    public Connection reserveConnection() throws SQLException;
+
+    /**
+     * Releases a connection that was retrieved earlier.
+     * 
+     * Note that depending on implementation, the transaction possibly open in
+     * the connection may or may not be rolled back.
+     * 
+     * @param conn
+     *            Connection to be released
+     */
+    public void releaseConnection(Connection conn);
+
+    /**
+     * Destroys the connection pool: close() is called an all the connections in
+     * the pool, whether available or reserved.
+     * 
+     * This method was added to fix PostgreSQL -related issues with connections
+     * that were left hanging 'idle'.
+     */
+    public void destroy();
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java b/src/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java
new file mode 100644 (file)
index 0000000..2c1a049
--- /dev/null
@@ -0,0 +1,162 @@
+package com.vaadin.data.util.sqlcontainer.connection;\r
+\r
+import java.io.IOException;\r
+import java.sql.Connection;\r
+import java.sql.DriverManager;\r
+import java.sql.SQLException;\r
+import java.sql.Statement;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+/**\r
+ * Simple implementation of the JDBCConnectionPool interface. Handles loading\r
+ * the JDBC driver, setting up the connections and ensuring they are still\r
+ * usable upon release.\r
+ */\r
+@SuppressWarnings("serial")\r
+public class SimpleJDBCConnectionPool implements JDBCConnectionPool {\r
+\r
+    private int initialConnections = 5;\r
+    private int maxConnections = 20;\r
+\r
+    private String driverName;\r
+    private String connectionUri;\r
+    private String userName;\r
+    private String password;\r
+\r
+    private transient Set<Connection> availableConnections;\r
+    private transient Set<Connection> reservedConnections;\r
+\r
+    private boolean initialized;\r
+\r
+    public SimpleJDBCConnectionPool(String driverName, String connectionUri,\r
+            String userName, String password) throws SQLException {\r
+        if (driverName == null) {\r
+            throw new IllegalArgumentException(\r
+                    "JDBC driver class name must be given.");\r
+        }\r
+        if (connectionUri == null) {\r
+            throw new IllegalArgumentException(\r
+                    "Database connection URI must be given.");\r
+        }\r
+        if (userName == null) {\r
+            throw new IllegalArgumentException(\r
+                    "Database username must be given.");\r
+        }\r
+        if (password == null) {\r
+            throw new IllegalArgumentException(\r
+                    "Database password must be given.");\r
+        }\r
+        this.driverName = driverName;\r
+        this.connectionUri = connectionUri;\r
+        this.userName = userName;\r
+        this.password = password;\r
+\r
+        /* Initialize JDBC driver */\r
+        try {\r
+            Class.forName(driverName).newInstance();\r
+        } catch (Exception ex) {\r
+            throw new RuntimeException("Specified JDBC Driver: " + driverName\r
+                    + " - initialization failed.", ex);\r
+        }\r
+    }\r
+\r
+    public SimpleJDBCConnectionPool(String driverName, String connectionUri,\r
+            String userName, String password, int initialConnections,\r
+            int maxConnections) throws SQLException {\r
+        this(driverName, connectionUri, userName, password);\r
+        this.initialConnections = initialConnections;\r
+        this.maxConnections = maxConnections;\r
+    }\r
+\r
+    private void initializeConnections() throws SQLException {\r
+        availableConnections = new HashSet<Connection>(initialConnections);\r
+        reservedConnections = new HashSet<Connection>(initialConnections);\r
+        for (int i = 0; i < initialConnections; i++) {\r
+            availableConnections.add(createConnection());\r
+        }\r
+        initialized = true;\r
+    }\r
+\r
+    public synchronized Connection reserveConnection() throws SQLException {\r
+        if (!initialized) {\r
+            initializeConnections();\r
+        }\r
+        if (availableConnections.isEmpty()) {\r
+            if (reservedConnections.size() < maxConnections) {\r
+                availableConnections.add(createConnection());\r
+            } else {\r
+                throw new SQLException("Connection limit has been reached.");\r
+            }\r
+        }\r
+\r
+        Connection c = availableConnections.iterator().next();\r
+        availableConnections.remove(c);\r
+        reservedConnections.add(c);\r
+\r
+        return c;\r
+    }\r
+\r
+    public synchronized void releaseConnection(Connection conn) {\r
+        if (conn == null || !initialized) {\r
+            return;\r
+        }\r
+        /* Try to roll back if necessary */\r
+        try {\r
+            if (!conn.getAutoCommit()) {\r
+                conn.rollback();\r
+            }\r
+        } catch (SQLException e) {\r
+            /* Roll back failed, close and discard connection */\r
+            try {\r
+                conn.close();\r
+            } catch (SQLException e1) {\r
+                /* Nothing needs to be done */\r
+            }\r
+            reservedConnections.remove(conn);\r
+            return;\r
+        }\r
+        reservedConnections.remove(conn);\r
+        availableConnections.add(conn);\r
+    }\r
+\r
+    private Connection createConnection() throws SQLException {\r
+        Connection c = DriverManager.getConnection(connectionUri, userName,\r
+                password);\r
+        c.setAutoCommit(false);\r
+        if (driverName.toLowerCase().contains("mysql")) {\r
+            try {\r
+                Statement s = c.createStatement();\r
+                s.execute("SET SESSION sql_mode = 'ANSI'");\r
+                s.close();\r
+            } catch (Exception e) {\r
+                // Failed to set ansi mode; continue\r
+            }\r
+        }\r
+        return c;\r
+    }\r
+\r
+    public void destroy() {\r
+        for (Connection c : availableConnections) {\r
+            try {\r
+                c.close();\r
+            } catch (SQLException e) {\r
+                // No need to do anything\r
+            }\r
+        }\r
+        for (Connection c : reservedConnections) {\r
+            try {\r
+                c.close();\r
+            } catch (SQLException e) {\r
+                // No need to do anything\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    private void writeObject(java.io.ObjectOutputStream out) throws IOException {\r
+        initialized = false;\r
+        out.defaultWriteObject();\r
+    }\r
+\r
+}\r
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java b/src/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java
new file mode 100644 (file)
index 0000000..26434b7
--- /dev/null
@@ -0,0 +1,450 @@
+package com.vaadin.data.util.sqlcontainer.query;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+import com.vaadin.data.util.sqlcontainer.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
+
+@SuppressWarnings("serial")
+public class FreeformQuery implements QueryDelegate {
+
+    FreeformQueryDelegate delegate = null;
+    private String queryString;
+    private List<String> primaryKeyColumns;
+    private JDBCConnectionPool connectionPool;
+    private transient Connection activeConnection = null;
+
+    /**
+     * Prevent no-parameters instantiation of FreeformQuery
+     */
+    @SuppressWarnings("unused")
+    private FreeformQuery() {
+    }
+
+    /**
+     * Creates a new freeform query delegate to be used with the
+     * {@link SQLContainer}.
+     * 
+     * @param queryString
+     *            The actual query to perform.
+     * @param primaryKeyColumns
+     *            The primary key columns. Read-only mode is forced if this
+     *            parameter is null or empty.
+     * @param connectionPool
+     *            the JDBCConnectionPool to use to open connections to the SQL
+     *            database.
+     * @deprecated @see
+     *             {@link FreeformQuery#FreeformQuery(String, JDBCConnectionPool, String...)}
+     */
+    @Deprecated
+    public FreeformQuery(String queryString, List<String> primaryKeyColumns,
+            JDBCConnectionPool connectionPool) {
+        if (primaryKeyColumns == null) {
+            primaryKeyColumns = new ArrayList<String>();
+        }
+        if (primaryKeyColumns.contains("")) {
+            throw new IllegalArgumentException(
+                    "The primary key columns contain an empty string!");
+        } else if (queryString == null || "".equals(queryString)) {
+            throw new IllegalArgumentException(
+                    "The query string may not be empty or null!");
+        } else if (connectionPool == null) {
+            throw new IllegalArgumentException(
+                    "The connectionPool may not be null!");
+        }
+        this.queryString = queryString;
+        this.primaryKeyColumns = Collections
+                .unmodifiableList(primaryKeyColumns);
+        this.connectionPool = connectionPool;
+    }
+
+    /**
+     * Creates a new freeform query delegate to be used with the
+     * {@link SQLContainer}.
+     * 
+     * @param queryString
+     *            The actual query to perform.
+     * @param connectionPool
+     *            the JDBCConnectionPool to use to open connections to the SQL
+     *            database.
+     * @param primaryKeyColumns
+     *            The primary key columns. Read-only mode is forced if none are
+     *            provided. (optional)
+     */
+    public FreeformQuery(String queryString, JDBCConnectionPool connectionPool,
+            String... primaryKeyColumns) {
+        this(queryString, Arrays.asList(primaryKeyColumns), connectionPool);
+    }
+
+    /**
+     * This implementation of getCount() actually fetches all records from the
+     * database, which might be a performance issue. Override this method with a
+     * SELECT COUNT(*) ... query if this is too slow for your needs.
+     * 
+     * {@inheritDoc}
+     */
+    public int getCount() throws SQLException {
+        // First try the delegate
+        int count = countByDelegate();
+        if (count < 0) {
+            // Couldn't use the delegate, use the bad way.
+            Connection conn = getConnection();
+            Statement statement = conn.createStatement(
+                    ResultSet.TYPE_SCROLL_INSENSITIVE,
+                    ResultSet.CONCUR_READ_ONLY);
+
+            ResultSet rs = statement.executeQuery(queryString);
+            if (rs.last()) {
+                count = rs.getRow();
+            } else {
+                count = 0;
+            }
+            rs.close();
+            statement.close();
+            releaseConnection(conn);
+        }
+        return count;
+    }
+
+    @SuppressWarnings("deprecation")
+    private int countByDelegate() throws SQLException {
+        int count = -1;
+        if (delegate == null) {
+            return count;
+        }
+        /* First try using prepared statement */
+        if (delegate instanceof FreeformStatementDelegate) {
+            try {
+                StatementHelper sh = ((FreeformStatementDelegate) delegate)
+                        .getCountStatement();
+                Connection c = getConnection();
+                PreparedStatement pstmt = c.prepareStatement(sh
+                        .getQueryString());
+                sh.setParameterValuesToStatement(pstmt);
+                ResultSet rs = pstmt.executeQuery();
+                rs.next();
+                count = rs.getInt(1);
+                rs.close();
+                pstmt.clearParameters();
+                pstmt.close();
+                releaseConnection(c);
+                return count;
+            } catch (UnsupportedOperationException e) {
+                // Count statement generation not supported
+            }
+        }
+        /* Try using regular statement */
+        try {
+            String countQuery = delegate.getCountQuery();
+            if (countQuery != null) {
+                Connection conn = getConnection();
+                Statement statement = conn.createStatement();
+                ResultSet rs = statement.executeQuery(countQuery);
+                rs.next();
+                count = rs.getInt(1);
+                rs.close();
+                statement.close();
+                releaseConnection(conn);
+                return count;
+            }
+        } catch (UnsupportedOperationException e) {
+            // Count query generation not supported
+        }
+        return count;
+    }
+
+    private Connection getConnection() throws SQLException {
+        if (activeConnection != null) {
+            return activeConnection;
+        }
+        return connectionPool.reserveConnection();
+    }
+
+    /**
+     * Fetches the results for the query. This implementation always fetches the
+     * entire record set, ignoring the offset and pagelength parameters. In
+     * order to support lazy loading of records, you must supply a
+     * FreeformQueryDelegate that implements the
+     * FreeformQueryDelegate.getQueryString(int,int) method.
+     * 
+     * @throws SQLException
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getQueryString(int,
+     *      int) {@inheritDoc}
+     */
+    @SuppressWarnings("deprecation")
+    public ResultSet getResults(int offset, int pagelength) throws SQLException {
+        if (activeConnection == null) {
+            throw new SQLException("No active transaction!");
+        }
+        String query = queryString;
+        if (delegate != null) {
+            /* First try using prepared statement */
+            if (delegate instanceof FreeformStatementDelegate) {
+                try {
+                    StatementHelper sh = ((FreeformStatementDelegate) delegate)
+                            .getQueryStatement(offset, pagelength);
+                    PreparedStatement pstmt = activeConnection
+                            .prepareStatement(sh.getQueryString());
+                    sh.setParameterValuesToStatement(pstmt);
+                    return pstmt.executeQuery();
+                } catch (UnsupportedOperationException e) {
+                    // Statement generation not supported, continue...
+                }
+            }
+            try {
+                query = delegate.getQueryString(offset, pagelength);
+            } catch (UnsupportedOperationException e) {
+                // This is fine, we'll just use the default queryString.
+            }
+        }
+        Statement statement = activeConnection.createStatement();
+        ResultSet rs = statement.executeQuery(query);
+        return rs;
+    }
+
+    @SuppressWarnings("deprecation")
+    public boolean implementationRespectsPagingLimits() {
+        if (delegate == null) {
+            return false;
+        }
+        /* First try using prepared statement */
+        if (delegate instanceof FreeformStatementDelegate) {
+            try {
+                StatementHelper sh = ((FreeformStatementDelegate) delegate)
+                        .getCountStatement();
+                if (sh != null && sh.getQueryString() != null
+                        && sh.getQueryString().length() > 0) {
+                    return true;
+                }
+            } catch (UnsupportedOperationException e) {
+                // Statement generation not supported, continue...
+            }
+        }
+        try {
+            String queryString = delegate.getQueryString(0, 50);
+            return queryString != null && queryString.length() > 0;
+        } catch (UnsupportedOperationException e) {
+            return false;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util
+     * .List)
+     */
+    public void setFilters(List<Filter> filters)
+            throws UnsupportedOperationException {
+        if (delegate != null) {
+            delegate.setFilters(filters);
+        } else if (filters != null) {
+            throw new UnsupportedOperationException(
+                    "FreeFormQueryDelegate not set!");
+        }
+    }
+
+    public void setOrderBy(List<OrderBy> orderBys)
+            throws UnsupportedOperationException {
+        if (delegate != null) {
+            delegate.setOrderBy(orderBys);
+        } else if (orderBys != null) {
+            throw new UnsupportedOperationException(
+                    "FreeFormQueryDelegate not set!");
+        }
+    }
+
+    public int storeRow(RowItem row) throws SQLException {
+        if (activeConnection == null) {
+            throw new IllegalStateException("No transaction is active!");
+        } else if (primaryKeyColumns.isEmpty()) {
+            throw new UnsupportedOperationException(
+                    "Cannot store items fetched with a read-only freeform query!");
+        }
+        if (delegate != null) {
+            return delegate.storeRow(activeConnection, row);
+        } else {
+            throw new UnsupportedOperationException(
+                    "FreeFormQueryDelegate not set!");
+        }
+    }
+
+    public boolean removeRow(RowItem row) throws SQLException {
+        if (activeConnection == null) {
+            throw new IllegalStateException("No transaction is active!");
+        } else if (primaryKeyColumns.isEmpty()) {
+            throw new UnsupportedOperationException(
+                    "Cannot remove items fetched with a read-only freeform query!");
+        }
+        if (delegate != null) {
+            return delegate.removeRow(activeConnection, row);
+        } else {
+            throw new UnsupportedOperationException(
+                    "FreeFormQueryDelegate not set!");
+        }
+    }
+
+    public synchronized void beginTransaction()
+            throws UnsupportedOperationException, SQLException {
+        if (activeConnection != null) {
+            throw new IllegalStateException("A transaction is already active!");
+        }
+        activeConnection = connectionPool.reserveConnection();
+        activeConnection.setAutoCommit(false);
+    }
+
+    public synchronized void commit() throws UnsupportedOperationException,
+            SQLException {
+        if (activeConnection == null) {
+            throw new SQLException("No active transaction");
+        }
+        if (!activeConnection.getAutoCommit()) {
+            activeConnection.commit();
+        }
+        connectionPool.releaseConnection(activeConnection);
+        activeConnection = null;
+    }
+
+    public synchronized void rollback() throws UnsupportedOperationException,
+            SQLException {
+        if (activeConnection == null) {
+            throw new SQLException("No active transaction");
+        }
+        activeConnection.rollback();
+        connectionPool.releaseConnection(activeConnection);
+        activeConnection = null;
+    }
+
+    public List<String> getPrimaryKeyColumns() {
+        return primaryKeyColumns;
+    }
+
+    public String getQueryString() {
+        return queryString;
+    }
+
+    public FreeformQueryDelegate getDelegate() {
+        return delegate;
+    }
+
+    public void setDelegate(FreeformQueryDelegate delegate) {
+        this.delegate = delegate;
+    }
+
+    /**
+     * This implementation of the containsRowWithKey method rewrites existing
+     * WHERE clauses in the query string. The logic is, however, not very
+     * complex and some times can do the Wrong Thing<sup>TM</sup>. For the
+     * situations where this logic is not enough, you can implement the
+     * getContainsRowQueryString method in FreeformQueryDelegate and this will
+     * be used instead of the logic.
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.FreeformQueryDelegate#getContainsRowQueryString(Object...)
+     * 
+     *      {@inheritDoc}
+     */
+    @SuppressWarnings("deprecation")
+    public boolean containsRowWithKey(Object... keys) throws SQLException {
+        String query = null;
+        boolean contains = false;
+        if (delegate != null) {
+            if (delegate instanceof FreeformStatementDelegate) {
+                try {
+                    StatementHelper sh = ((FreeformStatementDelegate) delegate)
+                            .getContainsRowQueryStatement(keys);
+                    Connection c = getConnection();
+                    PreparedStatement pstmt = c.prepareStatement(sh
+                            .getQueryString());
+                    sh.setParameterValuesToStatement(pstmt);
+                    ResultSet rs = pstmt.executeQuery();
+                    contains = rs.next();
+                    rs.close();
+                    pstmt.clearParameters();
+                    pstmt.close();
+                    releaseConnection(c);
+                    return contains;
+                } catch (UnsupportedOperationException e) {
+                    // Statement generation not supported, continue...
+                }
+            }
+            try {
+                query = delegate.getContainsRowQueryString(keys);
+            } catch (UnsupportedOperationException e) {
+                query = modifyWhereClause(keys);
+            }
+        } else {
+            query = modifyWhereClause(keys);
+        }
+        Connection conn = getConnection();
+        try {
+            Statement statement = conn.createStatement();
+            ResultSet rs = statement.executeQuery(query);
+            contains = rs.next();
+            rs.close();
+            statement.close();
+        } finally {
+            releaseConnection(conn);
+        }
+        return contains;
+    }
+
+    /**
+     * Releases the connection if it is not part of an active transaction.
+     * 
+     * @param conn
+     *            the connection to release
+     */
+    private void releaseConnection(Connection conn) {
+        if (conn != activeConnection) {
+            connectionPool.releaseConnection(conn);
+        }
+    }
+
+    private String modifyWhereClause(Object... keys) {
+        // Build the where rules for the provided keys
+        StringBuffer where = new StringBuffer();
+        for (int ix = 0; ix < primaryKeyColumns.size(); ix++) {
+            where.append(QueryBuilder.quote(primaryKeyColumns.get(ix)));
+            if (keys[ix] == null) {
+                where.append(" IS NULL");
+            } else {
+                where.append(" = '").append(keys[ix]).append("'");
+            }
+            if (ix < primaryKeyColumns.size() - 1) {
+                where.append(" AND ");
+            }
+        }
+        // Is there already a WHERE clause in the query string?
+        int index = queryString.toLowerCase().indexOf("where ");
+        if (index > -1) {
+            // Rewrite the where clause
+            return queryString.substring(0, index) + "WHERE " + where + " AND "
+                    + queryString.substring(index + 6);
+        }
+        // Append a where clause
+        return queryString + " WHERE " + where;
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+        try {
+            rollback();
+        } catch (SQLException ignored) {
+        }
+        out.defaultWriteObject();
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/FreeformQueryDelegate.java b/src/com/vaadin/data/util/sqlcontainer/query/FreeformQueryDelegate.java
new file mode 100644 (file)
index 0000000..443483d
--- /dev/null
@@ -0,0 +1,115 @@
+package com.vaadin.data.util.sqlcontainer.query;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+
+public interface FreeformQueryDelegate extends Serializable {
+    /**
+     * Should return the SQL query string to be performed. This method is
+     * responsible for gluing together the select query from the filters and the
+     * order by conditions if these are supported.
+     * 
+     * @param offset
+     *            the first record (row) to fetch.
+     * @param pagelength
+     *            the number of records (rows) to fetch. 0 means all records
+     *            starting from offset.
+     * @deprecated Implement {@link FreeformStatementDelegate} instead of
+     *             {@link FreeformQueryDelegate}
+     */
+    @Deprecated
+    public String getQueryString(int offset, int limit)
+            throws UnsupportedOperationException;
+
+    /**
+     * Generates and executes a query to determine the current row count from
+     * the DB. Row count will be fetched using filters that are currently set to
+     * the QueryDelegate.
+     * 
+     * @return row count
+     * @throws SQLException
+     * @deprecated Implement {@link FreeformStatementDelegate} instead of
+     *             {@link FreeformQueryDelegate}
+     */
+    @Deprecated
+    public String getCountQuery() throws UnsupportedOperationException;
+
+    /**
+     * Sets the filters to apply when performing the SQL query. These are
+     * translated into a WHERE clause. Default filtering mode will be used.
+     * 
+     * @param filters
+     *            The filters to apply.
+     * @throws UnsupportedOperationException
+     *             if the implementation doesn't support filtering.
+     */
+    public void setFilters(List<Filter> filters)
+            throws UnsupportedOperationException;
+
+    /**
+     * Sets the order in which to retrieve rows from the database. The result
+     * can be ordered by zero or more columns and each column can be in
+     * ascending or descending order. These are translated into an ORDER BY
+     * clause in the SQL query.
+     * 
+     * @param orderBys
+     *            A list of the OrderBy conditions.
+     * @throws UnsupportedOperationException
+     *             if the implementation doesn't support ordering.
+     */
+    public void setOrderBy(List<OrderBy> orderBys)
+            throws UnsupportedOperationException;
+
+    /**
+     * Stores a row in the database. The implementation of this interface
+     * decides how to identify whether to store a new row or update an existing
+     * one.
+     * 
+     * @param conn
+     *            the JDBC connection to use
+     * @param row
+     *            RowItem to be stored or updated.
+     * @throws UnsupportedOperationException
+     *             if the implementation is read only.
+     * @throws SQLException
+     */
+    public int storeRow(Connection conn, RowItem row)
+            throws UnsupportedOperationException, SQLException;
+
+    /**
+     * Removes the given RowItem from the database.
+     * 
+     * @param conn
+     *            the JDBC connection to use
+     * @param row
+     *            RowItem to be removed
+     * @return true on success
+     * @throws UnsupportedOperationException
+     * @throws SQLException
+     */
+    public boolean removeRow(Connection conn, RowItem row)
+            throws UnsupportedOperationException, SQLException;
+
+    /**
+     * Generates an SQL Query string that allows the user of the FreeformQuery
+     * class to customize the query string used by the
+     * FreeformQuery.containsRowWithKeys() method. This is useful for cases when
+     * the logic in the containsRowWithKeys method is not enough to support more
+     * complex free form queries.
+     * 
+     * @param keys
+     *            the values of the primary keys
+     * @throws UnsupportedOperationException
+     *             to use the default logic in FreeformQuery
+     * @deprecated Implement {@link FreeformStatementDelegate} instead of
+     *             {@link FreeformQueryDelegate}
+     */
+    @Deprecated
+    public String getContainsRowQueryString(Object... keys)
+            throws UnsupportedOperationException;
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/FreeformStatementDelegate.java b/src/com/vaadin/data/util/sqlcontainer/query/FreeformStatementDelegate.java
new file mode 100644 (file)
index 0000000..67b0213
--- /dev/null
@@ -0,0 +1,54 @@
+package com.vaadin.data.util.sqlcontainer.query;\r
+\r
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;\r
+\r
+/**\r
+ * FreeformStatementDelegate is an extension to FreeformQueryDelegate that\r
+ * provides definitions for methods that produce StatementHelper objects instead\r
+ * of basic query strings. This allows the FreeformQuery query delegate to use\r
+ * PreparedStatements instead of regular Statement when accessing the database.\r
+ * \r
+ * Due to the injection protection and other benefits of prepared statements, it\r
+ * is advisable to implement this interface instead of the FreeformQueryDelegate\r
+ * whenever possible.\r
+ */\r
+public interface FreeformStatementDelegate extends FreeformQueryDelegate {\r
+    /**\r
+     * Should return a new instance of StatementHelper that contains the query\r
+     * string and parameter values required to create a PreparedStatement. This\r
+     * method is responsible for gluing together the select query from the\r
+     * filters and the order by conditions if these are supported.\r
+     * \r
+     * @param offset\r
+     *            the first record (row) to fetch.\r
+     * @param pagelength\r
+     *            the number of records (rows) to fetch. 0 means all records\r
+     *            starting from offset.\r
+     */\r
+    public StatementHelper getQueryStatement(int offset, int limit)\r
+            throws UnsupportedOperationException;\r
+\r
+    /**\r
+     * Should return a new instance of StatementHelper that contains the query\r
+     * string and parameter values required to create a PreparedStatement that\r
+     * will fetch the row count from the DB. Row count should be fetched using\r
+     * filters that are currently set to the QueryDelegate.\r
+     */\r
+    public StatementHelper getCountStatement()\r
+            throws UnsupportedOperationException;\r
+\r
+    /**\r
+     * Should return a new instance of StatementHelper that contains the query\r
+     * string and parameter values required to create a PreparedStatement used\r
+     * by the FreeformQuery.containsRowWithKeys() method. This is useful for\r
+     * cases when the default logic in said method is not enough to support more\r
+     * complex free form queries.\r
+     * \r
+     * @param keys\r
+     *            the values of the primary keys\r
+     * @throws UnsupportedOperationException\r
+     *             to use the default logic in FreeformQuery\r
+     */\r
+    public StatementHelper getContainsRowQueryStatement(Object... keys)\r
+            throws UnsupportedOperationException;\r
+}\r
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/OrderBy.java b/src/com/vaadin/data/util/sqlcontainer/query/OrderBy.java
new file mode 100644 (file)
index 0000000..439e905
--- /dev/null
@@ -0,0 +1,43 @@
+package com.vaadin.data.util.sqlcontainer.query;
+
+import java.io.Serializable;
+
+/**
+ * OrderBy represents a sorting rule to be applied to a query made by the
+ * SQLContainer's QueryDelegate.
+ * 
+ * The sorting rule is simple and contains only the affected column's name and
+ * the direction of the sort.
+ */
+public class OrderBy implements Serializable {
+    private String column;
+    private boolean isAscending;
+
+    /**
+     * Prevent instantiation without required parameters.
+     */
+    @SuppressWarnings("unused")
+    private OrderBy() {
+    }
+
+    public OrderBy(String column, boolean isAscending) {
+        setColumn(column);
+        setAscending(isAscending);
+    }
+
+    public void setColumn(String column) {
+        this.column = column;
+    }
+
+    public String getColumn() {
+        return column;
+    }
+
+    public void setAscending(boolean isAscending) {
+        this.isAscending = isAscending;
+    }
+
+    public boolean isAscending() {
+        return isAscending;
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/QueryDelegate.java b/src/com/vaadin/data/util/sqlcontainer/query/QueryDelegate.java
new file mode 100644 (file)
index 0000000..6949fa0
--- /dev/null
@@ -0,0 +1,208 @@
+package com.vaadin.data.util.sqlcontainer.query;
+
+import java.io.Serializable;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.RowId;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+
+public interface QueryDelegate extends Serializable {
+    /**
+     * Generates and executes a query to determine the current row count from
+     * the DB. Row count will be fetched using filters that are currently set to
+     * the QueryDelegate.
+     * 
+     * @return row count
+     * @throws SQLException
+     */
+    public int getCount() throws SQLException;
+
+    /**
+     * Executes a paged SQL query and returns the ResultSet. The query is
+     * defined through implementations of this QueryDelegate interface.
+     * 
+     * @param offset
+     *            the first item of the page to load
+     * @param pagelength
+     *            the length of the page to load
+     * @return a ResultSet containing the rows of the page
+     * @throws SQLException
+     *             if the database access fails.
+     */
+    public ResultSet getResults(int offset, int pagelength) throws SQLException;
+
+    /**
+     * Allows the SQLContainer implementation to check whether the QueryDelegate
+     * implementation implements paging in the getResults method.
+     * 
+     * @see QueryDelegate#getResults(int, int)
+     * 
+     * @return true if the delegate implements paging
+     */
+    public boolean implementationRespectsPagingLimits();
+
+    /**
+     * Sets the filters to apply when performing the SQL query. These are
+     * translated into a WHERE clause. Default filtering mode will be used.
+     * 
+     * @param filters
+     *            The filters to apply.
+     * @throws UnsupportedOperationException
+     *             if the implementation doesn't support filtering.
+     */
+    public void setFilters(List<Filter> filters)
+            throws UnsupportedOperationException;
+
+    /**
+     * Sets the order in which to retrieve rows from the database. The result
+     * can be ordered by zero or more columns and each column can be in
+     * ascending or descending order. These are translated into an ORDER BY
+     * clause in the SQL query.
+     * 
+     * @param orderBys
+     *            A list of the OrderBy conditions.
+     * @throws UnsupportedOperationException
+     *             if the implementation doesn't support ordering.
+     */
+    public void setOrderBy(List<OrderBy> orderBys)
+            throws UnsupportedOperationException;
+
+    /**
+     * Stores a row in the database. The implementation of this interface
+     * decides how to identify whether to store a new row or update an existing
+     * one.
+     * 
+     * @param columnToValueMap
+     *            A map containing the values for all columns to be stored or
+     *            updated.
+     * @return the number of affected rows in the database table
+     * @throws UnsupportedOperationException
+     *             if the implementation is read only.
+     */
+    public int storeRow(RowItem row) throws UnsupportedOperationException,
+            SQLException;
+
+    /**
+     * Removes the given RowItem from the database.
+     * 
+     * @param row
+     *            RowItem to be removed
+     * @return true on success
+     * @throws UnsupportedOperationException
+     * @throws SQLException
+     */
+    public boolean removeRow(RowItem row) throws UnsupportedOperationException,
+            SQLException;
+
+    /**
+     * Starts a new database transaction. Used when storing multiple changes.
+     * 
+     * Note that if a transaction is already open, it will be rolled back when a
+     * new transaction is started.
+     * 
+     * @throws SQLException
+     *             if the database access fails.
+     */
+    public void beginTransaction() throws SQLException;
+
+    /**
+     * Commits a transaction. If a transaction is not open nothing should
+     * happen.
+     * 
+     * @throws SQLException
+     *             if the database access fails.
+     */
+    public void commit() throws SQLException;
+
+    /**
+     * Rolls a transaction back. If a transaction is not open nothing should
+     * happen.
+     * 
+     * @throws SQLException
+     *             if the database access fails.
+     */
+    public void rollback() throws SQLException;
+
+    /**
+     * Returns a list of primary key column names. The list is either fetched
+     * from the database (TableQuery) or given as an argument depending on
+     * implementation.
+     * 
+     * @return
+     */
+    public List<String> getPrimaryKeyColumns();
+
+    /**
+     * Performs a query to find out whether the SQL table contains a row with
+     * the given set of primary keys.
+     * 
+     * @param keys
+     *            the primary keys
+     * @return true if the SQL table contains a row with the provided keys
+     * @throws SQLException
+     */
+    public boolean containsRowWithKey(Object... keys) throws SQLException;
+
+    /************************/
+    /** ROWID CHANGE EVENT **/
+    /************************/
+
+    /**
+     * An <code>Event</code> object specifying the old and new RowId of an added
+     * item after the addition has been successfully committed.
+     */
+    public interface RowIdChangeEvent extends Serializable {
+        /**
+         * Gets the old (temporary) RowId of the added row that raised this
+         * event.
+         * 
+         * @return old RowId
+         */
+        public RowId getOldRowId();
+
+        /**
+         * Gets the new, possibly database assigned RowId of the added row that
+         * raised this event.
+         * 
+         * @return new RowId
+         */
+        public RowId getNewRowId();
+    }
+
+    /** RowId change listener interface. */
+    public interface RowIdChangeListener extends Serializable {
+        /**
+         * Lets the listener know that a RowId has been changed.
+         * 
+         * @param event
+         */
+        public void rowIdChange(QueryDelegate.RowIdChangeEvent event);
+    }
+
+    /**
+     * The interface for adding and removing <code>RowIdChangeEvent</code>
+     * listeners. By implementing this interface a class explicitly announces
+     * that it will generate a <code>RowIdChangeEvent</code> when it performs a
+     * database commit that may change the RowId.
+     */
+    public interface RowIdChangeNotifier extends Serializable {
+        /**
+         * Adds a RowIdChangeListener for the object.
+         * 
+         * @param listener
+         *            listener to be added
+         */
+        public void addListener(QueryDelegate.RowIdChangeListener listener);
+
+        /**
+         * Removes the specified RowIdChangeListener from the object.
+         * 
+         * @param listener
+         *            listener to be removed
+         */
+        public void removeListener(QueryDelegate.RowIdChangeListener listener);
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
new file mode 100644 (file)
index 0000000..58f83c2
--- /dev/null
@@ -0,0 +1,707 @@
+package com.vaadin.data.util.sqlcontainer.query;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.Compare.Equal;
+import com.vaadin.data.util.sqlcontainer.ColumnProperty;
+import com.vaadin.data.util.sqlcontainer.OptimisticLockException;
+import com.vaadin.data.util.sqlcontainer.RowId;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+import com.vaadin.data.util.sqlcontainer.SQLUtil;
+import com.vaadin.data.util.sqlcontainer.TemporaryRowId;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+@SuppressWarnings("serial")
+public class TableQuery implements QueryDelegate,
+        QueryDelegate.RowIdChangeNotifier {
+
+    /** Table name, primary key column name(s) and version column name */
+    private String tableName;
+    private List<String> primaryKeyColumns;
+    private String versionColumn;
+
+    /** Currently set Filters and OrderBys */
+    private List<Filter> filters;
+    private List<OrderBy> orderBys;
+
+    /** SQLGenerator instance to use for generating queries */
+    private SQLGenerator sqlGenerator;
+
+    /** Fields related to Connection and Transaction handling */
+    private JDBCConnectionPool connectionPool;
+    private transient Connection activeConnection;
+    private boolean transactionOpen;
+
+    /** Row ID change listeners */
+    private LinkedList<RowIdChangeListener> rowIdChangeListeners;
+    /** Row ID change events, stored until commit() is called */
+    private final List<RowIdChangeEvent> bufferedEvents = new ArrayList<RowIdChangeEvent>();
+
+    /** Set to true to output generated SQL Queries to System.out */
+    private boolean debug = false;
+
+    /** Prevent no-parameters instantiation of TableQuery */
+    @SuppressWarnings("unused")
+    private TableQuery() {
+    }
+
+    /**
+     * Creates a new TableQuery using the given connection pool, SQL generator
+     * and table name to fetch the data from. All parameters must be non-null.
+     * 
+     * @param tableName
+     *            Name of the database table to connect to
+     * @param connectionPool
+     *            Connection pool for accessing the database
+     * @param sqlGenerator
+     *            SQL query generator implementation
+     */
+    public TableQuery(String tableName, JDBCConnectionPool connectionPool,
+            SQLGenerator sqlGenerator) {
+        if (tableName == null || tableName.trim().length() < 1
+                || connectionPool == null || sqlGenerator == null) {
+            throw new IllegalArgumentException(
+                    "All parameters must be non-null and a table name must be given.");
+        }
+        this.tableName = tableName;
+        this.sqlGenerator = sqlGenerator;
+        this.connectionPool = connectionPool;
+        fetchMetaData();
+    }
+
+    /**
+     * Creates a new TableQuery using the given connection pool and table name
+     * to fetch the data from. All parameters must be non-null. The default SQL
+     * generator will be used for queries.
+     * 
+     * @param tableName
+     *            Name of the database table to connect to
+     * @param connectionPool
+     *            Connection pool for accessing the database
+     */
+    public TableQuery(String tableName, JDBCConnectionPool connectionPool) {
+        this(tableName, connectionPool, new DefaultSQLGenerator());
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getCount()
+     */
+    public int getCount() throws SQLException {
+        debug("Fetching count...");
+        StatementHelper sh = sqlGenerator.generateSelectQuery(tableName,
+                filters, null, 0, 0, "COUNT(*)");
+        boolean shouldCloseTransaction = false;
+        if (!transactionOpen) {
+            shouldCloseTransaction = true;
+            beginTransaction();
+        }
+        ResultSet r = executeQuery(sh);
+        r.next();
+        int count = r.getInt(1);
+        r.getStatement().close();
+        r.close();
+        if (shouldCloseTransaction) {
+            commit();
+        }
+        return count;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getResults(int,
+     * int)
+     */
+    public ResultSet getResults(int offset, int pagelength) throws SQLException {
+        StatementHelper sh;
+        /*
+         * If no ordering is explicitly set, results will be ordered by the
+         * first primary key column.
+         */
+        if (orderBys == null || orderBys.isEmpty()) {
+            List<OrderBy> ob = new ArrayList<OrderBy>();
+            ob.add(new OrderBy(primaryKeyColumns.get(0), true));
+            sh = sqlGenerator.generateSelectQuery(tableName, filters, ob,
+                    offset, pagelength, null);
+        } else {
+            sh = sqlGenerator.generateSelectQuery(tableName, filters, orderBys,
+                    offset, pagelength, null);
+        }
+        return executeQuery(sh);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#
+     * implementationRespectsPagingLimits()
+     */
+    public boolean implementationRespectsPagingLimits() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin
+     * .addon.sqlcontainer.RowItem)
+     */
+    public int storeRow(RowItem row) throws UnsupportedOperationException,
+            SQLException {
+        if (row == null) {
+            throw new IllegalArgumentException("Row argument must be non-null.");
+        }
+        StatementHelper sh;
+        int result = 0;
+        if (row.getId() instanceof TemporaryRowId) {
+            setVersionColumnFlagInProperty(row);
+            sh = sqlGenerator.generateInsertQuery(tableName, row);
+            result = executeUpdateReturnKeys(sh, row);
+        } else {
+            setVersionColumnFlagInProperty(row);
+            sh = sqlGenerator.generateUpdateQuery(tableName, row);
+            result = executeUpdate(sh);
+        }
+        if (versionColumn != null && result == 0) {
+            throw new OptimisticLockException(
+                    "Someone else changed the row that was being updated.",
+                    row.getId());
+        }
+        return result;
+    }
+
+    private void setVersionColumnFlagInProperty(RowItem row) {
+        ColumnProperty versionProperty = (ColumnProperty) row
+                .getItemProperty(versionColumn);
+        if (versionProperty != null) {
+            versionProperty.setVersionColumn(true);
+        }
+    }
+
+    /**
+     * Inserts the given row in the database table immediately. Begins and
+     * commits the transaction needed. This method was added specifically to
+     * solve the problem of returning the final RowId immediately on the
+     * SQLContainer.addItem() call when auto commit mode is enabled in the
+     * SQLContainer.
+     * 
+     * @param row
+     *            RowItem to add to the database
+     * @return Final RowId of the added row
+     * @throws SQLException
+     */
+    public RowId storeRowImmediately(RowItem row) throws SQLException {
+        beginTransaction();
+        /* Set version column, if one is provided */
+        setVersionColumnFlagInProperty(row);
+        /* Generate query */
+        StatementHelper sh = sqlGenerator.generateInsertQuery(tableName, row);
+        PreparedStatement pstmt = activeConnection.prepareStatement(
+                sh.getQueryString(), primaryKeyColumns.toArray(new String[0]));
+        sh.setParameterValuesToStatement(pstmt);
+        debug("DB -> " + sh.getQueryString());
+        int result = pstmt.executeUpdate();
+        if (result > 0) {
+            /*
+             * If affected rows exist, we'll get the new RowId, commit the
+             * transaction and return the new RowId.
+             */
+            ResultSet generatedKeys = pstmt.getGeneratedKeys();
+            RowId newId = getNewRowId(row, generatedKeys);
+            generatedKeys.close();
+            pstmt.clearParameters();
+            pstmt.close();
+            commit();
+            return newId;
+        } else {
+            pstmt.clearParameters();
+            pstmt.close();
+            /* On failure return null */
+            return null;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util
+     * .List)
+     */
+    public void setFilters(List<Filter> filters)
+            throws UnsupportedOperationException {
+        if (filters == null) {
+            this.filters = null;
+            return;
+        }
+        this.filters = Collections.unmodifiableList(filters);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setOrderBy(java.util
+     * .List)
+     */
+    public void setOrderBy(List<OrderBy> orderBys)
+            throws UnsupportedOperationException {
+        if (orderBys == null) {
+            this.orderBys = null;
+            return;
+        }
+        this.orderBys = Collections.unmodifiableList(orderBys);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#beginTransaction()
+     */
+    public void beginTransaction() throws UnsupportedOperationException,
+            SQLException {
+        if (transactionOpen && activeConnection != null) {
+            throw new IllegalStateException();
+        }
+        debug("DB -> begin transaction");
+        activeConnection = connectionPool.reserveConnection();
+        activeConnection.setAutoCommit(false);
+        transactionOpen = true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#commit()
+     */
+    public void commit() throws UnsupportedOperationException, SQLException {
+        if (transactionOpen && activeConnection != null) {
+            debug("DB -> commit");
+            activeConnection.commit();
+            connectionPool.releaseConnection(activeConnection);
+        } else {
+            throw new SQLException("No active transaction");
+        }
+        transactionOpen = false;
+
+        /* Handle firing row ID change events */
+        RowIdChangeEvent[] unFiredEvents = bufferedEvents
+                .toArray(new RowIdChangeEvent[] {});
+        bufferedEvents.clear();
+        if (rowIdChangeListeners != null && !rowIdChangeListeners.isEmpty()) {
+            for (RowIdChangeListener r : rowIdChangeListeners) {
+                for (RowIdChangeEvent e : unFiredEvents) {
+                    r.rowIdChange(e);
+                }
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#rollback()
+     */
+    public void rollback() throws UnsupportedOperationException, SQLException {
+        if (transactionOpen && activeConnection != null) {
+            debug("DB -> rollback");
+            activeConnection.rollback();
+            connectionPool.releaseConnection(activeConnection);
+        } else {
+            throw new SQLException("No active transaction");
+        }
+        transactionOpen = false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#getPrimaryKeyColumns()
+     */
+    public List<String> getPrimaryKeyColumns() {
+        return Collections.unmodifiableList(primaryKeyColumns);
+    }
+
+    public String getVersionColumn() {
+        return versionColumn;
+    }
+
+    public void setVersionColumn(String column) {
+        versionColumn = column;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public SQLGenerator getSqlGenerator() {
+        return sqlGenerator;
+    }
+
+    /**
+     * Executes the given query string using either the active connection if a
+     * transaction is already open, or a new connection from this query's
+     * connection pool.
+     * 
+     * @param sh
+     *            an instance of StatementHelper, containing the query string
+     *            and parameter values.
+     * @return ResultSet of the query
+     * @throws SQLException
+     */
+    private ResultSet executeQuery(StatementHelper sh) throws SQLException {
+        Connection c = null;
+        if (transactionOpen && activeConnection != null) {
+            c = activeConnection;
+        } else {
+            throw new SQLException("No active transaction!");
+        }
+        PreparedStatement pstmt = c.prepareStatement(sh.getQueryString());
+        sh.setParameterValuesToStatement(pstmt);
+        debug("DB -> " + sh.getQueryString());
+        return pstmt.executeQuery();
+    }
+
+    /**
+     * Executes the given update query string using either the active connection
+     * if a transaction is already open, or a new connection from this query's
+     * connection pool.
+     * 
+     * @param sh
+     *            an instance of StatementHelper, containing the query string
+     *            and parameter values.
+     * @return Number of affected rows
+     * @throws SQLException
+     */
+    private int executeUpdate(StatementHelper sh) throws SQLException {
+        Connection c = null;
+        PreparedStatement pstmt = null;
+        try {
+            if (transactionOpen && activeConnection != null) {
+                c = activeConnection;
+            } else {
+                c = connectionPool.reserveConnection();
+            }
+            pstmt = c.prepareStatement(sh.getQueryString());
+            sh.setParameterValuesToStatement(pstmt);
+            debug("DB -> " + sh.getQueryString());
+            int retval = pstmt.executeUpdate();
+            return retval;
+        } finally {
+            if (pstmt != null) {
+                pstmt.clearParameters();
+                pstmt.close();
+            }
+            if (!transactionOpen) {
+                connectionPool.releaseConnection(c);
+            }
+        }
+    }
+
+    /**
+     * Executes the given update query string using either the active connection
+     * if a transaction is already open, or a new connection from this query's
+     * connection pool.
+     * 
+     * Additionally adds a new RowIdChangeEvent to the event buffer.
+     * 
+     * @param sh
+     *            an instance of StatementHelper, containing the query string
+     *            and parameter values.
+     * @param row
+     *            the row item to update
+     * @return Number of affected rows
+     * @throws SQLException
+     */
+    private int executeUpdateReturnKeys(StatementHelper sh, RowItem row)
+            throws SQLException {
+        Connection c = null;
+        PreparedStatement pstmt = null;
+        ResultSet genKeys = null;
+        try {
+            if (transactionOpen && activeConnection != null) {
+                c = activeConnection;
+            } else {
+                c = connectionPool.reserveConnection();
+            }
+            pstmt = c.prepareStatement(sh.getQueryString(),
+                    primaryKeyColumns.toArray(new String[0]));
+            sh.setParameterValuesToStatement(pstmt);
+            debug("DB -> " + sh.getQueryString());
+            int result = pstmt.executeUpdate();
+            genKeys = pstmt.getGeneratedKeys();
+            RowId newId = getNewRowId(row, genKeys);
+            bufferedEvents.add(new RowIdChangeEvent(row.getId(), newId));
+            return result;
+        } finally {
+            if (genKeys != null) {
+                genKeys.close();
+            }
+            if (pstmt != null) {
+                pstmt.clearParameters();
+                pstmt.close();
+            }
+            if (!transactionOpen) {
+                connectionPool.releaseConnection(c);
+            }
+        }
+    }
+
+    /**
+     * Fetches name(s) of primary key column(s) from DB metadata.
+     * 
+     * Also tries to get the escape string to be used in search strings.
+     */
+    private void fetchMetaData() {
+        Connection c = null;
+        try {
+            c = connectionPool.reserveConnection();
+            DatabaseMetaData dbmd = c.getMetaData();
+            if (dbmd != null) {
+                tableName = SQLUtil.escapeSQL(tableName);
+                ResultSet tables = dbmd.getTables(null, null, tableName, null);
+                if (!tables.next()) {
+                    tables = dbmd.getTables(null, null,
+                            tableName.toUpperCase(), null);
+                    if (!tables.next()) {
+                        throw new IllegalArgumentException(
+                                "Table with the name \""
+                                        + tableName
+                                        + "\" was not found. Check your database contents.");
+                    } else {
+                        tableName = tableName.toUpperCase();
+                    }
+                }
+                tables.close();
+                ResultSet rs = dbmd.getPrimaryKeys(null, null, tableName);
+                List<String> names = new ArrayList<String>();
+                while (rs.next()) {
+                    names.add(rs.getString("COLUMN_NAME"));
+                }
+                rs.close();
+                if (!names.isEmpty()) {
+                    primaryKeyColumns = names;
+                }
+                if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
+                    throw new IllegalArgumentException(
+                            "Primary key constraints have not been defined for the table \""
+                                    + tableName
+                                    + "\". Use FreeFormQuery to access this table.");
+                }
+                for (String colName : primaryKeyColumns) {
+                    if (colName.equalsIgnoreCase("rownum")) {
+                        if (getSqlGenerator() instanceof MSSQLGenerator
+                                || getSqlGenerator() instanceof MSSQLGenerator) {
+                            throw new IllegalArgumentException(
+                                    "When using Oracle or MSSQL, a primary key column"
+                                            + " named \'rownum\' is not allowed!");
+                        }
+                    }
+                }
+            }
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        } finally {
+            connectionPool.releaseConnection(c);
+        }
+    }
+
+    private RowId getNewRowId(RowItem row, ResultSet genKeys) {
+        try {
+            /* Fetch primary key values and generate a map out of them. */
+            Map<String, Object> values = new HashMap<String, Object>();
+            ResultSetMetaData rsmd = genKeys.getMetaData();
+            int colCount = rsmd.getColumnCount();
+            if (genKeys.next()) {
+                for (int i = 1; i <= colCount; i++) {
+                    values.put(rsmd.getColumnName(i), genKeys.getObject(i));
+                }
+            }
+            /* Generate new RowId */
+            List<Object> newRowId = new ArrayList<Object>();
+            if (values.size() == 1) {
+                if (primaryKeyColumns.size() == 1) {
+                    newRowId.add(values.get(values.keySet().iterator().next()));
+                } else {
+                    for (String s : primaryKeyColumns) {
+                        if (!((ColumnProperty) row.getItemProperty(s))
+                                .isReadOnlyChangeAllowed()) {
+                            newRowId.add(values.get(values.keySet().iterator()
+                                    .next()));
+                        } else {
+                            newRowId.add(values.get(s));
+                        }
+                    }
+                }
+            } else {
+                for (String s : primaryKeyColumns) {
+                    newRowId.add(values.get(s));
+                }
+            }
+            return new RowId(newRowId.toArray());
+        } catch (Exception e) {
+            debug("Failed to fetch key values on insert: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#removeRow(com.vaadin
+     * .addon.sqlcontainer.RowItem)
+     */
+    public boolean removeRow(RowItem row) throws UnsupportedOperationException,
+            SQLException {
+        debug("Removing row with id: " + row.getId().getId()[0].toString());
+        if (executeUpdate(sqlGenerator.generateDeleteQuery(getTableName(),
+                primaryKeyColumns, versionColumn, row)) == 1) {
+            return true;
+        }
+        if (versionColumn != null) {
+            throw new OptimisticLockException(
+                    "Someone else changed the row that was being deleted.",
+                    row.getId());
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.vaadin.addon.sqlcontainer.query.QueryDelegate#containsRowWithKey(
+     * java.lang.Object[])
+     */
+    public boolean containsRowWithKey(Object... keys) throws SQLException {
+        ArrayList<Filter> filtersAndKeys = new ArrayList<Filter>();
+        if (filters != null) {
+            filtersAndKeys.addAll(filters);
+        }
+        int ix = 0;
+        for (String colName : primaryKeyColumns) {
+            filtersAndKeys.add(new Equal(colName, keys[ix]));
+            ix++;
+        }
+        StatementHelper sh = sqlGenerator.generateSelectQuery(tableName,
+                filtersAndKeys, orderBys, 0, 0, "*");
+
+        boolean shouldCloseTransaction = false;
+        if (!transactionOpen) {
+            shouldCloseTransaction = true;
+            beginTransaction();
+        }
+        ResultSet rs = null;
+        try {
+            rs = executeQuery(sh);
+            boolean contains = rs.next();
+            return contains;
+        } finally {
+            if (rs != null) {
+                if (rs.getStatement() != null) {
+                    rs.getStatement().close();
+                }
+                rs.close();
+            }
+            if (shouldCloseTransaction) {
+                commit();
+            }
+        }
+    }
+
+    /**
+     * Output a debug message
+     * 
+     * @param message
+     */
+    private void debug(String message) {
+        if (debug) {
+            System.out.println(message);
+        }
+    }
+
+    /**
+     * Enable or disable debug mode.
+     * 
+     * @param debug
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Custom writeObject to call rollback() if object is serialized.
+     */
+    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+        try {
+            rollback();
+        } catch (SQLException ignored) {
+        }
+        out.defaultWriteObject();
+    }
+
+    /**
+     * Simple RowIdChangeEvent implementation.
+     */
+    public class RowIdChangeEvent extends EventObject implements
+            QueryDelegate.RowIdChangeEvent {
+        private final RowId oldId;
+        private final RowId newId;
+
+        private RowIdChangeEvent(RowId oldId, RowId newId) {
+            super(oldId);
+            this.oldId = oldId;
+            this.newId = newId;
+        }
+
+        public RowId getNewRowId() {
+            return newId;
+        }
+
+        public RowId getOldRowId() {
+            return oldId;
+        }
+    }
+
+    /**
+     * Adds RowIdChangeListener to this query
+     */
+    public void addListener(RowIdChangeListener listener) {
+        if (rowIdChangeListeners == null) {
+            rowIdChangeListeners = new LinkedList<QueryDelegate.RowIdChangeListener>();
+        }
+        rowIdChangeListeners.add(listener);
+    }
+
+    /**
+     * Removes the given RowIdChangeListener from this query
+     */
+    public void removeListener(RowIdChangeListener listener) {
+        if (rowIdChangeListeners != null) {
+            rowIdChangeListeners.remove(listener);
+        }
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java
new file mode 100644 (file)
index 0000000..08881b0
--- /dev/null
@@ -0,0 +1,308 @@
+package com.vaadin.data.util.sqlcontainer.query.generator;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.ColumnProperty;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+import com.vaadin.data.util.sqlcontainer.SQLUtil;
+import com.vaadin.data.util.sqlcontainer.TemporaryRowId;
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.StringDecorator;
+
+/**
+ * Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL.
+ * 
+ * @author Jonatan Kronqvist / IT Mill Ltd
+ */
+@SuppressWarnings("serial")
+public class DefaultSQLGenerator implements SQLGenerator {
+
+    public DefaultSQLGenerator() {
+
+    }
+
+    /**
+     * Construct a DefaultSQLGenerator with the specified identifiers for start
+     * and end of quoted strings. The identifiers may be different depending on
+     * the database engine and it's settings.
+     * 
+     * @param quoteStart
+     *            the identifier (character) denoting the start of a quoted
+     *            string
+     * @param quoteEnd
+     *            the identifier (character) denoting the end of a quoted string
+     */
+    public DefaultSQLGenerator(String quoteStart, String quoteEnd) {
+        QueryBuilder.setStringDecorator(new StringDecorator(quoteStart,
+                quoteEnd));
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
+     * generateSelectQuery(java.lang.String, java.util.List, java.util.List,
+     * int, int, java.lang.String)
+     */
+    public StatementHelper generateSelectQuery(String tableName,
+            List<Filter> filters, List<OrderBy> orderBys, int offset,
+            int pagelength, String toSelect) {
+        if (tableName == null || tableName.trim().equals("")) {
+            throw new IllegalArgumentException("Table name must be given.");
+        }
+        toSelect = toSelect == null ? "*" : toSelect;
+        StatementHelper sh = new StatementHelper();
+        StringBuffer query = new StringBuffer();
+        query.append("SELECT " + toSelect + " FROM ").append(
+                SQLUtil.escapeSQL(tableName));
+        if (filters != null) {
+            query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
+        }
+        if (orderBys != null) {
+            for (OrderBy o : orderBys) {
+                generateOrderBy(query, o, orderBys.indexOf(o) == 0);
+            }
+        }
+        if (pagelength != 0) {
+            generateLimits(query, offset, pagelength);
+        }
+        sh.setQueryString(query.toString());
+        return sh;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
+     * generateUpdateQuery(java.lang.String,
+     * com.vaadin.addon.sqlcontainer.RowItem)
+     */
+    public StatementHelper generateUpdateQuery(String tableName, RowItem item) {
+        if (tableName == null || tableName.trim().equals("")) {
+            throw new IllegalArgumentException("Table name must be given.");
+        }
+        if (item == null) {
+            throw new IllegalArgumentException("Updated item must be given.");
+        }
+        StatementHelper sh = new StatementHelper();
+        StringBuffer query = new StringBuffer();
+        query.append("UPDATE ").append(tableName).append(" SET");
+
+        /* Generate column<->value and rowidentifiers map */
+        Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
+        Map<String, Object> rowIdentifiers = generateRowIdentifiers(item);
+        /* Generate columns and values to update */
+        boolean first = true;
+        for (String column : columnToValueMap.keySet()) {
+            if (first) {
+                query.append(" " + QueryBuilder.quote(column) + " = ?");
+            } else {
+                query.append(", " + QueryBuilder.quote(column) + " = ?");
+            }
+            sh.addParameterValue(columnToValueMap.get(column), item
+                    .getItemProperty(column).getType());
+            first = false;
+        }
+        /* Generate identifiers for the row to be updated */
+        first = true;
+        for (String column : rowIdentifiers.keySet()) {
+            if (first) {
+                query.append(" WHERE " + QueryBuilder.quote(column) + " = ?");
+            } else {
+                query.append(" AND " + QueryBuilder.quote(column) + " = ?");
+            }
+            sh.addParameterValue(rowIdentifiers.get(column), item
+                    .getItemProperty(column).getType());
+            first = false;
+        }
+        sh.setQueryString(query.toString());
+        return sh;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
+     * generateInsertQuery(java.lang.String,
+     * com.vaadin.addon.sqlcontainer.RowItem)
+     */
+    public StatementHelper generateInsertQuery(String tableName, RowItem item) {
+        if (tableName == null || tableName.trim().equals("")) {
+            throw new IllegalArgumentException("Table name must be given.");
+        }
+        if (item == null) {
+            throw new IllegalArgumentException("New item must be given.");
+        }
+        if (!(item.getId() instanceof TemporaryRowId)) {
+            throw new IllegalArgumentException(
+                    "Cannot generate an insert query for item already in database.");
+        }
+        StatementHelper sh = new StatementHelper();
+        StringBuffer query = new StringBuffer();
+        query.append("INSERT INTO ").append(tableName).append(" (");
+
+        /* Generate column<->value map */
+        Map<String, Object> columnToValueMap = generateColumnToValueMap(item);
+        /* Generate column names for insert query */
+        boolean first = true;
+        for (String column : columnToValueMap.keySet()) {
+            if (!first) {
+                query.append(", ");
+            }
+            query.append(QueryBuilder.quote(column));
+            first = false;
+        }
+
+        /* Generate values for insert query */
+        query.append(") VALUES (");
+        first = true;
+        for (String column : columnToValueMap.keySet()) {
+            if (!first) {
+                query.append(", ");
+            }
+            query.append("?");
+            sh.addParameterValue(columnToValueMap.get(column), item
+                    .getItemProperty(column).getType());
+            first = false;
+        }
+        query.append(")");
+        sh.setQueryString(query.toString());
+        return sh;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator#
+     * generateDeleteQuery(java.lang.String,
+     * com.vaadin.addon.sqlcontainer.RowItem)
+     */
+    public StatementHelper generateDeleteQuery(String tableName,
+            List<String> primaryKeyColumns, String versionColumn, RowItem item) {
+        if (tableName == null || tableName.trim().equals("")) {
+            throw new IllegalArgumentException("Table name must be given.");
+        }
+        if (item == null) {
+            throw new IllegalArgumentException(
+                    "Item to be deleted must be given.");
+        }
+        if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Valid keyColumnNames must be provided.");
+        }
+        StatementHelper sh = new StatementHelper();
+        StringBuffer query = new StringBuffer();
+        query.append("DELETE FROM ").append(tableName).append(" WHERE ");
+        int count = 1;
+        for (String keyColName : primaryKeyColumns) {
+            if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
+                    && keyColName.equalsIgnoreCase("rownum")) {
+                count++;
+                continue;
+            }
+            if (count > 1) {
+                query.append(" AND ");
+            }
+            if (item.getItemProperty(keyColName).getValue() != null) {
+                query.append(QueryBuilder.quote(keyColName) + " = ?");
+                sh.addParameterValue(item.getItemProperty(keyColName)
+                        .getValue(), item.getItemProperty(keyColName).getType());
+            }
+            count++;
+        }
+        if (versionColumn != null) {
+            query.append(String.format(" AND %s = ?",
+                    QueryBuilder.quote(versionColumn)));
+            sh.addParameterValue(
+                    item.getItemProperty(versionColumn).getValue(), item
+                            .getItemProperty(versionColumn).getType());
+        }
+
+        sh.setQueryString(query.toString());
+        return sh;
+    }
+
+    /**
+     * Generates sorting rules as an ORDER BY -clause
+     * 
+     * @param sb
+     *            StringBuffer to which the clause is appended.
+     * @param o
+     *            OrderBy object to be added into the sb.
+     * @param firstOrderBy
+     *            If true, this is the first OrderBy.
+     * @return
+     */
+    protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o,
+            boolean firstOrderBy) {
+        if (firstOrderBy) {
+            sb.append(" ORDER BY ");
+        } else {
+            sb.append(", ");
+        }
+        sb.append(QueryBuilder.quote(o.getColumn()));
+        if (o.isAscending()) {
+            sb.append(" ASC");
+        } else {
+            sb.append(" DESC");
+        }
+        return sb;
+    }
+
+    /**
+     * Generates the LIMIT and OFFSET clause.
+     * 
+     * @param sb
+     *            StringBuffer to which the clause is appended.
+     * @param offset
+     *            Value for offset.
+     * @param pagelength
+     *            Value for pagelength.
+     * @return StringBuffer with LIMIT and OFFSET clause added.
+     */
+    protected StringBuffer generateLimits(StringBuffer sb, int offset,
+            int pagelength) {
+        sb.append(" LIMIT ").append(pagelength).append(" OFFSET ")
+                .append(offset);
+        return sb;
+    }
+
+    protected Map<String, Object> generateColumnToValueMap(RowItem item) {
+        Map<String, Object> columnToValueMap = new HashMap<String, Object>();
+        for (Object id : item.getItemPropertyIds()) {
+            ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
+            /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
+            if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
+                    && cp.getPropertyId().equalsIgnoreCase("rownum")) {
+                continue;
+            }
+            Object value = cp.getValue() == null ? null : cp.getValue();
+            /* Only include properties whose read-only status can be altered */
+            if (cp.isReadOnlyChangeAllowed() && !cp.isVersionColumn()) {
+                columnToValueMap.put(cp.getPropertyId(), value);
+            }
+        }
+        return columnToValueMap;
+    }
+
+    protected Map<String, Object> generateRowIdentifiers(RowItem item) {
+        Map<String, Object> rowIdentifiers = new HashMap<String, Object>();
+        for (Object id : item.getItemPropertyIds()) {
+            ColumnProperty cp = (ColumnProperty) item.getItemProperty(id);
+            /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */
+            if ((this instanceof MSSQLGenerator || this instanceof OracleGenerator)
+                    && cp.getPropertyId().equalsIgnoreCase("rownum")) {
+                continue;
+            }
+            Object value = cp.getValue() == null ? null : cp.getValue();
+            if (!cp.isReadOnlyChangeAllowed() || cp.isVersionColumn()) {
+                rowIdentifiers.put(cp.getPropertyId(), value);
+            }
+        }
+        return rowIdentifiers;
+    }
+}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/MSSQLGenerator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/MSSQLGenerator.java
new file mode 100644 (file)
index 0000000..98f474a
--- /dev/null
@@ -0,0 +1,101 @@
+package com.vaadin.data.util.sqlcontainer.query.generator;
+
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
+
+@SuppressWarnings("serial")
+public class MSSQLGenerator extends DefaultSQLGenerator {
+
+    public MSSQLGenerator() {
+
+    }
+
+    /**
+     * Construct a MSSQLGenerator with the specified identifiers for start and
+     * end of quoted strings. The identifiers may be different depending on the
+     * database engine and it's settings.
+     * 
+     * @param quoteStart
+     *            the identifier (character) denoting the start of a quoted
+     *            string
+     * @param quoteEnd
+     *            the identifier (character) denoting the end of a quoted string
+     */
+    public MSSQLGenerator(String quoteStart, String quoteEnd) {
+        super(quoteStart, quoteEnd);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator#
+     * generateSelectQuery(java.lang.String, java.util.List,
+     * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int,
+     * int, java.lang.String)
+     */
+    @Override
+    public StatementHelper generateSelectQuery(String tableName,
+            List<Filter> filters, List<OrderBy> orderBys, int offset,
+            int pagelength, String toSelect) {
+        if (tableName == null || tableName.trim().equals("")) {
+            throw new IllegalArgumentException("Table name must be given.");
+        }
+        /* Adjust offset and page length parameters to match "row numbers" */
+        offset = pagelength > 1 ? ++offset : offset;
+        pagelength = pagelength > 1 ? --pagelength : pagelength;
+        toSelect = toSelect == null ? "*" : toSelect;
+        StatementHelper sh = new StatementHelper();
+        StringBuffer query = new StringBuffer();
+
+        /* Row count request is handled here */
+        if ("COUNT(*)".equalsIgnoreCase(toSelect)) {
+            query.append(String.format(
+                    "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s",
+                    QueryBuilder.quote("rowcount"), tableName));
+            if (filters != null && !filters.isEmpty()) {
+                query.append(QueryBuilder.getWhereStringForFilters(
+                        filters, sh));
+            }
+            query.append(") AS t");
+            sh.setQueryString(query.toString());
+            return sh;
+        }
+
+        /* SELECT without row number constraints */
+        if (offset == 0 && pagelength == 0) {
+            query.append("SELECT ").append(toSelect).append(" FROM ")
+                    .append(tableName);
+            if (filters != null) {
+                query.append(QueryBuilder.getWhereStringForFilters(
+                        filters, sh));
+            }
+            if (orderBys != null) {
+                for (OrderBy o : orderBys) {
+                    generateOrderBy(query, o, orderBys.indexOf(o) == 0);
+                }
+            }
+            sh.setQueryString(query.toString());
+            return sh;
+        }
+
+        /* Remaining SELECT cases are handled here */
+        query.append("SELECT * FROM (SELECT row_number() OVER (");
+        if (orderBys != null) {
+            for (OrderBy o : orderBys) {
+                generateOrderBy(query, o, orderBys.indexOf(o) == 0);
+            }
+        }
+        query.append(") AS rownum, " + toSelect + " FROM ").append(tableName);
+        if (filters != null) {
+            query.append(QueryBuilder.getWhereStringForFilters(
+                    filters, sh));
+        }
+        query.append(") AS a WHERE a.rownum BETWEEN ").append(offset)
+                .append(" AND ").append(Integer.toString(offset + pagelength));
+        sh.setQueryString(query.toString());
+        return sh;
+    }
+}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/OracleGenerator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/OracleGenerator.java
new file mode 100644 (file)
index 0000000..223211b
--- /dev/null
@@ -0,0 +1,99 @@
+package com.vaadin.data.util.sqlcontainer.query.generator;
+
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
+
+@SuppressWarnings("serial")
+public class OracleGenerator extends DefaultSQLGenerator {
+
+    public OracleGenerator() {
+
+    }
+
+    /**
+     * Construct an OracleSQLGenerator with the specified identifiers for start
+     * and end of quoted strings. The identifiers may be different depending on
+     * the database engine and it's settings.
+     * 
+     * @param quoteStart
+     *            the identifier (character) denoting the start of a quoted
+     *            string
+     * @param quoteEnd
+     *            the identifier (character) denoting the end of a quoted string
+     */
+    public OracleGenerator(String quoteStart, String quoteEnd) {
+        super(quoteStart, quoteEnd);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator#
+     * generateSelectQuery(java.lang.String, java.util.List,
+     * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int,
+     * int, java.lang.String)
+     */
+    @Override
+    public StatementHelper generateSelectQuery(String tableName,
+            List<Filter> filters, List<OrderBy> orderBys, int offset,
+            int pagelength, String toSelect) {
+        if (tableName == null || tableName.trim().equals("")) {
+            throw new IllegalArgumentException("Table name must be given.");
+        }
+        /* Adjust offset and page length parameters to match "row numbers" */
+        offset = pagelength > 1 ? ++offset : offset;
+        pagelength = pagelength > 1 ? --pagelength : pagelength;
+        toSelect = toSelect == null ? "*" : toSelect;
+        StatementHelper sh = new StatementHelper();
+        StringBuffer query = new StringBuffer();
+
+        /* Row count request is handled here */
+        if ("COUNT(*)".equalsIgnoreCase(toSelect)) {
+            query.append(String.format(
+                    "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s",
+                    QueryBuilder.quote("rowcount"), tableName));
+            if (filters != null && !filters.isEmpty()) {
+                query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
+            }
+            query.append(")");
+            sh.setQueryString(query.toString());
+            return sh;
+        }
+
+        /* SELECT without row number constraints */
+        if (offset == 0 && pagelength == 0) {
+            query.append("SELECT ").append(toSelect).append(" FROM ")
+                    .append(tableName);
+            if (filters != null) {
+                query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
+            }
+            if (orderBys != null) {
+                for (OrderBy o : orderBys) {
+                    generateOrderBy(query, o, orderBys.indexOf(o) == 0);
+                }
+            }
+            sh.setQueryString(query.toString());
+            return sh;
+        }
+
+        /* Remaining SELECT cases are handled here */
+        query.append(String
+                .format("SELECT * FROM (SELECT x.*, ROWNUM AS %s FROM (SELECT %s FROM %s",
+                        QueryBuilder.quote("rownum"), toSelect, tableName));
+        if (filters != null) {
+            query.append(QueryBuilder.getWhereStringForFilters(filters, sh));
+        }
+        if (orderBys != null) {
+            for (OrderBy o : orderBys) {
+                generateOrderBy(query, o, orderBys.indexOf(o) == 0);
+            }
+        }
+        query.append(String.format(") x) WHERE %s BETWEEN %d AND %d",
+                QueryBuilder.quote("rownum"), offset, offset + pagelength));
+        sh.setQueryString(query.toString());
+        return sh;
+    }
+}
\ No newline at end of file
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/SQLGenerator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/SQLGenerator.java
new file mode 100644 (file)
index 0000000..97c1041
--- /dev/null
@@ -0,0 +1,85 @@
+package com.vaadin.data.util.sqlcontainer.query.generator;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;
+
+/**
+ * The SQLGenerator interface is meant to be implemented for each different SQL
+ * syntax that is to be supported. By default there are implementations for
+ * HSQLDB, MySQL, PostgreSQL, MSSQL and Oracle syntaxes.
+ * 
+ * @author Jonatan Kronqvist / IT Mill Ltd
+ */
+public interface SQLGenerator extends Serializable {
+    /**
+     * Generates a SELECT query with the provided parameters. Uses default
+     * filtering mode (INCLUSIVE).
+     * 
+     * @param tableName
+     *            Name of the table queried
+     * @param filters
+     *            The filters, converted into a WHERE clause
+     * @param orderBys
+     *            The the ordering conditions, converted into an ORDER BY clause
+     * @param offset
+     *            The offset of the first row to be included
+     * @param pagelength
+     *            The number of rows to be returned when the query executes
+     * @param toSelect
+     *            String containing what to select, e.g. "*", "COUNT(*)"
+     * @return StatementHelper instance containing the query string for a
+     *         PreparedStatement and the values required for the parameters
+     */
+    public StatementHelper generateSelectQuery(String tableName,
+            List<Filter> filters, List<OrderBy> orderBys, int offset,
+            int pagelength, String toSelect);
+
+    /**
+     * Generates an UPDATE query with the provided parameters.
+     * 
+     * @param tableName
+     *            Name of the table queried
+     * @param item
+     *            RowItem containing the updated values update.
+     * @return StatementHelper instance containing the query string for a
+     *         PreparedStatement and the values required for the parameters
+     */
+    public StatementHelper generateUpdateQuery(String tableName, RowItem item);
+
+    /**
+     * Generates an INSERT query for inserting a new row with the provided
+     * values.
+     * 
+     * @param tableName
+     *            Name of the table queried
+     * @param item
+     *            New RowItem to be inserted into the database.
+     * @return StatementHelper instance containing the query string for a
+     *         PreparedStatement and the values required for the parameters
+     */
+    public StatementHelper generateInsertQuery(String tableName, RowItem item);
+
+    /**
+     * Generates a DELETE query for deleting data related to the given RowItem
+     * from the database.
+     * 
+     * @param tableName
+     *            Name of the table queried
+     * @param primaryKeyColumns
+     *            the names of the columns holding the primary key. Usually just
+     *            one column, but might be several.
+     * @param versionColumn
+     *            the column containing the version number of the row, null if
+     *            versioning (optimistic locking) not enabled.
+     * @param item
+     *            Item to be deleted from the database
+     * @return StatementHelper instance containing the query string for a
+     *         PreparedStatement and the values required for the parameters
+     */
+    public StatementHelper generateDeleteQuery(String tableName,
+            List<String> primaryKeyColumns, String versionColumn, RowItem item);
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/StatementHelper.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/StatementHelper.java
new file mode 100644 (file)
index 0000000..90d2983
--- /dev/null
@@ -0,0 +1,131 @@
+package com.vaadin.data.util.sqlcontainer.query.generator;\r
+\r
+import java.math.BigDecimal;\r
+import java.sql.Date;\r
+import java.sql.PreparedStatement;\r
+import java.sql.SQLException;\r
+import java.sql.Time;\r
+import java.sql.Timestamp;\r
+import java.sql.Types;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+/**\r
+ * StatementHelper is a simple helper class that assists TableQuery and the\r
+ * query generators in filling a PreparedStatement. The actual statement is\r
+ * generated by the query generator methods, but the resulting statement and all\r
+ * the parameter values are stored in an instance of StatementHelper.\r
+ * \r
+ * This class will also fill the values with correct setters into the\r
+ * PreparedStatement on request.\r
+ */\r
+public class StatementHelper {\r
+\r
+    private String queryString;\r
+\r
+    private List<Object> parameters = new ArrayList<Object>();\r
+    private Map<Integer, Class<?>> dataTypes = new HashMap<Integer, Class<?>>();\r
+\r
+    public StatementHelper() {\r
+    }\r
+\r
+    public void setQueryString(String queryString) {\r
+        this.queryString = queryString;\r
+    }\r
+\r
+    public String getQueryString() {\r
+        return queryString;\r
+    }\r
+\r
+    public void addParameterValue(Object parameter) {\r
+        if (parameter != null) {\r
+            parameters.add(parameter);\r
+            dataTypes.put(parameters.size() - 1, parameter.getClass());\r
+        }\r
+    }\r
+\r
+    public void addParameterValue(Object parameter, Class<?> type) {\r
+        parameters.add(parameter);\r
+        dataTypes.put(parameters.size() - 1, type);\r
+    }\r
+\r
+    public void setParameterValuesToStatement(PreparedStatement pstmt)\r
+            throws SQLException {\r
+        for (int i = 0; i < parameters.size(); i++) {\r
+            if (parameters.get(i) == null) {\r
+                handleNullValue(i, pstmt);\r
+            } else {\r
+                pstmt.setObject(i + 1, parameters.get(i));\r
+            }\r
+        }\r
+\r
+        /*\r
+         * The following list contains the data types supported by\r
+         * PreparedStatement but not supported by SQLContainer:\r
+         * \r
+         * [The list is provided as PreparedStatement method signatures]\r
+         * \r
+         * setNCharacterStream(int parameterIndex, Reader value)\r
+         * \r
+         * setNClob(int parameterIndex, NClob value)\r
+         * \r
+         * setNString(int parameterIndex, String value)\r
+         * \r
+         * setRef(int parameterIndex, Ref x)\r
+         * \r
+         * setRowId(int parameterIndex, RowId x)\r
+         * \r
+         * setSQLXML(int parameterIndex, SQLXML xmlObject)\r
+         * \r
+         * setBytes(int parameterIndex, byte[] x)\r
+         * \r
+         * setCharacterStream(int parameterIndex, Reader reader)\r
+         * \r
+         * setClob(int parameterIndex, Clob x)\r
+         * \r
+         * setURL(int parameterIndex, URL x)\r
+         * \r
+         * setArray(int parameterIndex, Array x)\r
+         * \r
+         * setAsciiStream(int parameterIndex, InputStream x)\r
+         * \r
+         * setBinaryStream(int parameterIndex, InputStream x)\r
+         * \r
+         * setBlob(int parameterIndex, Blob x)\r
+         */\r
+    }\r
+\r
+    private void handleNullValue(int i, PreparedStatement pstmt)\r
+            throws SQLException {\r
+        if (BigDecimal.class.equals(dataTypes.get(i))) {\r
+            pstmt.setBigDecimal(i + 1, null);\r
+        } else if (Boolean.class.equals(dataTypes.get(i))) {\r
+            pstmt.setNull(i + 1, Types.BOOLEAN);\r
+        } else if (Byte.class.equals(dataTypes.get(i))) {\r
+            pstmt.setNull(i + 1, Types.SMALLINT);\r
+        } else if (Date.class.equals(dataTypes.get(i))) {\r
+            pstmt.setDate(i + 1, null);\r
+        } else if (Double.class.equals(dataTypes.get(i))) {\r
+            pstmt.setNull(i + 1, Types.DOUBLE);\r
+        } else if (Float.class.equals(dataTypes.get(i))) {\r
+            pstmt.setNull(i + 1, Types.FLOAT);\r
+        } else if (Integer.class.equals(dataTypes.get(i))) {\r
+            pstmt.setNull(i + 1, Types.INTEGER);\r
+        } else if (Long.class.equals(dataTypes.get(i))) {\r
+            pstmt.setNull(i + 1, Types.BIGINT);\r
+        } else if (Short.class.equals(dataTypes.get(i))) {\r
+            pstmt.setNull(i + 1, Types.SMALLINT);\r
+        } else if (String.class.equals(dataTypes.get(i))) {\r
+            pstmt.setString(i + 1, null);\r
+        } else if (Time.class.equals(dataTypes.get(i))) {\r
+            pstmt.setTime(i + 1, null);\r
+        } else if (Timestamp.class.equals(dataTypes.get(i))) {\r
+            pstmt.setTimestamp(i + 1, null);\r
+        } else {\r
+            throw new SQLException("Data type not supported by SQLContainer: "\r
+                    + parameters.get(i).getClass().toString());\r
+        }\r
+    }\r
+}\r
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/AndTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/AndTranslator.java
new file mode 100644 (file)
index 0000000..edc3895
--- /dev/null
@@ -0,0 +1,18 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.And;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class AndTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof And;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        return QueryBuilder.group(QueryBuilder
+                .getJoinedFilterString(((And) filter).getFilters(), "AND", sh));
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java
new file mode 100644 (file)
index 0000000..55e7ab7
--- /dev/null
@@ -0,0 +1,21 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.Between;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class BetweenTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof Between;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        Between between = (Between) filter;
+        sh.addParameterValue(between.getStartValue());
+        sh.addParameterValue(between.getEndValue());
+        return QueryBuilder.quote(between.getPropertyId())
+                + " BETWEEN ? AND ?";
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java
new file mode 100644 (file)
index 0000000..07cdb11
--- /dev/null
@@ -0,0 +1,33 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.Compare;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class CompareTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof Compare;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        Compare compare = (Compare) filter;
+        sh.addParameterValue(compare.getValue());
+        String prop = QueryBuilder.quote(compare.getPropertyId());
+        switch (compare.getOperation()) {
+        case EQUAL:
+            return prop + " = ?";
+        case GREATER:
+            return prop + " > ?";
+        case GREATER_OR_EQUAL:
+            return prop + " >= ?";
+        case LESS:
+            return prop + " < ?";
+        case LESS_OR_EQUAL:
+            return prop + " <= ?";
+        default:
+            return "";
+        }
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java
new file mode 100644 (file)
index 0000000..be649df
--- /dev/null
@@ -0,0 +1,11 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public interface FilterTranslator {
+    public boolean translatesFilter(Filter filter);
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh);
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java
new file mode 100644 (file)
index 0000000..0a5392c
--- /dev/null
@@ -0,0 +1,17 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.IsNull;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class IsNullTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof IsNull;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        IsNull in = (IsNull) filter;
+        return QueryBuilder.quote(in.getPropertyId()) + " IS NULL";
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java
new file mode 100644 (file)
index 0000000..7146ff6
--- /dev/null
@@ -0,0 +1,27 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.Like;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class LikeTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof Like;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        Like like = (Like) filter;
+        if (like.isCaseSensitive()) {
+            sh.addParameterValue(like.getValue());
+            return QueryBuilder.quote(like.getPropertyId())
+                    + " LIKE ?";
+        } else {
+            sh.addParameterValue(like.getValue().toUpperCase());
+            return "UPPER("
+                    + QueryBuilder.quote(like.getPropertyId())
+                    + ") LIKE ?";
+        }
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/NotTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/NotTranslator.java
new file mode 100644 (file)
index 0000000..def6e54
--- /dev/null
@@ -0,0 +1,26 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.IsNull;
+import com.vaadin.data.util.filter.Not;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class NotTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof Not;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        Not not = (Not) filter;
+        if (not.getFilter() instanceof IsNull) {
+            IsNull in = (IsNull) not.getFilter();
+            return QueryBuilder.quote(in.getPropertyId())
+                    + " IS NOT NULL";
+        }
+        return "NOT "
+                + QueryBuilder.getWhereStringForFilter(
+                        not.getFilter(), sh);
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/OrTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/OrTranslator.java
new file mode 100644 (file)
index 0000000..880e711
--- /dev/null
@@ -0,0 +1,18 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.Or;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class OrTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof Or;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        return QueryBuilder.group(QueryBuilder
+                .getJoinedFilterString(((Or) filter).getFilters(), "OR", sh));
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java
new file mode 100644 (file)
index 0000000..7649671
--- /dev/null
@@ -0,0 +1,94 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class QueryBuilder {
+
+    private static ArrayList<FilterTranslator> filterTranslators = new ArrayList<FilterTranslator>();
+    private static StringDecorator stringDecorator = new StringDecorator("\"",
+            "\"");
+
+    static {
+        /* Register all default filter translators */
+        addFilterTranslator(new AndTranslator());
+        addFilterTranslator(new OrTranslator());
+        addFilterTranslator(new LikeTranslator());
+        addFilterTranslator(new BetweenTranslator());
+        addFilterTranslator(new CompareTranslator());
+        addFilterTranslator(new NotTranslator());
+        addFilterTranslator(new IsNullTranslator());
+        addFilterTranslator(new SimpleStringTranslator());
+    }
+
+    public synchronized static void addFilterTranslator(
+            FilterTranslator translator) {
+        filterTranslators.add(translator);
+    }
+
+    /**
+     * Allows specification of a custom ColumnQuoter instance that handles
+     * quoting of column names for the current DB dialect.
+     * 
+     * @param decorator
+     *            the ColumnQuoter instance to use.
+     */
+    public static void setStringDecorator(StringDecorator decorator) {
+        stringDecorator = decorator;
+    }
+
+    public static String quote(Object str) {
+        return stringDecorator.quote(str);
+    }
+
+    public static String group(String str) {
+        return stringDecorator.group(str);
+    }
+
+    /**
+     * Constructs and returns a string representing the filter that can be used
+     * in a WHERE clause.
+     * 
+     * @param filter
+     *            the filter to translate
+     * @param sh
+     *            the statement helper to update with the value(s) of the filter
+     * @return a string representing the filter.
+     */
+    public synchronized static String getWhereStringForFilter(Filter filter,
+            StatementHelper sh) {
+        for (FilterTranslator ft : filterTranslators) {
+            if (ft.translatesFilter(filter)) {
+                return ft.getWhereStringForFilter(filter, sh);
+            }
+        }
+        return "";
+    }
+
+    public static String getJoinedFilterString(Collection<Filter> filters,
+            String joinString, StatementHelper sh) {
+        StringBuilder result = new StringBuilder();
+        for (Filter f : filters) {
+            result.append(getWhereStringForFilter(f, sh));
+            result.append(" ").append(joinString).append(" ");
+        }
+        // Remove the last instance of joinString
+        result.delete(result.length() - joinString.length() - 2,
+                result.length());
+        return result.toString();
+    }
+
+    public static String getWhereStringForFilters(List<Filter> filters,
+            StatementHelper sh) {
+        if (filters == null || filters.isEmpty()) {
+            return "";
+        }
+        StringBuilder where = new StringBuilder(" WHERE ");
+        where.append(getJoinedFilterString(filters, "AND", sh));
+        return where.toString();
+    }
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java
new file mode 100644 (file)
index 0000000..96b64dd
--- /dev/null
@@ -0,0 +1,25 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+import com.vaadin.data.Container.Filter;
+import com.vaadin.data.util.filter.Like;
+import com.vaadin.data.util.filter.SimpleStringFilter;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+
+public class SimpleStringTranslator implements FilterTranslator {
+
+    public boolean translatesFilter(Filter filter) {
+        return filter instanceof SimpleStringFilter;
+    }
+
+    public String getWhereStringForFilter(Filter filter, StatementHelper sh) {
+        SimpleStringFilter ssf = (SimpleStringFilter) filter;
+        // Create a Like filter based on the SimpleStringFilter and execute the
+        // LikeTranslator
+        String likeStr = ssf.isOnlyMatchPrefix() ? ssf.getFilterString() + "%"
+                : "%" + ssf.getFilterString() + "%";
+        Like like = new Like(ssf.getPropertyId().toString(), likeStr);
+        like.setCaseSensitive(!ssf.isIgnoreCase());
+        return new LikeTranslator().getWhereStringForFilter(like, sh);
+    }
+
+}
diff --git a/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/StringDecorator.java b/src/com/vaadin/data/util/sqlcontainer/query/generator/filter/StringDecorator.java
new file mode 100644 (file)
index 0000000..10d33ea
--- /dev/null
@@ -0,0 +1,53 @@
+package com.vaadin.data.util.sqlcontainer.query.generator.filter;
+
+/**
+ * The StringDecorator knows how to produce a quoted string using the specified
+ * quote start and quote end characters. It also handles grouping of a string
+ * (surrounding it in parenthesis).
+ * 
+ * Extend this class if you need to support special characters for grouping
+ * (parenthesis).
+ * 
+ * @author Vaadin Ltd
+ */
+public class StringDecorator {
+
+    private final String quoteStart;
+    private final String quoteEnd;
+
+    /**
+     * Constructs a StringDecorator that uses the quoteStart and quoteEnd
+     * characters to create quoted strings.
+     * 
+     * @param quoteStart
+     *            the character denoting the start of a quote.
+     * @param quoteEnd
+     *            the character denoting the end of a quote.
+     */
+    public StringDecorator(String quoteStart, String quoteEnd) {
+        this.quoteStart = quoteStart;
+        this.quoteEnd = quoteEnd;
+    }
+
+    /**
+     * Surround a string with quote characters.
+     * 
+     * @param str
+     *            the string to quote
+     * @return the quoted string
+     */
+    public String quote(Object str) {
+        return quoteStart + str + quoteEnd;
+    }
+
+    /**
+     * Groups a string by surrounding it in parenthesis
+     * 
+     * @param str
+     *            the string to group
+     * @return the grouped string
+     */
+    public String group(String str) {
+        return "(" + str + ")";
+    }
+}
index 2a609bca42421ec5bbe784c83032592ac0257031..606e9e3697d8c8bd7c1d0d27fe141584eaa416c7 100644 (file)
@@ -10,10 +10,10 @@ import com.vaadin.data.Container.ItemSetChangeListener;
 import com.vaadin.data.Item;
 import com.vaadin.data.Property;
 import com.vaadin.data.Property.ValueChangeEvent;
-import com.vaadin.data.util.SQLContainer;
-import com.vaadin.data.util.connection.JDBCConnectionPool;
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
-import com.vaadin.data.util.query.TableQuery;
+import com.vaadin.data.util.sqlcontainer.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests;
 import com.vaadin.ui.Button;
 import com.vaadin.ui.Button.ClickEvent;
index 6ead5b17b92bd442bc9b0e6f390558358e631546..419e606bfc72793e1830d12d72b5478eb547de6e 100644 (file)
@@ -3,10 +3,10 @@ package com.vaadin.tests.containers.sqlcontainer;
 import java.sql.SQLException;
 
 import com.vaadin.Application;
-import com.vaadin.data.util.SQLContainer;
-import com.vaadin.data.util.connection.JDBCConnectionPool;
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
-import com.vaadin.data.util.query.TableQuery;
+import com.vaadin.data.util.sqlcontainer.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;
 import com.vaadin.ui.Alignment;
 import com.vaadin.ui.Button;
 import com.vaadin.ui.Button.ClickEvent;
index 709295d55260ded376ef34bfcad3f46b8a316b58..f985fa2ad9ab3be1577f76c09d858d3cbb89eb40 100644 (file)
@@ -4,10 +4,10 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
 
-import com.vaadin.data.util.query.generator.DefaultSQLGenerator;
-import com.vaadin.data.util.query.generator.MSSQLGenerator;
-import com.vaadin.data.util.query.generator.OracleGenerator;
-import com.vaadin.data.util.query.generator.SQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator;
 import com.vaadin.tests.server.container.sqlcontainer.connection.J2EEConnectionPoolTest;
 import com.vaadin.tests.server.container.sqlcontainer.connection.SimpleJDBCConnectionPoolTest;
 import com.vaadin.tests.server.container.sqlcontainer.filters.BetweenTest;
index d4331faada74824547e622bd517f7fc6bfc10fad..efe539f2c73d68c947e3cfed3a6bd791382478c3 100644 (file)
@@ -7,11 +7,11 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import com.vaadin.data.Property.ReadOnlyException;
-import com.vaadin.data.util.ColumnProperty;
-import com.vaadin.data.util.ColumnProperty.NotNullableException;
-import com.vaadin.data.util.RowId;
-import com.vaadin.data.util.RowItem;
-import com.vaadin.data.util.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.ColumnProperty;
+import com.vaadin.data.util.sqlcontainer.RowId;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+import com.vaadin.data.util.sqlcontainer.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.ColumnProperty.NotNullableException;
 
 public class ColumnPropertyTest {
 
index 4029eb8109955a30496b96aaea250141332d286d..52225984a7da754445f51c877e32fc09dfa4cad8 100644 (file)
@@ -7,7 +7,7 @@ import java.sql.Statement;
 
 import org.junit.Assert;
 
-import com.vaadin.data.util.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
 
 public class DataGenerator {
index a0170baf516455707a08276bd0fe3e2b797902c1..22e8531c65eb0b67dbafd3011a4ebfe3797777bb 100644 (file)
@@ -3,8 +3,8 @@ package com.vaadin.tests.server.container.sqlcontainer;
 import java.util.List;
 
 import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.query.generator.StatementHelper;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
 
 public class FreeformQueryUtil {
index aa4e144db63bed484516e7ff6e2400e43ee7cbe5..c395bc42bf07b19f0f2272919cf7296ef22a0c82 100644 (file)
@@ -4,7 +4,7 @@ import junit.framework.Assert;
 
 import org.junit.Test;
 
-import com.vaadin.data.util.ReadOnlyRowId;
+import com.vaadin.data.util.sqlcontainer.ReadOnlyRowId;
 
 public class ReadOnlyRowIdTest {
 
index e619f203e20cc245dda93462dfd15c51e50c26b9..f243c43ec2da12c32baf04a4df1806874c4f51fa 100644 (file)
@@ -3,7 +3,7 @@ package com.vaadin.tests.server.container.sqlcontainer;
 import org.junit.Assert;
 import org.junit.Test;
 
-import com.vaadin.data.util.RowId;
+import com.vaadin.data.util.sqlcontainer.RowId;
 
 public class RowIdTest {
 
index 9c42906cd56639f1ff2b43fe7033250c28aca64c..af9cdbf306cd455a18b0ea18523277f5579c4f21 100644 (file)
@@ -17,15 +17,15 @@ import org.junit.Test;
 import com.vaadin.data.Container.ItemSetChangeEvent;\r
 import com.vaadin.data.Container.ItemSetChangeListener;\r
 import com.vaadin.data.Item;\r
-import com.vaadin.data.util.RowId;\r
-import com.vaadin.data.util.RowItem;\r
-import com.vaadin.data.util.SQLContainer;\r
-import com.vaadin.data.util.TemporaryRowId;\r
-import com.vaadin.data.util.connection.JDBCConnectionPool;\r
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;\r
 import com.vaadin.data.util.filter.Like;\r
-import com.vaadin.data.util.query.OrderBy;\r
-import com.vaadin.data.util.query.TableQuery;\r
+import com.vaadin.data.util.sqlcontainer.RowId;\r
+import com.vaadin.data.util.sqlcontainer.RowItem;\r
+import com.vaadin.data.util.sqlcontainer.SQLContainer;\r
+import com.vaadin.data.util.sqlcontainer.TemporaryRowId;\r
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;\r
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;\r
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;\r
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;\r
 import com.vaadin.terminal.gwt.client.Util;\r
 import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;\r
 \r
index fbc32206c159e24a80499a8f6cc028fb0ca7825f..095e4a23455d85a4a3ee79796e60b96342f993a7 100644 (file)
@@ -20,23 +20,23 @@ import com.vaadin.data.Container.Filter;
 import com.vaadin.data.Container.ItemSetChangeEvent;
 import com.vaadin.data.Container.ItemSetChangeListener;
 import com.vaadin.data.Item;
-import com.vaadin.data.util.RowId;
-import com.vaadin.data.util.RowItem;
-import com.vaadin.data.util.SQLContainer;
-import com.vaadin.data.util.TemporaryRowId;
-import com.vaadin.data.util.connection.JDBCConnectionPool;
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
 import com.vaadin.data.util.filter.Compare.Equal;
 import com.vaadin.data.util.filter.Like;
-import com.vaadin.data.util.query.FreeformQuery;
-import com.vaadin.data.util.query.FreeformQueryDelegate;
-import com.vaadin.data.util.query.FreeformStatementDelegate;
-import com.vaadin.data.util.query.OrderBy;
-import com.vaadin.data.util.query.generator.MSSQLGenerator;
-import com.vaadin.data.util.query.generator.OracleGenerator;
-import com.vaadin.data.util.query.generator.SQLGenerator;
-import com.vaadin.data.util.query.generator.StatementHelper;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
+import com.vaadin.data.util.sqlcontainer.RowId;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+import com.vaadin.data.util.sqlcontainer.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.TemporaryRowId;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.query.FreeformQuery;
+import com.vaadin.data.util.sqlcontainer.query.FreeformQueryDelegate;
+import com.vaadin.data.util.sqlcontainer.query.FreeformStatementDelegate;
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;
+import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
 
 public class SQLContainerTest {
index 513bdf3329d3de94cf07236a6dbb1824406e8cb3..8123cdb5163b9bc1a7215e1d5a348501cc8a3cfb 100644 (file)
@@ -13,14 +13,14 @@ import org.junit.Before;
 import org.junit.Test;
 
 import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.SQLContainer;
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
 import com.vaadin.data.util.filter.Compare.Equal;
-import com.vaadin.data.util.query.FreeformQuery;
-import com.vaadin.data.util.query.FreeformStatementDelegate;
-import com.vaadin.data.util.query.TableQuery;
-import com.vaadin.data.util.query.generator.StatementHelper;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
+import com.vaadin.data.util.sqlcontainer.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.query.FreeformQuery;
+import com.vaadin.data.util.sqlcontainer.query.FreeformStatementDelegate;
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
 import com.vaadin.ui.Table;
 import com.vaadin.ui.Window;
index b99839616e58099fa25cdf231199949be88a8959..a8d4e48004fff10fe2ac87893efc5534dafaa723 100644 (file)
@@ -4,7 +4,7 @@ import junit.framework.Assert;
 
 import org.junit.Test;
 
-import com.vaadin.data.util.SQLUtil;
+import com.vaadin.data.util.sqlcontainer.SQLUtil;
 
 public class UtilTest {
 
index 7f9f5a019b501ea47e67cbb5b51454eef10f5052..dc5ca51d94f61aef542aa236968a550fcbfd562f 100644 (file)
@@ -12,7 +12,7 @@ import junit.framework.Assert;
 import org.easymock.EasyMock;
 import org.junit.Test;
 
-import com.vaadin.data.util.connection.J2EEConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.J2EEConnectionPool;
 
 public class J2EEConnectionPoolTest {
 
index 018d8938980a25f325f87ba6ec849bf0db728adf..c05b3cbf1b0f86ad7607a3e885227c2b18a41ded 100644 (file)
@@ -9,8 +9,8 @@ import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
-import com.vaadin.data.util.connection.JDBCConnectionPool;
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests;
 
 public class SimpleJDBCConnectionPoolTest {
index 3c16ebef46037006db894587285ab78c86da4dd3..06873d4aff8b17c5abd5be871c20461e2f6aba29 100644 (file)
@@ -11,19 +11,19 @@ import org.junit.Before;
 import org.junit.Test;\r
 \r
 import com.vaadin.data.Container.Filter;\r
-import com.vaadin.data.util.RowItem;\r
-import com.vaadin.data.util.SQLContainer;\r
-import com.vaadin.data.util.connection.JDBCConnectionPool;\r
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;\r
 import com.vaadin.data.util.filter.Like;\r
 import com.vaadin.data.util.filter.Or;\r
-import com.vaadin.data.util.query.OrderBy;\r
-import com.vaadin.data.util.query.TableQuery;\r
-import com.vaadin.data.util.query.generator.DefaultSQLGenerator;\r
-import com.vaadin.data.util.query.generator.MSSQLGenerator;\r
-import com.vaadin.data.util.query.generator.OracleGenerator;\r
-import com.vaadin.data.util.query.generator.SQLGenerator;\r
-import com.vaadin.data.util.query.generator.StatementHelper;\r
+import com.vaadin.data.util.sqlcontainer.RowItem;\r
+import com.vaadin.data.util.sqlcontainer.SQLContainer;\r
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;\r
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;\r
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;\r
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;\r
+import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator;\r
+import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator;\r
+import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator;\r
+import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator;\r
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;\r
 import com.vaadin.tests.server.container.sqlcontainer.AllTests;\r
 import com.vaadin.tests.server.container.sqlcontainer.DataGenerator;\r
 \r
index 743d8bd5acf81fe07008696b5aee3d0b092f8900..cc213f764ff9954895a3c067f7ed43015fb2a08f 100644 (file)
@@ -15,15 +15,15 @@ import org.junit.Before;
 import org.junit.Test;
 
 import com.vaadin.data.Container.Filter;
-import com.vaadin.data.util.RowId;
-import com.vaadin.data.util.RowItem;
-import com.vaadin.data.util.SQLContainer;
-import com.vaadin.data.util.connection.JDBCConnectionPool;
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;
 import com.vaadin.data.util.filter.Like;
-import com.vaadin.data.util.query.FreeformQuery;
-import com.vaadin.data.util.query.FreeformQueryDelegate;
-import com.vaadin.data.util.query.OrderBy;
+import com.vaadin.data.util.sqlcontainer.RowId;
+import com.vaadin.data.util.sqlcontainer.RowItem;
+import com.vaadin.data.util.sqlcontainer.SQLContainer;
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;
+import com.vaadin.data.util.sqlcontainer.query.FreeformQuery;
+import com.vaadin.data.util.sqlcontainer.query.FreeformQueryDelegate;
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests;
 import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;
 import com.vaadin.tests.server.container.sqlcontainer.DataGenerator;
index 8ccca120ccc9c757cea0969f3c826015d667eafb..02130fab83c2bb5433f986103ad892fefc5ac8b7 100644 (file)
@@ -20,9 +20,9 @@ import com.vaadin.data.util.filter.Like;
 import com.vaadin.data.util.filter.Not;
 import com.vaadin.data.util.filter.Or;
 import com.vaadin.data.util.filter.SimpleStringFilter;
-import com.vaadin.data.util.query.generator.StatementHelper;
-import com.vaadin.data.util.query.generator.filter.QueryBuilder;
-import com.vaadin.data.util.query.generator.filter.StringDecorator;
+import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder;
+import com.vaadin.data.util.sqlcontainer.query.generator.filter.StringDecorator;
 
 public class QueryBuilderTest {
 
index 600dcac44e83be53d5e37abdfc469833c53d8d23..a3b2b20ffef21b88a65a80ede8712ee130d80ba3 100644 (file)
@@ -15,16 +15,16 @@ import org.junit.Before;
 import org.junit.Test;\r
 \r
 import com.vaadin.data.Container.Filter;\r
-import com.vaadin.data.util.OptimisticLockException;\r
-import com.vaadin.data.util.RowItem;\r
-import com.vaadin.data.util.SQLContainer;\r
-import com.vaadin.data.util.connection.JDBCConnectionPool;\r
-import com.vaadin.data.util.connection.SimpleJDBCConnectionPool;\r
 import com.vaadin.data.util.filter.Compare.Equal;\r
 import com.vaadin.data.util.filter.Like;\r
-import com.vaadin.data.util.query.OrderBy;\r
-import com.vaadin.data.util.query.TableQuery;\r
-import com.vaadin.data.util.query.generator.DefaultSQLGenerator;\r
+import com.vaadin.data.util.sqlcontainer.OptimisticLockException;\r
+import com.vaadin.data.util.sqlcontainer.RowItem;\r
+import com.vaadin.data.util.sqlcontainer.SQLContainer;\r
+import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool;\r
+import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;\r
+import com.vaadin.data.util.sqlcontainer.query.OrderBy;\r
+import com.vaadin.data.util.sqlcontainer.query.TableQuery;\r
+import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator;\r
 import com.vaadin.tests.server.container.sqlcontainer.AllTests;\r
 import com.vaadin.tests.server.container.sqlcontainer.AllTests.DB;\r
 import com.vaadin.tests.server.container.sqlcontainer.DataGenerator;\r