diff options
Diffstat (limited to 'compatibility-server')
3 files changed, 441 insertions, 18 deletions
diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java index 1055d1921d..553bbc753c 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java @@ -99,6 +99,13 @@ public class FieldGroup implements Serializable { public void setItemDataSource(Item itemDataSource) { this.itemDataSource = itemDataSource; + bindFields(); + } + + /** + * Binds all fields to the properties in the item in use. + */ + protected void bindFields() { for (Field<?> f : fieldToPropertyId.keySet()) { bind(f, fieldToPropertyId.get(f)); } @@ -254,20 +261,7 @@ public class FieldGroup implements Serializable { fieldToPropertyId.put(field, propertyId); propertyIdToField.put(propertyId, field); if (itemDataSource == null) { - // Clear any possible existing binding to clear the field - field.setPropertyDataSource(null); - boolean fieldReadOnly = field.isReadOnly(); - if (!fieldReadOnly) { - field.clear(); - } else { - // Temporarily make the field read-write so we can clear the - // value. Needed because setPropertyDataSource(null) does not - // currently clear the field - // (https://dev.vaadin.com/ticket/14733) - field.setReadOnly(false); - field.clear(); - field.setReadOnly(true); - } + clearField(field); // Will be bound when data source is set return; @@ -279,6 +273,29 @@ public class FieldGroup implements Serializable { } /** + * Clears field and any possible existing binding. + * + * @param field + * The field to be cleared + */ + protected void clearField(Field<?> field) { + // Clear any possible existing binding to clear the field + field.setPropertyDataSource(null); + boolean fieldReadOnly = field.isReadOnly(); + if (!fieldReadOnly) { + field.clear(); + } else { + // Temporarily make the field read-write so we can clear the + // value. Needed because setPropertyDataSource(null) does not + // currently clear the field + // (https://dev.vaadin.com/ticket/14733) + field.setReadOnly(false); + field.clear(); + field.setReadOnly(true); + } + } + + /** * Wrap property to transactional property. */ protected <T> Property.Transactional<T> wrapInTransactionalProperty( diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java index f5399f60ce..e48e58824f 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java @@ -104,6 +104,7 @@ import com.vaadin.v7.event.SelectionEvent.SelectionListener; import com.vaadin.v7.event.SelectionEvent.SelectionNotifier; import com.vaadin.v7.server.communication.data.DataGenerator; import com.vaadin.v7.server.communication.data.RpcDataProviderExtension; +import com.vaadin.v7.shared.ui.grid.ColumnResizeMode; import com.vaadin.v7.shared.ui.grid.EditorClientRpc; import com.vaadin.v7.shared.ui.grid.EditorServerRpc; import com.vaadin.v7.shared.ui.grid.GridClientRpc; @@ -566,6 +567,35 @@ public class Grid extends AbstractComponent } return field; } + + @Override + protected void bindFields() { + List<Field<?>> fields = new ArrayList<>(getFields()); + Item itemDataSource = getItemDataSource(); + + if (itemDataSource == null) { + unbindFields(fields); + } else { + bindFields(fields, itemDataSource); + } + } + + private void unbindFields(List<Field<?>> fields) { + for (Field<?> field : fields) { + clearField(field); + unbind(field); + field.setParent(null); + } + } + + private void bindFields(List<Field<?>> fields, Item itemDataSource) { + for (Field<?> field : fields) { + if (itemDataSource + .getItemProperty(getPropertyId(field)) != null) { + bind(field, getPropertyId(field)); + } + } + } } /** @@ -2242,8 +2272,9 @@ public class Grid extends AbstractComponent Renderer<?> renderer = column.getRenderer(); Item item = cell.getItem(); - Object modelValue = item.getItemProperty(cell.getPropertyId()) - .getValue(); + Property itemProperty = item.getItemProperty(cell.getPropertyId()); + Object modelValue = itemProperty == null ? null + : itemProperty.getValue(); data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer .encodeValue(modelValue, renderer, converter, getLocale())); @@ -4583,6 +4614,12 @@ public class Grid extends AbstractComponent private boolean editorSaving = false; private FieldGroup editorFieldGroup = new CustomFieldGroup(); + /** + * Poperty ID to Field mapping that stores editor fields set by + * {@link #setEditorField(Object, Field)}. + */ + private Map<Object, Field<?>> editorFields = new HashMap<>(); + private CellStyleGenerator cellStyleGenerator; private RowStyleGenerator rowStyleGenerator; @@ -5286,6 +5323,27 @@ public class Grid extends AbstractComponent } /** + * Sets the column resize mode to use. The default mode is + * {@link ColumnResizeMode#ANIMATED}. + * + * @param mode + * a ColumnResizeMode value + */ + public void setColumnResizeMode(ColumnResizeMode mode) { + getState().columnResizeMode = mode; + } + + /** + * Returns the current column resize mode. The default mode is + * {@link ColumnResizeMode#ANIMATED}. + * + * @return a ColumnResizeMode value + */ + public ColumnResizeMode getColumnResizeMode() { + return getState(false).columnResizeMode; + } + + /** * Creates a new column based on a property id and appends it as the last * column. * @@ -6868,6 +6926,16 @@ public class Grid extends AbstractComponent Field<?> editor = editorFieldGroup.getField(propertyId); + // If field group has no field for this property, see if we have it + // stored + if (editor == null) { + editor = editorFields.get(propertyId); + if (editor != null) { + editorFieldGroup.bind(editor, propertyId); + } + } + + // Otherwise try to build one try { if (editor == null) { editor = editorFieldGroup.buildAndBind(propertyId); @@ -6923,8 +6991,9 @@ public class Grid extends AbstractComponent editorFieldGroup.setItemDataSource(item); for (Column column : getColumns()) { - column.getState().editorConnector = getEditorField( - column.getPropertyId()); + column.getState().editorConnector = item + .getItemProperty(column.getPropertyId()) == null ? null + : getEditorField(column.getPropertyId()); } editorActive = true; @@ -6953,6 +7022,9 @@ public class Grid extends AbstractComponent field.setParent(this); editorFieldGroup.bind(field, propertyId); } + + // Store field for this property for future reference + editorFields.put(propertyId, field); } /** diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java new file mode 100644 index 0000000000..be6334f8cc --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java @@ -0,0 +1,334 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.fieldgroup.FieldGroup; +import com.vaadin.v7.data.util.AbstractInMemoryContainer; +import com.vaadin.v7.data.util.BeanItem; +import com.vaadin.v7.ui.Field; +import com.vaadin.v7.ui.PasswordField; +import com.vaadin.v7.ui.TextField; + +public class GridEditorMissingPropertyTest { + + private static final String PROPERTY_NAME = "name"; + private static final String PROPERTY_SIZE = "size"; + + private static final String FOLDER_NAME_BEFORE = "Folder name"; + private static final String FOLDER_NAME_AFTER = "Modified folder name"; + private static final String FILE_NAME_BEFORE = "File name"; + private static final String FILE_NAME_AFTER = "Modified file name"; + private static final String FILE_SIZE_BEFORE = "10kB"; + private static final String FILE_SIZE_AFTER = "20MB"; + + private final Grid grid = new Grid(); + + // Test items + private final Folder folder = new Folder(FOLDER_NAME_BEFORE); + private final File file = new File(FILE_NAME_BEFORE, FILE_SIZE_BEFORE); + + @Before + public void setup() throws SecurityException, NoSuchMethodException { + final BeanItem<Entry> folderItem = new BeanItem<>(folder); + final BeanItem<Entry> childItem = new BeanItem<>(file); + + @SuppressWarnings("unchecked") + TestContainer container = new TestContainer( + Arrays.asList(folderItem, childItem), + Arrays.asList(PROPERTY_NAME, PROPERTY_SIZE)); + + grid.setContainerDataSource(container); + grid.setSelectionMode(Grid.SelectionMode.SINGLE); + grid.setEditorEnabled(true); + } + + @Test + public void testBindFields() { + FieldGroup fieldGroup = grid.getEditorFieldGroup(); + + // Item with incomplete property set + fieldGroup.setItemDataSource( + grid.getContainerDataSource().getItem(folder)); + grid.getColumn(PROPERTY_NAME).getEditorField(); // called in + // grid.doEditItem + assertTrue("Properties in item should be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_NAME)); + assertFalse("Properties not present in item should not be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_SIZE)); + assertTrue("All of item's properties should be bound", + fieldGroup.getUnboundPropertyIds().isEmpty()); + + // Unbind all fields + fieldGroup.setItemDataSource(null); + assertTrue("No properties should be bound", + fieldGroup.getBoundPropertyIds().isEmpty()); + assertTrue("No unbound properties should exist", + fieldGroup.getUnboundPropertyIds().isEmpty()); + + // Item with complete property set + fieldGroup + .setItemDataSource(grid.getContainerDataSource().getItem(file)); + grid.getColumn(PROPERTY_NAME).getEditorField(); + grid.getColumn(PROPERTY_SIZE).getEditorField(); + assertTrue("Properties in item should be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_NAME)); + assertTrue("Properties in item should be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_SIZE)); + assertTrue("All of item's properties should be bound", + fieldGroup.getUnboundPropertyIds().isEmpty()); + + // Unbind all fields + fieldGroup.setItemDataSource(null); + assertTrue("No properties should be bound", + fieldGroup.getBoundPropertyIds().isEmpty()); + assertTrue("No unbound properties should exist", + fieldGroup.getUnboundPropertyIds().isEmpty()); + } + + @Test + public void testSetEditorField() { + FieldGroup fieldGroup = grid.getEditorFieldGroup(); + Field editorField = new PasswordField(); + + // Explicitly set editor field + fieldGroup.setItemDataSource( + grid.getContainerDataSource().getItem(folder)); + grid.getColumn(PROPERTY_NAME).setEditorField(editorField); + assertTrue("Editor field should be the one that was previously set", + grid.getColumn(PROPERTY_NAME).getEditorField() == editorField); + + // Reset item + fieldGroup.setItemDataSource(null); + fieldGroup + .setItemDataSource(grid.getContainerDataSource().getItem(file)); + assertTrue("Editor field should be the one that was previously set", + grid.getColumn(PROPERTY_NAME).getEditorField() == editorField); + } + + @Test + public void testEditCell() { + // Row with missing property + startEdit(folder); + assertEquals(folder, grid.getEditedItemId()); + assertEquals(getEditedItem(), + grid.getEditorFieldGroup().getItemDataSource()); + + assertEquals(FOLDER_NAME_BEFORE, + grid.getColumn(PROPERTY_NAME).getEditorField().getValue()); + try { + grid.getColumn(PROPERTY_SIZE).getEditorField(); + fail("Grid.editorFieldGroup should throw BindException by default"); + } catch (FieldGroup.BindException e) { + // BindException is thrown using the default FieldGroup + } + grid.cancelEditor(); + + // Row with all properties + startEdit(file); + assertEquals(file, grid.getEditedItemId()); + assertEquals(getEditedItem(), + grid.getEditorFieldGroup().getItemDataSource()); + + assertEquals(FILE_NAME_BEFORE, + grid.getColumn(PROPERTY_NAME).getEditorField().getValue()); + assertEquals(FILE_SIZE_BEFORE, + grid.getColumn(PROPERTY_SIZE).getEditorField().getValue()); + grid.cancelEditor(); + } + + @Test + public void testCancelEditor() { + // Row with all properties + testCancel(file, PROPERTY_NAME, FILE_NAME_BEFORE, FILE_NAME_AFTER); + testCancel(file, PROPERTY_SIZE, FILE_SIZE_BEFORE, FILE_SIZE_AFTER); + + // Row with missing property + testCancel(folder, PROPERTY_NAME, FOLDER_NAME_BEFORE, + FOLDER_NAME_AFTER); + } + + private void testCancel(Object itemId, String propertyId, + String valueBefore, String valueAfter) { + startEdit(itemId); + + TextField field = (TextField) grid.getColumn(propertyId) + .getEditorField(); + field.setValue(valueAfter); + + Property<?> datasource = field.getPropertyDataSource(); + + grid.cancelEditor(); + assertFalse(grid.isEditorActive()); + assertNull(grid.getEditedItemId()); + assertFalse(field.isModified()); + assertEquals("", field.getValue()); + assertEquals(valueBefore, datasource.getValue()); + assertNull(field.getPropertyDataSource()); + assertNull(grid.getEditorFieldGroup().getItemDataSource()); + } + + @Test + public void testSaveEditor() throws Exception { + // Row with all properties + testSave(file, PROPERTY_SIZE, FILE_SIZE_BEFORE, FILE_SIZE_AFTER); + + // Row with missing property + testSave(folder, PROPERTY_NAME, FOLDER_NAME_BEFORE, FOLDER_NAME_AFTER); + } + + private void testSave(Object itemId, String propertyId, String valueBefore, + String valueAfter) throws Exception { + startEdit(itemId); + TextField field = (TextField) grid.getColumn(propertyId) + .getEditorField(); + + field.setValue(valueAfter); + assertEquals(valueBefore, field.getPropertyDataSource().getValue()); + + grid.saveEditor(); + assertTrue(grid.isEditorActive()); + assertFalse(field.isModified()); + assertEquals(valueAfter, field.getValue()); + assertEquals(valueAfter, getEditedProperty(propertyId).getValue()); + grid.cancelEditor(); + } + + private Item getEditedItem() { + assertNotNull(grid.getEditedItemId()); + return grid.getContainerDataSource().getItem(grid.getEditedItemId()); + } + + private Property<?> getEditedProperty(Object propertyId) { + return getEditedItem().getItemProperty(propertyId); + } + + private void startEdit(Object itemId) { + grid.setEditorEnabled(true); + grid.editItem(itemId); + // Simulate succesful client response to actually start the editing. + grid.doEditItem(); + } + + private class TestContainer + extends AbstractInMemoryContainer<Object, String, BeanItem> { + + private final List<BeanItem<Entry>> items; + private final List<String> pids; + + public TestContainer(List<BeanItem<Entry>> items, List<String> pids) { + this.items = items; + this.pids = pids; + } + + @Override + protected List<Object> getAllItemIds() { + List<Object> ids = new ArrayList<>(); + for (BeanItem<Entry> item : items) { + ids.add(item.getBean()); + } + return ids; + } + + @Override + protected BeanItem<Entry> getUnfilteredItem(Object itemId) { + for (BeanItem<Entry> item : items) { + if (item.getBean().equals(itemId)) { + return item; + } + } + return null; + } + + @Override + public Collection<?> getContainerPropertyIds() { + return pids; + } + + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + return getItem(itemId).getItemProperty(propertyId); + } + + @Override + public Class<?> getType(Object propertyId) { + return String.class; + } + } + + public class Entry { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Entry(String name) { + this.name = name; + } + } + + public class Folder extends Entry { + + public Folder(String name) { + super(name); + } + } + + public class File extends Entry { + private String size; + + public File(String name, String size) { + super(name); + this.size = size; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + } + + private class Grid extends com.vaadin.v7.ui.Grid { + @Override + protected void doEditItem() { + super.doEditItem(); + } + } +} |