]> source.dussan.org Git - vaadin-framework.git/commitdiff
Merge of (#10471) to Vaadin 7. 02/602/1
authorAnna Koskinen <anna@vaadin.com>
Wed, 9 Jan 2013 08:20:40 +0000 (10:20 +0200)
committerAnna Koskinen <anna@vaadin.com>
Wed, 9 Jan 2013 11:19:02 +0000 (13:19 +0200)
ComboBox filtering fix.

Change-Id: Ia274155c15a5319ec0c98d7bc0347e2f2bd0ddbd

server/src/com/vaadin/ui/ComboBox.java
uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.java [new file with mode: 0644]

index 4a82cf27e21b71e6f8683fb48ac67837f6cd90e0..0f42749acd5f7e86b6ea50036d434a7bcac87d00 100644 (file)
@@ -82,10 +82,10 @@ public class ComboBox extends AbstractSelect implements
     private boolean optionRequest;
 
     /**
-     * True if the container is being filtered temporarily and item set change
-     * notifications should be suppressed.
+     * True while painting to suppress item set change notifications that could
+     * be caused by temporary filtering.
      */
-    private boolean filteringContainer;
+    private boolean isPainting;
 
     /**
      * Flag to indicate whether to scroll the selected item visible (select the
@@ -147,141 +147,151 @@ public class ComboBox extends AbstractSelect implements
 
     @Override
     public void paintContent(PaintTarget target) throws PaintException {
-        if (inputPrompt != null) {
-            target.addAttribute(ComboBoxConstants.ATTR_INPUTPROMPT, inputPrompt);
-        }
+        isPainting = true;
+        try {
+            if (inputPrompt != null) {
+                target.addAttribute(ComboBoxConstants.ATTR_INPUTPROMPT,
+                        inputPrompt);
+            }
 
-        if (!textInputAllowed) {
-            target.addAttribute(ComboBoxConstants.ATTR_NO_TEXT_INPUT, true);
-        }
+            if (!textInputAllowed) {
+                target.addAttribute(ComboBoxConstants.ATTR_NO_TEXT_INPUT, true);
+            }
 
-        // clear caption change listeners
-        getCaptionChangeListener().clear();
+            // clear caption change listeners
+            getCaptionChangeListener().clear();
 
-        // The tab ordering number
-        if (getTabIndex() != 0) {
-            target.addAttribute("tabindex", getTabIndex());
-        }
+            // The tab ordering number
+            if (getTabIndex() != 0) {
+                target.addAttribute("tabindex", getTabIndex());
+            }
 
-        // If the field is modified, but not committed, set modified attribute
-        if (isModified()) {
-            target.addAttribute("modified", true);
-        }
+            // If the field is modified, but not committed, set modified
+            // attribute
+            if (isModified()) {
+                target.addAttribute("modified", true);
+            }
 
-        if (isNewItemsAllowed()) {
-            target.addAttribute("allownewitem", true);
-        }
+            if (isNewItemsAllowed()) {
+                target.addAttribute("allownewitem", true);
+            }
 
-        boolean needNullSelectOption = false;
-        if (isNullSelectionAllowed()) {
-            target.addAttribute("nullselect", true);
-            needNullSelectOption = (getNullSelectionItemId() == null);
-            if (!needNullSelectOption) {
-                target.addAttribute("nullselectitem", true);
+            boolean needNullSelectOption = false;
+            if (isNullSelectionAllowed()) {
+                target.addAttribute("nullselect", true);
+                needNullSelectOption = (getNullSelectionItemId() == null);
+                if (!needNullSelectOption) {
+                    target.addAttribute("nullselectitem", true);
+                }
             }
-        }
 
-        // Constructs selected keys array
-        String[] selectedKeys = new String[(getValue() == null
-                && getNullSelectionItemId() == null ? 0 : 1)];
+            // Constructs selected keys array
+            String[] selectedKeys = new String[(getValue() == null
+                    && getNullSelectionItemId() == null ? 0 : 1)];
 
-        target.addAttribute("pagelength", pageLength);
+            target.addAttribute("pagelength", pageLength);
 
-        target.addAttribute("filteringmode", getFilteringMode().toString());
+            target.addAttribute("filteringmode", getFilteringMode().toString());
 
-        // Paints the options and create array of selected id keys
-        int keyIndex = 0;
+            // Paints the options and create array of selected id keys
+            int keyIndex = 0;
 
-        target.startTag("options");
+            target.startTag("options");
 
-        if (currentPage < 0) {
-            optionRequest = false;
-            currentPage = 0;
-            filterstring = "";
-        }
+            if (currentPage < 0) {
+                optionRequest = false;
+                currentPage = 0;
+                filterstring = "";
+            }
 
-        boolean nullFilteredOut = filterstring != null
-                && !"".equals(filterstring)
-                && filteringMode != FilteringMode.OFF;
-        // null option is needed and not filtered out, even if not on current
-        // page
-        boolean nullOptionVisible = needNullSelectOption && !nullFilteredOut;
-
-        // first try if using container filters is possible
-        List<?> options = getOptionsWithFilter(nullOptionVisible);
-        if (null == options) {
-            // not able to use container filters, perform explicit in-memory
-            // filtering
-            options = getFilteredOptions();
-            filteredSize = options.size();
-            options = sanitetizeList(options, nullOptionVisible);
-        }
+            boolean nullFilteredOut = filterstring != null
+                    && !"".equals(filterstring)
+                    && filteringMode != FilteringMode.OFF;
+            // null option is needed and not filtered out, even if not on
+            // current
+            // page
+            boolean nullOptionVisible = needNullSelectOption
+                    && !nullFilteredOut;
+
+            // first try if using container filters is possible
+            List<?> options = getOptionsWithFilter(nullOptionVisible);
+            if (null == options) {
+                // not able to use container filters, perform explicit in-memory
+                // filtering
+                options = getFilteredOptions();
+                filteredSize = options.size();
+                options = sanitetizeList(options, nullOptionVisible);
+            }
 
-        final boolean paintNullSelection = needNullSelectOption
-                && currentPage == 0 && !nullFilteredOut;
+            final boolean paintNullSelection = needNullSelectOption
+                    && currentPage == 0 && !nullFilteredOut;
 
-        if (paintNullSelection) {
-            target.startTag("so");
-            target.addAttribute("caption", "");
-            target.addAttribute("key", "");
-            target.endTag("so");
-        }
+            if (paintNullSelection) {
+                target.startTag("so");
+                target.addAttribute("caption", "");
+                target.addAttribute("key", "");
+                target.endTag("so");
+            }
 
-        final Iterator<?> i = options.iterator();
-        // Paints the available selection options from data source
+            final Iterator<?> i = options.iterator();
+            // Paints the available selection options from data source
 
-        while (i.hasNext()) {
+            while (i.hasNext()) {
 
-            final Object id = i.next();
+                final Object id = i.next();
 
-            if (!isNullSelectionAllowed() && id != null
-                    && id.equals(getNullSelectionItemId()) && !isSelected(id)) {
-                continue;
-            }
+                if (!isNullSelectionAllowed() && id != null
+                        && id.equals(getNullSelectionItemId())
+                        && !isSelected(id)) {
+                    continue;
+                }
 
-            // Gets the option attribute values
-            final String key = itemIdMapper.key(id);
-            final String caption = getItemCaption(id);
-            final Resource icon = getItemIcon(id);
-            getCaptionChangeListener().addNotifierForItem(id);
+                // Gets the option attribute values
+                final String key = itemIdMapper.key(id);
+                final String caption = getItemCaption(id);
+                final Resource icon = getItemIcon(id);
+                getCaptionChangeListener().addNotifierForItem(id);
 
-            // Paints the option
-            target.startTag("so");
-            if (icon != null) {
-                target.addAttribute("icon", icon);
-            }
-            target.addAttribute("caption", caption);
-            if (id != null && id.equals(getNullSelectionItemId())) {
-                target.addAttribute("nullselection", true);
-            }
-            target.addAttribute("key", key);
-            if (isSelected(id) && keyIndex < selectedKeys.length) {
-                target.addAttribute("selected", true);
-                selectedKeys[keyIndex++] = key;
+                // Paints the option
+                target.startTag("so");
+                if (icon != null) {
+                    target.addAttribute("icon", icon);
+                }
+                target.addAttribute("caption", caption);
+                if (id != null && id.equals(getNullSelectionItemId())) {
+                    target.addAttribute("nullselection", true);
+                }
+                target.addAttribute("key", key);
+                if (isSelected(id) && keyIndex < selectedKeys.length) {
+                    target.addAttribute("selected", true);
+                    selectedKeys[keyIndex++] = key;
+                }
+                target.endTag("so");
             }
-            target.endTag("so");
-        }
-        target.endTag("options");
+            target.endTag("options");
 
-        target.addAttribute("totalitems", size()
-                + (needNullSelectOption ? 1 : 0));
-        if (filteredSize > 0 || nullOptionVisible) {
-            target.addAttribute("totalMatches", filteredSize
-                    + (nullOptionVisible ? 1 : 0));
-        }
+            target.addAttribute("totalitems", size()
+                    + (needNullSelectOption ? 1 : 0));
+            if (filteredSize > 0 || nullOptionVisible) {
+                target.addAttribute("totalMatches", filteredSize
+                        + (nullOptionVisible ? 1 : 0));
+            }
 
-        // Paint variables
-        target.addVariable(this, "selected", selectedKeys);
-        if (isNewItemsAllowed()) {
-            target.addVariable(this, "newitem", "");
-        }
+            // Paint variables
+            target.addVariable(this, "selected", selectedKeys);
+            if (isNewItemsAllowed()) {
+                target.addVariable(this, "newitem", "");
+            }
 
-        target.addVariable(this, "filter", filterstring);
-        target.addVariable(this, "page", currentPage);
+            target.addVariable(this, "filter", filterstring);
+            target.addVariable(this, "page", currentPage);
 
-        currentPage = -1; // current page is always set by client
+            currentPage = -1; // current page is always set by client
 
-        optionRequest = true;
+            optionRequest = true;
+        } finally {
+            isPainting = false;
+        }
 
     }
 
@@ -355,7 +365,6 @@ public class ComboBox extends AbstractSelect implements
         // change events from the underlying container, but the ComboBox does
         // not process or propagate them based on the flag filteringContainer
         if (filter != null) {
-            filteringContainer = true;
             filterable.addContainerFilter(filter);
         }
 
@@ -395,7 +404,6 @@ public class ComboBox extends AbstractSelect implements
             // to the outside, filtering should not be visible
             if (filter != null) {
                 filterable.removeContainerFilter(filter);
-                filteringContainer = false;
             }
         }
     }
@@ -435,7 +443,7 @@ public class ComboBox extends AbstractSelect implements
 
     @Override
     public void containerItemSetChange(Container.ItemSetChangeEvent event) {
-        if (!filteringContainer) {
+        if (!isPainting) {
             super.containerItemSetChange(event);
         }
     }
diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.html b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.html
new file mode 100644 (file)
index 0000000..f3f44a5
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://localhost:8888/" />
+<title>ComboBoxSQLContainerFilteredValueChange</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">ComboBoxSQLContainerFilteredValueChange</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.combobox.ComboBoxSQLContainerFilteredValueChange?restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestscomponentscomboboxComboBoxSQLContainerFilteredValueChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VFilterSelect[0]#textbox</td>
+       <td>a</td>
+</tr>
+<tr>
+       <td>pressSpecialKey</td>
+       <td>vaadin=runcomvaadintestscomponentscomboboxComboBoxSQLContainerFilteredValueChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VFilterSelect[0]#textbox</td>
+       <td>down</td>
+</tr>
+<tr>
+       <td>pressSpecialKey</td>
+       <td>vaadin=runcomvaadintestscomponentscomboboxComboBoxSQLContainerFilteredValueChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VFilterSelect[0]#textbox</td>
+       <td>enter</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestscomponentscomboboxComboBoxSQLContainerFilteredValueChange::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VLabel[0]</td>
+       <td>Selected: 1</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.java
new file mode 100644 (file)
index 0000000..23a75ae
--- /dev/null
@@ -0,0 +1,123 @@
+package com.vaadin.tests.components.combobox;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import com.vaadin.data.Property;
+import com.vaadin.data.Property.ValueChangeEvent;
+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.components.TestBase;
+import com.vaadin.ui.AbstractSelect.Filtering;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.VerticalLayout;
+
+public class ComboBoxSQLContainerFilteredValueChange extends TestBase {
+
+    @Override
+    protected void setup() {
+        VerticalLayout layout = new VerticalLayout();
+        addComponent(layout);
+
+        final ComboBox myCombo = new ComboBox("MyCaption");
+        layout.addComponent(myCombo);
+
+        final Label selectedLabel = new Label("Selected: null");
+        layout.addComponent(selectedLabel);
+
+        try {
+            JDBCConnectionPool connectionPool = new SimpleJDBCConnectionPool(
+                    "org.hsqldb.jdbc.JDBCDriver",
+                    "jdbc:hsqldb:mem:sqlcontainer", "SA", "", 2, 20);
+
+            createTestTable(connectionPool);
+            insertTestData(connectionPool);
+
+            TableQuery q = new TableQuery("mytable", connectionPool);
+            q.setVersionColumn("version");
+            SQLContainer myContainer = new SQLContainer(q);
+
+            myCombo.setContainerDataSource(myContainer);
+
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+        myCombo.setItemCaptionPropertyId("MYFIELD");
+        myCombo.setFilteringMode(Filtering.FILTERINGMODE_CONTAINS);
+        myCombo.setImmediate(true);
+        myCombo.setWidth("100.0%");
+        myCombo.setHeight("-1px");
+        myCombo.addListener(new Property.ValueChangeListener() {
+            public void valueChange(ValueChangeEvent event) {
+                selectedLabel.setValue("Selected: "
+                        + event.getProperty().getValue());
+            }
+        });
+    }
+
+    @Override
+    protected String getDescription() {
+        return "Selecting the first filtered item should change the value of the label under the ComboBox to 'Selected: 1'.";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 10471;
+    }
+
+    /**
+     * (Re)creates the test table
+     * @param connectionPool
+     */
+    private void createTestTable(JDBCConnectionPool connectionPool) {
+        Connection conn = null;
+        try {
+            conn = connectionPool.reserveConnection();
+            Statement statement = conn.createStatement();
+            try {
+                statement.executeUpdate("DROP TABLE mytable");
+            } catch (SQLException e) {
+            }
+            statement.execute("CREATE TABLE mytable "
+                    + "(id INTEGER GENERATED BY DEFAULT AS IDENTITY, "
+                    + "MYFIELD VARCHAR(45), " + "PRIMARY KEY(ID))");
+            statement.close();
+            conn.commit();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        } finally {
+            connectionPool.releaseConnection(conn);
+        }
+    }
+
+    /**
+     * Adds test data to the test table
+     * @param connectionPool
+     * @throws SQLException
+     */
+    private void insertTestData(JDBCConnectionPool connectionPool)
+            throws SQLException {
+        Connection conn = null;
+        try {
+            conn = connectionPool.reserveConnection();
+            Statement statement = conn.createStatement();
+
+            statement.executeUpdate("INSERT INTO mytable VALUES(1, 'A0')");
+            statement.executeUpdate("INSERT INTO mytable VALUES(2, 'A1')");
+            statement.executeUpdate("INSERT INTO mytable VALUES(3, 'B0')");
+            statement.executeUpdate("INSERT INTO mytable VALUES(4, 'B1')");
+            
+            statement.close();
+            conn.commit();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        } finally {
+            connectionPool.releaseConnection(conn);
+        }
+    }
+}
\ No newline at end of file