From 6e0f2efe996cfd3b38c960e04cbced0a91215cf0 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Thu, 18 Aug 2016 18:04:59 +0300 Subject: Move FieldGroup and Vaadin 7 Grid to compatibility package Change-Id: I9aaef478e0b67462641239802b924b8461cb9225 --- .../widgetsetutils/metadata/RendererVisitor.java | 11 +- .../connectors/AbstractRendererConnector.java | 55 +- .../AbstractSelectionModelConnector.java | 82 - .../client/connectors/ButtonRendererConnector.java | 45 - .../connectors/ClickableRendererConnector.java | 63 - .../client/connectors/DateRendererConnector.java | 34 - .../DetailComponentManagerConnector.java | 37 - .../vaadin/client/connectors/GridConnector.java | 1311 ---- .../client/connectors/ImageRendererConnector.java | 57 - .../connectors/JavaScriptRendererConnector.java | 278 - .../connectors/MultiSelectionModelConnector.java | 386 - .../connectors/NoSelectionModelConnector.java | 45 - .../client/connectors/NumberRendererConnector.java | 34 - .../connectors/ProgressBarRendererConnector.java | 35 - .../client/connectors/RpcDataSourceConnector.java | 253 - .../connectors/SingleSelectionModelConnector.java | 180 - .../client/connectors/TextRendererConnector.java | 34 - .../connectors/UnsafeHtmlRendererConnector.java | 43 - compatibility-client-compiled/pom.xml | 2 +- compatibility-client/pom.xml | 27 + .../connectors/AbstractGridRendererConnector.java | 84 + .../AbstractSelectionModelConnector.java | 82 + .../client/connectors/ButtonRendererConnector.java | 45 + .../connectors/ClickableRendererConnector.java | 63 + .../client/connectors/DateRendererConnector.java | 34 + .../DetailComponentManagerConnector.java | 37 + .../vaadin/client/connectors/GridConnector.java | 1311 ++++ .../client/connectors/ImageRendererConnector.java | 57 + .../connectors/JavaScriptRendererConnector.java | 278 + .../connectors/MultiSelectionModelConnector.java | 386 + .../connectors/NoSelectionModelConnector.java | 45 + .../client/connectors/NumberRendererConnector.java | 34 + .../connectors/ProgressBarRendererConnector.java | 35 + .../client/connectors/RpcDataSourceConnector.java | 253 + .../connectors/SingleSelectionModelConnector.java | 180 + .../client/connectors/TextRendererConnector.java | 34 + .../connectors/UnsafeHtmlRendererConnector.java | 43 + .../resources/com/vaadin/Vaadin7WidgetSet.gwt.xml | 7 + .../com/vaadin/v7/Vaadin7WidgetSet.gwt.xml | 7 - compatibility-server/pom.xml | 19 +- .../com/vaadin/data/fieldgroup/BeanFieldGroup.java | 272 + .../java/com/vaadin/data/fieldgroup/Caption.java | 27 + .../fieldgroup/DefaultFieldGroupFieldFactory.java | 254 + .../com/vaadin/data/fieldgroup/FieldGroup.java | 1258 ++++ .../data/fieldgroup/FieldGroupFieldFactory.java | 43 + .../com/vaadin/data/fieldgroup/PropertyId.java | 60 + .../server/communication/data/DataGenerator.java | 58 + .../data/RpcDataProviderExtension.java | 632 ++ .../src/main/java/com/vaadin/ui/LegacyGrid.java | 7352 ++++++++++++++++++++ .../ui/renderers/AbstractJavaScriptRenderer.java | 175 + .../com/vaadin/ui/renderers/ButtonRenderer.java | 76 + .../com/vaadin/ui/renderers/ClickableRenderer.java | 143 + .../java/com/vaadin/ui/renderers/DateRenderer.java | 240 + .../java/com/vaadin/ui/renderers/HtmlRenderer.java | 49 + .../com/vaadin/ui/renderers/ImageRenderer.java | 68 + .../com/vaadin/ui/renderers/NumberRenderer.java | 207 + .../vaadin/ui/renderers/ProgressBarRenderer.java | 46 + .../java/com/vaadin/ui/renderers/Renderer.java | 69 + .../java/com/vaadin/ui/renderers/TextRenderer.java | 50 + .../vaadin/data/fieldgroup/BeanFieldGroupTest.java | 70 + .../DefaultFieldGroupFieldFactoryTest.java | 125 + .../vaadin/data/fieldgroup/FieldGroupDateTest.java | 97 + .../data/fieldgroup/FieldGroupExceptionTest.java | 33 + .../com/vaadin/data/fieldgroup/FieldGroupTest.java | 96 + .../data/util/AbstractBeanContainerTestBase.java | 77 + .../data/util/AbstractContainerTestBase.java | 866 +++ .../AbstractHierarchicalContainerTestBase.java | 289 + .../util/AbstractInMemoryContainerTestBase.java | 6 + .../com/vaadin/data/util/BeanContainerTest.java | 516 ++ .../data/util/BeanItemContainerGenerator.java | 150 + .../data/util/BeanItemContainerSortTest.java | 166 + .../vaadin/data/util/BeanItemContainerTest.java | 997 +++ .../java/com/vaadin/data/util/BeanItemTest.java | 389 ++ .../util/ContainerHierarchicalWrapperTest.java | 26 + .../data/util/ContainerOrderedWrapperTest.java | 107 + .../vaadin/data/util/ContainerSizeAssertTest.java | 59 + .../com/vaadin/data/util/ContainerSortingTest.java | 222 + .../vaadin/data/util/FileSystemContainerTest.java | 16 + .../util/GeneratedPropertyContainerBasicTest.java | 596 ++ .../data/util/GeneratedPropertyContainerTest.java | 318 + .../HierarchicalContainerOrderedWrapperTest.java | 33 + .../data/util/HierarchicalContainerTest.java | 286 + .../com/vaadin/data/util/IndexedContainerTest.java | 533 ++ .../util/MethodPropertyMemoryConsumptionTest.java | 150 + .../vaadin/data/util/NestedMethodPropertyTest.java | 351 + .../com/vaadin/data/util/ObjectPropertyTest.java | 102 + .../util/PerformanceTestIndexedContainerTest.java | 120 + .../vaadin/data/util/PropertyDescriptorTest.java | 84 + .../com/vaadin/data/util/PropertySetItemTest.java | 432 ++ .../data/util/ReflectToolsGetSuperFieldTest.java | 35 + .../util/TransactionalPropertyWrapperTest.java | 117 + .../tests/server/ContextClickListenerTest.java | 163 + .../component/fieldgroup/BeanFieldGroupTest.java | 172 + .../fieldgroup/CaseInsensitiveBindingTest.java | 85 + .../component/fieldgroup/FieldGroupTest.java | 149 + .../FieldGroupWithReadOnlyPropertiesTest.java | 51 + .../fieldgroup/FieldNamedDescriptionTest.java | 53 + .../grid/GridAddRowBuiltinContainerTest.java | 219 + .../server/component/grid/GridChildrenTest.java | 59 + .../grid/GridColumnAddingAndRemovingTest.java | 134 + .../server/component/grid/GridColumnsTest.java | 413 ++ .../grid/GridContainerNotSortableTest.java | 103 + .../server/component/grid/GridContainerTest.java | 159 + .../server/component/grid/GridEditorTest.java | 296 + .../server/component/grid/GridExtensionTest.java | 41 + .../server/component/grid/GridSelectionTest.java | 377 + .../tests/server/component/grid/GridStateTest.java | 44 + .../component/grid/GridStaticSectionTest.java | 132 + .../component/grid/MultiSelectionModelTest.java | 171 + .../component/grid/SingleSelectionModelTest.java | 153 + .../tests/server/component/grid/TestGrid.java | 62 + .../declarative/GridColumnDeclarativeTest.java | 101 + .../declarative/GridDeclarativeAttributeTest.java | 84 + .../grid/declarative/GridDeclarativeTestBase.java | 159 + .../GridHeaderFooterDeclarativeTest.java | 356 + .../declarative/GridInlineDataDeclarativeTest.java | 124 + .../declarative/GridStructureDeclarativeTest.java | 50 + .../tests/server/component/grid/sort/SortTest.java | 205 + .../tests/server/renderer/ImageRendererTest.java | 85 + .../vaadin/tests/server/renderer/RendererTest.java | 268 + .../server/validation/BeanValidationTest.java | 129 + .../src/test/java/com/vaadin/ui/TableTest.java | 78 + ivysettings.xml | 8 + server/pom.xml | 9 + .../com/vaadin/data/fieldgroup/BeanFieldGroup.java | 272 - .../java/com/vaadin/data/fieldgroup/Caption.java | 27 - .../fieldgroup/DefaultFieldGroupFieldFactory.java | 254 - .../com/vaadin/data/fieldgroup/FieldGroup.java | 1258 ---- .../data/fieldgroup/FieldGroupFieldFactory.java | 43 - .../com/vaadin/data/fieldgroup/PropertyId.java | 60 - .../server/communication/data/DataGenerator.java | 58 - .../data/RpcDataProviderExtension.java | 632 -- server/src/main/java/com/vaadin/ui/LegacyGrid.java | 7352 -------------------- .../ui/renderers/AbstractJavaScriptRenderer.java | 175 - .../com/vaadin/ui/renderers/ButtonRenderer.java | 76 - .../com/vaadin/ui/renderers/ClickableRenderer.java | 143 - .../java/com/vaadin/ui/renderers/DateRenderer.java | 240 - .../java/com/vaadin/ui/renderers/HtmlRenderer.java | 49 - .../com/vaadin/ui/renderers/ImageRenderer.java | 68 - .../com/vaadin/ui/renderers/NumberRenderer.java | 207 - .../vaadin/ui/renderers/ProgressBarRenderer.java | 46 - .../java/com/vaadin/ui/renderers/Renderer.java | 69 - .../java/com/vaadin/ui/renderers/TextRenderer.java | 50 - .../data/DefaultFieldGroupFieldFactoryTest.java | 125 - .../vaadin/data/fieldgroup/BeanFieldGroupTest.java | 70 - .../vaadin/data/fieldgroup/FieldGroupDateTest.java | 97 - .../data/fieldgroup/FieldGroupExceptionTest.java | 33 - .../com/vaadin/data/fieldgroup/FieldGroupTest.java | 96 - .../data/util/AbstractBeanContainerTestBase.java | 77 - .../data/util/AbstractContainerTestBase.java | 866 --- .../AbstractHierarchicalContainerTestBase.java | 289 - .../util/AbstractInMemoryContainerTestBase.java | 6 - .../com/vaadin/data/util/BeanContainerTest.java | 516 -- .../data/util/BeanItemContainerGenerator.java | 150 - .../data/util/BeanItemContainerSortTest.java | 166 - .../vaadin/data/util/BeanItemContainerTest.java | 997 --- .../java/com/vaadin/data/util/BeanItemTest.java | 389 -- .../util/ContainerHierarchicalWrapperTest.java | 26 - .../data/util/ContainerOrderedWrapperTest.java | 107 - .../vaadin/data/util/ContainerSizeAssertTest.java | 59 - .../com/vaadin/data/util/ContainerSortingTest.java | 222 - .../vaadin/data/util/FileSystemContainerTest.java | 16 - .../util/GeneratedPropertyContainerBasicTest.java | 596 -- .../data/util/GeneratedPropertyContainerTest.java | 318 - .../HierarchicalContainerOrderedWrapperTest.java | 33 - .../data/util/HierarchicalContainerTest.java | 286 - .../com/vaadin/data/util/IndexedContainerTest.java | 533 -- .../util/MethodPropertyMemoryConsumptionTest.java | 150 - .../vaadin/data/util/NestedMethodPropertyTest.java | 351 - .../com/vaadin/data/util/ObjectPropertyTest.java | 102 - .../util/PerformanceTestIndexedContainerTest.java | 120 - .../vaadin/data/util/PropertyDescriptorTest.java | 84 - .../com/vaadin/data/util/PropertySetItemTest.java | 432 -- .../data/util/ReflectToolsGetSuperFieldTest.java | 35 - .../util/TransactionalPropertyWrapperTest.java | 117 - .../tests/server/ContextClickListenerTest.java | 163 - .../component/fieldgroup/BeanFieldGroupTest.java | 172 - .../fieldgroup/CaseInsensitiveBindingTest.java | 85 - .../component/fieldgroup/FieldGroupTest.java | 149 - .../FieldGroupWithReadOnlyPropertiesTest.java | 51 - .../fieldgroup/FieldNamedDescriptionTest.java | 53 - .../grid/GridAddRowBuiltinContainerTest.java | 219 - .../server/component/grid/GridChildrenTest.java | 59 - .../grid/GridColumnAddingAndRemovingTest.java | 134 - .../server/component/grid/GridColumnsTest.java | 413 -- .../grid/GridContainerNotSortableTest.java | 103 - .../server/component/grid/GridContainerTest.java | 159 - .../server/component/grid/GridEditorTest.java | 296 - .../server/component/grid/GridExtensionTest.java | 41 - .../server/component/grid/GridSelectionTest.java | 377 - .../tests/server/component/grid/GridStateTest.java | 44 - .../component/grid/GridStaticSectionTest.java | 132 - .../component/grid/MultiSelectionModelTest.java | 171 - .../component/grid/SingleSelectionModelTest.java | 153 - .../tests/server/component/grid/TestGrid.java | 62 - .../declarative/GridColumnDeclarativeTest.java | 101 - .../declarative/GridDeclarativeAttributeTest.java | 84 - .../grid/declarative/GridDeclarativeTestBase.java | 159 - .../GridHeaderFooterDeclarativeTest.java | 356 - .../declarative/GridInlineDataDeclarativeTest.java | 124 - .../declarative/GridStructureDeclarativeTest.java | 50 - .../tests/server/component/grid/sort/SortTest.java | 205 - .../tests/server/renderer/ImageRendererTest.java | 85 - .../vaadin/tests/server/renderer/RendererTest.java | 268 - .../server/validation/BeanValidationTest.java | 129 - server/src/test/java/com/vaadin/ui/TableTest.java | 78 - uitest/ivy.xml | 14 + .../components/UnknownComponentConnector.java | 47 - .../ui/ComponentMissingFromDefaultWidgetset.java | 2 + .../client/grid/IntArrayRendererConnector.java | 4 +- .../client/grid/PojoRendererConnector.java | 4 +- .../client/grid/RowAwareRendererConnector.java | 4 +- .../tests/widgetset/TestingWidgetSet.gwt.xml | 3 +- uitest/src/main/webapp/WEB-INF/web.xml | 8 +- .../application/DeploymentConfigurationTest.java | 6 +- .../components/UnknownComponentConnectorTest.java | 39 - 216 files changed, 26326 insertions(+), 26304 deletions(-) delete mode 100644 client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/GridConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java delete mode 100644 client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractGridRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/GridConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java create mode 100644 compatibility-client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java create mode 100755 compatibility-client/src/main/resources/com/vaadin/Vaadin7WidgetSet.gwt.xml delete mode 100755 compatibility-client/src/main/resources/com/vaadin/v7/Vaadin7WidgetSet.gwt.xml create mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java create mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/Caption.java create mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java create mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java create mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java create mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java create mode 100644 compatibility-server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/Renderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/ui/TableTest.java delete mode 100644 server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java delete mode 100644 server/src/main/java/com/vaadin/data/fieldgroup/Caption.java delete mode 100644 server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java delete mode 100644 server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java delete mode 100644 server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java delete mode 100644 server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java delete mode 100644 server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java delete mode 100644 server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java delete mode 100644 server/src/main/java/com/vaadin/ui/LegacyGrid.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/Renderer.java delete mode 100644 server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java delete mode 100644 server/src/test/java/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java delete mode 100644 server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java delete mode 100644 server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java delete mode 100644 server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java delete mode 100644 server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java delete mode 100644 server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java delete mode 100644 server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java delete mode 100644 server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java delete mode 100644 server/src/test/java/com/vaadin/data/util/BeanContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java delete mode 100644 server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/BeanItemTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java delete mode 100644 server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java delete mode 100644 server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java delete mode 100644 server/src/test/java/com/vaadin/ui/TableTest.java delete mode 100644 uitest/src/main/java/com/vaadin/tests/components/UnknownComponentConnector.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/components/UnknownComponentConnectorTest.java diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java index 5f75569509..9305758b02 100644 --- a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java @@ -31,10 +31,10 @@ import elemental.json.JsonValue; * Generates type data for renderer connectors. * * @@ -63,11 +63,12 @@ public class RendererVisitor extends TypeVisitor { // Needs GWT constructor if createRenderer is not overridden if (createRendererClass.getQualifiedSourceName() .equals(AbstractRendererConnector.class.getCanonicalName())) { - + // createRenderer not overridden JMethod getRenderer = ConnectorBundle.findInheritedMethod(type, "getRenderer"); if (getRenderer.getEnclosingType().getQualifiedSourceName().equals( AbstractRendererConnector.class.getCanonicalName())) { + // getRenderer not overridden logger.log(Type.ERROR, type.getQualifiedSourceName() + " must override either createRenderer or getRenderer"); throw new UnableToCompleteException(); @@ -84,7 +85,7 @@ public class RendererVisitor extends TypeVisitor { } } - private void doPresentationType(TreeLogger logger, JClassType type, + private static void doPresentationType(TreeLogger logger, JClassType type, ConnectorBundle bundle) throws UnableToCompleteException { JType presentationType = getPresentationType(type, logger); bundle.setPresentationType(type, presentationType); diff --git a/client/src/main/java/com/vaadin/client/connectors/AbstractRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/AbstractRendererConnector.java index 4f9ef980b8..989f10f266 100644 --- a/client/src/main/java/com/vaadin/client/connectors/AbstractRendererConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/AbstractRendererConnector.java @@ -23,25 +23,14 @@ import com.vaadin.client.metadata.Type; import com.vaadin.client.metadata.TypeData; import com.vaadin.client.metadata.TypeDataStore; import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widgets.Grid.Column; -import elemental.json.JsonObject; import elemental.json.JsonValue; /** - * An abstract base class for renderer connectors. A renderer connector is used - * to link a client-side {@link Renderer} to a server-side - * {@link com.vaadin.ui.components.grid.Renderer Renderer}. As a connector, it - * can use the regular Vaadin RPC and shared state mechanism to pass additional - * state and information between the client and the server. This base class - * itself only uses the basic {@link com.vaadin.shared.communication.SharedState - * SharedState} and no RPC interfaces. + * An abstract base class for renderer connectors. * * @param * the presentation type of the renderer - * - * @since 7.4 - * @author Vaadin Ltd */ public abstract class AbstractRendererConnector extends AbstractExtensionConnector { @@ -136,46 +125,4 @@ public abstract class AbstractRendererConnector // NOOP } - /** - * Gets the row key for a row object. - *

- * In case this renderer wants be able to identify a row in such a way that - * the server also understands it, the row key is used for that. Rows are - * identified by unified keys between the client and the server. - * - * @param row - * the row object - * @return the row key for the given row - */ - protected String getRowKey(JsonObject row) { - final ServerConnector parent = getParent(); - if (parent instanceof GridConnector) { - return ((GridConnector) parent).getRowKey(row); - } else { - throw new IllegalStateException( - "Renderers can only be used " + "with a Grid."); - } - } - - /** - * Gets the column id for a column. - *

- * In case this renderer wants be able to identify a column in such a way - * that the server also understands it, the column id is used for that. - * Columns are identified by unified ids between the client and the server. - * - * @param column - * the column object - * @return the column id for the given column - */ - protected String getColumnId(Column column) { - final ServerConnector parent = getParent(); - if (parent instanceof GridConnector) { - return ((GridConnector) parent).getColumnId(column); - } else { - throw new IllegalStateException( - "Renderers can only be used " + "with a Grid."); - } - } - } diff --git a/client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java deleted file mode 100644 index 75664d04f9..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.client.connectors; - -import java.util.Collection; - -import com.vaadin.client.data.DataSource.RowHandle; -import com.vaadin.client.extensions.AbstractExtensionConnector; -import com.vaadin.client.widget.grid.selection.SelectionModel; -import com.vaadin.client.widgets.Grid; -import com.vaadin.shared.ui.grid.GridState; - -import elemental.json.JsonObject; - -/** - * Base class for all selection model connectors. - * - * @since 7.6 - * @author Vaadin Ltd - */ -public abstract class AbstractSelectionModelConnector> - extends AbstractExtensionConnector { - - @Override - public GridConnector getParent() { - return (GridConnector) super.getParent(); - } - - protected Grid getGrid() { - return getParent().getWidget(); - } - - protected RowHandle getRowHandle(JsonObject row) { - return getGrid().getDataSource().getHandle(row); - } - - protected String getRowKey(JsonObject row) { - return row != null ? getParent().getRowKey(row) : null; - } - - protected abstract T createSelectionModel(); - - public abstract static class AbstractSelectionModel - implements SelectionModel { - - @Override - public boolean isSelected(JsonObject row) { - return row.hasKey(GridState.JSONKEY_SELECTED); - } - - @Override - public void setGrid(Grid grid) { - // NO-OP - } - - @Override - public void reset() { - // Should not need any actions. - } - - @Override - public Collection getSelectedRows() { - throw new UnsupportedOperationException( - "This client-side selection model " - + getClass().getSimpleName() - + " does not know selected rows."); - } - } -} diff --git a/client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java deleted file mode 100644 index fad8918c92..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.client.connectors; - -import com.google.web.bindery.event.shared.HandlerRegistration; -import com.vaadin.client.renderers.ButtonRenderer; -import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; -import com.vaadin.shared.ui.Connect; - -import elemental.json.JsonObject; - -/** - * A connector for {@link ButtonRenderer}. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.renderers.ButtonRenderer.class) -public class ButtonRendererConnector - extends ClickableRendererConnector { - - @Override - public ButtonRenderer getRenderer() { - return (ButtonRenderer) super.getRenderer(); - } - - @Override - protected HandlerRegistration addClickHandler( - RendererClickHandler handler) { - return getRenderer().addClickHandler(handler); - } -} diff --git a/client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java deleted file mode 100644 index 770b149daf..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.client.connectors; - -import com.google.web.bindery.event.shared.HandlerRegistration; -import com.vaadin.client.MouseEventDetailsBuilder; -import com.vaadin.client.renderers.ClickableRenderer; -import com.vaadin.client.renderers.ClickableRenderer.RendererClickEvent; -import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; -import com.vaadin.shared.ui.grid.renderers.RendererClickRpc; - -import elemental.json.JsonObject; - -/** - * An abstract base class for {@link ClickableRenderer} connectors. - * - * @param - * the presentation type of the renderer - * - * @since 7.4 - * @author Vaadin Ltd - */ -public abstract class ClickableRendererConnector - extends AbstractRendererConnector { - - HandlerRegistration clickRegistration; - - @Override - protected void init() { - clickRegistration = addClickHandler( - new RendererClickHandler() { - @Override - public void onClick(RendererClickEvent event) { - getRpcProxy(RendererClickRpc.class).click( - getRowKey(event.getCell().getRow()), - getColumnId(event.getCell().getColumn()), - MouseEventDetailsBuilder.buildMouseEventDetails( - event.getNativeEvent())); - } - }); - } - - @Override - public void onUnregister() { - clickRegistration.removeHandler(); - } - - protected abstract HandlerRegistration addClickHandler( - RendererClickHandler handler); -} diff --git a/client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java deleted file mode 100644 index 4b8c3872da..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.shared.ui.Connect; - -/** - * A connector for {@link com.vaadin.ui.components.grid.renderers.DateRenderer - * DateRenderer}. - *

- * The server-side Renderer operates on dates, but the data is serialized as a - * string, and displayed as-is on the client side. This is to be able to support - * the server's locale. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.renderers.DateRenderer.class) -public class DateRendererConnector extends TextRendererConnector { - // No implementation needed -} diff --git a/client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java b/client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java deleted file mode 100644 index 000a24af00..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.client.ServerConnector; -import com.vaadin.client.extensions.AbstractExtensionConnector; -import com.vaadin.shared.ui.Connect; -import com.vaadin.ui.LegacyGrid.DetailComponentManager; - -/** - * Client-side connector for the DetailComponentManager of Grid. - * - * @since 7.6.1 - */ -@Connect(DetailComponentManager.class) -public class DetailComponentManagerConnector - extends AbstractExtensionConnector { - - @Override - protected void extend(ServerConnector target) { - // TODO: Move DetailsGenerator logic here. - } - -} diff --git a/client/src/main/java/com/vaadin/client/connectors/GridConnector.java b/client/src/main/java/com/vaadin/client/connectors/GridConnector.java deleted file mode 100644 index 5ad90b7a45..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/GridConnector.java +++ /dev/null @@ -1,1311 +0,0 @@ -/* - * 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.client.connectors; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Logger; - -import com.google.gwt.core.client.Scheduler; -import com.google.gwt.core.client.Scheduler.ScheduledCommand; -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.EventTarget; -import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.client.ComponentConnector; -import com.vaadin.client.ConnectorHierarchyChangeEvent; -import com.vaadin.client.DeferredWorker; -import com.vaadin.client.MouseEventDetailsBuilder; -import com.vaadin.client.ServerConnector; -import com.vaadin.client.TooltipInfo; -import com.vaadin.client.WidgetUtil; -import com.vaadin.client.communication.StateChangeEvent; -import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; -import com.vaadin.client.connectors.RpcDataSourceConnector.DetailsListener; -import com.vaadin.client.connectors.RpcDataSourceConnector.RpcDataSource; -import com.vaadin.client.ui.AbstractComponentConnector; -import com.vaadin.client.ui.AbstractHasComponentsConnector; -import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; -import com.vaadin.client.ui.SimpleManagedLayout; -import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent; -import com.vaadin.client.widget.escalator.events.RowHeightChangedHandler; -import com.vaadin.client.widget.grid.CellReference; -import com.vaadin.client.widget.grid.CellStyleGenerator; -import com.vaadin.client.widget.grid.EditorHandler; -import com.vaadin.client.widget.grid.EventCellReference; -import com.vaadin.client.widget.grid.HeightAwareDetailsGenerator; -import com.vaadin.client.widget.grid.RowReference; -import com.vaadin.client.widget.grid.RowStyleGenerator; -import com.vaadin.client.widget.grid.events.BodyClickHandler; -import com.vaadin.client.widget.grid.events.BodyDoubleClickHandler; -import com.vaadin.client.widget.grid.events.ColumnReorderEvent; -import com.vaadin.client.widget.grid.events.ColumnReorderHandler; -import com.vaadin.client.widget.grid.events.ColumnResizeEvent; -import com.vaadin.client.widget.grid.events.ColumnResizeHandler; -import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeEvent; -import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeHandler; -import com.vaadin.client.widget.grid.events.GridClickEvent; -import com.vaadin.client.widget.grid.events.GridDoubleClickEvent; -import com.vaadin.client.widget.grid.sort.SortEvent; -import com.vaadin.client.widget.grid.sort.SortHandler; -import com.vaadin.client.widget.grid.sort.SortOrder; -import com.vaadin.client.widgets.Grid; -import com.vaadin.client.widgets.Grid.Column; -import com.vaadin.client.widgets.Grid.FooterCell; -import com.vaadin.client.widgets.Grid.FooterRow; -import com.vaadin.client.widgets.Grid.HeaderCell; -import com.vaadin.client.widgets.Grid.HeaderRow; -import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.data.sort.SortDirection; -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.grid.EditorClientRpc; -import com.vaadin.shared.ui.grid.EditorServerRpc; -import com.vaadin.shared.ui.grid.GridClientRpc; -import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridConstants; -import com.vaadin.shared.ui.grid.GridConstants.Section; -import com.vaadin.shared.ui.grid.GridServerRpc; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.GridStaticSectionState; -import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; -import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; -import com.vaadin.shared.ui.grid.ScrollDestination; - -import com.vaadin.ui.LegacyGrid; -import elemental.json.JsonObject; -import elemental.json.JsonValue; - -/** - * Connects the client side {@link Grid} widget with the server side - * {@link com.vaadin.ui.components.grid.Grid} component. - *

- * The Grid is typed to JSONObject. The structure of the JSONObject is described - * at {@link com.vaadin.shared.data.DataProviderRpc#setRowData(int, List) - * DataProviderRpc.setRowData(int, List)}. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(LegacyGrid.class) -public class GridConnector extends AbstractHasComponentsConnector - implements SimpleManagedLayout, DeferredWorker { - - private static final class CustomStyleGenerator implements - CellStyleGenerator, RowStyleGenerator { - @Override - public String getStyle(CellReference cellReference) { - JsonObject row = cellReference.getRow(); - if (!row.hasKey(GridState.JSONKEY_CELLSTYLES)) { - return null; - } - - Column column = cellReference.getColumn(); - if (!(column instanceof CustomGridColumn)) { - // Selection checkbox column - return null; - } - CustomGridColumn c = (CustomGridColumn) column; - - JsonObject cellStylesObject = row - .getObject(GridState.JSONKEY_CELLSTYLES); - assert cellStylesObject != null; - - if (cellStylesObject.hasKey(c.id)) { - return cellStylesObject.getString(c.id); - } else { - return null; - } - } - - @Override - public String getStyle(RowReference rowReference) { - JsonObject row = rowReference.getRow(); - if (row.hasKey(GridState.JSONKEY_ROWSTYLE)) { - return row.getString(GridState.JSONKEY_ROWSTYLE); - } else { - return null; - } - } - } - - /** - * Custom implementation of the custom grid column using a JSONObject to - * represent the cell value and String as a column type. - */ - private class CustomGridColumn extends Grid.Column { - - private final String id; - - private AbstractRendererConnector rendererConnector; - - private AbstractComponentConnector editorConnector; - - private HandlerRegistration errorStateHandler; - - public CustomGridColumn(String id, - AbstractRendererConnector rendererConnector) { - super(rendererConnector.getRenderer()); - this.rendererConnector = rendererConnector; - this.id = id; - } - - /** - * Sets a new renderer for this column object - * - * @param rendererConnector - * a renderer connector object - */ - public void setRenderer( - AbstractRendererConnector rendererConnector) { - setRenderer(rendererConnector.getRenderer()); - this.rendererConnector = rendererConnector; - } - - @Override - public Object getValue(final JsonObject obj) { - final JsonObject rowData = obj.getObject(GridState.JSONKEY_DATA); - - if (rowData.hasKey(id)) { - final JsonValue columnValue = rowData.get(id); - - return rendererConnector.decode(columnValue); - } - - return null; - } - - private AbstractComponentConnector getEditorConnector() { - return editorConnector; - } - - private void setEditorConnector( - final AbstractComponentConnector editorConnector) { - this.editorConnector = editorConnector; - - if (errorStateHandler != null) { - errorStateHandler.removeHandler(); - errorStateHandler = null; - } - - // Avoid nesting too deep - if (editorConnector == null) { - return; - } - - errorStateHandler = editorConnector.addStateChangeHandler( - "errorMessage", new StateChangeHandler() { - - @Override - public void onStateChanged( - StateChangeEvent stateChangeEvent) { - - String error = editorConnector - .getState().errorMessage; - - if (error == null) { - columnToErrorMessage - .remove(CustomGridColumn.this); - } else { - // The error message is formatted as HTML; - // therefore, we use this hack to make the - // string human-readable. - Element e = DOM.createElement("div"); - e.setInnerHTML(editorConnector - .getState().errorMessage); - error = getHeaderCaption() + ": " - + e.getInnerText(); - - columnToErrorMessage.put(CustomGridColumn.this, - error); - } - - // Handle Editor RPC before updating error status - Scheduler.get() - .scheduleFinally(new ScheduledCommand() { - - @Override - public void execute() { - updateErrorColumns(); - } - }); - } - - public void updateErrorColumns() { - getWidget().getEditor().setEditorError( - getColumnErrors(), - columnToErrorMessage.keySet()); - } - }); - } - } - - /* - * An editor handler using Vaadin RPC to manage the editor state. - */ - private class CustomEditorHandler implements EditorHandler { - - private EditorServerRpc rpc = getRpcProxy(EditorServerRpc.class); - - private EditorRequest currentRequest = null; - private boolean serverInitiated = false; - - public CustomEditorHandler() { - registerRpc(EditorClientRpc.class, new EditorClientRpc() { - - @Override - public void bind(final int rowIndex) { - // call this deferred to avoid issues with editing on init - Scheduler.get().scheduleDeferred(new ScheduledCommand() { - @Override - public void execute() { - GridConnector.this.getWidget().editRow(rowIndex); - } - }); - } - - @Override - public void cancel(int rowIndex) { - serverInitiated = true; - GridConnector.this.getWidget().cancelEditor(); - } - - @Override - public void confirmBind(final boolean bindSucceeded) { - endRequest(bindSucceeded, null, null); - } - - @Override - public void confirmSave(boolean saveSucceeded, - String errorMessage, List errorColumnsIds) { - endRequest(saveSucceeded, errorMessage, errorColumnsIds); - } - }); - } - - @Override - public void bind(EditorRequest request) { - startRequest(request); - rpc.bind(request.getRowIndex()); - } - - @Override - public void save(EditorRequest request) { - startRequest(request); - rpc.save(request.getRowIndex()); - } - - @Override - public void cancel(EditorRequest request) { - if (!handleServerInitiated(request)) { - // No startRequest as we don't get (or need) - // a confirmation from the server - rpc.cancel(request.getRowIndex()); - } - } - - @Override - public Widget getWidget(Grid.Column column) { - assert column != null; - - if (column instanceof CustomGridColumn) { - AbstractComponentConnector c = ((CustomGridColumn) column) - .getEditorConnector(); - - if (c == null) { - return null; - } - - return c.getWidget(); - } else { - throw new IllegalStateException("Unexpected column type: " - + column.getClass().getName()); - } - } - - /** - * Used to handle the case where the editor calls us because it was - * invoked by the server via RPC and not by the client. In that case, - * the request can be simply synchronously completed. - * - * @param request - * the request object - * @return true if the request was originally triggered by the server, - * false otherwise - */ - private boolean handleServerInitiated(EditorRequest request) { - assert request != null : "Cannot handle null request"; - assert currentRequest == null : "Earlier request not yet finished"; - - if (serverInitiated) { - serverInitiated = false; - request.success(); - return true; - } else { - return false; - } - } - - private void startRequest(EditorRequest request) { - assert currentRequest == null : "Earlier request not yet finished"; - - currentRequest = request; - } - - private void endRequest(boolean succeeded, String errorMessage, - List errorColumnsIds) { - assert currentRequest != null : "Current request was null"; - /* - * Clear current request first to ensure the state is valid if - * another request is made in the callback. - */ - EditorRequest request = currentRequest; - currentRequest = null; - if (succeeded) { - request.success(); - } else { - Collection> errorColumns; - if (errorColumnsIds != null) { - errorColumns = new ArrayList>(); - for (String colId : errorColumnsIds) { - errorColumns.add(columnIdToColumn.get(colId)); - } - } else { - errorColumns = null; - } - - request.failure(errorMessage, errorColumns); - } - } - } - - private class ItemClickHandler - implements BodyClickHandler, BodyDoubleClickHandler { - - @Override - public void onClick(GridClickEvent event) { - if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) { - fireItemClick(event.getTargetCell(), event.getNativeEvent()); - } - } - - @Override - public void onDoubleClick(GridDoubleClickEvent event) { - if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) { - fireItemClick(event.getTargetCell(), event.getNativeEvent()); - } - } - - private void fireItemClick(CellReference cell, - NativeEvent mouseEvent) { - String rowKey = getRowKey((JsonObject) cell.getRow()); - String columnId = getColumnId(cell.getColumn()); - getRpcProxy(GridServerRpc.class).itemClick(rowKey, columnId, - MouseEventDetailsBuilder - .buildMouseEventDetails(mouseEvent)); - } - } - - private ColumnReorderHandler columnReorderHandler = new ColumnReorderHandler() { - - @Override - public void onColumnReorder(ColumnReorderEvent event) { - if (!columnsUpdatedFromState) { - List> columns = getWidget().getColumns(); - final List newColumnOrder = new ArrayList(); - for (Column column : columns) { - if (column instanceof CustomGridColumn) { - newColumnOrder.add(((CustomGridColumn) column).id); - } // the other case would be the multi selection column - } - getRpcProxy(GridServerRpc.class) - .columnsReordered(newColumnOrder, columnOrder); - columnOrder = newColumnOrder; - getState().columnOrder = newColumnOrder; - } - } - }; - - private ColumnVisibilityChangeHandler columnVisibilityChangeHandler = new ColumnVisibilityChangeHandler() { - - @Override - public void onVisibilityChange( - ColumnVisibilityChangeEvent event) { - if (!columnsUpdatedFromState) { - Column column = event.getColumn(); - if (column instanceof CustomGridColumn) { - getRpcProxy(GridServerRpc.class).columnVisibilityChanged( - ((CustomGridColumn) column).id, column.isHidden(), - event.isUserOriginated()); - for (GridColumnState state : getState().columns) { - if (state.id.equals(((CustomGridColumn) column).id)) { - state.hidden = event.isHidden(); - break; - } - } - } else { - getLogger().warning( - "Visibility changed for a unknown column type in Grid: " - + column.toString() + ", type " - + column.getClass()); - } - } - } - }; - - private ColumnResizeHandler columnResizeHandler = new ColumnResizeHandler() { - - @Override - public void onColumnResize(ColumnResizeEvent event) { - if (!columnsUpdatedFromState) { - Column column = event.getColumn(); - if (column instanceof CustomGridColumn) { - getRpcProxy(GridServerRpc.class).columnResized( - ((CustomGridColumn) column).id, - column.getWidthActual()); - } - } - } - }; - - private class CustomDetailsGenerator - implements HeightAwareDetailsGenerator { - - private final Map idToDetailsMap = new HashMap(); - private final Map idToRowIndex = new HashMap(); - - @Override - public Widget getDetails(int rowIndex) { - String id = getId(rowIndex); - if (id == null) { - return null; - } - ComponentConnector componentConnector = idToDetailsMap.get(id); - idToRowIndex.put(id, rowIndex); - - return componentConnector.getWidget(); - } - - @Override - public double getDetailsHeight(int rowIndex) { - // Case of null is handled in the getDetails method and this method - // will not called if it returns null. - String id = getId(rowIndex); - ComponentConnector componentConnector = idToDetailsMap.get(id); - - getLayoutManager().setNeedsMeasureRecursively(componentConnector); - getLayoutManager().layoutNow(); - - return getLayoutManager().getOuterHeightDouble( - componentConnector.getWidget().getElement()); - } - - /** - * Fetches id from the row object that corresponds with the given - * rowIndex. - * - * @since 7.6.1 - * @param rowIndex - * the index of the row for which to fetch the id - * @return id of the row if such id exists, {@code null} otherwise - */ - private String getId(int rowIndex) { - JsonObject row = getWidget().getDataSource().getRow(rowIndex); - - if (!row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE) || row - .getString(GridState.JSONKEY_DETAILS_VISIBLE).isEmpty()) { - return null; - } - - return row.getString(GridState.JSONKEY_DETAILS_VISIBLE); - } - - public void updateConnectorHierarchy(List children) { - Set connectorIds = new HashSet(); - for (ServerConnector child : children) { - if (child instanceof ComponentConnector) { - connectorIds.add(child.getConnectorId()); - idToDetailsMap.put(child.getConnectorId(), - (ComponentConnector) child); - } - } - - Set removedDetails = new HashSet(); - for (Entry entry : idToDetailsMap - .entrySet()) { - ComponentConnector connector = entry.getValue(); - String id = connector.getConnectorId(); - if (!connectorIds.contains(id)) { - removedDetails.add(entry.getKey()); - if (idToRowIndex.containsKey(id)) { - getWidget().setDetailsVisible(idToRowIndex.get(id), - false); - } - } - } - - for (String id : removedDetails) { - idToDetailsMap.remove(id); - idToRowIndex.remove(id); - } - } - } - - /** - * Class for handling scrolling issues with open details. - * - * @since 7.5.2 - */ - private class LazyDetailsScroller implements DeferredWorker { - - /* Timer value tested to work in our test cluster with slow IE8s. */ - private static final int DISABLE_LAZY_SCROLL_TIMEOUT = 1500; - - /* - * Cancels details opening scroll after timeout. Avoids any unexpected - * scrolls via details opening. - */ - private Timer disableScroller = new Timer() { - @Override - public void run() { - targetRow = -1; - } - }; - - private Integer targetRow = -1; - private ScrollDestination destination = null; - - public void scrollToRow(Integer row, ScrollDestination dest) { - targetRow = row; - destination = dest; - disableScroller.schedule(DISABLE_LAZY_SCROLL_TIMEOUT); - } - - /** - * Inform LazyDetailsScroller that a details row has opened on a row. - * - * @param rowIndex - * index of row with details now open - */ - public void detailsOpened(int rowIndex) { - if (targetRow == rowIndex) { - getWidget().scrollToRow(targetRow, destination); - disableScroller.run(); - } - } - - @Override - public boolean isWorkPending() { - return disableScroller.isRunning(); - } - } - - /** - * Maps a generated column id to a grid column instance - */ - private Map columnIdToColumn = new HashMap(); - - private List columnOrder = new ArrayList(); - - /** - * {@link #columnsUpdatedFromState} is set to true when - * {@link #updateColumnOrderFromState(List)} is updating the column order - * for the widget. This flag tells the {@link #columnReorderHandler} to not - * send same data straight back to server. After updates, listener sets the - * value back to false. - */ - private boolean columnsUpdatedFromState; - - private RpcDataSource dataSource; - - /* Used to track Grid editor columns with validation errors */ - private final Map, String> columnToErrorMessage = new HashMap, String>(); - - private ItemClickHandler itemClickHandler = new ItemClickHandler(); - - private String lastKnownTheme = null; - - private final CustomDetailsGenerator customDetailsGenerator = new CustomDetailsGenerator(); - private final CustomStyleGenerator styleGenerator = new CustomStyleGenerator(); - - private final DetailsListener detailsListener = new DetailsListener() { - @Override - public void reapplyDetailsVisibility(final int rowIndex, - final JsonObject row) { - - if (hasDetailsOpen(row)) { - // Command for opening details row. - ScheduledCommand openDetails = new ScheduledCommand() { - @Override - public void execute() { - // Re-apply to force redraw. - getWidget().setDetailsVisible(rowIndex, false); - getWidget().setDetailsVisible(rowIndex, true); - lazyDetailsScroller.detailsOpened(rowIndex); - } - }; - - if (initialChange) { - Scheduler.get().scheduleDeferred(openDetails); - } else { - Scheduler.get().scheduleFinally(openDetails); - } - } else { - getWidget().setDetailsVisible(rowIndex, false); - } - } - - private boolean hasDetailsOpen(JsonObject row) { - return row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE) - && row.getString(GridState.JSONKEY_DETAILS_VISIBLE) != null; - } - }; - - private final LazyDetailsScroller lazyDetailsScroller = new LazyDetailsScroller(); - private final CustomEditorHandler editorHandler = new CustomEditorHandler(); - - /* - * Initially details need to behave a bit differently to allow some - * escalator magic. - */ - private boolean initialChange; - - @Override - @SuppressWarnings("unchecked") - public Grid getWidget() { - return (Grid) super.getWidget(); - } - - @Override - public GridState getState() { - return (GridState) super.getState(); - } - - @Override - protected void init() { - super.init(); - - // All scroll RPC calls are executed finally to avoid issues on init - registerRpc(GridClientRpc.class, new GridClientRpc() { - @Override - public void scrollToStart() { - /* - * no need for lazyDetailsScrollAdjuster, because the start is - * always 0, won't change a bit. - */ - Scheduler.get().scheduleFinally(new ScheduledCommand() { - @Override - public void execute() { - getWidget().scrollToStart(); - } - }); - } - - @Override - public void scrollToEnd() { - Scheduler.get().scheduleFinally(new ScheduledCommand() { - @Override - public void execute() { - getWidget().scrollToEnd(); - // Scrolls further if details opens. - lazyDetailsScroller.scrollToRow(dataSource.size() - 1, - ScrollDestination.END); - } - }); - } - - @Override - public void scrollToRow(final int row, - final ScrollDestination destination) { - Scheduler.get().scheduleFinally(new ScheduledCommand() { - @Override - public void execute() { - getWidget().scrollToRow(row, destination); - // Scrolls a bit further if details opens. - lazyDetailsScroller.scrollToRow(row, destination); - } - }); - } - - @Override - public void recalculateColumnWidths() { - getWidget().recalculateColumnWidths(); - } - }); - - /* Item click events */ - getWidget().addBodyClickHandler(itemClickHandler); - getWidget().addBodyDoubleClickHandler(itemClickHandler); - - /* Style Generators */ - getWidget().setCellStyleGenerator(styleGenerator); - getWidget().setRowStyleGenerator(styleGenerator); - - getWidget().addSortHandler(new SortHandler() { - @Override - public void sort(SortEvent event) { - List order = event.getOrder(); - String[] columnIds = new String[order.size()]; - SortDirection[] directions = new SortDirection[order.size()]; - for (int i = 0; i < order.size(); i++) { - SortOrder sortOrder = order.get(i); - CustomGridColumn column = (CustomGridColumn) sortOrder - .getColumn(); - columnIds[i] = column.id; - - directions[i] = sortOrder.getDirection(); - } - - if (!Arrays.equals(columnIds, getState().sortColumns) - || !Arrays.equals(directions, getState().sortDirs)) { - // Report back to server if changed - getRpcProxy(GridServerRpc.class).sort(columnIds, directions, - event.isUserOriginated()); - } - } - }); - - getWidget().setEditorHandler(editorHandler); - getWidget().addColumnReorderHandler(columnReorderHandler); - getWidget().addColumnVisibilityChangeHandler( - columnVisibilityChangeHandler); - getWidget().addColumnResizeHandler(columnResizeHandler); - - ConnectorFocusAndBlurHandler.addHandlers(this); - - getWidget().setDetailsGenerator(customDetailsGenerator); - getLayoutManager().registerDependency(this, getWidget().getElement()); - - // Handling row height changes - getWidget().addRowHeightChangedHandler(new RowHeightChangedHandler() { - @Override - public void onRowHeightChanged(RowHeightChangedEvent event) { - getLayoutManager() - .setNeedsMeasureRecursively(GridConnector.this); - getLayoutManager().layoutNow(); - } - }); - - layout(); - } - - @Override - public void onStateChanged(final StateChangeEvent stateChangeEvent) { - super.onStateChanged(stateChangeEvent); - - initialChange = stateChangeEvent.isInitialStateChange(); - - // Column updates - if (stateChangeEvent.hasPropertyChanged("columns")) { - - // Remove old columns - purgeRemovedColumns(); - - // Add new columns - for (GridColumnState state : getState().columns) { - if (!columnIdToColumn.containsKey(state.id)) { - addColumnFromStateChangeEvent(state); - } - updateColumnFromStateChangeEvent(state); - } - } - - if (stateChangeEvent.hasPropertyChanged("columnOrder")) { - if (orderNeedsUpdate(getState().columnOrder)) { - updateColumnOrderFromState(getState().columnOrder); - } - } - - // Header and footer - if (stateChangeEvent.hasPropertyChanged("header")) { - updateHeaderFromState(getState().header); - } - - if (stateChangeEvent.hasPropertyChanged("footer")) { - updateFooterFromState(getState().footer); - } - - // Sorting - if (stateChangeEvent.hasPropertyChanged("sortColumns") - || stateChangeEvent.hasPropertyChanged("sortDirs")) { - onSortStateChange(); - } - - // Editor - if (stateChangeEvent.hasPropertyChanged("editorEnabled")) { - getWidget().setEditorEnabled(getState().editorEnabled); - } - - // Frozen columns - if (stateChangeEvent.hasPropertyChanged("frozenColumnCount")) { - getWidget().setFrozenColumnCount(getState().frozenColumnCount); - } - - // Theme features - String activeTheme = getConnection().getUIConnector().getActiveTheme(); - if (lastKnownTheme == null) { - lastKnownTheme = activeTheme; - } else if (!lastKnownTheme.equals(activeTheme)) { - getWidget().resetSizesFromDom(); - lastKnownTheme = activeTheme; - } - } - - private void updateColumnOrderFromState(List stateColumnOrder) { - CustomGridColumn[] columns = new CustomGridColumn[stateColumnOrder - .size()]; - int i = 0; - for (String id : stateColumnOrder) { - columns[i] = columnIdToColumn.get(id); - i++; - } - columnsUpdatedFromState = true; - getWidget().setColumnOrder(columns); - columnsUpdatedFromState = false; - columnOrder = stateColumnOrder; - } - - private boolean orderNeedsUpdate(List stateColumnOrder) { - if (stateColumnOrder.size() == columnOrder.size()) { - for (int i = 0; i < columnOrder.size(); ++i) { - if (!stateColumnOrder.get(i).equals(columnOrder.get(i))) { - return true; - } - } - return false; - } - return true; - } - - private void updateHeaderFromState(GridStaticSectionState state) { - getWidget().setHeaderVisible(state.visible); - - while (getWidget().getHeaderRowCount() > 0) { - getWidget().removeHeaderRow(0); - } - - for (RowState rowState : state.rows) { - HeaderRow row = getWidget().appendHeaderRow(); - - if (rowState.defaultRow) { - getWidget().setDefaultHeaderRow(row); - } - - for (CellState cellState : rowState.cells) { - CustomGridColumn column = columnIdToColumn - .get(cellState.columnId); - updateHeaderCellFromState(row.getCell(column), cellState); - } - - for (Set group : rowState.cellGroups.keySet()) { - Grid.Column[] columns = new Grid.Column[group - .size()]; - CellState cellState = rowState.cellGroups.get(group); - - int i = 0; - for (String columnId : group) { - columns[i] = columnIdToColumn.get(columnId); - i++; - } - - // Set state to be the same as first in group. - updateHeaderCellFromState(row.join(columns), cellState); - } - - row.setStyleName(rowState.styleName); - } - } - - private void updateHeaderCellFromState(HeaderCell cell, - CellState cellState) { - switch (cellState.type) { - case TEXT: - cell.setText(cellState.text); - break; - case HTML: - cell.setHtml(cellState.html); - break; - case WIDGET: - ComponentConnector connector = (ComponentConnector) cellState.connector; - if (connector != null) { - cell.setWidget(connector.getWidget()); - } else { - // This happens if you do setVisible(false) on the component on - // the server side - cell.setWidget(null); - } - break; - default: - throw new IllegalStateException( - "unexpected cell type: " + cellState.type); - } - cell.setStyleName(cellState.styleName); - } - - private void updateFooterFromState(GridStaticSectionState state) { - getWidget().setFooterVisible(state.visible); - - while (getWidget().getFooterRowCount() > 0) { - getWidget().removeFooterRow(0); - } - - for (RowState rowState : state.rows) { - FooterRow row = getWidget().appendFooterRow(); - - for (CellState cellState : rowState.cells) { - CustomGridColumn column = columnIdToColumn - .get(cellState.columnId); - updateFooterCellFromState(row.getCell(column), cellState); - } - - for (Set group : rowState.cellGroups.keySet()) { - Grid.Column[] columns = new Grid.Column[group - .size()]; - CellState cellState = rowState.cellGroups.get(group); - - int i = 0; - for (String columnId : group) { - columns[i] = columnIdToColumn.get(columnId); - i++; - } - - // Set state to be the same as first in group. - updateFooterCellFromState(row.join(columns), cellState); - } - - row.setStyleName(rowState.styleName); - } - } - - private void updateFooterCellFromState(FooterCell cell, - CellState cellState) { - switch (cellState.type) { - case TEXT: - cell.setText(cellState.text); - break; - case HTML: - cell.setHtml(cellState.html); - break; - case WIDGET: - ComponentConnector connector = (ComponentConnector) cellState.connector; - if (connector != null) { - cell.setWidget(connector.getWidget()); - } else { - // This happens if you do setVisible(false) on the component on - // the server side - cell.setWidget(null); - } - break; - default: - throw new IllegalStateException( - "unexpected cell type: " + cellState.type); - } - cell.setStyleName(cellState.styleName); - } - - /** - * Updates a column from a state change event. - * - * @param columnIndex - * The index of the column to update - */ - private void updateColumnFromStateChangeEvent(GridColumnState columnState) { - CustomGridColumn column = columnIdToColumn.get(columnState.id); - - columnsUpdatedFromState = true; - updateColumnFromState(column, columnState); - columnsUpdatedFromState = false; - } - - /** - * Adds a new column to the grid widget from a state change event - * - * @param columnIndex - * The index of the column, according to how it - */ - private void addColumnFromStateChangeEvent(GridColumnState state) { - @SuppressWarnings("unchecked") - CustomGridColumn column = new CustomGridColumn(state.id, - ((AbstractRendererConnector) state.rendererConnector)); - columnIdToColumn.put(state.id, column); - - /* - * Add column to grid. Reordering is handled as a separate problem. - */ - getWidget().addColumn(column); - columnOrder.add(state.id); - } - - /** - * Updates the column values from a state - * - * @param column - * The column to update - * @param state - * The state to get the data from - */ - @SuppressWarnings("unchecked") - private static void updateColumnFromState(CustomGridColumn column, - GridColumnState state) { - column.setWidth(state.width); - column.setMinimumWidth(state.minWidth); - column.setMaximumWidth(state.maxWidth); - column.setExpandRatio(state.expandRatio); - - assert state.rendererConnector instanceof AbstractRendererConnector : "GridColumnState.rendererConnector is invalid (not subclass of AbstractRendererConnector)"; - column.setRenderer( - (AbstractRendererConnector) state.rendererConnector); - - column.setSortable(state.sortable); - - column.setResizable(state.resizable); - - column.setHeaderCaption(state.headerCaption); - - column.setHidden(state.hidden); - column.setHidable(state.hidable); - column.setHidingToggleCaption(state.hidingToggleCaption); - - column.setEditable(state.editable); - column.setEditorConnector( - (AbstractComponentConnector) state.editorConnector); - } - - /** - * Removes any orphan columns that has been removed from the state from the - * grid - */ - private void purgeRemovedColumns() { - - // Get columns still registered in the state - Set columnsInState = new HashSet(); - for (GridColumnState columnState : getState().columns) { - columnsInState.add(columnState.id); - } - - // Remove column no longer in state - Iterator columnIdIterator = columnIdToColumn.keySet() - .iterator(); - while (columnIdIterator.hasNext()) { - String id = columnIdIterator.next(); - if (!columnsInState.contains(id)) { - CustomGridColumn column = columnIdToColumn.get(id); - columnIdIterator.remove(); - getWidget().removeColumn(column); - columnOrder.remove(id); - } - } - } - - public void setDataSource(RpcDataSource dataSource) { - this.dataSource = dataSource; - getWidget().setDataSource(this.dataSource); - } - - private void onSortStateChange() { - List sortOrder = new ArrayList(); - - String[] sortColumns = getState().sortColumns; - SortDirection[] sortDirs = getState().sortDirs; - - for (int i = 0; i < sortColumns.length; i++) { - sortOrder.add(new SortOrder(columnIdToColumn.get(sortColumns[i]), - sortDirs[i])); - } - - getWidget().setSortOrder(sortOrder); - } - - private Logger getLogger() { - return Logger.getLogger(getClass().getName()); - } - - /** - * Gets the row key for a row object. - * - * @param row - * the row object - * @return the key for the given row - */ - public String getRowKey(JsonObject row) { - final Object key = dataSource.getRowKey(row); - assert key instanceof String : "Internal key was not a String but a " - + key.getClass().getSimpleName() + " (" + key + ")"; - return (String) key; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin.client - * .ComponentConnector) - */ - @Override - public void updateCaption(ComponentConnector connector) { - // TODO Auto-generated method stub - } - - @Override - public void onConnectorHierarchyChange( - ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { - customDetailsGenerator.updateConnectorHierarchy(getChildren()); - } - - public String getColumnId(Grid.Column column) { - if (column instanceof CustomGridColumn) { - return ((CustomGridColumn) column).id; - } - return null; - } - - @Override - public void layout() { - getWidget().onResize(); - } - - @Override - public boolean isWorkPending() { - return lazyDetailsScroller.isWorkPending(); - } - - /** - * Gets the listener used by this connector for tracking when row detail - * visibility changes. - * - * @since 7.5.0 - * @return the used details listener - */ - public DetailsListener getDetailsListener() { - return detailsListener; - } - - @Override - public boolean hasTooltip() { - return getState().hasDescriptions || super.hasTooltip(); - } - - @Override - public TooltipInfo getTooltipInfo(Element element) { - CellReference cell = getWidget().getCellReference(element); - - if (cell != null) { - JsonObject row = cell.getRow(); - if (row == null) { - return null; - } - - Column column = cell.getColumn(); - if (!(column instanceof CustomGridColumn)) { - // Selection checkbox column - return null; - } - CustomGridColumn c = (CustomGridColumn) column; - - JsonObject cellDescriptions = row - .getObject(GridState.JSONKEY_CELLDESCRIPTION); - - if (cellDescriptions != null && cellDescriptions.hasKey(c.id)) { - return new TooltipInfo(cellDescriptions.getString(c.id)); - } else if (row.hasKey(GridState.JSONKEY_ROWDESCRIPTION)) { - return new TooltipInfo( - row.getString(GridState.JSONKEY_ROWDESCRIPTION)); - } else { - return null; - } - } - - return super.getTooltipInfo(element); - } - - @Override - protected void sendContextClickEvent(MouseEventDetails details, - EventTarget eventTarget) { - // if element is the resize indicator, ignore the event - if (isResizeHandle(eventTarget)) { - WidgetUtil.clearTextSelection(); - return; - } - - EventCellReference eventCell = getWidget().getEventCell(); - - Section section = eventCell.getSection(); - String rowKey = null; - if (eventCell.isBody() && eventCell.getRow() != null) { - rowKey = getRowKey(eventCell.getRow()); - } - - String columnId = getColumnId(eventCell.getColumn()); - - getRpcProxy(GridServerRpc.class).contextClick(eventCell.getRowIndex(), - rowKey, columnId, section, details); - - WidgetUtil.clearTextSelection(); - } - - private boolean isResizeHandle(EventTarget eventTarget) { - if (Element.is(eventTarget)) { - Element e = Element.as(eventTarget); - if (e.getClassName().contains("-column-resize-handle")) { - return true; - } - } - return false; - } - - /** - * Creates a concatenation of all columns errors for Editor. - * - * @since 7.6 - * @return displayed error string - */ - private String getColumnErrors() { - List errors = new ArrayList(); - - for (Grid.Column c : getWidget().getColumns()) { - if (!(c instanceof CustomGridColumn)) { - continue; - } - - String error = columnToErrorMessage.get(c); - if (error != null) { - errors.add(error); - } - } - - String result = ""; - Iterator i = errors.iterator(); - while (i.hasNext()) { - result += i.next(); - if (i.hasNext()) { - result += ", "; - } - } - return result.isEmpty() ? null : result; - } - -} diff --git a/client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java deleted file mode 100644 index 7949eeab3c..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.client.connectors; - -import com.google.web.bindery.event.shared.HandlerRegistration; -import com.vaadin.client.communication.JsonDecoder; -import com.vaadin.client.metadata.TypeDataStore; -import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; -import com.vaadin.client.renderers.ImageRenderer; -import com.vaadin.shared.communication.URLReference; -import com.vaadin.shared.ui.Connect; - -import elemental.json.JsonObject; -import elemental.json.JsonValue; - -/** - * A connector for {@link ImageRenderer}. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.renderers.ImageRenderer.class) -public class ImageRendererConnector extends ClickableRendererConnector { - - @Override - public ImageRenderer getRenderer() { - return (ImageRenderer) super.getRenderer(); - } - - @Override - public String decode(JsonValue value) { - URLReference reference = (URLReference) JsonDecoder.decodeValue( - TypeDataStore.getType(URLReference.class), value, null, - getConnection()); - - return reference != null ? reference.getURL() : null; - } - - @Override - protected HandlerRegistration addClickHandler( - RendererClickHandler handler) { - return getRenderer().addClickHandler(handler); - } -} diff --git a/client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java deleted file mode 100644 index 0194678ef1..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * 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.client.connectors; - -import java.util.ArrayList; -import java.util.Collection; - -import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.core.client.JsArrayString; -import com.google.gwt.dom.client.NativeEvent; -import com.vaadin.client.JavaScriptConnectorHelper; -import com.vaadin.client.Util; -import com.vaadin.client.communication.HasJavaScriptConnectorHelper; -import com.vaadin.client.renderers.ComplexRenderer; -import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widget.grid.CellReference; -import com.vaadin.client.widget.grid.RendererCellReference; -import com.vaadin.shared.JavaScriptExtensionState; -import com.vaadin.shared.ui.Connect; -import com.vaadin.ui.renderers.AbstractJavaScriptRenderer; - -import elemental.json.JsonObject; -import elemental.json.JsonValue; - -/** - * Connector for server-side renderer implemented using JavaScript. - * - * @since 7.4 - * @author Vaadin Ltd - */ -// This is really typed to , but because of the way native strings -// are not always instanceof JsonValue, we need to accept Object -@Connect(AbstractJavaScriptRenderer.class) -public class JavaScriptRendererConnector - extends AbstractRendererConnector - implements HasJavaScriptConnectorHelper { - private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper( - this); - - private final JavaScriptObject cellReferenceWrapper = createCellReferenceWrapper(); - - @Override - protected void init() { - super.init(); - helper.init(); - - addGetRowKey(helper.getConnectorWrapper()); - } - - private static native JavaScriptObject createCellReferenceWrapper() - /*-{ - var reference = {}; - - var setProperty = function(name, getter, setter) { - var descriptor = { - get: getter - } - if (setter) { - descriptor.set = setter; - } - Object.defineProperty(reference, name, descriptor); - }; - - setProperty("element", function() { - return reference.target.@CellReference::getElement()(); - }, null); - - setProperty("rowIndex", function() { - return reference.target.@CellReference::getRowIndex()(); - }, null); - - setProperty("columnIndex", function() { - return reference.target.@CellReference::getColumnIndex()(); - }, null); - - setProperty("colSpan", function() { - return reference.target.@RendererCellReference::getColSpan()(); - }, function(colSpan) { - reference.target.@RendererCellReference::setColSpan(*)(colSpan); - }); - - return reference; - }-*/; - - @Override - public JavaScriptExtensionState getState() { - return (JavaScriptExtensionState) super.getState(); - } - - private native void addGetRowKey(JavaScriptObject wrapper) - /*-{ - var self = this; - wrapper.getRowKey = $entry(function(rowIndex) { - return @JavaScriptRendererConnector::findRowKey(*)(self, rowIndex); - }); - }-*/; - - private static String findRowKey(JavaScriptRendererConnector connector, - int rowIndex) { - GridConnector gc = (GridConnector) connector.getParent(); - JsonObject row = gc.getWidget().getDataSource().getRow(rowIndex); - return connector.getRowKey(row); - } - - private boolean hasFunction(String name) { - return hasFunction(helper.getConnectorWrapper(), name); - } - - private static native boolean hasFunction(JavaScriptObject wrapper, - String name) - /*-{ - return typeof wrapper[name] === 'function'; - }-*/; - - @Override - protected Renderer createRenderer() { - helper.ensureJavascriptInited(); - - if (!hasFunction("render")) { - throw new RuntimeException( - "JavaScriptRenderer " + helper.getInitFunctionName() - + " must have a function named 'render'"); - } - - final boolean hasInit = hasFunction("init"); - final boolean hasDestroy = hasFunction("destroy"); - final boolean hasOnActivate = hasFunction("onActivate"); - final boolean hasGetConsumedEvents = hasFunction("getConsumedEvents"); - final boolean hasOnBrowserEvent = hasFunction("onBrowserEvent"); - - return new ComplexRenderer() { - @Override - public void render(RendererCellReference cell, Object data) { - if (data instanceof JsonValue) { - data = Util.json2jso((JsonValue) data); - } - render(helper.getConnectorWrapper(), getJsCell(cell), data); - } - - private JavaScriptObject getJsCell(CellReference cell) { - updateCellReference(cellReferenceWrapper, cell); - return cellReferenceWrapper; - } - - public native void render(JavaScriptObject wrapper, - JavaScriptObject cell, Object data) - /*-{ - wrapper.render(cell, data); - }-*/; - - @Override - public void init(RendererCellReference cell) { - if (hasInit) { - init(helper.getConnectorWrapper(), getJsCell(cell)); - } - } - - private native void init(JavaScriptObject wrapper, - JavaScriptObject cell) - /*-{ - wrapper.init(cell); - }-*/; - - private native void updateCellReference( - JavaScriptObject cellWrapper, CellReference target) - /*-{ - cellWrapper.target = target; - }-*/; - - @Override - public void destroy(RendererCellReference cell) { - if (hasDestroy) { - destory(helper.getConnectorWrapper(), getJsCell(cell)); - } else { - super.destroy(cell); - } - } - - private native void destory(JavaScriptObject wrapper, - JavaScriptObject cell) - /*-{ - wrapper.destory(cell); - }-*/; - - @Override - public boolean onActivate(CellReference cell) { - if (hasOnActivate) { - return onActivate(helper.getConnectorWrapper(), - getJsCell(cell)); - } else { - return super.onActivate(cell); - } - } - - private native boolean onActivate(JavaScriptObject wrapper, - JavaScriptObject cell) - /*-{ - return !!wrapper.onActivate(cell); - }-*/; - - @Override - public Collection getConsumedEvents() { - if (hasGetConsumedEvents) { - JsArrayString events = getConsumedEvents( - helper.getConnectorWrapper()); - - ArrayList list = new ArrayList( - events.length()); - for (int i = 0; i < events.length(); i++) { - list.add(events.get(i)); - } - return list; - } else { - return super.getConsumedEvents(); - } - } - - private native JsArrayString getConsumedEvents( - JavaScriptObject wrapper) - /*-{ - var rawEvents = wrapper.getConsumedEvents(); - var events = []; - for(var i = 0; i < rawEvents.length; i++) { - events[i] = ""+rawEvents[i]; - } - return events; - }-*/; - - @Override - public boolean onBrowserEvent(CellReference cell, - NativeEvent event) { - if (hasOnBrowserEvent) { - return onBrowserEvent(helper.getConnectorWrapper(), - getJsCell(cell), event); - } else { - return super.onBrowserEvent(cell, event); - } - } - - private native boolean onBrowserEvent(JavaScriptObject wrapper, - JavaScriptObject cell, NativeEvent event) - /*-{ - return !!wrapper.onBrowserEvent(cell, event); - }-*/; - }; - } - - @Override - public Object decode(JsonValue value) { - // Let the js logic decode the raw json that the server sent - return value; - } - - @Override - public void onUnregister() { - super.onUnregister(); - helper.onUnregister(); - } - - @Override - public JavaScriptConnectorHelper getJavascriptConnectorHelper() { - return helper; - } -} - diff --git a/client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java deleted file mode 100644 index e7494737cb..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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.client.connectors; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.user.client.ui.CheckBox; -import com.vaadin.client.ServerConnector; -import com.vaadin.client.annotations.OnStateChange; -import com.vaadin.client.data.DataSource; -import com.vaadin.client.data.DataSource.RowHandle; -import com.vaadin.client.renderers.ComplexRenderer; -import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widget.grid.DataAvailableEvent; -import com.vaadin.client.widget.grid.DataAvailableHandler; -import com.vaadin.client.widget.grid.events.SelectAllEvent; -import com.vaadin.client.widget.grid.events.SelectAllHandler; -import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; -import com.vaadin.client.widget.grid.selection.SelectionModel; -import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; -import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; -import com.vaadin.client.widgets.Grid; -import com.vaadin.client.widgets.Grid.HeaderCell; -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.Range; -import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; -import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; -import com.vaadin.ui.LegacyGrid.MultiSelectionModel; - -import elemental.json.JsonObject; - -/** - * Connector for server-side {@link MultiSelectionModel}. - * - * @since 7.6 - * @author Vaadin Ltd - */ -@Connect(MultiSelectionModel.class) -public class MultiSelectionModelConnector extends - AbstractSelectionModelConnector> { - - private Multi selectionModel = createSelectionModel(); - private SpaceSelectHandler spaceHandler; - - @Override - protected void extend(ServerConnector target) { - getGrid().setSelectionModel(selectionModel); - spaceHandler = new SpaceSelectHandler(getGrid()); - } - - @Override - public void onUnregister() { - spaceHandler.removeHandler(); - } - - @Override - protected Multi createSelectionModel() { - return new MultiSelectionModel(); - } - - @Override - public MultiSelectionModelState getState() { - return (MultiSelectionModelState) super.getState(); - } - - @OnStateChange("allSelected") - void updateSelectAllCheckbox() { - if (selectionModel.getSelectionColumnRenderer() != null) { - HeaderCell cell = getGrid().getDefaultHeaderRow() - .getCell(getGrid().getColumn(0)); - CheckBox widget = (CheckBox) cell.getWidget(); - widget.setValue(getState().allSelected, false); - } - } - - protected class MultiSelectionModel extends AbstractSelectionModel - implements SelectionModel.Multi.Batched { - - private ComplexRenderer renderer = null; - private Set> selected = new HashSet>(); - private Set> deselected = new HashSet>(); - private HandlerRegistration selectAll; - private HandlerRegistration dataAvailable; - private Range availableRows; - private boolean batchSelect = false; - - @Override - public void setGrid(Grid grid) { - super.setGrid(grid); - if (grid != null) { - renderer = createSelectionColumnRenderer(grid); - selectAll = getGrid().addSelectAllHandler( - new SelectAllHandler() { - - @Override - public void onSelectAll( - SelectAllEvent event) { - selectAll(); - } - }); - dataAvailable = getGrid() - .addDataAvailableHandler(new DataAvailableHandler() { - - @Override - public void onDataAvailable( - DataAvailableEvent event) { - availableRows = event.getAvailableRows(); - } - }); - } else if (renderer != null) { - selectAll.removeHandler(); - dataAvailable.removeHandler(); - renderer = null; - } - } - - /** - * Creates a selection column renderer. This method can be overridden to - * use a custom renderer or use {@code null} to disable the selection - * column. - * - * @param grid - * the grid for this selection model - * @return selection column renderer or {@code null} if not needed - */ - protected ComplexRenderer createSelectionColumnRenderer( - Grid grid) { - return new MultiSelectionRenderer(grid); - } - - /** - * Selects all available rows, sends request to server to select - * everything. - */ - public void selectAll() { - assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; - - DataSource dataSource = getGrid().getDataSource(); - for (int i = availableRows.getStart(); i < availableRows - .getEnd(); ++i) { - final JsonObject row = dataSource.getRow(i); - if (row != null) { - RowHandle handle = dataSource.getHandle(row); - markAsSelected(handle, true); - } - } - - getRpcProxy(MultiSelectionModelServerRpc.class).selectAll(); - } - - @Override - public Renderer getSelectionColumnRenderer() { - return renderer; - } - - /** - * {@inheritDoc} - * - * @return {@code false} if rows is empty, else {@code true} - */ - @Override - public boolean select(JsonObject... rows) { - return select(Arrays.asList(rows)); - } - - /** - * {@inheritDoc} - * - * @return {@code false} if rows is empty, else {@code true} - */ - @Override - public boolean deselect(JsonObject... rows) { - return deselect(Arrays.asList(rows)); - } - - /** - * {@inheritDoc} - * - * @return always {@code true} - */ - @Override - public boolean deselectAll() { - assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; - - DataSource dataSource = getGrid().getDataSource(); - for (int i = availableRows.getStart(); i < availableRows - .getEnd(); ++i) { - final JsonObject row = dataSource.getRow(i); - if (row != null) { - RowHandle handle = dataSource.getHandle(row); - markAsSelected(handle, false); - } - } - - getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll(); - - return true; - } - - /** - * {@inheritDoc} - * - * @return {@code false} if rows is empty, else {@code true} - */ - @Override - public boolean select(Collection rows) { - if (rows.isEmpty()) { - return false; - } - - for (JsonObject row : rows) { - RowHandle rowHandle = getRowHandle(row); - if (markAsSelected(rowHandle, true)) { - selected.add(rowHandle); - } - } - - if (!isBeingBatchSelected()) { - sendSelected(); - } - return true; - } - - /** - * Marks the given row to be selected or deselected. Returns true if the - * value actually changed. - *

- * Note: If selection model is in batch select state, the row will be - * pinned on select. - * - * @param row - * row handle - * @param selected - * {@code true} if row should be selected; {@code false} if - * not - * @return {@code true} if selected status changed; {@code false} if not - */ - protected boolean markAsSelected(RowHandle row, - boolean selected) { - if (selected && !isSelected(row.getRow())) { - row.getRow().put(GridState.JSONKEY_SELECTED, true); - } else if (!selected && isSelected(row.getRow())) { - row.getRow().remove(GridState.JSONKEY_SELECTED); - } else { - return false; - } - - row.updateRow(); - - if (isBeingBatchSelected()) { - row.pin(); - } - return true; - } - - /** - * {@inheritDoc} - * - * @return {@code false} if rows is empty, else {@code true} - */ - @Override - public boolean deselect(Collection rows) { - if (rows.isEmpty()) { - return false; - } - - for (JsonObject row : rows) { - RowHandle rowHandle = getRowHandle(row); - if (markAsSelected(rowHandle, false)) { - deselected.add(rowHandle); - } - } - - if (!isBeingBatchSelected()) { - sendDeselected(); - } - return true; - } - - /** - * Sends a deselect RPC call to server-side containing all deselected - * rows. Unpins any pinned rows. - */ - private void sendDeselected() { - getRpcProxy(MultiSelectionModelServerRpc.class) - .deselect(getRowKeys(deselected)); - - if (isBeingBatchSelected()) { - for (RowHandle row : deselected) { - row.unpin(); - } - } - - deselected.clear(); - } - - /** - * Sends a select RPC call to server-side containing all selected rows. - * Unpins any pinned rows. - */ - private void sendSelected() { - getRpcProxy(MultiSelectionModelServerRpc.class) - .select(getRowKeys(selected)); - - if (isBeingBatchSelected()) { - for (RowHandle row : selected) { - row.unpin(); - } - } - - selected.clear(); - } - - private List getRowKeys(Set> handles) { - List keys = new ArrayList(); - for (RowHandle handle : handles) { - keys.add(getRowKey(handle.getRow())); - } - return keys; - } - - private Set getRows(Set> handles) { - Set rows = new HashSet(); - for (RowHandle handle : handles) { - rows.add(handle.getRow()); - } - return rows; - } - - @Override - public void startBatchSelect() { - assert selected.isEmpty() - && deselected.isEmpty() : "Row caches were not clear."; - batchSelect = true; - } - - @Override - public void commitBatchSelect() { - assert batchSelect : "Not batch selecting."; - if (!selected.isEmpty()) { - sendSelected(); - } - - if (!deselected.isEmpty()) { - sendDeselected(); - } - batchSelect = false; - } - - @Override - public boolean isBeingBatchSelected() { - return batchSelect; - } - - @Override - public Collection getSelectedRowsBatch() { - return Collections.unmodifiableSet(getRows(selected)); - } - - @Override - public Collection getDeselectedRowsBatch() { - return Collections.unmodifiableSet(getRows(deselected)); - } - } -} diff --git a/client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java deleted file mode 100644 index 1a080f5082..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.client.ServerConnector; -import com.vaadin.client.widget.grid.selection.SelectionModel; -import com.vaadin.client.widget.grid.selection.SelectionModelNone; -import com.vaadin.shared.ui.Connect; -import com.vaadin.ui.LegacyGrid.NoSelectionModel; - -import elemental.json.JsonObject; - -/** - * Connector for server-side {@link NoSelectionModel}. - * - * @since 7.6 - * @author Vaadin Ltd - */ -@Connect(NoSelectionModel.class) -public class NoSelectionModelConnector - extends AbstractSelectionModelConnector> { - - @Override - protected void extend(ServerConnector target) { - getGrid().setSelectionModel(createSelectionModel()); - } - - @Override - protected SelectionModel createSelectionModel() { - return new SelectionModelNone(); - } -} \ No newline at end of file diff --git a/client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java deleted file mode 100644 index ff16047b0d..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.shared.ui.Connect; - -/** - * A connector for {@link com.vaadin.ui.components.grid.renderers.NumberRenderer - * NumberRenderer} . - *

- * The server-side Renderer operates on numbers, but the data is serialized as a - * string, and displayed as-is on the client side. This is to be able to support - * the server's locale. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.renderers.NumberRenderer.class) -public class NumberRendererConnector extends TextRendererConnector { - // no implementation needed -} diff --git a/client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java deleted file mode 100644 index e298c423e7..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.client.renderers.ProgressBarRenderer; -import com.vaadin.shared.ui.Connect; - -/** - * A connector for {@link ProgressBarRenderer}. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.renderers.ProgressBarRenderer.class) -public class ProgressBarRendererConnector - extends AbstractRendererConnector { - - @Override - public ProgressBarRenderer getRenderer() { - return (ProgressBarRenderer) super.getRenderer(); - } -} diff --git a/client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java b/client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java deleted file mode 100644 index 52fa8a2e48..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * 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.client.connectors; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.vaadin.client.ServerConnector; -import com.vaadin.client.data.AbstractRemoteDataSource; -import com.vaadin.client.extensions.AbstractExtensionConnector; -import com.vaadin.shared.data.DataProviderRpc; -import com.vaadin.shared.data.DataRequestRpc; -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.Range; - -import elemental.json.Json; -import elemental.json.JsonArray; -import elemental.json.JsonObject; - -/** - * Connects a Vaadin server-side container data source to a Grid. This is - * currently implemented as an Extension hardcoded to support a specific - * connector type. This will be changed once framework support for something - * more flexible has been implemented. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.server.communication.data.RpcDataProviderExtension.class) -public class RpcDataSourceConnector extends AbstractExtensionConnector { - - /** - * A callback interface to let {@link GridConnector} know that detail - * visibilities might have changed. - * - * @since 7.5.0 - * @author Vaadin Ltd - */ - interface DetailsListener { - - /** - * A request to verify (and correct) the visibility for a row, given - * updated metadata. - * - * @param rowIndex - * the index of the row that should be checked - * @param row - * the row object to check visibility for - * @see GridState#JSONKEY_DETAILS_VISIBLE - */ - void reapplyDetailsVisibility(int rowIndex, JsonObject row); - } - - public class RpcDataSource extends AbstractRemoteDataSource { - - protected RpcDataSource() { - registerRpc(DataProviderRpc.class, new DataProviderRpc() { - @Override - public void setRowData(int firstRow, JsonArray rowArray) { - ArrayList rows = new ArrayList( - rowArray.length()); - for (int i = 0; i < rowArray.length(); i++) { - JsonObject rowObject = rowArray.getObject(i); - rows.add(rowObject); - } - - RpcDataSource.this.setRowData(firstRow, rows); - } - - @Override - public void removeRowData(int firstRow, int count) { - RpcDataSource.this.removeRowData(firstRow, count); - } - - @Override - public void insertRowData(int firstRow, int count) { - RpcDataSource.this.insertRowData(firstRow, count); - } - - @Override - public void resetDataAndSize(int size) { - RpcDataSource.this.resetDataAndSize(size); - } - - @Override - public void updateRowData(JsonArray rowArray) { - for (int i = 0; i < rowArray.length(); ++i) { - RpcDataSource.this.updateRowData(rowArray.getObject(i)); - } - } - }); - } - - private DataRequestRpc rpcProxy = getRpcProxy(DataRequestRpc.class); - private DetailsListener detailsListener; - private JsonArray droppedRowKeys = Json.createArray(); - - @Override - protected void requestRows(int firstRowIndex, int numberOfRows, - RequestRowsCallback callback) { - if (droppedRowKeys.length() > 0) { - rpcProxy.dropRows(droppedRowKeys); - droppedRowKeys = Json.createArray(); - } - - /* - * If you're looking at this code because you want to learn how to - * use AbstactRemoteDataSource, please look somewhere else instead. - * - * We're not doing things in the conventional way with the callback - * here since Vaadin doesn't directly support RPC with return - * values. We're instead asking the server to push us some data, and - * when we receive pushed data, we just push it along to the - * underlying cache in the same way no matter if it was a genuine - * push or just a result of us requesting rows. - */ - - Range cached = getCachedRange(); - - rpcProxy.requestRows(firstRowIndex, numberOfRows, cached.getStart(), - cached.length()); - - /* - * Show the progress indicator if there is a pending data request - * and some of the visible rows are being requested. The RPC in - * itself will not trigger the indicator since it might just fetch - * some rows in the background to fill the cache. - * - * The indicator will be hidden by the framework when the response - * is received (unless another request is already on its way at that - * point). - */ - if (getRequestedAvailability().intersects( - Range.withLength(firstRowIndex, numberOfRows))) { - getConnection().getLoadingIndicator().ensureTriggered(); - } - } - - @Override - public void ensureAvailability(int firstRowIndex, int numberOfRows) { - super.ensureAvailability(firstRowIndex, numberOfRows); - - /* - * We trigger the indicator already at this point since the actual - * RPC will not be sent right away when waiting for the response to - * a previous request. - * - * Only triggering here would not be enough since the check that - * sets isWaitingForData is deferred. We don't want to trigger the - * loading indicator here if we don't know that there is actually a - * request going on since some other bug might then cause the - * loading indicator to not be hidden. - */ - if (isWaitingForData() - && !Range.withLength(firstRowIndex, numberOfRows) - .isSubsetOf(getCachedRange())) { - getConnection().getLoadingIndicator().ensureTriggered(); - } - } - - @Override - public String getRowKey(JsonObject row) { - if (row.hasKey(GridState.JSONKEY_ROWKEY)) { - return row.getString(GridState.JSONKEY_ROWKEY); - } else { - return null; - } - } - - public RowHandle getHandleByKey(Object key) { - JsonObject row = Json.createObject(); - row.put(GridState.JSONKEY_ROWKEY, (String) key); - return new RowHandleImpl(row, key); - } - - @Override - protected void unpinHandle(RowHandleImpl handle) { - // Row data is no longer available after it has been unpinned. - String key = getRowKey(handle.getRow()); - super.unpinHandle(handle); - if (!handle.isPinned()) { - if (indexOfKey(key) == -1) { - // Row out of view has been unpinned. drop it - droppedRowKeys.set(droppedRowKeys.length(), key); - } - } - } - - void setDetailsListener(DetailsListener detailsListener) { - this.detailsListener = detailsListener; - } - - @Override - protected void setRowData(int firstRowIndex, List rowData) { - super.setRowData(firstRowIndex, rowData); - - /* - * Intercepting details information from the data source, rerouting - * them back to the GridConnector (as a details listener) - */ - for (int i = 0; i < rowData.size(); i++) { - detailsListener.reapplyDetailsVisibility(firstRowIndex + i, - rowData.get(i)); - } - } - - /** - * Updates row data based on row key. - * - * @since 7.6 - * @param row - * new row object - */ - protected void updateRowData(JsonObject row) { - int index = indexOfKey(getRowKey(row)); - if (index >= 0) { - setRowData(index, Collections.singletonList(row)); - } - } - - @Override - protected void onDropFromCache(int rowIndex, JsonObject row) { - if (!isPinned(row)) { - droppedRowKeys.set(droppedRowKeys.length(), getRowKey(row)); - } - } - } - - private final RpcDataSource dataSource = new RpcDataSource(); - - @Override - protected void extend(ServerConnector target) { - GridConnector gridConnector = (GridConnector) target; - dataSource.setDetailsListener(gridConnector.getDetailsListener()); - gridConnector.setDataSource(dataSource); - } -} diff --git a/client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java b/client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java deleted file mode 100644 index 980c4458d4..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.client.ServerConnector; -import com.vaadin.client.annotations.OnStateChange; -import com.vaadin.client.data.DataSource.RowHandle; -import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widget.grid.selection.ClickSelectHandler; -import com.vaadin.client.widget.grid.selection.SelectionModel; -import com.vaadin.client.widget.grid.selection.SelectionModel.Single; -import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; -import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; -import com.vaadin.ui.LegacyGrid.SingleSelectionModel; - -import elemental.json.JsonObject; - -/** - * Connector for server-side {@link SingleSelectionModel}. - * - * @since 7.6 - * @author Vaadin Ltd - */ -@Connect(SingleSelectionModel.class) -public class SingleSelectionModelConnector extends - AbstractSelectionModelConnector> { - - private SpaceSelectHandler spaceHandler; - private ClickSelectHandler clickHandler; - private Single selectionModel = createSelectionModel(); - - @Override - protected void extend(ServerConnector target) { - getGrid().setSelectionModel(selectionModel); - spaceHandler = new SpaceSelectHandler(getGrid()); - clickHandler = new ClickSelectHandler(getGrid()); - } - - @Override - public SingleSelectionModelState getState() { - return (SingleSelectionModelState) super.getState(); - } - - @Override - public void onUnregister() { - spaceHandler.removeHandler(); - clickHandler.removeHandler(); - - super.onUnregister(); - } - - @Override - protected Single createSelectionModel() { - return new SingleSelectionModel(); - } - - @OnStateChange("deselectAllowed") - void updateDeselectAllowed() { - selectionModel.setDeselectAllowed(getState().deselectAllowed); - } - - /** - * SingleSelectionModel without a selection column renderer. - */ - public class SingleSelectionModel extends AbstractSelectionModel - implements SelectionModel.Single { - - private RowHandle selectedRow; - private boolean deselectAllowed; - - @Override - public Renderer getSelectionColumnRenderer() { - return null; - } - - @Override - public void reset() { - super.reset(); - - // Clean up selected row - if (selectedRow != null) { - clearSelectedRow(); - } - } - - @Override - public boolean select(JsonObject row) { - boolean changed = false; - - if (row == null && !isDeselectAllowed()) { - // Attempting to deselect, even though it's not allowed. - } else { - if (selectedRow != null) { - // Check if currently re-selected row was deselected from - // the server. - if (row != null && getRowHandle(row).equals(selectedRow)) { - if (selectedRow.getRow() - .hasKey(GridState.JSONKEY_SELECTED)) { - // Everything is OK, no need to do anything. - return false; - } - } - - // Remove old selected row - clearSelectedRow(); - changed = true; - } - - if (row != null) { - // Select the new row. - setSelectedRow(row); - changed = true; - } - } - - if (changed) { - getRpcProxy(SingleSelectionModelServerRpc.class) - .select(getRowKey(row)); - } - - return changed; - } - - private void setSelectedRow(JsonObject row) { - selectedRow = getRowHandle(row); - selectedRow.pin(); - selectedRow.getRow().put(GridState.JSONKEY_SELECTED, true); - selectedRow.updateRow(); - } - - private void clearSelectedRow() { - selectedRow.getRow().remove(GridState.JSONKEY_SELECTED); - selectedRow.updateRow(); - selectedRow.unpin(); - selectedRow = null; - } - - @Override - public boolean deselect(JsonObject row) { - if (getRowHandle(row).equals(selectedRow)) { - select(null); - } - return false; - } - - @Override - public JsonObject getSelectedRow() { - throw new UnsupportedOperationException( - "This client-side selection model " - + getClass().getSimpleName() - + " does not know selected row."); - } - - @Override - public void setDeselectAllowed(boolean deselectAllowed) { - this.deselectAllowed = deselectAllowed; - } - - @Override - public boolean isDeselectAllowed() { - return deselectAllowed; - } - } -} \ No newline at end of file diff --git a/client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java deleted file mode 100644 index d0fbe3c010..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.client.renderers.TextRenderer; -import com.vaadin.shared.ui.Connect; - -/** - * A connector for {@link TextRenderer}. - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.renderers.TextRenderer.class) -public class TextRendererConnector extends AbstractRendererConnector { - - @Override - public TextRenderer getRenderer() { - return (TextRenderer) super.getRenderer(); - } -} diff --git a/client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java b/client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java deleted file mode 100644 index a26627a2ba..0000000000 --- a/client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.client.connectors; - -import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widget.grid.RendererCellReference; -import com.vaadin.shared.ui.Connect; - -/** - * A connector for {@link UnsafeHtmlRenderer} - * - * @since 7.4 - * @author Vaadin Ltd - */ -@Connect(com.vaadin.ui.renderers.HtmlRenderer.class) -public class UnsafeHtmlRendererConnector - extends AbstractRendererConnector { - - public static class UnsafeHtmlRenderer implements Renderer { - @Override - public void render(RendererCellReference cell, String data) { - cell.getElement().setInnerHTML(data); - } - } - - @Override - public UnsafeHtmlRenderer getRenderer() { - return (UnsafeHtmlRenderer) super.getRenderer(); - } -} diff --git a/compatibility-client-compiled/pom.xml b/compatibility-client-compiled/pom.xml index ebd405fe38..25e7d59369 100644 --- a/compatibility-client-compiled/pom.xml +++ b/compatibility-client-compiled/pom.xml @@ -12,7 +12,7 @@ jar - com.vaadin.v7.Vaadin7WidgetSet + com.vaadin.Vaadin7WidgetSet OBF 6 diff --git a/compatibility-client/pom.xml b/compatibility-client/pom.xml index 868dccf7d9..bab3989642 100644 --- a/compatibility-client/pom.xml +++ b/compatibility-client/pom.xml @@ -43,6 +43,33 @@ + + maven-resources-plugin + + + + copy-sources + + prepare-package + + copy-resources + + + ${project.build.outputDirectory} + + + src/main/resources + false + + + src/main/java + false + + + + + + org.apache.maven.plugins maven-surefire-plugin diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractGridRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractGridRendererConnector.java new file mode 100644 index 0000000000..7824e41dd7 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractGridRendererConnector.java @@ -0,0 +1,84 @@ +/* + * 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.client.connectors; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widgets.Grid.Column; + +import elemental.json.JsonObject; + +/** + * An abstract base class for renderer connectors. A renderer connector is used + * to link a client-side {@link Renderer} to a server-side + * {@link com.vaadin.ui.components.grid.Renderer Renderer}. As a connector, it + * can use the regular Vaadin RPC and shared state mechanism to pass additional + * state and information between the client and the server. This base class + * itself only uses the basic {@link com.vaadin.shared.communication.SharedState + * SharedState} and no RPC interfaces. + * + * @param + * the presentation type of the renderer + * + * @since 7.4 + * @author Vaadin Ltd + */ +public abstract class AbstractGridRendererConnector + extends AbstractRendererConnector { + + /** + * Gets the row key for a row object. + *

+ * In case this renderer wants be able to identify a row in such a way that + * the server also understands it, the row key is used for that. Rows are + * identified by unified keys between the client and the server. + * + * @param row + * the row object + * @return the row key for the given row + */ + protected String getRowKey(JsonObject row) { + final ServerConnector parent = getParent(); + if (parent instanceof GridConnector) { + return ((GridConnector) parent).getRowKey(row); + } else { + throw new IllegalStateException( + "Renderers can only be used " + "with a Grid."); + } + } + + /** + * Gets the column id for a column. + *

+ * In case this renderer wants be able to identify a column in such a way + * that the server also understands it, the column id is used for that. + * Columns are identified by unified ids between the client and the server. + * + * @param column + * the column object + * @return the column id for the given column + */ + protected String getColumnId(Column column) { + final ServerConnector parent = getParent(); + if (parent instanceof GridConnector) { + return ((GridConnector) parent).getColumnId(column); + } else { + throw new IllegalStateException( + "Renderers can only be used " + "with a Grid."); + } + } + +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java new file mode 100644 index 0000000000..75664d04f9 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/AbstractSelectionModelConnector.java @@ -0,0 +1,82 @@ +/* + * 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.client.connectors; + +import java.util.Collection; + +import com.vaadin.client.data.DataSource.RowHandle; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widgets.Grid; +import com.vaadin.shared.ui.grid.GridState; + +import elemental.json.JsonObject; + +/** + * Base class for all selection model connectors. + * + * @since 7.6 + * @author Vaadin Ltd + */ +public abstract class AbstractSelectionModelConnector> + extends AbstractExtensionConnector { + + @Override + public GridConnector getParent() { + return (GridConnector) super.getParent(); + } + + protected Grid getGrid() { + return getParent().getWidget(); + } + + protected RowHandle getRowHandle(JsonObject row) { + return getGrid().getDataSource().getHandle(row); + } + + protected String getRowKey(JsonObject row) { + return row != null ? getParent().getRowKey(row) : null; + } + + protected abstract T createSelectionModel(); + + public abstract static class AbstractSelectionModel + implements SelectionModel { + + @Override + public boolean isSelected(JsonObject row) { + return row.hasKey(GridState.JSONKEY_SELECTED); + } + + @Override + public void setGrid(Grid grid) { + // NO-OP + } + + @Override + public void reset() { + // Should not need any actions. + } + + @Override + public Collection getSelectedRows() { + throw new UnsupportedOperationException( + "This client-side selection model " + + getClass().getSimpleName() + + " does not know selected rows."); + } + } +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java new file mode 100644 index 0000000000..fad8918c92 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java @@ -0,0 +1,45 @@ +/* + * 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.client.connectors; + +import com.google.web.bindery.event.shared.HandlerRegistration; +import com.vaadin.client.renderers.ButtonRenderer; +import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; +import com.vaadin.shared.ui.Connect; + +import elemental.json.JsonObject; + +/** + * A connector for {@link ButtonRenderer}. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.renderers.ButtonRenderer.class) +public class ButtonRendererConnector + extends ClickableRendererConnector { + + @Override + public ButtonRenderer getRenderer() { + return (ButtonRenderer) super.getRenderer(); + } + + @Override + protected HandlerRegistration addClickHandler( + RendererClickHandler handler) { + return getRenderer().addClickHandler(handler); + } +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java new file mode 100644 index 0000000000..89549bc2bc --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/ClickableRendererConnector.java @@ -0,0 +1,63 @@ +/* + * 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.client.connectors; + +import com.google.web.bindery.event.shared.HandlerRegistration; +import com.vaadin.client.MouseEventDetailsBuilder; +import com.vaadin.client.renderers.ClickableRenderer; +import com.vaadin.client.renderers.ClickableRenderer.RendererClickEvent; +import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; +import com.vaadin.shared.ui.grid.renderers.RendererClickRpc; + +import elemental.json.JsonObject; + +/** + * An abstract base class for {@link ClickableRenderer} connectors. + * + * @param + * the presentation type of the renderer + * + * @since 7.4 + * @author Vaadin Ltd + */ +public abstract class ClickableRendererConnector + extends AbstractGridRendererConnector { + + HandlerRegistration clickRegistration; + + @Override + protected void init() { + clickRegistration = addClickHandler( + new RendererClickHandler() { + @Override + public void onClick(RendererClickEvent event) { + getRpcProxy(RendererClickRpc.class).click( + getRowKey(event.getCell().getRow()), + getColumnId(event.getCell().getColumn()), + MouseEventDetailsBuilder.buildMouseEventDetails( + event.getNativeEvent())); + } + }); + } + + @Override + public void onUnregister() { + clickRegistration.removeHandler(); + } + + protected abstract HandlerRegistration addClickHandler( + RendererClickHandler handler); +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java new file mode 100644 index 0000000000..4b8c3872da --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java @@ -0,0 +1,34 @@ +/* + * 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.client.connectors; + +import com.vaadin.shared.ui.Connect; + +/** + * A connector for {@link com.vaadin.ui.components.grid.renderers.DateRenderer + * DateRenderer}. + *

+ * The server-side Renderer operates on dates, but the data is serialized as a + * string, and displayed as-is on the client side. This is to be able to support + * the server's locale. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.renderers.DateRenderer.class) +public class DateRendererConnector extends TextRendererConnector { + // No implementation needed +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java new file mode 100644 index 0000000000..000a24af00 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java @@ -0,0 +1,37 @@ +/* + * 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.client.connectors; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.shared.ui.Connect; +import com.vaadin.ui.LegacyGrid.DetailComponentManager; + +/** + * Client-side connector for the DetailComponentManager of Grid. + * + * @since 7.6.1 + */ +@Connect(DetailComponentManager.class) +public class DetailComponentManagerConnector + extends AbstractExtensionConnector { + + @Override + protected void extend(ServerConnector target) { + // TODO: Move DetailsGenerator logic here. + } + +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/GridConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/GridConnector.java new file mode 100644 index 0000000000..045f51f508 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/GridConnector.java @@ -0,0 +1,1311 @@ +/* + * 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.client.connectors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.logging.Logger; + +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.EventTarget; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ComponentConnector; +import com.vaadin.client.ConnectorHierarchyChangeEvent; +import com.vaadin.client.DeferredWorker; +import com.vaadin.client.MouseEventDetailsBuilder; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.TooltipInfo; +import com.vaadin.client.WidgetUtil; +import com.vaadin.client.communication.StateChangeEvent; +import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; +import com.vaadin.client.connectors.RpcDataSourceConnector.DetailsListener; +import com.vaadin.client.connectors.RpcDataSourceConnector.RpcDataSource; +import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.AbstractHasComponentsConnector; +import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; +import com.vaadin.client.ui.SimpleManagedLayout; +import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent; +import com.vaadin.client.widget.escalator.events.RowHeightChangedHandler; +import com.vaadin.client.widget.grid.CellReference; +import com.vaadin.client.widget.grid.CellStyleGenerator; +import com.vaadin.client.widget.grid.EditorHandler; +import com.vaadin.client.widget.grid.EventCellReference; +import com.vaadin.client.widget.grid.HeightAwareDetailsGenerator; +import com.vaadin.client.widget.grid.RowReference; +import com.vaadin.client.widget.grid.RowStyleGenerator; +import com.vaadin.client.widget.grid.events.BodyClickHandler; +import com.vaadin.client.widget.grid.events.BodyDoubleClickHandler; +import com.vaadin.client.widget.grid.events.ColumnReorderEvent; +import com.vaadin.client.widget.grid.events.ColumnReorderHandler; +import com.vaadin.client.widget.grid.events.ColumnResizeEvent; +import com.vaadin.client.widget.grid.events.ColumnResizeHandler; +import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeEvent; +import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeHandler; +import com.vaadin.client.widget.grid.events.GridClickEvent; +import com.vaadin.client.widget.grid.events.GridDoubleClickEvent; +import com.vaadin.client.widget.grid.sort.SortEvent; +import com.vaadin.client.widget.grid.sort.SortHandler; +import com.vaadin.client.widget.grid.sort.SortOrder; +import com.vaadin.client.widgets.Grid; +import com.vaadin.client.widgets.Grid.Column; +import com.vaadin.client.widgets.Grid.FooterCell; +import com.vaadin.client.widgets.Grid.FooterRow; +import com.vaadin.client.widgets.Grid.HeaderCell; +import com.vaadin.client.widgets.Grid.HeaderRow; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.EditorClientRpc; +import com.vaadin.shared.ui.grid.EditorServerRpc; +import com.vaadin.shared.ui.grid.GridClientRpc; +import com.vaadin.shared.ui.grid.GridColumnState; +import com.vaadin.shared.ui.grid.GridConstants; +import com.vaadin.shared.ui.grid.GridConstants.Section; +import com.vaadin.shared.ui.grid.GridServerRpc; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.GridStaticSectionState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; +import com.vaadin.shared.ui.grid.ScrollDestination; +import com.vaadin.ui.LegacyGrid; + +import elemental.json.JsonObject; +import elemental.json.JsonValue; + +/** + * Connects the client side {@link Grid} widget with the server side + * {@link com.vaadin.ui.components.grid.Grid} component. + *

+ * The Grid is typed to JSONObject. The structure of the JSONObject is described + * at {@link com.vaadin.shared.data.DataProviderRpc#setRowData(int, List) + * DataProviderRpc.setRowData(int, List)}. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(LegacyGrid.class) +public class GridConnector extends AbstractHasComponentsConnector + implements SimpleManagedLayout, DeferredWorker { + + private static final class CustomStyleGenerator implements + CellStyleGenerator, RowStyleGenerator { + @Override + public String getStyle(CellReference cellReference) { + JsonObject row = cellReference.getRow(); + if (!row.hasKey(GridState.JSONKEY_CELLSTYLES)) { + return null; + } + + Column column = cellReference.getColumn(); + if (!(column instanceof CustomGridColumn)) { + // Selection checkbox column + return null; + } + CustomGridColumn c = (CustomGridColumn) column; + + JsonObject cellStylesObject = row + .getObject(GridState.JSONKEY_CELLSTYLES); + assert cellStylesObject != null; + + if (cellStylesObject.hasKey(c.id)) { + return cellStylesObject.getString(c.id); + } else { + return null; + } + } + + @Override + public String getStyle(RowReference rowReference) { + JsonObject row = rowReference.getRow(); + if (row.hasKey(GridState.JSONKEY_ROWSTYLE)) { + return row.getString(GridState.JSONKEY_ROWSTYLE); + } else { + return null; + } + } + } + + /** + * Custom implementation of the custom grid column using a JSONObject to + * represent the cell value and String as a column type. + */ + private class CustomGridColumn extends Grid.Column { + + private final String id; + + private AbstractGridRendererConnector rendererConnector; + + private AbstractComponentConnector editorConnector; + + private HandlerRegistration errorStateHandler; + + public CustomGridColumn(String id, + AbstractGridRendererConnector rendererConnector) { + super(rendererConnector.getRenderer()); + this.rendererConnector = rendererConnector; + this.id = id; + } + + /** + * Sets a new renderer for this column object + * + * @param rendererConnector + * a renderer connector object + */ + public void setRenderer( + AbstractGridRendererConnector rendererConnector) { + setRenderer(rendererConnector.getRenderer()); + this.rendererConnector = rendererConnector; + } + + @Override + public Object getValue(final JsonObject obj) { + final JsonObject rowData = obj.getObject(GridState.JSONKEY_DATA); + + if (rowData.hasKey(id)) { + final JsonValue columnValue = rowData.get(id); + + return rendererConnector.decode(columnValue); + } + + return null; + } + + private AbstractComponentConnector getEditorConnector() { + return editorConnector; + } + + private void setEditorConnector( + final AbstractComponentConnector editorConnector) { + this.editorConnector = editorConnector; + + if (errorStateHandler != null) { + errorStateHandler.removeHandler(); + errorStateHandler = null; + } + + // Avoid nesting too deep + if (editorConnector == null) { + return; + } + + errorStateHandler = editorConnector.addStateChangeHandler( + "errorMessage", new StateChangeHandler() { + + @Override + public void onStateChanged( + StateChangeEvent stateChangeEvent) { + + String error = editorConnector + .getState().errorMessage; + + if (error == null) { + columnToErrorMessage + .remove(CustomGridColumn.this); + } else { + // The error message is formatted as HTML; + // therefore, we use this hack to make the + // string human-readable. + Element e = DOM.createElement("div"); + e.setInnerHTML(editorConnector + .getState().errorMessage); + error = getHeaderCaption() + ": " + + e.getInnerText(); + + columnToErrorMessage.put(CustomGridColumn.this, + error); + } + + // Handle Editor RPC before updating error status + Scheduler.get() + .scheduleFinally(new ScheduledCommand() { + + @Override + public void execute() { + updateErrorColumns(); + } + }); + } + + public void updateErrorColumns() { + getWidget().getEditor().setEditorError( + getColumnErrors(), + columnToErrorMessage.keySet()); + } + }); + } + } + + /* + * An editor handler using Vaadin RPC to manage the editor state. + */ + private class CustomEditorHandler implements EditorHandler { + + private EditorServerRpc rpc = getRpcProxy(EditorServerRpc.class); + + private EditorRequest currentRequest = null; + private boolean serverInitiated = false; + + public CustomEditorHandler() { + registerRpc(EditorClientRpc.class, new EditorClientRpc() { + + @Override + public void bind(final int rowIndex) { + // call this deferred to avoid issues with editing on init + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + GridConnector.this.getWidget().editRow(rowIndex); + } + }); + } + + @Override + public void cancel(int rowIndex) { + serverInitiated = true; + GridConnector.this.getWidget().cancelEditor(); + } + + @Override + public void confirmBind(final boolean bindSucceeded) { + endRequest(bindSucceeded, null, null); + } + + @Override + public void confirmSave(boolean saveSucceeded, + String errorMessage, List errorColumnsIds) { + endRequest(saveSucceeded, errorMessage, errorColumnsIds); + } + }); + } + + @Override + public void bind(EditorRequest request) { + startRequest(request); + rpc.bind(request.getRowIndex()); + } + + @Override + public void save(EditorRequest request) { + startRequest(request); + rpc.save(request.getRowIndex()); + } + + @Override + public void cancel(EditorRequest request) { + if (!handleServerInitiated(request)) { + // No startRequest as we don't get (or need) + // a confirmation from the server + rpc.cancel(request.getRowIndex()); + } + } + + @Override + public Widget getWidget(Grid.Column column) { + assert column != null; + + if (column instanceof CustomGridColumn) { + AbstractComponentConnector c = ((CustomGridColumn) column) + .getEditorConnector(); + + if (c == null) { + return null; + } + + return c.getWidget(); + } else { + throw new IllegalStateException("Unexpected column type: " + + column.getClass().getName()); + } + } + + /** + * Used to handle the case where the editor calls us because it was + * invoked by the server via RPC and not by the client. In that case, + * the request can be simply synchronously completed. + * + * @param request + * the request object + * @return true if the request was originally triggered by the server, + * false otherwise + */ + private boolean handleServerInitiated(EditorRequest request) { + assert request != null : "Cannot handle null request"; + assert currentRequest == null : "Earlier request not yet finished"; + + if (serverInitiated) { + serverInitiated = false; + request.success(); + return true; + } else { + return false; + } + } + + private void startRequest(EditorRequest request) { + assert currentRequest == null : "Earlier request not yet finished"; + + currentRequest = request; + } + + private void endRequest(boolean succeeded, String errorMessage, + List errorColumnsIds) { + assert currentRequest != null : "Current request was null"; + /* + * Clear current request first to ensure the state is valid if + * another request is made in the callback. + */ + EditorRequest request = currentRequest; + currentRequest = null; + if (succeeded) { + request.success(); + } else { + Collection> errorColumns; + if (errorColumnsIds != null) { + errorColumns = new ArrayList>(); + for (String colId : errorColumnsIds) { + errorColumns.add(columnIdToColumn.get(colId)); + } + } else { + errorColumns = null; + } + + request.failure(errorMessage, errorColumns); + } + } + } + + private class ItemClickHandler + implements BodyClickHandler, BodyDoubleClickHandler { + + @Override + public void onClick(GridClickEvent event) { + if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) { + fireItemClick(event.getTargetCell(), event.getNativeEvent()); + } + } + + @Override + public void onDoubleClick(GridDoubleClickEvent event) { + if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) { + fireItemClick(event.getTargetCell(), event.getNativeEvent()); + } + } + + private void fireItemClick(CellReference cell, + NativeEvent mouseEvent) { + String rowKey = getRowKey((JsonObject) cell.getRow()); + String columnId = getColumnId(cell.getColumn()); + getRpcProxy(GridServerRpc.class).itemClick(rowKey, columnId, + MouseEventDetailsBuilder + .buildMouseEventDetails(mouseEvent)); + } + } + + private ColumnReorderHandler columnReorderHandler = new ColumnReorderHandler() { + + @Override + public void onColumnReorder(ColumnReorderEvent event) { + if (!columnsUpdatedFromState) { + List> columns = getWidget().getColumns(); + final List newColumnOrder = new ArrayList(); + for (Column column : columns) { + if (column instanceof CustomGridColumn) { + newColumnOrder.add(((CustomGridColumn) column).id); + } // the other case would be the multi selection column + } + getRpcProxy(GridServerRpc.class) + .columnsReordered(newColumnOrder, columnOrder); + columnOrder = newColumnOrder; + getState().columnOrder = newColumnOrder; + } + } + }; + + private ColumnVisibilityChangeHandler columnVisibilityChangeHandler = new ColumnVisibilityChangeHandler() { + + @Override + public void onVisibilityChange( + ColumnVisibilityChangeEvent event) { + if (!columnsUpdatedFromState) { + Column column = event.getColumn(); + if (column instanceof CustomGridColumn) { + getRpcProxy(GridServerRpc.class).columnVisibilityChanged( + ((CustomGridColumn) column).id, column.isHidden(), + event.isUserOriginated()); + for (GridColumnState state : getState().columns) { + if (state.id.equals(((CustomGridColumn) column).id)) { + state.hidden = event.isHidden(); + break; + } + } + } else { + getLogger().warning( + "Visibility changed for a unknown column type in Grid: " + + column.toString() + ", type " + + column.getClass()); + } + } + } + }; + + private ColumnResizeHandler columnResizeHandler = new ColumnResizeHandler() { + + @Override + public void onColumnResize(ColumnResizeEvent event) { + if (!columnsUpdatedFromState) { + Column column = event.getColumn(); + if (column instanceof CustomGridColumn) { + getRpcProxy(GridServerRpc.class).columnResized( + ((CustomGridColumn) column).id, + column.getWidthActual()); + } + } + } + }; + + private class CustomDetailsGenerator + implements HeightAwareDetailsGenerator { + + private final Map idToDetailsMap = new HashMap(); + private final Map idToRowIndex = new HashMap(); + + @Override + public Widget getDetails(int rowIndex) { + String id = getId(rowIndex); + if (id == null) { + return null; + } + ComponentConnector componentConnector = idToDetailsMap.get(id); + idToRowIndex.put(id, rowIndex); + + return componentConnector.getWidget(); + } + + @Override + public double getDetailsHeight(int rowIndex) { + // Case of null is handled in the getDetails method and this method + // will not called if it returns null. + String id = getId(rowIndex); + ComponentConnector componentConnector = idToDetailsMap.get(id); + + getLayoutManager().setNeedsMeasureRecursively(componentConnector); + getLayoutManager().layoutNow(); + + return getLayoutManager().getOuterHeightDouble( + componentConnector.getWidget().getElement()); + } + + /** + * Fetches id from the row object that corresponds with the given + * rowIndex. + * + * @since 7.6.1 + * @param rowIndex + * the index of the row for which to fetch the id + * @return id of the row if such id exists, {@code null} otherwise + */ + private String getId(int rowIndex) { + JsonObject row = getWidget().getDataSource().getRow(rowIndex); + + if (!row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE) || row + .getString(GridState.JSONKEY_DETAILS_VISIBLE).isEmpty()) { + return null; + } + + return row.getString(GridState.JSONKEY_DETAILS_VISIBLE); + } + + public void updateConnectorHierarchy(List children) { + Set connectorIds = new HashSet(); + for (ServerConnector child : children) { + if (child instanceof ComponentConnector) { + connectorIds.add(child.getConnectorId()); + idToDetailsMap.put(child.getConnectorId(), + (ComponentConnector) child); + } + } + + Set removedDetails = new HashSet(); + for (Entry entry : idToDetailsMap + .entrySet()) { + ComponentConnector connector = entry.getValue(); + String id = connector.getConnectorId(); + if (!connectorIds.contains(id)) { + removedDetails.add(entry.getKey()); + if (idToRowIndex.containsKey(id)) { + getWidget().setDetailsVisible(idToRowIndex.get(id), + false); + } + } + } + + for (String id : removedDetails) { + idToDetailsMap.remove(id); + idToRowIndex.remove(id); + } + } + } + + /** + * Class for handling scrolling issues with open details. + * + * @since 7.5.2 + */ + private class LazyDetailsScroller implements DeferredWorker { + + /* Timer value tested to work in our test cluster with slow IE8s. */ + private static final int DISABLE_LAZY_SCROLL_TIMEOUT = 1500; + + /* + * Cancels details opening scroll after timeout. Avoids any unexpected + * scrolls via details opening. + */ + private Timer disableScroller = new Timer() { + @Override + public void run() { + targetRow = -1; + } + }; + + private Integer targetRow = -1; + private ScrollDestination destination = null; + + public void scrollToRow(Integer row, ScrollDestination dest) { + targetRow = row; + destination = dest; + disableScroller.schedule(DISABLE_LAZY_SCROLL_TIMEOUT); + } + + /** + * Inform LazyDetailsScroller that a details row has opened on a row. + * + * @param rowIndex + * index of row with details now open + */ + public void detailsOpened(int rowIndex) { + if (targetRow == rowIndex) { + getWidget().scrollToRow(targetRow, destination); + disableScroller.run(); + } + } + + @Override + public boolean isWorkPending() { + return disableScroller.isRunning(); + } + } + + /** + * Maps a generated column id to a grid column instance + */ + private Map columnIdToColumn = new HashMap(); + + private List columnOrder = new ArrayList(); + + /** + * {@link #columnsUpdatedFromState} is set to true when + * {@link #updateColumnOrderFromState(List)} is updating the column order + * for the widget. This flag tells the {@link #columnReorderHandler} to not + * send same data straight back to server. After updates, listener sets the + * value back to false. + */ + private boolean columnsUpdatedFromState; + + private RpcDataSource dataSource; + + /* Used to track Grid editor columns with validation errors */ + private final Map, String> columnToErrorMessage = new HashMap, String>(); + + private ItemClickHandler itemClickHandler = new ItemClickHandler(); + + private String lastKnownTheme = null; + + private final CustomDetailsGenerator customDetailsGenerator = new CustomDetailsGenerator(); + private final CustomStyleGenerator styleGenerator = new CustomStyleGenerator(); + + private final DetailsListener detailsListener = new DetailsListener() { + @Override + public void reapplyDetailsVisibility(final int rowIndex, + final JsonObject row) { + + if (hasDetailsOpen(row)) { + // Command for opening details row. + ScheduledCommand openDetails = new ScheduledCommand() { + @Override + public void execute() { + // Re-apply to force redraw. + getWidget().setDetailsVisible(rowIndex, false); + getWidget().setDetailsVisible(rowIndex, true); + lazyDetailsScroller.detailsOpened(rowIndex); + } + }; + + if (initialChange) { + Scheduler.get().scheduleDeferred(openDetails); + } else { + Scheduler.get().scheduleFinally(openDetails); + } + } else { + getWidget().setDetailsVisible(rowIndex, false); + } + } + + private boolean hasDetailsOpen(JsonObject row) { + return row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE) + && row.getString(GridState.JSONKEY_DETAILS_VISIBLE) != null; + } + }; + + private final LazyDetailsScroller lazyDetailsScroller = new LazyDetailsScroller(); + private final CustomEditorHandler editorHandler = new CustomEditorHandler(); + + /* + * Initially details need to behave a bit differently to allow some + * escalator magic. + */ + private boolean initialChange; + + @Override + @SuppressWarnings("unchecked") + public Grid getWidget() { + return (Grid) super.getWidget(); + } + + @Override + public GridState getState() { + return (GridState) super.getState(); + } + + @Override + protected void init() { + super.init(); + + // All scroll RPC calls are executed finally to avoid issues on init + registerRpc(GridClientRpc.class, new GridClientRpc() { + @Override + public void scrollToStart() { + /* + * no need for lazyDetailsScrollAdjuster, because the start is + * always 0, won't change a bit. + */ + Scheduler.get().scheduleFinally(new ScheduledCommand() { + @Override + public void execute() { + getWidget().scrollToStart(); + } + }); + } + + @Override + public void scrollToEnd() { + Scheduler.get().scheduleFinally(new ScheduledCommand() { + @Override + public void execute() { + getWidget().scrollToEnd(); + // Scrolls further if details opens. + lazyDetailsScroller.scrollToRow(dataSource.size() - 1, + ScrollDestination.END); + } + }); + } + + @Override + public void scrollToRow(final int row, + final ScrollDestination destination) { + Scheduler.get().scheduleFinally(new ScheduledCommand() { + @Override + public void execute() { + getWidget().scrollToRow(row, destination); + // Scrolls a bit further if details opens. + lazyDetailsScroller.scrollToRow(row, destination); + } + }); + } + + @Override + public void recalculateColumnWidths() { + getWidget().recalculateColumnWidths(); + } + }); + + /* Item click events */ + getWidget().addBodyClickHandler(itemClickHandler); + getWidget().addBodyDoubleClickHandler(itemClickHandler); + + /* Style Generators */ + getWidget().setCellStyleGenerator(styleGenerator); + getWidget().setRowStyleGenerator(styleGenerator); + + getWidget().addSortHandler(new SortHandler() { + @Override + public void sort(SortEvent event) { + List order = event.getOrder(); + String[] columnIds = new String[order.size()]; + SortDirection[] directions = new SortDirection[order.size()]; + for (int i = 0; i < order.size(); i++) { + SortOrder sortOrder = order.get(i); + CustomGridColumn column = (CustomGridColumn) sortOrder + .getColumn(); + columnIds[i] = column.id; + + directions[i] = sortOrder.getDirection(); + } + + if (!Arrays.equals(columnIds, getState().sortColumns) + || !Arrays.equals(directions, getState().sortDirs)) { + // Report back to server if changed + getRpcProxy(GridServerRpc.class).sort(columnIds, directions, + event.isUserOriginated()); + } + } + }); + + getWidget().setEditorHandler(editorHandler); + getWidget().addColumnReorderHandler(columnReorderHandler); + getWidget().addColumnVisibilityChangeHandler( + columnVisibilityChangeHandler); + getWidget().addColumnResizeHandler(columnResizeHandler); + + ConnectorFocusAndBlurHandler.addHandlers(this); + + getWidget().setDetailsGenerator(customDetailsGenerator); + getLayoutManager().registerDependency(this, getWidget().getElement()); + + // Handling row height changes + getWidget().addRowHeightChangedHandler(new RowHeightChangedHandler() { + @Override + public void onRowHeightChanged(RowHeightChangedEvent event) { + getLayoutManager() + .setNeedsMeasureRecursively(GridConnector.this); + getLayoutManager().layoutNow(); + } + }); + + layout(); + } + + @Override + public void onStateChanged(final StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); + + initialChange = stateChangeEvent.isInitialStateChange(); + + // Column updates + if (stateChangeEvent.hasPropertyChanged("columns")) { + + // Remove old columns + purgeRemovedColumns(); + + // Add new columns + for (GridColumnState state : getState().columns) { + if (!columnIdToColumn.containsKey(state.id)) { + addColumnFromStateChangeEvent(state); + } + updateColumnFromStateChangeEvent(state); + } + } + + if (stateChangeEvent.hasPropertyChanged("columnOrder")) { + if (orderNeedsUpdate(getState().columnOrder)) { + updateColumnOrderFromState(getState().columnOrder); + } + } + + // Header and footer + if (stateChangeEvent.hasPropertyChanged("header")) { + updateHeaderFromState(getState().header); + } + + if (stateChangeEvent.hasPropertyChanged("footer")) { + updateFooterFromState(getState().footer); + } + + // Sorting + if (stateChangeEvent.hasPropertyChanged("sortColumns") + || stateChangeEvent.hasPropertyChanged("sortDirs")) { + onSortStateChange(); + } + + // Editor + if (stateChangeEvent.hasPropertyChanged("editorEnabled")) { + getWidget().setEditorEnabled(getState().editorEnabled); + } + + // Frozen columns + if (stateChangeEvent.hasPropertyChanged("frozenColumnCount")) { + getWidget().setFrozenColumnCount(getState().frozenColumnCount); + } + + // Theme features + String activeTheme = getConnection().getUIConnector().getActiveTheme(); + if (lastKnownTheme == null) { + lastKnownTheme = activeTheme; + } else if (!lastKnownTheme.equals(activeTheme)) { + getWidget().resetSizesFromDom(); + lastKnownTheme = activeTheme; + } + } + + private void updateColumnOrderFromState(List stateColumnOrder) { + CustomGridColumn[] columns = new CustomGridColumn[stateColumnOrder + .size()]; + int i = 0; + for (String id : stateColumnOrder) { + columns[i] = columnIdToColumn.get(id); + i++; + } + columnsUpdatedFromState = true; + getWidget().setColumnOrder(columns); + columnsUpdatedFromState = false; + columnOrder = stateColumnOrder; + } + + private boolean orderNeedsUpdate(List stateColumnOrder) { + if (stateColumnOrder.size() == columnOrder.size()) { + for (int i = 0; i < columnOrder.size(); ++i) { + if (!stateColumnOrder.get(i).equals(columnOrder.get(i))) { + return true; + } + } + return false; + } + return true; + } + + private void updateHeaderFromState(GridStaticSectionState state) { + getWidget().setHeaderVisible(state.visible); + + while (getWidget().getHeaderRowCount() > 0) { + getWidget().removeHeaderRow(0); + } + + for (RowState rowState : state.rows) { + HeaderRow row = getWidget().appendHeaderRow(); + + if (rowState.defaultRow) { + getWidget().setDefaultHeaderRow(row); + } + + for (CellState cellState : rowState.cells) { + CustomGridColumn column = columnIdToColumn + .get(cellState.columnId); + updateHeaderCellFromState(row.getCell(column), cellState); + } + + for (Set group : rowState.cellGroups.keySet()) { + Grid.Column[] columns = new Grid.Column[group + .size()]; + CellState cellState = rowState.cellGroups.get(group); + + int i = 0; + for (String columnId : group) { + columns[i] = columnIdToColumn.get(columnId); + i++; + } + + // Set state to be the same as first in group. + updateHeaderCellFromState(row.join(columns), cellState); + } + + row.setStyleName(rowState.styleName); + } + } + + private void updateHeaderCellFromState(HeaderCell cell, + CellState cellState) { + switch (cellState.type) { + case TEXT: + cell.setText(cellState.text); + break; + case HTML: + cell.setHtml(cellState.html); + break; + case WIDGET: + ComponentConnector connector = (ComponentConnector) cellState.connector; + if (connector != null) { + cell.setWidget(connector.getWidget()); + } else { + // This happens if you do setVisible(false) on the component on + // the server side + cell.setWidget(null); + } + break; + default: + throw new IllegalStateException( + "unexpected cell type: " + cellState.type); + } + cell.setStyleName(cellState.styleName); + } + + private void updateFooterFromState(GridStaticSectionState state) { + getWidget().setFooterVisible(state.visible); + + while (getWidget().getFooterRowCount() > 0) { + getWidget().removeFooterRow(0); + } + + for (RowState rowState : state.rows) { + FooterRow row = getWidget().appendFooterRow(); + + for (CellState cellState : rowState.cells) { + CustomGridColumn column = columnIdToColumn + .get(cellState.columnId); + updateFooterCellFromState(row.getCell(column), cellState); + } + + for (Set group : rowState.cellGroups.keySet()) { + Grid.Column[] columns = new Grid.Column[group + .size()]; + CellState cellState = rowState.cellGroups.get(group); + + int i = 0; + for (String columnId : group) { + columns[i] = columnIdToColumn.get(columnId); + i++; + } + + // Set state to be the same as first in group. + updateFooterCellFromState(row.join(columns), cellState); + } + + row.setStyleName(rowState.styleName); + } + } + + private void updateFooterCellFromState(FooterCell cell, + CellState cellState) { + switch (cellState.type) { + case TEXT: + cell.setText(cellState.text); + break; + case HTML: + cell.setHtml(cellState.html); + break; + case WIDGET: + ComponentConnector connector = (ComponentConnector) cellState.connector; + if (connector != null) { + cell.setWidget(connector.getWidget()); + } else { + // This happens if you do setVisible(false) on the component on + // the server side + cell.setWidget(null); + } + break; + default: + throw new IllegalStateException( + "unexpected cell type: " + cellState.type); + } + cell.setStyleName(cellState.styleName); + } + + /** + * Updates a column from a state change event. + * + * @param columnIndex + * The index of the column to update + */ + private void updateColumnFromStateChangeEvent(GridColumnState columnState) { + CustomGridColumn column = columnIdToColumn.get(columnState.id); + + columnsUpdatedFromState = true; + updateColumnFromState(column, columnState); + columnsUpdatedFromState = false; + } + + /** + * Adds a new column to the grid widget from a state change event + * + * @param columnIndex + * The index of the column, according to how it + */ + private void addColumnFromStateChangeEvent(GridColumnState state) { + @SuppressWarnings("unchecked") + CustomGridColumn column = new CustomGridColumn(state.id, + ((AbstractGridRendererConnector) state.rendererConnector)); + columnIdToColumn.put(state.id, column); + + /* + * Add column to grid. Reordering is handled as a separate problem. + */ + getWidget().addColumn(column); + columnOrder.add(state.id); + } + + /** + * Updates the column values from a state + * + * @param column + * The column to update + * @param state + * The state to get the data from + */ + @SuppressWarnings("unchecked") + private static void updateColumnFromState(CustomGridColumn column, + GridColumnState state) { + column.setWidth(state.width); + column.setMinimumWidth(state.minWidth); + column.setMaximumWidth(state.maxWidth); + column.setExpandRatio(state.expandRatio); + + assert state.rendererConnector instanceof AbstractGridRendererConnector : "GridColumnState.rendererConnector is invalid (not subclass of AbstractGridRendererConnector)"; + column.setRenderer( + (AbstractGridRendererConnector) state.rendererConnector); + + column.setSortable(state.sortable); + + column.setResizable(state.resizable); + + column.setHeaderCaption(state.headerCaption); + + column.setHidden(state.hidden); + column.setHidable(state.hidable); + column.setHidingToggleCaption(state.hidingToggleCaption); + + column.setEditable(state.editable); + column.setEditorConnector( + (AbstractComponentConnector) state.editorConnector); + } + + /** + * Removes any orphan columns that has been removed from the state from the + * grid + */ + private void purgeRemovedColumns() { + + // Get columns still registered in the state + Set columnsInState = new HashSet(); + for (GridColumnState columnState : getState().columns) { + columnsInState.add(columnState.id); + } + + // Remove column no longer in state + Iterator columnIdIterator = columnIdToColumn.keySet() + .iterator(); + while (columnIdIterator.hasNext()) { + String id = columnIdIterator.next(); + if (!columnsInState.contains(id)) { + CustomGridColumn column = columnIdToColumn.get(id); + columnIdIterator.remove(); + getWidget().removeColumn(column); + columnOrder.remove(id); + } + } + } + + public void setDataSource(RpcDataSource dataSource) { + this.dataSource = dataSource; + getWidget().setDataSource(this.dataSource); + } + + private void onSortStateChange() { + List sortOrder = new ArrayList(); + + String[] sortColumns = getState().sortColumns; + SortDirection[] sortDirs = getState().sortDirs; + + for (int i = 0; i < sortColumns.length; i++) { + sortOrder.add(new SortOrder(columnIdToColumn.get(sortColumns[i]), + sortDirs[i])); + } + + getWidget().setSortOrder(sortOrder); + } + + private Logger getLogger() { + return Logger.getLogger(getClass().getName()); + } + + /** + * Gets the row key for a row object. + * + * @param row + * the row object + * @return the key for the given row + */ + public String getRowKey(JsonObject row) { + final Object key = dataSource.getRowKey(row); + assert key instanceof String : "Internal key was not a String but a " + + key.getClass().getSimpleName() + " (" + key + ")"; + return (String) key; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin.client + * .ComponentConnector) + */ + @Override + public void updateCaption(ComponentConnector connector) { + // TODO Auto-generated method stub + } + + @Override + public void onConnectorHierarchyChange( + ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { + customDetailsGenerator.updateConnectorHierarchy(getChildren()); + } + + public String getColumnId(Grid.Column column) { + if (column instanceof CustomGridColumn) { + return ((CustomGridColumn) column).id; + } + return null; + } + + @Override + public void layout() { + getWidget().onResize(); + } + + @Override + public boolean isWorkPending() { + return lazyDetailsScroller.isWorkPending(); + } + + /** + * Gets the listener used by this connector for tracking when row detail + * visibility changes. + * + * @since 7.5.0 + * @return the used details listener + */ + public DetailsListener getDetailsListener() { + return detailsListener; + } + + @Override + public boolean hasTooltip() { + return getState().hasDescriptions || super.hasTooltip(); + } + + @Override + public TooltipInfo getTooltipInfo(Element element) { + CellReference cell = getWidget().getCellReference(element); + + if (cell != null) { + JsonObject row = cell.getRow(); + if (row == null) { + return null; + } + + Column column = cell.getColumn(); + if (!(column instanceof CustomGridColumn)) { + // Selection checkbox column + return null; + } + CustomGridColumn c = (CustomGridColumn) column; + + JsonObject cellDescriptions = row + .getObject(GridState.JSONKEY_CELLDESCRIPTION); + + if (cellDescriptions != null && cellDescriptions.hasKey(c.id)) { + return new TooltipInfo(cellDescriptions.getString(c.id)); + } else if (row.hasKey(GridState.JSONKEY_ROWDESCRIPTION)) { + return new TooltipInfo( + row.getString(GridState.JSONKEY_ROWDESCRIPTION)); + } else { + return null; + } + } + + return super.getTooltipInfo(element); + } + + @Override + protected void sendContextClickEvent(MouseEventDetails details, + EventTarget eventTarget) { + // if element is the resize indicator, ignore the event + if (isResizeHandle(eventTarget)) { + WidgetUtil.clearTextSelection(); + return; + } + + EventCellReference eventCell = getWidget().getEventCell(); + + Section section = eventCell.getSection(); + String rowKey = null; + if (eventCell.isBody() && eventCell.getRow() != null) { + rowKey = getRowKey(eventCell.getRow()); + } + + String columnId = getColumnId(eventCell.getColumn()); + + getRpcProxy(GridServerRpc.class).contextClick(eventCell.getRowIndex(), + rowKey, columnId, section, details); + + WidgetUtil.clearTextSelection(); + } + + private boolean isResizeHandle(EventTarget eventTarget) { + if (Element.is(eventTarget)) { + Element e = Element.as(eventTarget); + if (e.getClassName().contains("-column-resize-handle")) { + return true; + } + } + return false; + } + + /** + * Creates a concatenation of all columns errors for Editor. + * + * @since 7.6 + * @return displayed error string + */ + private String getColumnErrors() { + List errors = new ArrayList(); + + for (Grid.Column c : getWidget().getColumns()) { + if (!(c instanceof CustomGridColumn)) { + continue; + } + + String error = columnToErrorMessage.get(c); + if (error != null) { + errors.add(error); + } + } + + String result = ""; + Iterator i = errors.iterator(); + while (i.hasNext()) { + result += i.next(); + if (i.hasNext()) { + result += ", "; + } + } + return result.isEmpty() ? null : result; + } + +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java new file mode 100644 index 0000000000..7949eeab3c --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java @@ -0,0 +1,57 @@ +/* + * 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.client.connectors; + +import com.google.web.bindery.event.shared.HandlerRegistration; +import com.vaadin.client.communication.JsonDecoder; +import com.vaadin.client.metadata.TypeDataStore; +import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; +import com.vaadin.client.renderers.ImageRenderer; +import com.vaadin.shared.communication.URLReference; +import com.vaadin.shared.ui.Connect; + +import elemental.json.JsonObject; +import elemental.json.JsonValue; + +/** + * A connector for {@link ImageRenderer}. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.renderers.ImageRenderer.class) +public class ImageRendererConnector extends ClickableRendererConnector { + + @Override + public ImageRenderer getRenderer() { + return (ImageRenderer) super.getRenderer(); + } + + @Override + public String decode(JsonValue value) { + URLReference reference = (URLReference) JsonDecoder.decodeValue( + TypeDataStore.getType(URLReference.class), value, null, + getConnection()); + + return reference != null ? reference.getURL() : null; + } + + @Override + protected HandlerRegistration addClickHandler( + RendererClickHandler handler) { + return getRenderer().addClickHandler(handler); + } +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java new file mode 100644 index 0000000000..1515f1aead --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java @@ -0,0 +1,278 @@ +/* + * 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.client.connectors; + +import java.util.ArrayList; +import java.util.Collection; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArrayString; +import com.google.gwt.dom.client.NativeEvent; +import com.vaadin.client.JavaScriptConnectorHelper; +import com.vaadin.client.Util; +import com.vaadin.client.communication.HasJavaScriptConnectorHelper; +import com.vaadin.client.renderers.ComplexRenderer; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.CellReference; +import com.vaadin.client.widget.grid.RendererCellReference; +import com.vaadin.shared.JavaScriptExtensionState; +import com.vaadin.shared.ui.Connect; +import com.vaadin.ui.renderers.AbstractJavaScriptRenderer; + +import elemental.json.JsonObject; +import elemental.json.JsonValue; + +/** + * Connector for server-side renderer implemented using JavaScript. + * + * @since 7.4 + * @author Vaadin Ltd + */ +// This is really typed to , but because of the way native strings +// are not always instanceof JsonValue, we need to accept Object +@Connect(AbstractJavaScriptRenderer.class) +public class JavaScriptRendererConnector + extends AbstractGridRendererConnector + implements HasJavaScriptConnectorHelper { + private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper( + this); + + private final JavaScriptObject cellReferenceWrapper = createCellReferenceWrapper(); + + @Override + protected void init() { + super.init(); + helper.init(); + + addGetRowKey(helper.getConnectorWrapper()); + } + + private static native JavaScriptObject createCellReferenceWrapper() + /*-{ + var reference = {}; + + var setProperty = function(name, getter, setter) { + var descriptor = { + get: getter + } + if (setter) { + descriptor.set = setter; + } + Object.defineProperty(reference, name, descriptor); + }; + + setProperty("element", function() { + return reference.target.@CellReference::getElement()(); + }, null); + + setProperty("rowIndex", function() { + return reference.target.@CellReference::getRowIndex()(); + }, null); + + setProperty("columnIndex", function() { + return reference.target.@CellReference::getColumnIndex()(); + }, null); + + setProperty("colSpan", function() { + return reference.target.@RendererCellReference::getColSpan()(); + }, function(colSpan) { + reference.target.@RendererCellReference::setColSpan(*)(colSpan); + }); + + return reference; + }-*/; + + @Override + public JavaScriptExtensionState getState() { + return (JavaScriptExtensionState) super.getState(); + } + + private native void addGetRowKey(JavaScriptObject wrapper) + /*-{ + var self = this; + wrapper.getRowKey = $entry(function(rowIndex) { + return @JavaScriptRendererConnector::findRowKey(*)(self, rowIndex); + }); + }-*/; + + private static String findRowKey(JavaScriptRendererConnector connector, + int rowIndex) { + GridConnector gc = (GridConnector) connector.getParent(); + JsonObject row = gc.getWidget().getDataSource().getRow(rowIndex); + return connector.getRowKey(row); + } + + private boolean hasFunction(String name) { + return hasFunction(helper.getConnectorWrapper(), name); + } + + private static native boolean hasFunction(JavaScriptObject wrapper, + String name) + /*-{ + return typeof wrapper[name] === 'function'; + }-*/; + + @Override + protected Renderer createRenderer() { + helper.ensureJavascriptInited(); + + if (!hasFunction("render")) { + throw new RuntimeException( + "JavaScriptRenderer " + helper.getInitFunctionName() + + " must have a function named 'render'"); + } + + final boolean hasInit = hasFunction("init"); + final boolean hasDestroy = hasFunction("destroy"); + final boolean hasOnActivate = hasFunction("onActivate"); + final boolean hasGetConsumedEvents = hasFunction("getConsumedEvents"); + final boolean hasOnBrowserEvent = hasFunction("onBrowserEvent"); + + return new ComplexRenderer() { + @Override + public void render(RendererCellReference cell, Object data) { + if (data instanceof JsonValue) { + data = Util.json2jso((JsonValue) data); + } + render(helper.getConnectorWrapper(), getJsCell(cell), data); + } + + private JavaScriptObject getJsCell(CellReference cell) { + updateCellReference(cellReferenceWrapper, cell); + return cellReferenceWrapper; + } + + public native void render(JavaScriptObject wrapper, + JavaScriptObject cell, Object data) + /*-{ + wrapper.render(cell, data); + }-*/; + + @Override + public void init(RendererCellReference cell) { + if (hasInit) { + init(helper.getConnectorWrapper(), getJsCell(cell)); + } + } + + private native void init(JavaScriptObject wrapper, + JavaScriptObject cell) + /*-{ + wrapper.init(cell); + }-*/; + + private native void updateCellReference( + JavaScriptObject cellWrapper, CellReference target) + /*-{ + cellWrapper.target = target; + }-*/; + + @Override + public void destroy(RendererCellReference cell) { + if (hasDestroy) { + destory(helper.getConnectorWrapper(), getJsCell(cell)); + } else { + super.destroy(cell); + } + } + + private native void destory(JavaScriptObject wrapper, + JavaScriptObject cell) + /*-{ + wrapper.destory(cell); + }-*/; + + @Override + public boolean onActivate(CellReference cell) { + if (hasOnActivate) { + return onActivate(helper.getConnectorWrapper(), + getJsCell(cell)); + } else { + return super.onActivate(cell); + } + } + + private native boolean onActivate(JavaScriptObject wrapper, + JavaScriptObject cell) + /*-{ + return !!wrapper.onActivate(cell); + }-*/; + + @Override + public Collection getConsumedEvents() { + if (hasGetConsumedEvents) { + JsArrayString events = getConsumedEvents( + helper.getConnectorWrapper()); + + ArrayList list = new ArrayList( + events.length()); + for (int i = 0; i < events.length(); i++) { + list.add(events.get(i)); + } + return list; + } else { + return super.getConsumedEvents(); + } + } + + private native JsArrayString getConsumedEvents( + JavaScriptObject wrapper) + /*-{ + var rawEvents = wrapper.getConsumedEvents(); + var events = []; + for(var i = 0; i < rawEvents.length; i++) { + events[i] = ""+rawEvents[i]; + } + return events; + }-*/; + + @Override + public boolean onBrowserEvent(CellReference cell, + NativeEvent event) { + if (hasOnBrowserEvent) { + return onBrowserEvent(helper.getConnectorWrapper(), + getJsCell(cell), event); + } else { + return super.onBrowserEvent(cell, event); + } + } + + private native boolean onBrowserEvent(JavaScriptObject wrapper, + JavaScriptObject cell, NativeEvent event) + /*-{ + return !!wrapper.onBrowserEvent(cell, event); + }-*/; + }; + } + + @Override + public Object decode(JsonValue value) { + // Let the js logic decode the raw json that the server sent + return value; + } + + @Override + public void onUnregister() { + super.onUnregister(); + helper.onUnregister(); + } + + @Override + public JavaScriptConnectorHelper getJavascriptConnectorHelper() { + return helper; + } +} + diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java new file mode 100644 index 0000000000..e7494737cb --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java @@ -0,0 +1,386 @@ +/* + * 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.client.connectors; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.CheckBox; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.data.DataSource; +import com.vaadin.client.data.DataSource.RowHandle; +import com.vaadin.client.renderers.ComplexRenderer; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.DataAvailableEvent; +import com.vaadin.client.widget.grid.DataAvailableHandler; +import com.vaadin.client.widget.grid.events.SelectAllEvent; +import com.vaadin.client.widget.grid.events.SelectAllHandler; +import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; +import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; +import com.vaadin.client.widgets.Grid; +import com.vaadin.client.widgets.Grid.HeaderCell; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.Range; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; +import com.vaadin.ui.LegacyGrid.MultiSelectionModel; + +import elemental.json.JsonObject; + +/** + * Connector for server-side {@link MultiSelectionModel}. + * + * @since 7.6 + * @author Vaadin Ltd + */ +@Connect(MultiSelectionModel.class) +public class MultiSelectionModelConnector extends + AbstractSelectionModelConnector> { + + private Multi selectionModel = createSelectionModel(); + private SpaceSelectHandler spaceHandler; + + @Override + protected void extend(ServerConnector target) { + getGrid().setSelectionModel(selectionModel); + spaceHandler = new SpaceSelectHandler(getGrid()); + } + + @Override + public void onUnregister() { + spaceHandler.removeHandler(); + } + + @Override + protected Multi createSelectionModel() { + return new MultiSelectionModel(); + } + + @Override + public MultiSelectionModelState getState() { + return (MultiSelectionModelState) super.getState(); + } + + @OnStateChange("allSelected") + void updateSelectAllCheckbox() { + if (selectionModel.getSelectionColumnRenderer() != null) { + HeaderCell cell = getGrid().getDefaultHeaderRow() + .getCell(getGrid().getColumn(0)); + CheckBox widget = (CheckBox) cell.getWidget(); + widget.setValue(getState().allSelected, false); + } + } + + protected class MultiSelectionModel extends AbstractSelectionModel + implements SelectionModel.Multi.Batched { + + private ComplexRenderer renderer = null; + private Set> selected = new HashSet>(); + private Set> deselected = new HashSet>(); + private HandlerRegistration selectAll; + private HandlerRegistration dataAvailable; + private Range availableRows; + private boolean batchSelect = false; + + @Override + public void setGrid(Grid grid) { + super.setGrid(grid); + if (grid != null) { + renderer = createSelectionColumnRenderer(grid); + selectAll = getGrid().addSelectAllHandler( + new SelectAllHandler() { + + @Override + public void onSelectAll( + SelectAllEvent event) { + selectAll(); + } + }); + dataAvailable = getGrid() + .addDataAvailableHandler(new DataAvailableHandler() { + + @Override + public void onDataAvailable( + DataAvailableEvent event) { + availableRows = event.getAvailableRows(); + } + }); + } else if (renderer != null) { + selectAll.removeHandler(); + dataAvailable.removeHandler(); + renderer = null; + } + } + + /** + * Creates a selection column renderer. This method can be overridden to + * use a custom renderer or use {@code null} to disable the selection + * column. + * + * @param grid + * the grid for this selection model + * @return selection column renderer or {@code null} if not needed + */ + protected ComplexRenderer createSelectionColumnRenderer( + Grid grid) { + return new MultiSelectionRenderer(grid); + } + + /** + * Selects all available rows, sends request to server to select + * everything. + */ + public void selectAll() { + assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; + + DataSource dataSource = getGrid().getDataSource(); + for (int i = availableRows.getStart(); i < availableRows + .getEnd(); ++i) { + final JsonObject row = dataSource.getRow(i); + if (row != null) { + RowHandle handle = dataSource.getHandle(row); + markAsSelected(handle, true); + } + } + + getRpcProxy(MultiSelectionModelServerRpc.class).selectAll(); + } + + @Override + public Renderer getSelectionColumnRenderer() { + return renderer; + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean select(JsonObject... rows) { + return select(Arrays.asList(rows)); + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean deselect(JsonObject... rows) { + return deselect(Arrays.asList(rows)); + } + + /** + * {@inheritDoc} + * + * @return always {@code true} + */ + @Override + public boolean deselectAll() { + assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; + + DataSource dataSource = getGrid().getDataSource(); + for (int i = availableRows.getStart(); i < availableRows + .getEnd(); ++i) { + final JsonObject row = dataSource.getRow(i); + if (row != null) { + RowHandle handle = dataSource.getHandle(row); + markAsSelected(handle, false); + } + } + + getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll(); + + return true; + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean select(Collection rows) { + if (rows.isEmpty()) { + return false; + } + + for (JsonObject row : rows) { + RowHandle rowHandle = getRowHandle(row); + if (markAsSelected(rowHandle, true)) { + selected.add(rowHandle); + } + } + + if (!isBeingBatchSelected()) { + sendSelected(); + } + return true; + } + + /** + * Marks the given row to be selected or deselected. Returns true if the + * value actually changed. + *

+ * Note: If selection model is in batch select state, the row will be + * pinned on select. + * + * @param row + * row handle + * @param selected + * {@code true} if row should be selected; {@code false} if + * not + * @return {@code true} if selected status changed; {@code false} if not + */ + protected boolean markAsSelected(RowHandle row, + boolean selected) { + if (selected && !isSelected(row.getRow())) { + row.getRow().put(GridState.JSONKEY_SELECTED, true); + } else if (!selected && isSelected(row.getRow())) { + row.getRow().remove(GridState.JSONKEY_SELECTED); + } else { + return false; + } + + row.updateRow(); + + if (isBeingBatchSelected()) { + row.pin(); + } + return true; + } + + /** + * {@inheritDoc} + * + * @return {@code false} if rows is empty, else {@code true} + */ + @Override + public boolean deselect(Collection rows) { + if (rows.isEmpty()) { + return false; + } + + for (JsonObject row : rows) { + RowHandle rowHandle = getRowHandle(row); + if (markAsSelected(rowHandle, false)) { + deselected.add(rowHandle); + } + } + + if (!isBeingBatchSelected()) { + sendDeselected(); + } + return true; + } + + /** + * Sends a deselect RPC call to server-side containing all deselected + * rows. Unpins any pinned rows. + */ + private void sendDeselected() { + getRpcProxy(MultiSelectionModelServerRpc.class) + .deselect(getRowKeys(deselected)); + + if (isBeingBatchSelected()) { + for (RowHandle row : deselected) { + row.unpin(); + } + } + + deselected.clear(); + } + + /** + * Sends a select RPC call to server-side containing all selected rows. + * Unpins any pinned rows. + */ + private void sendSelected() { + getRpcProxy(MultiSelectionModelServerRpc.class) + .select(getRowKeys(selected)); + + if (isBeingBatchSelected()) { + for (RowHandle row : selected) { + row.unpin(); + } + } + + selected.clear(); + } + + private List getRowKeys(Set> handles) { + List keys = new ArrayList(); + for (RowHandle handle : handles) { + keys.add(getRowKey(handle.getRow())); + } + return keys; + } + + private Set getRows(Set> handles) { + Set rows = new HashSet(); + for (RowHandle handle : handles) { + rows.add(handle.getRow()); + } + return rows; + } + + @Override + public void startBatchSelect() { + assert selected.isEmpty() + && deselected.isEmpty() : "Row caches were not clear."; + batchSelect = true; + } + + @Override + public void commitBatchSelect() { + assert batchSelect : "Not batch selecting."; + if (!selected.isEmpty()) { + sendSelected(); + } + + if (!deselected.isEmpty()) { + sendDeselected(); + } + batchSelect = false; + } + + @Override + public boolean isBeingBatchSelected() { + return batchSelect; + } + + @Override + public Collection getSelectedRowsBatch() { + return Collections.unmodifiableSet(getRows(selected)); + } + + @Override + public Collection getDeselectedRowsBatch() { + return Collections.unmodifiableSet(getRows(deselected)); + } + } +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java new file mode 100644 index 0000000000..1a080f5082 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java @@ -0,0 +1,45 @@ +/* + * 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.client.connectors; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModelNone; +import com.vaadin.shared.ui.Connect; +import com.vaadin.ui.LegacyGrid.NoSelectionModel; + +import elemental.json.JsonObject; + +/** + * Connector for server-side {@link NoSelectionModel}. + * + * @since 7.6 + * @author Vaadin Ltd + */ +@Connect(NoSelectionModel.class) +public class NoSelectionModelConnector + extends AbstractSelectionModelConnector> { + + @Override + protected void extend(ServerConnector target) { + getGrid().setSelectionModel(createSelectionModel()); + } + + @Override + protected SelectionModel createSelectionModel() { + return new SelectionModelNone(); + } +} \ No newline at end of file diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java new file mode 100644 index 0000000000..ff16047b0d --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java @@ -0,0 +1,34 @@ +/* + * 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.client.connectors; + +import com.vaadin.shared.ui.Connect; + +/** + * A connector for {@link com.vaadin.ui.components.grid.renderers.NumberRenderer + * NumberRenderer} . + *

+ * The server-side Renderer operates on numbers, but the data is serialized as a + * string, and displayed as-is on the client side. This is to be able to support + * the server's locale. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.renderers.NumberRenderer.class) +public class NumberRendererConnector extends TextRendererConnector { + // no implementation needed +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java new file mode 100644 index 0000000000..5687d031aa --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java @@ -0,0 +1,35 @@ +/* + * 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.client.connectors; + +import com.vaadin.client.renderers.ProgressBarRenderer; +import com.vaadin.shared.ui.Connect; + +/** + * A connector for {@link ProgressBarRenderer}. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.renderers.ProgressBarRenderer.class) +public class ProgressBarRendererConnector + extends AbstractGridRendererConnector { + + @Override + public ProgressBarRenderer getRenderer() { + return (ProgressBarRenderer) super.getRenderer(); + } +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java new file mode 100644 index 0000000000..52fa8a2e48 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java @@ -0,0 +1,253 @@ +/* + * 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.client.connectors; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.data.AbstractRemoteDataSource; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.shared.data.DataProviderRpc; +import com.vaadin.shared.data.DataRequestRpc; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.Range; + +import elemental.json.Json; +import elemental.json.JsonArray; +import elemental.json.JsonObject; + +/** + * Connects a Vaadin server-side container data source to a Grid. This is + * currently implemented as an Extension hardcoded to support a specific + * connector type. This will be changed once framework support for something + * more flexible has been implemented. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.server.communication.data.RpcDataProviderExtension.class) +public class RpcDataSourceConnector extends AbstractExtensionConnector { + + /** + * A callback interface to let {@link GridConnector} know that detail + * visibilities might have changed. + * + * @since 7.5.0 + * @author Vaadin Ltd + */ + interface DetailsListener { + + /** + * A request to verify (and correct) the visibility for a row, given + * updated metadata. + * + * @param rowIndex + * the index of the row that should be checked + * @param row + * the row object to check visibility for + * @see GridState#JSONKEY_DETAILS_VISIBLE + */ + void reapplyDetailsVisibility(int rowIndex, JsonObject row); + } + + public class RpcDataSource extends AbstractRemoteDataSource { + + protected RpcDataSource() { + registerRpc(DataProviderRpc.class, new DataProviderRpc() { + @Override + public void setRowData(int firstRow, JsonArray rowArray) { + ArrayList rows = new ArrayList( + rowArray.length()); + for (int i = 0; i < rowArray.length(); i++) { + JsonObject rowObject = rowArray.getObject(i); + rows.add(rowObject); + } + + RpcDataSource.this.setRowData(firstRow, rows); + } + + @Override + public void removeRowData(int firstRow, int count) { + RpcDataSource.this.removeRowData(firstRow, count); + } + + @Override + public void insertRowData(int firstRow, int count) { + RpcDataSource.this.insertRowData(firstRow, count); + } + + @Override + public void resetDataAndSize(int size) { + RpcDataSource.this.resetDataAndSize(size); + } + + @Override + public void updateRowData(JsonArray rowArray) { + for (int i = 0; i < rowArray.length(); ++i) { + RpcDataSource.this.updateRowData(rowArray.getObject(i)); + } + } + }); + } + + private DataRequestRpc rpcProxy = getRpcProxy(DataRequestRpc.class); + private DetailsListener detailsListener; + private JsonArray droppedRowKeys = Json.createArray(); + + @Override + protected void requestRows(int firstRowIndex, int numberOfRows, + RequestRowsCallback callback) { + if (droppedRowKeys.length() > 0) { + rpcProxy.dropRows(droppedRowKeys); + droppedRowKeys = Json.createArray(); + } + + /* + * If you're looking at this code because you want to learn how to + * use AbstactRemoteDataSource, please look somewhere else instead. + * + * We're not doing things in the conventional way with the callback + * here since Vaadin doesn't directly support RPC with return + * values. We're instead asking the server to push us some data, and + * when we receive pushed data, we just push it along to the + * underlying cache in the same way no matter if it was a genuine + * push or just a result of us requesting rows. + */ + + Range cached = getCachedRange(); + + rpcProxy.requestRows(firstRowIndex, numberOfRows, cached.getStart(), + cached.length()); + + /* + * Show the progress indicator if there is a pending data request + * and some of the visible rows are being requested. The RPC in + * itself will not trigger the indicator since it might just fetch + * some rows in the background to fill the cache. + * + * The indicator will be hidden by the framework when the response + * is received (unless another request is already on its way at that + * point). + */ + if (getRequestedAvailability().intersects( + Range.withLength(firstRowIndex, numberOfRows))) { + getConnection().getLoadingIndicator().ensureTriggered(); + } + } + + @Override + public void ensureAvailability(int firstRowIndex, int numberOfRows) { + super.ensureAvailability(firstRowIndex, numberOfRows); + + /* + * We trigger the indicator already at this point since the actual + * RPC will not be sent right away when waiting for the response to + * a previous request. + * + * Only triggering here would not be enough since the check that + * sets isWaitingForData is deferred. We don't want to trigger the + * loading indicator here if we don't know that there is actually a + * request going on since some other bug might then cause the + * loading indicator to not be hidden. + */ + if (isWaitingForData() + && !Range.withLength(firstRowIndex, numberOfRows) + .isSubsetOf(getCachedRange())) { + getConnection().getLoadingIndicator().ensureTriggered(); + } + } + + @Override + public String getRowKey(JsonObject row) { + if (row.hasKey(GridState.JSONKEY_ROWKEY)) { + return row.getString(GridState.JSONKEY_ROWKEY); + } else { + return null; + } + } + + public RowHandle getHandleByKey(Object key) { + JsonObject row = Json.createObject(); + row.put(GridState.JSONKEY_ROWKEY, (String) key); + return new RowHandleImpl(row, key); + } + + @Override + protected void unpinHandle(RowHandleImpl handle) { + // Row data is no longer available after it has been unpinned. + String key = getRowKey(handle.getRow()); + super.unpinHandle(handle); + if (!handle.isPinned()) { + if (indexOfKey(key) == -1) { + // Row out of view has been unpinned. drop it + droppedRowKeys.set(droppedRowKeys.length(), key); + } + } + } + + void setDetailsListener(DetailsListener detailsListener) { + this.detailsListener = detailsListener; + } + + @Override + protected void setRowData(int firstRowIndex, List rowData) { + super.setRowData(firstRowIndex, rowData); + + /* + * Intercepting details information from the data source, rerouting + * them back to the GridConnector (as a details listener) + */ + for (int i = 0; i < rowData.size(); i++) { + detailsListener.reapplyDetailsVisibility(firstRowIndex + i, + rowData.get(i)); + } + } + + /** + * Updates row data based on row key. + * + * @since 7.6 + * @param row + * new row object + */ + protected void updateRowData(JsonObject row) { + int index = indexOfKey(getRowKey(row)); + if (index >= 0) { + setRowData(index, Collections.singletonList(row)); + } + } + + @Override + protected void onDropFromCache(int rowIndex, JsonObject row) { + if (!isPinned(row)) { + droppedRowKeys.set(droppedRowKeys.length(), getRowKey(row)); + } + } + } + + private final RpcDataSource dataSource = new RpcDataSource(); + + @Override + protected void extend(ServerConnector target) { + GridConnector gridConnector = (GridConnector) target; + dataSource.setDetailsListener(gridConnector.getDetailsListener()); + gridConnector.setDataSource(dataSource); + } +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java new file mode 100644 index 0000000000..980c4458d4 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java @@ -0,0 +1,180 @@ +/* + * 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.client.connectors; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.data.DataSource.RowHandle; +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.selection.ClickSelectHandler; +import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.client.widget.grid.selection.SelectionModel.Single; +import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; +import com.vaadin.ui.LegacyGrid.SingleSelectionModel; + +import elemental.json.JsonObject; + +/** + * Connector for server-side {@link SingleSelectionModel}. + * + * @since 7.6 + * @author Vaadin Ltd + */ +@Connect(SingleSelectionModel.class) +public class SingleSelectionModelConnector extends + AbstractSelectionModelConnector> { + + private SpaceSelectHandler spaceHandler; + private ClickSelectHandler clickHandler; + private Single selectionModel = createSelectionModel(); + + @Override + protected void extend(ServerConnector target) { + getGrid().setSelectionModel(selectionModel); + spaceHandler = new SpaceSelectHandler(getGrid()); + clickHandler = new ClickSelectHandler(getGrid()); + } + + @Override + public SingleSelectionModelState getState() { + return (SingleSelectionModelState) super.getState(); + } + + @Override + public void onUnregister() { + spaceHandler.removeHandler(); + clickHandler.removeHandler(); + + super.onUnregister(); + } + + @Override + protected Single createSelectionModel() { + return new SingleSelectionModel(); + } + + @OnStateChange("deselectAllowed") + void updateDeselectAllowed() { + selectionModel.setDeselectAllowed(getState().deselectAllowed); + } + + /** + * SingleSelectionModel without a selection column renderer. + */ + public class SingleSelectionModel extends AbstractSelectionModel + implements SelectionModel.Single { + + private RowHandle selectedRow; + private boolean deselectAllowed; + + @Override + public Renderer getSelectionColumnRenderer() { + return null; + } + + @Override + public void reset() { + super.reset(); + + // Clean up selected row + if (selectedRow != null) { + clearSelectedRow(); + } + } + + @Override + public boolean select(JsonObject row) { + boolean changed = false; + + if (row == null && !isDeselectAllowed()) { + // Attempting to deselect, even though it's not allowed. + } else { + if (selectedRow != null) { + // Check if currently re-selected row was deselected from + // the server. + if (row != null && getRowHandle(row).equals(selectedRow)) { + if (selectedRow.getRow() + .hasKey(GridState.JSONKEY_SELECTED)) { + // Everything is OK, no need to do anything. + return false; + } + } + + // Remove old selected row + clearSelectedRow(); + changed = true; + } + + if (row != null) { + // Select the new row. + setSelectedRow(row); + changed = true; + } + } + + if (changed) { + getRpcProxy(SingleSelectionModelServerRpc.class) + .select(getRowKey(row)); + } + + return changed; + } + + private void setSelectedRow(JsonObject row) { + selectedRow = getRowHandle(row); + selectedRow.pin(); + selectedRow.getRow().put(GridState.JSONKEY_SELECTED, true); + selectedRow.updateRow(); + } + + private void clearSelectedRow() { + selectedRow.getRow().remove(GridState.JSONKEY_SELECTED); + selectedRow.updateRow(); + selectedRow.unpin(); + selectedRow = null; + } + + @Override + public boolean deselect(JsonObject row) { + if (getRowHandle(row).equals(selectedRow)) { + select(null); + } + return false; + } + + @Override + public JsonObject getSelectedRow() { + throw new UnsupportedOperationException( + "This client-side selection model " + + getClass().getSimpleName() + + " does not know selected row."); + } + + @Override + public void setDeselectAllowed(boolean deselectAllowed) { + this.deselectAllowed = deselectAllowed; + } + + @Override + public boolean isDeselectAllowed() { + return deselectAllowed; + } + } +} \ No newline at end of file diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java new file mode 100644 index 0000000000..d3a289ec3e --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java @@ -0,0 +1,34 @@ +/* + * 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.client.connectors; + +import com.vaadin.client.renderers.TextRenderer; +import com.vaadin.shared.ui.Connect; + +/** + * A connector for {@link TextRenderer}. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.renderers.TextRenderer.class) +public class TextRendererConnector extends AbstractGridRendererConnector { + + @Override + public TextRenderer getRenderer() { + return (TextRenderer) super.getRenderer(); + } +} diff --git a/compatibility-client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java b/compatibility-client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java new file mode 100644 index 0000000000..95c47dd242 --- /dev/null +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java @@ -0,0 +1,43 @@ +/* + * 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.client.connectors; + +import com.vaadin.client.renderers.Renderer; +import com.vaadin.client.widget.grid.RendererCellReference; +import com.vaadin.shared.ui.Connect; + +/** + * A connector for {@link UnsafeHtmlRenderer} + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.renderers.HtmlRenderer.class) +public class UnsafeHtmlRendererConnector + extends AbstractGridRendererConnector { + + public static class UnsafeHtmlRenderer implements Renderer { + @Override + public void render(RendererCellReference cell, String data) { + cell.getElement().setInnerHTML(data); + } + } + + @Override + public UnsafeHtmlRenderer getRenderer() { + return (UnsafeHtmlRenderer) super.getRenderer(); + } +} diff --git a/compatibility-client/src/main/resources/com/vaadin/Vaadin7WidgetSet.gwt.xml b/compatibility-client/src/main/resources/com/vaadin/Vaadin7WidgetSet.gwt.xml new file mode 100755 index 0000000000..e124491d3a --- /dev/null +++ b/compatibility-client/src/main/resources/com/vaadin/Vaadin7WidgetSet.gwt.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/compatibility-client/src/main/resources/com/vaadin/v7/Vaadin7WidgetSet.gwt.xml b/compatibility-client/src/main/resources/com/vaadin/v7/Vaadin7WidgetSet.gwt.xml deleted file mode 100755 index e124491d3a..0000000000 --- a/compatibility-client/src/main/resources/com/vaadin/v7/Vaadin7WidgetSet.gwt.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/compatibility-server/pom.xml b/compatibility-server/pom.xml index 713c39c055..38dcc07f21 100644 --- a/compatibility-server/pom.xml +++ b/compatibility-server/pom.xml @@ -21,12 +21,29 @@ vaadin-server ${project.version} + + ${project.groupId} + vaadin-server + ${project.version} + tests + test + ${project.groupId} vaadin-compatibility-shared ${project.version} - + + javax.servlet + javax.servlet-api + provided + + + + javax.validation + validation-api + provided + diff --git a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java new file mode 100644 index 0000000000..96e4621761 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java @@ -0,0 +1,272 @@ +/* + * 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.data.fieldgroup; + +import java.beans.IntrospectionException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import com.vaadin.data.Item; +import com.vaadin.data.util.BeanItem; +import com.vaadin.data.util.BeanUtil; +import com.vaadin.v7.data.validator.LegacyBeanValidator; +import com.vaadin.v7.ui.LegacyField; + +public class BeanFieldGroup extends FieldGroup { + + private final Class beanType; + + private static Boolean beanValidationImplementationAvailable = null; + private final Map, LegacyBeanValidator> defaultValidators; + + public BeanFieldGroup(Class beanType) { + this.beanType = beanType; + this.defaultValidators = new HashMap, LegacyBeanValidator>(); + } + + @Override + protected Class getPropertyType(Object propertyId) { + if (getItemDataSource() != null) { + return super.getPropertyType(propertyId); + } else { + // Data source not set so we need to figure out the type manually + /* + * toString should never really be needed as propertyId should be of + * form "fieldName" or "fieldName.subField[.subField2]" but the + * method declaration comes from parent. + */ + try { + Class type = BeanUtil.getPropertyType(beanType, + propertyId.toString()); + if (type == null) { + throw new BindException( + "Cannot determine type of propertyId '" + propertyId + + "'. The propertyId was not found in " + + beanType.getName()); + } + return type; + } catch (IntrospectionException e) { + throw new BindException("Cannot determine type of propertyId '" + + propertyId + "'. Unable to introspect " + beanType, + e); + } + } + } + + @Override + protected Object findPropertyId(java.lang.reflect.Field memberField) { + String fieldName = memberField.getName(); + Item dataSource = getItemDataSource(); + if (dataSource != null + && dataSource.getItemProperty(fieldName) != null) { + return fieldName; + } else { + String minifiedFieldName = minifyFieldName(fieldName); + try { + return getFieldName(beanType, minifiedFieldName); + } catch (SecurityException e) { + } catch (NoSuchFieldException e) { + } + } + return null; + } + + private static String getFieldName(Class cls, String propertyId) + throws SecurityException, NoSuchFieldException { + for (java.lang.reflect.Field field1 : cls.getDeclaredFields()) { + if (propertyId.equals(minifyFieldName(field1.getName()))) { + return field1.getName(); + } + } + // Try super classes until we reach Object + Class superClass = cls.getSuperclass(); + if (superClass != null && superClass != Object.class) { + return getFieldName(superClass, propertyId); + } else { + throw new NoSuchFieldException(); + } + } + + /** + * Helper method for setting the data source directly using a bean. This + * method wraps the bean in a {@link BeanItem} and calls + * {@link #setItemDataSource(Item)}. + *

+ * For null values, a null item is passed to + * {@link #setItemDataSource(Item)} to be properly clear fields. + * + * @param bean + * The bean to use as data source. + */ + public void setItemDataSource(T bean) { + if (bean == null) { + setItemDataSource((Item) null); + } else { + setItemDataSource(new BeanItem(bean, beanType)); + } + } + + @Override + public void setItemDataSource(Item item) { + if (item == null || (item instanceof BeanItem)) { + super.setItemDataSource(item); + } else { + throw new RuntimeException(getClass().getSimpleName() + + " only supports BeanItems as item data source"); + } + } + + @Override + public BeanItem getItemDataSource() { + return (BeanItem) super.getItemDataSource(); + } + + private void ensureNestedPropertyAdded(Object propertyId) { + if (getItemDataSource() != null) { + // The data source is set so the property must be found in the item. + // If it is not we try to add it. + try { + getItemProperty(propertyId); + } catch (BindException e) { + // Not found, try to add a nested property; + // BeanItem property ids are always strings so this is safe + getItemDataSource().addNestedProperty((String) propertyId); + } + } + } + + @Override + public void bind(LegacyField field, Object propertyId) { + ensureNestedPropertyAdded(propertyId); + super.bind(field, propertyId); + } + + @Override + public T buildAndBind(String caption, + Object propertyId, Class fieldType) throws BindException { + ensureNestedPropertyAdded(propertyId); + return super.buildAndBind(caption, propertyId, fieldType); + } + + @Override + public void unbind(LegacyField field) throws BindException { + super.unbind(field); + + LegacyBeanValidator removed = defaultValidators.remove(field); + if (removed != null) { + field.removeValidator(removed); + } + } + + @Override + protected void configureField(LegacyField field) { + super.configureField(field); + // Add Bean validators if there are annotations + if (isBeanValidationImplementationAvailable() + && !defaultValidators.containsKey(field)) { + LegacyBeanValidator validator = new LegacyBeanValidator(beanType, + getPropertyId(field).toString()); + field.addValidator(validator); + if (field.getLocale() != null) { + validator.setLocale(field.getLocale()); + } + defaultValidators.put(field, validator); + } + } + + /** + * Checks whether a bean validation implementation (e.g. Hibernate Validator + * or Apache Bean Validation) is available. + * + * TODO move this method to some more generic location + * + * @return true if a JSR-303 bean validation implementation is available + */ + protected static boolean isBeanValidationImplementationAvailable() { + if (beanValidationImplementationAvailable != null) { + return beanValidationImplementationAvailable; + } + try { + Class validationClass = Class + .forName("javax.validation.Validation"); + Method buildFactoryMethod = validationClass + .getMethod("buildDefaultValidatorFactory"); + Object factory = buildFactoryMethod.invoke(null); + beanValidationImplementationAvailable = (factory != null); + } catch (Exception e) { + // no bean validation implementation available + beanValidationImplementationAvailable = false; + } + return beanValidationImplementationAvailable; + } + + /** + * Convenience method to bind Fields from a given "field container" to a + * given bean with buffering disabled. + *

+ * The returned {@link BeanFieldGroup} can be used for further + * configuration. + * + * @see #bindFieldsBuffered(Object, Object) + * @see #bindMemberFields(Object) + * @since 7.2 + * @param bean + * the bean to be bound + * @param objectWithMemberFields + * the class that contains {@link LegacyField}s for bean + * properties + * @return the bean field group used to make binding + */ + public static BeanFieldGroup bindFieldsUnbuffered(T bean, + Object objectWithMemberFields) { + return createAndBindFields(bean, objectWithMemberFields, false); + } + + /** + * Convenience method to bind Fields from a given "field container" to a + * given bean with buffering enabled. + *

+ * The returned {@link BeanFieldGroup} can be used for further + * configuration. + * + * @see #bindFieldsUnbuffered(Object, Object) + * @see #bindMemberFields(Object) + * @since 7.2 + * @param bean + * the bean to be bound + * @param objectWithMemberFields + * the class that contains {@link LegacyField}s for bean + * properties + * @return the bean field group used to make binding + */ + public static BeanFieldGroup bindFieldsBuffered(T bean, + Object objectWithMemberFields) { + return createAndBindFields(bean, objectWithMemberFields, true); + } + + private static BeanFieldGroup createAndBindFields(T bean, + Object objectWithMemberFields, boolean buffered) { + @SuppressWarnings("unchecked") + BeanFieldGroup beanFieldGroup = new BeanFieldGroup( + (Class) bean.getClass()); + beanFieldGroup.setItemDataSource(bean); + beanFieldGroup.setBuffered(buffered); + beanFieldGroup.bindMemberFields(objectWithMemberFields); + return beanFieldGroup; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/Caption.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/Caption.java new file mode 100644 index 0000000000..d752aa78d2 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/Caption.java @@ -0,0 +1,27 @@ +/* + * 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.data.fieldgroup; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface Caption { + String value(); +} diff --git a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java new file mode 100644 index 0000000000..24c97eedc5 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java @@ -0,0 +1,254 @@ +/* + * 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.data.fieldgroup; + +import java.util.Date; +import java.util.EnumSet; + +import com.vaadin.data.Item; +import com.vaadin.data.fieldgroup.FieldGroup.BindException; +import com.vaadin.ui.AbstractSelect; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.ListSelect; +import com.vaadin.ui.NativeSelect; +import com.vaadin.ui.OptionGroup; +import com.vaadin.ui.RichTextArea; +import com.vaadin.ui.Table; +import com.vaadin.v7.ui.LegacyAbstractField; +import com.vaadin.v7.ui.LegacyAbstractTextField; +import com.vaadin.v7.ui.LegacyCheckBox; +import com.vaadin.v7.ui.LegacyDateField; +import com.vaadin.v7.ui.LegacyField; +import com.vaadin.v7.ui.LegacyInlineDateField; +import com.vaadin.v7.ui.LegacyPopupDateField; +import com.vaadin.v7.ui.LegacyTextField; + +/** + * This class contains a basic implementation for {@link FieldGroupFieldFactory} + * .The class is singleton, use {@link #get()} method to get reference to the + * instance. + * + * @author Vaadin Ltd + */ +public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory { + + private static final DefaultFieldGroupFieldFactory INSTANCE = new DefaultFieldGroupFieldFactory(); + + public static final Object CAPTION_PROPERTY_ID = "Caption"; + + protected DefaultFieldGroupFieldFactory() { + } + + /** + * Gets the singleton instance. + * + * @since 7.4 + * + * @return the singleton instance + */ + public static DefaultFieldGroupFieldFactory get() { + return INSTANCE; + } + + @Override + public T createField(Class type, + Class fieldType) { + if (Enum.class.isAssignableFrom(type)) { + return createEnumField(type, fieldType); + } else if (Date.class.isAssignableFrom(type)) { + return createDateField(type, fieldType); + } else if (Boolean.class.isAssignableFrom(type) + || boolean.class.isAssignableFrom(type)) { + return createBooleanField(fieldType); + } + if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { + return fieldType.cast(createAbstractTextField( + fieldType.asSubclass(LegacyAbstractTextField.class))); + } else if (fieldType == RichTextArea.class) { + return fieldType.cast(createRichTextArea()); + } + return createDefaultField(type, fieldType); + } + + protected RichTextArea createRichTextArea() { + RichTextArea rta = new RichTextArea(); + rta.setImmediate(true); + + return rta; + } + + private T createEnumField(Class type, + Class fieldType) { + // Determine first if we should (or can) create a select for the enum + Class selectClass = null; + if (AbstractSelect.class.isAssignableFrom(fieldType)) { + selectClass = (Class) fieldType; + } else if (anySelect(fieldType)) { + selectClass = AbstractSelect.class; + } + + if (selectClass != null) { + AbstractSelect s = createCompatibleSelect(selectClass); + populateWithEnumData(s, (Class) type); + return (T) s; + } else if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { + return (T) createAbstractTextField( + (Class) fieldType); + } + + return null; + } + + private T createDateField(Class type, + Class fieldType) { + LegacyAbstractField field; + + if (LegacyInlineDateField.class.isAssignableFrom(fieldType)) { + field = new LegacyInlineDateField(); + } else if (anyField(fieldType) + || LegacyDateField.class.isAssignableFrom(fieldType)) { + field = new LegacyPopupDateField(); + } else if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { + field = createAbstractTextField( + (Class) fieldType); + } else { + return null; + } + + field.setImmediate(true); + return (T) field; + } + + protected AbstractSelect createCompatibleSelect( + Class fieldType) { + AbstractSelect select; + if (fieldType.isAssignableFrom(ListSelect.class)) { + select = new ListSelect(); + select.setMultiSelect(false); + } else if (fieldType.isAssignableFrom(NativeSelect.class)) { + select = new NativeSelect(); + } else if (fieldType.isAssignableFrom(OptionGroup.class)) { + select = new OptionGroup(); + select.setMultiSelect(false); + } else if (fieldType.isAssignableFrom(Table.class)) { + Table t = new Table(); + t.setSelectable(true); + select = t; + } else { + select = new ComboBox(null); + } + select.setImmediate(true); + select.setNullSelectionAllowed(false); + + return select; + } + + /** + * @since 7.4 + * @param fieldType + * the type of the field + * @return true if any LegacyAbstractField can be assigned to the field + */ + protected boolean anyField(Class fieldType) { + return fieldType == LegacyField.class + || fieldType == LegacyAbstractField.class; + } + + /** + * @since 7.4 + * @param fieldType + * the type of the field + * @return true if any AbstractSelect can be assigned to the field + */ + protected boolean anySelect(Class fieldType) { + return anyField(fieldType) || fieldType == AbstractSelect.class; + } + + protected T createBooleanField(Class fieldType) { + if (fieldType.isAssignableFrom(LegacyCheckBox.class)) { + LegacyCheckBox cb = new LegacyCheckBox(null); + cb.setImmediate(true); + return (T) cb; + } else if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { + return (T) createAbstractTextField( + (Class) fieldType); + } + + return null; + } + + protected T createAbstractTextField( + Class fieldType) { + if (fieldType == LegacyAbstractTextField.class) { + fieldType = (Class) LegacyTextField.class; + } + try { + T field = fieldType.newInstance(); + field.setImmediate(true); + return field; + } catch (Exception e) { + throw new BindException( + "Could not create a field of type " + fieldType, e); + } + } + + /** + * Fallback when no specific field has been created. Typically returns a + * TextField. + * + * @param + * The type of field to create + * @param type + * The type of data that should be edited + * @param fieldType + * The type of field to create + * @return A field capable of editing the data or null if no field could be + * created + */ + protected T createDefaultField(Class type, + Class fieldType) { + if (fieldType.isAssignableFrom(LegacyTextField.class)) { + return fieldType + .cast(createAbstractTextField(LegacyTextField.class)); + } + return null; + } + + /** + * Populates the given select with all the enums in the given {@link Enum} + * class. Uses {@link Enum}.toString() for caption. + * + * @param select + * The select to populate + * @param enumClass + * The Enum class to use + */ + protected void populateWithEnumData(AbstractSelect select, + Class enumClass) { + select.removeAllItems(); + for (Object p : select.getContainerPropertyIds()) { + select.removeContainerProperty(p); + } + select.addContainerProperty(CAPTION_PROPERTY_ID, String.class, ""); + select.setItemCaptionPropertyId(CAPTION_PROPERTY_ID); + @SuppressWarnings("unchecked") + EnumSet enumSet = EnumSet.allOf(enumClass); + for (Object r : enumSet) { + Item newItem = select.addItem(r); + newItem.getItemProperty(CAPTION_PROPERTY_ID).setValue(r.toString()); + } + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java new file mode 100644 index 0000000000..1254009cfc --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java @@ -0,0 +1,1258 @@ +/* + * 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.data.fieldgroup; + +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.util.TransactionalPropertyWrapper; +import com.vaadin.ui.DefaultFieldFactory; +import com.vaadin.util.ReflectTools; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.ui.LegacyAbstractField; +import com.vaadin.v7.ui.LegacyField; + +/** + * FieldGroup provides an easy way of binding fields to data and handling + * commits of these fields. + *

+ * The typical use case is to create a layout outside the FieldGroup and then + * use FieldGroup to bind the fields to a data source. + *

+ *

+ * {@link FieldGroup} is not a UI component so it cannot be added to a layout. + * Using the buildAndBind methods {@link FieldGroup} can create fields for you + * using a FieldGroupFieldFactory but you still have to add them to the correct + * position in your layout. + *

+ * + * @author Vaadin Ltd + * @since 7.0 + */ +public class FieldGroup implements Serializable { + + private Item itemDataSource; + private boolean buffered = true; + + private boolean enabled = true; + private boolean readOnly = false; + + private HashMap> propertyIdToField = new HashMap>(); + private LinkedHashMap, Object> fieldToPropertyId = new LinkedHashMap, Object>(); + private List commitHandlers = new ArrayList(); + + /** + * The field factory used by builder methods. + */ + private FieldGroupFieldFactory fieldFactory = DefaultFieldGroupFieldFactory + .get(); + + /** + * Constructs a field binder. Use {@link #setItemDataSource(Item)} to set a + * data source for the field binder. + * + */ + public FieldGroup() { + } + + /** + * Constructs a field binder that uses the given data source. + * + * @param itemDataSource + * The data source to bind the fields to + */ + public FieldGroup(Item itemDataSource) { + setItemDataSource(itemDataSource); + } + + /** + * Updates the item that is used by this FieldBinder. Rebinds all fields to + * the properties in the new item. + * + * @param itemDataSource + * The new item to use + */ + public void setItemDataSource(Item itemDataSource) { + this.itemDataSource = itemDataSource; + + for (LegacyField f : fieldToPropertyId.keySet()) { + bind(f, fieldToPropertyId.get(f)); + } + } + + /** + * Gets the item used by this FieldBinder. Note that you must call + * {@link #commit()} for the item to be updated unless buffered mode has + * been switched off. + * + * @see #setBuffered(boolean) + * @see #commit() + * + * @return The item used by this FieldBinder + */ + public Item getItemDataSource() { + return itemDataSource; + } + + /** + * Checks the buffered mode for the bound fields. + *

+ * + * @see #setBuffered(boolean) for more details on buffered mode + * + * @see LegacyField#isBuffered() + * @return true if buffered mode is on, false otherwise + * + */ + public boolean isBuffered() { + return buffered; + } + + /** + * Sets the buffered mode for the bound fields. + *

+ * When buffered mode is on the item will not be updated until + * {@link #commit()} is called. If buffered mode is off the item will be + * updated once the fields are updated. + *

+ *

+ * The default is to use buffered mode. + *

+ * + * @see LegacyField#setBuffered(boolean) + * @param buffered + * true to turn on buffered mode, false otherwise + */ + public void setBuffered(boolean buffered) { + if (buffered == this.buffered) { + return; + } + + this.buffered = buffered; + for (LegacyField field : getFields()) { + field.setBuffered(buffered); + } + } + + /** + * Returns the enabled status for the fields. + *

+ * Note that this will not accurately represent the enabled status of all + * fields if you change the enabled status of the fields through some other + * method than {@link #setEnabled(boolean)}. + * + * @return true if the fields are enabled, false otherwise + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Updates the enabled state of all bound fields. + * + * @param fieldsEnabled + * true to enable all bound fields, false to disable them + */ + public void setEnabled(boolean fieldsEnabled) { + enabled = fieldsEnabled; + for (LegacyField field : getFields()) { + field.setEnabled(fieldsEnabled); + } + } + + /** + * Returns the read only status that is used by default with all fields that + * have a writable data source. + *

+ * Note that this will not accurately represent the read only status of all + * fields if you change the read only status of the fields through some + * other method than {@link #setReadOnly(boolean)}. + * + * @return true if the fields are set to read only, false otherwise + */ + public boolean isReadOnly() { + return readOnly; + } + + /** + * Sets the read only state to the given value for all fields with writable + * data source. Fields with read only data source will always be set to read + * only. + * + * @param fieldsReadOnly + * true to set the fields with writable data source to read only, + * false to set them to read write + */ + public void setReadOnly(boolean fieldsReadOnly) { + readOnly = fieldsReadOnly; + for (LegacyField field : getFields()) { + if (field.getPropertyDataSource() == null + || !field.getPropertyDataSource().isReadOnly()) { + field.setReadOnly(fieldsReadOnly); + } else { + field.setReadOnly(true); + } + } + } + + /** + * Returns a collection of all fields that have been bound. + *

+ * The fields are not returned in any specific order. + *

+ * + * @return A collection with all bound Fields + */ + public Collection> getFields() { + return fieldToPropertyId.keySet(); + } + + /** + * Binds the field with the given propertyId from the current item. If an + * item has not been set then the binding is postponed until the item is set + * using {@link #setItemDataSource(Item)}. + *

+ * This method also adds validators when applicable. + *

+ * + * @param field + * The field to bind + * @param propertyId + * The propertyId to bind to the field + * @throws BindException + * If the field is null or the property id is already bound to + * another field by this field binder + */ + public void bind(LegacyField field, Object propertyId) + throws BindException { + throwIfFieldIsNull(field, propertyId); + throwIfPropertyIdAlreadyBound(field, propertyId); + + 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); + } + + // Will be bound when data source is set + return; + } + + field.setPropertyDataSource( + wrapInTransactionalProperty(getItemProperty(propertyId))); + configureField(field); + } + + /** + * Wrap property to transactional property. + */ + protected Property.Transactional wrapInTransactionalProperty( + Property itemProperty) { + return new TransactionalPropertyWrapper(itemProperty); + } + + private void throwIfFieldIsNull(LegacyField field, Object propertyId) { + if (field == null) { + throw new BindException(String.format( + "Cannot bind property id '%s' to a null field.", + propertyId)); + } + } + + private void throwIfPropertyIdAlreadyBound(LegacyField field, + Object propertyId) { + if (propertyIdToField.containsKey(propertyId) + && propertyIdToField.get(propertyId) != field) { + throw new BindException("Property id " + propertyId + + " is already bound to another field"); + } + } + + /** + * Gets the property with the given property id from the item. + * + * @param propertyId + * The id if the property to find + * @return The property with the given id from the item + * @throws BindException + * If the property was not found in the item or no item has been + * set + */ + protected Property getItemProperty(Object propertyId) throws BindException { + Item item = getItemDataSource(); + if (item == null) { + throw new BindException("Could not lookup property with id " + + propertyId + " as no item has been set"); + } + Property p = item.getItemProperty(propertyId); + if (p == null) { + throw new BindException("A property with id " + propertyId + + " was not found in the item"); + } + return p; + } + + /** + * Detaches the field from its property id and removes it from this + * FieldBinder. + *

+ * Note that the field is not detached from its property data source if it + * is no longer connected to the same property id it was bound to using this + * FieldBinder. + * + * @param field + * The field to detach + * @throws BindException + * If the field is not bound by this field binder or not bound + * to the correct property id + */ + public void unbind(LegacyField field) throws BindException { + Object propertyId = fieldToPropertyId.get(field); + if (propertyId == null) { + throw new BindException( + "The given field is not part of this FieldBinder"); + } + + TransactionalPropertyWrapper wrapper = null; + Property fieldDataSource = field.getPropertyDataSource(); + if (fieldDataSource instanceof TransactionalPropertyWrapper) { + wrapper = (TransactionalPropertyWrapper) fieldDataSource; + fieldDataSource = ((TransactionalPropertyWrapper) fieldDataSource) + .getWrappedProperty(); + + } + if (getItemDataSource() != null + && fieldDataSource == getItemProperty(propertyId)) { + if (null != wrapper) { + wrapper.detachFromProperty(); + } + field.setPropertyDataSource(null); + } + fieldToPropertyId.remove(field); + propertyIdToField.remove(propertyId); + } + + /** + * Configures a field with the settings set for this FieldBinder. + *

+ * By default this updates the buffered, read only and enabled state of the + * field. Also adds validators when applicable. Fields with read only data + * source are always configured as read only. + * + * @param field + * The field to update + */ + protected void configureField(LegacyField field) { + field.setBuffered(isBuffered()); + + field.setEnabled(isEnabled()); + + if (field.getPropertyDataSource().isReadOnly()) { + field.setReadOnly(true); + } else { + field.setReadOnly(isReadOnly()); + } + } + + /** + * Gets the type of the property with the given property id. + * + * @param propertyId + * The propertyId. Must be find + * @return The type of the property + */ + protected Class getPropertyType(Object propertyId) throws BindException { + if (getItemDataSource() == null) { + throw new BindException("Property type for '" + propertyId + + "' could not be determined. No item data source has been set."); + } + Property p = getItemDataSource().getItemProperty(propertyId); + if (p == null) { + throw new BindException("Property type for '" + propertyId + + "' could not be determined. No property with that id was found."); + } + + return p.getType(); + } + + /** + * Returns a collection of all property ids that have been bound to fields. + *

+ * Note that this will return property ids even before the item has been + * set. In that case it returns the property ids that will be bound once the + * item is set. + *

+ *

+ * No guarantee is given for the order of the property ids + *

+ * + * @return A collection of bound property ids + */ + public Collection getBoundPropertyIds() { + return Collections.unmodifiableCollection(propertyIdToField.keySet()); + } + + /** + * Returns a collection of all property ids that exist in the item set using + * {@link #setItemDataSource(Item)} but have not been bound to fields. + *

+ * Will always return an empty collection before an item has been set using + * {@link #setItemDataSource(Item)}. + *

+ *

+ * No guarantee is given for the order of the property ids + *

+ * + * @return A collection of property ids that have not been bound to fields + */ + public Collection getUnboundPropertyIds() { + if (getItemDataSource() == null) { + return new ArrayList(); + } + List unboundPropertyIds = new ArrayList(); + unboundPropertyIds.addAll(getItemDataSource().getItemPropertyIds()); + unboundPropertyIds.removeAll(propertyIdToField.keySet()); + return unboundPropertyIds; + } + + /** + * Commits all changes done to the bound fields. + *

+ * Calls all {@link CommitHandler}s before and after committing the field + * changes to the item data source. The whole commit is aborted and state is + * restored to what it was before commit was called if any + * {@link CommitHandler} throws a CommitException or there is a problem + * committing the fields + * + * @throws CommitException + * If the commit was aborted + */ + public void commit() throws CommitException { + if (!isBuffered()) { + // Not using buffered mode, nothing to do + return; + } + + startTransactions(); + + try { + firePreCommitEvent(); + + Map, InvalidValueException> invalidValueExceptions = commitFields(); + + if (invalidValueExceptions.isEmpty()) { + firePostCommitEvent(); + commitTransactions(); + } else { + throw new FieldGroupInvalidValueException( + invalidValueExceptions); + } + } catch (Exception e) { + rollbackTransactions(); + throw new CommitException("Commit failed", this, e); + } + + } + + /** + * Tries to commit all bound fields one by one and gathers any validation + * exceptions in a map, which is returned to the caller + * + * @return a propertyId to validation exception map which is empty if all + * commits succeeded + */ + private Map, InvalidValueException> commitFields() { + Map, InvalidValueException> invalidValueExceptions = new HashMap, InvalidValueException>(); + + for (LegacyField f : fieldToPropertyId.keySet()) { + try { + f.commit(); + } catch (InvalidValueException e) { + invalidValueExceptions.put(f, e); + } + } + + return invalidValueExceptions; + } + + /** + * Exception which wraps InvalidValueExceptions from all invalid fields in a + * FieldGroup + * + * @since 7.4 + */ + public static class FieldGroupInvalidValueException + extends InvalidValueException { + private Map, InvalidValueException> invalidValueExceptions; + + /** + * Constructs a new exception with the specified validation exceptions. + * + * @param invalidValueExceptions + * a property id to exception map + */ + public FieldGroupInvalidValueException( + Map, InvalidValueException> invalidValueExceptions) { + super(null, invalidValueExceptions.values().toArray( + new InvalidValueException[invalidValueExceptions.size()])); + this.invalidValueExceptions = invalidValueExceptions; + } + + /** + * Returns a map containing fields which failed validation and the + * exceptions the corresponding validators threw. + * + * @return a map with all the invalid value exceptions + */ + public Map, InvalidValueException> getInvalidFields() { + return invalidValueExceptions; + } + } + + private void startTransactions() throws CommitException { + for (LegacyField f : fieldToPropertyId.keySet()) { + Property.Transactional property = (Property.Transactional) f + .getPropertyDataSource(); + if (property == null) { + throw new CommitException( + "Property \"" + fieldToPropertyId.get(f) + + "\" not bound to datasource."); + } + property.startTransaction(); + } + } + + private void commitTransactions() { + for (LegacyField f : fieldToPropertyId.keySet()) { + ((Property.Transactional) f.getPropertyDataSource()).commit(); + } + } + + private void rollbackTransactions() { + for (LegacyField f : fieldToPropertyId.keySet()) { + try { + ((Property.Transactional) f.getPropertyDataSource()) + .rollback(); + } catch (Exception rollbackException) { + // FIXME: What to do ? + } + } + } + + /** + * Sends a preCommit event to all registered commit handlers + * + * @throws CommitException + * If the commit should be aborted + */ + private void firePreCommitEvent() throws CommitException { + CommitHandler[] handlers = commitHandlers + .toArray(new CommitHandler[commitHandlers.size()]); + + for (CommitHandler handler : handlers) { + handler.preCommit(new CommitEvent(this)); + } + } + + /** + * Sends a postCommit event to all registered commit handlers + * + * @throws CommitException + * If the commit should be aborted + */ + private void firePostCommitEvent() throws CommitException { + CommitHandler[] handlers = commitHandlers + .toArray(new CommitHandler[commitHandlers.size()]); + + for (CommitHandler handler : handlers) { + handler.postCommit(new CommitEvent(this)); + } + } + + /** + * Discards all changes done to the bound fields. + *

+ * Only has effect if buffered mode is used. + * + */ + public void discard() { + for (LegacyField f : fieldToPropertyId.keySet()) { + try { + f.discard(); + } catch (Exception e) { + // TODO: handle exception + // What can we do if discard fails other than try to discard all + // other fields? + } + } + } + + /** + * Returns the field that is bound to the given property id + * + * @param propertyId + * The property id to use to lookup the field + * @return The field that is bound to the property id or null if no field is + * bound to that property id + */ + public LegacyField getField(Object propertyId) { + return propertyIdToField.get(propertyId); + } + + /** + * Returns the property id that is bound to the given field + * + * @param field + * The field to use to lookup the property id + * @return The property id that is bound to the field or null if the field + * is not bound to any property id by this FieldBinder + */ + public Object getPropertyId(LegacyField field) { + return fieldToPropertyId.get(field); + } + + /** + * Adds a commit handler. + *

+ * The commit handler is called before the field values are committed to the + * item ( {@link CommitHandler#preCommit(CommitEvent)}) and after the item + * has been updated ({@link CommitHandler#postCommit(CommitEvent)}). If a + * {@link CommitHandler} throws a CommitException the whole commit is + * aborted and the fields retain their old values. + * + * @param commitHandler + * The commit handler to add + */ + public void addCommitHandler(CommitHandler commitHandler) { + commitHandlers.add(commitHandler); + } + + /** + * Removes the given commit handler. + * + * @see #addCommitHandler(CommitHandler) + * + * @param commitHandler + * The commit handler to remove + */ + public void removeCommitHandler(CommitHandler commitHandler) { + commitHandlers.remove(commitHandler); + } + + /** + * Returns a list of all commit handlers for this {@link FieldGroup}. + *

+ * Use {@link #addCommitHandler(CommitHandler)} and + * {@link #removeCommitHandler(CommitHandler)} to register or unregister a + * commit handler. + * + * @return A collection of commit handlers + */ + protected Collection getCommitHandlers() { + return Collections.unmodifiableCollection(commitHandlers); + } + + /** + * CommitHandlers are used by {@link FieldGroup#commit()} as part of the + * commit transactions. CommitHandlers can perform custom operations as part + * of the commit and cause the commit to be aborted by throwing a + * {@link CommitException}. + */ + public interface CommitHandler extends Serializable { + /** + * Called before changes are committed to the field and the item is + * updated. + *

+ * Throw a {@link CommitException} to abort the commit. + * + * @param commitEvent + * An event containing information regarding the commit + * @throws CommitException + * if the commit should be aborted + */ + public void preCommit(CommitEvent commitEvent) throws CommitException; + + /** + * Called after changes are committed to the fields and the item is + * updated. + *

+ * Throw a {@link CommitException} to abort the commit. + * + * @param commitEvent + * An event containing information regarding the commit + * @throws CommitException + * if the commit should be aborted + */ + public void postCommit(CommitEvent commitEvent) throws CommitException; + } + + /** + * FIXME javadoc + * + */ + public static class CommitEvent implements Serializable { + private FieldGroup fieldBinder; + + private CommitEvent(FieldGroup fieldBinder) { + this.fieldBinder = fieldBinder; + } + + /** + * Returns the field binder that this commit relates to + * + * @return The FieldBinder that is being committed. + */ + public FieldGroup getFieldBinder() { + return fieldBinder; + } + + } + + /** + * Checks the validity of the bound fields. + *

+ * Call the {@link LegacyField#validate()} for the fields to get the + * individual error messages. + * + * @return true if all bound fields are valid, false otherwise. + */ + public boolean isValid() { + try { + for (LegacyField field : getFields()) { + field.validate(); + } + return true; + } catch (InvalidValueException e) { + return false; + } + } + + /** + * Checks if any bound field has been modified. + * + * @return true if at least one field has been modified, false otherwise + */ + public boolean isModified() { + for (LegacyField field : getFields()) { + if (field.isModified()) { + return true; + } + } + return false; + } + + /** + * Gets the field factory for the {@link FieldGroup}. The field factory is + * only used when {@link FieldGroup} creates a new field. + * + * @return The field factory in use + * + */ + public FieldGroupFieldFactory getFieldFactory() { + return fieldFactory; + } + + /** + * Sets the field factory for the {@link FieldGroup}. The field factory is + * only used when {@link FieldGroup} creates a new field. + * + * @param fieldFactory + * The field factory to use + */ + public void setFieldFactory(FieldGroupFieldFactory fieldFactory) { + this.fieldFactory = fieldFactory; + } + + /** + * Binds member fields found in the given object. + *

+ * This method processes all (Java) member fields whose type extends + * {@link LegacyField} and that can be mapped to a property id. Property id + * mapping is done based on the field name or on a @{@link PropertyId} + * annotation on the field. All non-null fields for which a property id can + * be determined are bound to the property id. + *

+ *

+ * For example: + * + *

+     * public class MyForm extends VerticalLayout {
+     * private TextField firstName = new TextField("First name");
+     * @PropertyId("last")
+     * private TextField lastName = new TextField("Last name");
+     * private TextField age = new TextField("Age"); ... }
+     *
+     * MyForm myForm = new MyForm();
+     * ...
+     * fieldGroup.bindMemberFields(myForm);
+     * 
+ * + *

+ * This binds the firstName TextField to a "firstName" property in the item, + * lastName TextField to a "last" property and the age TextField to a "age" + * property. + * + * @param objectWithMemberFields + * The object that contains (Java) member fields to bind + * @throws BindException + * If there is a problem binding a field + */ + public void bindMemberFields(Object objectWithMemberFields) + throws BindException { + buildAndBindMemberFields(objectWithMemberFields, false); + } + + /** + * Binds member fields found in the given object and builds member fields + * that have not been initialized. + *

+ * This method processes all (Java) member fields whose type extends + * {@link LegacyField} and that can be mapped to a property id. Property ids + * are searched in the following order: @{@link PropertyId} annotations, + * exact field name matches and the case-insensitive matching that ignores + * underscores. Fields that are not initialized (null) are built using the + * field factory. All non-null fields for which a property id can be + * determined are bound to the property id. + *

+ *

+ * For example: + * + *

+     * public class MyForm extends VerticalLayout {
+     * private TextField firstName = new TextField("First name");
+     * @PropertyId("last")
+     * private TextField lastName = new TextField("Last name");
+     * private TextField age;
+     *
+     * MyForm myForm = new MyForm();
+     * ...
+     * fieldGroup.buildAndBindMemberFields(myForm);
+     * 
+ * + *

+ *

+ * This binds the firstName TextField to a "firstName" property in the item, + * lastName TextField to a "last" property and builds an age TextField using + * the field factory and then binds it to the "age" property. + *

+ * + * @param objectWithMemberFields + * The object that contains (Java) member fields to build and + * bind + * @throws BindException + * If there is a problem binding or building a field + */ + public void buildAndBindMemberFields(Object objectWithMemberFields) + throws BindException { + buildAndBindMemberFields(objectWithMemberFields, true); + } + + /** + * Binds member fields found in the given object and optionally builds + * member fields that have not been initialized. + *

+ * This method processes all (Java) member fields whose type extends + * {@link LegacyField} and that can be mapped to a property id. Property ids + * are searched in the following order: @{@link PropertyId} annotations, + * exact field name matches and the case-insensitive matching that ignores + * underscores. Fields that are not initialized (null) are built using the + * field factory is buildFields is true. All non-null fields for which a + * property id can be determined are bound to the property id. + *

+ * + * @param objectWithMemberFields + * The object that contains (Java) member fields to build and + * bind + * @throws BindException + * If there is a problem binding or building a field + */ + protected void buildAndBindMemberFields(Object objectWithMemberFields, + boolean buildFields) throws BindException { + Class objectClass = objectWithMemberFields.getClass(); + + for (java.lang.reflect.Field memberField : getFieldsInDeclareOrder( + objectClass)) { + + if (!LegacyField.class.isAssignableFrom(memberField.getType())) { + // Process next field + continue; + } + + PropertyId propertyIdAnnotation = memberField + .getAnnotation(PropertyId.class); + + Class fieldType = (Class) memberField + .getType(); + + Object propertyId = null; + if (propertyIdAnnotation != null) { + // @PropertyId(propertyId) always overrides property id + propertyId = propertyIdAnnotation.value(); + } else { + try { + propertyId = findPropertyId(memberField); + } catch (SearchException e) { + // Property id was not found, skip this field + continue; + } + if (propertyId == null) { + // Property id was not found, skip this field + continue; + } + } + + // Ensure that the property id exists + Class propertyType; + + try { + propertyType = getPropertyType(propertyId); + } catch (BindException e) { + // Property id was not found, skip this field + continue; + } + + LegacyField field; + try { + // Get the field from the object + field = (LegacyField) ReflectTools.getJavaFieldValue( + objectWithMemberFields, memberField, LegacyField.class); + } catch (Exception e) { + // If we cannot determine the value, just skip the field and try + // the next one + continue; + } + + if (field == null && buildFields) { + Caption captionAnnotation = memberField + .getAnnotation(Caption.class); + String caption; + if (captionAnnotation != null) { + caption = captionAnnotation.value(); + } else { + caption = DefaultFieldFactory + .createCaptionByPropertyId(propertyId); + } + + // Create the component (LegacyField) + field = build(caption, propertyType, fieldType); + + // Store it in the field + try { + ReflectTools.setJavaFieldValue(objectWithMemberFields, + memberField, field); + } catch (IllegalArgumentException e) { + throw new BindException("Could not assign value to field '" + + memberField.getName() + "'", e); + } catch (IllegalAccessException e) { + throw new BindException("Could not assign value to field '" + + memberField.getName() + "'", e); + } catch (InvocationTargetException e) { + throw new BindException("Could not assign value to field '" + + memberField.getName() + "'", e); + } + } + + if (field != null) { + // Bind it to the property id + bind(field, propertyId); + } + } + } + + /** + * Searches for a property id from the current itemDataSource that matches + * the given memberField. + *

+ * If perfect match is not found, uses a case insensitive search that also + * ignores underscores. Returns null if no match is found. Throws a + * SearchException if no item data source has been set. + *

+ *

+ * The propertyId search logic used by + * {@link #buildAndBindMemberFields(Object, boolean) + * buildAndBindMemberFields} can easily be customized by overriding this + * method. No other changes are needed. + *

+ * + * @param memberField + * The field an object id is searched for + * @return + */ + protected Object findPropertyId(java.lang.reflect.Field memberField) { + String fieldName = memberField.getName(); + if (getItemDataSource() == null) { + throw new SearchException("Property id type for field '" + fieldName + + "' could not be determined. No item data source has been set."); + } + Item dataSource = getItemDataSource(); + if (dataSource.getItemProperty(fieldName) != null) { + return fieldName; + } else { + String minifiedFieldName = minifyFieldName(fieldName); + for (Object itemPropertyId : dataSource.getItemPropertyIds()) { + if (itemPropertyId instanceof String) { + String itemPropertyName = (String) itemPropertyId; + if (minifiedFieldName + .equals(minifyFieldName(itemPropertyName))) { + return itemPropertyName; + } + } + } + } + return null; + } + + protected static String minifyFieldName(String fieldName) { + return fieldName.toLowerCase().replace("_", ""); + } + + /** + * Exception thrown by a FieldGroup when the commit operation fails. + * + * Provides information about validation errors through + * {@link #getInvalidFields()} if the cause of the failure is that all bound + * fields did not pass validation + * + */ + public static class CommitException extends Exception { + + private FieldGroup fieldGroup; + + public CommitException() { + super(); + } + + public CommitException(String message, FieldGroup fieldGroup, + Throwable cause) { + super(message, cause); + this.fieldGroup = fieldGroup; + } + + public CommitException(String message, Throwable cause) { + super(message, cause); + } + + public CommitException(String message) { + super(message); + } + + public CommitException(Throwable cause) { + super(cause); + } + + /** + * Returns a map containing the fields which failed validation and the + * exceptions the corresponding validators threw. + * + * @since 7.4 + * @return a map with all the invalid value exceptions. Can be empty but + * not null + */ + public Map, InvalidValueException> getInvalidFields() { + if (getCause() instanceof FieldGroupInvalidValueException) { + return ((FieldGroupInvalidValueException) getCause()) + .getInvalidFields(); + } + return new HashMap, InvalidValueException>(); + } + + /** + * Returns the field group where the exception occurred + * + * @since 7.4 + * @return the field group + */ + public FieldGroup getFieldGroup() { + return fieldGroup; + } + + } + + public static class BindException extends RuntimeException { + + public BindException(String message) { + super(message); + } + + public BindException(String message, Throwable t) { + super(message, t); + } + + } + + public static class SearchException extends RuntimeException { + + public SearchException(String message) { + super(message); + } + + public SearchException(String message, Throwable t) { + super(message, t); + } + + } + + /** + * Builds a field and binds it to the given property id using the field + * binder. + * + * @param propertyId + * The property id to bind to. Must be present in the field + * finder. + * @throws BindException + * If there is a problem while building or binding + * @return The created and bound field + */ + public LegacyField buildAndBind(Object propertyId) throws BindException { + String caption = DefaultFieldFactory + .createCaptionByPropertyId(propertyId); + return buildAndBind(caption, propertyId); + } + + /** + * Builds a field using the given caption and binds it to the given property + * id using the field binder. + * + * @param caption + * The caption for the field + * @param propertyId + * The property id to bind to. Must be present in the field + * finder. + * @throws BindException + * If there is a problem while building or binding + * @return The created and bound field. Can be any type of + * {@link LegacyField}. + */ + public LegacyField buildAndBind(String caption, Object propertyId) + throws BindException { + return buildAndBind(caption, propertyId, LegacyField.class); + } + + /** + * Builds a field using the given caption and binds it to the given property + * id using the field binder. Ensures the new field is of the given type. + * + * @param caption + * The caption for the field + * @param propertyId + * The property id to bind to. Must be present in the field + * finder. + * @throws BindException + * If the field could not be created + * @return The created and bound field. Can be any type of + * {@link LegacyField}. + */ + + public T buildAndBind(String caption, + Object propertyId, Class fieldType) throws BindException { + Class type = getPropertyType(propertyId); + + T field = build(caption, type, fieldType); + bind(field, propertyId); + + return field; + } + + /** + * Creates a field based on the given data type. + *

+ * The data type is the type that we want to edit using the field. The field + * type is the type of field we want to create, can be {@link LegacyField} + * if any LegacyField is good. + *

+ * + * @param caption + * The caption for the new field + * @param dataType + * The data model type that we want to edit using the field + * @param fieldType + * The type of field that we want to create + * @return A LegacyField capable of editing the given type + * @throws BindException + * If the field could not be created + */ + protected T build(String caption, Class dataType, + Class fieldType) throws BindException { + T field = getFieldFactory().createField(dataType, fieldType); + if (field == null) { + throw new BindException( + "Unable to build a field of type " + fieldType.getName() + + " for editing " + dataType.getName()); + } + + field.setCaption(caption); + return field; + } + + /** + * Returns an array containing LegacyField objects reflecting all the fields + * of the class or interface represented by this Class object. The elements + * in the array returned are sorted in declare order from sub class to super + * class. + * + * @param searchClass + * @return + */ + protected static List getFieldsInDeclareOrder( + Class searchClass) { + ArrayList memberFieldInOrder = new ArrayList(); + + while (searchClass != null) { + for (java.lang.reflect.Field memberField : searchClass + .getDeclaredFields()) { + memberFieldInOrder.add(memberField); + } + searchClass = searchClass.getSuperclass(); + } + return memberFieldInOrder; + } + + /** + * Clears the value of all fields. + * + * @since 7.4 + */ + public void clear() { + for (LegacyField f : getFields()) { + if (f instanceof LegacyAbstractField) { + ((LegacyAbstractField) f).clear(); + } + } + + } + +} \ No newline at end of file diff --git a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java new file mode 100644 index 0000000000..a80d51c6df --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java @@ -0,0 +1,43 @@ +/* + * 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.data.fieldgroup; + +import java.io.Serializable; + +import com.vaadin.v7.ui.LegacyField; + +/** + * Factory interface for creating new LegacyField-instances based on the data + * type that should be edited. + * + * @author Vaadin Ltd. + * @since 7.0 + */ +public interface FieldGroupFieldFactory extends Serializable { + /** + * Creates a field based on the data type that we want to edit + * + * @param dataType + * The type that we want to edit using the field + * @param fieldType + * The type of field we want to create. If set to + * {@link LegacyField} then any type of field is accepted + * @return A field that can be assigned to the given fieldType and that is + * capable of editing the given type of data + */ + T createField(Class dataType, + Class fieldType); +} diff --git a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java new file mode 100644 index 0000000000..a2a8aa27af --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java @@ -0,0 +1,60 @@ +/* + * 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.data.fieldgroup; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines the custom property name to be bound to a {@link LegacyField} using + * {@link FieldGroup} or {@link BeanFieldGroup}. + *

+ * The automatic data binding in FieldGroup and BeanFieldGroup relies on a + * naming convention by default: properties of an item are bound to similarly + * named field components in given a editor object. If you want to map a + * property with a different name (ID) to a + * {@link com.vaadin.client.ui.LegacyField}, you can use this annotation for the + * member fields, with the name (ID) of the desired property as the parameter. + *

+ * In following usage example, the text field would be bound to property "foo" + * in the Entity class. + *

+ *    class Editor extends FormLayout {
+        @PropertyId("foo")
+        TextField myField = new TextField();
+    }
+
+    class Entity {
+        String foo;
+    }
+
+    {
+        Editor e = new Editor();
+        BeanFieldGroup.bindFieldsUnbuffered(new Entity(), e);
+    }
+   
+ * + * + * @since 7.0 + * @author Vaadin Ltd + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface PropertyId { + String value(); +} diff --git a/compatibility-server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java b/compatibility-server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java new file mode 100644 index 0000000000..f7459a7dd3 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java @@ -0,0 +1,58 @@ +/* + * 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.server.communication.data; + +import java.io.Serializable; + +import com.vaadin.data.Item; +import com.vaadin.ui.LegacyGrid.AbstractGridExtension; + +import elemental.json.JsonObject; + +/** + * Interface for {@link AbstractGridExtension}s that allows adding data to row + * objects being sent to client by the {@link RpcDataProviderExtension}. + *

+ * This class also provides a way to remove any unneeded data once the data + * object is no longer used on the client-side. + * + * @since 7.6 + * @author Vaadin Ltd + */ +public interface DataGenerator extends Serializable { + + /** + * Adds data to row object for given item and item id being sent to client. + * + * @param itemId + * item id of item + * @param item + * item being sent to client + * @param rowData + * row object being sent to client + */ + public void generateData(Object itemId, Item item, JsonObject rowData); + + /** + * Informs the DataGenerator that an item id has been dropped and is no + * longer needed. This method should clean up any unneeded stored data + * related to the item. + * + * @param itemId + * removed item id + */ + public void destroyData(Object itemId); +} diff --git a/compatibility-server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java b/compatibility-server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java new file mode 100644 index 0000000000..341c3f1a45 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java @@ -0,0 +1,632 @@ +/* + * 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.server.communication.data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Container.Indexed.ItemAddEvent; +import com.vaadin.data.Container.Indexed.ItemRemoveEvent; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Container.ItemSetChangeNotifier; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.Property.ValueChangeNotifier; +import com.vaadin.server.AbstractExtension; +import com.vaadin.server.ClientConnector; +import com.vaadin.server.KeyMapper; +import com.vaadin.shared.data.DataProviderRpc; +import com.vaadin.shared.data.DataRequestRpc; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.Range; +import com.vaadin.ui.LegacyGrid; +import com.vaadin.ui.LegacyGrid.Column; + +import elemental.json.Json; +import elemental.json.JsonArray; +import elemental.json.JsonObject; + +/** + * Provides Vaadin server-side container data source to a + * {@link com.vaadin.client.ui.grid.GridConnector}. This is currently + * implemented as an Extension hardcoded to support a specific connector type. + * This will be changed once framework support for something more flexible has + * been implemented. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class RpcDataProviderExtension extends AbstractExtension { + + /** + * Class for keeping track of current items and ValueChangeListeners. + * + * @since 7.6 + */ + private class ActiveItemHandler implements Serializable, DataGenerator { + + private final Map activeItemMap = new HashMap(); + private final KeyMapper keyMapper = new KeyMapper(); + private final Set droppedItems = new HashSet(); + + /** + * Registers ValueChangeListeners for given item ids. + *

+ * Note: This method will clean up any unneeded listeners and key + * mappings + * + * @param itemIds + * collection of new active item ids + */ + public void addActiveItems(Collection itemIds) { + for (Object itemId : itemIds) { + if (!activeItemMap.containsKey(itemId)) { + activeItemMap.put(itemId, new GridValueChangeListener( + itemId, container.getItem(itemId))); + } + } + + // Remove still active rows that were "dropped" + droppedItems.removeAll(itemIds); + internalDropItems(droppedItems); + droppedItems.clear(); + } + + /** + * Marks given item id as dropped. Dropped items are cleared when adding + * new active items. + * + * @param itemId + * dropped item id + */ + public void dropActiveItem(Object itemId) { + if (activeItemMap.containsKey(itemId)) { + droppedItems.add(itemId); + } + } + + /** + * Gets a collection copy of currently active item ids. + * + * @return collection of item ids + */ + public Collection getActiveItemIds() { + return new HashSet(activeItemMap.keySet()); + } + + /** + * Gets a collection copy of currently active ValueChangeListeners. + * + * @return collection of value change listeners + */ + public Collection getValueChangeListeners() { + return new HashSet(activeItemMap.values()); + } + + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + rowData.put(GridState.JSONKEY_ROWKEY, keyMapper.key(itemId)); + } + + @Override + public void destroyData(Object itemId) { + keyMapper.remove(itemId); + removeListener(itemId); + } + + private void removeListener(Object itemId) { + GridValueChangeListener removed = activeItemMap.remove(itemId); + + if (removed != null) { + removed.removeListener(); + } + } + } + + /** + * A class to listen to changes in property values in the Container added + * with {@link LegacyGrid#setContainerDatasource(Container.Indexed)}, and notifies + * the data source to update the client-side representation of the modified + * item. + *

+ * One instance of this class can (and should) be reused for all the + * properties in an item, since this class will inform that the entire row + * needs to be re-evaluated (in contrast to a property-based change + * management) + *

+ * Since there's no Container-wide possibility to listen to any kind of + * value changes, an instance of this class needs to be attached to each and + * every Item's Property in the container. + * + * @see LegacyGrid#addValueChangeListener(Container, Object, Object) + * @see LegacyGrid#valueChangeListeners + */ + private class GridValueChangeListener implements ValueChangeListener { + private final Object itemId; + private final Item item; + + public GridValueChangeListener(Object itemId, Item item) { + /* + * Using an assert instead of an exception throw, just to optimize + * prematurely + */ + assert itemId != null : "null itemId not accepted"; + this.itemId = itemId; + this.item = item; + + internalAddColumns(getGrid().getColumns()); + } + + @Override + public void valueChange(ValueChangeEvent event) { + updateRowData(itemId); + } + + public void removeListener() { + removeColumns(getGrid().getColumns()); + } + + public void addColumns(Collection addedColumns) { + internalAddColumns(addedColumns); + updateRowData(itemId); + } + + private void internalAddColumns(Collection addedColumns) { + for (final Column column : addedColumns) { + final Property property = item + .getItemProperty(column.getPropertyId()); + if (property instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) property) + .addValueChangeListener(this); + } + } + } + + public void removeColumns(Collection removedColumns) { + for (final Column column : removedColumns) { + final Property property = item + .getItemProperty(column.getPropertyId()); + if (property instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) property) + .removeValueChangeListener(this); + } + } + } + } + + private final Indexed container; + + private DataProviderRpc rpc; + + private final ItemSetChangeListener itemListener = new ItemSetChangeListener() { + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + + if (event instanceof ItemAddEvent) { + ItemAddEvent addEvent = (ItemAddEvent) event; + int firstIndex = addEvent.getFirstIndex(); + int count = addEvent.getAddedItemsCount(); + insertRowData(firstIndex, count); + } + + else if (event instanceof ItemRemoveEvent) { + ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; + int firstIndex = removeEvent.getFirstIndex(); + int count = removeEvent.getRemovedItemsCount(); + removeRowData(firstIndex, count); + } + + else { + // Remove obsolete value change listeners. + Set keySet = new HashSet( + activeItemHandler.activeItemMap.keySet()); + for (Object itemId : keySet) { + activeItemHandler.removeListener(itemId); + } + + /* Mark as dirty to push changes in beforeClientResponse */ + bareItemSetTriggeredSizeChange = true; + markAsDirty(); + } + } + }; + + /** RpcDataProvider should send the current cache again. */ + private boolean refreshCache = false; + + /** Set of updated item ids */ + private transient Set updatedItemIds; + + /** + * Queued RPC calls for adding and removing rows. Queue will be handled in + * {@link beforeClientResponse} + */ + private transient List rowChanges; + + /** Size possibly changed with a bare ItemSetChangeEvent */ + private boolean bareItemSetTriggeredSizeChange = false; + + private final Set dataGenerators = new LinkedHashSet(); + + private final ActiveItemHandler activeItemHandler = new ActiveItemHandler(); + + /** + * Creates a new data provider using the given container. + * + * @param container + * the container to make available + */ + public RpcDataProviderExtension(Indexed container) { + this.container = container; + rpc = getRpcProxy(DataProviderRpc.class); + + registerRpc(new DataRequestRpc() { + @Override + public void requestRows(int firstRow, int numberOfRows, + int firstCachedRowIndex, int cacheSize) { + pushRowData(firstRow, numberOfRows, firstCachedRowIndex, + cacheSize); + } + + @Override + public void dropRows(JsonArray rowKeys) { + for (int i = 0; i < rowKeys.length(); ++i) { + activeItemHandler.dropActiveItem( + getKeyMapper().get(rowKeys.getString(i))); + } + } + }); + + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container) + .addItemSetChangeListener(itemListener); + } + + addDataGenerator(activeItemHandler); + } + + /** + * {@inheritDoc} + *

+ * RpcDataProviderExtension makes all actual RPC calls from this function + * based on changes in the container. + */ + @Override + public void beforeClientResponse(boolean initial) { + if (initial || bareItemSetTriggeredSizeChange) { + /* + * Push initial set of rows, assuming Grid will initially be + * rendered scrolled to the top and with a decent amount of rows + * visible. If this guess is right, initial data can be shown + * without a round-trip and if it's wrong, the data will simply be + * discarded. + */ + int size = container.size(); + rpc.resetDataAndSize(size); + + int numberOfRows = Math.min(40, size); + pushRowData(0, numberOfRows, 0, 0); + } else { + // Only do row changes if not initial response. + if (rowChanges != null) { + for (Runnable r : rowChanges) { + r.run(); + } + } + + // Send current rows again if needed. + if (refreshCache) { + for (Object itemId : activeItemHandler.getActiveItemIds()) { + updateRowData(itemId); + } + } + } + + internalUpdateRows(updatedItemIds); + + // Clear all changes. + if (rowChanges != null) { + rowChanges.clear(); + } + if (updatedItemIds != null) { + updatedItemIds.clear(); + } + refreshCache = false; + bareItemSetTriggeredSizeChange = false; + + super.beforeClientResponse(initial); + } + + private void pushRowData(int firstRowToPush, int numberOfRows, + int firstCachedRowIndex, int cacheSize) { + Range newRange = Range.withLength(firstRowToPush, numberOfRows); + Range cached = Range.withLength(firstCachedRowIndex, cacheSize); + Range fullRange = newRange; + if (!cached.isEmpty()) { + fullRange = newRange.combineWith(cached); + } + + List itemIds = container.getItemIds(fullRange.getStart(), + fullRange.length()); + + JsonArray rows = Json.createArray(); + + // Offset the index to match the wanted range. + int diff = 0; + if (!cached.isEmpty() && newRange.getStart() > cached.getStart()) { + diff = cached.length(); + } + + for (int i = 0; i < newRange.length() + && i + diff < itemIds.size(); ++i) { + Object itemId = itemIds.get(i + diff); + + Item item = container.getItem(itemId); + + rows.set(i, getRowData(getGrid().getColumns(), itemId, item)); + } + rpc.setRowData(firstRowToPush, rows); + + activeItemHandler.addActiveItems(itemIds); + } + + private JsonObject getRowData(Collection columns, Object itemId, + Item item) { + + final JsonObject rowObject = Json.createObject(); + for (DataGenerator dg : dataGenerators) { + dg.generateData(itemId, item, rowObject); + } + + return rowObject; + } + + /** + * Makes the data source available to the given {@link LegacyGrid} component. + * + * @param component + * the remote data grid component to extend + * @param columnKeys + * the key mapper for columns + */ + public void extend(LegacyGrid component) { + super.extend(component); + } + + /** + * Adds a {@link DataGenerator} for this {@code RpcDataProviderExtension}. + * DataGenerators are called when sending row data to client. If given + * DataGenerator is already added, this method does nothing. + * + * @since 7.6 + * @param generator + * generator to add + */ + public void addDataGenerator(DataGenerator generator) { + dataGenerators.add(generator); + } + + /** + * Removes a {@link DataGenerator} from this + * {@code RpcDataProviderExtension}. If given DataGenerator is not added to + * this data provider, this method does nothing. + * + * @since 7.6 + * @param generator + * generator to remove + */ + public void removeDataGenerator(DataGenerator generator) { + dataGenerators.remove(generator); + } + + /** + * Informs the client side that new rows have been inserted into the data + * source. + * + * @param index + * the index at which new rows have been inserted + * @param count + * the number of rows inserted at index + */ + private void insertRowData(final int index, final int count) { + if (rowChanges == null) { + rowChanges = new ArrayList(); + } + + if (rowChanges.isEmpty()) { + markAsDirty(); + } + + /* + * Since all changes should be processed in a consistent order, we don't + * send the RPC call immediately. beforeClientResponse will decide + * whether to send these or not. Valid situation to not send these is + * initial response or bare ItemSetChange event. + */ + rowChanges.add(new Runnable() { + @Override + public void run() { + rpc.insertRowData(index, count); + } + }); + } + + /** + * Informs the client side that rows have been removed from the data source. + * + * @param index + * the index of the first row removed + * @param count + * the number of rows removed + * @param firstItemId + * the item id of the first removed item + */ + private void removeRowData(final int index, final int count) { + if (rowChanges == null) { + rowChanges = new ArrayList(); + } + + if (rowChanges.isEmpty()) { + markAsDirty(); + } + + /* See comment in insertRowData */ + rowChanges.add(new Runnable() { + @Override + public void run() { + rpc.removeRowData(index, count); + } + }); + } + + /** + * Informs the client side that data of a row has been modified in the data + * source. + * + * @param itemId + * the item Id the row that was updated + */ + public void updateRowData(Object itemId) { + if (updatedItemIds == null) { + updatedItemIds = new LinkedHashSet(); + } + + if (updatedItemIds.isEmpty()) { + // At least one new item will be updated. Mark as dirty to actually + // update before response to client. + markAsDirty(); + } + + updatedItemIds.add(itemId); + } + + private void internalUpdateRows(Set itemIds) { + if (itemIds == null || itemIds.isEmpty()) { + return; + } + + Collection activeItemIds = activeItemHandler.getActiveItemIds(); + List columns = getGrid().getColumns(); + JsonArray rowData = Json.createArray(); + int i = 0; + for (Object itemId : itemIds) { + if (activeItemIds.contains(itemId)) { + Item item = container.getItem(itemId); + if (item != null) { + JsonObject row = getRowData(columns, itemId, item); + rowData.set(i++, row); + } + } + } + rpc.updateRowData(rowData); + } + + /** + * Pushes a new version of all the rows in the active cache range. + */ + public void refreshCache() { + if (!refreshCache) { + refreshCache = true; + markAsDirty(); + } + } + + @Override + public void setParent(ClientConnector parent) { + if (parent == null) { + // We're being detached, release various listeners + internalDropItems(activeItemHandler.getActiveItemIds()); + + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container) + .removeItemSetChangeListener(itemListener); + } + + } else if (!(parent instanceof LegacyGrid)) { + throw new IllegalStateException( + "Grid is the only accepted parent type"); + } + super.setParent(parent); + } + + /** + * Informs all DataGenerators than an item id has been dropped. + * + * @param droppedItemIds + * collection of dropped item ids + */ + private void internalDropItems(Collection droppedItemIds) { + for (Object itemId : droppedItemIds) { + for (DataGenerator generator : dataGenerators) { + generator.destroyData(itemId); + } + } + } + + /** + * Informs this data provider that given columns have been removed from + * grid. + * + * @param removedColumns + * a list of removed columns + */ + public void columnsRemoved(List removedColumns) { + for (GridValueChangeListener l : activeItemHandler + .getValueChangeListeners()) { + l.removeColumns(removedColumns); + } + + // No need to resend unchanged data. Client will remember the old + // columns until next set of rows is sent. + } + + /** + * Informs this data provider that given columns have been added to grid. + * + * @param addedColumns + * a list of added columns + */ + public void columnsAdded(List addedColumns) { + for (GridValueChangeListener l : activeItemHandler + .getValueChangeListeners()) { + l.addColumns(addedColumns); + } + + // Resend all rows to contain new data. + refreshCache(); + } + + public KeyMapper getKeyMapper() { + return activeItemHandler.keyMapper; + } + + protected LegacyGrid getGrid() { + return (LegacyGrid) getParent(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java b/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java new file mode 100644 index 0000000000..9a98593b83 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java @@ -0,0 +1,7352 @@ +/* + * 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.ui; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Container.ItemSetChangeNotifier; +import com.vaadin.data.Container.PropertySetChangeEvent; +import com.vaadin.data.Container.PropertySetChangeListener; +import com.vaadin.data.Container.PropertySetChangeNotifier; +import com.vaadin.data.Container.Sortable; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.fieldgroup.FieldGroupFieldFactory; +import com.vaadin.data.sort.Sort; +import com.vaadin.data.sort.SortOrder; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.event.ContextClickEvent; +import com.vaadin.event.ItemClickEvent; +import com.vaadin.event.ItemClickEvent.ItemClickListener; +import com.vaadin.event.ItemClickEvent.ItemClickNotifier; +import com.vaadin.event.SelectionEvent; +import com.vaadin.event.SelectionEvent.SelectionListener; +import com.vaadin.event.SelectionEvent.SelectionNotifier; +import com.vaadin.event.SortEvent; +import com.vaadin.event.SortEvent.SortListener; +import com.vaadin.event.SortEvent.SortNotifier; +import com.vaadin.server.AbstractClientConnector; +import com.vaadin.server.AbstractExtension; +import com.vaadin.server.EncodeResult; +import com.vaadin.server.ErrorMessage; +import com.vaadin.server.Extension; +import com.vaadin.server.JsonCodec; +import com.vaadin.server.KeyMapper; +import com.vaadin.server.VaadinSession; +import com.vaadin.server.communication.data.DataGenerator; +import com.vaadin.server.communication.data.RpcDataProviderExtension; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.grid.EditorClientRpc; +import com.vaadin.shared.ui.grid.EditorServerRpc; +import com.vaadin.shared.ui.grid.GridClientRpc; +import com.vaadin.shared.ui.grid.GridColumnState; +import com.vaadin.shared.ui.grid.GridConstants; +import com.vaadin.shared.ui.grid.GridConstants.Section; +import com.vaadin.shared.ui.grid.GridServerRpc; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.GridStaticCellType; +import com.vaadin.shared.ui.grid.GridStaticSectionState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; +import com.vaadin.shared.ui.grid.HeightMode; +import com.vaadin.shared.ui.grid.ScrollDestination; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; +import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; +import com.vaadin.shared.util.SharedUtil; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; +import com.vaadin.ui.declarative.DesignFormatter; +import com.vaadin.ui.renderers.HtmlRenderer; +import com.vaadin.ui.renderers.Renderer; +import com.vaadin.ui.renderers.TextRenderer; +import com.vaadin.util.ReflectTools; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.data.util.converter.LegacyConverter; +import com.vaadin.v7.data.util.converter.LegacyConverterUtil; +import com.vaadin.v7.ui.LegacyCheckBox; +import com.vaadin.v7.ui.LegacyField; + +import elemental.json.Json; +import elemental.json.JsonObject; +import elemental.json.JsonValue; + +/** + * A grid component for displaying tabular data. + *

+ * Grid is always bound to a {@link Container.Indexed}, but is not a + * {@code Container} of any kind in of itself. The contents of the given + * Container is displayed with the help of {@link Renderer Renderers}. + * + *

Headers and Footers

+ *

+ * + * + *

Converters and Renderers

+ *

+ * Each column has its own {@link Renderer} that displays data into something + * that can be displayed in the browser. That data is first converted with a + * {@link com.vaadin.v7.data.util.converter.LegacyConverter Converter} into + * something that the Renderer can process. This can also be an implicit step - + * if a column has a simple data type, like a String, no explicit assignment is + * needed. + *

+ * Usually a renderer takes some kind of object, and converts it into a + * HTML-formatted string. + *

+ *

+ * Grid grid = new Grid(myContainer);
+ * Column column = grid.getColumn(STRING_DATE_PROPERTY);
+ * column.setConverter(new StringToDateConverter());
+ * column.setRenderer(new MyColorfulDateRenderer());
+ * 
+ * + *

Lazy Loading

+ *

+ * The data is accessed as it is needed by Grid and not any sooner. In other + * words, if the given Container is huge, but only the first few rows are + * displayed to the user, only those (and a few more, for caching purposes) are + * accessed. + * + *

Selection Modes and Models

+ *

+ * Grid supports three selection {@link SelectionMode modes} (single, + * multi, none), and comes bundled with one {@link SelectionModel + * model} for each of the modes. The distinction between a selection mode + * and selection model is as follows: a mode essentially says whether + * you can have one, many or no rows selected. The model, however, has the + * behavioral details of each. A single selection model may require that the + * user deselects one row before selecting another one. A variant of a + * multiselect might have a configurable maximum of rows that may be selected. + * And so on. + *

+ *

+ * Grid grid = new Grid(myContainer);
+ *
+ * // uses the bundled SingleSelectionModel class
+ * grid.setSelectionMode(SelectionMode.SINGLE);
+ *
+ * // changes the behavior to a custom selection model
+ * grid.setSelectionModel(new MyTwoSelectionModel());
+ * 
+ * + * @since 7.4 + * @author Vaadin Ltd + */ +public class LegacyGrid extends AbstractFocusable implements SelectionNotifier, + SortNotifier, SelectiveRenderer, ItemClickNotifier { + + /** + * An event listener for column visibility change events in the Grid. + * + * @since 7.5.0 + */ + public interface ColumnVisibilityChangeListener extends Serializable { + /** + * Called when a column has become hidden or unhidden. + * + * @param event + */ + void columnVisibilityChanged(ColumnVisibilityChangeEvent event); + } + + /** + * An event that is fired when a column's visibility changes. + * + * @since 7.5.0 + */ + public static class ColumnVisibilityChangeEvent extends Component.Event { + + private final Column column; + private final boolean userOriginated; + private final boolean hidden; + + /** + * Constructor for a column visibility change event. + * + * @param source + * the grid from which this event originates + * @param column + * the column that changed its visibility + * @param hidden + * true if the column was hidden, + * false if it became visible + * @param isUserOriginated + * true iff the event was triggered by an UI + * interaction + */ + public ColumnVisibilityChangeEvent(LegacyGrid source, Column column, + boolean hidden, boolean isUserOriginated) { + super(source); + this.column = column; + this.hidden = hidden; + userOriginated = isUserOriginated; + } + + /** + * Gets the column that became hidden or visible. + * + * @return the column that became hidden or visible. + * @see Column#isHidden() + */ + public Column getColumn() { + return column; + } + + /** + * Was the column set hidden or visible. + * + * @return true if the column was hidden false + * if it was set visible + */ + public boolean isHidden() { + return hidden; + } + + /** + * Returns true if the column reorder was done by the user, + * false if not and it was triggered by server side code. + * + * @return true if event is a result of user interaction + */ + public boolean isUserOriginated() { + return userOriginated; + } + } + + /** + * A callback interface for generating details for a particular row in Grid. + * + * @since 7.5.0 + * @author Vaadin Ltd + * @see DetailsGenerator#NULL + */ + public interface DetailsGenerator extends Serializable { + + /** A details generator that provides no details */ + public DetailsGenerator NULL = new DetailsGenerator() { + @Override + public Component getDetails(RowReference rowReference) { + return null; + } + }; + + /** + * This method is called for whenever a details row needs to be shown on + * the client. Grid removes all of its references to details components + * when they are no longer displayed on the client-side and will + * re-request once needed again. + *

+ * Note: If a component gets generated, it may not be manually + * attached anywhere. The same details component can not be displayed + * for multiple different rows. + * + * @param rowReference + * the reference for the row for which to generate details + * @return the details for the given row, or null to leave + * the details empty. + */ + Component getDetails(RowReference rowReference); + } + + /** + * A class that manages details components by calling + * {@link DetailsGenerator} as needed. Details components are attached by + * this class when the {@link RpcDataProviderExtension} is sending data to + * the client. Details components are detached and forgotten when client + * informs that it has dropped the corresponding item. + * + * @since 7.6.1 + */ + public final static class DetailComponentManager + extends AbstractGridExtension implements DataGenerator { + + /** + * The user-defined details generator. + * + * @see #setDetailsGenerator(DetailsGenerator) + */ + private DetailsGenerator detailsGenerator; + + /** + * This map represents all details that are currently visible on the + * client. Details components get destroyed once they scroll out of + * view. + */ + private final Map itemIdToDetailsComponent = new HashMap<>(); + + /** + * Set of item ids that got null from DetailsGenerator when + * {@link DetailsGenerator#getDetails(RowReference)} was called. + */ + private final Set emptyDetails = new HashSet<>(); + + /** + * Set of item IDs for all open details rows. Contains even the ones + * that are not currently visible on the client. + */ + private final Set openDetails = new HashSet<>(); + + public DetailComponentManager(LegacyGrid grid) { + this(grid, DetailsGenerator.NULL); + } + + public DetailComponentManager(LegacyGrid grid, + DetailsGenerator detailsGenerator) { + super(grid); + setDetailsGenerator(detailsGenerator); + } + + /** + * Creates a details component with the help of the user-defined + * {@link DetailsGenerator}. + *

+ * This method attaches created components to the parent {@link LegacyGrid}. + * + * @param itemId + * the item id for which to create the details component. + * @throws IllegalStateException + * if the current details generator provides a component + * that was manually attached. + */ + private void createDetails(Object itemId) throws IllegalStateException { + assert itemId != null : "itemId was null"; + + if (itemIdToDetailsComponent.containsKey(itemId) + || emptyDetails.contains(itemId)) { + // Don't overwrite existing components + return; + } + + RowReference rowReference = new RowReference(getParentGrid()); + rowReference.set(itemId); + + DetailsGenerator detailsGenerator = getParentGrid() + .getDetailsGenerator(); + Component details = detailsGenerator.getDetails(rowReference); + if (details != null) { + if (details.getParent() != null) { + String name = detailsGenerator.getClass().getName(); + throw new IllegalStateException( + name + " generated a details component that already " + + "was attached. (itemId: " + itemId + + ", component: " + details + ")"); + } + + itemIdToDetailsComponent.put(itemId, details); + + addComponentToGrid(details); + + assert !emptyDetails.contains(itemId) : "Bookeeping thinks " + + "itemId is empty even though we just created a " + + "component for it (" + itemId + ")"; + } else { + emptyDetails.add(itemId); + } + + } + + /** + * Destroys a details component correctly. + *

+ * This method will detach the component from parent {@link LegacyGrid}. + * + * @param itemId + * the item id for which to destroy the details component + */ + private void destroyDetails(Object itemId) { + emptyDetails.remove(itemId); + + Component removedComponent = itemIdToDetailsComponent + .remove(itemId); + if (removedComponent == null) { + return; + } + + removeComponentFromGrid(removedComponent); + } + + /** + * Recreates all visible details components. + */ + public void refreshDetails() { + Set visibleItemIds = new HashSet<>( + itemIdToDetailsComponent.keySet()); + for (Object itemId : visibleItemIds) { + destroyDetails(itemId); + createDetails(itemId); + refreshRow(itemId); + } + } + + /** + * Sets details visiblity status of given item id. + * + * @param itemId + * item id to set + * @param visible + * true if visible; false if not + */ + public void setDetailsVisible(Object itemId, boolean visible) { + if ((visible && openDetails.contains(itemId)) + || (!visible && !openDetails.contains(itemId))) { + return; + } + + if (visible) { + openDetails.add(itemId); + refreshRow(itemId); + } else { + openDetails.remove(itemId); + destroyDetails(itemId); + refreshRow(itemId); + } + } + + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + // DetailComponentManager should not send anything if details + // generator is the default null version. + if (openDetails.contains(itemId) + && !detailsGenerator.equals(DetailsGenerator.NULL)) { + // Double check to be sure details component exists. + createDetails(itemId); + + Component detailsComponent = itemIdToDetailsComponent + .get(itemId); + rowData.put(GridState.JSONKEY_DETAILS_VISIBLE, + (detailsComponent != null + ? detailsComponent.getConnectorId() : "")); + } + } + + @Override + public void destroyData(Object itemId) { + if (openDetails.contains(itemId)) { + destroyDetails(itemId); + } + } + + /** + * Sets a new details generator for row details. + *

+ * The currently opened row details will be re-rendered. + * + * @param detailsGenerator + * the details generator to set + * @throws IllegalArgumentException + * if detailsGenerator is null; + */ + public void setDetailsGenerator(DetailsGenerator detailsGenerator) + throws IllegalArgumentException { + if (detailsGenerator == null) { + throw new IllegalArgumentException( + "Details generator may not be null"); + } else if (detailsGenerator == this.detailsGenerator) { + return; + } + + this.detailsGenerator = detailsGenerator; + + refreshDetails(); + } + + /** + * Gets the current details generator for row details. + * + * @return the detailsGenerator the current details generator + */ + public DetailsGenerator getDetailsGenerator() { + return detailsGenerator; + } + + /** + * Checks whether details are visible for the given item. + * + * @param itemId + * the id of the item for which to check details visibility + * @return true iff the details are visible + */ + public boolean isDetailsVisible(Object itemId) { + return openDetails.contains(itemId); + } + } + + /** + * Custom field group that allows finding property types before an item has + * been bound. + */ + private final class CustomFieldGroup extends FieldGroup { + + public CustomFieldGroup() { + setFieldFactory(EditorFieldFactory.get()); + } + + @Override + protected Class getPropertyType(Object propertyId) + throws BindException { + if (getItemDataSource() == null) { + return datasource.getType(propertyId); + } else { + return super.getPropertyType(propertyId); + } + } + + @Override + protected T build(String caption, + Class dataType, Class fieldType) throws BindException { + T field = super.build(caption, dataType, fieldType); + if (field instanceof LegacyCheckBox) { + field.setCaption(null); + } + return field; + } + } + + /** + * Field factory used by default in the editor. + * + * Aims to fields of suitable type and with suitable size for use in the + * editor row. + */ + public static class EditorFieldFactory + extends DefaultFieldGroupFieldFactory { + private static final EditorFieldFactory INSTANCE = new EditorFieldFactory(); + + protected EditorFieldFactory() { + } + + /** + * Returns the singleton instance + * + * @return the singleton instance + */ + public static EditorFieldFactory get() { + return INSTANCE; + } + + @Override + public T createField(Class type, + Class fieldType) { + T f = super.createField(type, fieldType); + if (f != null) { + f.setWidth("100%"); + } + return f; + } + + @Override + protected AbstractSelect createCompatibleSelect( + Class fieldType) { + if (anySelect(fieldType)) { + return super.createCompatibleSelect(ComboBox.class); + } + return super.createCompatibleSelect(fieldType); + } + + @Override + protected void populateWithEnumData(AbstractSelect select, + Class enumClass) { + // Use enums directly and the EnumToStringConverter to be consistent + // with what is shown in the Grid + @SuppressWarnings("unchecked") + EnumSet enumSet = EnumSet.allOf(enumClass); + for (Object r : enumSet) { + select.addItem(r); + } + } + } + + /** + * Error handler for the editor + */ + public interface EditorErrorHandler extends Serializable { + + /** + * Called when an exception occurs while the editor row is being saved + * + * @param event + * An event providing more information about the error + */ + void commitError(CommitErrorEvent event); + } + + /** + * ContextClickEvent for the Grid Component. + * + * @since 7.6 + */ + public static class GridContextClickEvent extends ContextClickEvent { + + private final Object itemId; + private final int rowIndex; + private final Object propertyId; + private final Section section; + + public GridContextClickEvent(LegacyGrid source, + MouseEventDetails mouseEventDetails, Section section, + int rowIndex, Object itemId, Object propertyId) { + super(source, mouseEventDetails); + this.itemId = itemId; + this.propertyId = propertyId; + this.section = section; + this.rowIndex = rowIndex; + } + + /** + * Returns the item id of context clicked row. + * + * @return item id of clicked row; null if header or footer + */ + public Object getItemId() { + return itemId; + } + + /** + * Returns property id of clicked column. + * + * @return property id + */ + public Object getPropertyId() { + return propertyId; + } + + /** + * Return the clicked section of Grid. + * + * @return section of grid + */ + public Section getSection() { + return section; + } + + /** + * Returns the clicked row index relative to Grid section. In the body + * of the Grid the index is the item index in the Container. Header and + * Footer rows for index can be fetched with + * {@link LegacyGrid#getHeaderRow(int)} and {@link LegacyGrid#getFooterRow(int)}. + * + * @return row index in section + */ + public int getRowIndex() { + return rowIndex; + } + + @Override + public LegacyGrid getComponent() { + return (LegacyGrid) super.getComponent(); + } + } + + /** + * An event which is fired when saving the editor fails + */ + public static class CommitErrorEvent extends Component.Event { + + private CommitException cause; + + private Set errorColumns = new HashSet<>(); + + private String userErrorMessage; + + public CommitErrorEvent(LegacyGrid grid, CommitException cause) { + super(grid); + this.cause = cause; + userErrorMessage = cause.getLocalizedMessage(); + } + + /** + * Retrieves the cause of the failure + * + * @return the cause of the failure + */ + public CommitException getCause() { + return cause; + } + + @Override + public LegacyGrid getComponent() { + return (LegacyGrid) super.getComponent(); + } + + /** + * Checks if validation exceptions caused this error + * + * @return true if the problem was caused by a validation error + */ + public boolean isValidationFailure() { + return cause.getCause() instanceof InvalidValueException; + } + + /** + * Marks that an error indicator should be shown for the editor of a + * column. + * + * @param column + * the column to show an error for + */ + public void addErrorColumn(Column column) { + errorColumns.add(column); + } + + /** + * Gets all the columns that have been marked as erroneous. + * + * @return an umodifiable collection of erroneous columns + */ + public Collection getErrorColumns() { + return Collections.unmodifiableCollection(errorColumns); + } + + /** + * Gets the error message to show to the user. + * + * @return error message to show + */ + public String getUserErrorMessage() { + return userErrorMessage; + } + + /** + * Sets the error message to show to the user. + * + * @param userErrorMessage + * the user error message to set + */ + public void setUserErrorMessage(String userErrorMessage) { + this.userErrorMessage = userErrorMessage; + } + + } + + /** + * An event listener for column reorder events in the Grid. + * + * @since 7.5.0 + */ + public interface ColumnReorderListener extends Serializable { + + /** + * Called when the columns of the grid have been reordered. + * + * @param event + * An event providing more information + */ + void columnReorder(ColumnReorderEvent event); + } + + /** + * An event that is fired when the columns are reordered. + * + * @since 7.5.0 + */ + public static class ColumnReorderEvent extends Component.Event { + + private final boolean userOriginated; + + /** + * + * @param source + * the grid where the event originated from + * @param userOriginated + * true if event is a result of user + * interaction, false if from API call + */ + public ColumnReorderEvent(LegacyGrid source, boolean userOriginated) { + super(source); + this.userOriginated = userOriginated; + } + + /** + * Returns true if the column reorder was done by the user, + * false if not and it was triggered by server side code. + * + * @return true if event is a result of user interaction + */ + public boolean isUserOriginated() { + return userOriginated; + } + + } + + /** + * An event listener for column resize events in the Grid. + * + * @since 7.6 + */ + public interface ColumnResizeListener extends Serializable { + + /** + * Called when the columns of the grid have been resized. + * + * @param event + * An event providing more information + */ + void columnResize(ColumnResizeEvent event); + } + + /** + * An event that is fired when a column is resized, either programmatically + * or by the user. + * + * @since 7.6 + */ + public static class ColumnResizeEvent extends Component.Event { + + private final Column column; + private final boolean userOriginated; + + /** + * + * @param source + * the grid where the event originated from + * @param userOriginated + * true if event is a result of user + * interaction, false if from API call + */ + public ColumnResizeEvent(LegacyGrid source, Column column, + boolean userOriginated) { + super(source); + this.column = column; + this.userOriginated = userOriginated; + } + + /** + * Returns the column that was resized. + * + * @return the resized column. + */ + public Column getColumn() { + return column; + } + + /** + * Returns true if the column resize was done by the user, + * false if not and it was triggered by server side code. + * + * @return true if event is a result of user interaction + */ + public boolean isUserOriginated() { + return userOriginated; + } + + } + + /** + * Interface for an editor event listener + */ + public interface EditorListener extends Serializable { + + public static final Method EDITOR_OPEN_METHOD = ReflectTools.findMethod( + EditorListener.class, "editorOpened", EditorOpenEvent.class); + public static final Method EDITOR_MOVE_METHOD = ReflectTools.findMethod( + EditorListener.class, "editorMoved", EditorMoveEvent.class); + public static final Method EDITOR_CLOSE_METHOD = ReflectTools + .findMethod(EditorListener.class, "editorClosed", + EditorCloseEvent.class); + + /** + * Called when an editor is opened + * + * @param e + * an editor open event object + */ + public void editorOpened(EditorOpenEvent e); + + /** + * Called when an editor is reopened without closing it first + * + * @param e + * an editor move event object + */ + public void editorMoved(EditorMoveEvent e); + + /** + * Called when an editor is closed + * + * @param e + * an editor close event object + */ + public void editorClosed(EditorCloseEvent e); + + } + + /** + * Base class for editor related events + */ + public static abstract class EditorEvent extends Component.Event { + + private Object itemID; + + protected EditorEvent(LegacyGrid source, Object itemID) { + super(source); + this.itemID = itemID; + } + + /** + * Get the item (row) for which this editor was opened + */ + public Object getItem() { + return itemID; + } + + } + + /** + * This event gets fired when an editor is opened + */ + public static class EditorOpenEvent extends EditorEvent { + + public EditorOpenEvent(LegacyGrid source, Object itemID) { + super(source, itemID); + } + } + + /** + * This event gets fired when an editor is opened while another row is being + * edited (i.e. editor focus moves elsewhere) + */ + public static class EditorMoveEvent extends EditorEvent { + + public EditorMoveEvent(LegacyGrid source, Object itemID) { + super(source, itemID); + } + } + + /** + * This event gets fired when an editor is dismissed or closed by other + * means. + */ + public static class EditorCloseEvent extends EditorEvent { + + public EditorCloseEvent(LegacyGrid source, Object itemID) { + super(source, itemID); + } + } + + /** + * Default error handler for the editor + * + */ + public class DefaultEditorErrorHandler implements EditorErrorHandler { + + @Override + public void commitError(CommitErrorEvent event) { + Map, InvalidValueException> invalidFields = event + .getCause().getInvalidFields(); + + if (!invalidFields.isEmpty()) { + Object firstErrorPropertyId = null; + LegacyField firstErrorField = null; + + FieldGroup fieldGroup = event.getCause().getFieldGroup(); + for (Column column : getColumns()) { + Object propertyId = column.getPropertyId(); + LegacyField field = fieldGroup.getField(propertyId); + if (invalidFields.keySet().contains(field)) { + event.addErrorColumn(column); + + if (firstErrorPropertyId == null) { + firstErrorPropertyId = propertyId; + firstErrorField = field; + } + } + } + + /* + * Validation error, show first failure as + * ": " + */ + String caption = getColumn(firstErrorPropertyId) + .getHeaderCaption(); + String message = invalidFields.get(firstErrorField) + .getLocalizedMessage(); + + event.setUserErrorMessage(caption + ": " + message); + } else { + com.vaadin.server.ErrorEvent.findErrorHandler(LegacyGrid.this).error( + new ConnectorErrorEvent(LegacyGrid.this, event.getCause())); + } + } + + private Object getFirstPropertyId(FieldGroup fieldGroup, + Set> keySet) { + for (Column c : getColumns()) { + Object propertyId = c.getPropertyId(); + LegacyField f = fieldGroup.getField(propertyId); + if (keySet.contains(f)) { + return propertyId; + } + } + return null; + } + } + + /** + * Selection modes representing built-in {@link SelectionModel + * SelectionModels} that come bundled with {@link LegacyGrid}. + *

+ * Passing one of these enums into + * {@link LegacyGrid#setSelectionMode(SelectionMode)} is equivalent to calling + * {@link LegacyGrid#setSelectionModel(SelectionModel)} with one of the built-in + * implementations of {@link SelectionModel}. + * + * @see LegacyGrid#setSelectionMode(SelectionMode) + * @see LegacyGrid#setSelectionModel(SelectionModel) + */ + public enum SelectionMode { + /** A SelectionMode that maps to {@link SingleSelectionModel} */ + SINGLE { + @Override + protected SelectionModel createModel() { + return new SingleSelectionModel(); + } + + }, + + /** A SelectionMode that maps to {@link MultiSelectionModel} */ + MULTI { + @Override + protected SelectionModel createModel() { + return new MultiSelectionModel(); + } + }, + + /** A SelectionMode that maps to {@link NoSelectionModel} */ + NONE { + @Override + protected SelectionModel createModel() { + return new NoSelectionModel(); + } + }; + + protected abstract SelectionModel createModel(); + } + + /** + * The server-side interface that controls Grid's selection state. + * SelectionModel should extend {@link AbstractGridExtension}. + */ + public interface SelectionModel extends Serializable, Extension { + /** + * Checks whether an item is selected or not. + * + * @param itemId + * the item id to check for + * @return true iff the item is selected + */ + boolean isSelected(Object itemId); + + /** + * Returns a collection of all the currently selected itemIds. + * + * @return a collection of all the currently selected itemIds + */ + Collection getSelectedRows(); + + /** + * Injects the current {@link LegacyGrid} instance into the SelectionModel. + * This method should usually call the extend method of + * {@link AbstractExtension}. + *

+ * Note: This method should not be called manually. + * + * @param grid + * the Grid in which the SelectionModel currently is, or + * null when a selection model is being detached + * from a Grid. + */ + void setGrid(LegacyGrid grid); + + /** + * Resets the SelectiomModel to an initial state. + *

+ * Most often this means that the selection state is cleared, but + * implementations are free to interpret the "initial state" as they + * wish. Some, for example, may want to keep the first selected item as + * selected. + */ + void reset(); + + /** + * A SelectionModel that supports multiple selections to be made. + *

+ * This interface has a contract of having the same behavior, no matter + * how the selection model is interacted with. In other words, if + * something is forbidden to do in e.g. the user interface, it must also + * be forbidden to do in the server-side and client-side APIs. + */ + public interface Multi extends SelectionModel { + + /** + * Marks items as selected. + *

+ * This method does not clear any previous selection state, only + * adds to it. + * + * @param itemIds + * the itemId(s) to mark as selected + * @return true if the selection state changed. + * false if all the given itemIds already were + * selected + * @throws IllegalArgumentException + * if the itemIds varargs array is + * null or given itemIds don't exist in the + * container of Grid + * @see #deselect(Object...) + */ + boolean select(Object... itemIds) throws IllegalArgumentException; + + /** + * Marks items as selected. + *

+ * This method does not clear any previous selection state, only + * adds to it. + * + * @param itemIds + * the itemIds to mark as selected + * @return true if the selection state changed. + * false if all the given itemIds already were + * selected + * @throws IllegalArgumentException + * if itemIds is null or given + * itemIds don't exist in the container of Grid + * @see #deselect(Collection) + */ + boolean select(Collection itemIds) + throws IllegalArgumentException; + + /** + * Marks items as deselected. + * + * @param itemIds + * the itemId(s) to remove from being selected + * @return true if the selection state changed. + * false if none the given itemIds were + * selected previously + * @throws IllegalArgumentException + * if the itemIds varargs array is + * null + * @see #select(Object...) + */ + boolean deselect(Object... itemIds) throws IllegalArgumentException; + + /** + * Marks items as deselected. + * + * @param itemIds + * the itemId(s) to remove from being selected + * @return true if the selection state changed. + * false if none the given itemIds were + * selected previously + * @throws IllegalArgumentException + * if itemIds is null + * @see #select(Collection) + */ + boolean deselect(Collection itemIds) + throws IllegalArgumentException; + + /** + * Marks all the items in the current Container as selected + * + * @return true iff some items were previously not + * selected + * @see #deselectAll() + */ + boolean selectAll(); + + /** + * Marks all the items in the current Container as deselected + * + * @return true iff some items were previously selected + * @see #selectAll() + */ + boolean deselectAll(); + + /** + * Marks items as selected while deselecting all items not in the + * given Collection. + * + * @param itemIds + * the itemIds to mark as selected + * @return true if the selection state changed. + * false if all the given itemIds already were + * selected + * @throws IllegalArgumentException + * if itemIds is null or given + * itemIds don't exist in the container of Grid + */ + boolean setSelected(Collection itemIds) + throws IllegalArgumentException; + + /** + * Marks items as selected while deselecting all items not in the + * varargs array. + * + * @param itemIds + * the itemIds to mark as selected + * @return true if the selection state changed. + * false if all the given itemIds already were + * selected + * @throws IllegalArgumentException + * if the itemIds varargs array is + * null or given itemIds don't exist in the + * container of Grid + */ + boolean setSelected(Object... itemIds) + throws IllegalArgumentException; + } + + /** + * A SelectionModel that supports for only single rows to be selected at + * a time. + *

+ * This interface has a contract of having the same behavior, no matter + * how the selection model is interacted with. In other words, if + * something is forbidden to do in e.g. the user interface, it must also + * be forbidden to do in the server-side and client-side APIs. + */ + public interface Single extends SelectionModel { + + /** + * Marks an item as selected. + * + * @param itemId + * the itemId to mark as selected; null for + * deselect + * @return true if the selection state changed. + * false if the itemId already was selected + * @throws IllegalStateException + * if the selection was illegal. One such reason might + * be that the given id was null, indicating a deselect, + * but implementation doesn't allow deselecting. + * re-selecting something + * @throws IllegalArgumentException + * if given itemId does not exist in the container of + * Grid + */ + boolean select(Object itemId) + throws IllegalStateException, IllegalArgumentException; + + /** + * Gets the item id of the currently selected item. + * + * @return the item id of the currently selected item, or + * null if nothing is selected + */ + Object getSelectedRow(); + + /** + * Sets whether it's allowed to deselect the selected row through + * the UI. Deselection is allowed by default. + * + * @param deselectAllowed + * true if the selected row can be + * deselected without selecting another row instead; + * otherwise false. + */ + public void setDeselectAllowed(boolean deselectAllowed); + + /** + * Sets whether it's allowed to deselect the selected row through + * the UI. + * + * @return true if deselection is allowed; otherwise + * false + */ + public boolean isDeselectAllowed(); + } + + /** + * A SelectionModel that does not allow for rows to be selected. + *

+ * This interface has a contract of having the same behavior, no matter + * how the selection model is interacted with. In other words, if the + * developer is unable to select something programmatically, it is not + * allowed for the end-user to select anything, either. + */ + public interface None extends SelectionModel { + + /** + * {@inheritDoc} + * + * @return always false. + */ + @Override + public boolean isSelected(Object itemId); + + /** + * {@inheritDoc} + * + * @return always an empty collection. + */ + @Override + public Collection getSelectedRows(); + } + } + + /** + * A base class for SelectionModels that contains some of the logic that is + * reusable. + */ + public static abstract class AbstractSelectionModel extends + AbstractGridExtension implements SelectionModel, DataGenerator { + protected final LinkedHashSet selection = new LinkedHashSet<>(); + + @Override + public boolean isSelected(final Object itemId) { + return selection.contains(itemId); + } + + @Override + public Collection getSelectedRows() { + return new ArrayList<>(selection); + } + + @Override + public void setGrid(final LegacyGrid grid) { + if (grid != null) { + extend(grid); + } + } + + /** + * Sanity check for existence of item id. + * + * @param itemId + * item id to be selected / deselected + * + * @throws IllegalArgumentException + * if item Id doesn't exist in the container of Grid + */ + protected void checkItemIdExists(Object itemId) + throws IllegalArgumentException { + if (!getParentGrid().getContainerDataSource().containsId(itemId)) { + throw new IllegalArgumentException("Given item id (" + itemId + + ") does not exist in the container"); + } + } + + /** + * Sanity check for existence of item ids in given collection. + * + * @param itemIds + * item id collection to be selected / deselected + * + * @throws IllegalArgumentException + * if at least one item id doesn't exist in the container of + * Grid + */ + protected void checkItemIdsExist(Collection itemIds) + throws IllegalArgumentException { + for (Object itemId : itemIds) { + checkItemIdExists(itemId); + } + } + + /** + * Fires a {@link SelectionEvent} to all the {@link SelectionListener + * SelectionListeners} currently added to the Grid in which this + * SelectionModel is. + *

+ * Note that this is only a helper method, and routes the call all the + * way to Grid. A {@link SelectionModel} is not a + * {@link SelectionNotifier} + * + * @param oldSelection + * the complete {@link Collection} of the itemIds that were + * selected before this event happened + * @param newSelection + * the complete {@link Collection} of the itemIds that are + * selected after this event happened + */ + protected void fireSelectionEvent(final Collection oldSelection, + final Collection newSelection) { + getParentGrid().fireSelectionEvent(oldSelection, newSelection); + } + + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + if (isSelected(itemId)) { + rowData.put(GridState.JSONKEY_SELECTED, true); + } + } + + @Override + public void destroyData(Object itemId) { + // NO-OP + } + + @Override + protected Object getItemId(String rowKey) { + return rowKey != null ? super.getItemId(rowKey) : null; + } + } + + /** + * A default implementation of a {@link SelectionModel.Single} + */ + public static class SingleSelectionModel extends AbstractSelectionModel + implements SelectionModel.Single { + + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + registerRpc(new SingleSelectionModelServerRpc() { + + @Override + public void select(String rowKey) { + SingleSelectionModel.this.select(getItemId(rowKey), false); + } + }); + } + + @Override + public boolean select(final Object itemId) { + return select(itemId, true); + } + + protected boolean select(final Object itemId, boolean refresh) { + if (itemId == null) { + return deselect(getSelectedRow()); + } + + checkItemIdExists(itemId); + + final Object selectedRow = getSelectedRow(); + final boolean modified = selection.add(itemId); + if (modified) { + final Collection deselected; + if (selectedRow != null) { + deselectInternal(selectedRow, false, true); + deselected = Collections.singleton(selectedRow); + } else { + deselected = Collections.emptySet(); + } + + fireSelectionEvent(deselected, selection); + } + + if (refresh) { + refreshRow(itemId); + } + + return modified; + } + + private boolean deselect(final Object itemId) { + return deselectInternal(itemId, true, true); + } + + private boolean deselectInternal(final Object itemId, + boolean fireEventIfNeeded, boolean refresh) { + final boolean modified = selection.remove(itemId); + if (modified) { + if (refresh) { + refreshRow(itemId); + } + if (fireEventIfNeeded) { + fireSelectionEvent(Collections.singleton(itemId), + Collections.emptySet()); + } + } + return modified; + } + + @Override + public Object getSelectedRow() { + if (selection.isEmpty()) { + return null; + } else { + return selection.iterator().next(); + } + } + + /** + * Resets the selection state. + *

+ * If an item is selected, it will become deselected. + */ + @Override + public void reset() { + deselect(getSelectedRow()); + } + + @Override + public void setDeselectAllowed(boolean deselectAllowed) { + getState().deselectAllowed = deselectAllowed; + } + + @Override + public boolean isDeselectAllowed() { + return getState().deselectAllowed; + } + + @Override + protected SingleSelectionModelState getState() { + return (SingleSelectionModelState) super.getState(); + } + } + + /** + * A default implementation for a {@link SelectionModel.None} + */ + public static class NoSelectionModel extends AbstractSelectionModel + implements SelectionModel.None { + + @Override + public boolean isSelected(final Object itemId) { + return false; + } + + @Override + public Collection getSelectedRows() { + return Collections.emptyList(); + } + + /** + * Semantically resets the selection model. + *

+ * Effectively a no-op. + */ + @Override + public void reset() { + // NOOP + } + } + + /** + * A default implementation of a {@link SelectionModel.Multi} + */ + public static class MultiSelectionModel extends AbstractSelectionModel + implements SelectionModel.Multi { + + /** + * The default selection size limit. + * + * @see #setSelectionLimit(int) + */ + public static final int DEFAULT_MAX_SELECTIONS = 1000; + + private int selectionLimit = DEFAULT_MAX_SELECTIONS; + + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + registerRpc(new MultiSelectionModelServerRpc() { + + @Override + public void select(List rowKeys) { + List items = new ArrayList<>(); + for (String rowKey : rowKeys) { + items.add(getItemId(rowKey)); + } + MultiSelectionModel.this.select(items, false); + } + + @Override + public void deselect(List rowKeys) { + List items = new ArrayList<>(); + for (String rowKey : rowKeys) { + items.add(getItemId(rowKey)); + } + MultiSelectionModel.this.deselect(items, false); + } + + @Override + public void selectAll() { + MultiSelectionModel.this.selectAll(false); + } + + @Override + public void deselectAll() { + MultiSelectionModel.this.deselectAll(false); + } + }); + } + + @Override + public boolean select(final Object... itemIds) + throws IllegalArgumentException { + if (itemIds != null) { + // select will fire the event + return select(Arrays.asList(itemIds)); + } else { + throw new IllegalArgumentException( + "Vararg array of itemIds may not be null"); + } + } + + /** + * {@inheritDoc} + *

+ * All items might not be selected if the limit set using + * {@link #setSelectionLimit(int)} is exceeded. + */ + @Override + public boolean select(final Collection itemIds) + throws IllegalArgumentException { + return select(itemIds, true); + } + + protected boolean select(final Collection itemIds, boolean refresh) { + if (itemIds == null) { + throw new IllegalArgumentException("itemIds may not be null"); + } + + // Sanity check + checkItemIdsExist(itemIds); + + final boolean selectionWillChange = !selection.containsAll(itemIds) + && selection.size() < selectionLimit; + if (selectionWillChange) { + final HashSet oldSelection = new HashSet<>(selection); + if (selection.size() + itemIds.size() >= selectionLimit) { + // Add one at a time if there's a risk of overflow + Iterator iterator = itemIds.iterator(); + while (iterator.hasNext() + && selection.size() < selectionLimit) { + selection.add(iterator.next()); + } + } else { + selection.addAll(itemIds); + } + fireSelectionEvent(oldSelection, selection); + } + + updateAllSelectedState(); + + if (refresh) { + for (Object itemId : itemIds) { + refreshRow(itemId); + } + } + + return selectionWillChange; + } + + /** + * Sets the maximum number of rows that can be selected at once. This is + * a mechanism to prevent exhausting server memory in situations where + * users select lots of rows. If the limit is reached, newly selected + * rows will not become recorded. + *

+ * Old selections are not discarded if the current number of selected + * row exceeds the new limit. + *

+ * The default limit is {@value #DEFAULT_MAX_SELECTIONS} rows. + * + * @param selectionLimit + * the non-negative selection limit to set + * @throws IllegalArgumentException + * if the limit is negative + */ + public void setSelectionLimit(int selectionLimit) { + if (selectionLimit < 0) { + throw new IllegalArgumentException( + "The selection limit must be non-negative"); + } + this.selectionLimit = selectionLimit; + } + + /** + * Gets the selection limit. + * + * @see #setSelectionLimit(int) + * + * @return the selection limit + */ + public int getSelectionLimit() { + return selectionLimit; + } + + @Override + public boolean deselect(final Object... itemIds) + throws IllegalArgumentException { + if (itemIds != null) { + // deselect will fire the event + return deselect(Arrays.asList(itemIds)); + } else { + throw new IllegalArgumentException( + "Vararg array of itemIds may not be null"); + } + } + + @Override + public boolean deselect(final Collection itemIds) + throws IllegalArgumentException { + return deselect(itemIds, true); + } + + protected boolean deselect(final Collection itemIds, + boolean refresh) { + if (itemIds == null) { + throw new IllegalArgumentException("itemIds may not be null"); + } + + final boolean hasCommonElements = !Collections.disjoint(itemIds, + selection); + if (hasCommonElements) { + final HashSet oldSelection = new HashSet<>(selection); + selection.removeAll(itemIds); + fireSelectionEvent(oldSelection, selection); + } + + updateAllSelectedState(); + + if (refresh) { + for (Object itemId : itemIds) { + refreshRow(itemId); + } + } + + return hasCommonElements; + } + + @Override + public boolean selectAll() { + return selectAll(true); + } + + protected boolean selectAll(boolean refresh) { + // select will fire the event + final Indexed container = getParentGrid().getContainerDataSource(); + if (container != null) { + return select(container.getItemIds(), refresh); + } else if (selection.isEmpty()) { + return false; + } else { + /* + * this should never happen (no container but has a selection), + * but I guess the only theoretically correct course of + * action... + */ + return deselectAll(false); + } + } + + @Override + public boolean deselectAll() { + return deselectAll(true); + } + + protected boolean deselectAll(boolean refresh) { + // deselect will fire the event + return deselect(getSelectedRows(), refresh); + } + + /** + * {@inheritDoc} + *

+ * The returned Collection is in order of selection + * – the item that was first selected will be first in the + * collection, and so on. Should an item have been selected twice + * without being deselected in between, it will have remained in its + * original position. + */ + @Override + public Collection getSelectedRows() { + // overridden only for JavaDoc + return super.getSelectedRows(); + } + + /** + * Resets the selection model. + *

+ * Equivalent to calling {@link #deselectAll()} + */ + @Override + public void reset() { + deselectAll(); + } + + @Override + public boolean setSelected(Collection itemIds) + throws IllegalArgumentException { + if (itemIds == null) { + throw new IllegalArgumentException("itemIds may not be null"); + } + + checkItemIdsExist(itemIds); + + boolean changed = false; + Set selectedRows = new HashSet<>(itemIds); + final Collection oldSelection = getSelectedRows(); + Set added = getDifference(selectedRows, selection); + if (!added.isEmpty()) { + changed = true; + selection.addAll(added); + for (Object id : added) { + refreshRow(id); + } + } + + Set removed = getDifference(selection, selectedRows); + if (!removed.isEmpty()) { + changed = true; + selection.removeAll(removed); + for (Object id : removed) { + refreshRow(id); + } + } + + if (changed) { + fireSelectionEvent(oldSelection, selection); + } + + updateAllSelectedState(); + + return changed; + } + + /** + * Compares two sets and returns a set containing all values that are + * present in the first, but not in the second. + * + * @param set1 + * first item set + * @param set2 + * second item set + * @return all values from set1 which are not present in set2 + */ + private static Set getDifference(Set set1, + Set set2) { + Set diff = new HashSet<>(set1); + diff.removeAll(set2); + return diff; + } + + @Override + public boolean setSelected(Object... itemIds) + throws IllegalArgumentException { + if (itemIds != null) { + return setSelected(Arrays.asList(itemIds)); + } else { + throw new IllegalArgumentException( + "Vararg array of itemIds may not be null"); + } + } + + private void updateAllSelectedState() { + int totalRowCount = getParentGrid().datasource.size(); + int rows = Math.min(totalRowCount, selectionLimit); + if (getState().allSelected != selection.size() >= rows) { + getState().allSelected = selection.size() >= rows; + } + } + + @Override + protected MultiSelectionModelState getState() { + return (MultiSelectionModelState) super.getState(); + } + } + + /** + * A data class which contains information which identifies a row in a + * {@link LegacyGrid}. + *

+ * Since this class follows the Flyweight-pattern any instance + * of this object is subject to change without the user knowing it and so + * should not be stored anywhere outside of the method providing these + * instances. + */ + public static class RowReference implements Serializable { + private final LegacyGrid grid; + + private Object itemId; + + /** + * Creates a new row reference for the given grid. + * + * @param grid + * the grid that the row belongs to + */ + public RowReference(LegacyGrid grid) { + this.grid = grid; + } + + /** + * Sets the identifying information for this row + * + * @param itemId + * the item id of the row + */ + public void set(Object itemId) { + this.itemId = itemId; + } + + /** + * Gets the grid that contains the referenced row. + * + * @return the grid that contains referenced row + */ + public LegacyGrid getGrid() { + return grid; + } + + /** + * Gets the item id of the row. + * + * @return the item id of the row + */ + public Object getItemId() { + return itemId; + } + + /** + * Gets the item for the row. + * + * @return the item for the row + */ + public Item getItem() { + return grid.getContainerDataSource().getItem(itemId); + } + } + + /** + * A data class which contains information which identifies a cell in a + * {@link LegacyGrid}. + *

+ * Since this class follows the Flyweight-pattern any instance + * of this object is subject to change without the user knowing it and so + * should not be stored anywhere outside of the method providing these + * instances. + */ + public static class CellReference implements Serializable { + private final RowReference rowReference; + + private Object propertyId; + + public CellReference(RowReference rowReference) { + this.rowReference = rowReference; + } + + /** + * Sets the identifying information for this cell + * + * @param propertyId + * the property id of the column + */ + public void set(Object propertyId) { + this.propertyId = propertyId; + } + + /** + * Gets the grid that contains the referenced cell. + * + * @return the grid that contains referenced cell + */ + public LegacyGrid getGrid() { + return rowReference.getGrid(); + } + + /** + * @return the property id of the column + */ + public Object getPropertyId() { + return propertyId; + } + + /** + * @return the property for the cell + */ + public Property getProperty() { + return getItem().getItemProperty(propertyId); + } + + /** + * Gets the item id of the row of the cell. + * + * @return the item id of the row + */ + public Object getItemId() { + return rowReference.getItemId(); + } + + /** + * Gets the item for the row of the cell. + * + * @return the item for the row + */ + public Item getItem() { + return rowReference.getItem(); + } + + /** + * Gets the value of the cell. + * + * @return the value of the cell + */ + public Object getValue() { + return getProperty().getValue(); + } + } + + /** + * A callback interface for generating custom style names for Grid rows. + * + * @see LegacyGrid#setRowStyleGenerator(RowStyleGenerator) + */ + public interface RowStyleGenerator extends Serializable { + + /** + * Called by Grid to generate a style name for a row. + * + * @param row + * the row to generate a style for + * @return the style name to add to this row, or {@code null} to not set + * any style + */ + public String getStyle(RowReference row); + } + + /** + * A callback interface for generating custom style names for Grid cells. + * + * @see LegacyGrid#setCellStyleGenerator(CellStyleGenerator) + */ + public interface CellStyleGenerator extends Serializable { + + /** + * Called by Grid to generate a style name for a column. + * + * @param cell + * the cell to generate a style for + * @return the style name to add to this cell, or {@code null} to not + * set any style + */ + public String getStyle(CellReference cell); + } + + /** + * A callback interface for generating optional descriptions (tooltips) for + * Grid rows. If a description is generated for a row, it is used for all + * the cells in the row for which a {@link CellDescriptionGenerator cell + * description} is not generated. + * + * @see LegacyGrid#setRowDescriptionGenerator + * + * @since 7.6 + */ + public interface RowDescriptionGenerator extends Serializable { + + /** + * Called by Grid to generate a description (tooltip) for a row. The + * description may contain HTML which is rendered directly; if this is + * not desired the returned string must be escaped by the implementing + * method. + * + * @param row + * the row to generate a description for + * @return the row description or {@code null} for no description + */ + public String getDescription(RowReference row); + } + + /** + * A callback interface for generating optional descriptions (tooltips) for + * Grid cells. If a cell has both a {@link RowDescriptionGenerator row + * description} and a cell description, the latter has precedence. + * + * @see LegacyGrid#setCellDescriptionGenerator(CellDescriptionGenerator) + * + * @since 7.6 + */ + public interface CellDescriptionGenerator extends Serializable { + + /** + * Called by Grid to generate a description (tooltip) for a cell. The + * description may contain HTML which is rendered directly; if this is + * not desired the returned string must be escaped by the implementing + * method. + * + * @param cell + * the cell to generate a description for + * @return the cell description or {@code null} for no description + */ + public String getDescription(CellReference cell); + } + + /** + * Class for generating all row and cell related data for the essential + * parts of Grid. + */ + private class RowDataGenerator implements DataGenerator { + + private void put(String key, String value, JsonObject object) { + if (value != null && !value.isEmpty()) { + object.put(key, value); + } + } + + @Override + public void generateData(Object itemId, Item item, JsonObject rowData) { + RowReference row = new RowReference(LegacyGrid.this); + row.set(itemId); + + if (rowStyleGenerator != null) { + String style = rowStyleGenerator.getStyle(row); + put(GridState.JSONKEY_ROWSTYLE, style, rowData); + } + + if (rowDescriptionGenerator != null) { + String description = rowDescriptionGenerator + .getDescription(row); + put(GridState.JSONKEY_ROWDESCRIPTION, description, rowData); + + } + + JsonObject cellStyles = Json.createObject(); + JsonObject cellData = Json.createObject(); + JsonObject cellDescriptions = Json.createObject(); + + CellReference cell = new CellReference(row); + + for (Column column : getColumns()) { + cell.set(column.getPropertyId()); + + writeData(cell, cellData); + writeStyles(cell, cellStyles); + writeDescriptions(cell, cellDescriptions); + } + + if (cellDescriptionGenerator != null + && cellDescriptions.keys().length > 0) { + rowData.put(GridState.JSONKEY_CELLDESCRIPTION, + cellDescriptions); + } + + if (cellStyleGenerator != null && cellStyles.keys().length > 0) { + rowData.put(GridState.JSONKEY_CELLSTYLES, cellStyles); + } + + rowData.put(GridState.JSONKEY_DATA, cellData); + } + + private void writeStyles(CellReference cell, JsonObject styles) { + if (cellStyleGenerator != null) { + String style = cellStyleGenerator.getStyle(cell); + put(columnKeys.key(cell.getPropertyId()), style, styles); + } + } + + private void writeDescriptions(CellReference cell, + JsonObject descriptions) { + if (cellDescriptionGenerator != null) { + String description = cellDescriptionGenerator + .getDescription(cell); + put(columnKeys.key(cell.getPropertyId()), description, + descriptions); + } + } + + private void writeData(CellReference cell, JsonObject data) { + Column column = getColumn(cell.getPropertyId()); + LegacyConverter converter = column.getConverter(); + Renderer renderer = column.getRenderer(); + + Item item = cell.getItem(); + Object modelValue = item.getItemProperty(cell.getPropertyId()) + .getValue(); + + data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer + .encodeValue(modelValue, renderer, converter, getLocale())); + } + + @Override + public void destroyData(Object itemId) { + // NO-OP + } + } + + /** + * Abstract base class for Grid header and footer sections. + * + * @since 7.6 + * @param + * the type of the rows in the section + */ + public abstract static class StaticSection> + implements Serializable { + + /** + * Abstract base class for Grid header and footer rows. + * + * @param + * the type of the cells in the row + */ + public abstract static class StaticRow + implements Serializable { + + private RowState rowState = new RowState(); + protected StaticSection section; + private Map cells = new LinkedHashMap<>(); + private Map, CELLTYPE> cellGroups = new HashMap<>(); + + protected StaticRow(StaticSection section) { + this.section = section; + } + + protected void addCell(Object propertyId) { + CELLTYPE cell = createCell(); + cell.setColumnId( + section.grid.getColumn(propertyId).getState().id); + cells.put(propertyId, cell); + rowState.cells.add(cell.getCellState()); + } + + protected void removeCell(Object propertyId) { + CELLTYPE cell = cells.remove(propertyId); + if (cell != null) { + Set cellGroupForCell = getCellGroupForCell(cell); + if (cellGroupForCell != null) { + removeCellFromGroup(cell, cellGroupForCell); + } + rowState.cells.remove(cell.getCellState()); + } + } + + private void removeCellFromGroup(CELLTYPE cell, + Set cellGroup) { + String columnId = cell.getColumnId(); + for (Set group : rowState.cellGroups.keySet()) { + if (group.contains(columnId)) { + if (group.size() > 2) { + // Update map key correctly + CELLTYPE mergedCell = cellGroups.remove(cellGroup); + cellGroup.remove(cell); + cellGroups.put(cellGroup, mergedCell); + + group.remove(columnId); + } else { + rowState.cellGroups.remove(group); + cellGroups.remove(cellGroup); + } + return; + } + } + } + + /** + * Creates and returns a new instance of the cell type. + * + * @return the created cell + */ + protected abstract CELLTYPE createCell(); + + protected RowState getRowState() { + return rowState; + } + + /** + * Returns the cell for the given property id on this row. If the + * column is merged returned cell is the cell for the whole group. + * + * @param propertyId + * the property id of the column + * @return the cell for the given property, merged cell for merged + * properties, null if not found + */ + public CELLTYPE getCell(Object propertyId) { + CELLTYPE cell = cells.get(propertyId); + Set cellGroup = getCellGroupForCell(cell); + if (cellGroup != null) { + cell = cellGroups.get(cellGroup); + } + return cell; + } + + /** + * Merges columns cells in a row + * + * @param propertyIds + * The property ids of columns to merge + * @return The remaining visible cell after the merge + */ + public CELLTYPE join(Object... propertyIds) { + assert propertyIds.length > 1 : "You need to merge at least 2 properties"; + + Set cells = new HashSet<>(); + for (int i = 0; i < propertyIds.length; ++i) { + cells.add(getCell(propertyIds[i])); + } + + return join(cells); + } + + /** + * Merges columns cells in a row + * + * @param cells + * The cells to merge. Must be from the same row. + * @return The remaining visible cell after the merge + */ + public CELLTYPE join(CELLTYPE... cells) { + assert cells.length > 1 : "You need to merge at least 2 cells"; + + return join(new HashSet<>(Arrays.asList(cells))); + } + + protected CELLTYPE join(Set cells) { + for (CELLTYPE cell : cells) { + if (getCellGroupForCell(cell) != null) { + throw new IllegalArgumentException( + "Cell already merged"); + } else if (!this.cells.containsValue(cell)) { + throw new IllegalArgumentException( + "Cell does not exist on this row"); + } + } + + // Create new cell data for the group + CELLTYPE newCell = createCell(); + + Set columnGroup = new HashSet<>(); + for (CELLTYPE cell : cells) { + columnGroup.add(cell.getColumnId()); + } + rowState.cellGroups.put(columnGroup, newCell.getCellState()); + cellGroups.put(cells, newCell); + return newCell; + } + + private Set getCellGroupForCell(CELLTYPE cell) { + for (Set group : cellGroups.keySet()) { + if (group.contains(cell)) { + return group; + } + } + return null; + } + + /** + * Returns the custom style name for this row. + * + * @return the style name or null if no style name has been set + */ + public String getStyleName() { + return getRowState().styleName; + } + + /** + * Sets a custom style name for this row. + * + * @param styleName + * the style name to set or null to not use any style + * name + */ + public void setStyleName(String styleName) { + getRowState().styleName = styleName; + } + + /** + * Writes the declarative design to the given table row element. + * + * @since 7.5.0 + * @param trElement + * Element to write design to + * @param designContext + * the design context + */ + protected void writeDesign(Element trElement, + DesignContext designContext) { + Set visited = new HashSet<>(); + for (LegacyGrid.Column column : section.grid.getColumns()) { + CELLTYPE cell = getCell(column.getPropertyId()); + if (visited.contains(cell)) { + continue; + } + visited.add(cell); + + Element cellElement = trElement + .appendElement(getCellTagName()); + cell.writeDesign(cellElement, designContext); + + for (Entry, CELLTYPE> entry : cellGroups + .entrySet()) { + if (entry.getValue() == cell) { + cellElement.attr("colspan", + "" + entry.getKey().size()); + break; + } + } + } + } + + /** + * Reads the declarative design from the given table row element. + * + * @since 7.5.0 + * @param trElement + * Element to read design from + * @param designContext + * the design context + * @throws DesignException + * if the given table row contains unexpected children + */ + protected void readDesign(Element trElement, + DesignContext designContext) throws DesignException { + Elements cellElements = trElement.children(); + int totalColSpans = 0; + for (int i = 0; i < cellElements.size(); ++i) { + Element element = cellElements.get(i); + if (!element.tagName().equals(getCellTagName())) { + throw new DesignException( + "Unexpected element in tr while expecting " + + getCellTagName() + ": " + + element.tagName()); + } + + int columnIndex = i + totalColSpans; + + int colspan = DesignAttributeHandler.readAttribute( + "colspan", element.attributes(), 1, int.class); + + Set cells = new HashSet<>(); + for (int c = 0; c < colspan; ++c) { + cells.add(getCell(section.grid.getColumns() + .get(columnIndex + c).getPropertyId())); + } + + if (colspan > 1) { + totalColSpans += colspan - 1; + join(cells).readDesign(element, designContext); + } else { + cells.iterator().next().readDesign(element, + designContext); + } + } + } + + abstract protected String getCellTagName(); + + void detach() { + for (CELLTYPE cell : cells.values()) { + cell.detach(); + } + } + } + + /** + * A header or footer cell. Has a simple textual caption. + */ + abstract static class StaticCell implements Serializable { + + private CellState cellState = new CellState(); + private StaticRow row; + + protected StaticCell(StaticRow row) { + this.row = row; + } + + void setColumnId(String id) { + cellState.columnId = id; + } + + String getColumnId() { + return cellState.columnId; + } + + /** + * Gets the row where this cell is. + * + * @return row for this cell + */ + public StaticRow getRow() { + return row; + } + + protected CellState getCellState() { + return cellState; + } + + /** + * Sets the text displayed in this cell. + * + * @param text + * a plain text caption + */ + public void setText(String text) { + removeComponentIfPresent(); + cellState.text = text; + cellState.type = GridStaticCellType.TEXT; + row.section.markAsDirty(); + } + + /** + * Returns the text displayed in this cell. + * + * @return the plain text caption + */ + public String getText() { + if (cellState.type != GridStaticCellType.TEXT) { + throw new IllegalStateException( + "Cannot fetch Text from a cell with type " + + cellState.type); + } + return cellState.text; + } + + /** + * Returns the HTML content displayed in this cell. + * + * @return the html + * + */ + public String getHtml() { + if (cellState.type != GridStaticCellType.HTML) { + throw new IllegalStateException( + "Cannot fetch HTML from a cell with type " + + cellState.type); + } + return cellState.html; + } + + /** + * Sets the HTML content displayed in this cell. + * + * @param html + * the html to set + */ + public void setHtml(String html) { + removeComponentIfPresent(); + cellState.html = html; + cellState.type = GridStaticCellType.HTML; + row.section.markAsDirty(); + } + + /** + * Returns the component displayed in this cell. + * + * @return the component + */ + public Component getComponent() { + if (cellState.type != GridStaticCellType.WIDGET) { + throw new IllegalStateException( + "Cannot fetch Component from a cell with type " + + cellState.type); + } + return (Component) cellState.connector; + } + + /** + * Sets the component displayed in this cell. + * + * @param component + * the component to set + */ + public void setComponent(Component component) { + removeComponentIfPresent(); + component.setParent(row.section.grid); + cellState.connector = component; + cellState.type = GridStaticCellType.WIDGET; + row.section.markAsDirty(); + } + + /** + * Returns the type of content stored in this cell. + * + * @return cell content type + */ + public GridStaticCellType getCellType() { + return cellState.type; + } + + /** + * Returns the custom style name for this cell. + * + * @return the style name or null if no style name has been set + */ + public String getStyleName() { + return cellState.styleName; + } + + /** + * Sets a custom style name for this cell. + * + * @param styleName + * the style name to set or null to not use any style + * name + */ + public void setStyleName(String styleName) { + cellState.styleName = styleName; + row.section.markAsDirty(); + } + + private void removeComponentIfPresent() { + Component component = (Component) cellState.connector; + if (component != null) { + component.setParent(null); + cellState.connector = null; + } + } + + /** + * Writes the declarative design to the given table cell element. + * + * @since 7.5.0 + * @param cellElement + * Element to write design to + * @param designContext + * the design context + */ + protected void writeDesign(Element cellElement, + DesignContext designContext) { + switch (cellState.type) { + case TEXT: + cellElement.attr("plain-text", true); + cellElement.appendText(getText()); + break; + case HTML: + cellElement.append(getHtml()); + break; + case WIDGET: + cellElement.appendChild( + designContext.createElement(getComponent())); + break; + } + } + + /** + * Reads the declarative design from the given table cell element. + * + * @since 7.5.0 + * @param cellElement + * Element to read design from + * @param designContext + * the design context + */ + protected void readDesign(Element cellElement, + DesignContext designContext) { + if (!cellElement.hasAttr("plain-text")) { + if (cellElement.children().size() > 0 + && cellElement.child(0).tagName().contains("-")) { + setComponent( + designContext.readDesign(cellElement.child(0))); + } else { + setHtml(cellElement.html()); + } + } else { + // text – need to unescape HTML entities + setText(DesignFormatter + .decodeFromTextNode(cellElement.html())); + } + } + + void detach() { + removeComponentIfPresent(); + } + } + + protected LegacyGrid grid; + protected List rows = new ArrayList<>(); + + /** + * Sets the visibility of the whole section. + * + * @param visible + * true to show this section, false to hide + */ + public void setVisible(boolean visible) { + if (getSectionState().visible != visible) { + getSectionState().visible = visible; + markAsDirty(); + } + } + + /** + * Returns the visibility of this section. + * + * @return true if visible, false otherwise. + */ + public boolean isVisible() { + return getSectionState().visible; + } + + /** + * Removes the row at the given position. + * + * @param rowIndex + * the position of the row + * + * @throws IllegalArgumentException + * if no row exists at given index + * @see #removeRow(StaticRow) + * @see #addRowAt(int) + * @see #appendRow() + * @see #prependRow() + */ + public ROWTYPE removeRow(int rowIndex) { + if (rowIndex >= rows.size() || rowIndex < 0) { + throw new IllegalArgumentException( + "No row at given index " + rowIndex); + } + ROWTYPE row = rows.remove(rowIndex); + row.detach(); + getSectionState().rows.remove(rowIndex); + + markAsDirty(); + return row; + } + + /** + * Removes the given row from the section. + * + * @param row + * the row to be removed + * + * @throws IllegalArgumentException + * if the row does not exist in this section + * @see #removeRow(int) + * @see #addRowAt(int) + * @see #appendRow() + * @see #prependRow() + */ + public void removeRow(ROWTYPE row) { + try { + removeRow(rows.indexOf(row)); + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Section does not contain the given row"); + } + } + + /** + * Gets row at given index. + * + * @param rowIndex + * 0 based index for row. Counted from top to bottom + * @return row at given index + */ + public ROWTYPE getRow(int rowIndex) { + if (rowIndex >= rows.size() || rowIndex < 0) { + throw new IllegalArgumentException( + "No row at given index " + rowIndex); + } + return rows.get(rowIndex); + } + + /** + * Adds a new row at the top of this section. + * + * @return the new row + * @see #appendRow() + * @see #addRowAt(int) + * @see #removeRow(StaticRow) + * @see #removeRow(int) + */ + public ROWTYPE prependRow() { + return addRowAt(0); + } + + /** + * Adds a new row at the bottom of this section. + * + * @return the new row + * @see #prependRow() + * @see #addRowAt(int) + * @see #removeRow(StaticRow) + * @see #removeRow(int) + */ + public ROWTYPE appendRow() { + return addRowAt(rows.size()); + } + + /** + * Inserts a new row at the given position. + * + * @param index + * the position at which to insert the row + * @return the new row + * + * @throws IndexOutOfBoundsException + * if the index is out of bounds + * @see #appendRow() + * @see #prependRow() + * @see #removeRow(StaticRow) + * @see #removeRow(int) + */ + public ROWTYPE addRowAt(int index) { + if (index > rows.size() || index < 0) { + throw new IllegalArgumentException( + "Unable to add row at index " + index); + } + ROWTYPE row = createRow(); + rows.add(index, row); + getSectionState().rows.add(index, row.getRowState()); + + for (Object id : grid.columns.keySet()) { + row.addCell(id); + } + + markAsDirty(); + return row; + } + + /** + * Gets the amount of rows in this section. + * + * @return row count + */ + public int getRowCount() { + return rows.size(); + } + + protected abstract GridStaticSectionState getSectionState(); + + protected abstract ROWTYPE createRow(); + + /** + * Informs the grid that state has changed and it should be redrawn. + */ + protected void markAsDirty() { + grid.markAsDirty(); + } + + /** + * Removes a column for given property id from the section. + * + * @param propertyId + * property to be removed + */ + protected void removeColumn(Object propertyId) { + for (ROWTYPE row : rows) { + row.removeCell(propertyId); + } + } + + /** + * Adds a column for given property id to the section. + * + * @param propertyId + * property to be added + */ + protected void addColumn(Object propertyId) { + for (ROWTYPE row : rows) { + row.addCell(propertyId); + } + } + + /** + * Performs a sanity check that section is in correct state. + * + * @throws IllegalStateException + * if merged cells are not i n continuous range + */ + protected void sanityCheck() throws IllegalStateException { + List columnOrder = grid.getState().columnOrder; + for (ROWTYPE row : rows) { + for (Set cellGroup : row.getRowState().cellGroups + .keySet()) { + if (!checkCellGroupAndOrder(columnOrder, cellGroup)) { + throw new IllegalStateException( + "Not all merged cells were in a continuous range."); + } + } + } + } + + private boolean checkCellGroupAndOrder(List columnOrder, + Set cellGroup) { + if (!columnOrder.containsAll(cellGroup)) { + return false; + } + + for (int i = 0; i < columnOrder.size(); ++i) { + if (!cellGroup.contains(columnOrder.get(i))) { + continue; + } + + for (int j = 1; j < cellGroup.size(); ++j) { + if (!cellGroup.contains(columnOrder.get(i + j))) { + return false; + } + } + return true; + } + return false; + } + + /** + * Writes the declarative design to the given table section element. + * + * @since 7.5.0 + * @param tableSectionElement + * Element to write design to + * @param designContext + * the design context + */ + protected void writeDesign(Element tableSectionElement, + DesignContext designContext) { + for (ROWTYPE row : rows) { + row.writeDesign(tableSectionElement.appendElement("tr"), + designContext); + } + } + + /** + * Writes the declarative design from the given table section element. + * + * @since 7.5.0 + * @param tableSectionElement + * Element to read design from + * @param designContext + * the design context + * @throws DesignException + * if the table section contains unexpected children + */ + protected void readDesign(Element tableSectionElement, + DesignContext designContext) throws DesignException { + while (rows.size() > 0) { + removeRow(0); + } + + for (Element row : tableSectionElement.children()) { + if (!row.tagName().equals("tr")) { + throw new DesignException("Unexpected element in " + + tableSectionElement.tagName() + ": " + + row.tagName()); + } + appendRow().readDesign(row, designContext); + } + } + } + + /** + * Represents the header section of a Grid. + */ + protected static class Header extends StaticSection { + + private HeaderRow defaultRow = null; + private final GridStaticSectionState headerState = new GridStaticSectionState(); + + protected Header(LegacyGrid grid) { + this.grid = grid; + grid.getState(true).header = headerState; + HeaderRow row = createRow(); + rows.add(row); + setDefaultRow(row); + getSectionState().rows.add(row.getRowState()); + } + + /** + * Sets the default row of this header. The default row is a special + * header row providing a user interface for sorting columns. + * + * @param row + * the new default row, or null for no default row + * + * @throws IllegalArgumentException + * this header does not contain the row + */ + public void setDefaultRow(HeaderRow row) { + if (row == defaultRow) { + return; + } + + if (row != null && !rows.contains(row)) { + throw new IllegalArgumentException( + "Cannot set a default row that does not exist in the section"); + } + + if (defaultRow != null) { + defaultRow.setDefaultRow(false); + } + + if (row != null) { + row.setDefaultRow(true); + } + + defaultRow = row; + markAsDirty(); + } + + /** + * Returns the current default row of this header. The default row is a + * special header row providing a user interface for sorting columns. + * + * @return the default row or null if no default row set + */ + public HeaderRow getDefaultRow() { + return defaultRow; + } + + @Override + protected GridStaticSectionState getSectionState() { + return headerState; + } + + @Override + protected HeaderRow createRow() { + return new HeaderRow(this); + } + + @Override + public HeaderRow removeRow(int rowIndex) { + HeaderRow row = super.removeRow(rowIndex); + if (row == defaultRow) { + // Default Header Row was just removed. + setDefaultRow(null); + } + return row; + } + + @Override + protected void sanityCheck() throws IllegalStateException { + super.sanityCheck(); + + boolean hasDefaultRow = false; + for (HeaderRow row : rows) { + if (row.getRowState().defaultRow) { + if (!hasDefaultRow) { + hasDefaultRow = true; + } else { + throw new IllegalStateException( + "Multiple default rows in header"); + } + } + } + } + + @Override + protected void readDesign(Element tableSectionElement, + DesignContext designContext) { + super.readDesign(tableSectionElement, designContext); + + if (defaultRow == null && !rows.isEmpty()) { + grid.setDefaultHeaderRow(rows.get(0)); + } + } + } + + /** + * Represents a header row in Grid. + */ + public static class HeaderRow extends StaticSection.StaticRow { + + protected HeaderRow(StaticSection section) { + super(section); + } + + private void setDefaultRow(boolean value) { + getRowState().defaultRow = value; + } + + private boolean isDefaultRow() { + return getRowState().defaultRow; + } + + @Override + protected HeaderCell createCell() { + return new HeaderCell(this); + } + + @Override + protected String getCellTagName() { + return "th"; + } + + @Override + protected void writeDesign(Element trElement, + DesignContext designContext) { + super.writeDesign(trElement, designContext); + + if (section.grid.getDefaultHeaderRow() == this) { + DesignAttributeHandler.writeAttribute("default", + trElement.attributes(), true, null, boolean.class); + } + } + + @Override + protected void readDesign(Element trElement, + DesignContext designContext) { + super.readDesign(trElement, designContext); + + boolean defaultRow = DesignAttributeHandler.readAttribute("default", + trElement.attributes(), false, boolean.class); + if (defaultRow) { + section.grid.setDefaultHeaderRow(this); + } + } + } + + /** + * Represents a header cell in Grid. Can be a merged cell for multiple + * columns. + */ + public static class HeaderCell extends StaticSection.StaticCell { + + protected HeaderCell(HeaderRow row) { + super(row); + } + } + + /** + * Represents the footer section of a Grid. By default Footer is not + * visible. + */ + protected static class Footer extends StaticSection { + + private final GridStaticSectionState footerState = new GridStaticSectionState(); + + protected Footer(LegacyGrid grid) { + this.grid = grid; + grid.getState(true).footer = footerState; + } + + @Override + protected GridStaticSectionState getSectionState() { + return footerState; + } + + @Override + protected FooterRow createRow() { + return new FooterRow(this); + } + + @Override + protected void sanityCheck() throws IllegalStateException { + super.sanityCheck(); + } + } + + /** + * Represents a footer row in Grid. + */ + public static class FooterRow extends StaticSection.StaticRow { + + protected FooterRow(StaticSection section) { + super(section); + } + + @Override + protected FooterCell createCell() { + return new FooterCell(this); + } + + @Override + protected String getCellTagName() { + return "td"; + } + + } + + /** + * Represents a footer cell in Grid. + */ + public static class FooterCell extends StaticSection.StaticCell { + + protected FooterCell(FooterRow row) { + super(row); + } + } + + /** + * A column in the grid. Can be obtained by calling + * {@link LegacyGrid#getColumn(Object propertyId)}. + */ + public static class Column implements Serializable { + + /** + * The state of the column shared to the client + */ + private final GridColumnState state; + + /** + * The grid this column is associated with + */ + private final LegacyGrid grid; + + /** + * Backing property for column + */ + private final Object propertyId; + + private LegacyConverter converter; + + /** + * A check for allowing the + * {@link #Column(LegacyGrid, GridColumnState, Object) constructor} to call + * {@link #setConverter(LegacyConverter)} with a null, even + * if model and renderer aren't compatible. + */ + private boolean isFirstConverterAssignment = true; + + /** + * Internally used constructor. + * + * @param grid + * The grid this column belongs to. Should not be null. + * @param state + * the shared state of this column + * @param propertyId + * the backing property id for this column + */ + Column(LegacyGrid grid, GridColumnState state, Object propertyId) { + this.grid = grid; + this.state = state; + this.propertyId = propertyId; + internalSetRenderer(new TextRenderer()); + } + + /** + * Returns the serializable state of this column that is sent to the + * client side connector. + * + * @return the internal state of the column + */ + GridColumnState getState() { + return state; + } + + /** + * Returns the property id for the backing property of this Column + * + * @return property id + */ + public Object getPropertyId() { + return propertyId; + } + + /** + * Returns the caption of the header. By default the header caption is + * the property id of the column. + * + * @return the text in the default row of header. + * + * @throws IllegalStateException + * if the column no longer is attached to the grid + */ + public String getHeaderCaption() throws IllegalStateException { + checkColumnIsAttached(); + + return state.headerCaption; + } + + /** + * Sets the caption of the header. This caption is also used as the + * hiding toggle caption, unless it is explicitly set via + * {@link #setHidingToggleCaption(String)}. + * + * @param caption + * the text to show in the caption + * @return the column itself + * + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + public Column setHeaderCaption(String caption) + throws IllegalStateException { + checkColumnIsAttached(); + if (caption == null) { + caption = ""; // Render null as empty + } + state.headerCaption = caption; + + HeaderRow row = grid.getHeader().getDefaultRow(); + if (row != null) { + row.getCell(grid.getPropertyIdByColumnId(state.id)) + .setText(caption); + } + return this; + } + + /** + * Gets the caption of the hiding toggle for this column. + * + * @since 7.5.0 + * @see #setHidingToggleCaption(String) + * @return the caption for the hiding toggle for this column + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + public String getHidingToggleCaption() throws IllegalStateException { + checkColumnIsAttached(); + return state.hidingToggleCaption; + } + + /** + * Sets the caption of the hiding toggle for this column. Shown in the + * toggle for this column in the grid's sidebar when the column is + * {@link #isHidable() hidable}. + *

+ * The default value is null, and in that case the column's + * {@link #getHeaderCaption() header caption} is used. + *

+ * NOTE: setting this to empty string might cause the hiding + * toggle to not render correctly. + * + * @since 7.5.0 + * @param hidingToggleCaption + * the text to show in the column hiding toggle + * @return the column itself + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + public Column setHidingToggleCaption(String hidingToggleCaption) + throws IllegalStateException { + checkColumnIsAttached(); + state.hidingToggleCaption = hidingToggleCaption; + grid.markAsDirty(); + return this; + } + + /** + * Returns the width (in pixels). By default a column is 100px wide. + * + * @return the width in pixels of the column + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + public double getWidth() throws IllegalStateException { + checkColumnIsAttached(); + return state.width; + } + + /** + * Sets the width (in pixels). + *

+ * This overrides any configuration set by any of + * {@link #setExpandRatio(int)}, {@link #setMinimumWidth(double)} or + * {@link #setMaximumWidth(double)}. + * + * @param pixelWidth + * the new pixel width of the column + * @return the column itself + * + * @throws IllegalStateException + * if the column is no longer attached to any grid + * @throws IllegalArgumentException + * thrown if pixel width is less than zero + */ + public Column setWidth(double pixelWidth) + throws IllegalStateException, IllegalArgumentException { + checkColumnIsAttached(); + if (pixelWidth < 0) { + throw new IllegalArgumentException( + "Pixel width should be greated than 0 (in " + toString() + + ")"); + } + if (state.width != pixelWidth) { + state.width = pixelWidth; + grid.markAsDirty(); + grid.fireColumnResizeEvent(this, false); + } + return this; + } + + /** + * Returns whether this column has an undefined width. + * + * @since 7.6 + * @return whether the width is undefined + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + public boolean isWidthUndefined() { + checkColumnIsAttached(); + return state.width < 0; + } + + /** + * Marks the column width as undefined. An undefined width means the + * grid is free to resize the column based on the cell contents and + * available space in the grid. + * + * @return the column itself + */ + public Column setWidthUndefined() { + checkColumnIsAttached(); + if (!isWidthUndefined()) { + state.width = -1; + grid.markAsDirty(); + grid.fireColumnResizeEvent(this, false); + } + return this; + } + + /** + * Checks if column is attached and throws an + * {@link IllegalStateException} if it is not + * + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + protected void checkColumnIsAttached() throws IllegalStateException { + if (grid.getColumnByColumnId(state.id) == null) { + throw new IllegalStateException("Column no longer exists."); + } + } + + /** + * Sets this column as the last frozen column in its grid. + * + * @return the column itself + * + * @throws IllegalArgumentException + * if the column is no longer attached to any grid + * @see LegacyGrid#setFrozenColumnCount(int) + */ + public Column setLastFrozenColumn() { + checkColumnIsAttached(); + grid.setFrozenColumnCount( + grid.getState(false).columnOrder.indexOf(getState().id) + + 1); + return this; + } + + /** + * Sets the renderer for this column. + *

+ * If a suitable converter isn't defined explicitly, the session + * converter factory is used to find a compatible converter. + * + * @param renderer + * the renderer to use + * @return the column itself + * + * @throws IllegalArgumentException + * if no compatible converter could be found + * + * @see VaadinSession#getConverterFactory() + * @see LegacyConverterUtil#getConverter(Class, Class, VaadinSession) + * @see #setConverter(LegacyConverter) + */ + public Column setRenderer(Renderer renderer) { + if (!internalSetRenderer(renderer)) { + throw new IllegalArgumentException( + "Could not find a converter for converting from the model type " + + getModelType() + + " to the renderer presentation type " + + renderer.getPresentationType() + " (in " + + toString() + ")"); + } + return this; + } + + /** + * Sets the renderer for this column and the converter used to convert + * from the property value type to the renderer presentation type. + * + * @param renderer + * the renderer to use, cannot be null + * @param converter + * the converter to use + * @return the column itself + * + * @throws IllegalArgumentException + * if the renderer is already associated with a grid column + */ + public Column setRenderer(Renderer renderer, + LegacyConverter converter) { + if (renderer.getParent() != null) { + throw new IllegalArgumentException( + "Cannot set a renderer that is already connected to a grid column (in " + + toString() + ")"); + } + + if (getRenderer() != null) { + grid.removeExtension(getRenderer()); + } + + grid.addRenderer(renderer); + state.rendererConnector = renderer; + setConverter(converter); + return this; + } + + /** + * Sets the converter used to convert from the property value type to + * the renderer presentation type. + * + * @param converter + * the converter to use, or {@code null} to not use any + * converters + * @return the column itself + * + * @throws IllegalArgumentException + * if the types are not compatible + */ + public Column setConverter(LegacyConverter converter) + throws IllegalArgumentException { + Class modelType = getModelType(); + if (converter != null) { + if (!converter.getModelType().isAssignableFrom(modelType)) { + throw new IllegalArgumentException( + "The converter model type " + + converter.getModelType() + + " is not compatible with the property type " + + modelType + " (in " + toString() + ")"); + + } else if (!getRenderer().getPresentationType() + .isAssignableFrom(converter.getPresentationType())) { + throw new IllegalArgumentException( + "The converter presentation type " + + converter.getPresentationType() + + " is not compatible with the renderer presentation type " + + getRenderer().getPresentationType() + + " (in " + toString() + ")"); + } + } + + else { + /* + * Since the converter is null (i.e. will be removed), we need + * to know that the renderer and model are compatible. If not, + * we can't allow for this to happen. + * + * The constructor is allowed to call this method with null + * without any compatibility checks, therefore we have a special + * case for it. + */ + + Class rendererPresentationType = getRenderer() + .getPresentationType(); + if (!isFirstConverterAssignment && !rendererPresentationType + .isAssignableFrom(modelType)) { + throw new IllegalArgumentException( + "Cannot remove converter, " + + "as renderer's presentation type " + + rendererPresentationType.getName() + + " and column's " + "model " + + modelType.getName() + " type aren't " + + "directly compatible with each other (in " + + toString() + ")"); + } + } + + isFirstConverterAssignment = false; + + @SuppressWarnings("unchecked") + LegacyConverter castConverter = (LegacyConverter) converter; + this.converter = castConverter; + + return this; + } + + /** + * Returns the renderer instance used by this column. + * + * @return the renderer + */ + public Renderer getRenderer() { + return (Renderer) getState().rendererConnector; + } + + /** + * Returns the converter instance used by this column. + * + * @return the converter + */ + public LegacyConverter getConverter() { + return converter; + } + + private boolean internalSetRenderer(Renderer renderer) { + + LegacyConverter converter; + if (isCompatibleWithProperty(renderer, getConverter())) { + // Use the existing converter (possibly none) if types + // compatible + converter = (LegacyConverter) getConverter(); + } else { + converter = LegacyConverterUtil.getConverter( + renderer.getPresentationType(), getModelType(), + getSession()); + } + setRenderer(renderer, converter); + return isCompatibleWithProperty(renderer, converter); + } + + private VaadinSession getSession() { + UI ui = grid.getUI(); + return ui != null ? ui.getSession() : null; + } + + private boolean isCompatibleWithProperty(Renderer renderer, + LegacyConverter converter) { + Class type; + if (converter == null) { + type = getModelType(); + } else { + type = converter.getPresentationType(); + } + return renderer.getPresentationType().isAssignableFrom(type); + } + + private Class getModelType() { + return grid.getContainerDataSource() + .getType(grid.getPropertyIdByColumnId(state.id)); + } + + /** + * Sets whether this column is sortable by the user. The grid can be + * sorted by a sortable column by clicking or tapping the column's + * default header. Programmatic sorting using the Grid#sort methods is + * not affected by this setting. + * + * @param sortable + * {@code true} if the user should be able to sort the + * column, {@code false} otherwise + * @return the column itself + * + * @throws IllegalStateException + * if the data source of the Grid does not implement + * {@link Sortable} + * @throws IllegalStateException + * if the data source does not support sorting by the + * property associated with this column + */ + public Column setSortable(boolean sortable) { + checkColumnIsAttached(); + + if (sortable) { + if (!(grid.datasource instanceof Sortable)) { + throw new IllegalStateException("Can't set column " + + toString() + + " sortable. The Container of Grid does not implement Sortable"); + } else if (!((Sortable) grid.datasource) + .getSortableContainerPropertyIds() + .contains(propertyId)) { + throw new IllegalStateException( + "Can't set column " + toString() + + " sortable. Container doesn't support sorting by property " + + propertyId); + } + } + + state.sortable = sortable; + grid.markAsDirty(); + return this; + } + + /** + * Returns whether the user can sort the grid by this column. + *

+ * Note: it is possible to sort by this column programmatically + * using the Grid#sort methods regardless of the returned value. + * + * @return {@code true} if the column is sortable by the user, + * {@code false} otherwise + */ + public boolean isSortable() { + return state.sortable; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[propertyId:" + + grid.getPropertyIdByColumnId(state.id) + "]"; + } + + /** + * Sets the ratio with which the column expands. + *

+ * By default, all columns expand equally (treated as if all of them had + * an expand ratio of 1). Once at least one column gets a defined expand + * ratio, the implicit expand ratio is removed, and only the defined + * expand ratios are taken into account. + *

+ * If a column has a defined width ({@link #setWidth(double)}), it + * overrides this method's effects. + *

+ * Example: A grid with three columns, with expand ratios 0, 1 + * and 2, respectively. The column with a ratio of 0 is exactly + * as wide as its contents requires. The column with a ratio of + * 1 is as wide as it needs, plus a third of any excess + * space, because we have 3 parts total, and this column + * reserves only one of those. The column with a ratio of 2, is as wide + * as it needs to be, plus two thirds of the excess + * width. + * + * @param expandRatio + * the expand ratio of this column. {@code 0} to not have it + * expand at all. A negative number to clear the expand + * value. + * @throws IllegalStateException + * if the column is no longer attached to any grid + * @see #setWidth(double) + */ + public Column setExpandRatio(int expandRatio) + throws IllegalStateException { + checkColumnIsAttached(); + + getState().expandRatio = expandRatio; + grid.markAsDirty(); + return this; + } + + /** + * Returns the column's expand ratio. + * + * @return the column's expand ratio + * @see #setExpandRatio(int) + */ + public int getExpandRatio() { + return getState().expandRatio; + } + + /** + * Clears the expand ratio for this column. + *

+ * Equal to calling {@link #setExpandRatio(int) setExpandRatio(-1)} + * + * @throws IllegalStateException + * if the column is no longer attached to any grid + */ + public Column clearExpandRatio() throws IllegalStateException { + return setExpandRatio(-1); + } + + /** + * Sets the minimum width for this column. + *

+ * This defines the minimum guaranteed pixel width of the column + * when it is set to expand. + * + * @throws IllegalStateException + * if the column is no longer attached to any grid + * @see #setExpandRatio(int) + */ + public Column setMinimumWidth(double pixels) + throws IllegalStateException { + checkColumnIsAttached(); + + final double maxwidth = getMaximumWidth(); + if (pixels >= 0 && pixels > maxwidth && maxwidth >= 0) { + throw new IllegalArgumentException("New minimum width (" + + pixels + ") was greater than maximum width (" + + maxwidth + ")"); + } + getState().minWidth = pixels; + grid.markAsDirty(); + return this; + } + + /** + * Return the minimum width for this column. + * + * @return the minimum width for this column + * @see #setMinimumWidth(double) + */ + public double getMinimumWidth() { + return getState().minWidth; + } + + /** + * Sets the maximum width for this column. + *

+ * This defines the maximum allowed pixel width of the column when + * it is set to expand. + * + * @param pixels + * the maximum width + * @throws IllegalStateException + * if the column is no longer attached to any grid + * @see #setExpandRatio(int) + */ + public Column setMaximumWidth(double pixels) { + checkColumnIsAttached(); + + final double minwidth = getMinimumWidth(); + if (pixels >= 0 && pixels < minwidth && minwidth >= 0) { + throw new IllegalArgumentException("New maximum width (" + + pixels + ") was less than minimum width (" + minwidth + + ")"); + } + + getState().maxWidth = pixels; + grid.markAsDirty(); + return this; + } + + /** + * Returns the maximum width for this column. + * + * @return the maximum width for this column + * @see #setMaximumWidth(double) + */ + public double getMaximumWidth() { + return getState().maxWidth; + } + + /** + * Sets whether the properties corresponding to this column should be + * editable when the item editor is active. By default columns are + * editable. + *

+ * Values in non-editable columns are currently not displayed when the + * editor is active, but this will probably change in the future. They + * are not automatically assigned an editor field and, if one is + * manually assigned, it is not used. Columns that cannot (or should + * not) be edited even in principle should be set non-editable. + * + * @param editable + * {@code true} if this column should be editable, + * {@code false} otherwise + * @return this column + * + * @throws IllegalStateException + * if the editor is currently active + * + * @see LegacyGrid#editItem(Object) + * @see LegacyGrid#isEditorActive() + */ + public Column setEditable(boolean editable) { + checkColumnIsAttached(); + if (grid.isEditorActive()) { + throw new IllegalStateException( + "Cannot change column editable status while the editor is active"); + } + getState().editable = editable; + grid.markAsDirty(); + return this; + } + + /** + * Returns whether the properties corresponding to this column should be + * editable when the item editor is active. + * + * @return {@code true} if this column is editable, {@code false} + * otherwise + * + * @see LegacyGrid#editItem(Object) + * @see #setEditable(boolean) + */ + + public boolean isEditable() { + return getState().editable; + } + + /** + * Sets the field component used to edit the properties in this column + * when the item editor is active. If an item has not been set, then the + * binding is postponed until the item is set using + * {@link #editItem(Object)}. + *

+ * Setting the field to null clears any previously set + * field, causing a new field to be created the next time the item + * editor is opened. + * + * @param editor + * the editor field + * @return this column + */ + public Column setEditorField(LegacyField editor) { + grid.setEditorField(getPropertyId(), editor); + return this; + } + + /** + * Returns the editor field used to edit the properties in this column + * when the item editor is active. Returns null if the column is not + * {@link Column#isEditable() editable}. + *

+ * When {@link #editItem(Object) editItem} is called, fields are + * automatically created and bound for any unbound properties. + *

+ * Getting a field before the editor has been opened depends on special + * support from the {@link FieldGroup} in use. Using this method with a + * user-provided FieldGroup might cause + * {@link com.vaadin.data.fieldgroup.FieldGroup.BindException + * BindException} to be thrown. + * + * @return the bound field; or null if the respective + * column is not editable + * + * @throws IllegalArgumentException + * if there is no column for the provided property id + * @throws FieldGroup.BindException + * if no field has been configured and there is a problem + * building or binding + */ + public LegacyField getEditorField() { + return grid.getEditorField(getPropertyId()); + } + + /** + * Hides or shows the column. By default columns are visible before + * explicitly hiding them. + * + * @since 7.5.0 + * @param hidden + * true to hide the column, false + * to show + * @return this column + */ + public Column setHidden(boolean hidden) { + if (hidden != getState().hidden) { + getState().hidden = hidden; + grid.markAsDirty(); + grid.fireColumnVisibilityChangeEvent(this, hidden, false); + } + return this; + } + + /** + * Returns whether this column is hidden. Default is {@code false}. + * + * @since 7.5.0 + * @return true if the column is currently hidden, + * false otherwise + */ + public boolean isHidden() { + return getState().hidden; + } + + /** + * Sets whether this column can be hidden by the user. Hidable columns + * can be hidden and shown via the sidebar menu. + * + * @since 7.5.0 + * @param hidable + * true iff the column may be hidable by the + * user via UI interaction + * @return this column + */ + public Column setHidable(boolean hidable) { + if (hidable != getState().hidable) { + getState().hidable = hidable; + grid.markAsDirty(); + } + return this; + } + + /** + * Returns whether this column can be hidden by the user. Default is + * {@code false}. + *

+ * Note: the column can be programmatically hidden using + * {@link #setHidden(boolean)} regardless of the returned value. + * + * @since 7.5.0 + * @return true if the user can hide the column, + * false if not + */ + public boolean isHidable() { + return getState().hidable; + } + + /** + * Sets whether this column can be resized by the user. + * + * @since 7.6 + * @param resizable + * {@code true} if this column should be resizable, + * {@code false} otherwise + */ + public Column setResizable(boolean resizable) { + if (resizable != getState().resizable) { + getState().resizable = resizable; + grid.markAsDirty(); + } + return this; + } + + /** + * Returns whether this column can be resized by the user. Default is + * {@code true}. + *

+ * Note: the column can be programmatically resized using + * {@link #setWidth(double)} and {@link #setWidthUndefined()} regardless + * of the returned value. + * + * @since 7.6 + * @return {@code true} if this column is resizable, {@code false} + * otherwise + */ + public boolean isResizable() { + return getState().resizable; + } + + /** + * Writes the design attributes for this column into given element. + * + * @since 7.5.0 + * + * @param design + * Element to write attributes into + * + * @param designContext + * the design context + */ + protected void writeDesign(Element design, + DesignContext designContext) { + Attributes attributes = design.attributes(); + GridColumnState def = new GridColumnState(); + + DesignAttributeHandler.writeAttribute("property-id", attributes, + getPropertyId(), null, Object.class); + + // Sortable is a special attribute that depends on the container. + DesignAttributeHandler.writeAttribute("sortable", attributes, + isSortable(), null, boolean.class); + DesignAttributeHandler.writeAttribute("editable", attributes, + isEditable(), def.editable, boolean.class); + DesignAttributeHandler.writeAttribute("resizable", attributes, + isResizable(), def.resizable, boolean.class); + + DesignAttributeHandler.writeAttribute("hidable", attributes, + isHidable(), def.hidable, boolean.class); + DesignAttributeHandler.writeAttribute("hidden", attributes, + isHidden(), def.hidden, boolean.class); + DesignAttributeHandler.writeAttribute("hiding-toggle-caption", + attributes, getHidingToggleCaption(), null, String.class); + + DesignAttributeHandler.writeAttribute("width", attributes, + getWidth(), def.width, Double.class); + DesignAttributeHandler.writeAttribute("min-width", attributes, + getMinimumWidth(), def.minWidth, Double.class); + DesignAttributeHandler.writeAttribute("max-width", attributes, + getMaximumWidth(), def.maxWidth, Double.class); + DesignAttributeHandler.writeAttribute("expand", attributes, + getExpandRatio(), def.expandRatio, Integer.class); + } + + /** + * Reads the design attributes for this column from given element. + * + * @since 7.5.0 + * @param design + * Element to read attributes from + * @param designContext + * the design context + */ + protected void readDesign(Element design, DesignContext designContext) { + Attributes attributes = design.attributes(); + + if (design.hasAttr("sortable")) { + setSortable(DesignAttributeHandler.readAttribute("sortable", + attributes, boolean.class)); + } + if (design.hasAttr("editable")) { + setEditable(DesignAttributeHandler.readAttribute("editable", + attributes, boolean.class)); + } + if (design.hasAttr("resizable")) { + setResizable(DesignAttributeHandler.readAttribute("resizable", + attributes, boolean.class)); + } + + if (design.hasAttr("hidable")) { + setHidable(DesignAttributeHandler.readAttribute("hidable", + attributes, boolean.class)); + } + if (design.hasAttr("hidden")) { + setHidden(DesignAttributeHandler.readAttribute("hidden", + attributes, boolean.class)); + } + if (design.hasAttr("hiding-toggle-caption")) { + setHidingToggleCaption(DesignAttributeHandler.readAttribute( + "hiding-toggle-caption", attributes, String.class)); + } + + // Read size info where necessary. + if (design.hasAttr("width")) { + setWidth(DesignAttributeHandler.readAttribute("width", + attributes, Double.class)); + } + if (design.hasAttr("min-width")) { + setMinimumWidth(DesignAttributeHandler + .readAttribute("min-width", attributes, Double.class)); + } + if (design.hasAttr("max-width")) { + setMaximumWidth(DesignAttributeHandler + .readAttribute("max-width", attributes, Double.class)); + } + if (design.hasAttr("expand")) { + if (design.attr("expand").isEmpty()) { + setExpandRatio(1); + } else { + setExpandRatio(DesignAttributeHandler.readAttribute( + "expand", attributes, Integer.class)); + } + } + } + } + + /** + * An abstract base class for server-side + * {@link com.vaadin.ui.renderers.Renderer Grid renderers}. This class + * currently extends the AbstractExtension superclass, but this fact should + * be regarded as an implementation detail and subject to change in a future + * major or minor Vaadin revision. + * + * @param + * the type this renderer knows how to present + */ + public static abstract class AbstractRenderer + extends AbstractGridExtension implements Renderer { + + private final Class presentationType; + + private final String nullRepresentation; + + protected AbstractRenderer(Class presentationType, + String nullRepresentation) { + this.presentationType = presentationType; + this.nullRepresentation = nullRepresentation; + } + + protected AbstractRenderer(Class presentationType) { + this(presentationType, null); + } + + /** + * This method is inherited from AbstractExtension but should never be + * called directly with an AbstractRenderer. + */ + @Deprecated + @Override + protected Class getSupportedParentType() { + return LegacyGrid.class; + } + + /** + * This method is inherited from AbstractExtension but should never be + * called directly with an AbstractRenderer. + */ + @Deprecated + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + } + + @Override + public Class getPresentationType() { + return presentationType; + } + + @Override + public JsonValue encode(T value) { + if (value == null) { + return encode(getNullRepresentation(), String.class); + } else { + return encode(value, getPresentationType()); + } + } + + /** + * Null representation for the renderer + * + * @return a textual representation of {@code null} + */ + protected String getNullRepresentation() { + return nullRepresentation; + } + + /** + * Encodes the given value to JSON. + *

+ * This is a helper method that can be invoked by an + * {@link #encode(Object) encode(T)} override if serializing a value of + * type other than {@link #getPresentationType() the presentation type} + * is desired. For instance, a {@code Renderer} could first turn a + * date value into a formatted string and return + * {@code encode(dateString, String.class)}. + * + * @param value + * the value to be encoded + * @param type + * the type of the value + * @return a JSON representation of the given value + */ + protected JsonValue encode(U value, Class type) { + return JsonCodec + .encode(value, null, type, getUI().getConnectorTracker()) + .getEncodedValue(); + } + + /** + * Converts and encodes the given data model property value using the + * given converter and renderer. This method is public only for testing + * purposes. + * + * @since 7.6 + * @param renderer + * the renderer to use + * @param converter + * the converter to use + * @param modelValue + * the value to convert and encode + * @param locale + * the locale to use in conversion + * @return an encoded value ready to be sent to the client + */ + public static JsonValue encodeValue(Object modelValue, + Renderer renderer, LegacyConverter converter, + Locale locale) { + Class presentationType = renderer.getPresentationType(); + T presentationValue; + + if (converter == null) { + try { + presentationValue = presentationType.cast(modelValue); + } catch (ClassCastException e) { + if (presentationType == String.class) { + // If there is no converter, just fallback to using + // toString(). modelValue can't be null as + // Class.cast(null) will always succeed + presentationValue = (T) modelValue.toString(); + } else { + throw new LegacyConverter.ConversionException( + "Unable to convert value of type " + + modelValue.getClass().getName() + + " to presentation type " + + presentationType.getName() + + ". No converter is set and the types are not compatible."); + } + } + } else { + assert presentationType + .isAssignableFrom(converter.getPresentationType()); + @SuppressWarnings("unchecked") + LegacyConverter safeConverter = (LegacyConverter) converter; + presentationValue = safeConverter.convertToPresentation( + modelValue, safeConverter.getPresentationType(), + locale); + } + + JsonValue encodedValue; + try { + encodedValue = renderer.encode(presentationValue); + } catch (Exception e) { + getLogger().log(Level.SEVERE, "Unable to encode data", e); + encodedValue = renderer.encode(null); + } + + return encodedValue; + } + + private static Logger getLogger() { + return Logger.getLogger(AbstractRenderer.class.getName()); + } + + } + + /** + * An abstract base class for server-side Grid extensions. + *

+ * Note: If the extension is an instance of {@link DataGenerator} it will + * automatically register itself to {@link RpcDataProviderExtension} of + * extended Grid. On remove this registration is automatically removed. + * + * @since 7.5 + */ + public static abstract class AbstractGridExtension + extends AbstractExtension { + + /** + * Constructs a new Grid extension. + */ + public AbstractGridExtension() { + super(); + } + + /** + * Constructs a new Grid extension and extends given Grid. + * + * @param grid + * a grid instance + */ + public AbstractGridExtension(LegacyGrid grid) { + super(); + extend(grid); + } + + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + + if (this instanceof DataGenerator) { + getParentGrid().datasourceExtension + .addDataGenerator((DataGenerator) this); + } + } + + @Override + public void remove() { + if (this instanceof DataGenerator) { + getParentGrid().datasourceExtension + .removeDataGenerator((DataGenerator) this); + } + + super.remove(); + } + + /** + * Gets the item id for a row key. + *

+ * A key is used to identify a particular row on both a server and a + * client. This method can be used to get the item id for the row key + * that the client has sent. + * + * @param rowKey + * the row key for which to retrieve an item id + * @return the item id corresponding to {@code key} + */ + protected Object getItemId(String rowKey) { + return getParentGrid().getKeyMapper().get(rowKey); + } + + /** + * Gets the column for a column id. + *

+ * An id is used to identify a particular column on both a server and a + * client. This method can be used to get the column for the column id + * that the client has sent. + * + * @param columnId + * the column id for which to retrieve a column + * @return the column corresponding to {@code columnId} + */ + protected Column getColumn(String columnId) { + return getParentGrid().getColumnByColumnId(columnId); + } + + /** + * Gets the parent Grid of the renderer. + * + * @return parent grid + * @throws IllegalStateException + * if parent is not Grid + */ + protected LegacyGrid getParentGrid() { + if (getParent() instanceof LegacyGrid) { + LegacyGrid grid = (LegacyGrid) getParent(); + return grid; + } else if (getParent() == null) { + throw new IllegalStateException( + "Renderer is not attached to any parent"); + } else { + throw new IllegalStateException( + "Renderers can be used only with Grid. Extended " + + getParent().getClass().getSimpleName() + + " instead"); + } + } + + /** + * Resends the row data for given item id to the client. + * + * @since 7.6 + * @param itemId + * row to refresh + */ + protected void refreshRow(Object itemId) { + getParentGrid().datasourceExtension.updateRowData(itemId); + } + + /** + * Informs the parent Grid that this Extension wants to add a child + * component to it. + * + * @since 7.6 + * @param c + * component + */ + protected void addComponentToGrid(Component c) { + getParentGrid().addComponent(c); + } + + /** + * Informs the parent Grid that this Extension wants to remove a child + * component from it. + * + * @since 7.6 + * @param c + * component + */ + protected void removeComponentFromGrid(Component c) { + getParentGrid().removeComponent(c); + } + } + + /** + * The data source attached to the grid + */ + private Container.Indexed datasource; + + /** + * Property id to column instance mapping + */ + private final Map columns = new HashMap<>(); + + /** + * Key generator for column server-to-client communication + */ + private final KeyMapper columnKeys = new KeyMapper<>(); + + /** + * The current sort order + */ + private final List sortOrder = new ArrayList<>(); + + /** + * Property listener for listening to changes in data source properties. + */ + private final PropertySetChangeListener propertyListener = new PropertySetChangeListener() { + + @Override + public void containerPropertySetChange(PropertySetChangeEvent event) { + Collection properties = new HashSet( + event.getContainer().getContainerPropertyIds()); + + // Find columns that need to be removed. + List removedColumns = new LinkedList<>(); + for (Object propertyId : columns.keySet()) { + if (!properties.contains(propertyId)) { + removedColumns.add(getColumn(propertyId)); + } + } + + // Actually remove columns. + for (Column column : removedColumns) { + Object propertyId = column.getPropertyId(); + internalRemoveColumn(propertyId); + columnKeys.remove(propertyId); + } + datasourceExtension.columnsRemoved(removedColumns); + + // Add new columns + List addedColumns = new LinkedList<>(); + for (Object propertyId : properties) { + if (!columns.containsKey(propertyId)) { + addedColumns.add(appendColumn(propertyId)); + } + } + datasourceExtension.columnsAdded(addedColumns); + + if (getFrozenColumnCount() > columns.size()) { + setFrozenColumnCount(columns.size()); + } + + // Unset sortable for non-sortable columns. + if (datasource instanceof Sortable) { + Collection sortables = ((Sortable) datasource) + .getSortableContainerPropertyIds(); + for (Object propertyId : columns.keySet()) { + Column column = columns.get(propertyId); + if (!sortables.contains(propertyId) + && column.isSortable()) { + column.setSortable(false); + } + } + } + } + }; + + private final ItemSetChangeListener editorClosingItemSetListener = new ItemSetChangeListener() { + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + cancelEditor(); + } + }; + + private RpcDataProviderExtension datasourceExtension; + + /** + * The selection model that is currently in use. Never null + * after the constructor has been run. + */ + private SelectionModel selectionModel; + + /** + * Used to know whether selection change events originate from the server or + * the client so the selection change handler knows whether the changes + * should be sent to the client. + */ + private boolean applyingSelectionFromClient; + + private final Header header = new Header(this); + private final Footer footer = new Footer(this); + + private Object editedItemId = null; + private boolean editorActive = false; + private FieldGroup editorFieldGroup = new CustomFieldGroup(); + + private CellStyleGenerator cellStyleGenerator; + private RowStyleGenerator rowStyleGenerator; + + private CellDescriptionGenerator cellDescriptionGenerator; + private RowDescriptionGenerator rowDescriptionGenerator; + + /** + * true if Grid is using the internal IndexedContainer created + * in Grid() constructor, or false if the user has set their + * own Container. + * + * @see #setContainerDataSource(Indexed) + * @see #LegacyGrid() + */ + private boolean defaultContainer = true; + + private EditorErrorHandler editorErrorHandler = new DefaultEditorErrorHandler(); + + private DetailComponentManager detailComponentManager = null; + + private Set extensionComponents = new HashSet<>(); + + private static final Method SELECTION_CHANGE_METHOD = ReflectTools + .findMethod(SelectionListener.class, "select", + SelectionEvent.class); + + private static final Method SORT_ORDER_CHANGE_METHOD = ReflectTools + .findMethod(SortListener.class, "sort", SortEvent.class); + + private static final Method COLUMN_REORDER_METHOD = ReflectTools.findMethod( + ColumnReorderListener.class, "columnReorder", + ColumnReorderEvent.class); + + private static final Method COLUMN_RESIZE_METHOD = ReflectTools.findMethod( + ColumnResizeListener.class, "columnResize", + ColumnResizeEvent.class); + + private static final Method COLUMN_VISIBILITY_METHOD = ReflectTools + .findMethod(ColumnVisibilityChangeListener.class, + "columnVisibilityChanged", + ColumnVisibilityChangeEvent.class); + + /** + * Creates a new Grid with a new {@link IndexedContainer} as the data + * source. + */ + public LegacyGrid() { + this(null, null); + } + + /** + * Creates a new Grid using the given data source. + * + * @param dataSource + * the indexed container to use as a data source + */ + public LegacyGrid(final Container.Indexed dataSource) { + this(null, dataSource); + } + + /** + * Creates a new Grid with the given caption and a new + * {@link IndexedContainer} data source. + * + * @param caption + * the caption of the grid + */ + public LegacyGrid(String caption) { + this(caption, null); + } + + /** + * Creates a new Grid with the given caption and data source. If the data + * source is null, a new {@link IndexedContainer} will be used. + * + * @param caption + * the caption of the grid + * @param dataSource + * the indexed container to use as a data source + */ + public LegacyGrid(String caption, Container.Indexed dataSource) { + if (dataSource == null) { + internalSetContainerDataSource(new IndexedContainer()); + } else { + setContainerDataSource(dataSource); + } + setCaption(caption); + initGrid(); + } + + /** + * Grid initial setup + */ + private void initGrid() { + setSelectionMode(getDefaultSelectionMode()); + + registerRpc(new GridServerRpc() { + + @Override + public void sort(String[] columnIds, SortDirection[] directions, + boolean userOriginated) { + assert columnIds.length == directions.length; + + List order = new ArrayList<>(columnIds.length); + for (int i = 0; i < columnIds.length; i++) { + Object propertyId = getPropertyIdByColumnId(columnIds[i]); + order.add(new SortOrder(propertyId, directions[i])); + } + setSortOrder(order, userOriginated); + if (!order.equals(getSortOrder())) { + /* + * Actual sort order is not what the client expects. Make + * sure the client gets a state change event by clearing the + * diffstate and marking as dirty + */ + ConnectorTracker connectorTracker = getUI() + .getConnectorTracker(); + JsonObject diffState = connectorTracker + .getDiffState(LegacyGrid.this); + diffState.remove("sortColumns"); + diffState.remove("sortDirs"); + markAsDirty(); + } + } + + @Override + public void itemClick(String rowKey, String columnId, + MouseEventDetails details) { + Object itemId = getKeyMapper().get(rowKey); + Item item = datasource.getItem(itemId); + Object propertyId = getPropertyIdByColumnId(columnId); + fireEvent(new ItemClickEvent(LegacyGrid.this, item, itemId, + propertyId, details)); + } + + @Override + public void columnsReordered(List newColumnOrder, + List oldColumnOrder) { + final String diffStateKey = "columnOrder"; + ConnectorTracker connectorTracker = getUI() + .getConnectorTracker(); + JsonObject diffState = connectorTracker.getDiffState(LegacyGrid.this); + // discard the change if the columns have been reordered from + // the server side, as the server side is always right + if (getState(false).columnOrder.equals(oldColumnOrder)) { + // Don't mark as dirty since client has the state already + getState(false).columnOrder = newColumnOrder; + // write changes to diffState so that possible reverting the + // column order is sent to client + assert diffState + .hasKey(diffStateKey) : "Field name has changed"; + Type type = null; + try { + type = (getState(false).getClass() + .getDeclaredField(diffStateKey) + .getGenericType()); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + EncodeResult encodeResult = JsonCodec.encode( + getState(false).columnOrder, diffState, type, + connectorTracker); + + diffState.put(diffStateKey, encodeResult.getEncodedValue()); + fireColumnReorderEvent(true); + } else { + // make sure the client is reverted to the order that the + // server thinks it is + diffState.remove(diffStateKey); + markAsDirty(); + } + } + + @Override + public void columnVisibilityChanged(String id, boolean hidden, + boolean userOriginated) { + final Column column = getColumnByColumnId(id); + final GridColumnState columnState = column.getState(); + + if (columnState.hidden != hidden) { + columnState.hidden = hidden; + + final String diffStateKey = "columns"; + ConnectorTracker connectorTracker = getUI() + .getConnectorTracker(); + JsonObject diffState = connectorTracker + .getDiffState(LegacyGrid.this); + + assert diffState + .hasKey(diffStateKey) : "Field name has changed"; + Type type = null; + try { + type = (getState(false).getClass() + .getDeclaredField(diffStateKey) + .getGenericType()); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + EncodeResult encodeResult = JsonCodec.encode( + getState(false).columns, diffState, type, + connectorTracker); + + diffState.put(diffStateKey, encodeResult.getEncodedValue()); + + fireColumnVisibilityChangeEvent(column, hidden, + userOriginated); + } + } + + @Override + public void contextClick(int rowIndex, String rowKey, + String columnId, Section section, + MouseEventDetails details) { + Object itemId = null; + if (rowKey != null) { + itemId = getKeyMapper().get(rowKey); + } + fireEvent(new GridContextClickEvent(LegacyGrid.this, details, section, + rowIndex, itemId, getPropertyIdByColumnId(columnId))); + } + + @Override + public void columnResized(String id, double pixels) { + final Column column = getColumnByColumnId(id); + if (column != null && column.isResizable()) { + column.getState().width = pixels; + fireColumnResizeEvent(column, true); + markAsDirty(); + } + } + }); + + registerRpc(new EditorServerRpc() { + + @Override + public void bind(int rowIndex) { + try { + Object id = getContainerDataSource().getIdByIndex(rowIndex); + + final boolean opening = editedItemId == null; + + final boolean moving = !opening && !editedItemId.equals(id); + + final boolean allowMove = !isEditorBuffered() + && getEditorFieldGroup().isValid(); + + if (opening || !moving || allowMove) { + doBind(id); + } else { + failBind(null); + } + } catch (Exception e) { + failBind(e); + } + } + + private void doBind(Object id) { + editedItemId = id; + doEditItem(); + getEditorRpc().confirmBind(true); + } + + private void failBind(Exception e) { + if (e != null) { + handleError(e); + } + getEditorRpc().confirmBind(false); + } + + @Override + public void cancel(int rowIndex) { + try { + // For future proofing even though cannot currently fail + doCancelEditor(); + } catch (Exception e) { + handleError(e); + } + } + + @Override + public void save(int rowIndex) { + List errorColumnIds = null; + String errorMessage = null; + boolean success = false; + try { + saveEditor(); + success = true; + } catch (CommitException e) { + try { + CommitErrorEvent event = new CommitErrorEvent(LegacyGrid.this, + e); + getEditorErrorHandler().commitError(event); + + errorMessage = event.getUserErrorMessage(); + + errorColumnIds = new ArrayList<>(); + for (Column column : event.getErrorColumns()) { + errorColumnIds.add(column.state.id); + } + } catch (Exception ee) { + // A badly written error handler can throw an exception, + // which would lock up the Grid + handleError(ee); + } + } catch (Exception e) { + handleError(e); + } + getEditorRpc().confirmSave(success, errorMessage, + errorColumnIds); + } + + private void handleError(Exception e) { + com.vaadin.server.ErrorEvent.findErrorHandler(LegacyGrid.this) + .error(new ConnectorErrorEvent(LegacyGrid.this, e)); + } + }); + } + + @Override + public void beforeClientResponse(boolean initial) { + try { + header.sanityCheck(); + footer.sanityCheck(); + } catch (Exception e) { + e.printStackTrace(); + setComponentError(new ErrorMessage() { + + @Override + public ErrorLevel getErrorLevel() { + return ErrorLevel.CRITICAL; + } + + @Override + public String getFormattedHtmlMessage() { + return "Incorrectly merged cells"; + } + + }); + } + + super.beforeClientResponse(initial); + } + + /** + * Sets the grid data source. + *

+ * + * Note Grid columns are based on properties and try to + * detect a correct converter for the data type. The columns are not + * reinitialized automatically if the container is changed, and if the same + * properties are present after container change, the columns are reused. + * Properties with same names, but different data types will lead to + * unpredictable behaviour. + * + * @param container + * The container data source. Cannot be null. + * @throws IllegalArgumentException + * if the data source is null + */ + public void setContainerDataSource(Container.Indexed container) { + defaultContainer = false; + internalSetContainerDataSource(container); + } + + private void internalSetContainerDataSource(Container.Indexed container) { + if (container == null) { + throw new IllegalArgumentException( + "Cannot set the datasource to null"); + } + if (datasource == container) { + return; + } + + // Remove old listeners + if (datasource instanceof PropertySetChangeNotifier) { + ((PropertySetChangeNotifier) datasource) + .removePropertySetChangeListener(propertyListener); + } + + if (datasourceExtension != null) { + removeExtension(datasourceExtension); + } + + // Remove old DetailComponentManager + if (detailComponentManager != null) { + detailComponentManager.remove(); + } + + resetEditor(); + + datasource = container; + + // + // Adjust sort order + // + + if (container instanceof Container.Sortable) { + + // If the container is sortable, go through the current sort order + // and match each item to the sortable properties of the new + // container. If the new container does not support an item in the + // current sort order, that item is removed from the current sort + // order list. + Collection sortableProps = ((Container.Sortable) getContainerDataSource()) + .getSortableContainerPropertyIds(); + + Iterator i = sortOrder.iterator(); + while (i.hasNext()) { + if (!sortableProps.contains(i.next().getPropertyId())) { + i.remove(); + } + } + + sort(false); + } else { + // Clear sorting order. Don't sort. + sortOrder.clear(); + } + + datasourceExtension = new RpcDataProviderExtension(container); + datasourceExtension.extend(this); + datasourceExtension.addDataGenerator(new RowDataGenerator()); + for (Extension e : getExtensions()) { + if (e instanceof DataGenerator) { + datasourceExtension.addDataGenerator((DataGenerator) e); + } + } + + if (detailComponentManager != null) { + detailComponentManager = new DetailComponentManager(this, + detailComponentManager.getDetailsGenerator()); + } else { + detailComponentManager = new DetailComponentManager(this); + } + + /* + * selectionModel == null when the invocation comes from the + * constructor. + */ + if (selectionModel != null) { + selectionModel.reset(); + } + + // Listen to changes in properties and remove columns if needed + if (datasource instanceof PropertySetChangeNotifier) { + ((PropertySetChangeNotifier) datasource) + .addPropertySetChangeListener(propertyListener); + } + + /* + * activeRowHandler will be updated by the client-side request that + * occurs on container change - no need to actively re-insert any + * ValueChangeListeners at this point. + */ + + setFrozenColumnCount(0); + + if (columns.isEmpty()) { + // Add columns + for (Object propertyId : datasource.getContainerPropertyIds()) { + Column column = appendColumn(propertyId); + + // Initial sorting is defined by container + if (datasource instanceof Sortable) { + column.setSortable(((Sortable) datasource) + .getSortableContainerPropertyIds() + .contains(propertyId)); + } else { + column.setSortable(false); + } + } + } else { + Collection properties = datasource.getContainerPropertyIds(); + for (Object property : columns.keySet()) { + if (!properties.contains(property)) { + throw new IllegalStateException( + "Found at least one column in Grid that does not exist in the given container: " + + property + " with the header \"" + + getColumn(property).getHeaderCaption() + + "\". " + + "Call removeAllColumns() before setContainerDataSource() if you want to reconfigure the columns based on the new container."); + } + + if (!(datasource instanceof Sortable) + || !((Sortable) datasource) + .getSortableContainerPropertyIds() + .contains(property)) { + columns.get(property).setSortable(false); + } + } + } + } + + /** + * Returns the grid data source. + * + * @return the container data source of the grid + */ + public Container.Indexed getContainerDataSource() { + return datasource; + } + + /** + * Returns a column based on the property id + * + * @param propertyId + * the property id of the column + * @return the column or null if not found + */ + public Column getColumn(Object propertyId) { + return columns.get(propertyId); + } + + /** + * Returns a copy of currently configures columns in their current visual + * order in this Grid. + * + * @return unmodifiable copy of current columns in visual order + */ + public List getColumns() { + List columns = new ArrayList<>(); + for (String columnId : getState(false).columnOrder) { + columns.add(getColumnByColumnId(columnId)); + } + return Collections.unmodifiableList(columns); + } + + /** + * Adds a new Column to Grid. Also adds the property to container with data + * type String, if property for column does not exist in it. Default value + * for the new property is an empty String. + *

+ * Note that adding a new property is only done for the default container + * that Grid sets up with the default constructor. + * + * @param propertyId + * the property id of the new column + * @return the new column + * + * @throws IllegalStateException + * if column for given property already exists in this grid + */ + + public Column addColumn(Object propertyId) throws IllegalStateException { + if (datasource.getContainerPropertyIds().contains(propertyId) + && !columns.containsKey(propertyId)) { + appendColumn(propertyId); + } else if (defaultContainer) { + addColumnProperty(propertyId, String.class, ""); + } else { + if (columns.containsKey(propertyId)) { + throw new IllegalStateException( + "A column for property id '" + propertyId.toString() + + "' already exists in this grid"); + } else { + throw new IllegalStateException( + "Property id '" + propertyId.toString() + + "' does not exist in the container"); + } + } + + // Inform the data provider of this new column. + Column column = getColumn(propertyId); + List addedColumns = new ArrayList<>(); + addedColumns.add(column); + datasourceExtension.columnsAdded(addedColumns); + + return column; + } + + /** + * Adds a new Column to Grid. This function makes sure that the property + * with the given id and data type exists in the container. If property does + * not exists, it will be created. + *

+ * Default value for the new property is 0 if type is Integer, Double and + * Float. If type is String, default value is an empty string. For all other + * types the default value is null. + *

+ * Note that adding a new property is only done for the default container + * that Grid sets up with the default constructor. + * + * @param propertyId + * the property id of the new column + * @param type + * the data type for the new property + * @return the new column + * + * @throws IllegalStateException + * if column for given property already exists in this grid or + * property already exists in the container with wrong type + */ + public Column addColumn(Object propertyId, Class type) { + addColumnProperty(propertyId, type, null); + return getColumn(propertyId); + } + + protected void addColumnProperty(Object propertyId, Class type, + Object defaultValue) throws IllegalStateException { + if (!defaultContainer) { + throw new IllegalStateException( + "Container for this Grid is not a default container from Grid() constructor"); + } + + if (!columns.containsKey(propertyId)) { + if (!datasource.getContainerPropertyIds().contains(propertyId)) { + datasource.addContainerProperty(propertyId, type, defaultValue); + } else { + Property containerProperty = datasource.getContainerProperty( + datasource.firstItemId(), propertyId); + if (containerProperty.getType() == type) { + appendColumn(propertyId); + } else { + throw new IllegalStateException( + "DataSource already has the given property " + + propertyId + " with a different type"); + } + } + } else { + throw new IllegalStateException( + "Grid already has a column for property " + propertyId); + } + } + + /** + * Removes all columns from this Grid. + */ + public void removeAllColumns() { + List removed = new ArrayList<>(columns.values()); + Set properties = new HashSet<>(columns.keySet()); + for (Object propertyId : properties) { + removeColumn(propertyId); + } + datasourceExtension.columnsRemoved(removed); + } + + /** + * Used internally by the {@link LegacyGrid} to get a {@link Column} by + * referencing its generated state id. Also used by {@link Column} to verify + * if it has been detached from the {@link LegacyGrid}. + * + * @param columnId + * the client id generated for the column when the column is + * added to the grid + * @return the column with the id or null if not found + */ + Column getColumnByColumnId(String columnId) { + Object propertyId = getPropertyIdByColumnId(columnId); + return getColumn(propertyId); + } + + /** + * Used internally by the {@link LegacyGrid} to get a property id by referencing + * the columns generated state id. + * + * @param columnId + * The state id of the column + * @return The column instance or null if not found + */ + Object getPropertyIdByColumnId(String columnId) { + return columnKeys.get(columnId); + } + + /** + * Returns whether column reordering is allowed. Default value is + * false. + * + * @since 7.5.0 + * @return true if reordering is allowed + */ + public boolean isColumnReorderingAllowed() { + return getState(false).columnReorderingAllowed; + } + + /** + * Sets whether or not column reordering is allowed. Default value is + * false. + * + * @since 7.5.0 + * @param columnReorderingAllowed + * specifies whether column reordering is allowed + */ + public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { + if (isColumnReorderingAllowed() != columnReorderingAllowed) { + getState().columnReorderingAllowed = columnReorderingAllowed; + } + } + + @Override + protected GridState getState() { + return (GridState) super.getState(); + } + + @Override + protected GridState getState(boolean markAsDirty) { + return (GridState) super.getState(markAsDirty); + } + + /** + * Creates a new column based on a property id and appends it as the last + * column. + * + * @param datasourcePropertyId + * The property id of a property in the datasource + */ + private Column appendColumn(Object datasourcePropertyId) { + if (datasourcePropertyId == null) { + throw new IllegalArgumentException("Property id cannot be null"); + } + assert datasource.getContainerPropertyIds().contains( + datasourcePropertyId) : "Datasource should contain the property id"; + + GridColumnState columnState = new GridColumnState(); + columnState.id = columnKeys.key(datasourcePropertyId); + + Column column = new Column(this, columnState, datasourcePropertyId); + columns.put(datasourcePropertyId, column); + + getState().columns.add(columnState); + getState().columnOrder.add(columnState.id); + header.addColumn(datasourcePropertyId); + footer.addColumn(datasourcePropertyId); + + String humanFriendlyPropertyId = SharedUtil.propertyIdToHumanFriendly( + String.valueOf(datasourcePropertyId)); + column.setHeaderCaption(humanFriendlyPropertyId); + + if (datasource instanceof Sortable + && ((Sortable) datasource).getSortableContainerPropertyIds() + .contains(datasourcePropertyId)) { + column.setSortable(true); + } + + return column; + } + + /** + * Removes a column from Grid based on a property id. + * + * @param propertyId + * The property id of column to be removed + * + * @throws IllegalArgumentException + * if there is no column for given property id in this grid + */ + public void removeColumn(Object propertyId) + throws IllegalArgumentException { + if (!columns.keySet().contains(propertyId)) { + throw new IllegalArgumentException( + "There is no column for given property id " + propertyId); + } + + List removed = new ArrayList<>(); + removed.add(getColumn(propertyId)); + internalRemoveColumn(propertyId); + datasourceExtension.columnsRemoved(removed); + } + + private void internalRemoveColumn(Object propertyId) { + setEditorField(propertyId, null); + header.removeColumn(propertyId); + footer.removeColumn(propertyId); + Column column = columns.remove(propertyId); + getState().columnOrder.remove(columnKeys.key(propertyId)); + getState().columns.remove(column.getState()); + removeExtension(column.getRenderer()); + } + + /** + * Sets the columns and their order for the grid. Current columns whose + * property id is not in propertyIds are removed. Similarly, a column is + * added for any property id in propertyIds that has no corresponding column + * in this Grid. + * + * @since 7.5.0 + * + * @param propertyIds + * properties in the desired column order + */ + public void setColumns(Object... propertyIds) { + Set removePids = new HashSet<>(columns.keySet()); + removePids.removeAll(Arrays.asList(propertyIds)); + for (Object removePid : removePids) { + removeColumn(removePid); + } + Set addPids = new HashSet<>(Arrays.asList(propertyIds)); + addPids.removeAll(columns.keySet()); + for (Object propertyId : addPids) { + addColumn(propertyId); + } + setColumnOrder(propertyIds); + } + + /** + * Sets a new column order for the grid. All columns which are not ordered + * here will remain in the order they were before as the last columns of + * grid. + * + * @param propertyIds + * properties in the order columns should be + */ + public void setColumnOrder(Object... propertyIds) { + List columnOrder = new ArrayList<>(); + for (Object propertyId : propertyIds) { + if (columns.containsKey(propertyId)) { + columnOrder.add(columnKeys.key(propertyId)); + } else { + throw new IllegalArgumentException( + "Grid does not contain column for property " + + String.valueOf(propertyId)); + } + } + + List stateColumnOrder = getState().columnOrder; + if (stateColumnOrder.size() != columnOrder.size()) { + stateColumnOrder.removeAll(columnOrder); + columnOrder.addAll(stateColumnOrder); + } + getState().columnOrder = columnOrder; + fireColumnReorderEvent(false); + } + + /** + * Sets the number of frozen columns in this grid. Setting the count to 0 + * means that no data columns will be frozen, but the built-in selection + * checkbox column will still be frozen if it's in use. Setting the count to + * -1 will also disable the selection column. + *

+ * The default value is 0. + * + * @param numberOfColumns + * the number of columns that should be frozen + * + * @throws IllegalArgumentException + * if the column count is < 0 or > the number of visible columns + */ + public void setFrozenColumnCount(int numberOfColumns) { + if (numberOfColumns < -1 || numberOfColumns > columns.size()) { + throw new IllegalArgumentException( + "count must be between -1 and the current number of columns (" + + columns.size() + "): " + numberOfColumns); + } + + getState().frozenColumnCount = numberOfColumns; + } + + /** + * Gets the number of frozen columns in this grid. 0 means that no data + * columns will be frozen, but the built-in selection checkbox column will + * still be frozen if it's in use. -1 means that not even the selection + * column is frozen. + *

+ * NOTE: this count includes {@link Column#isHidden() hidden + * columns} in the count. + * + * @see #setFrozenColumnCount(int) + * + * @return the number of frozen columns + */ + public int getFrozenColumnCount() { + return getState(false).frozenColumnCount; + } + + /** + * Scrolls to a certain item, using {@link ScrollDestination#ANY}. + *

+ * If the item has visible details, its size will also be taken into + * account. + * + * @param itemId + * id of item to scroll to. + * @throws IllegalArgumentException + * if the provided id is not recognized by the data source. + */ + public void scrollTo(Object itemId) throws IllegalArgumentException { + scrollTo(itemId, ScrollDestination.ANY); + } + + /** + * Scrolls to a certain item, using user-specified scroll destination. + *

+ * If the item has visible details, its size will also be taken into + * account. + * + * @param itemId + * id of item to scroll to. + * @param destination + * value specifying desired position of scrolled-to row. + * @throws IllegalArgumentException + * if the provided id is not recognized by the data source. + */ + public void scrollTo(Object itemId, ScrollDestination destination) + throws IllegalArgumentException { + + int row = datasource.indexOfId(itemId); + + if (row == -1) { + throw new IllegalArgumentException( + "Item with specified ID does not exist in data source"); + } + + GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); + clientRPC.scrollToRow(row, destination); + } + + /** + * Scrolls to the beginning of the first data row. + */ + public void scrollToStart() { + GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); + clientRPC.scrollToStart(); + } + + /** + * Scrolls to the end of the last data row. + */ + public void scrollToEnd() { + GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); + clientRPC.scrollToEnd(); + } + + /** + * Sets the number of rows that should be visible in Grid's body, while + * {@link #getHeightMode()} is {@link HeightMode#ROW}. + *

+ * If Grid is currently not in {@link HeightMode#ROW}, the given value is + * remembered, and applied once the mode is applied. + * + * @param rows + * The height in terms of number of rows displayed in Grid's + * body. If Grid doesn't contain enough rows, white space is + * displayed instead. If null is given, then Grid's + * height is undefined + * @throws IllegalArgumentException + * if {@code rows} is zero or less + * @throws IllegalArgumentException + * if {@code rows} is {@link Double#isInfinite(double) infinite} + * @throws IllegalArgumentException + * if {@code rows} is {@link Double#isNaN(double) NaN} + */ + public void setHeightByRows(double rows) { + if (rows <= 0.0d) { + throw new IllegalArgumentException( + "More than zero rows must be shown."); + } else if (Double.isInfinite(rows)) { + throw new IllegalArgumentException( + "Grid doesn't support infinite heights"); + } else if (Double.isNaN(rows)) { + throw new IllegalArgumentException("NaN is not a valid row count"); + } + + getState().heightByRows = rows; + } + + /** + * Gets the amount of rows in Grid's body that are shown, while + * {@link #getHeightMode()} is {@link HeightMode#ROW}. + * + * @return the amount of rows that are being shown in Grid's body + * @see #setHeightByRows(double) + */ + public double getHeightByRows() { + return getState(false).heightByRows; + } + + /** + * {@inheritDoc} + *

+ * Note: This method will change the widget's size in the browser + * only if {@link #getHeightMode()} returns {@link HeightMode#CSS}. + * + * @see #setHeightMode(HeightMode) + */ + @Override + public void setHeight(float height, Unit unit) { + super.setHeight(height, unit); + } + + /** + * Defines the mode in which the Grid widget's height is calculated. + *

+ * If {@link HeightMode#CSS} is given, Grid will respect the values given + * via a {@code setHeight}-method, and behave as a traditional Component. + *

+ * If {@link HeightMode#ROW} is given, Grid will make sure that the body + * will display as many rows as {@link #getHeightByRows()} defines. + * Note: If headers/footers are inserted or removed, the widget + * will resize itself to still display the required amount of rows in its + * body. It also takes the horizontal scrollbar into account. + * + * @param heightMode + * the mode in to which Grid should be set + */ + public void setHeightMode(HeightMode heightMode) { + /* + * This method is a workaround for the fact that Vaadin re-applies + * widget dimensions (height/width) on each state change event. The + * original design was to have setHeight and setHeightByRow be equals, + * and whichever was called the latest was considered in effect. + * + * But, because of Vaadin always calling setHeight on the widget, this + * approach doesn't work. + */ + + getState().heightMode = heightMode; + } + + /** + * Returns the current {@link HeightMode} the Grid is in. + *

+ * Defaults to {@link HeightMode#CSS}. + * + * @return the current HeightMode + */ + public HeightMode getHeightMode() { + return getState(false).heightMode; + } + + /* Selection related methods: */ + + /** + * Takes a new {@link SelectionModel} into use. + *

+ * The SelectionModel that is previously in use will have all its items + * deselected. + *

+ * If the given SelectionModel is already in use, this method does nothing. + * + * @param selectionModel + * the new SelectionModel to use + * @throws IllegalArgumentException + * if {@code selectionModel} is null + */ + public void setSelectionModel(SelectionModel selectionModel) + throws IllegalArgumentException { + if (selectionModel == null) { + throw new IllegalArgumentException( + "Selection model may not be null"); + } + + if (this.selectionModel != selectionModel) { + // this.selectionModel is null on init + if (this.selectionModel != null) { + this.selectionModel.remove(); + } + + this.selectionModel = selectionModel; + selectionModel.setGrid(this); + } + } + + /** + * Returns the currently used {@link SelectionModel}. + * + * @return the currently used SelectionModel + */ + public SelectionModel getSelectionModel() { + return selectionModel; + } + + /** + * Sets the Grid's selection mode. + *

+ * Grid supports three selection modes: multiselect, single select and no + * selection, and this is a convenience method for choosing between one of + * them. + *

+ * Technically, this method is a shortcut that can be used instead of + * calling {@code setSelectionModel} with a specific SelectionModel + * instance. Grid comes with three built-in SelectionModel classes, and the + * {@link SelectionMode} enum represents each of them. + *

+ * Essentially, the two following method calls are equivalent: + *

+ *

+     * grid.setSelectionMode(SelectionMode.MULTI);
+     * grid.setSelectionModel(new MultiSelectionMode());
+     * 
+ * + * + * @param selectionMode + * the selection mode to switch to + * @return The {@link SelectionModel} instance that was taken into use + * @throws IllegalArgumentException + * if {@code selectionMode} is null + * @see SelectionModel + */ + public SelectionModel setSelectionMode(final SelectionMode selectionMode) + throws IllegalArgumentException { + if (selectionMode == null) { + throw new IllegalArgumentException( + "selection mode may not be null"); + } + final SelectionModel newSelectionModel = selectionMode.createModel(); + setSelectionModel(newSelectionModel); + return newSelectionModel; + } + + /** + * Checks whether an item is selected or not. + * + * @param itemId + * the item id to check for + * @return true iff the item is selected + */ + // keep this javadoc in sync with SelectionModel.isSelected + public boolean isSelected(Object itemId) { + return selectionModel.isSelected(itemId); + } + + /** + * Returns a collection of all the currently selected itemIds. + *

+ * This method is a shorthand that delegates to the + * {@link #getSelectionModel() selection model}. + * + * @return a collection of all the currently selected itemIds + */ + // keep this javadoc in sync with SelectionModel.getSelectedRows + public Collection getSelectedRows() { + return getSelectionModel().getSelectedRows(); + } + + /** + * Gets the item id of the currently selected item. + *

+ * This method is a shorthand that delegates to the + * {@link #getSelectionModel() selection model}. Only + * {@link SelectionModel.Single} is supported. + * + * @return the item id of the currently selected item, or null + * if nothing is selected + * @throws IllegalStateException + * if the selection model does not implement + * {@code SelectionModel.Single} + */ + // keep this javadoc in sync with SelectionModel.Single.getSelectedRow + public Object getSelectedRow() throws IllegalStateException { + if (selectionModel instanceof SelectionModel.Single) { + return ((SelectionModel.Single) selectionModel).getSelectedRow(); + } else if (selectionModel instanceof SelectionModel.Multi) { + throw new IllegalStateException("Cannot get unique selected row: " + + "Grid is in multiselect mode " + + "(the current selection model is " + + selectionModel.getClass().getName() + ")."); + } else if (selectionModel instanceof SelectionModel.None) { + throw new IllegalStateException( + "Cannot get selected row: " + "Grid selection is disabled " + + "(the current selection model is " + + selectionModel.getClass().getName() + ")."); + } else { + throw new IllegalStateException("Cannot get selected row: " + + "Grid selection model does not implement " + + SelectionModel.Single.class.getName() + " or " + + SelectionModel.Multi.class.getName() + + "(the current model is " + + selectionModel.getClass().getName() + ")."); + } + } + + /** + * Marks an item as selected. + *

+ * This method is a shorthand that delegates to the + * {@link #getSelectionModel() selection model}. Only + * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are + * supported. + * + * @param itemId + * the itemId to mark as selected + * @return true if the selection state changed, + * false if the itemId already was selected + * @throws IllegalArgumentException + * if the {@code itemId} doesn't exist in the currently active + * Container + * @throws IllegalStateException + * if the selection was illegal. One such reason might be that + * the implementation already had an item selected, and that + * needs to be explicitly deselected before re-selecting + * something. + * @throws IllegalStateException + * if the selection model does not implement + * {@code SelectionModel.Single} or {@code SelectionModel.Multi} + */ + // keep this javadoc in sync with SelectionModel.Single.select + public boolean select(Object itemId) + throws IllegalArgumentException, IllegalStateException { + if (selectionModel instanceof SelectionModel.Single) { + return ((SelectionModel.Single) selectionModel).select(itemId); + } else if (selectionModel instanceof SelectionModel.Multi) { + return ((SelectionModel.Multi) selectionModel).select(itemId); + } else if (selectionModel instanceof SelectionModel.None) { + throw new IllegalStateException("Cannot select row '" + itemId + + "': Grid selection is disabled " + + "(the current selection model is " + + selectionModel.getClass().getName() + ")."); + } else { + throw new IllegalStateException("Cannot select row '" + itemId + + "': Grid selection model does not implement " + + SelectionModel.Single.class.getName() + " or " + + SelectionModel.Multi.class.getName() + + "(the current model is " + + selectionModel.getClass().getName() + ")."); + } + } + + /** + * Marks an item as unselected. + *

+ * This method is a shorthand that delegates to the + * {@link #getSelectionModel() selection model}. Only + * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are + * supported. + * + * @param itemId + * the itemId to remove from being selected + * @return true if the selection state changed, + * false if the itemId was already selected + * @throws IllegalArgumentException + * if the {@code itemId} doesn't exist in the currently active + * Container + * @throws IllegalStateException + * if the deselection was illegal. One such reason might be that + * the implementation requires one or more items to be selected + * at all times. + * @throws IllegalStateException + * if the selection model does not implement + * {@code SelectionModel.Single} or {code SelectionModel.Multi} + */ + // keep this javadoc in sync with SelectionModel.Single.deselect + public boolean deselect(Object itemId) throws IllegalStateException { + if (selectionModel instanceof SelectionModel.Single) { + if (isSelected(itemId)) { + return ((SelectionModel.Single) selectionModel).select(null); + } + return false; + } else if (selectionModel instanceof SelectionModel.Multi) { + return ((SelectionModel.Multi) selectionModel).deselect(itemId); + } else if (selectionModel instanceof SelectionModel.None) { + throw new IllegalStateException("Cannot deselect row '" + itemId + + "': Grid selection is disabled " + + "(the current selection model is " + + selectionModel.getClass().getName() + ")."); + } else { + throw new IllegalStateException("Cannot deselect row '" + itemId + + "': Grid selection model does not implement " + + SelectionModel.Single.class.getName() + " or " + + SelectionModel.Multi.class.getName() + + "(the current model is " + + selectionModel.getClass().getName() + ")."); + } + } + + /** + * Marks all items as unselected. + *

+ * This method is a shorthand that delegates to the + * {@link #getSelectionModel() selection model}. Only + * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are + * supported. + * + * @return true if the selection state changed, + * false if the itemId was already selected + * @throws IllegalStateException + * if the deselection was illegal. One such reason might be that + * the implementation requires one or more items to be selected + * at all times. + * @throws IllegalStateException + * if the selection model does not implement + * {@code SelectionModel.Single} or {code SelectionModel.Multi} + */ + public boolean deselectAll() throws IllegalStateException { + if (selectionModel instanceof SelectionModel.Single) { + if (getSelectedRow() != null) { + return deselect(getSelectedRow()); + } + return false; + } else if (selectionModel instanceof SelectionModel.Multi) { + return ((SelectionModel.Multi) selectionModel).deselectAll(); + } else if (selectionModel instanceof SelectionModel.None) { + throw new IllegalStateException( + "Cannot deselect all rows" + ": Grid selection is disabled " + + "(the current selection model is " + + selectionModel.getClass().getName() + ")."); + } else { + throw new IllegalStateException("Cannot deselect all rows:" + + " Grid selection model does not implement " + + SelectionModel.Single.class.getName() + " or " + + SelectionModel.Multi.class.getName() + + "(the current model is " + + selectionModel.getClass().getName() + ")."); + } + } + + /** + * Fires a selection change event. + *

+ * Note: This is not a method that should be called by + * application logic. This method is publicly accessible only so that + * {@link SelectionModel SelectionModels} would be able to inform Grid of + * these events. + * + * @param newSelection + * the selection that was added by this event + * @param oldSelection + * the selection that was removed by this event + */ + public void fireSelectionEvent(Collection oldSelection, + Collection newSelection) { + fireEvent(new SelectionEvent(this, oldSelection, newSelection)); + } + + @Override + public void addSelectionListener(SelectionListener listener) { + addListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD); + } + + @Override + public void removeSelectionListener(SelectionListener listener) { + removeListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD); + } + + private void fireColumnReorderEvent(boolean userOriginated) { + fireEvent(new ColumnReorderEvent(this, userOriginated)); + } + + /** + * Registers a new column reorder listener. + * + * @since 7.5.0 + * @param listener + * the listener to register + */ + public void addColumnReorderListener(ColumnReorderListener listener) { + addListener(ColumnReorderEvent.class, listener, COLUMN_REORDER_METHOD); + } + + /** + * Removes a previously registered column reorder listener. + * + * @since 7.5.0 + * @param listener + * the listener to remove + */ + public void removeColumnReorderListener(ColumnReorderListener listener) { + removeListener(ColumnReorderEvent.class, listener, + COLUMN_REORDER_METHOD); + } + + private void fireColumnResizeEvent(Column column, boolean userOriginated) { + fireEvent(new ColumnResizeEvent(this, column, userOriginated)); + } + + /** + * Registers a new column resize listener. + * + * @param listener + * the listener to register + */ + public void addColumnResizeListener(ColumnResizeListener listener) { + addListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD); + } + + /** + * Removes a previously registered column resize listener. + * + * @param listener + * the listener to remove + */ + public void removeColumnResizeListener(ColumnResizeListener listener) { + removeListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD); + } + + /** + * Gets the {@link KeyMapper } being used by the data source. + * + * @return the key mapper being used by the data source + */ + KeyMapper getKeyMapper() { + return datasourceExtension.getKeyMapper(); + } + + /** + * Adds a renderer to this grid's connector hierarchy. + * + * @param renderer + * the renderer to add + */ + void addRenderer(Renderer renderer) { + addExtension(renderer); + } + + /** + * Sets the current sort order using the fluid Sort API. Read the + * documentation for {@link Sort} for more information. + *

+ * Note: Sorting by a property that has no column in Grid will hide + * all possible sorting indicators. + * + * @param s + * a sort instance + * + * @throws IllegalStateException + * if container is not sortable (does not implement + * Container.Sortable) + * @throws IllegalArgumentException + * if trying to sort by non-existing property + */ + public void sort(Sort s) { + setSortOrder(s.build()); + } + + /** + * Sort this Grid in ascending order by a specified property. + *

+ * Note: Sorting by a property that has no column in Grid will hide + * all possible sorting indicators. + * + * @param propertyId + * a property ID + * + * @throws IllegalStateException + * if container is not sortable (does not implement + * Container.Sortable) + * @throws IllegalArgumentException + * if trying to sort by non-existing property + */ + public void sort(Object propertyId) { + sort(propertyId, SortDirection.ASCENDING); + } + + /** + * Sort this Grid in user-specified {@link SortOrder} by a property. + *

+ * Note: Sorting by a property that has no column in Grid will hide + * all possible sorting indicators. + * + * @param propertyId + * a property ID + * @param direction + * a sort order value (ascending/descending) + * + * @throws IllegalStateException + * if container is not sortable (does not implement + * Container.Sortable) + * @throws IllegalArgumentException + * if trying to sort by non-existing property + */ + public void sort(Object propertyId, SortDirection direction) { + sort(Sort.by(propertyId, direction)); + } + + /** + * Clear the current sort order, and re-sort the grid. + */ + public void clearSortOrder() { + sortOrder.clear(); + sort(false); + } + + /** + * Sets the sort order to use. + *

+ * Note: Sorting by a property that has no column in Grid will hide + * all possible sorting indicators. + * + * @param order + * a sort order list. + * + * @throws IllegalStateException + * if container is not sortable (does not implement + * Container.Sortable) + * @throws IllegalArgumentException + * if order is null or trying to sort by non-existing property + */ + public void setSortOrder(List order) { + setSortOrder(order, false); + } + + private void setSortOrder(List order, boolean userOriginated) + throws IllegalStateException, IllegalArgumentException { + if (!(getContainerDataSource() instanceof Container.Sortable)) { + throw new IllegalStateException( + "Attached container is not sortable (does not implement Container.Sortable)"); + } + + if (order == null) { + throw new IllegalArgumentException("Order list may not be null!"); + } + + sortOrder.clear(); + + Collection sortableProps = ((Container.Sortable) getContainerDataSource()) + .getSortableContainerPropertyIds(); + + for (SortOrder o : order) { + if (!sortableProps.contains(o.getPropertyId())) { + throw new IllegalArgumentException("Property " + + o.getPropertyId() + + " does not exist or is not sortable in the current container"); + } + } + + sortOrder.addAll(order); + sort(userOriginated); + } + + /** + * Get the current sort order list. + * + * @return a sort order list + */ + public List getSortOrder() { + return Collections.unmodifiableList(sortOrder); + } + + /** + * Apply sorting to data source. + */ + private void sort(boolean userOriginated) { + + Container c = getContainerDataSource(); + if (c instanceof Container.Sortable) { + Container.Sortable cs = (Container.Sortable) c; + + final int items = sortOrder.size(); + Object[] propertyIds = new Object[items]; + boolean[] directions = new boolean[items]; + + SortDirection[] stateDirs = new SortDirection[items]; + + for (int i = 0; i < items; ++i) { + SortOrder order = sortOrder.get(i); + + stateDirs[i] = order.getDirection(); + propertyIds[i] = order.getPropertyId(); + switch (order.getDirection()) { + case ASCENDING: + directions[i] = true; + break; + case DESCENDING: + directions[i] = false; + break; + default: + throw new IllegalArgumentException("getDirection() of " + + order + " returned an unexpected value"); + } + } + + cs.sort(propertyIds, directions); + + if (columns.keySet().containsAll(Arrays.asList(propertyIds))) { + String[] columnKeys = new String[items]; + for (int i = 0; i < items; ++i) { + columnKeys[i] = this.columnKeys.key(propertyIds[i]); + } + getState().sortColumns = columnKeys; + getState(false).sortDirs = stateDirs; + } else { + // Not all sorted properties are in Grid. Remove any indicators. + getState().sortColumns = new String[] {}; + getState(false).sortDirs = new SortDirection[] {}; + } + fireEvent(new SortEvent(this, new ArrayList<>(sortOrder), + userOriginated)); + } else { + throw new IllegalStateException( + "Container is not sortable (does not implement Container.Sortable)"); + } + } + + /** + * Adds a sort order change listener that gets notified when the sort order + * changes. + * + * @param listener + * the sort order change listener to add + */ + @Override + public void addSortListener(SortListener listener) { + addListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD); + } + + /** + * Removes a sort order change listener previously added using + * {@link #addSortListener(SortListener)}. + * + * @param listener + * the sort order change listener to remove + */ + @Override + public void removeSortListener(SortListener listener) { + removeListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD); + } + + /* Grid Headers */ + + /** + * Returns the header section of this grid. The default header contains a + * single row displaying the column captions. + * + * @return the header + */ + protected Header getHeader() { + return header; + } + + /** + * Gets the header row at given index. + * + * @param rowIndex + * 0 based index for row. Counted from top to bottom + * @return header row at given index + * @throws IllegalArgumentException + * if no row exists at given index + */ + public HeaderRow getHeaderRow(int rowIndex) { + return header.getRow(rowIndex); + } + + /** + * Inserts a new row at the given position to the header section. Shifts the + * row currently at that position and any subsequent rows down (adds one to + * their indices). + * + * @param index + * the position at which to insert the row + * @return the new row + * + * @throws IllegalArgumentException + * if the index is less than 0 or greater than row count + * @see #appendHeaderRow() + * @see #prependHeaderRow() + * @see #removeHeaderRow(HeaderRow) + * @see #removeHeaderRow(int) + */ + public HeaderRow addHeaderRowAt(int index) { + return header.addRowAt(index); + } + + /** + * Adds a new row at the bottom of the header section. + * + * @return the new row + * @see #prependHeaderRow() + * @see #addHeaderRowAt(int) + * @see #removeHeaderRow(HeaderRow) + * @see #removeHeaderRow(int) + */ + public HeaderRow appendHeaderRow() { + return header.appendRow(); + } + + /** + * Returns the current default row of the header section. The default row is + * a special header row providing a user interface for sorting columns. + * Setting a header text for column updates cells in the default header. + * + * @return the default row or null if no default row set + */ + public HeaderRow getDefaultHeaderRow() { + return header.getDefaultRow(); + } + + /** + * Gets the row count for the header section. + * + * @return row count + */ + public int getHeaderRowCount() { + return header.getRowCount(); + } + + /** + * Adds a new row at the top of the header section. + * + * @return the new row + * @see #appendHeaderRow() + * @see #addHeaderRowAt(int) + * @see #removeHeaderRow(HeaderRow) + * @see #removeHeaderRow(int) + */ + public HeaderRow prependHeaderRow() { + return header.prependRow(); + } + + /** + * Removes the given row from the header section. + * + * @param row + * the row to be removed + * + * @throws IllegalArgumentException + * if the row does not exist in this section + * @see #removeHeaderRow(int) + * @see #addHeaderRowAt(int) + * @see #appendHeaderRow() + * @see #prependHeaderRow() + */ + public void removeHeaderRow(HeaderRow row) { + header.removeRow(row); + } + + /** + * Removes the row at the given position from the header section. + * + * @param rowIndex + * the position of the row + * + * @throws IllegalArgumentException + * if no row exists at given index + * @see #removeHeaderRow(HeaderRow) + * @see #addHeaderRowAt(int) + * @see #appendHeaderRow() + * @see #prependHeaderRow() + */ + public void removeHeaderRow(int rowIndex) { + header.removeRow(rowIndex); + } + + /** + * Sets the default row of the header. The default row is a special header + * row providing a user interface for sorting columns. + * + * @param row + * the new default row, or null for no default row + * + * @throws IllegalArgumentException + * header does not contain the row + */ + public void setDefaultHeaderRow(HeaderRow row) { + header.setDefaultRow(row); + } + + /** + * Sets the visibility of the header section. + * + * @param visible + * true to show header section, false to hide + */ + public void setHeaderVisible(boolean visible) { + header.setVisible(visible); + } + + /** + * Returns the visibility of the header section. + * + * @return true if visible, false otherwise. + */ + public boolean isHeaderVisible() { + return header.isVisible(); + } + + /* Grid Footers */ + + /** + * Returns the footer section of this grid. The default header contains a + * single row displaying the column captions. + * + * @return the footer + */ + protected Footer getFooter() { + return footer; + } + + /** + * Gets the footer row at given index. + * + * @param rowIndex + * 0 based index for row. Counted from top to bottom + * @return footer row at given index + * @throws IllegalArgumentException + * if no row exists at given index + */ + public FooterRow getFooterRow(int rowIndex) { + return footer.getRow(rowIndex); + } + + /** + * Inserts a new row at the given position to the footer section. Shifts the + * row currently at that position and any subsequent rows down (adds one to + * their indices). + * + * @param index + * the position at which to insert the row + * @return the new row + * + * @throws IllegalArgumentException + * if the index is less than 0 or greater than row count + * @see #appendFooterRow() + * @see #prependFooterRow() + * @see #removeFooterRow(FooterRow) + * @see #removeFooterRow(int) + */ + public FooterRow addFooterRowAt(int index) { + return footer.addRowAt(index); + } + + /** + * Adds a new row at the bottom of the footer section. + * + * @return the new row + * @see #prependFooterRow() + * @see #addFooterRowAt(int) + * @see #removeFooterRow(FooterRow) + * @see #removeFooterRow(int) + */ + public FooterRow appendFooterRow() { + return footer.appendRow(); + } + + /** + * Gets the row count for the footer. + * + * @return row count + */ + public int getFooterRowCount() { + return footer.getRowCount(); + } + + /** + * Adds a new row at the top of the footer section. + * + * @return the new row + * @see #appendFooterRow() + * @see #addFooterRowAt(int) + * @see #removeFooterRow(FooterRow) + * @see #removeFooterRow(int) + */ + public FooterRow prependFooterRow() { + return footer.prependRow(); + } + + /** + * Removes the given row from the footer section. + * + * @param row + * the row to be removed + * + * @throws IllegalArgumentException + * if the row does not exist in this section + * @see #removeFooterRow(int) + * @see #addFooterRowAt(int) + * @see #appendFooterRow() + * @see #prependFooterRow() + */ + public void removeFooterRow(FooterRow row) { + footer.removeRow(row); + } + + /** + * Removes the row at the given position from the footer section. + * + * @param rowIndex + * the position of the row + * + * @throws IllegalArgumentException + * if no row exists at given index + * @see #removeFooterRow(FooterRow) + * @see #addFooterRowAt(int) + * @see #appendFooterRow() + * @see #prependFooterRow() + */ + public void removeFooterRow(int rowIndex) { + footer.removeRow(rowIndex); + } + + /** + * Sets the visibility of the footer section. + * + * @param visible + * true to show footer section, false to hide + */ + public void setFooterVisible(boolean visible) { + footer.setVisible(visible); + } + + /** + * Returns the visibility of the footer section. + * + * @return true if visible, false otherwise. + */ + public boolean isFooterVisible() { + return footer.isVisible(); + } + + private void addComponent(Component c) { + extensionComponents.add(c); + c.setParent(this); + markAsDirty(); + } + + private void removeComponent(Component c) { + extensionComponents.remove(c); + c.setParent(null); + markAsDirty(); + } + + @Override + public Iterator iterator() { + // This is a hash set to avoid adding header/footer components inside + // merged cells multiple times + LinkedHashSet componentList = new LinkedHashSet<>(); + + Header header = getHeader(); + for (int i = 0; i < header.getRowCount(); ++i) { + HeaderRow row = header.getRow(i); + for (Object propId : columns.keySet()) { + HeaderCell cell = row.getCell(propId); + if (cell.getCellState().type == GridStaticCellType.WIDGET) { + componentList.add(cell.getComponent()); + } + } + } + + Footer footer = getFooter(); + for (int i = 0; i < footer.getRowCount(); ++i) { + FooterRow row = footer.getRow(i); + for (Object propId : columns.keySet()) { + FooterCell cell = row.getCell(propId); + if (cell.getCellState().type == GridStaticCellType.WIDGET) { + componentList.add(cell.getComponent()); + } + } + } + + componentList.addAll(getEditorFields()); + + componentList.addAll(extensionComponents); + + return componentList.iterator(); + } + + @Override + public boolean isRendered(Component childComponent) { + if (getEditorFields().contains(childComponent)) { + // Only render editor fields if the editor is open + return isEditorActive(); + } else { + // TODO Header and footer components should also only be rendered if + // the header/footer is visible + return true; + } + } + + EditorClientRpc getEditorRpc() { + return getRpcProxy(EditorClientRpc.class); + } + + /** + * Sets the {@code CellDescriptionGenerator} instance for generating + * optional descriptions (tooltips) for individual Grid cells. If a + * {@link RowDescriptionGenerator} is also set, the row description it + * generates is displayed for cells for which {@code generator} returns + * null. + * + * @param generator + * the description generator to use or {@code null} to remove a + * previously set generator if any + * + * @see #setRowDescriptionGenerator(RowDescriptionGenerator) + * + * @since 7.6 + */ + public void setCellDescriptionGenerator( + CellDescriptionGenerator generator) { + cellDescriptionGenerator = generator; + getState().hasDescriptions = (generator != null + || rowDescriptionGenerator != null); + datasourceExtension.refreshCache(); + } + + /** + * Returns the {@code CellDescriptionGenerator} instance used to generate + * descriptions (tooltips) for Grid cells. + * + * @return the description generator or {@code null} if no generator is set + * + * @since 7.6 + */ + public CellDescriptionGenerator getCellDescriptionGenerator() { + return cellDescriptionGenerator; + } + + /** + * Sets the {@code RowDescriptionGenerator} instance for generating optional + * descriptions (tooltips) for Grid rows. If a + * {@link CellDescriptionGenerator} is also set, the row description + * generated by {@code generator} is used for cells for which the cell + * description generator returns null. + * + * + * @param generator + * the description generator to use or {@code null} to remove a + * previously set generator if any + * + * @see #setCellDescriptionGenerator(CellDescriptionGenerator) + * + * @since 7.6 + */ + public void setRowDescriptionGenerator(RowDescriptionGenerator generator) { + rowDescriptionGenerator = generator; + getState().hasDescriptions = (generator != null + || cellDescriptionGenerator != null); + datasourceExtension.refreshCache(); + } + + /** + * Returns the {@code RowDescriptionGenerator} instance used to generate + * descriptions (tooltips) for Grid rows + * + * @return the description generator or {@code} null if no generator is set + * + * @since 7.6 + */ + public RowDescriptionGenerator getRowDescriptionGenerator() { + return rowDescriptionGenerator; + } + + /** + * Sets the style generator that is used for generating styles for cells + * + * @param cellStyleGenerator + * the cell style generator to set, or null to + * remove a previously set generator + */ + public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) { + this.cellStyleGenerator = cellStyleGenerator; + datasourceExtension.refreshCache(); + } + + /** + * Gets the style generator that is used for generating styles for cells + * + * @return the cell style generator, or null if no generator is + * set + */ + public CellStyleGenerator getCellStyleGenerator() { + return cellStyleGenerator; + } + + /** + * Sets the style generator that is used for generating styles for rows + * + * @param rowStyleGenerator + * the row style generator to set, or null to remove + * a previously set generator + */ + public void setRowStyleGenerator(RowStyleGenerator rowStyleGenerator) { + this.rowStyleGenerator = rowStyleGenerator; + datasourceExtension.refreshCache(); + } + + /** + * Gets the style generator that is used for generating styles for rows + * + * @return the row style generator, or null if no generator is + * set + */ + public RowStyleGenerator getRowStyleGenerator() { + return rowStyleGenerator; + } + + /** + * Adds a row to the underlying container. The order of the parameters + * should match the current visible column order. + *

+ * Please note that it's generally only safe to use this method during + * initialization. After Grid has been initialized and the visible column + * order might have been changed, it's better to instead add items directly + * to the underlying container and use {@link Item#getItemProperty(Object)} + * to make sure each value is assigned to the intended property. + * + * @param values + * the cell values of the new row, in the same order as the + * visible column order, not null. + * @return the item id of the new row + * @throws IllegalArgumentException + * if values is null + * @throws IllegalArgumentException + * if its length does not match the number of visible columns + * @throws IllegalArgumentException + * if a parameter value is not an instance of the corresponding + * property type + * @throws UnsupportedOperationException + * if the container does not support adding new items + */ + public Object addRow(Object... values) { + if (values == null) { + throw new IllegalArgumentException("Values cannot be null"); + } + + Indexed dataSource = getContainerDataSource(); + List columnOrder = getState(false).columnOrder; + + if (values.length != columnOrder.size()) { + throw new IllegalArgumentException( + "There are " + columnOrder.size() + " visible columns, but " + + values.length + " cell values were provided."); + } + + // First verify all parameter types + for (int i = 0; i < columnOrder.size(); i++) { + Object propertyId = getPropertyIdByColumnId(columnOrder.get(i)); + + Class propertyType = dataSource.getType(propertyId); + if (values[i] != null && !propertyType.isInstance(values[i])) { + throw new IllegalArgumentException("Parameter " + i + "(" + + values[i] + ") is not an instance of " + + propertyType.getCanonicalName()); + } + } + + Object itemId = dataSource.addItem(); + try { + Item item = dataSource.getItem(itemId); + for (int i = 0; i < columnOrder.size(); i++) { + Object propertyId = getPropertyIdByColumnId(columnOrder.get(i)); + Property property = item.getItemProperty(propertyId); + property.setValue(values[i]); + } + } catch (RuntimeException e) { + try { + dataSource.removeItem(itemId); + } catch (Exception e2) { + getLogger().log(Level.SEVERE, + "Error recovering from exception in addRow", e); + } + throw e; + } + + return itemId; + } + + private static Logger getLogger() { + return Logger.getLogger(LegacyGrid.class.getName()); + } + + /** + * Sets whether or not the item editor UI is enabled for this grid. When the + * editor is enabled, the user can open it by double-clicking a row or + * hitting enter when a row is focused. The editor can also be opened + * programmatically using the {@link #editItem(Object)} method. + * + * @param isEnabled + * true to enable the feature, false + * otherwise + * @throws IllegalStateException + * if an item is currently being edited + * + * @see #getEditedItemId() + */ + public void setEditorEnabled(boolean isEnabled) + throws IllegalStateException { + if (isEditorActive()) { + throw new IllegalStateException( + "Cannot disable the editor while an item (" + + getEditedItemId() + ") is being edited"); + } + if (isEditorEnabled() != isEnabled) { + getState().editorEnabled = isEnabled; + } + } + + /** + * Checks whether the item editor UI is enabled for this grid. + * + * @return true iff the editor is enabled for this grid + * + * @see #setEditorEnabled(boolean) + * @see #getEditedItemId() + */ + public boolean isEditorEnabled() { + return getState(false).editorEnabled; + } + + /** + * Gets the id of the item that is currently being edited. + * + * @return the id of the item that is currently being edited, or + * null if no item is being edited at the moment + */ + public Object getEditedItemId() { + return editedItemId; + } + + /** + * Gets the field group that is backing the item editor of this grid. + * + * @return the backing field group + */ + public FieldGroup getEditorFieldGroup() { + return editorFieldGroup; + } + + /** + * Sets the field group that is backing the item editor of this grid. + * + * @param fieldGroup + * the backing field group + * + * @throws IllegalStateException + * if the editor is currently active + */ + public void setEditorFieldGroup(FieldGroup fieldGroup) { + if (isEditorActive()) { + throw new IllegalStateException( + "Cannot change field group while an item (" + + getEditedItemId() + ") is being edited"); + } + editorFieldGroup = fieldGroup; + } + + /** + * Returns whether an item is currently being edited in the editor. + * + * @return true iff the editor is open + */ + public boolean isEditorActive() { + return editorActive; + } + + private void checkColumnExists(Object propertyId) { + if (getColumn(propertyId) == null) { + throw new IllegalArgumentException( + "There is no column with the property id " + propertyId); + } + } + + private LegacyField getEditorField(Object propertyId) { + checkColumnExists(propertyId); + + if (!getColumn(propertyId).isEditable()) { + return null; + } + + LegacyField editor = editorFieldGroup.getField(propertyId); + + try { + if (editor == null) { + editor = editorFieldGroup.buildAndBind(propertyId); + } + } finally { + if (editor == null) { + editor = editorFieldGroup.getField(propertyId); + } + + if (editor != null && editor.getParent() != LegacyGrid.this) { + assert editor.getParent() == null; + editor.setParent(this); + } + } + return editor; + } + + /** + * Opens the editor interface for the provided item. Scrolls the Grid to + * bring the item to view if it is not already visible. + * + * Note that any cell content rendered by a WidgetRenderer will not be + * visible in the editor row. + * + * @param itemId + * the id of the item to edit + * @throws IllegalStateException + * if the editor is not enabled or already editing an item in + * buffered mode + * @throws IllegalArgumentException + * if the {@code itemId} is not in the backing container + * @see #setEditorEnabled(boolean) + */ + public void editItem(Object itemId) + throws IllegalStateException, IllegalArgumentException { + if (!isEditorEnabled()) { + throw new IllegalStateException("Item editor is not enabled"); + } else if (isEditorBuffered() && editedItemId != null) { + throw new IllegalStateException("Editing item " + itemId + + " failed. Item editor is already editing item " + + editedItemId); + } else if (!getContainerDataSource().containsId(itemId)) { + throw new IllegalArgumentException("Item with id " + itemId + + " not found in current container"); + } + editedItemId = itemId; + getEditorRpc().bind(getContainerDataSource().indexOfId(itemId)); + } + + protected void doEditItem() { + Item item = getContainerDataSource().getItem(editedItemId); + + editorFieldGroup.setItemDataSource(item); + + for (Column column : getColumns()) { + column.getState().editorConnector = getEditorField( + column.getPropertyId()); + } + + editorActive = true; + // Must ensure that all fields, recursively, are sent to the client + // This is needed because the fields are hidden using isRendered + for (LegacyField f : getEditorFields()) { + f.markAsDirtyRecursive(); + } + + if (datasource instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) datasource) + .addItemSetChangeListener(editorClosingItemSetListener); + } + } + + private void setEditorField(Object propertyId, LegacyField field) { + checkColumnExists(propertyId); + + LegacyField oldField = editorFieldGroup.getField(propertyId); + if (oldField != null) { + editorFieldGroup.unbind(oldField); + oldField.setParent(null); + } + + if (field != null) { + field.setParent(this); + editorFieldGroup.bind(field, propertyId); + } + } + + /** + * Saves all changes done to the bound fields. + *

+ * Note: This is a pass-through call to the backing field group. + * + * @throws CommitException + * If the commit was aborted + * + * @see FieldGroup#commit() + */ + public void saveEditor() throws CommitException { + editorFieldGroup.commit(); + } + + /** + * Cancels the currently active edit if any. Hides the editor and discards + * possible unsaved changes in the editor fields. + */ + public void cancelEditor() { + if (isEditorActive()) { + getEditorRpc() + .cancel(getContainerDataSource().indexOfId(editedItemId)); + doCancelEditor(); + } + } + + protected void doCancelEditor() { + editedItemId = null; + editorActive = false; + editorFieldGroup.discard(); + editorFieldGroup.setItemDataSource(null); + + if (datasource instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) datasource) + .removeItemSetChangeListener(editorClosingItemSetListener); + } + + // Mark Grid as dirty so the client side gets to know that the editors + // are no longer attached + markAsDirty(); + } + + void resetEditor() { + if (isEditorActive()) { + /* + * Simply force cancel the editing; throwing here would just make + * Grid.setContainerDataSource semantics more complicated. + */ + cancelEditor(); + } + for (LegacyField editor : getEditorFields()) { + editor.setParent(null); + } + + editedItemId = null; + editorActive = false; + editorFieldGroup = new CustomFieldGroup(); + } + + /** + * Gets a collection of all fields bound to the item editor of this grid. + *

+ * When {@link #editItem(Object) editItem} is called, fields are + * automatically created and bound to any unbound properties. + * + * @return a collection of all the fields bound to the item editor + */ + Collection> getEditorFields() { + Collection> fields = editorFieldGroup.getFields(); + assert allAttached(fields); + return fields; + } + + private boolean allAttached(Collection components) { + for (Component component : components) { + if (component.getParent() != this) { + return false; + } + } + return true; + } + + /** + * Sets the field factory for the {@link FieldGroup}. The field factory is + * only used when {@link FieldGroup} creates a new field. + *

+ * Note: This is a pass-through call to the backing field group. + * + * @param fieldFactory + * The field factory to use + */ + public void setEditorFieldFactory(FieldGroupFieldFactory fieldFactory) { + editorFieldGroup.setFieldFactory(fieldFactory); + } + + /** + * Sets the error handler for the editor. + * + * The error handler is called whenever there is an exception in the editor. + * + * @param editorErrorHandler + * The editor error handler to use + * @throws IllegalArgumentException + * if the error handler is null + */ + public void setEditorErrorHandler(EditorErrorHandler editorErrorHandler) + throws IllegalArgumentException { + if (editorErrorHandler == null) { + throw new IllegalArgumentException( + "The error handler cannot be null"); + } + this.editorErrorHandler = editorErrorHandler; + } + + /** + * Gets the error handler used for the editor + * + * @see #setErrorHandler(com.vaadin.server.ErrorHandler) + * @return the editor error handler, never null + */ + public EditorErrorHandler getEditorErrorHandler() { + return editorErrorHandler; + } + + /** + * Gets the field factory for the {@link FieldGroup}. The field factory is + * only used when {@link FieldGroup} creates a new field. + *

+ * Note: This is a pass-through call to the backing field group. + * + * @return The field factory in use + */ + public FieldGroupFieldFactory getEditorFieldFactory() { + return editorFieldGroup.getFieldFactory(); + } + + /** + * Sets the caption on the save button in the Grid editor. + * + * @param saveCaption + * the caption to set + * @throws IllegalArgumentException + * if {@code saveCaption} is {@code null} + */ + public void setEditorSaveCaption(String saveCaption) + throws IllegalArgumentException { + if (saveCaption == null) { + throw new IllegalArgumentException("Save caption cannot be null"); + } + getState().editorSaveCaption = saveCaption; + } + + /** + * Gets the current caption of the save button in the Grid editor. + * + * @return the current caption of the save button + */ + public String getEditorSaveCaption() { + return getState(false).editorSaveCaption; + } + + /** + * Sets the caption on the cancel button in the Grid editor. + * + * @param cancelCaption + * the caption to set + * @throws IllegalArgumentException + * if {@code cancelCaption} is {@code null} + */ + public void setEditorCancelCaption(String cancelCaption) + throws IllegalArgumentException { + if (cancelCaption == null) { + throw new IllegalArgumentException("Cancel caption cannot be null"); + } + getState().editorCancelCaption = cancelCaption; + } + + /** + * Gets the current caption of the cancel button in the Grid editor. + * + * @return the current caption of the cancel button + */ + public String getEditorCancelCaption() { + return getState(false).editorCancelCaption; + } + + /** + * Sets the buffered editor mode. The default mode is buffered ( + * true). + * + * @since 7.6 + * @param editorBuffered + * true to enable buffered editor, + * false to disable it + * @throws IllegalStateException + * If editor is active while attempting to change the buffered + * mode. + */ + public void setEditorBuffered(boolean editorBuffered) + throws IllegalStateException { + if (isEditorActive()) { + throw new IllegalStateException( + "Can't change editor unbuffered mode while editor is active."); + } + getState().editorBuffered = editorBuffered; + editorFieldGroup.setBuffered(editorBuffered); + } + + /** + * Gets the buffered editor mode. + * + * @since 7.6 + * @return true if buffered editor is enabled, + * false otherwise + */ + public boolean isEditorBuffered() { + return getState(false).editorBuffered; + } + + @Override + public void addItemClickListener(ItemClickListener listener) { + addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, + listener, ItemClickEvent.ITEM_CLICK_METHOD); + } + + @Override + @Deprecated + public void addListener(ItemClickListener listener) { + addItemClickListener(listener); + } + + @Override + public void removeItemClickListener(ItemClickListener listener) { + removeListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, + listener); + } + + @Override + @Deprecated + public void removeListener(ItemClickListener listener) { + removeItemClickListener(listener); + } + + /** + * Requests that the column widths should be recalculated. + *

+ * In most cases Grid will know when column widths need to be recalculated + * but this method can be used to force recalculation in situations when + * grid does not recalculate automatically. + * + * @since 7.4.1 + */ + public void recalculateColumnWidths() { + getRpcProxy(GridClientRpc.class).recalculateColumnWidths(); + } + + /** + * Registers a new column visibility change listener + * + * @since 7.5.0 + * @param listener + * the listener to register + */ + public void addColumnVisibilityChangeListener( + ColumnVisibilityChangeListener listener) { + addListener(ColumnVisibilityChangeEvent.class, listener, + COLUMN_VISIBILITY_METHOD); + } + + /** + * Removes a previously registered column visibility change listener + * + * @since 7.5.0 + * @param listener + * the listener to remove + */ + public void removeColumnVisibilityChangeListener( + ColumnVisibilityChangeListener listener) { + removeListener(ColumnVisibilityChangeEvent.class, listener, + COLUMN_VISIBILITY_METHOD); + } + + private void fireColumnVisibilityChangeEvent(Column column, boolean hidden, + boolean isUserOriginated) { + fireEvent(new ColumnVisibilityChangeEvent(this, column, hidden, + isUserOriginated)); + } + + /** + * Sets a new details generator for row details. + *

+ * The currently opened row details will be re-rendered. + * + * @since 7.5.0 + * @param detailsGenerator + * the details generator to set + * @throws IllegalArgumentException + * if detailsGenerator is null; + */ + public void setDetailsGenerator(DetailsGenerator detailsGenerator) + throws IllegalArgumentException { + detailComponentManager.setDetailsGenerator(detailsGenerator); + } + + /** + * Gets the current details generator for row details. + * + * @since 7.5.0 + * @return the detailsGenerator the current details generator + */ + public DetailsGenerator getDetailsGenerator() { + return detailComponentManager.getDetailsGenerator(); + } + + /** + * Shows or hides the details for a specific item. + * + * @since 7.5.0 + * @param itemId + * the id of the item for which to set details visibility + * @param visible + * true to show the details, or false + * to hide them + */ + public void setDetailsVisible(Object itemId, boolean visible) { + detailComponentManager.setDetailsVisible(itemId, visible); + } + + /** + * Checks whether details are visible for the given item. + * + * @since 7.5.0 + * @param itemId + * the id of the item for which to check details visibility + * @return true iff the details are visible + */ + public boolean isDetailsVisible(Object itemId) { + return detailComponentManager.isDetailsVisible(itemId); + } + + private static SelectionMode getDefaultSelectionMode() { + return SelectionMode.SINGLE; + } + + @Override + public void readDesign(Element design, DesignContext context) { + super.readDesign(design, context); + + Attributes attrs = design.attributes(); + if (attrs.hasKey("editable")) { + setEditorEnabled(DesignAttributeHandler.readAttribute("editable", + attrs, boolean.class)); + } + if (attrs.hasKey("rows")) { + setHeightByRows(DesignAttributeHandler.readAttribute("rows", attrs, + double.class)); + setHeightMode(HeightMode.ROW); + } + if (attrs.hasKey("selection-mode")) { + setSelectionMode(DesignAttributeHandler.readAttribute( + "selection-mode", attrs, SelectionMode.class)); + } + + if (design.children().size() > 0) { + if (design.children().size() > 1 + || !design.child(0).tagName().equals("table")) { + throw new DesignException( + "Grid needs to have a table element as its only child"); + } + Element table = design.child(0); + + Elements colgroups = table.getElementsByTag("colgroup"); + if (colgroups.size() != 1) { + throw new DesignException( + "Table element in declarative Grid needs to have a" + + " colgroup defining the columns used in Grid"); + } + + int i = 0; + for (Element col : colgroups.get(0).getElementsByTag("col")) { + String propertyId = DesignAttributeHandler.readAttribute( + "property-id", col.attributes(), "property-" + i, + String.class); + addColumn(propertyId, String.class).readDesign(col, context); + ++i; + } + + for (Element child : table.children()) { + if (child.tagName().equals("thead")) { + header.readDesign(child, context); + } else if (child.tagName().equals("tbody")) { + for (Element row : child.children()) { + Elements cells = row.children(); + Object[] data = new String[cells.size()]; + for (int c = 0; c < cells.size(); ++c) { + data[c] = cells.get(c).html(); + } + addRow(data); + } + + // Since inline data is used, set HTML renderer for columns + for (Column c : getColumns()) { + c.setRenderer(new HtmlRenderer()); + } + } else if (child.tagName().equals("tfoot")) { + footer.readDesign(child, context); + } + } + } + + // Read frozen columns after columns are read. + if (attrs.hasKey("frozen-columns")) { + setFrozenColumnCount(DesignAttributeHandler + .readAttribute("frozen-columns", attrs, int.class)); + } + } + + @Override + public void writeDesign(Element design, DesignContext context) { + super.writeDesign(design, context); + + Attributes attrs = design.attributes(); + LegacyGrid def = context.getDefaultInstance(this); + + DesignAttributeHandler.writeAttribute("editable", attrs, + isEditorEnabled(), def.isEditorEnabled(), boolean.class); + + DesignAttributeHandler.writeAttribute("frozen-columns", attrs, + getFrozenColumnCount(), def.getFrozenColumnCount(), int.class); + + if (getHeightMode() == HeightMode.ROW) { + DesignAttributeHandler.writeAttribute("rows", attrs, + getHeightByRows(), def.getHeightByRows(), double.class); + } + + SelectionMode selectionMode = null; + + if (selectionModel.getClass().equals(SingleSelectionModel.class)) { + selectionMode = SelectionMode.SINGLE; + } else if (selectionModel.getClass() + .equals(MultiSelectionModel.class)) { + selectionMode = SelectionMode.MULTI; + } else if (selectionModel.getClass().equals(NoSelectionModel.class)) { + selectionMode = SelectionMode.NONE; + } + + assert selectionMode != null : "Unexpected selection model " + + selectionModel.getClass().getName(); + + DesignAttributeHandler.writeAttribute("selection-mode", attrs, + selectionMode, getDefaultSelectionMode(), SelectionMode.class); + + if (columns.isEmpty()) { + // Empty grid. Structure not needed. + return; + } + + // Do structure. + Element tableElement = design.appendElement("table"); + Element colGroup = tableElement.appendElement("colgroup"); + + List columnOrder = getColumns(); + for (int i = 0; i < columnOrder.size(); ++i) { + Column column = columnOrder.get(i); + Element colElement = colGroup.appendElement("col"); + column.writeDesign(colElement, context); + } + + // Always write thead. Reads correctly when there no header rows + header.writeDesign(tableElement.appendElement("thead"), context); + + if (context.shouldWriteData(this)) { + Element bodyElement = tableElement.appendElement("tbody"); + for (Object itemId : datasource.getItemIds()) { + Element tableRow = bodyElement.appendElement("tr"); + for (Column c : getColumns()) { + Object value = datasource.getItem(itemId) + .getItemProperty(c.getPropertyId()).getValue(); + tableRow.appendElement("td") + .append((value != null ? DesignFormatter + .encodeForTextNode(value.toString()) : "")); + } + } + } + + if (footer.getRowCount() > 0) { + footer.writeDesign(tableElement.appendElement("tfoot"), context); + } + } + + @Override + protected Collection getCustomAttributes() { + Collection result = super.getCustomAttributes(); + result.add("editor-enabled"); + result.add("editable"); + result.add("frozen-column-count"); + result.add("frozen-columns"); + result.add("height-by-rows"); + result.add("rows"); + result.add("selection-mode"); + result.add("header-visible"); + result.add("footer-visible"); + result.add("editor-error-handler"); + result.add("height-mode"); + + return result; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java new file mode 100644 index 0000000000..63c6f5aeaf --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java @@ -0,0 +1,175 @@ +/* + * 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.ui.renderers; + +import com.vaadin.server.AbstractJavaScriptExtension; +import com.vaadin.server.JavaScriptCallbackHelper; +import com.vaadin.server.JsonCodec; +import com.vaadin.shared.JavaScriptExtensionState; +import com.vaadin.shared.communication.ServerRpc; +import com.vaadin.ui.LegacyGrid.AbstractRenderer; +import com.vaadin.ui.JavaScriptFunction; + +import elemental.json.Json; +import elemental.json.JsonValue; + +/** + * Base class for Renderers with all client-side logic implemented using + * JavaScript. + *

+ * When a new JavaScript renderer is initialized in the browser, the framework + * will look for a globally defined JavaScript function that will initialize the + * renderer. The name of the initialization function is formed by replacing . + * with _ in the name of the server-side class. If no such function is defined, + * each super class is used in turn until a match is found. The framework will + * thus first attempt with com_example_MyRenderer for the + * server-side + * com.example.MyRenderer extends AbstractJavaScriptRenderer class. + * If MyRenderer instead extends com.example.SuperRenderer , then + * com_example_SuperRenderer will also be attempted if + * com_example_MyRenderer has not been defined. + *

+ * + * In addition to the general JavaScript extension functionality explained in + * {@link AbstractJavaScriptExtension}, this class also provides some + * functionality specific for renderers. + *

+ * The initialization function will be called with this pointing to + * a connector wrapper object providing integration to Vaadin. Please note that + * in JavaScript, this is not necessarily defined inside callback + * functions and it might therefore be necessary to assign the reference to a + * separate variable, e.g. var self = this;. In addition to the + * extension functions described for {@link AbstractJavaScriptExtension}, the + * connector wrapper object also provides this function: + *

    + *
  • getRowKey(rowIndex) - Gets a unique identifier for the row + * at the given index. This identifier can be used on the server to retrieve the + * corresponding ItemId using {@link #getItemId(String)}.
  • + *
+ * The connector wrapper also supports these special functions that can be + * implemented by the connector: + *
    + *
  • render(cell, data) - Callback for rendering the given data + * into the given cell. The structure of cell and data are described in separate + * sections below. The renderer is required to implement this function. + * Corresponds to + * {@link com.vaadin.client.renderers.Renderer#render(com.vaadin.client.widget.grid.RendererCellReference, Object)} + * .
  • + *
  • init(cell) - Prepares a cell for rendering. Corresponds to + * {@link com.vaadin.client.renderers.ComplexRenderer#init(com.vaadin.client.widget.grid.RendererCellReference)} + * .
  • + *
  • destory(cell) - Allows the renderer to release resources + * allocate for a cell that will no longer be used. Corresponds to + * {@link com.vaadin.client.renderers.ComplexRenderer#destroy(com.vaadin.client.widget.grid.RendererCellReference)} + * .
  • + *
  • onActivate(cell) - Called when the cell is activated by the + * user e.g. by double clicking on the cell or pressing enter with the cell + * focused. Corresponds to + * {@link com.vaadin.client.renderers.ComplexRenderer#onActivate(com.vaadin.client.widget.grid.CellReference)} + * .
  • + *
  • getConsumedEvents() - Returns a JavaScript array of event + * names that should cause onBrowserEvent to be invoked whenever an event is + * fired for a cell managed by this renderer. Corresponds to + * {@link com.vaadin.client.renderers.ComplexRenderer#getConsumedEvents()}.
  • + *
  • onBrowserEvent(cell, event) - Called by Grid when an event + * of a type returned by getConsumedEvents is fired for a cell managed by this + * renderer. Corresponds to + * {@link com.vaadin.client.renderers.ComplexRenderer#onBrowserEvent(com.vaadin.client.widget.grid.CellReference, com.google.gwt.dom.client.NativeEvent)} + * .
  • + *
+ * + *

+ * The cell object passed to functions defined by the renderer has these + * properties: + *

    + *
  • element - The DOM element corresponding to this cell. + * Readonly.
  • + *
  • rowIndex - The current index of the row of this cell. + * Readonly.
  • + *
  • columnIndex - The current index of the column of this cell. + * Readonly.
  • + *
  • colSpan - The number of columns spanned by this cell. Only + * supported in the object passed to the render function - other + * functions should not use the property. Readable and writable. + *
+ * + * @author Vaadin Ltd + * @since 7.4 + */ +public abstract class AbstractJavaScriptRenderer + extends AbstractRenderer { + private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( + this); + + protected AbstractJavaScriptRenderer(Class presentationType, + String nullRepresentation) { + super(presentationType, nullRepresentation); + } + + protected AbstractJavaScriptRenderer(Class presentationType) { + super(presentationType, null); + } + + @Override + protected void registerRpc(R implementation, + Class rpcInterfaceType) { + super.registerRpc(implementation, rpcInterfaceType); + callbackHelper.registerRpc(rpcInterfaceType); + } + + /** + * Register a {@link JavaScriptFunction} that can be called from the + * JavaScript using the provided name. A JavaScript function with the + * provided name will be added to the connector wrapper object (initially + * available as this). Calling that JavaScript function will + * cause the call method in the registered {@link JavaScriptFunction} to be + * invoked with the same arguments. + * + * @param functionName + * the name that should be used for client-side callback + * @param function + * the {@link JavaScriptFunction} object that will be invoked + * when the JavaScript function is called + */ + protected void addFunction(String functionName, + JavaScriptFunction function) { + callbackHelper.registerCallback(functionName, function); + } + + /** + * Invoke a named function that the connector JavaScript has added to the + * JavaScript connector wrapper object. The arguments can be any boxed + * primitive type, String, {@link JsonValue} or arrays of any other + * supported type. Complex types (e.g. List, Set, Map, Connector or any + * JavaBean type) must be explicitly serialized to a {@link JsonValue} + * before sending. This can be done either with + * {@link JsonCodec#encode(Object, JsonValue, java.lang.reflect.Type, com.vaadin.ui.ConnectorTracker)} + * or using the factory methods in {@link Json}. + * + * @param name + * the name of the function + * @param arguments + * function arguments + */ + protected void callFunction(String name, Object... arguments) { + callbackHelper.invokeCallback(name, arguments); + } + + @Override + protected JavaScriptExtensionState getState() { + return (JavaScriptExtensionState) super.getState(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java new file mode 100644 index 0000000000..7a87f8f4c1 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java @@ -0,0 +1,76 @@ +/* + * 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.ui.renderers; + +import elemental.json.JsonValue; + +/** + * A Renderer that displays a button with a textual caption. The value of the + * corresponding property is used as the caption. Click listeners can be added + * to the renderer, invoked when any of the rendered buttons is clicked. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class ButtonRenderer extends ClickableRenderer { + + /** + * Creates a new button renderer. + * + * @param nullRepresentation + * the textual representation of {@code null} value + */ + public ButtonRenderer(String nullRepresentation) { + super(String.class, nullRepresentation); + } + + /** + * Creates a new button renderer and adds the given click listener to it. + * + * @param listener + * the click listener to register + * @param nullRepresentation + * the textual representation of {@code null} value + */ + public ButtonRenderer(RendererClickListener listener, + String nullRepresentation) { + this(nullRepresentation); + addClickListener(listener); + } + + /** + * Creates a new button renderer. + */ + public ButtonRenderer() { + this(""); + } + + /** + * Creates a new button renderer and adds the given click listener to it. + * + * @param listener + * the click listener to register + */ + public ButtonRenderer(RendererClickListener listener) { + this(listener, ""); + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java new file mode 100644 index 0000000000..d8fcdc3b4e --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java @@ -0,0 +1,143 @@ +/* + * 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.ui.renderers; + +import java.lang.reflect.Method; + +import com.vaadin.event.ConnectorEventListener; +import com.vaadin.event.MouseEvents.ClickEvent; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.ui.grid.renderers.RendererClickRpc; +import com.vaadin.ui.LegacyGrid; +import com.vaadin.ui.LegacyGrid.AbstractRenderer; +import com.vaadin.ui.LegacyGrid.Column; +import com.vaadin.util.ReflectTools; + +/** + * An abstract superclass for Renderers that render clickable items. Click + * listeners can be added to a renderer to be notified when any of the rendered + * items is clicked. + * + * @param + * the type presented by the renderer + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class ClickableRenderer extends AbstractRenderer { + + /** + * An interface for listening to {@link RendererClickEvent renderer click + * events}. + * + * @see {@link ButtonRenderer#addClickListener(RendererClickListener)} + */ + public interface RendererClickListener extends ConnectorEventListener { + + static final Method CLICK_METHOD = ReflectTools.findMethod( + RendererClickListener.class, "click", RendererClickEvent.class); + + /** + * Called when a rendered button is clicked. + * + * @param event + * the event representing the click + */ + void click(RendererClickEvent event); + } + + /** + * An event fired when a button rendered by a ButtonRenderer is clicked. + */ + public static class RendererClickEvent extends ClickEvent { + + private Object itemId; + private Column column; + + protected RendererClickEvent(LegacyGrid source, Object itemId, Column column, + MouseEventDetails mouseEventDetails) { + super(source, mouseEventDetails); + this.itemId = itemId; + this.column = column; + } + + /** + * Returns the item ID of the row where the click event originated. + * + * @return the item ID of the clicked row + */ + public Object getItemId() { + return itemId; + } + + /** + * Returns the {@link Column} where the click event originated. + * + * @return the column of the click event + */ + public Column getColumn() { + return column; + } + + /** + * Returns the property ID where the click event originated. + * + * @return the property ID of the clicked cell + */ + public Object getPropertyId() { + return column.getPropertyId(); + } + } + + protected ClickableRenderer(Class presentationType) { + this(presentationType, null); + } + + protected ClickableRenderer(Class presentationType, + String nullRepresentation) { + super(presentationType, nullRepresentation); + registerRpc(new RendererClickRpc() { + @Override + public void click(String rowKey, String columnId, + MouseEventDetails mouseDetails) { + fireEvent(new RendererClickEvent(getParentGrid(), + getItemId(rowKey), getColumn(columnId), mouseDetails)); + } + }); + } + + /** + * Adds a click listener to this button renderer. The listener is invoked + * every time one of the buttons rendered by this renderer is clicked. + * + * @param listener + * the click listener to be added + */ + public void addClickListener(RendererClickListener listener) { + addListener(RendererClickEvent.class, listener, + RendererClickListener.CLICK_METHOD); + } + + /** + * Removes the given click listener from this renderer. + * + * @param listener + * the click listener to be removed + */ + public void removeClickListener(RendererClickListener listener) { + removeListener(RendererClickEvent.class, listener); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java new file mode 100644 index 0000000000..eea18e7445 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java @@ -0,0 +1,240 @@ +/* + * 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.ui.renderers; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; + +import com.vaadin.ui.LegacyGrid.AbstractRenderer; + +import elemental.json.JsonValue; + +/** + * A renderer for presenting date values. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class DateRenderer extends AbstractRenderer { + private final Locale locale; + private final String formatString; + private final DateFormat dateFormat; + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with the {@link Date#toString()} + * representation for the default locale. + */ + public DateRenderer() { + this(Locale.getDefault(), ""); + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with the {@link Date#toString()} + * representation for the given locale. + * + * @param locale + * the locale in which to present dates + * @throws IllegalArgumentException + * if {@code locale} is {@code null} + */ + public DateRenderer(Locale locale) throws IllegalArgumentException { + this("%s", locale, ""); + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with the {@link Date#toString()} + * representation for the given locale. + * + * @param locale + * the locale in which to present dates + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if {@code locale} is {@code null} + */ + public DateRenderer(Locale locale, String nullRepresentation) + throws IllegalArgumentException { + this("%s", locale, nullRepresentation); + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with the given string format, as + * displayed in the default locale. + * + * @param formatString + * the format string with which to format the date + * @throws IllegalArgumentException + * if {@code formatString} is {@code null} + * @see Format + * String Syntax + */ + public DateRenderer(String formatString) throws IllegalArgumentException { + this(formatString, ""); + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with the given string format, as + * displayed in the default locale. + * + * @param formatString + * the format string with which to format the date + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if {@code formatString} is {@code null} + * @see Format + * String Syntax + */ + public DateRenderer(String formatString, String nullRepresentation) + throws IllegalArgumentException { + this(formatString, Locale.getDefault(), nullRepresentation); + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with the given string format, as + * displayed in the given locale. + * + * @param formatString + * the format string to format the date with + * @param locale + * the locale to use + * @throws IllegalArgumentException + * if either argument is {@code null} + * @see Format + * String Syntax + */ + public DateRenderer(String formatString, Locale locale) + throws IllegalArgumentException { + this(formatString, locale, ""); + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with the given string format, as + * displayed in the given locale. + * + * @param formatString + * the format string to format the date with + * @param locale + * the locale to use + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if either argument is {@code null} + * @see Format + * String Syntax + */ + public DateRenderer(String formatString, Locale locale, + String nullRepresentation) throws IllegalArgumentException { + super(Date.class, nullRepresentation); + + if (formatString == null) { + throw new IllegalArgumentException("format string may not be null"); + } + + if (locale == null) { + throw new IllegalArgumentException("locale may not be null"); + } + + this.locale = locale; + this.formatString = formatString; + dateFormat = null; + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with he given date format. + * + * @param dateFormat + * the date format to use when rendering dates + * @throws IllegalArgumentException + * if {@code dateFormat} is {@code null} + */ + public DateRenderer(DateFormat dateFormat) throws IllegalArgumentException { + this(dateFormat, ""); + } + + /** + * Creates a new date renderer. + *

+ * The renderer is configured to render with he given date format. + * + * @param dateFormat + * the date format to use when rendering dates + * @throws IllegalArgumentException + * if {@code dateFormat} is {@code null} + */ + public DateRenderer(DateFormat dateFormat, String nullRepresentation) + throws IllegalArgumentException { + super(Date.class, nullRepresentation); + if (dateFormat == null) { + throw new IllegalArgumentException("date format may not be null"); + } + + locale = null; + formatString = null; + this.dateFormat = dateFormat; + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } + + @Override + public JsonValue encode(Date value) { + String dateString; + if (value == null) { + dateString = getNullRepresentation(); + } else if (dateFormat != null) { + dateString = dateFormat.format(value); + } else { + dateString = String.format(locale, formatString, value); + } + return encode(dateString, String.class); + } + + @Override + public String toString() { + final String fieldInfo; + if (dateFormat != null) { + fieldInfo = "dateFormat: " + dateFormat.toString(); + } else { + fieldInfo = "locale: " + locale + ", formatString: " + formatString; + } + + return String.format("%s [%s]", getClass().getSimpleName(), fieldInfo); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java new file mode 100644 index 0000000000..c197f7415a --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java @@ -0,0 +1,49 @@ +/* + * 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.ui.renderers; + +import com.vaadin.ui.LegacyGrid.AbstractRenderer; +import elemental.json.JsonValue; + +/** + * A renderer for presenting HTML content. + * + * @author Vaadin Ltd + * @since 7.4 + */ +public class HtmlRenderer extends AbstractRenderer { + /** + * Creates a new HTML renderer. + * + * @param nullRepresentation + * the html representation of {@code null} value + */ + public HtmlRenderer(String nullRepresentation) { + super(String.class, nullRepresentation); + } + + /** + * Creates a new HTML renderer. + */ + public HtmlRenderer() { + this(""); + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java new file mode 100644 index 0000000000..56e319b47d --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java @@ -0,0 +1,68 @@ +/* + * 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.ui.renderers; + +import com.vaadin.server.ExternalResource; +import com.vaadin.server.Resource; +import com.vaadin.server.ResourceReference; +import com.vaadin.server.ThemeResource; +import com.vaadin.shared.communication.URLReference; + +import elemental.json.JsonValue; + +/** + * A renderer for presenting images. + *

+ * The image for each rendered cell is read from a Resource-typed property in + * the data source. Only {@link ExternalResource}s and {@link ThemeResource}s + * are currently supported. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class ImageRenderer extends ClickableRenderer { + + /** + * Creates a new image renderer. + */ + public ImageRenderer() { + super(Resource.class, null); + } + + /** + * Creates a new image renderer and adds the given click listener to it. + * + * @param listener + * the click listener to register + */ + public ImageRenderer(RendererClickListener listener) { + this(); + addClickListener(listener); + } + + @Override + public JsonValue encode(Resource resource) { + if (!(resource == null || resource instanceof ExternalResource + || resource instanceof ThemeResource)) { + throw new IllegalArgumentException( + "ImageRenderer only supports ExternalResource and ThemeResource (" + + resource.getClass().getSimpleName() + " given)"); + } + + return encode(ResourceReference.create(resource, this, null), + URLReference.class); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java new file mode 100644 index 0000000000..e54eecc6ef --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java @@ -0,0 +1,207 @@ +/* + * 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.ui.renderers; + +import java.text.NumberFormat; +import java.util.Locale; + +import com.vaadin.ui.LegacyGrid.AbstractRenderer; + +import elemental.json.JsonValue; + +/** + * A renderer for presenting number values. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class NumberRenderer extends AbstractRenderer { + private final Locale locale; + private final NumberFormat numberFormat; + private final String formatString; + + /** + * Creates a new number renderer. + *

+ * The renderer is configured to render with the number's natural string + * representation in the default locale. + */ + public NumberRenderer() { + this(Locale.getDefault()); + } + + /** + * Creates a new number renderer. + *

+ * The renderer is configured to render the number as defined with the given + * number format. + * + * @param numberFormat + * the number format with which to display numbers + * @throws IllegalArgumentException + * if {@code numberFormat} is {@code null} + */ + public NumberRenderer(NumberFormat numberFormat) { + this(numberFormat, ""); + } + + /** + * Creates a new number renderer. + *

+ * The renderer is configured to render the number as defined with the given + * number format. + * + * @param numberFormat + * the number format with which to display numbers + * @param nullRepresentation + * the textual representation of {@code null} value + * @throws IllegalArgumentException + * if {@code numberFormat} is {@code null} + */ + public NumberRenderer(NumberFormat numberFormat, String nullRepresentation) + throws IllegalArgumentException { + super(Number.class, nullRepresentation); + + if (numberFormat == null) { + throw new IllegalArgumentException("Number format may not be null"); + } + + locale = null; + this.numberFormat = numberFormat; + formatString = null; + } + + /** + * Creates a new number renderer. + *

+ * The renderer is configured to render with the number's natural string + * representation in the given locale. + * + * @param locale + * the locale in which to display numbers + * @throws IllegalArgumentException + * if {@code locale} is {@code null} + */ + public NumberRenderer(Locale locale) throws IllegalArgumentException { + this("%s", locale); + } + + /** + * Creates a new number renderer. + *

+ * The renderer is configured to render with the number's natural string + * representation in the given locale. + * + * @param formatString + * the format string with which to format the number + * @param locale + * the locale in which to display numbers + * @throws IllegalArgumentException + * if {@code locale} is {@code null} + */ + public NumberRenderer(String formatString, Locale locale) + throws IllegalArgumentException { + this(formatString, locale, ""); // This will call #toString() during + // formatting + } + + /** + * Creates a new number renderer. + *

+ * The renderer is configured to render with the given format string in the + * default locale. + * + * @param formatString + * the format string with which to format the number + * @throws IllegalArgumentException + * if {@code formatString} is {@code null} + * @see Format + * String Syntax + */ + public NumberRenderer(String formatString) throws IllegalArgumentException { + this(formatString, Locale.getDefault(), ""); + } + + /** + * Creates a new number renderer. + *

+ * The renderer is configured to render with the given format string in the + * given locale. + * + * @param formatString + * the format string with which to format the number + * @param locale + * the locale in which to present numbers + * @throws IllegalArgumentException + * if either argument is {@code null} + * @see Format + * String Syntax + */ + public NumberRenderer(String formatString, Locale locale, + String nullRepresentation) { + super(Number.class, nullRepresentation); + + if (formatString == null) { + throw new IllegalArgumentException("Format string may not be null"); + } + + if (locale == null) { + throw new IllegalArgumentException("Locale may not be null"); + } + + this.locale = locale; + numberFormat = null; + this.formatString = formatString; + } + + @Override + public JsonValue encode(Number value) { + String stringValue; + if (value == null) { + stringValue = getNullRepresentation(); + } else if (formatString != null && locale != null) { + stringValue = String.format(locale, formatString, value); + } else if (numberFormat != null) { + stringValue = numberFormat.format(value); + } else { + throw new IllegalStateException(String.format( + "Internal bug: " + "%s is in an illegal state: " + + "[locale: %s, numberFormat: %s, formatString: %s]", + getClass().getSimpleName(), locale, numberFormat, + formatString)); + } + return encode(stringValue, String.class); + } + + @Override + public String toString() { + final String fieldInfo; + if (numberFormat != null) { + fieldInfo = "numberFormat: " + numberFormat.toString(); + } else { + fieldInfo = "locale: " + locale + ", formatString: " + formatString; + } + + return String.format("%s [%s]", getClass().getSimpleName(), fieldInfo); + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java new file mode 100644 index 0000000000..fe90dfdee0 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java @@ -0,0 +1,46 @@ +/* + * 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.ui.renderers; + +import com.vaadin.ui.LegacyGrid.AbstractRenderer; + +import elemental.json.JsonValue; + +/** + * A renderer that represents a double values as a graphical progress bar. + * + * @author Vaadin Ltd + * @since 7.4 + */ +public class ProgressBarRenderer extends AbstractRenderer { + + /** + * Creates a new text renderer + */ + public ProgressBarRenderer() { + super(Double.class, null); + } + + @Override + public JsonValue encode(Double value) { + if (value != null) { + value = Math.max(Math.min(value, 1), 0); + } else { + value = 0d; + } + return super.encode(value); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/Renderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/Renderer.java new file mode 100644 index 0000000000..d42e299ea8 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/Renderer.java @@ -0,0 +1,69 @@ +/* + * 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.ui.renderers; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.Extension; + +import elemental.json.JsonValue; + +/** + * A ClientConnector for controlling client-side + * {@link com.vaadin.client.widget.grid.Renderer Grid renderers}. Renderers + * currently extend the Extension interface, but this fact should be regarded as + * an implementation detail and subject to change in a future major or minor + * Vaadin revision. + * + * @param + * the type this renderer knows how to present + * + * @since 7.4 + * @author Vaadin Ltd + */ +public interface Renderer extends Extension { + + /** + * Returns the class literal corresponding to the presentation type T. + * + * @return the class literal of T + */ + Class getPresentationType(); + + /** + * Encodes the given value into a {@link JsonValue}. + * + * @param value + * the value to encode + * @return a JSON representation of the given value + */ + JsonValue encode(T value); + + /** + * This method is inherited from Extension but should never be called + * directly with a Renderer. + */ + @Override + @Deprecated + void remove(); + + /** + * This method is inherited from Extension but should never be called + * directly with a Renderer. + */ + @Override + @Deprecated + void setParent(ClientConnector parent); +} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java new file mode 100644 index 0000000000..ac73615272 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java @@ -0,0 +1,50 @@ +/* + * 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.ui.renderers; + +import com.vaadin.ui.LegacyGrid.AbstractRenderer; +import elemental.json.JsonValue; + +/** + * A renderer for presenting simple plain-text string values. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class TextRenderer extends AbstractRenderer { + + /** + * Creates a new text renderer + */ + public TextRenderer() { + this(""); + } + + /** + * Creates a new text renderer + * + * @param nullRepresentation + * the textual representation of {@code null} value + */ + public TextRenderer(String nullRepresentation) { + super(String.class, nullRepresentation); + } + + @Override + public String getNullRepresentation() { + return super.getNullRepresentation(); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java new file mode 100644 index 0000000000..3333cd7744 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012 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.data.fieldgroup; + +import org.junit.Assert; +import org.junit.Test; + +public class BeanFieldGroupTest { + + class Main { + private String mainField; + + public String getMainField() { + return mainField; + } + + public void setMainField(String mainField) { + this.mainField = mainField; + } + + } + + class Sub1 extends Main { + private Integer sub1Field; + + public Integer getSub1Field() { + return sub1Field; + } + + public void setSub1Field(Integer sub1Field) { + this.sub1Field = sub1Field; + } + + } + + class Sub2 extends Sub1 { + private boolean sub2field; + + public boolean isSub2field() { + return sub2field; + } + + public void setSub2field(boolean sub2field) { + this.sub2field = sub2field; + } + + } + + @Test + public void propertyTypeWithoutItem() { + BeanFieldGroup s = new BeanFieldGroup( + Sub2.class); + Assert.assertEquals(boolean.class, s.getPropertyType("sub2field")); + Assert.assertEquals(Integer.class, s.getPropertyType("sub1Field")); + Assert.assertEquals(String.class, s.getPropertyType("mainField")); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java new file mode 100644 index 0000000000..93aa1350b0 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java @@ -0,0 +1,125 @@ +/* + * 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.data.fieldgroup; + +import java.lang.reflect.Constructor; +import java.util.Date; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; +import com.vaadin.ui.AbstractSelect; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.ListSelect; +import com.vaadin.v7.ui.LegacyDateField; +import com.vaadin.v7.ui.LegacyField; +import com.vaadin.v7.ui.LegacyInlineDateField; +import com.vaadin.v7.ui.LegacyPopupDateField; +import com.vaadin.v7.ui.LegacyTextField; + +public class DefaultFieldGroupFieldFactoryTest { + + private DefaultFieldGroupFieldFactory fieldFactory; + + @Before + public void setupFieldFactory() { + fieldFactory = DefaultFieldGroupFieldFactory.get(); + } + + @Test + public void noPublicConstructor() { + Class clazz = DefaultFieldGroupFieldFactory.class; + Constructor[] constructors = clazz.getConstructors(); + Assert.assertEquals( + "DefaultFieldGroupFieldFactory contains public constructors", 0, + constructors.length); + } + + @Test + public void testSameInstance() { + DefaultFieldGroupFieldFactory factory1 = DefaultFieldGroupFieldFactory + .get(); + DefaultFieldGroupFieldFactory factory2 = DefaultFieldGroupFieldFactory + .get(); + Assert.assertTrue( + "DefaultFieldGroupFieldFactory.get() method returns different instances", + factory1 == factory2); + Assert.assertNotNull( + "DefaultFieldGroupFieldFactory.get() method returns null", + factory1); + } + + @Test + public void testDateGenerationForPopupDateField() { + LegacyField f = fieldFactory.createField(Date.class, + LegacyDateField.class); + Assert.assertNotNull(f); + Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); + } + + @Test + public void testDateGenerationForInlineDateField() { + LegacyField f = fieldFactory.createField(Date.class, + LegacyInlineDateField.class); + Assert.assertNotNull(f); + Assert.assertEquals(LegacyInlineDateField.class, f.getClass()); + } + + @Test + public void testDateGenerationForTextField() { + LegacyField f = fieldFactory.createField(Date.class, + LegacyTextField.class); + Assert.assertNotNull(f); + Assert.assertEquals(LegacyTextField.class, f.getClass()); + } + + @Test + public void testDateGenerationForField() { + LegacyField f = fieldFactory.createField(Date.class, LegacyField.class); + Assert.assertNotNull(f); + Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); + } + + public enum SomeEnum { + FOO, BAR; + } + + @Test + public void testEnumComboBox() { + LegacyField f = fieldFactory.createField(SomeEnum.class, + ComboBox.class); + Assert.assertNotNull(f); + Assert.assertEquals(ComboBox.class, f.getClass()); + } + + @Test + public void testEnumAnySelect() { + LegacyField f = fieldFactory.createField(SomeEnum.class, + AbstractSelect.class); + Assert.assertNotNull(f); + Assert.assertEquals(ListSelect.class, f.getClass()); + } + + @Test + public void testEnumAnyField() { + LegacyField f = fieldFactory.createField(SomeEnum.class, + LegacyField.class); + Assert.assertNotNull(f); + Assert.assertEquals(ListSelect.class, f.getClass()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java new file mode 100644 index 0000000000..e270211abf --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java @@ -0,0 +1,97 @@ +/* + * 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.data.fieldgroup; + +import java.util.Date; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.BeanItem; +import com.vaadin.v7.ui.LegacyField; +import com.vaadin.v7.ui.LegacyPopupDateField; + +public class FieldGroupDateTest { + + private FieldGroup fieldGroup; + + public class TestBean { + private Date javaDate; + private java.sql.Date sqlDate; + + public TestBean(Date javaDate, java.sql.Date sqlDate) { + super(); + this.javaDate = javaDate; + this.sqlDate = sqlDate; + } + + public java.sql.Date getSqlDate() { + return sqlDate; + } + + public void setSqlDate(java.sql.Date sqlDate) { + this.sqlDate = sqlDate; + } + + public Date getJavaDate() { + return javaDate; + } + + public void setJavaDate(Date date) { + javaDate = date; + } + } + + @SuppressWarnings("deprecation") + @Before + public void setup() { + fieldGroup = new FieldGroup(); + fieldGroup.setItemDataSource(new BeanItem(new TestBean( + new Date(2010, 5, 7), new java.sql.Date(2011, 6, 8)))); + } + + @Test + public void testBuildAndBindDate() { + LegacyField f = fieldGroup.buildAndBind("javaDate"); + Assert.assertNotNull(f); + Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); + } + + @Test + public void testBuildAndBindSqlDate() { + LegacyField f = fieldGroup.buildAndBind("sqlDate"); + Assert.assertNotNull(f); + Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); + } + + @Test + public void clearFields() { + LegacyPopupDateField sqlDate = new LegacyPopupDateField(); + LegacyPopupDateField javaDate = new LegacyPopupDateField(); + fieldGroup.bind(sqlDate, "sqlDate"); + fieldGroup.bind(javaDate, "javaDate"); + + Assert.assertEquals(new Date(2010, 5, 7), javaDate.getValue()); + Assert.assertEquals(new Date(2011, 6, 8), sqlDate.getValue()); + + fieldGroup.clear(); + Assert.assertEquals(null, javaDate.getValue()); + Assert.assertEquals(null, sqlDate.getValue()); + + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java new file mode 100644 index 0000000000..4622f0960e --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java @@ -0,0 +1,33 @@ +/* + * 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.data.fieldgroup; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.v7.ui.LegacyPopupDateField; + +public class FieldGroupExceptionTest { + + @Test(expected = CommitException.class) + public void testUnboundCommitException() throws CommitException { + FieldGroup fieldGroup = new FieldGroup(); + LegacyPopupDateField dateField = new LegacyPopupDateField(); + fieldGroup.bind(dateField, "date"); + fieldGroup.commit(); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java new file mode 100644 index 0000000000..c0039fc4fb --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java @@ -0,0 +1,96 @@ +package com.vaadin.data.fieldgroup; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.nullValue; +import static org.mockito.Mockito.mock; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Property; +import com.vaadin.data.Property.Transactional; +import com.vaadin.data.util.BeanItem; +import com.vaadin.data.util.TransactionalPropertyWrapper; +import com.vaadin.v7.ui.LegacyField; +import com.vaadin.v7.ui.LegacyTextField; + +public class FieldGroupTest { + + private FieldGroup sut; + private LegacyField field; + + @Before + public void setup() { + sut = new FieldGroup(); + field = mock(LegacyField.class); + } + + @Test + public void fieldIsBound() { + sut.bind(field, "foobar"); + + assertThat(sut.getField("foobar"), is(field)); + } + + @Test(expected = FieldGroup.BindException.class) + public void cannotBindToAlreadyBoundProperty() { + sut.bind(field, "foobar"); + sut.bind(mock(LegacyField.class), "foobar"); + } + + @Test(expected = FieldGroup.BindException.class) + public void cannotBindNullField() { + sut.bind(null, "foobar"); + } + + public void canUnbindWithoutItem() { + sut.bind(field, "foobar"); + + sut.unbind(field); + assertThat(sut.getField("foobar"), is(nullValue())); + } + + @Test + public void wrapInTransactionalProperty_provideCustomImpl_customTransactionalWrapperIsUsed() { + Bean bean = new Bean(); + FieldGroup group = new FieldGroup() { + @Override + protected Transactional wrapInTransactionalProperty( + Property itemProperty) { + return new TransactionalPropertyImpl(itemProperty); + } + }; + group.setItemDataSource(new BeanItem(bean)); + LegacyTextField field = new LegacyTextField(); + group.bind(field, "name"); + + Property propertyDataSource = field.getPropertyDataSource(); + Assert.assertTrue( + "Custom implementation of transactional property " + + "has not been used", + propertyDataSource instanceof TransactionalPropertyImpl); + } + + public static class TransactionalPropertyImpl + extends TransactionalPropertyWrapper { + + public TransactionalPropertyImpl(Property wrappedProperty) { + super(wrappedProperty); + } + + } + + public static class Bean { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java new file mode 100644 index 0000000000..4a9e4f9891 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java @@ -0,0 +1,77 @@ +package com.vaadin.data.util; + +/** + * Automated test for {@link AbstractBeanContainer}. + * + * Only a limited subset of the functionality is tested here, the rest in tests + * of subclasses including {@link BeanItemContainer} and {@link BeanContainer}. + */ +public abstract class AbstractBeanContainerTestBase + extends AbstractInMemoryContainerTestBase { + + public static class Person { + private String name; + + public Person(String name) { + setName(name); + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + public static class ClassName { + // field names match constants in parent test class + private String fullyQualifiedName; + private String simpleName; + private String reverseFullyQualifiedName; + private Integer idNumber; + + public ClassName(String fullyQualifiedName, Integer idNumber) { + this.fullyQualifiedName = fullyQualifiedName; + simpleName = AbstractContainerTestBase + .getSimpleName(fullyQualifiedName); + reverseFullyQualifiedName = reverse(fullyQualifiedName); + this.idNumber = idNumber; + } + + public String getFullyQualifiedName() { + return fullyQualifiedName; + } + + public void setFullyQualifiedName(String fullyQualifiedName) { + this.fullyQualifiedName = fullyQualifiedName; + } + + public String getSimpleName() { + return simpleName; + } + + public void setSimpleName(String simpleName) { + this.simpleName = simpleName; + } + + public String getReverseFullyQualifiedName() { + return reverseFullyQualifiedName; + } + + public void setReverseFullyQualifiedName( + String reverseFullyQualifiedName) { + this.reverseFullyQualifiedName = reverseFullyQualifiedName; + } + + public Integer getIdNumber() { + return idNumber; + } + + public void setIdNumber(Integer idNumber) { + this.idNumber = idNumber; + } + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java new file mode 100644 index 0000000000..955b609735 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java @@ -0,0 +1,866 @@ +package com.vaadin.data.util; + +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 java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.Assert; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Filterable; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Container.Ordered; +import com.vaadin.data.Container.Sortable; +import com.vaadin.data.Item; +import com.vaadin.data.util.filter.SimpleStringFilter; + +public abstract class AbstractContainerTestBase { + + /** + * Helper class for testing e.g. listeners expecting events to be fired. + */ + protected abstract static class AbstractEventCounter { + private int eventCount = 0; + private int lastAssertedEventCount = 0; + + /** + * Increment the event count. To be called by subclasses e.g. from a + * listener method. + */ + protected void increment() { + ++eventCount; + } + + /** + * Check that no one event has occurred since the previous assert call. + */ + public void assertNone() { + Assert.assertEquals(lastAssertedEventCount, eventCount); + } + + /** + * Check that exactly one event has occurred since the previous assert + * call. + */ + public void assertOnce() { + Assert.assertEquals(++lastAssertedEventCount, eventCount); + } + + /** + * Reset the counter and the expected count. + */ + public void reset() { + eventCount = 0; + lastAssertedEventCount = 0; + } + } + + /** + * Test class for counting item set change events and verifying they have + * been received. + */ + protected static class ItemSetChangeCounter extends AbstractEventCounter + implements ItemSetChangeListener { + + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + increment(); + } + + } + + // #6043: for items that have been filtered out, Container interface does + // not specify what to return from getItem() and getContainerProperty(), so + // need checkGetItemNull parameter for the test to be usable for most + // current containers + protected void validateContainer(Container container, + Object expectedFirstItemId, Object expectedLastItemId, + Object itemIdInSet, Object itemIdNotInSet, boolean checkGetItemNull, + int expectedSize) { + Container.Indexed indexed = null; + if (container instanceof Container.Indexed) { + indexed = (Container.Indexed) container; + } + + List itemIdList = new ArrayList(container.getItemIds()); + + // size() + assertEquals(expectedSize, container.size()); + assertEquals(expectedSize, itemIdList.size()); + + // first item, last item + Object first = itemIdList.get(0); + Object last = itemIdList.get(itemIdList.size() - 1); + + assertEquals(expectedFirstItemId, first); + assertEquals(expectedLastItemId, last); + + // containsId + assertFalse(container.containsId(itemIdNotInSet)); + assertTrue(container.containsId(itemIdInSet)); + + // getItem + if (checkGetItemNull) { + assertNull(container.getItem(itemIdNotInSet)); + } + assertNotNull(container.getItem(itemIdInSet)); + + // getContainerProperty + for (Object propId : container.getContainerPropertyIds()) { + if (checkGetItemNull) { + assertNull( + container.getContainerProperty(itemIdNotInSet, propId)); + } + assertNotNull(container.getContainerProperty(itemIdInSet, propId)); + } + + if (indexed != null) { + // firstItemId + assertEquals(first, indexed.firstItemId()); + + // lastItemId + assertEquals(last, indexed.lastItemId()); + + // nextItemId + assertEquals(itemIdList.get(1), indexed.nextItemId(first)); + + // prevItemId + assertEquals(itemIdList.get(itemIdList.size() - 2), + indexed.prevItemId(last)); + + // isFirstId + assertTrue(indexed.isFirstId(first)); + assertFalse(indexed.isFirstId(last)); + + // isLastId + assertTrue(indexed.isLastId(last)); + assertFalse(indexed.isLastId(first)); + + // indexOfId + assertEquals(0, indexed.indexOfId(first)); + assertEquals(expectedSize - 1, indexed.indexOfId(last)); + + // getIdByIndex + assertEquals(indexed.getIdByIndex(0), first); + assertEquals(indexed.getIdByIndex(expectedSize - 1), last); + + } + + // getItemProperty + Assert.assertNull( + container.getItem(itemIdInSet).getItemProperty("notinset")); + + } + + protected static final Object FULLY_QUALIFIED_NAME = "fullyQualifiedName"; + protected static final Object SIMPLE_NAME = "simpleName"; + protected static final Object REVERSE_FULLY_QUALIFIED_NAME = "reverseFullyQualifiedName"; + protected static final Object ID_NUMBER = "idNumber"; + + protected void testBasicContainerOperations(Container container) { + initializeContainer(container); + + // Basic container + validateContainer(container, sampleData[0], + sampleData[sampleData.length - 1], sampleData[10], "abc", true, + sampleData.length); + + validateRemovingItems(container); + validateAddItem(container); + if (container instanceof Container.Indexed) { + validateAddItemAt((Container.Indexed) container); + } + if (container instanceof Container.Ordered) { + validateAddItemAfter((Container.Ordered) container); + } + + } + + protected void validateRemovingItems(Container container) { + int sizeBeforeRemoving = container.size(); + + List itemIdList = new ArrayList(container.getItemIds()); + // There should be at least four items in the list + Object first = itemIdList.get(0); + Object middle = itemIdList.get(2); + Object last = itemIdList.get(itemIdList.size() - 1); + + container.removeItem(first); + container.removeItem(middle); // Middle now that first has been removed + container.removeItem(last); + + assertEquals(sizeBeforeRemoving - 3, container.size()); + + container.removeAllItems(); + + assertEquals(0, container.size()); + } + + protected void validateAddItem(Container container) { + try { + container.removeAllItems(); + + Object id = container.addItem(); + Assert.assertTrue(container.containsId(id)); + Assert.assertNotNull(container.getItem(id)); + + Item item = container.addItem("foo"); + Assert.assertNotNull(item); + Assert.assertTrue(container.containsId("foo")); + Assert.assertEquals(item, container.getItem("foo")); + + // Add again + Item item2 = container.addItem("foo"); + Assert.assertNull(item2); + + // Null is not a valid itemId + Assert.assertNull(container.addItem(null)); + } catch (UnsupportedOperationException e) { + // Ignore contains which do not support addItem* + } + } + + protected void validateAddItemAt(Container.Indexed container) { + try { + container.removeAllItems(); + + Object id = container.addItemAt(0); + Assert.assertTrue(container.containsId(id)); + Assert.assertEquals(id, container.getIdByIndex(0)); + Assert.assertNotNull(container.getItem(id)); + + Item item = container.addItemAt(0, "foo"); + Assert.assertNotNull(item); + Assert.assertTrue(container.containsId("foo")); + Assert.assertEquals(item, container.getItem("foo")); + Assert.assertEquals("foo", container.getIdByIndex(0)); + + Item itemAtEnd = container.addItemAt(2, "atend"); + Assert.assertNotNull(itemAtEnd); + Assert.assertTrue(container.containsId("atend")); + Assert.assertEquals(itemAtEnd, container.getItem("atend")); + Assert.assertEquals("atend", container.getIdByIndex(2)); + + // Add again + Item item2 = container.addItemAt(0, "foo"); + Assert.assertNull(item2); + } catch (UnsupportedOperationException e) { + // Ignore contains which do not support addItem* + } + } + + protected void validateAddItemAfter(Container.Ordered container) { + if (container instanceof AbstractBeanContainer) { + // Doesn't work as bean container requires beans + return; + } + + try { + container.removeAllItems(); + + Assert.assertNotNull(container.addItem(0)); + + Item item = container.addItemAfter(null, "foo"); + Assert.assertNotNull(item); + Assert.assertTrue(container.containsId("foo")); + Assert.assertEquals(item, container.getItem("foo")); + Assert.assertEquals("foo", + container.getItemIds().iterator().next()); + + Item itemAtEnd = container.addItemAfter(0, "atend"); + Assert.assertNotNull(itemAtEnd); + Assert.assertTrue(container.containsId("atend")); + Assert.assertEquals(itemAtEnd, container.getItem("atend")); + Iterator i = container.getItemIds().iterator(); + i.next(); + i.next(); + Assert.assertEquals("atend", i.next()); + + // Add again + Assert.assertNull(container.addItemAfter(null, "foo")); + Assert.assertNull(container.addItemAfter("atend", "foo")); + Assert.assertNull(container.addItemAfter("nonexistant", "123123")); + } catch (UnsupportedOperationException e) { + // Ignore contains which do not support addItem* + } + } + + protected void testContainerOrdered(Container.Ordered container) { + // addItem with empty container + Object id = container.addItem(); + assertOrderedContents(container, id); + Item item = container.getItem(id); + assertNotNull(item); + + // addItemAfter with empty container + container.removeAllItems(); + assertOrderedContents(container); + id = container.addItemAfter(null); + assertOrderedContents(container, id); + item = container.getItem(id); + assertNotNull(item); + + // Add a new item before the first + // addItemAfter + Object newFirstId = container.addItemAfter(null); + assertOrderedContents(container, newFirstId, id); + + // addItemAfter(Object) + Object newSecondItemId = container.addItemAfter(newFirstId); + // order is now: newFirstId, newSecondItemId, id + assertOrderedContents(container, newFirstId, newSecondItemId, id); + + // addItemAfter(Object,Object) + String fourthId = "id of the fourth item"; + Item fourth = container.addItemAfter(newFirstId, fourthId); + // order is now: newFirstId, fourthId, newSecondItemId, id + assertNotNull(fourth); + assertEquals(fourth, container.getItem(fourthId)); + assertOrderedContents(container, newFirstId, fourthId, newSecondItemId, + id); + + // addItemAfter(Object,Object) + Object fifthId = new Object(); + Item fifth = container.addItemAfter(null, fifthId); + // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id + assertNotNull(fifth); + assertEquals(fifth, container.getItem(fifthId)); + assertOrderedContents(container, fifthId, newFirstId, fourthId, + newSecondItemId, id); + + // addItemAfter(Object,Object) + Object sixthId = new Object(); + Item sixth = container.addItemAfter(id, sixthId); + // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id, + // sixthId + assertNotNull(sixth); + assertEquals(sixth, container.getItem(sixthId)); + assertOrderedContents(container, fifthId, newFirstId, fourthId, + newSecondItemId, id, sixthId); + + // Test order after removing first item 'fifthId' + container.removeItem(fifthId); + // order is now: newFirstId, fourthId, newSecondItemId, id, sixthId + assertOrderedContents(container, newFirstId, fourthId, newSecondItemId, + id, sixthId); + + // Test order after removing last item 'sixthId' + container.removeItem(sixthId); + // order is now: newFirstId, fourthId, newSecondItemId, id + assertOrderedContents(container, newFirstId, fourthId, newSecondItemId, + id); + + // Test order after removing item from the middle 'fourthId' + container.removeItem(fourthId); + // order is now: newFirstId, newSecondItemId, id + assertOrderedContents(container, newFirstId, newSecondItemId, id); + + // Delete remaining items + container.removeItem(newFirstId); + container.removeItem(newSecondItemId); + container.removeItem(id); + assertOrderedContents(container); + + Object finalItem = container.addItem(); + assertOrderedContents(container, finalItem); + } + + private void assertOrderedContents(Ordered container, Object... ids) { + assertEquals(ids.length, container.size()); + for (int i = 0; i < ids.length - 1; i++) { + assertNotNull("The item id should not be null", ids[i]); + } + if (ids.length == 0) { + assertNull("The first id is wrong", container.firstItemId()); + assertNull("The last id is wrong", container.lastItemId()); + return; + } + + assertEquals("The first id is wrong", ids[0], container.firstItemId()); + assertEquals("The last id is wrong", ids[ids.length - 1], + container.lastItemId()); + + // isFirstId & isLastId + assertTrue(container.isFirstId(container.firstItemId())); + assertTrue(container.isLastId(container.lastItemId())); + + // nextId + Object ref = container.firstItemId(); + for (int i = 1; i < ids.length; i++) { + Object next = container.nextItemId(ref); + assertEquals("The id after " + ref + " is wrong", ids[i], next); + ref = next; + } + assertNull("The last id should not have a next id", + container.nextItemId(ids[ids.length - 1])); + assertNull(container.nextItemId("not-in-container")); + + // prevId + ref = container.lastItemId(); + for (int i = ids.length - 2; i >= 0; i--) { + Object prev = container.prevItemId(ref); + assertEquals("The id before " + ref + " is wrong", ids[i], prev); + ref = prev; + } + assertNull("The first id should not have a prev id", + container.prevItemId(ids[0])); + assertNull(container.prevItemId("not-in-container")); + + } + + protected void testContainerIndexed(Container.Indexed container, + Object itemId, int itemPosition, boolean testAddEmptyItemAt, + Object newItemId, boolean testAddItemAtWithId) { + initializeContainer(container); + + // indexOfId + Assert.assertEquals(itemPosition, container.indexOfId(itemId)); + + // getIdByIndex + Assert.assertEquals(itemId, container.getIdByIndex(itemPosition)); + + // addItemAt + if (testAddEmptyItemAt) { + Object addedId = container.addItemAt(itemPosition); + Assert.assertEquals(itemPosition, container.indexOfId(addedId)); + Assert.assertEquals(itemPosition + 1, container.indexOfId(itemId)); + Assert.assertEquals(addedId, container.getIdByIndex(itemPosition)); + Assert.assertEquals(itemId, + container.getIdByIndex(itemPosition + 1)); + + Object newFirstId = container.addItemAt(0); + Assert.assertEquals(0, container.indexOfId(newFirstId)); + Assert.assertEquals(itemPosition + 2, container.indexOfId(itemId)); + Assert.assertEquals(newFirstId, container.firstItemId()); + Assert.assertEquals(newFirstId, container.getIdByIndex(0)); + Assert.assertEquals(itemId, + container.getIdByIndex(itemPosition + 2)); + + Object newLastId = container.addItemAt(container.size()); + Assert.assertEquals(container.size() - 1, + container.indexOfId(newLastId)); + Assert.assertEquals(itemPosition + 2, container.indexOfId(itemId)); + Assert.assertEquals(newLastId, container.lastItemId()); + Assert.assertEquals(newLastId, + container.getIdByIndex(container.size() - 1)); + Assert.assertEquals(itemId, + container.getIdByIndex(itemPosition + 2)); + + Assert.assertTrue(container.removeItem(addedId)); + Assert.assertTrue(container.removeItem(newFirstId)); + Assert.assertTrue(container.removeItem(newLastId)); + + Assert.assertFalse( + "Removing non-existing item should indicate failure", + container.removeItem(addedId)); + } + + // addItemAt + if (testAddItemAtWithId) { + container.addItemAt(itemPosition, newItemId); + Assert.assertEquals(itemPosition, container.indexOfId(newItemId)); + Assert.assertEquals(itemPosition + 1, container.indexOfId(itemId)); + Assert.assertEquals(newItemId, + container.getIdByIndex(itemPosition)); + Assert.assertEquals(itemId, + container.getIdByIndex(itemPosition + 1)); + Assert.assertTrue(container.removeItem(newItemId)); + Assert.assertFalse(container.containsId(newItemId)); + + container.addItemAt(0, newItemId); + Assert.assertEquals(0, container.indexOfId(newItemId)); + Assert.assertEquals(itemPosition + 1, container.indexOfId(itemId)); + Assert.assertEquals(newItemId, container.firstItemId()); + Assert.assertEquals(newItemId, container.getIdByIndex(0)); + Assert.assertEquals(itemId, + container.getIdByIndex(itemPosition + 1)); + Assert.assertTrue(container.removeItem(newItemId)); + Assert.assertFalse(container.containsId(newItemId)); + + container.addItemAt(container.size(), newItemId); + Assert.assertEquals(container.size() - 1, + container.indexOfId(newItemId)); + Assert.assertEquals(itemPosition, container.indexOfId(itemId)); + Assert.assertEquals(newItemId, container.lastItemId()); + Assert.assertEquals(newItemId, + container.getIdByIndex(container.size() - 1)); + Assert.assertEquals(itemId, container.getIdByIndex(itemPosition)); + Assert.assertTrue(container.removeItem(newItemId)); + Assert.assertFalse(container.containsId(newItemId)); + } + } + + protected void testContainerFiltering(Container.Filterable container) { + initializeContainer(container); + + // Filter by "contains ab" + SimpleStringFilter filter1 = new SimpleStringFilter( + FULLY_QUALIFIED_NAME, "ab", false, false); + container.addContainerFilter(filter1); + + assertTrue(container.getContainerFilters().size() == 1); + assertEquals(filter1, + container.getContainerFilters().iterator().next()); + + validateContainer(container, "com.vaadin.data.BufferedValidatable", + "com.vaadin.ui.TabSheet", + "com.vaadin.terminal.gwt.client.Focusable", + "com.vaadin.data.Buffered", isFilteredOutItemNull(), 20); + + // Filter by "contains da" (reversed as ad here) + container.removeAllContainerFilters(); + + assertTrue(container.getContainerFilters().isEmpty()); + + SimpleStringFilter filter2 = new SimpleStringFilter( + REVERSE_FULLY_QUALIFIED_NAME, "ad", false, false); + container.addContainerFilter(filter2); + + assertTrue(container.getContainerFilters().size() == 1); + assertEquals(filter2, + container.getContainerFilters().iterator().next()); + + validateContainer(container, "com.vaadin.data.Buffered", + "com.vaadin.server.ComponentSizeValidator", + "com.vaadin.data.util.IndexedContainer", + "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", + isFilteredOutItemNull(), 37); + } + + /** + * Override in subclasses to return false if the container getItem() method + * returns a non-null value for an item that has been filtered out. + * + * @return + */ + protected boolean isFilteredOutItemNull() { + return true; + } + + protected void testContainerSortingAndFiltering( + Container.Sortable sortable) { + Filterable filterable = (Filterable) sortable; + + initializeContainer(sortable); + + // Filter by "contains ab" + filterable.addContainerFilter(new SimpleStringFilter( + FULLY_QUALIFIED_NAME, "ab", false, false)); + + // Must be able to sort based on PROP1 for this test + assertTrue(sortable.getSortableContainerPropertyIds() + .contains(FULLY_QUALIFIED_NAME)); + + sortable.sort(new Object[] { FULLY_QUALIFIED_NAME }, + new boolean[] { true }); + + validateContainer(sortable, "com.vaadin.data.BufferedValidatable", + "com.vaadin.ui.TableFieldFactory", + "com.vaadin.ui.TableFieldFactory", + "com.vaadin.data.util.BeanItem", isFilteredOutItemNull(), 20); + } + + protected void testContainerSorting(Container.Filterable container) { + Container.Sortable sortable = (Sortable) container; + + initializeContainer(container); + + // Must be able to sort based on PROP1 for this test + assertTrue(sortable.getSortableContainerPropertyIds() + .contains(FULLY_QUALIFIED_NAME)); + assertTrue(sortable.getSortableContainerPropertyIds() + .contains(REVERSE_FULLY_QUALIFIED_NAME)); + + sortable.sort(new Object[] { FULLY_QUALIFIED_NAME }, + new boolean[] { true }); + + validateContainer(container, "com.vaadin.Application", + "org.vaadin.test.LastClass", + "com.vaadin.server.ApplicationResource", "blah", true, + sampleData.length); + + sortable.sort(new Object[] { REVERSE_FULLY_QUALIFIED_NAME }, + new boolean[] { true }); + + validateContainer(container, "com.vaadin.server.ApplicationPortlet2", + "com.vaadin.data.util.ObjectProperty", + "com.vaadin.ui.BaseFieldFactory", "blah", true, + sampleData.length); + + } + + protected void initializeContainer(Container container) { + Assert.assertTrue(container.removeAllItems()); + Object[] propertyIds = container.getContainerPropertyIds().toArray(); + for (Object propertyId : propertyIds) { + container.removeContainerProperty(propertyId); + } + + container.addContainerProperty(FULLY_QUALIFIED_NAME, String.class, ""); + container.addContainerProperty(SIMPLE_NAME, String.class, ""); + container.addContainerProperty(REVERSE_FULLY_QUALIFIED_NAME, + String.class, null); + container.addContainerProperty(ID_NUMBER, Integer.class, null); + + for (int i = 0; i < sampleData.length; i++) { + String id = sampleData[i]; + Item item = container.addItem(id); + + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(sampleData[i]); + item.getItemProperty(SIMPLE_NAME) + .setValue(getSimpleName(sampleData[i])); + item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) + .setValue(reverse(sampleData[i])); + item.getItemProperty(ID_NUMBER).setValue(i); + } + } + + protected static String getSimpleName(String name) { + if (name.contains(".")) { + return name.substring(name.lastIndexOf('.') + 1); + } else { + return name; + } + } + + protected static String reverse(String string) { + return new StringBuilder(string).reverse().toString(); + } + + protected final String[] sampleData = { + "com.vaadin.annotations.AutoGenerated", "com.vaadin.Application", + "com.vaadin.data.Buffered", "com.vaadin.data.BufferedValidatable", + "com.vaadin.data.Container", "com.vaadin.data.Item", + "com.vaadin.data.Property", "com.vaadin.data.util.BeanItem", + "com.vaadin.data.util.BeanItemContainer", + "com.vaadin.data.util.ContainerHierarchicalWrapper", + "com.vaadin.data.util.ContainerOrderedWrapper", + "com.vaadin.data.util.DefaultItemSorter", + "com.vaadin.data.util.FilesystemContainer", + "com.vaadin.data.util.Filter", + "com.vaadin.data.util.HierarchicalContainer", + "com.vaadin.data.util.IndexedContainer", + "com.vaadin.data.util.ItemSorter", + "com.vaadin.data.util.MethodProperty", + "com.vaadin.data.util.ObjectProperty", + "com.vaadin.data.util.PropertyFormatter", + "com.vaadin.data.util.PropertysetItem", + "com.vaadin.data.util.QueryContainer", + "com.vaadin.data.util.TextFileProperty", + "com.vaadin.data.Validatable", + "com.vaadin.data.validator.AbstractStringValidator", + "com.vaadin.data.validator.AbstractValidator", + "com.vaadin.data.validator.CompositeValidator", + "com.vaadin.data.validator.DoubleValidator", + "com.vaadin.data.validator.EmailValidator", + "com.vaadin.data.validator.IntegerValidator", + "com.vaadin.data.validator.NullValidator", + "com.vaadin.data.validator.RegexpValidator", + "com.vaadin.data.validator.StringLengthValidator", + "com.vaadin.data.Validator", "com.vaadin.event.Action", + "com.vaadin.event.ComponentEventListener", + "com.vaadin.event.EventRouter", "com.vaadin.event.FieldEvents", + "com.vaadin.event.ItemClickEvent", "com.vaadin.event.LayoutEvents", + "com.vaadin.event.ListenerMethod", + "com.vaadin.event.MethodEventSource", + "com.vaadin.event.MouseEvents", "com.vaadin.event.ShortcutAction", + "com.vaadin.launcher.DemoLauncher", + "com.vaadin.launcher.DevelopmentServerLauncher", + "com.vaadin.launcher.util.BrowserLauncher", + "com.vaadin.service.ApplicationContext", + "com.vaadin.service.FileTypeResolver", + "com.vaadin.server.ApplicationResource", + "com.vaadin.server.ClassResource", + "com.vaadin.server.CompositeErrorMessage", + "com.vaadin.server.DownloadStream", + "com.vaadin.server.ErrorMessage", + "com.vaadin.server.ExternalResource", + "com.vaadin.server.FileResource", + "com.vaadin.terminal.gwt.client.ApplicationConfiguration", + "com.vaadin.terminal.gwt.client.ApplicationConnection", + "com.vaadin.terminal.gwt.client.BrowserInfo", + "com.vaadin.terminal.gwt.client.ClientExceptionHandler", + "com.vaadin.terminal.gwt.client.ComponentDetail", + "com.vaadin.terminal.gwt.client.ComponentDetailMap", + "com.vaadin.terminal.gwt.client.ComponentLocator", + "com.vaadin.terminal.gwt.client.Console", + "com.vaadin.terminal.gwt.client.Container", + "com.vaadin.terminal.gwt.client.ContainerResizedListener", + "com.vaadin.terminal.gwt.client.CSSRule", + "com.vaadin.terminal.gwt.client.DateTimeService", + "com.vaadin.terminal.gwt.client.DefaultWidgetSet", + "com.vaadin.terminal.gwt.client.Focusable", + "com.vaadin.terminal.gwt.client.HistoryImplIEVaadin", + "com.vaadin.terminal.gwt.client.LocaleNotLoadedException", + "com.vaadin.terminal.gwt.client.LocaleService", + "com.vaadin.terminal.gwt.client.MouseEventDetails", + "com.vaadin.terminal.gwt.client.NullConsole", + "com.vaadin.terminal.gwt.client.Paintable", + "com.vaadin.terminal.gwt.client.RenderInformation", + "com.vaadin.terminal.gwt.client.RenderSpace", + "com.vaadin.terminal.gwt.client.StyleConstants", + "com.vaadin.terminal.gwt.client.TooltipInfo", + "com.vaadin.terminal.gwt.client.ui.Action", + "com.vaadin.terminal.gwt.client.ui.ActionOwner", + "com.vaadin.terminal.gwt.client.ui.AlignmentInfo", + "com.vaadin.terminal.gwt.client.ui.CalendarEntry", + "com.vaadin.terminal.gwt.client.ui.ClickEventHandler", + "com.vaadin.terminal.gwt.client.ui.Field", + "com.vaadin.terminal.gwt.client.ui.Icon", + "com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout", + "com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer", + "com.vaadin.terminal.gwt.client.ui.layout.Margins", + "com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler", + "com.vaadin.terminal.gwt.client.ui.MenuBar", + "com.vaadin.terminal.gwt.client.ui.MenuItem", + "com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextToolbar", + "com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler", + "com.vaadin.terminal.gwt.client.ui.SubPartAware", + "com.vaadin.terminal.gwt.client.ui.Table", + "com.vaadin.terminal.gwt.client.ui.TreeAction", + "com.vaadin.terminal.gwt.client.ui.TreeImages", + "com.vaadin.terminal.gwt.client.ui.VAbsoluteLayout", + "com.vaadin.terminal.gwt.client.ui.VAccordion", + "com.vaadin.terminal.gwt.client.ui.VButton", + "com.vaadin.terminal.gwt.client.ui.VCalendarPanel", + "com.vaadin.terminal.gwt.client.ui.VCheckBox", + "com.vaadin.terminal.gwt.client.ui.VContextMenu", + "com.vaadin.terminal.gwt.client.ui.VCssLayout", + "com.vaadin.terminal.gwt.client.ui.VCustomComponent", + "com.vaadin.terminal.gwt.client.ui.VCustomLayout", + "com.vaadin.terminal.gwt.client.ui.VDateField", + "com.vaadin.terminal.gwt.client.ui.VDateFieldCalendar", + "com.vaadin.terminal.gwt.client.ui.VEmbedded", + "com.vaadin.terminal.gwt.client.ui.VFilterSelect", + "com.vaadin.terminal.gwt.client.ui.VForm", + "com.vaadin.terminal.gwt.client.ui.VFormLayout", + "com.vaadin.terminal.gwt.client.ui.VGridLayout", + "com.vaadin.terminal.gwt.client.ui.VHorizontalLayout", + "com.vaadin.terminal.gwt.client.ui.VLabel", + "com.vaadin.terminal.gwt.client.ui.VLink", + "com.vaadin.terminal.gwt.client.ui.VListSelect", + "com.vaadin.terminal.gwt.client.ui.VMarginInfo", + "com.vaadin.terminal.gwt.client.ui.VMenuBar", + "com.vaadin.terminal.gwt.client.ui.VNativeButton", + "com.vaadin.terminal.gwt.client.ui.VNativeSelect", + "com.vaadin.terminal.gwt.client.ui.VNotification", + "com.vaadin.terminal.gwt.client.ui.VOptionGroup", + "com.vaadin.terminal.gwt.client.ui.VOptionGroupBase", + "com.vaadin.terminal.gwt.client.ui.VOrderedLayout", + "com.vaadin.terminal.gwt.client.ui.VOverlay", + "com.vaadin.terminal.gwt.client.ui.VPanel", + "com.vaadin.terminal.gwt.client.ui.VPasswordField", + "com.vaadin.terminal.gwt.client.ui.VPopupCalendar", + "com.vaadin.terminal.gwt.client.ui.VPopupView", + "com.vaadin.terminal.gwt.client.ui.VProgressIndicator", + "com.vaadin.terminal.gwt.client.ui.VRichTextArea", + "com.vaadin.terminal.gwt.client.ui.VScrollTable", + "com.vaadin.terminal.gwt.client.ui.VSlider", + "com.vaadin.terminal.gwt.client.ui.VSplitPanel", + "com.vaadin.terminal.gwt.client.ui.VSplitPanelHorizontal", + "com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical", + "com.vaadin.terminal.gwt.client.ui.VTablePaging", + "com.vaadin.terminal.gwt.client.ui.VTabsheet", + "com.vaadin.terminal.gwt.client.ui.VTabsheetBase", + "com.vaadin.terminal.gwt.client.ui.VTabsheetPanel", + "com.vaadin.terminal.gwt.client.ui.VTextArea", + "com.vaadin.terminal.gwt.client.ui.VTextField", + "com.vaadin.terminal.gwt.client.ui.VTextualDate", + "com.vaadin.terminal.gwt.client.ui.VTime", + "com.vaadin.terminal.gwt.client.ui.VTree", + "com.vaadin.terminal.gwt.client.ui.VTwinColSelect", + "com.vaadin.terminal.gwt.client.ui.VUnknownComponent", + "com.vaadin.terminal.gwt.client.ui.VUpload", + "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", + "com.vaadin.terminal.gwt.client.ui.VVerticalLayout", + "com.vaadin.terminal.gwt.client.ui.VView", + "com.vaadin.terminal.gwt.client.ui.VWindow", + "com.vaadin.terminal.gwt.client.UIDL", + "com.vaadin.terminal.gwt.client.Util", + "com.vaadin.terminal.gwt.client.ValueMap", + "com.vaadin.terminal.gwt.client.VCaption", + "com.vaadin.terminal.gwt.client.VCaptionWrapper", + "com.vaadin.terminal.gwt.client.VDebugConsole", + "com.vaadin.terminal.gwt.client.VErrorMessage", + "com.vaadin.terminal.gwt.client.VTooltip", + "com.vaadin.terminal.gwt.client.VUIDLBrowser", + "com.vaadin.terminal.gwt.client.WidgetMap", + "com.vaadin.terminal.gwt.client.WidgetSet", + "com.vaadin.server.AbstractApplicationPortlet", + "com.vaadin.server.AbstractApplicationServlet", + "com.vaadin.server.AbstractCommunicationManager", + "com.vaadin.server.AbstractWebApplicationContext", + "com.vaadin.server.ApplicationPortlet", + "com.vaadin.server.ApplicationPortlet2", + "com.vaadin.server.ApplicationRunnerServlet", + "com.vaadin.server.ApplicationServlet", + "com.vaadin.server.ChangeVariablesErrorEvent", + "com.vaadin.server.CommunicationManager", + "com.vaadin.server.ComponentSizeValidator", + "com.vaadin.server.Constants", + "com.vaadin.server.GAEApplicationServlet", + "com.vaadin.server.HttpServletRequestListener", + "com.vaadin.server.HttpUploadStream", + "com.vaadin.server.JsonPaintTarget", + "com.vaadin.server.PortletApplicationContext", + "com.vaadin.server.PortletApplicationContext2", + "com.vaadin.server.PortletCommunicationManager", + "com.vaadin.server.PortletRequestListener", + "com.vaadin.server.RestrictedRenderResponse", + "com.vaadin.server.SessionExpiredException", + "com.vaadin.server.SystemMessageException", + "com.vaadin.server.WebApplicationContext", + "com.vaadin.server.WebBrowser", + "com.vaadin.server.widgetsetutils.ClassPathExplorer", + "com.vaadin.server.widgetsetutils.WidgetMapGenerator", + "com.vaadin.server.widgetsetutils.WidgetSetBuilder", + "com.vaadin.server.KeyMapper", "com.vaadin.server.Paintable", + "com.vaadin.server.PaintException", "com.vaadin.server.PaintTarget", + "com.vaadin.server.ParameterHandler", "com.vaadin.server.Resource", + "com.vaadin.server.Scrollable", "com.vaadin.server.Sizeable", + "com.vaadin.server.StreamResource", "com.vaadin.server.SystemError", + "com.vaadin.server.Terminal", "com.vaadin.server.ThemeResource", + "com.vaadin.server.UploadStream", "com.vaadin.server.URIHandler", + "com.vaadin.server.UserError", "com.vaadin.server.VariableOwner", + "com.vaadin.tools.ReflectTools", + "com.vaadin.tools.WidgetsetCompiler", + "com.vaadin.ui.AbsoluteLayout", "com.vaadin.ui.AbstractComponent", + "com.vaadin.ui.AbstractComponentContainer", + "com.vaadin.ui.AbstractField", "com.vaadin.ui.AbstractLayout", + "com.vaadin.ui.AbstractOrderedLayout", + "com.vaadin.ui.AbstractSelect", "com.vaadin.ui.Accordion", + "com.vaadin.ui.Alignment", "com.vaadin.ui.AlignmentUtils", + "com.vaadin.ui.BaseFieldFactory", "com.vaadin.ui.Button", + "com.vaadin.ui.CheckBox", "com.vaadin.ui.ClientWidget", + "com.vaadin.ui.ComboBox", "com.vaadin.ui.Component", + "com.vaadin.ui.ComponentContainer", "com.vaadin.ui.CssLayout", + "com.vaadin.ui.CustomComponent", "com.vaadin.ui.CustomLayout", + "com.vaadin.ui.DateField", "com.vaadin.ui.DefaultFieldFactory", + "com.vaadin.ui.Embedded", "com.vaadin.ui.ExpandLayout", + "com.vaadin.ui.Field", "com.vaadin.ui.FieldFactory", + "com.vaadin.ui.Form", "com.vaadin.ui.FormFieldFactory", + "com.vaadin.ui.FormLayout", "com.vaadin.ui.GridLayout", + "com.vaadin.ui.HorizontalLayout", "com.vaadin.ui.InlineDateField", + "com.vaadin.ui.Label", "com.vaadin.ui.Layout", "com.vaadin.ui.Link", + "com.vaadin.ui.ListSelect", "com.vaadin.ui.LoginForm", + "com.vaadin.ui.MenuBar", "com.vaadin.ui.NativeButton", + "com.vaadin.ui.NativeSelect", "com.vaadin.ui.OptionGroup", + "com.vaadin.ui.OrderedLayout", "com.vaadin.ui.Panel", + "com.vaadin.ui.PopupDateField", "com.vaadin.ui.PopupView", + "com.vaadin.ui.ProgressIndicator", "com.vaadin.ui.RichTextArea", + "com.vaadin.ui.Select", "com.vaadin.ui.Slider", + "com.vaadin.ui.SplitPanel", "com.vaadin.ui.Table", + "com.vaadin.ui.TableFieldFactory", "com.vaadin.ui.TabSheet", + "com.vaadin.ui.TextField", "com.vaadin.ui.Tree", + "com.vaadin.ui.TwinColSelect", "com.vaadin.ui.Upload", + "com.vaadin.ui.UriFragmentUtility", "com.vaadin.ui.VerticalLayout", + "com.vaadin.ui.Window", "com.vaadin.util.SerializerHelper", + "org.vaadin.test.LastClass" }; +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java new file mode 100644 index 0000000000..f3eda74100 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java @@ -0,0 +1,289 @@ +package com.vaadin.data.util; + +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 java.util.Collection; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Hierarchical; +import com.vaadin.data.Container.Sortable; +import com.vaadin.data.Item; + +public abstract class AbstractHierarchicalContainerTestBase + extends AbstractContainerTestBase { + + /** + * @param container + * The container to validate + * @param expectedFirstItemId + * Expected first item id + * @param expectedLastItemId + * Expected last item id + * @param itemIdInSet + * An item id that is in the container + * @param itemIdNotInSet + * An item id that is not in the container + * @param checkGetItemNull + * true if getItem() should return null for itemIdNotInSet, false + * to skip the check (container.containsId() is checked in any + * case) + * @param expectedSize + * Expected number of items in the container. Not related to + * hierarchy. + * @param expectedTraversalSize + * Expected number of items found when traversing from the roots + * down to all available nodes. + * @param expectedRootSize + * Expected number of root items + * @param rootsHaveChildren + * true if all roots have children, false otherwise (skips some + * asserts) + */ + protected void validateHierarchicalContainer(Hierarchical container, + Object expectedFirstItemId, Object expectedLastItemId, + Object itemIdInSet, Object itemIdNotInSet, boolean checkGetItemNull, + int expectedSize, int expectedRootSize, boolean rootsHaveChildren) { + + validateContainer(container, expectedFirstItemId, expectedLastItemId, + itemIdInSet, itemIdNotInSet, checkGetItemNull, expectedSize); + + // rootItemIds + Collection rootIds = container.rootItemIds(); + assertEquals(expectedRootSize, rootIds.size()); + + for (Object rootId : rootIds) { + // All roots must be in container + assertTrue(container.containsId(rootId)); + + // All roots must have no parent + assertNull(container.getParent(rootId)); + + // all roots must be roots + assertTrue(container.isRoot(rootId)); + + if (rootsHaveChildren) { + // all roots have children allowed in this case + assertTrue(container.areChildrenAllowed(rootId)); + + // all roots have children in this case + Collection children = container.getChildren(rootId); + assertNotNull(rootId + " should have children", children); + assertTrue(rootId + " should have children", + (children.size() > 0)); + // getParent + for (Object childId : children) { + assertEquals(container.getParent(childId), rootId); + } + + } + } + + // isRoot should return false for unknown items + assertFalse(container.isRoot(itemIdNotInSet)); + + // hasChildren should return false for unknown items + assertFalse(container.hasChildren(itemIdNotInSet)); + + // areChildrenAllowed should return false for unknown items + assertFalse(container.areChildrenAllowed(itemIdNotInSet)); + + // removeItem of unknown items should return false + assertFalse(container.removeItem(itemIdNotInSet)); + + assertEquals(expectedSize, countNodes(container)); + + validateHierarchy(container); + } + + private int countNodes(Hierarchical container) { + int totalNodes = 0; + for (Object rootId : container.rootItemIds()) { + totalNodes += countNodes(container, rootId); + } + + return totalNodes; + } + + private int countNodes(Hierarchical container, Object itemId) { + int nodes = 1; // This + Collection children = container.getChildren(itemId); + if (children != null) { + for (Object id : children) { + nodes += countNodes(container, id); + } + } + + return nodes; + } + + private void validateHierarchy(Hierarchical container) { + for (Object rootId : container.rootItemIds()) { + validateHierarchy(container, rootId, null); + } + } + + private void validateHierarchy(Hierarchical container, Object itemId, + Object parentId) { + Collection children = container.getChildren(itemId); + + // getParent + assertEquals(container.getParent(itemId), parentId); + + if (!container.areChildrenAllowed(itemId)) { + // If no children is allowed the item should have no children + assertFalse(container.hasChildren(itemId)); + assertTrue(children == null || children.size() == 0); + + return; + } + if (children != null) { + for (Object id : children) { + validateHierarchy(container, id, itemId); + } + } + } + + protected void testHierarchicalContainer(Container.Hierarchical container) { + initializeContainer(container); + + int packages = 21 + 3; + int expectedSize = sampleData.length + packages; + validateHierarchicalContainer(container, "com", + "org.vaadin.test.LastClass", + "com.vaadin.server.ApplicationResource", "blah", true, + expectedSize, 2, true); + + } + + protected void testHierarchicalSorting(Container.Hierarchical container) { + Container.Sortable sortable = (Sortable) container; + + initializeContainer(container); + + // Must be able to sort based on PROP1 and PROP2 for this test + assertTrue(sortable.getSortableContainerPropertyIds() + .contains(FULLY_QUALIFIED_NAME)); + assertTrue(sortable.getSortableContainerPropertyIds() + .contains(REVERSE_FULLY_QUALIFIED_NAME)); + + sortable.sort(new Object[] { FULLY_QUALIFIED_NAME }, + new boolean[] { true }); + + int packages = 21 + 3; + int expectedSize = sampleData.length + packages; + validateHierarchicalContainer(container, "com", + "org.vaadin.test.LastClass", + "com.vaadin.server.ApplicationResource", "blah", true, + expectedSize, 2, true); + + sortable.sort(new Object[] { REVERSE_FULLY_QUALIFIED_NAME }, + new boolean[] { true }); + + validateHierarchicalContainer(container, + "com.vaadin.server.ApplicationPortlet2", + "com.vaadin.data.util.ObjectProperty", + "com.vaadin.server.ApplicationResource", "blah", true, + expectedSize, 2, true); + + } + + protected void initializeContainer(Container.Hierarchical container) { + container.removeAllItems(); + Object[] propertyIds = container.getContainerPropertyIds().toArray(); + for (Object propertyId : propertyIds) { + container.removeContainerProperty(propertyId); + } + + container.addContainerProperty(FULLY_QUALIFIED_NAME, String.class, ""); + container.addContainerProperty(SIMPLE_NAME, String.class, ""); + container.addContainerProperty(REVERSE_FULLY_QUALIFIED_NAME, + String.class, null); + container.addContainerProperty(ID_NUMBER, Integer.class, null); + + for (int i = 0; i < sampleData.length; i++) { + String id = sampleData[i]; + + // Add path as parent + String paths[] = id.split("\\."); + String path = paths[0]; + // Adds "com" and other items multiple times so should return null + // for all but the first time + if (container.addItem(path) != null) { + assertTrue(container.setChildrenAllowed(path, false)); + Item item = container.getItem(path); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(path); + item.getItemProperty(SIMPLE_NAME).setValue(getSimpleName(path)); + item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) + .setValue(reverse(path)); + item.getItemProperty(ID_NUMBER).setValue(1); + } + for (int j = 1; j < paths.length; j++) { + String parent = path; + path = path + "." + paths[j]; + + // Adds "com" and other items multiple times so should return + // null for all but the first time + if (container.addItem(path) != null) { + assertTrue(container.setChildrenAllowed(path, false)); + + Item item = container.getItem(path); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(path); + item.getItemProperty(SIMPLE_NAME) + .setValue(getSimpleName(path)); + item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) + .setValue(reverse(path)); + item.getItemProperty(ID_NUMBER).setValue(1); + + } + assertTrue(container.setChildrenAllowed(parent, true)); + assertTrue("Failed to set " + parent + " as parent for " + path, + container.setParent(path, parent)); + } + + Item item = container.getItem(id); + assertNotNull(item); + String parent = id.substring(0, id.lastIndexOf('.')); + assertTrue(container.setParent(id, parent)); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(sampleData[i]); + item.getItemProperty(SIMPLE_NAME) + .setValue(getSimpleName(sampleData[i])); + item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) + .setValue(reverse(sampleData[i])); + item.getItemProperty(ID_NUMBER).setValue(i % 2); + } + } + + protected void testRemoveHierarchicalWrapperSubtree( + Container.Hierarchical container) { + initializeContainer(container); + + // remove root item + removeItemRecursively(container, "org"); + + int packages = 21 + 3 - 3; + int expectedSize = sampleData.length + packages - 1; + + validateContainer(container, "com", "com.vaadin.util.SerializerHelper", + "com.vaadin.server.ApplicationResource", "blah", true, + expectedSize); + + // rootItemIds + Collection rootIds = container.rootItemIds(); + assertEquals(1, rootIds.size()); + } + + private void removeItemRecursively(Container.Hierarchical container, + Object itemId) { + if (container instanceof ContainerHierarchicalWrapper) { + ((ContainerHierarchicalWrapper) container) + .removeItemRecursively("org"); + } else { + HierarchicalContainer.removeItemRecursively(container, itemId); + } + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java new file mode 100644 index 0000000000..3858504bc7 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java @@ -0,0 +1,6 @@ +package com.vaadin.data.util; + +public abstract class AbstractInMemoryContainerTestBase + extends AbstractContainerTestBase { + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/BeanContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanContainerTest.java new file mode 100644 index 0000000000..bdf6ba1958 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/BeanContainerTest.java @@ -0,0 +1,516 @@ +package com.vaadin.data.util; + +import static org.junit.Assert.assertEquals; +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.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import com.vaadin.data.util.AbstractBeanContainer.BeanIdResolver; + +public class BeanContainerTest extends AbstractBeanContainerTestBase { + + protected static class PersonNameResolver + implements BeanIdResolver { + + @Override + public String getIdForBean(Person bean) { + return bean != null ? bean.getName() : null; + } + + } + + protected static class NullResolver + implements BeanIdResolver { + + @Override + public String getIdForBean(Person bean) { + return null; + } + + } + + private Map nameToBean = new LinkedHashMap(); + + private BeanContainer getContainer() { + return new BeanContainer(ClassName.class); + } + + @Before + public void setUp() { + nameToBean.clear(); + + for (int i = 0; i < sampleData.length; i++) { + ClassName className = new ClassName(sampleData[i], i); + nameToBean.put(sampleData[i], className); + } + } + + @Override + @SuppressWarnings("unchecked") + protected void initializeContainer(Container container) { + BeanContainer beanItemContainer = (BeanContainer) container; + + beanItemContainer.removeAllItems(); + + for (Entry entry : nameToBean.entrySet()) { + beanItemContainer.addItem(entry.getKey(), entry.getValue()); + } + } + + @Override + protected boolean isFilteredOutItemNull() { + return false; + } + + @Test + public void testGetType_existingProperty_typeReturned() { + BeanContainer container = getContainer(); + Assert.assertEquals( + "Unexpected type is returned for property 'simpleName'", + String.class, container.getType("simpleName")); + } + + @Test + public void testGetType_notExistingProperty_nullReturned() { + BeanContainer container = getContainer(); + Assert.assertNull("Not null type is returned for property ''", + container.getType("")); + } + + @Test + public void testBasicOperations() { + testBasicContainerOperations(getContainer()); + } + + @Test + public void testFiltering() { + testContainerFiltering(getContainer()); + } + + @Test + public void testSorting() { + testContainerSorting(getContainer()); + } + + @Test + public void testSortingAndFiltering() { + testContainerSortingAndFiltering(getContainer()); + } + + // duplicated from parent class and modified - adding items to + // BeanContainer differs from other containers + @Test + public void testContainerOrdered() { + BeanContainer container = new BeanContainer( + String.class); + + String id = "test1"; + + Item item = container.addItem(id, "value"); + assertNotNull(item); + + assertEquals(id, container.firstItemId()); + assertEquals(id, container.lastItemId()); + + // isFirstId + assertTrue(container.isFirstId(id)); + assertTrue(container.isFirstId(container.firstItemId())); + // isLastId + assertTrue(container.isLastId(id)); + assertTrue(container.isLastId(container.lastItemId())); + + // Add a new item before the first + // addItemAfter + String newFirstId = "newFirst"; + item = container.addItemAfter(null, newFirstId, "newFirstValue"); + assertNotNull(item); + assertNotNull(container.getItem(newFirstId)); + + // isFirstId + assertTrue(container.isFirstId(newFirstId)); + assertTrue(container.isFirstId(container.firstItemId())); + // isLastId + assertTrue(container.isLastId(id)); + assertTrue(container.isLastId(container.lastItemId())); + + // nextItemId + assertEquals(id, container.nextItemId(newFirstId)); + assertNull(container.nextItemId(id)); + assertNull(container.nextItemId("not-in-container")); + + // prevItemId + assertEquals(newFirstId, container.prevItemId(id)); + assertNull(container.prevItemId(newFirstId)); + assertNull(container.prevItemId("not-in-container")); + + // addItemAfter(IDTYPE, IDTYPE, BT) + String newSecondItemId = "newSecond"; + item = container.addItemAfter(newFirstId, newSecondItemId, + "newSecondValue"); + // order is now: newFirstId, newSecondItemId, id + assertNotNull(item); + assertNotNull(container.getItem(newSecondItemId)); + assertEquals(id, container.nextItemId(newSecondItemId)); + assertEquals(newFirstId, container.prevItemId(newSecondItemId)); + + // addItemAfter(IDTYPE, IDTYPE, BT) + String fourthId = "id of the fourth item"; + Item fourth = container.addItemAfter(newFirstId, fourthId, + "fourthValue"); + // order is now: newFirstId, fourthId, newSecondItemId, id + assertNotNull(fourth); + assertEquals(fourth, container.getItem(fourthId)); + assertEquals(newSecondItemId, container.nextItemId(fourthId)); + assertEquals(newFirstId, container.prevItemId(fourthId)); + + // addItemAfter(IDTYPE, IDTYPE, BT) + String fifthId = "fifth"; + Item fifth = container.addItemAfter(null, fifthId, "fifthValue"); + // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id + assertNotNull(fifth); + assertEquals(fifth, container.getItem(fifthId)); + assertEquals(newFirstId, container.nextItemId(fifthId)); + assertNull(container.prevItemId(fifthId)); + + } + + // TODO test Container.Indexed interface operation - testContainerIndexed()? + + @Test + public void testAddItemAt() { + BeanContainer container = new BeanContainer( + String.class); + + container.addItem("id1", "value1"); + // id1 + container.addItemAt(0, "id2", "value2"); + // id2, id1 + container.addItemAt(1, "id3", "value3"); + // id2, id3, id1 + container.addItemAt(container.size(), "id4", "value4"); + // id2, id3, id1, id4 + + assertNull(container.addItemAt(-1, "id5", "value5")); + assertNull(container.addItemAt(container.size() + 1, "id6", "value6")); + + assertEquals(4, container.size()); + assertEquals("id2", container.getIdByIndex(0)); + assertEquals("id3", container.getIdByIndex(1)); + assertEquals("id1", container.getIdByIndex(2)); + assertEquals("id4", container.getIdByIndex(3)); + } + + @Test + public void testUnsupportedMethods() { + BeanContainer container = new BeanContainer( + Person.class); + container.addItem("John", new Person("John")); + + try { + container.addItem(); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addItem(null); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addItemAfter(null, null); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addItemAfter(new Person("Jane")); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addItemAt(0); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addItemAt(0, new Person("Jane")); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addContainerProperty("lastName", String.class, ""); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + assertEquals(1, container.size()); + } + + @Test + public void testRemoveContainerProperty() { + BeanContainer container = new BeanContainer( + Person.class); + container.setBeanIdResolver(new PersonNameResolver()); + container.addBean(new Person("John")); + + Assert.assertEquals("John", + container.getContainerProperty("John", "name").getValue()); + Assert.assertTrue(container.removeContainerProperty("name")); + Assert.assertNull(container.getContainerProperty("John", "name")); + + Assert.assertNotNull(container.getItem("John")); + // property removed also from item + Assert.assertNull(container.getItem("John").getItemProperty("name")); + } + + @Test + public void testAddNullBeans() { + BeanContainer container = new BeanContainer( + Person.class); + + assertNull(container.addItem("id1", null)); + assertNull(container.addItemAfter(null, "id2", null)); + assertNull(container.addItemAt(0, "id3", null)); + + assertEquals(0, container.size()); + } + + @Test + public void testAddNullId() { + BeanContainer container = new BeanContainer( + Person.class); + + Person john = new Person("John"); + + assertNull(container.addItem(null, john)); + assertNull(container.addItemAfter(null, null, john)); + assertNull(container.addItemAt(0, null, john)); + + assertEquals(0, container.size()); + } + + @Test + public void testEmptyContainer() { + BeanContainer container = new BeanContainer( + Person.class); + + assertNull(container.firstItemId()); + assertNull(container.lastItemId()); + + assertEquals(0, container.size()); + + // could test more about empty container + } + + @Test + public void testAddBeanWithoutResolver() { + BeanContainer container = new BeanContainer( + Person.class); + + try { + container.addBean(new Person("John")); + Assert.fail(); + } catch (IllegalStateException e) { + // should get exception + } + try { + container.addBeanAfter(null, new Person("Jane")); + Assert.fail(); + } catch (IllegalStateException e) { + // should get exception + } + try { + container.addBeanAt(0, new Person("Jack")); + Assert.fail(); + } catch (IllegalStateException e) { + // should get exception + } + try { + container + .addAll(Arrays.asList(new Person[] { new Person("Jack") })); + Assert.fail(); + } catch (IllegalStateException e) { + // should get exception + } + + assertEquals(0, container.size()); + } + + @Test + public void testAddAllWithNullItemId() { + BeanContainer container = new BeanContainer( + Person.class); + // resolver that returns null as item id + container.setBeanIdResolver( + new BeanIdResolver() { + + @Override + public String getIdForBean(Person bean) { + return bean.getName(); + } + }); + + List persons = new ArrayList(); + persons.add(new Person("John")); + persons.add(new Person("Marc")); + persons.add(new Person(null)); + persons.add(new Person("foo")); + + try { + container.addAll(persons); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + + container.removeAllItems(); + persons.remove(2); + container.addAll(persons); + assertEquals(3, container.size()); + } + + @Test + public void testAddBeanWithNullResolver() { + BeanContainer container = new BeanContainer( + Person.class); + // resolver that returns null as item id + container.setBeanIdResolver(new NullResolver()); + + try { + container.addBean(new Person("John")); + Assert.fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + try { + container.addBeanAfter(null, new Person("Jane")); + Assert.fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + try { + container.addBeanAt(0, new Person("Jack")); + Assert.fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + + assertEquals(0, container.size()); + } + + @Test + public void testAddBeanWithResolver() { + BeanContainer container = new BeanContainer( + Person.class); + container.setBeanIdResolver(new PersonNameResolver()); + + assertNotNull(container.addBean(new Person("John"))); + assertNotNull(container.addBeanAfter(null, new Person("Jane"))); + assertNotNull(container.addBeanAt(0, new Person("Jack"))); + + container.addAll(Arrays.asList( + new Person[] { new Person("Jill"), new Person("Joe") })); + + assertTrue(container.containsId("John")); + assertTrue(container.containsId("Jane")); + assertTrue(container.containsId("Jack")); + assertTrue(container.containsId("Jill")); + assertTrue(container.containsId("Joe")); + assertEquals(3, container.indexOfId("Jill")); + assertEquals(4, container.indexOfId("Joe")); + assertEquals(5, container.size()); + } + + @Test + public void testAddNullBeansWithResolver() { + BeanContainer container = new BeanContainer( + Person.class); + container.setBeanIdResolver(new PersonNameResolver()); + + assertNull(container.addBean(null)); + assertNull(container.addBeanAfter(null, null)); + assertNull(container.addBeanAt(0, null)); + + assertEquals(0, container.size()); + } + + @Test + public void testAddBeanWithPropertyResolver() { + BeanContainer container = new BeanContainer( + Person.class); + container.setBeanIdProperty("name"); + + assertNotNull(container.addBean(new Person("John"))); + assertNotNull(container.addBeanAfter(null, new Person("Jane"))); + assertNotNull(container.addBeanAt(0, new Person("Jack"))); + + container.addAll(Arrays.asList( + new Person[] { new Person("Jill"), new Person("Joe") })); + + assertTrue(container.containsId("John")); + assertTrue(container.containsId("Jane")); + assertTrue(container.containsId("Jack")); + assertTrue(container.containsId("Jill")); + assertTrue(container.containsId("Joe")); + assertEquals(3, container.indexOfId("Jill")); + assertEquals(4, container.indexOfId("Joe")); + assertEquals(5, container.size()); + } + + @Test + public void testAddNestedContainerProperty() { + BeanContainer container = new BeanContainer( + NestedMethodPropertyTest.Person.class); + container.setBeanIdProperty("name"); + + container.addBean(new NestedMethodPropertyTest.Person("John", + new NestedMethodPropertyTest.Address("Ruukinkatu 2-4", 20540))); + + assertTrue(container.addNestedContainerProperty("address.street")); + assertEquals("Ruukinkatu 2-4", container + .getContainerProperty("John", "address.street").getValue()); + } + + @Test + public void testNestedContainerPropertyWithNullBean() { + BeanContainer container = new BeanContainer( + NestedMethodPropertyTest.Person.class); + container.setBeanIdProperty("name"); + + container.addBean(new NestedMethodPropertyTest.Person("John", null)); + assertTrue(container + .addNestedContainerProperty("address.postalCodeObject")); + assertTrue(container.addNestedContainerProperty("address.street")); + // the nested properties added with allowNullBean setting should return + // null + assertNull(container.getContainerProperty("John", "address.street") + .getValue()); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java new file mode 100644 index 0000000000..a5bdcc7cf9 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java @@ -0,0 +1,150 @@ +package com.vaadin.data.util; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +public class BeanItemContainerGenerator { + + public static class PortableRandom { + private final static long multiplier = 0x5DEECE66DL; + private final static long addend = 0xBL; + private final static long mask = (1L << 48) - 1; + private AtomicLong seed; + + public PortableRandom(long seed) { + this.seed = new AtomicLong(0L); + setSeed(seed); + } + + synchronized public void setSeed(long seed) { + seed = (seed ^ multiplier) & mask; + this.seed.set(seed); + } + + public int nextInt(int n) { + if (n <= 0) { + throw new IllegalArgumentException("n must be positive"); + } + + if ((n & -n) == n) { + return (int) ((n * (long) next(31)) >> 31); + } + + int bits, val; + do { + bits = next(31); + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; + } + + protected int next(int bits) { + long oldseed, nextseed; + AtomicLong seed = this.seed; + do { + oldseed = seed.get(); + nextseed = (oldseed * multiplier + addend) & mask; + } while (!seed.compareAndSet(oldseed, nextseed)); + return (int) (nextseed >>> (48 - bits)); + } + + public boolean nextBoolean() { + return next(1) != 0; + } + + } + + public static BeanItemContainer createContainer(int size) { + return createContainer(size, new Date().getTime()); + } + + public static BeanItemContainer createContainer(int size, + long seed) { + + BeanItemContainer container = new BeanItemContainer( + TestBean.class); + PortableRandom r = new PortableRandom(seed); + for (int i = 0; i < size; i++) { + container.addBean(new TestBean(r)); + } + + return container; + + } + + public static class TestBean { + private String name, address, city, country; + private int age, shoesize; + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public int getShoesize() { + return shoesize; + } + + public void setShoesize(int shoesize) { + this.shoesize = shoesize; + } + + public TestBean(PortableRandom r) { + age = r.nextInt(100) + 5; + shoesize = r.nextInt(10) + 35; + name = createRandomString(r, r.nextInt(5) + 5); + address = createRandomString(r, r.nextInt(15) + 5) + " " + + r.nextInt(100) + 1; + city = createRandomString(r, r.nextInt(7) + 3); + if (r.nextBoolean()) { + country = createRandomString(r, r.nextInt(4) + 4); + } + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + } + + public static String createRandomString(PortableRandom r, int len) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < len; i++) { + b.append((char) (r.nextInt('z' - 'a') + 'a')); + } + + return b.toString(); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java new file mode 100644 index 0000000000..4f4e35258f --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java @@ -0,0 +1,166 @@ +package com.vaadin.data.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +public class BeanItemContainerSortTest { + public class Person { + private String name; + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + private int age; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + public class Parent extends Person { + private Set children = new HashSet(); + + public void setChildren(Set children) { + this.children = children; + } + + public Set getChildren() { + return children; + } + } + + String[] names = new String[] { "Antti", "Ville", "Sirkka", "Jaakko", + "Pekka", "John" }; + int[] ages = new int[] { 10, 20, 50, 12, 64, 67 }; + String[] sortedByAge = new String[] { names[0], names[3], names[1], + names[2], names[4], names[5] }; + + public BeanItemContainer getContainer() { + BeanItemContainer bc = new BeanItemContainer( + Person.class); + for (int i = 0; i < names.length; i++) { + Person p = new Person(); + p.setName(names[i]); + p.setAge(ages[i]); + bc.addBean(p); + } + return bc; + + } + + public BeanItemContainer getParentContainer() { + BeanItemContainer bc = new BeanItemContainer( + Parent.class); + for (int i = 0; i < names.length; i++) { + Parent p = new Parent(); + p.setName(names[i]); + p.setAge(ages[i]); + bc.addBean(p); + } + return bc; + } + + @Test + public void testSort() { + testSort(true); + } + + public void testSort(boolean b) { + BeanItemContainer container = getContainer(); + container.sort(new Object[] { "name" }, new boolean[] { b }); + + List asList = Arrays.asList(names); + Collections.sort(asList); + if (!b) { + Collections.reverse(asList); + } + + int i = 0; + for (String string : asList) { + Person idByIndex = container.getIdByIndex(i++); + Assert.assertTrue(container.containsId(idByIndex)); + Assert.assertEquals(string, idByIndex.getName()); + } + } + + @Test + public void testReverseSort() { + testSort(false); + } + + @Test + public void primitiveSorting() { + BeanItemContainer container = getContainer(); + container.sort(new Object[] { "age" }, new boolean[] { true }); + + int i = 0; + for (String string : sortedByAge) { + Person idByIndex = container.getIdByIndex(i++); + Assert.assertTrue(container.containsId(idByIndex)); + Assert.assertEquals(string, idByIndex.getName()); + } + } + + @Test + public void customSorting() { + BeanItemContainer container = getContainer(); + + // custom sorter using the reverse order + container.setItemSorter(new DefaultItemSorter() { + @Override + public int compare(Object o1, Object o2) { + return -super.compare(o1, o2); + } + }); + + container.sort(new Object[] { "age" }, new boolean[] { true }); + + int i = container.size() - 1; + for (String string : sortedByAge) { + Person idByIndex = container.getIdByIndex(i--); + Assert.assertTrue(container.containsId(idByIndex)); + Assert.assertEquals(string, idByIndex.getName()); + } + } + + @Test + public void testGetSortableProperties() { + BeanItemContainer container = getContainer(); + + Collection sortablePropertyIds = container + .getSortableContainerPropertyIds(); + Assert.assertEquals(2, sortablePropertyIds.size()); + Assert.assertTrue(sortablePropertyIds.contains("name")); + Assert.assertTrue(sortablePropertyIds.contains("age")); + } + + @Test + public void testGetNonSortableProperties() { + BeanItemContainer container = getParentContainer(); + + Assert.assertEquals(3, container.getContainerPropertyIds().size()); + + Collection sortablePropertyIds = container + .getSortableContainerPropertyIds(); + Assert.assertEquals(2, sortablePropertyIds.size()); + Assert.assertTrue(sortablePropertyIds.contains("name")); + Assert.assertTrue(sortablePropertyIds.contains("age")); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java new file mode 100644 index 0000000000..19b0835fd6 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java @@ -0,0 +1,997 @@ +package com.vaadin.data.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Indexed.ItemAddEvent; +import com.vaadin.data.Container.Indexed.ItemRemoveEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Item; +import com.vaadin.data.util.NestedMethodPropertyTest.Address; +import com.vaadin.data.util.filter.Compare; + +/** + * Test basic functionality of BeanItemContainer. + * + * Most sorting related tests are in {@link BeanItemContainerSortTest}. + */ +public class BeanItemContainerTest extends AbstractBeanContainerTestBase { + + // basics from the common container test + + private Map nameToBean = new LinkedHashMap(); + + private BeanItemContainer getContainer() { + return new BeanItemContainer(ClassName.class); + } + + @Before + public void setUp() { + nameToBean.clear(); + + for (int i = 0; i < sampleData.length; i++) { + ClassName className = new ClassName(sampleData[i], i); + nameToBean.put(sampleData[i], className); + } + } + + @Override + @SuppressWarnings("unchecked") + protected void initializeContainer(Container container) { + BeanItemContainer beanItemContainer = (BeanItemContainer) container; + + beanItemContainer.removeAllItems(); + + Iterator it = nameToBean.values().iterator(); + while (it.hasNext()) { + beanItemContainer.addBean(it.next()); + } + } + + @Override + protected void validateContainer(Container container, + Object expectedFirstItemId, Object expectedLastItemId, + Object itemIdInSet, Object itemIdNotInSet, boolean checkGetItemNull, + int expectedSize) { + Object notInSet = nameToBean.get(itemIdNotInSet); + if (notInSet == null && itemIdNotInSet != null) { + notInSet = new ClassName(String.valueOf(itemIdNotInSet), 9999); + } + super.validateContainer(container, nameToBean.get(expectedFirstItemId), + nameToBean.get(expectedLastItemId), nameToBean.get(itemIdInSet), + notInSet, checkGetItemNull, expectedSize); + } + + @Override + protected boolean isFilteredOutItemNull() { + return false; + } + + @Test + public void testGetType_existingProperty_typeReturned() { + BeanItemContainer container = getContainer(); + Assert.assertEquals( + "Unexpected type is returned for property 'simpleName'", + String.class, container.getType("simpleName")); + } + + @Test + public void testGetType_notExistingProperty_nullReturned() { + BeanItemContainer container = getContainer(); + Assert.assertNull("Not null type is returned for property ''", + container.getType("")); + } + + @Test + public void testBasicOperations() { + testBasicContainerOperations(getContainer()); + } + + @Test + public void testFiltering() { + testContainerFiltering(getContainer()); + } + + @Test + public void testSorting() { + testContainerSorting(getContainer()); + } + + @Test + public void testSortingAndFiltering() { + testContainerSortingAndFiltering(getContainer()); + } + + // duplicated from parent class and modified - adding items to + // BeanItemContainer differs from other containers + @Test + public void testContainerOrdered() { + BeanItemContainer container = new BeanItemContainer( + String.class); + + String id = "test1"; + + Item item = container.addBean(id); + assertNotNull(item); + + assertEquals(id, container.firstItemId()); + assertEquals(id, container.lastItemId()); + + // isFirstId + assertTrue(container.isFirstId(id)); + assertTrue(container.isFirstId(container.firstItemId())); + // isLastId + assertTrue(container.isLastId(id)); + assertTrue(container.isLastId(container.lastItemId())); + + // Add a new item before the first + // addItemAfter + String newFirstId = "newFirst"; + item = container.addItemAfter(null, newFirstId); + assertNotNull(item); + assertNotNull(container.getItem(newFirstId)); + + // isFirstId + assertTrue(container.isFirstId(newFirstId)); + assertTrue(container.isFirstId(container.firstItemId())); + // isLastId + assertTrue(container.isLastId(id)); + assertTrue(container.isLastId(container.lastItemId())); + + // nextItemId + assertEquals(id, container.nextItemId(newFirstId)); + assertNull(container.nextItemId(id)); + assertNull(container.nextItemId("not-in-container")); + + // prevItemId + assertEquals(newFirstId, container.prevItemId(id)); + assertNull(container.prevItemId(newFirstId)); + assertNull(container.prevItemId("not-in-container")); + + // addItemAfter(Object) + String newSecondItemId = "newSecond"; + item = container.addItemAfter(newFirstId, newSecondItemId); + // order is now: newFirstId, newSecondItemId, id + assertNotNull(item); + assertNotNull(container.getItem(newSecondItemId)); + assertEquals(id, container.nextItemId(newSecondItemId)); + assertEquals(newFirstId, container.prevItemId(newSecondItemId)); + + // addItemAfter(Object,Object) + String fourthId = "id of the fourth item"; + Item fourth = container.addItemAfter(newFirstId, fourthId); + // order is now: newFirstId, fourthId, newSecondItemId, id + assertNotNull(fourth); + assertEquals(fourth, container.getItem(fourthId)); + assertEquals(newSecondItemId, container.nextItemId(fourthId)); + assertEquals(newFirstId, container.prevItemId(fourthId)); + + // addItemAfter(Object,Object) + Object fifthId = "fifth"; + Item fifth = container.addItemAfter(null, fifthId); + // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id + assertNotNull(fifth); + assertEquals(fifth, container.getItem(fifthId)); + assertEquals(newFirstId, container.nextItemId(fifthId)); + assertNull(container.prevItemId(fifthId)); + + } + + @Test + public void testContainerIndexed() { + testContainerIndexed(getContainer(), nameToBean.get(sampleData[2]), 2, + false, new ClassName("org.vaadin.test.Test", 8888), true); + } + + @SuppressWarnings("deprecation") + @Test + public void testCollectionConstructors() { + List classNames = new ArrayList(); + classNames.add(new ClassName("a.b.c.Def", 1)); + classNames.add(new ClassName("a.b.c.Fed", 2)); + classNames.add(new ClassName("b.c.d.Def", 3)); + + // note that this constructor is problematic, users should use the + // version that + // takes the bean class as a parameter + BeanItemContainer container = new BeanItemContainer( + classNames); + + Assert.assertEquals(3, container.size()); + Assert.assertEquals(classNames.get(0), container.firstItemId()); + Assert.assertEquals(classNames.get(1), container.getIdByIndex(1)); + Assert.assertEquals(classNames.get(2), container.lastItemId()); + + BeanItemContainer container2 = new BeanItemContainer( + ClassName.class, classNames); + + Assert.assertEquals(3, container2.size()); + Assert.assertEquals(classNames.get(0), container2.firstItemId()); + Assert.assertEquals(classNames.get(1), container2.getIdByIndex(1)); + Assert.assertEquals(classNames.get(2), container2.lastItemId()); + } + + // this only applies to the collection constructor with no type parameter + @SuppressWarnings("deprecation") + @Test + public void testEmptyCollectionConstructor() { + try { + new BeanItemContainer((Collection) null); + Assert.fail( + "Initializing BeanItemContainer from a null collection should not work!"); + } catch (IllegalArgumentException e) { + // success + } + try { + new BeanItemContainer(new ArrayList()); + Assert.fail( + "Initializing BeanItemContainer from an empty collection should not work!"); + } catch (IllegalArgumentException e) { + // success + } + } + + @Test + public void testItemSetChangeListeners() { + BeanItemContainer container = getContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + container.addListener(counter); + + ClassName cn1 = new ClassName("com.example.Test", 1111); + ClassName cn2 = new ClassName("com.example.Test2", 2222); + + initializeContainer(container); + counter.reset(); + container.addBean(cn1); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + container.addItem(cn1); + counter.assertOnce(); + // no notification if already in container + container.addItem(cn1); + counter.assertNone(); + container.addItem(cn2); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(null, cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.firstItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(container.firstItemId(), cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.getIdByIndex(1), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(container.lastItemId(), cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.lastItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAt(0, cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.firstItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAt(1, cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.getIdByIndex(1), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAt(container.size(), cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.lastItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.removeItem(nameToBean.get(sampleData[0])); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + // no notification for removing a non-existing item + container.removeItem(cn1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.removeAllItems(); + counter.assertOnce(); + // already empty + container.removeAllItems(); + counter.assertNone(); + + } + + @Test + public void testItemSetChangeListenersFiltering() { + BeanItemContainer container = getContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + container.addListener(counter); + + ClassName cn1 = new ClassName("com.example.Test", 1111); + ClassName cn2 = new ClassName("com.example.Test2", 2222); + ClassName other = new ClassName("com.example.Other", 3333); + + // simply adding or removing container filters should cause event + // (content changes) + + initializeContainer(container); + counter.reset(); + container.addContainerFilter(SIMPLE_NAME, "a", true, false); + counter.assertOnce(); + container.removeContainerFilters(SIMPLE_NAME); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + container.addContainerFilter(SIMPLE_NAME, "a", true, false); + counter.assertOnce(); + container.removeAllContainerFilters(); + counter.assertOnce(); + + // perform operations while filtering container + + initializeContainer(container); + counter.reset(); + container.addContainerFilter(FULLY_QUALIFIED_NAME, "Test", true, false); + counter.assertOnce(); + + // passes filter + container.addBean(cn1); + counter.assertOnce(); + + // passes filter but already in the container + container.addBean(cn1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + + // passes filter + container.addItem(cn1); + counter.assertOnce(); + // already in the container + container.addItem(cn1); + counter.assertNone(); + container.addItem(cn2); + counter.assertOnce(); + // does not pass filter + container.addItem(other); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(null, cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.firstItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(container.firstItemId(), cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.getIdByIndex(1), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(container.lastItemId(), cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.lastItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAt(0, cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.firstItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAt(1, cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.getIdByIndex(1), + FULLY_QUALIFIED_NAME).getValue()); + + initializeContainer(container); + counter.reset(); + container.addItemAt(container.size(), cn1); + counter.assertOnce(); + Assert.assertEquals("com.example.Test", + container.getContainerProperty(container.lastItemId(), + FULLY_QUALIFIED_NAME).getValue()); + + // does not pass filter + // note: testAddRemoveWhileFiltering() checks position for these after + // removing filter etc, here concentrating on listeners + + initializeContainer(container); + counter.reset(); + container.addItemAfter(null, other); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(container.firstItemId(), other); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(container.lastItemId(), other); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAt(0, other); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAt(1, other); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAt(container.size(), other); + counter.assertNone(); + + // passes filter + + initializeContainer(container); + counter.reset(); + container.addItem(cn1); + counter.assertOnce(); + container.removeItem(cn1); + counter.assertOnce(); + + // does not pass filter + + initializeContainer(container); + counter.reset(); + // not visible + container.removeItem(nameToBean.get(sampleData[0])); + counter.assertNone(); + + container.removeAllItems(); + counter.assertOnce(); + // no visible items + container.removeAllItems(); + counter.assertNone(); + } + + @Test + public void testAddRemoveWhileFiltering() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + + Person john = new Person("John"); + Person jane = new Person("Jane"); + Person matthew = new Person("Matthew"); + + Person jack = new Person("Jack"); + Person michael = new Person("Michael"); + Person william = new Person("William"); + Person julia = new Person("Julia"); + Person george = new Person("George"); + Person mark = new Person("Mark"); + + container.addBean(john); + container.addBean(jane); + container.addBean(matthew); + + assertEquals(3, container.size()); + // john, jane, matthew + + container.addContainerFilter("name", "j", true, true); + + assertEquals(2, container.size()); + // john, jane, (matthew) + + // add a bean that passes the filter + container.addBean(jack); + assertEquals(3, container.size()); + assertEquals(jack, container.lastItemId()); + // john, jane, (matthew), jack + + // add beans that do not pass the filter + container.addBean(michael); + // john, jane, (matthew), jack, (michael) + container.addItemAfter(null, william); + // (william), john, jane, (matthew), jack, (michael) + + // add after an item that is shown + container.addItemAfter(john, george); + // (william), john, (george), jane, (matthew), jack, (michael) + assertEquals(3, container.size()); + assertEquals(john, container.firstItemId()); + + // add after an item that is not shown does nothing + container.addItemAfter(william, julia); + // (william), john, (george), jane, (matthew), jack, (michael) + assertEquals(3, container.size()); + assertEquals(john, container.firstItemId()); + + container.addItemAt(1, julia); + // (william), john, julia, (george), jane, (matthew), jack, (michael) + + container.addItemAt(2, mark); + // (william), john, julia, (mark), (george), jane, (matthew), jack, + // (michael) + + container.removeItem(matthew); + // (william), john, julia, (mark), (george), jane, jack, (michael) + + assertEquals(4, container.size()); + assertEquals(jack, container.lastItemId()); + + container.removeContainerFilters("name"); + + assertEquals(8, container.size()); + assertEquals(william, container.firstItemId()); + assertEquals(john, container.nextItemId(william)); + assertEquals(julia, container.nextItemId(john)); + assertEquals(mark, container.nextItemId(julia)); + assertEquals(george, container.nextItemId(mark)); + assertEquals(jane, container.nextItemId(george)); + assertEquals(jack, container.nextItemId(jane)); + assertEquals(michael, container.lastItemId()); + } + + @Test + public void testRefilterOnPropertyModification() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + + Person john = new Person("John"); + Person jane = new Person("Jane"); + Person matthew = new Person("Matthew"); + + container.addBean(john); + container.addBean(jane); + container.addBean(matthew); + + assertEquals(3, container.size()); + // john, jane, matthew + + container.addContainerFilter("name", "j", true, true); + + assertEquals(2, container.size()); + // john, jane, (matthew) + + // #6053 currently, modification of an item that is not visible does not + // trigger refiltering - should it? + // matthew.setName("Julia"); + // assertEquals(3, container.size()); + // john, jane, julia + + john.setName("Mark"); + assertEquals(2, container.size()); + // (mark), jane, julia + + container.removeAllContainerFilters(); + + assertEquals(3, container.size()); + } + + @Test + public void testAddAll() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + + Person john = new Person("John"); + Person jane = new Person("Jane"); + Person matthew = new Person("Matthew"); + + container.addBean(john); + container.addBean(jane); + container.addBean(matthew); + + assertEquals(3, container.size()); + // john, jane, matthew + + Person jack = new Person("Jack"); + Person michael = new Person("Michael"); + + // addAll + container.addAll(Arrays.asList(jack, michael)); + // john, jane, matthew, jack, michael + + assertEquals(5, container.size()); + assertEquals(jane, container.nextItemId(john)); + assertEquals(matthew, container.nextItemId(jane)); + assertEquals(jack, container.nextItemId(matthew)); + assertEquals(michael, container.nextItemId(jack)); + } + + @Test + public void testUnsupportedMethods() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + container.addBean(new Person("John")); + + try { + container.addItem(); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addItemAfter(new Person("Jane")); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addItemAt(0); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + try { + container.addContainerProperty("lastName", String.class, ""); + Assert.fail(); + } catch (UnsupportedOperationException e) { + // should get exception + } + + assertEquals(1, container.size()); + } + + @Test + public void testRemoveContainerProperty() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person john = new Person("John"); + container.addBean(john); + + Assert.assertEquals("John", + container.getContainerProperty(john, "name").getValue()); + Assert.assertTrue(container.removeContainerProperty("name")); + Assert.assertNull(container.getContainerProperty(john, "name")); + + Assert.assertNotNull(container.getItem(john)); + // property removed also from item + Assert.assertNull(container.getItem(john).getItemProperty("name")); + } + + @Test + public void testAddNullBean() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person john = new Person("John"); + container.addBean(john); + + assertNull(container.addItem(null)); + assertNull(container.addItemAfter(null, null)); + assertNull(container.addItemAfter(john, null)); + assertNull(container.addItemAt(0, null)); + + assertEquals(1, container.size()); + } + + @Test + public void testBeanIdResolver() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person john = new Person("John"); + + assertSame(john, container.getBeanIdResolver().getIdForBean(john)); + } + + @Test(expected = IllegalArgumentException.class) + public void testNullBeanClass() { + new BeanItemContainer((Class) null); + } + + @Test + public void testAddNestedContainerProperty() { + BeanItemContainer container = new BeanItemContainer( + NestedMethodPropertyTest.Person.class); + + NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( + "John", + new NestedMethodPropertyTest.Address("Ruukinkatu 2-4", 20540)); + container.addBean(john); + + assertTrue(container.addNestedContainerProperty("address.street")); + assertEquals("Ruukinkatu 2-4", container + .getContainerProperty(john, "address.street").getValue()); + } + + @Test + public void testNestedContainerPropertyWithNullBean() { + BeanItemContainer container = new BeanItemContainer( + NestedMethodPropertyTest.Person.class); + NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( + "John", null); + assertNotNull(container.addBean(john)); + assertTrue(container + .addNestedContainerProperty("address.postalCodeObject")); + assertTrue(container.addNestedContainerProperty("address.street")); + // the nested properties should return null + assertNull(container.getContainerProperty(john, "address.street") + .getValue()); + } + + @Test + public void testItemAddedEvent() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person bean = new Person("John"); + ItemSetChangeListener addListener = createListenerMockFor(container); + addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); + EasyMock.replay(addListener); + + container.addItem(bean); + + EasyMock.verify(addListener); + } + + @Test + public void testItemAddedEvent_AddedItem() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person bean = new Person("John"); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + + container.addItem(bean); + + assertEquals(bean, capturedEvent.getValue().getFirstItemId()); + } + + @Test + public void testItemAddedEvent_addItemAt_IndexOfAddedItem() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person bean = new Person("John"); + container.addItem(bean); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + + container.addItemAt(1, new Person("")); + + assertEquals(1, capturedEvent.getValue().getFirstIndex()); + } + + @Test + public void testItemAddedEvent_addItemAfter_IndexOfAddedItem() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person bean = new Person("John"); + container.addItem(bean); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + + container.addItemAfter(bean, new Person("")); + + assertEquals(1, capturedEvent.getValue().getFirstIndex()); + } + + @Test + public void testItemAddedEvent_amountOfAddedItems() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + List beans = Arrays.asList(new Person("Jack"), + new Person("John")); + + container.addAll(beans); + + assertEquals(2, capturedEvent.getValue().getAddedItemsCount()); + } + + @Test + public void testItemAddedEvent_someItemsAreFiltered_amountOfAddedItemsIsReducedByAmountOfFilteredItems() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + List beans = Arrays.asList(new Person("Jack"), + new Person("John")); + container.addFilter(new Compare.Equal("name", "John")); + + container.addAll(beans); + + assertEquals(1, capturedEvent.getValue().getAddedItemsCount()); + } + + @Test + public void testItemAddedEvent_someItemsAreFiltered_addedItemIsTheFirstVisibleItem() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person bean = new Person("John"); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + List beans = Arrays.asList(new Person("Jack"), bean); + container.addFilter(new Compare.Equal("name", "John")); + + container.addAll(beans); + + assertEquals(bean, capturedEvent.getValue().getFirstItemId()); + } + + @Test + public void testItemRemovedEvent() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person bean = new Person("John"); + container.addItem(bean); + ItemSetChangeListener removeListener = createListenerMockFor(container); + removeListener + .containerItemSetChange(EasyMock.isA(ItemRemoveEvent.class)); + EasyMock.replay(removeListener); + + container.removeItem(bean); + + EasyMock.verify(removeListener); + } + + @Test + public void testItemRemovedEvent_RemovedItem() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + Person bean = new Person("John"); + container.addItem(bean); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeItem(bean); + + assertEquals(bean, capturedEvent.getValue().getFirstItemId()); + } + + @Test + public void testItemRemovedEvent_indexOfRemovedItem() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + container.addItem(new Person("Jack")); + Person secondBean = new Person("John"); + container.addItem(secondBean); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeItem(secondBean); + + assertEquals(1, capturedEvent.getValue().getFirstIndex()); + } + + @Test + public void testItemRemovedEvent_amountOfRemovedItems() { + BeanItemContainer container = new BeanItemContainer( + Person.class); + container.addItem(new Person("Jack")); + container.addItem(new Person("John")); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeAllItems(); + + assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); + } + + private Capture captureAddEvent( + ItemSetChangeListener addListener) { + Capture capturedEvent = new Capture(); + addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); + return capturedEvent; + } + + private Capture captureRemoveEvent( + ItemSetChangeListener removeListener) { + Capture capturedEvent = new Capture(); + removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); + return capturedEvent; + } + + private ItemSetChangeListener createListenerMockFor( + BeanItemContainer container) { + ItemSetChangeListener listener = EasyMock + .createNiceMock(ItemSetChangeListener.class); + container.addItemSetChangeListener(listener); + return listener; + } + + @Test + public void testAddNestedContainerBeanBeforeData() { + BeanItemContainer container = new BeanItemContainer( + NestedMethodPropertyTest.Person.class); + + container.addNestedContainerBean("address"); + + assertTrue( + container.getContainerPropertyIds().contains("address.street")); + + NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( + "John", new Address("streetname", 12345)); + container.addBean(john); + + assertTrue(container.getItem(john).getItemPropertyIds() + .contains("address.street")); + assertEquals("streetname", container.getItem(john) + .getItemProperty("address.street").getValue()); + + } + + @Test + public void testAddNestedContainerBeanAfterData() { + BeanItemContainer container = new BeanItemContainer( + NestedMethodPropertyTest.Person.class); + + NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( + "John", new Address("streetname", 12345)); + container.addBean(john); + + container.addNestedContainerBean("address"); + + assertTrue( + container.getContainerPropertyIds().contains("address.street")); + assertTrue(container.getItem(john).getItemPropertyIds() + .contains("address.street")); + assertEquals("streetname", container.getItem(john) + .getItemProperty("address.street").getValue()); + + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemTest.java new file mode 100644 index 0000000000..1bbe74818c --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemTest.java @@ -0,0 +1,389 @@ +package com.vaadin.data.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Property; + +/** + * Test BeanItem specific features. + * + * Only public API is tested, not the methods with package visibility. + * + * See also {@link PropertySetItemTest}, which tests the base class. + */ +public class BeanItemTest { + + @SuppressWarnings("unused") + protected static class MySuperClass { + private int superPrivate = 1; + private int superPrivate2 = 2; + protected double superProtected = 3.0; + private double superProtected2 = 4.0; + public boolean superPublic = true; + private boolean superPublic2 = true; + + public int getSuperPrivate() { + return superPrivate; + } + + public void setSuperPrivate(int superPrivate) { + this.superPrivate = superPrivate; + } + + public double getSuperProtected() { + return superProtected; + } + + public void setSuperProtected(double superProtected) { + this.superProtected = superProtected; + } + + public boolean isSuperPublic() { + return superPublic; + } + + public void setSuperPublic(boolean superPublic) { + this.superPublic = superPublic; + } + + } + + protected static class MyClass extends MySuperClass { + private String name; + public int value = 123; + + public MyClass(String name) { + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setNoField(String name) { + } + + public String getNoField() { + return "no field backing this setter"; + } + + public String getName2() { + return name; + } + } + + protected static class MyClass2 extends MyClass { + public MyClass2(String name) { + super(name); + } + + @Override + public void setName(String name) { + super.setName(name + "2"); + } + + @Override + public String getName() { + return super.getName() + "2"; + } + + @Override + public String getName2() { + return super.getName(); + } + + public void setName2(String name) { + super.setName(name); + } + } + + protected static interface MySuperInterface { + public int getSuper1(); + + public void setSuper1(int i); + + public int getOverride(); + } + + protected static interface MySuperInterface2 { + public int getSuper2(); + } + + protected static class Generic { + + public T getProperty() { + return null; + } + + public void setProperty(T t) { + throw new UnsupportedOperationException(); + } + } + + protected static class SubClass extends Generic { + + @Override + // Has a bridged method + public String getProperty() { + return ""; + } + + @Override + // Has a bridged method + public void setProperty(String t) { + } + } + + protected static interface MySubInterface + extends MySuperInterface, MySuperInterface2 { + public int getSub(); + + public void setSub(int i); + + @Override + public int getOverride(); + + public void setOverride(int i); + } + + @Test + public void testGetProperties() { + BeanItem item = new BeanItem( + new MySuperClass()); + + Collection itemPropertyIds = item.getItemPropertyIds(); + Assert.assertEquals(3, itemPropertyIds.size()); + Assert.assertTrue(itemPropertyIds.contains("superPrivate")); + Assert.assertTrue(itemPropertyIds.contains("superProtected")); + Assert.assertTrue(itemPropertyIds.contains("superPublic")); + } + + @Test + public void testGetSuperClassProperties() { + BeanItem item = new BeanItem(new MyClass("bean1")); + + Collection itemPropertyIds = item.getItemPropertyIds(); + Assert.assertEquals(6, itemPropertyIds.size()); + Assert.assertTrue(itemPropertyIds.contains("superPrivate")); + Assert.assertTrue(itemPropertyIds.contains("superProtected")); + Assert.assertTrue(itemPropertyIds.contains("superPublic")); + Assert.assertTrue(itemPropertyIds.contains("name")); + Assert.assertTrue(itemPropertyIds.contains("noField")); + Assert.assertTrue(itemPropertyIds.contains("name2")); + } + + @Test + public void testOverridingProperties() { + BeanItem item = new BeanItem(new MyClass2("bean2")); + + Collection itemPropertyIds = item.getItemPropertyIds(); + Assert.assertEquals(6, itemPropertyIds.size()); + + Assert.assertTrue(MyClass2.class.equals(item.getBean().getClass())); + + // check that name2 accessed via MyClass2, not MyClass + Assert.assertFalse(item.getItemProperty("name2").isReadOnly()); + } + + @Test + public void testGetInterfaceProperties() throws SecurityException, + NoSuchMethodException, IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + Method method = BeanItem.class + .getDeclaredMethod("getPropertyDescriptors", Class.class); + method.setAccessible(true); + LinkedHashMap> propertyDescriptors = (LinkedHashMap>) method + .invoke(null, MySuperInterface.class); + + Assert.assertEquals(2, propertyDescriptors.size()); + Assert.assertTrue(propertyDescriptors.containsKey("super1")); + Assert.assertTrue(propertyDescriptors.containsKey("override")); + + MethodProperty property = (MethodProperty) propertyDescriptors + .get("override").createProperty(getClass()); + Assert.assertTrue(property.isReadOnly()); + } + + @Test + public void testGetSuperInterfaceProperties() throws SecurityException, + NoSuchMethodException, IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + Method method = BeanItem.class + .getDeclaredMethod("getPropertyDescriptors", Class.class); + method.setAccessible(true); + LinkedHashMap> propertyDescriptors = (LinkedHashMap>) method + .invoke(null, MySubInterface.class); + + Assert.assertEquals(4, propertyDescriptors.size()); + Assert.assertTrue(propertyDescriptors.containsKey("sub")); + Assert.assertTrue(propertyDescriptors.containsKey("super1")); + Assert.assertTrue(propertyDescriptors.containsKey("super2")); + Assert.assertTrue(propertyDescriptors.containsKey("override")); + + MethodProperty property = (MethodProperty) propertyDescriptors + .get("override").createProperty(getClass()); + Assert.assertFalse(property.isReadOnly()); + } + + @Test + public void testPropertyExplicitOrder() { + Collection ids = new ArrayList(); + ids.add("name"); + ids.add("superPublic"); + ids.add("name2"); + ids.add("noField"); + + BeanItem item = new BeanItem(new MyClass("bean1"), + ids); + + Iterator it = item.getItemPropertyIds().iterator(); + Assert.assertEquals("name", it.next()); + Assert.assertEquals("superPublic", it.next()); + Assert.assertEquals("name2", it.next()); + Assert.assertEquals("noField", it.next()); + Assert.assertFalse(it.hasNext()); + } + + @Test + public void testPropertyExplicitOrder2() { + BeanItem item = new BeanItem(new MyClass("bean1"), + new String[] { "name", "superPublic", "name2", "noField" }); + + Iterator it = item.getItemPropertyIds().iterator(); + Assert.assertEquals("name", it.next()); + Assert.assertEquals("superPublic", it.next()); + Assert.assertEquals("name2", it.next()); + Assert.assertEquals("noField", it.next()); + Assert.assertFalse(it.hasNext()); + } + + @Test + public void testPropertyBadPropertyName() { + Collection ids = new ArrayList(); + ids.add("name3"); + ids.add("name"); + + // currently silently ignores non-existent properties + BeanItem item = new BeanItem(new MyClass("bean1"), + ids); + + Iterator it = item.getItemPropertyIds().iterator(); + Assert.assertEquals("name", it.next()); + Assert.assertFalse(it.hasNext()); + } + + @Test + public void testRemoveProperty() { + BeanItem item = new BeanItem(new MyClass("bean1")); + + Collection itemPropertyIds = item.getItemPropertyIds(); + Assert.assertEquals(6, itemPropertyIds.size()); + + item.removeItemProperty("name2"); + Assert.assertEquals(5, itemPropertyIds.size()); + Assert.assertFalse(itemPropertyIds.contains("name2")); + } + + @Test + public void testRemoveSuperProperty() { + BeanItem item = new BeanItem(new MyClass("bean1")); + + Collection itemPropertyIds = item.getItemPropertyIds(); + Assert.assertEquals(6, itemPropertyIds.size()); + + item.removeItemProperty("superPrivate"); + Assert.assertEquals(5, itemPropertyIds.size()); + Assert.assertFalse(itemPropertyIds.contains("superPrivate")); + } + + @Test + public void testPropertyTypes() { + BeanItem item = new BeanItem(new MyClass("bean1")); + + Assert.assertTrue(Integer.class + .equals(item.getItemProperty("superPrivate").getType())); + Assert.assertTrue(Double.class + .equals(item.getItemProperty("superProtected").getType())); + Assert.assertTrue(Boolean.class + .equals(item.getItemProperty("superPublic").getType())); + Assert.assertTrue( + String.class.equals(item.getItemProperty("name").getType())); + } + + @Test + public void testPropertyReadOnly() { + BeanItem item = new BeanItem(new MyClass("bean1")); + + Assert.assertFalse(item.getItemProperty("name").isReadOnly()); + Assert.assertTrue(item.getItemProperty("name2").isReadOnly()); + } + + @Test + public void testCustomProperties() throws Exception { + LinkedHashMap> propertyDescriptors = new LinkedHashMap>(); + propertyDescriptors.put("myname", + new MethodPropertyDescriptor("myname", + MyClass.class, + MyClass.class.getDeclaredMethod("getName"), + MyClass.class.getDeclaredMethod("setName", + String.class))); + MyClass instance = new MyClass("bean1"); + Constructor constructor = BeanItem.class + .getDeclaredConstructor(Object.class, Map.class); + constructor.setAccessible(true); + BeanItem item = constructor.newInstance(instance, + propertyDescriptors); + + Assert.assertEquals(1, item.getItemPropertyIds().size()); + Assert.assertEquals("bean1", item.getItemProperty("myname").getValue()); + } + + @Test + public void testAddRemoveProperty() throws Exception { + MethodPropertyDescriptor pd = new MethodPropertyDescriptor( + "myname", MyClass.class, + MyClass.class.getDeclaredMethod("getName"), + MyClass.class.getDeclaredMethod("setName", String.class)); + + BeanItem item = new BeanItem(new MyClass("bean1")); + + Assert.assertEquals(6, item.getItemPropertyIds().size()); + Assert.assertEquals(null, item.getItemProperty("myname")); + + item.addItemProperty("myname", pd.createProperty(item.getBean())); + Assert.assertEquals(7, item.getItemPropertyIds().size()); + Assert.assertEquals("bean1", item.getItemProperty("myname").getValue()); + item.removeItemProperty("myname"); + Assert.assertEquals(6, item.getItemPropertyIds().size()); + Assert.assertEquals(null, item.getItemProperty("myname")); + } + + @Test + public void testOverridenGenericMethods() { + BeanItem item = new BeanItem(new SubClass()); + + Property property = item.getItemProperty("property"); + Assert.assertEquals("Unexpected class for property type", String.class, + property.getType()); + + Assert.assertEquals("Unexpected property value", "", + property.getValue()); + + // Should not be exception + property.setValue(null); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java new file mode 100644 index 0000000000..e41f751bfd --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java @@ -0,0 +1,26 @@ +package com.vaadin.data.util; + +import org.junit.Test; + +public class ContainerHierarchicalWrapperTest + extends AbstractHierarchicalContainerTestBase { + + @Test + public void testBasicOperations() { + testBasicContainerOperations( + new ContainerHierarchicalWrapper(new IndexedContainer())); + } + + @Test + public void testHierarchicalContainer() { + testHierarchicalContainer( + new ContainerHierarchicalWrapper(new IndexedContainer())); + } + + @Test + public void testRemoveSubtree() { + testRemoveHierarchicalWrapperSubtree( + new ContainerHierarchicalWrapper(new IndexedContainer())); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java new file mode 100644 index 0000000000..c3a8b2c4f9 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java @@ -0,0 +1,107 @@ +package com.vaadin.data.util; + +import java.util.Collection; + +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import com.vaadin.data.Property; + +public class ContainerOrderedWrapperTest extends AbstractContainerTestBase { + + // This class is needed to get an implementation of container + // which is not an implementation of Ordered interface. + private class NotOrderedContainer implements Container { + + private IndexedContainer container; + + public NotOrderedContainer() { + container = new IndexedContainer(); + } + + @Override + public Item getItem(Object itemId) { + return container.getItem(itemId); + } + + @Override + public Collection getContainerPropertyIds() { + return container.getContainerPropertyIds(); + } + + @Override + public Collection getItemIds() { + return container.getItemIds(); + } + + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + return container.getContainerProperty(itemId, propertyId); + } + + @Override + public Class getType(Object propertyId) { + return container.getType(propertyId); + } + + @Override + public int size() { + return container.size(); + } + + @Override + public boolean containsId(Object itemId) { + return container.containsId(itemId); + } + + @Override + public Item addItem(Object itemId) + throws UnsupportedOperationException { + return container.addItem(itemId); + } + + @Override + public Object addItem() throws UnsupportedOperationException { + return container.addItem(); + } + + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + return container.removeItem(itemId); + } + + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + return container.addContainerProperty(propertyId, type, + defaultValue); + } + + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + return container.removeContainerProperty(propertyId); + } + + @Override + public boolean removeAllItems() throws UnsupportedOperationException { + return container.removeAllItems(); + } + + } + + @Test + public void testBasicOperations() { + testBasicContainerOperations( + new ContainerOrderedWrapper(new NotOrderedContainer())); + } + + @Test + public void testOrdered() { + testContainerOrdered( + new ContainerOrderedWrapper(new NotOrderedContainer())); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java new file mode 100644 index 0000000000..58cb2b1d27 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java @@ -0,0 +1,59 @@ +/* + * 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.data.util; + +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.ui.Table; + +public class ContainerSizeAssertTest { + + @Test(expected = AssertionError.class) + public void testNegativeSizeAssert() { + Table table = createAttachedTable(); + + table.setContainerDataSource(createNegativeSizeContainer()); + } + + @Test + public void testZeroSizeNoAssert() { + Table table = createAttachedTable(); + + table.setContainerDataSource(new IndexedContainer()); + } + + private Container createNegativeSizeContainer() { + return new IndexedContainer() { + @Override + public int size() { + return -1; + } + }; + } + + private Table createAttachedTable() { + return new Table() { + private boolean initialized = true; + + @Override + public boolean isAttached() { + // This returns false until the super constructor has finished + return initialized; + } + }; + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java new file mode 100644 index 0000000000..1c4dc1de5e --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java @@ -0,0 +1,222 @@ +package com.vaadin.data.util; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import com.vaadin.tests.util.TestUtil; + +public class ContainerSortingTest { + + private static final String ITEM_DATA_MINUS2_NULL = "Data -2 null"; + private static final String ITEM_DATA_MINUS2 = "Data -2"; + private static final String ITEM_DATA_MINUS1 = "Data -1"; + private static final String ITEM_DATA_MINUS1_NULL = "Data -1 null"; + private static final String ITEM_ANOTHER_NULL = "Another null"; + private static final String ITEM_STRING_2 = "String 2"; + private static final String ITEM_STRING_NULL2 = "String null"; + private static final String ITEM_STRING_1 = "String 1"; + + private static final String PROPERTY_INTEGER_NULL2 = "integer-null"; + private static final String PROPERTY_INTEGER_NOT_NULL = "integer-not-null"; + private static final String PROPERTY_STRING_NULL = "string-null"; + private static final String PROPERTY_STRING_ID = "string-not-null"; + + @Test + public void testEmptyFilteredIndexedContainer() { + IndexedContainer ic = new IndexedContainer(); + + addProperties(ic); + populate(ic); + + ic.addContainerFilter(PROPERTY_STRING_ID, "aasdfasdfasdf", true, false); + ic.sort(new Object[] { PROPERTY_STRING_ID }, new boolean[] { true }); + + } + + @Test + public void testFilteredIndexedContainer() { + IndexedContainer ic = new IndexedContainer(); + + addProperties(ic); + populate(ic); + + ic.addContainerFilter(PROPERTY_STRING_ID, "a", true, false); + ic.sort(new Object[] { PROPERTY_STRING_ID }, new boolean[] { true }); + verifyOrder(ic, + new String[] { ITEM_ANOTHER_NULL, ITEM_DATA_MINUS1, + ITEM_DATA_MINUS1_NULL, ITEM_DATA_MINUS2, + ITEM_DATA_MINUS2_NULL, }); + } + + @Test + public void testIndexedContainer() { + IndexedContainer ic = new IndexedContainer(); + + addProperties(ic); + populate(ic); + + ic.sort(new Object[] { PROPERTY_STRING_ID }, new boolean[] { true }); + verifyOrder(ic, new String[] { ITEM_ANOTHER_NULL, ITEM_DATA_MINUS1, + ITEM_DATA_MINUS1_NULL, ITEM_DATA_MINUS2, ITEM_DATA_MINUS2_NULL, + ITEM_STRING_1, ITEM_STRING_2, ITEM_STRING_NULL2 }); + + ic.sort(new Object[] { PROPERTY_INTEGER_NOT_NULL, + PROPERTY_INTEGER_NULL2, PROPERTY_STRING_ID }, + new boolean[] { true, false, true }); + verifyOrder(ic, new String[] { ITEM_DATA_MINUS2, ITEM_DATA_MINUS2_NULL, + ITEM_DATA_MINUS1, ITEM_DATA_MINUS1_NULL, ITEM_ANOTHER_NULL, + ITEM_STRING_NULL2, ITEM_STRING_1, ITEM_STRING_2 }); + + ic.sort(new Object[] { PROPERTY_INTEGER_NOT_NULL, + PROPERTY_INTEGER_NULL2, PROPERTY_STRING_ID }, + new boolean[] { true, true, true }); + verifyOrder(ic, new String[] { ITEM_DATA_MINUS2_NULL, ITEM_DATA_MINUS2, + ITEM_DATA_MINUS1_NULL, ITEM_DATA_MINUS1, ITEM_ANOTHER_NULL, + ITEM_STRING_NULL2, ITEM_STRING_1, ITEM_STRING_2 }); + + } + + @Test + public void testHierarchicalContainer() { + HierarchicalContainer hc = new HierarchicalContainer(); + populateContainer(hc); + hc.sort(new Object[] { "name" }, new boolean[] { true }); + verifyOrder(hc, + new String[] { "Audi", "C++", "Call of Duty", "Cars", "English", + "Fallout", "Finnish", "Ford", "Games", "Java", + "Might and Magic", "Natural languages", "PHP", + "Programming languages", "Python", "Red Alert", + "Swedish", "Toyota", "Volvo" }); + TestUtil.assertArrays(hc.rootItemIds().toArray(), + new Integer[] { nameToId.get("Cars"), nameToId.get("Games"), + nameToId.get("Natural languages"), + nameToId.get("Programming languages") }); + TestUtil.assertArrays(hc.getChildren(nameToId.get("Games")).toArray(), + new Integer[] { nameToId.get("Call of Duty"), + nameToId.get("Fallout"), + nameToId.get("Might and Magic"), + nameToId.get("Red Alert") }); + } + + private static void populateContainer(HierarchicalContainer container) { + container.addContainerProperty("name", String.class, null); + + addItem(container, "Games", null); + addItem(container, "Call of Duty", "Games"); + addItem(container, "Might and Magic", "Games"); + addItem(container, "Fallout", "Games"); + addItem(container, "Red Alert", "Games"); + + addItem(container, "Cars", null); + addItem(container, "Toyota", "Cars"); + addItem(container, "Volvo", "Cars"); + addItem(container, "Audi", "Cars"); + addItem(container, "Ford", "Cars"); + + addItem(container, "Natural languages", null); + addItem(container, "Swedish", "Natural languages"); + addItem(container, "English", "Natural languages"); + addItem(container, "Finnish", "Natural languages"); + + addItem(container, "Programming languages", null); + addItem(container, "C++", "Programming languages"); + addItem(container, "PHP", "Programming languages"); + addItem(container, "Java", "Programming languages"); + addItem(container, "Python", "Programming languages"); + + } + + private static int index = 0; + private static Map nameToId = new HashMap(); + private static Map idToName = new HashMap(); + + public static void addItem(IndexedContainer container, String string, + String parent) { + nameToId.put(string, index); + idToName.put(index, string); + + Item item = container.addItem(index); + item.getItemProperty("name").setValue(string); + + if (parent != null && container instanceof HierarchicalContainer) { + ((HierarchicalContainer) container).setParent(index, + nameToId.get(parent)); + } + + index++; + } + + private void verifyOrder(Container.Sortable ic, Object[] idOrder) { + int size = ic.size(); + Object[] actual = new Object[size]; + Iterator i = ic.getItemIds().iterator(); + int index = 0; + while (i.hasNext()) { + Object o = i.next(); + if (o.getClass() == Integer.class + && idOrder[index].getClass() == String.class) { + o = idToName.get(o); + } + actual[index++] = o; + } + + TestUtil.assertArrays(actual, idOrder); + + } + + private void populate(IndexedContainer ic) { + addItem(ic, ITEM_STRING_1, ITEM_STRING_1, 1, 1); + addItem(ic, ITEM_STRING_NULL2, null, 0, null); + addItem(ic, ITEM_STRING_2, ITEM_STRING_2, 2, 2); + addItem(ic, ITEM_ANOTHER_NULL, null, 0, null); + addItem(ic, ITEM_DATA_MINUS1, ITEM_DATA_MINUS1, -1, -1); + addItem(ic, ITEM_DATA_MINUS1_NULL, null, -1, null); + addItem(ic, ITEM_DATA_MINUS2, ITEM_DATA_MINUS2, -2, -2); + addItem(ic, ITEM_DATA_MINUS2_NULL, null, -2, null); + } + + private Item addItem(Container ic, String id, String string_null, + int integer, Integer integer_null) { + Item i = ic.addItem(id); + i.getItemProperty(PROPERTY_STRING_ID).setValue(id); + i.getItemProperty(PROPERTY_STRING_NULL).setValue(string_null); + i.getItemProperty(PROPERTY_INTEGER_NOT_NULL).setValue(integer); + i.getItemProperty(PROPERTY_INTEGER_NULL2).setValue(integer_null); + + return i; + } + + private void addProperties(IndexedContainer ic) { + ic.addContainerProperty("id", String.class, null); + ic.addContainerProperty(PROPERTY_STRING_ID, String.class, ""); + ic.addContainerProperty(PROPERTY_STRING_NULL, String.class, null); + ic.addContainerProperty(PROPERTY_INTEGER_NULL2, Integer.class, null); + ic.addContainerProperty(PROPERTY_INTEGER_NOT_NULL, Integer.class, 0); + ic.addContainerProperty("comparable-null", Integer.class, 0); + } + + public class MyObject implements Comparable { + private String data; + + @Override + public int compareTo(MyObject o) { + if (o == null) { + return 1; + } + + if (o.data == null) { + return data == null ? 0 : 1; + } else if (data == null) { + return -1; + } else { + return data.compareTo(o.data); + } + } + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java new file mode 100644 index 0000000000..992b265702 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java @@ -0,0 +1,16 @@ +package com.vaadin.data.util; + +import java.io.File; + +import org.junit.Assert; +import org.junit.Test; + +public class FileSystemContainerTest { + + @Test + public void nonExistingDirectory() { + FilesystemContainer fsc = new FilesystemContainer( + new File("/non/existing")); + Assert.assertTrue(fsc.getItemIds().isEmpty()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java new file mode 100644 index 0000000000..11335314f0 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java @@ -0,0 +1,596 @@ +package com.vaadin.data.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.List; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Indexed.ItemAddEvent; +import com.vaadin.data.Container.Indexed.ItemRemoveEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Container.ItemSetChangeNotifier; +import com.vaadin.data.Item; +import com.vaadin.data.util.filter.SimpleStringFilter; + +public class GeneratedPropertyContainerBasicTest + extends AbstractInMemoryContainerTestBase { + + @Test + public void testBasicOperations() { + testBasicContainerOperations(createContainer()); + } + + private GeneratedPropertyContainer createContainer() { + return new GeneratedPropertyContainer(new IndexedContainer()); + } + + @Test + public void testFiltering() { + testContainerFiltering(createContainer()); + } + + @Test + public void testSorting() { + testContainerSorting(createContainer()); + } + + @Test + public void testSortingAndFiltering() { + testContainerSortingAndFiltering(createContainer()); + } + + @Test + public void testContainerOrdered() { + testContainerOrdered(createContainer()); + } + + @Test + public void testContainerIndexed() { + testContainerIndexed(createContainer(), sampleData[2], 2, true, + "newItemId", true); + } + + @Test + public void testItemSetChangeListeners() { + GeneratedPropertyContainer container = createContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + container.addListener(counter); + + String id1 = "id1"; + String id2 = "id2"; + String id3 = "id3"; + + initializeContainer(container); + counter.reset(); + container.addItem(); + counter.assertOnce(); + container.addItem(id1); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + container.addItemAt(0); + counter.assertOnce(); + container.addItemAt(0, id1); + counter.assertOnce(); + container.addItemAt(0, id2); + counter.assertOnce(); + container.addItemAt(container.size(), id3); + counter.assertOnce(); + // no notification if already in container + container.addItemAt(0, id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(null); + counter.assertOnce(); + container.addItemAfter(null, id1); + counter.assertOnce(); + container.addItemAfter(id1); + counter.assertOnce(); + container.addItemAfter(id1, id2); + counter.assertOnce(); + container.addItemAfter(container.firstItemId()); + counter.assertOnce(); + container.addItemAfter(container.lastItemId()); + counter.assertOnce(); + container.addItemAfter(container.lastItemId(), id3); + counter.assertOnce(); + // no notification if already in container + container.addItemAfter(0, id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.removeItem(sampleData[0]); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + // no notification for removing a non-existing item + container.removeItem(id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.removeAllItems(); + counter.assertOnce(); + // already empty + container.removeAllItems(); + counter.assertNone(); + + } + + @Test + public void testAddRemoveContainerFilter() { + GeneratedPropertyContainer container = createContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + container.addListener(counter); + + // simply adding or removing container filters should cause events + // (content changes) + + initializeContainer(container); + counter.reset(); + SimpleStringFilter filter = new SimpleStringFilter(SIMPLE_NAME, "a", + true, false); + container.addContainerFilter(filter); + counter.assertOnce(); + container.removeContainerFilter(filter); + counter.assertOnce(); + container.addContainerFilter( + new SimpleStringFilter(SIMPLE_NAME, "a", true, false)); + counter.assertOnce(); + container.removeAllContainerFilters(); + counter.assertOnce(); + } + + // TODO other tests should check positions after removing filter etc, + // here concentrating on listeners + @Test + public void testItemSetChangeListenersFiltering() { + Container.Indexed container = createContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + ((GeneratedPropertyContainer) container).addListener(counter); + + counter.reset(); + ((Container.Filterable) container) + .addContainerFilter(new SimpleStringFilter(FULLY_QUALIFIED_NAME, + "Test", true, false)); + // no real change, so no notification required + counter.assertNone(); + + String id1 = "com.example.Test1"; + String id2 = "com.example.Test2"; + String id3 = "com.example.Other"; + + // perform operations while filtering container + + Item item; + + initializeContainer(container); + counter.reset(); + // passes filter + item = container.addItem(id1); + // no event if filtered out + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + // passes filter but already in the container + item = container.addItem(id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + // passes filter after change + item = container.addItemAt(0, id1); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + item = container.addItemAt(container.size(), id2); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); + counter.assertOnce(); + // passes filter but already in the container + item = container.addItemAt(0, id1); + counter.assertNone(); + item = container.addItemAt(container.size(), id2); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + // passes filter + item = container.addItemAfter(null, id1); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + item = container.addItemAfter(container.lastItemId(), id2); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); + counter.assertOnce(); + // passes filter but already in the container + item = container.addItemAfter(null, id1); + counter.assertNone(); + item = container.addItemAfter(container.lastItemId(), id2); + counter.assertNone(); + + // does not pass filter + + // TODO implement rest + + initializeContainer(container); + counter.reset(); + item = container.addItemAfter(null, id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAfter(container.firstItemId(), id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAfter(container.lastItemId(), id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAt(0, id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAt(1, id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAt(container.size(), id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + // passes filter + + initializeContainer(container); + counter.reset(); + item = container.addItem(id1); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + container.removeItem(id1); + counter.assertOnce(); + // already removed + container.removeItem(id1); + counter.assertNone(); + + item = container.addItem(id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + // not visible + container.removeItem(id3); + counter.assertNone(); + + // remove all + + initializeContainer(container); + item = container.addItem(id1); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.reset(); + container.removeAllItems(); + counter.assertOnce(); + // no visible items + container.removeAllItems(); + counter.assertNone(); + } + + @Test + public void testItemAdd_idSequence() { + GeneratedPropertyContainer container = createContainer(); + Object itemId; + + itemId = container.addItem(); + assertEquals(Integer.valueOf(1), itemId); + + itemId = container.addItem(); + assertEquals(Integer.valueOf(2), itemId); + + itemId = container.addItemAfter(null); + assertEquals(Integer.valueOf(3), itemId); + + itemId = container.addItemAt(2); + assertEquals(Integer.valueOf(4), itemId); + } + + @Test + public void testItemAddRemove_idSequence() { + GeneratedPropertyContainer container = createContainer(); + Object itemId; + + itemId = container.addItem(); + assertEquals(Integer.valueOf(1), itemId); + + container.removeItem(itemId); + + itemId = container.addItem(); + assertEquals( + "Id sequence should continue from the previous value even if an item is removed", + Integer.valueOf(2), itemId); + } + + @Test + public void testItemAddedEvent() { + GeneratedPropertyContainer container = createContainer(); + ItemSetChangeListener addListener = createListenerMockFor(container); + addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); + EasyMock.replay(addListener); + + container.addItem(); + + EasyMock.verify(addListener); + } + + @Test + public void testItemAddedEvent_AddedItem() { + GeneratedPropertyContainer container = createContainer(); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + + Object itemId = container.addItem(); + + assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); + } + + @Test + public void testItemAddedEvent_IndexOfAddedItem() { + GeneratedPropertyContainer container = createContainer(); + ItemSetChangeListener addListener = createListenerMockFor(container); + container.addItem(); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + + Object itemId = container.addItemAt(1); + + assertEquals(1, capturedEvent.getValue().getFirstIndex()); + } + + @Test + public void testItemRemovedEvent() { + GeneratedPropertyContainer container = createContainer(); + Object itemId = container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + removeListener + .containerItemSetChange(EasyMock.isA(ItemRemoveEvent.class)); + EasyMock.replay(removeListener); + + container.removeItem(itemId); + + EasyMock.verify(removeListener); + } + + @Test + public void testItemRemovedEvent_RemovedItem() { + GeneratedPropertyContainer container = createContainer(); + Object itemId = container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeItem(itemId); + + assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); + } + + @Test + public void testItemRemovedEvent_indexOfRemovedItem() { + GeneratedPropertyContainer container = createContainer(); + container.addItem(); + Object secondItemId = container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeItem(secondItemId); + + assertEquals(1, capturedEvent.getValue().getFirstIndex()); + } + + @Test + public void testItemRemovedEvent_amountOfRemovedItems() { + GeneratedPropertyContainer container = createContainer(); + container.addItem(); + container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeAllItems(); + + assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); + } + + private Capture captureAddEvent( + ItemSetChangeListener addListener) { + Capture capturedEvent = new Capture(); + addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); + return capturedEvent; + } + + private Capture captureRemoveEvent( + ItemSetChangeListener removeListener) { + Capture capturedEvent = new Capture(); + removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); + return capturedEvent; + } + + private ItemSetChangeListener createListenerMockFor( + ItemSetChangeNotifier container) { + ItemSetChangeListener listener = EasyMock + .createNiceMock(ItemSetChangeListener.class); + container.addItemSetChangeListener(listener); + return listener; + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeIndexOutOfBounds() { + GeneratedPropertyContainer ic = createContainer(); + try { + ic.getItemIds(-1, 10); + fail("Container returned items starting from index -1, something very wrong here!"); + } catch (IndexOutOfBoundsException e) { + // This is expected... + } catch (Exception e) { + // Should not happen! + fail("Container threw unspecified exception when fetching a range of items and the range started from -1"); + } + + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeIndexOutOfBounds2() { + GeneratedPropertyContainer ic = createContainer(); + ic.addItem(new Object()); + try { + ic.getItemIds(2, 1); + fail("Container returned items starting from index -1, something very wrong here!"); + } catch (IndexOutOfBoundsException e) { + // This is expected... + } catch (Exception e) { + // Should not happen! + fail("Container threw unspecified exception when fetching a out of bounds range of items"); + } + + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeZeroRange() { + GeneratedPropertyContainer ic = createContainer(); + ic.addItem(new Object()); + try { + List itemIds = (List) ic.getItemIds(1, 0); + + assertTrue( + "Container returned actual values when asking for 0 items...", + itemIds.isEmpty()); + } catch (Exception e) { + // Should not happen! + fail("Container threw unspecified exception when fetching 0 items..."); + } + + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeNegativeRange() { + GeneratedPropertyContainer ic = createContainer(); + ic.addItem(new Object()); + try { + List itemIds = (List) ic.getItemIds(1, -1); + + assertTrue( + "Container returned actual values when asking for -1 items...", + itemIds.isEmpty()); + } catch (IllegalArgumentException e) { + // this is expected + + } catch (Exception e) { + // Should not happen! + fail("Container threw unspecified exception when fetching -1 items..."); + } + + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeIndexOutOfBoundsDueToSizeChange() { + GeneratedPropertyContainer ic = createContainer(); + ic.addItem(new Object()); + Assert.assertEquals( + "Container returned too many items when the range was >> container size", + 1, ic.getItemIds(0, 10).size()); + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeBaseCase() { + GeneratedPropertyContainer ic = createContainer(); + String object1 = new String("Obj1"); + String object2 = new String("Obj2"); + String object3 = new String("Obj3"); + String object4 = new String("Obj4"); + String object5 = new String("Obj5"); + + ic.addItem(object1); + ic.addItem(object2); + ic.addItem(object3); + ic.addItem(object4); + ic.addItem(object5); + + try { + List itemIds = (List) ic.getItemIds(1, 2); + + assertTrue(itemIds.contains(object2)); + assertTrue(itemIds.contains(object3)); + assertEquals(2, itemIds.size()); + + } catch (Exception e) { + // Should not happen! + fail("Container threw exception when fetching a range of items "); + } + } + + // test getting non-existing property (#10445) + @Test + public void testNonExistingProperty() { + Container ic = createContainer(); + String object1 = new String("Obj1"); + ic.addItem(object1); + assertNull(ic.getContainerProperty(object1, "xyz")); + } + + // test getting null property id (#10445) + @Test + public void testNullPropertyId() { + Container ic = createContainer(); + String object1 = new String("Obj1"); + ic.addItem(object1); + assertNull(ic.getContainerProperty(object1, null)); + } + + @Override + protected void initializeContainer(Container container) { + if (container instanceof GeneratedPropertyContainer) { + super.initializeContainer(((GeneratedPropertyContainer) container) + .getWrappedContainer()); + } else { + super.initializeContainer(container); + } + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java new file mode 100644 index 0000000000..1e34567439 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java @@ -0,0 +1,318 @@ +/* + * 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.data.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container.Filter; +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Container.PropertySetChangeEvent; +import com.vaadin.data.Container.PropertySetChangeListener; +import com.vaadin.data.Item; +import com.vaadin.data.sort.SortOrder; +import com.vaadin.data.util.GeneratedPropertyContainer.GeneratedPropertyItem; +import com.vaadin.data.util.filter.Compare; +import com.vaadin.data.util.filter.UnsupportedFilterException; + +public class GeneratedPropertyContainerTest { + + GeneratedPropertyContainer container; + Indexed wrappedContainer; + private static double MILES_CONVERSION = 0.6214d; + + private class GeneratedPropertyListener + implements PropertySetChangeListener { + + private int callCount = 0; + + public int getCallCount() { + return callCount; + } + + @Override + public void containerPropertySetChange(PropertySetChangeEvent event) { + ++callCount; + assertEquals( + "Container for event was not GeneratedPropertyContainer", + event.getContainer(), container); + } + } + + private class GeneratedItemSetListener implements ItemSetChangeListener { + + private int callCount = 0; + + public int getCallCount() { + return callCount; + } + + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + ++callCount; + assertEquals( + "Container for event was not GeneratedPropertyContainer", + event.getContainer(), container); + } + } + + @Before + public void setUp() { + container = new GeneratedPropertyContainer(createContainer()); + } + + @Test + public void testSimpleGeneratedProperty() { + container.addGeneratedProperty("hello", + new PropertyValueGenerator() { + + @Override + public String getValue(Item item, Object itemId, + Object propertyId) { + return "Hello World!"; + } + + @Override + public Class getType() { + return String.class; + } + }); + + Object itemId = container.addItem(); + assertEquals("Expected value not in item.", + container.getItem(itemId).getItemProperty("hello").getValue(), + "Hello World!"); + } + + @Test + public void testSortableProperties() { + container.addGeneratedProperty("baz", + new PropertyValueGenerator() { + + @Override + public String getValue(Item item, Object itemId, + Object propertyId) { + return item.getItemProperty("foo").getValue() + " " + + item.getItemProperty("bar").getValue(); + } + + @Override + public Class getType() { + return String.class; + } + + @Override + public SortOrder[] getSortProperties(SortOrder order) { + SortOrder[] sortOrder = new SortOrder[1]; + sortOrder[0] = new SortOrder("bar", + order.getDirection()); + return sortOrder; + } + }); + + container.sort(new Object[] { "baz" }, new boolean[] { true }); + assertEquals("foo 0", container.getItem(container.getIdByIndex(0)) + .getItemProperty("baz").getValue()); + + container.sort(new Object[] { "baz" }, new boolean[] { false }); + assertEquals("foo 10", container.getItem(container.getIdByIndex(0)) + .getItemProperty("baz").getValue()); + } + + @Test + public void testOverrideSortableProperties() { + + assertTrue(container.getSortableContainerPropertyIds().contains("bar")); + + container.addGeneratedProperty("bar", + new PropertyValueGenerator() { + + @Override + public String getValue(Item item, Object itemId, + Object propertyId) { + return item.getItemProperty("foo").getValue() + " " + + item.getItemProperty("bar").getValue(); + } + + @Override + public Class getType() { + return String.class; + } + }); + + assertFalse( + container.getSortableContainerPropertyIds().contains("bar")); + } + + @Test + public void testFilterByMiles() { + container.addGeneratedProperty("miles", + new PropertyValueGenerator() { + + @Override + public Double getValue(Item item, Object itemId, + Object propertyId) { + return (Double) item.getItemProperty("km").getValue() + * MILES_CONVERSION; + } + + @Override + public Class getType() { + return Double.class; + } + + @Override + public Filter modifyFilter(Filter filter) + throws UnsupportedFilterException { + if (filter instanceof Compare.LessOrEqual) { + Double value = (Double) ((Compare.LessOrEqual) filter) + .getValue(); + value = value / MILES_CONVERSION; + return new Compare.LessOrEqual("km", value); + } + return super.modifyFilter(filter); + } + }); + + for (Object itemId : container.getItemIds()) { + Item item = container.getItem(itemId); + Double km = (Double) item.getItemProperty("km").getValue(); + Double miles = (Double) item.getItemProperty("miles").getValue(); + assertTrue(miles.equals(km * MILES_CONVERSION)); + } + + Filter filter = new Compare.LessOrEqual("miles", MILES_CONVERSION); + container.addContainerFilter(filter); + for (Object itemId : container.getItemIds()) { + Item item = container.getItem(itemId); + assertTrue("Item did not pass original filter.", + filter.passesFilter(itemId, item)); + } + + assertTrue(container.getContainerFilters().contains(filter)); + container.removeContainerFilter(filter); + assertFalse(container.getContainerFilters().contains(filter)); + + boolean allPass = true; + for (Object itemId : container.getItemIds()) { + Item item = container.getItem(itemId); + if (!filter.passesFilter(itemId, item)) { + allPass = false; + } + } + + if (allPass) { + fail("Removing filter did not introduce any previous filtered items"); + } + } + + @Test + public void testPropertySetChangeNotifier() { + GeneratedPropertyListener listener = new GeneratedPropertyListener(); + GeneratedPropertyListener removedListener = new GeneratedPropertyListener(); + container.addPropertySetChangeListener(listener); + container.addPropertySetChangeListener(removedListener); + + container.addGeneratedProperty("foo", + new PropertyValueGenerator() { + + @Override + public String getValue(Item item, Object itemId, + Object propertyId) { + return ""; + } + + @Override + public Class getType() { + return String.class; + } + }); + + // Adding property to wrapped container should cause an event + wrappedContainer.addContainerProperty("baz", String.class, ""); + container.removePropertySetChangeListener(removedListener); + container.removeGeneratedProperty("foo"); + + assertEquals("Listener was not called correctly.", 3, + listener.getCallCount()); + assertEquals("Removed listener was not called correctly.", 2, + removedListener.getCallCount()); + } + + @Test + public void testItemSetChangeNotifier() { + GeneratedItemSetListener listener = new GeneratedItemSetListener(); + container.addItemSetChangeListener(listener); + + container.sort(new Object[] { "foo" }, new boolean[] { true }); + container.sort(new Object[] { "foo" }, new boolean[] { false }); + + assertEquals("Listener was not called correctly.", 2, + listener.getCallCount()); + + } + + @Test + public void testRemoveProperty() { + container.removeContainerProperty("foo"); + assertFalse("Container contained removed property", + container.getContainerPropertyIds().contains("foo")); + assertTrue("Wrapped container did not contain removed property", + wrappedContainer.getContainerPropertyIds().contains("foo")); + + assertFalse(container.getItem(container.firstItemId()) + .getItemPropertyIds().contains("foo")); + + container.addContainerProperty("foo", null, null); + assertTrue("Container did not contain returned property", + container.getContainerPropertyIds().contains("foo")); + } + + @Test + public void testGetWrappedItem() { + Object itemId = wrappedContainer.getItemIds().iterator().next(); + Item wrappedItem = wrappedContainer.getItem(itemId); + GeneratedPropertyItem generatedPropertyItem = (GeneratedPropertyItem) container + .getItem(itemId); + assertEquals(wrappedItem, generatedPropertyItem.getWrappedItem()); + } + + private Indexed createContainer() { + wrappedContainer = new IndexedContainer(); + wrappedContainer.addContainerProperty("foo", String.class, "foo"); + wrappedContainer.addContainerProperty("bar", Integer.class, 0); + // km contains double values from 0.0 to 2.0 + wrappedContainer.addContainerProperty("km", Double.class, 0); + + for (int i = 0; i <= 10; ++i) { + Object itemId = wrappedContainer.addItem(); + Item item = wrappedContainer.getItem(itemId); + item.getItemProperty("foo").setValue("foo"); + item.getItemProperty("bar").setValue(i); + item.getItemProperty("km").setValue(i / 5.0d); + } + + return wrappedContainer; + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java new file mode 100644 index 0000000000..54984f07ad --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java @@ -0,0 +1,33 @@ +package com.vaadin.data.util; + +import org.junit.Test; + +public class HierarchicalContainerOrderedWrapperTest + extends AbstractHierarchicalContainerTestBase { + + private HierarchicalContainerOrderedWrapper createContainer() { + return new HierarchicalContainerOrderedWrapper( + new ContainerHierarchicalWrapper(new IndexedContainer())); + } + + @Test + public void testBasicOperations() { + testBasicContainerOperations(createContainer()); + } + + @Test + public void testHierarchicalContainer() { + testHierarchicalContainer(createContainer()); + } + + @Test + public void testContainerOrdered() { + testContainerOrdered(createContainer()); + } + + @Test + public void testRemoveSubtree() { + testRemoveHierarchicalWrapperSubtree(createContainer()); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java new file mode 100644 index 0000000000..7ab20ca3dd --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java @@ -0,0 +1,286 @@ +package com.vaadin.data.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +import com.vaadin.data.Container.Filter; +import com.vaadin.data.Item; + +public class HierarchicalContainerTest + extends AbstractHierarchicalContainerTestBase { + + @Test + public void testBasicOperations() { + testBasicContainerOperations(new HierarchicalContainer()); + } + + @Test + public void testFiltering() { + testContainerFiltering(new HierarchicalContainer()); + } + + @Test + public void testSorting() { + testContainerSorting(new HierarchicalContainer()); + } + + @Test + public void testOrdered() { + testContainerOrdered(new HierarchicalContainer()); + } + + @Test + public void testHierarchicalSorting() { + testHierarchicalSorting(new HierarchicalContainer()); + } + + @Test + public void testSortingAndFiltering() { + testContainerSortingAndFiltering(new HierarchicalContainer()); + } + + @Test + public void testRemovingItemsFromFilteredContainer() { + HierarchicalContainer container = new HierarchicalContainer(); + initializeContainer(container); + container.setIncludeParentsWhenFiltering(true); + container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); + Object p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertEquals("com.vaadin.ui", p1); + + container.removeItem("com.vaadin.ui.TabSheet"); + // Parent for the removed item must be null because the item is no + // longer in the container + p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertNull("Parent should be null, is " + p1, p1); + + container.removeAllItems(); + p1 = container.getParent("com.vaadin.terminal.gwt.client.Focusable"); + assertNull("Parent should be null, is " + p1, p1); + + } + + @Test + public void testParentWhenRemovingFilterFromContainer() { + HierarchicalContainer container = new HierarchicalContainer(); + initializeContainer(container); + container.setIncludeParentsWhenFiltering(true); + container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); + Object p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertEquals("com.vaadin.ui", p1); + p1 = container + .getParent("com.vaadin.terminal.gwt.client.ui.VPopupCalendar"); + assertNull(p1); + container.removeAllContainerFilters(); + p1 = container + .getParent("com.vaadin.terminal.gwt.client.ui.VPopupCalendar"); + assertEquals("com.vaadin.terminal.gwt.client.ui", p1); + + } + + @Test + public void testChangeParentInFilteredContainer() { + HierarchicalContainer container = new HierarchicalContainer(); + initializeContainer(container); + container.setIncludeParentsWhenFiltering(true); + container.addContainerFilter(FULLY_QUALIFIED_NAME, "Tab", false, false); + + // Change parent of filtered item + Object p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertEquals("com.vaadin.ui", p1); + container.setParent("com.vaadin.ui.TabSheet", "com.vaadin"); + p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertEquals("com.vaadin", p1); + container.setParent("com.vaadin.ui.TabSheet", "com"); + p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertEquals("com", p1); + container.setParent("com.vaadin.ui.TabSheet", null); + p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertNull(p1); + + // root -> non-root + container.setParent("com.vaadin.ui.TabSheet", "com"); + p1 = container.getParent("com.vaadin.ui.TabSheet"); + assertEquals("com", p1); + + } + + @Test + public void testHierarchicalFilteringWithParents() { + HierarchicalContainer container = new HierarchicalContainer(); + initializeContainer(container); + container.setIncludeParentsWhenFiltering(true); + + // Filter by "contains ab" + container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); + + // 20 items match the filters and the have 8 parents that should also be + // included + // only one root "com" should exist + // filtered + int expectedSize = 29; + int expectedRoots = 1; + + validateHierarchicalContainer(container, "com", + "com.vaadin.ui.TabSheet", + "com.vaadin.terminal.gwt.client.Focusable", "blah", true, + expectedSize, expectedRoots, true); + + // only include .gwt.client classes + container.removeAllContainerFilters(); + container.addContainerFilter(FULLY_QUALIFIED_NAME, ".gwt.client.", + false, false); + + int packages = 6; + int classes = 112; + + expectedSize = packages + classes; + expectedRoots = 1; + + validateHierarchicalContainer(container, "com", + "com.vaadin.terminal.gwt.client.WidgetSet", + "com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical", "blah", + true, expectedSize, expectedRoots, true); + + // Additionally remove all without 'm' in the simple name. + container.addContainerFilter(SIMPLE_NAME, "m", false, false); + + expectedSize = 7 + 18; + expectedRoots = 1; + + validateHierarchicalContainer(container, "com", + "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", + "com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer", + "blah", true, expectedSize, expectedRoots, true); + + } + + @Test + public void testRemoveLastChild() { + HierarchicalContainer c = new HierarchicalContainer(); + + c.addItem("root"); + assertEquals(false, c.hasChildren("root")); + + c.addItem("child"); + c.setParent("child", "root"); + assertEquals(true, c.hasChildren("root")); + + c.removeItem("child"); + assertFalse(c.containsId("child")); + assertNull(c.getChildren("root")); + assertNull(c.getChildren("child")); + assertFalse(c.hasChildren("child")); + assertFalse(c.hasChildren("root")); + } + + @Test + public void testRemoveLastChildFromFiltered() { + HierarchicalContainer c = new HierarchicalContainer(); + + c.addItem("root"); + assertEquals(false, c.hasChildren("root")); + + c.addItem("child"); + c.setParent("child", "root"); + assertEquals(true, c.hasChildren("root")); + + // Dummy filter that does not remove any items + c.addContainerFilter(new Filter() { + + @Override + public boolean passesFilter(Object itemId, Item item) + throws UnsupportedOperationException { + return true; + } + + @Override + public boolean appliesToProperty(Object propertyId) { + return true; + } + }); + c.removeItem("child"); + + assertFalse(c.containsId("child")); + assertNull(c.getChildren("root")); + assertNull(c.getChildren("child")); + assertFalse(c.hasChildren("child")); + assertFalse(c.hasChildren("root")); + } + + @Test + public void testHierarchicalFilteringWithoutParents() { + HierarchicalContainer container = new HierarchicalContainer(); + + initializeContainer(container); + container.setIncludeParentsWhenFiltering(false); + + // Filter by "contains ab" + container.addContainerFilter(SIMPLE_NAME, "ab", false, false); + + // 20 items match the filter. + // com.vaadin.data.BufferedValidatable + // com.vaadin.data.Validatable + // com.vaadin.terminal.gwt.client.Focusable + // com.vaadin.terminal.gwt.client.Paintable + // com.vaadin.terminal.gwt.client.ui.Table + // com.vaadin.terminal.gwt.client.ui.VLabel + // com.vaadin.terminal.gwt.client.ui.VScrollTable + // com.vaadin.terminal.gwt.client.ui.VTablePaging + // com.vaadin.terminal.gwt.client.ui.VTabsheet + // com.vaadin.terminal.gwt.client.ui.VTabsheetBase + // com.vaadin.terminal.gwt.client.ui.VTabsheetPanel + // com.vaadin.server.ChangeVariablesErrorEvent + // com.vaadin.server.Paintable + // com.vaadin.server.Scrollable + // com.vaadin.server.Sizeable + // com.vaadin.server.VariableOwner + // com.vaadin.ui.Label + // com.vaadin.ui.Table + // com.vaadin.ui.TableFieldFactory + // com.vaadin.ui.TabSheet + // all become roots. + int expectedSize = 20; + int expectedRoots = 20; + + validateHierarchicalContainer(container, + "com.vaadin.data.BufferedValidatable", "com.vaadin.ui.TabSheet", + "com.vaadin.terminal.gwt.client.ui.VTabsheetBase", "blah", true, + expectedSize, expectedRoots, false); + + // only include .gwt.client classes + container.removeAllContainerFilters(); + container.addContainerFilter(FULLY_QUALIFIED_NAME, ".gwt.client.", + false, false); + + int packages = 3; + int classes = 110; + + expectedSize = packages + classes; + expectedRoots = 35 + 1; // com.vaadin.terminal.gwt.client.ui + + // com.vaadin.terminal.gwt.client.* + + // Sorting is case insensitive + validateHierarchicalContainer(container, + "com.vaadin.terminal.gwt.client.ApplicationConfiguration", + "com.vaadin.terminal.gwt.client.WidgetSet", + "com.vaadin.terminal.gwt.client.ui.VOptionGroup", "blah", true, + expectedSize, expectedRoots, false); + + // Additionally remove all without 'P' in the simple name. + container.addContainerFilter(SIMPLE_NAME, "P", false, false); + + expectedSize = 13; + expectedRoots = expectedSize; + + validateHierarchicalContainer(container, + "com.vaadin.terminal.gwt.client.Paintable", + "com.vaadin.terminal.gwt.client.ui.VTabsheetPanel", + "com.vaadin.terminal.gwt.client.ui.VPopupCalendar", "blah", + true, expectedSize, expectedRoots, false); + + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java new file mode 100644 index 0000000000..b19a425518 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java @@ -0,0 +1,533 @@ +package com.vaadin.data.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Container.Indexed.ItemAddEvent; +import com.vaadin.data.Container.Indexed.ItemRemoveEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Item; + +public class IndexedContainerTest extends AbstractInMemoryContainerTestBase { + + @Test + public void testBasicOperations() { + testBasicContainerOperations(new IndexedContainer()); + } + + @Test + public void testFiltering() { + testContainerFiltering(new IndexedContainer()); + } + + @Test + public void testSorting() { + testContainerSorting(new IndexedContainer()); + } + + @Test + public void testSortingAndFiltering() { + testContainerSortingAndFiltering(new IndexedContainer()); + } + + @Test + public void testContainerOrdered() { + testContainerOrdered(new IndexedContainer()); + } + + @Test + public void testContainerIndexed() { + testContainerIndexed(new IndexedContainer(), sampleData[2], 2, true, + "newItemId", true); + } + + @Test + public void testItemSetChangeListeners() { + IndexedContainer container = new IndexedContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + container.addListener(counter); + + String id1 = "id1"; + String id2 = "id2"; + String id3 = "id3"; + + initializeContainer(container); + counter.reset(); + container.addItem(); + counter.assertOnce(); + container.addItem(id1); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + container.addItemAt(0); + counter.assertOnce(); + container.addItemAt(0, id1); + counter.assertOnce(); + container.addItemAt(0, id2); + counter.assertOnce(); + container.addItemAt(container.size(), id3); + counter.assertOnce(); + // no notification if already in container + container.addItemAt(0, id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.addItemAfter(null); + counter.assertOnce(); + container.addItemAfter(null, id1); + counter.assertOnce(); + container.addItemAfter(id1); + counter.assertOnce(); + container.addItemAfter(id1, id2); + counter.assertOnce(); + container.addItemAfter(container.firstItemId()); + counter.assertOnce(); + container.addItemAfter(container.lastItemId()); + counter.assertOnce(); + container.addItemAfter(container.lastItemId(), id3); + counter.assertOnce(); + // no notification if already in container + container.addItemAfter(0, id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.removeItem(sampleData[0]); + counter.assertOnce(); + + initializeContainer(container); + counter.reset(); + // no notification for removing a non-existing item + container.removeItem(id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + container.removeAllItems(); + counter.assertOnce(); + // already empty + container.removeAllItems(); + counter.assertNone(); + + } + + @Test + public void testAddRemoveContainerFilter() { + IndexedContainer container = new IndexedContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + container.addListener(counter); + + // simply adding or removing container filters should cause events + // (content changes) + + initializeContainer(container); + counter.reset(); + container.addContainerFilter(SIMPLE_NAME, "a", true, false); + counter.assertOnce(); + container.removeContainerFilters(SIMPLE_NAME); + counter.assertOnce(); + container.addContainerFilter(SIMPLE_NAME, "a", true, false); + counter.assertOnce(); + container.removeAllContainerFilters(); + counter.assertOnce(); + } + + // TODO other tests should check positions after removing filter etc, + // here concentrating on listeners + @Test + public void testItemSetChangeListenersFiltering() { + IndexedContainer container = new IndexedContainer(); + ItemSetChangeCounter counter = new ItemSetChangeCounter(); + container.addListener(counter); + + counter.reset(); + container.addContainerFilter(FULLY_QUALIFIED_NAME, "Test", true, false); + // no real change, so no notification required + counter.assertNone(); + + String id1 = "com.example.Test1"; + String id2 = "com.example.Test2"; + String id3 = "com.example.Other"; + + // perform operations while filtering container + + Item item; + + initializeContainer(container); + counter.reset(); + // passes filter + item = container.addItem(id1); + // no event if filtered out + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + // passes filter but already in the container + item = container.addItem(id1); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + // passes filter after change + item = container.addItemAt(0, id1); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + item = container.addItemAt(container.size(), id2); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); + counter.assertOnce(); + // passes filter but already in the container + item = container.addItemAt(0, id1); + counter.assertNone(); + item = container.addItemAt(container.size(), id2); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + // passes filter + item = container.addItemAfter(null, id1); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + item = container.addItemAfter(container.lastItemId(), id2); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); + counter.assertOnce(); + // passes filter but already in the container + item = container.addItemAfter(null, id1); + counter.assertNone(); + item = container.addItemAfter(container.lastItemId(), id2); + counter.assertNone(); + + // does not pass filter + + // TODO implement rest + + initializeContainer(container); + counter.reset(); + item = container.addItemAfter(null, id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAfter(container.firstItemId(), id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAfter(container.lastItemId(), id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAt(0, id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAt(1, id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + initializeContainer(container); + counter.reset(); + item = container.addItemAt(container.size(), id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + + // passes filter + + initializeContainer(container); + counter.reset(); + item = container.addItem(id1); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.assertOnce(); + container.removeItem(id1); + counter.assertOnce(); + // already removed + container.removeItem(id1); + counter.assertNone(); + + item = container.addItem(id3); + counter.assertNone(); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); + counter.assertNone(); + // not visible + container.removeItem(id3); + counter.assertNone(); + + // remove all + + initializeContainer(container); + item = container.addItem(id1); + item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); + counter.reset(); + container.removeAllItems(); + counter.assertOnce(); + // no visible items + container.removeAllItems(); + counter.assertNone(); + } + + @Test + public void testItemAdd_idSequence() { + IndexedContainer container = new IndexedContainer(); + Object itemId; + + itemId = container.addItem(); + assertEquals(Integer.valueOf(1), itemId); + + itemId = container.addItem(); + assertEquals(Integer.valueOf(2), itemId); + + itemId = container.addItemAfter(null); + assertEquals(Integer.valueOf(3), itemId); + + itemId = container.addItemAt(2); + assertEquals(Integer.valueOf(4), itemId); + } + + @Test + public void testItemAddRemove_idSequence() { + IndexedContainer container = new IndexedContainer(); + Object itemId; + + itemId = container.addItem(); + assertEquals(Integer.valueOf(1), itemId); + + container.removeItem(itemId); + + itemId = container.addItem(); + assertEquals( + "Id sequence should continue from the previous value even if an item is removed", + Integer.valueOf(2), itemId); + } + + @Test + public void testItemAddedEvent() { + IndexedContainer container = new IndexedContainer(); + ItemSetChangeListener addListener = createListenerMockFor(container); + addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); + EasyMock.replay(addListener); + + container.addItem(); + + EasyMock.verify(addListener); + } + + @Test + public void testItemAddedEvent_AddedItem() { + IndexedContainer container = new IndexedContainer(); + ItemSetChangeListener addListener = createListenerMockFor(container); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + + Object itemId = container.addItem(); + + assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); + } + + @Test + public void testItemAddedEvent_IndexOfAddedItem() { + IndexedContainer container = new IndexedContainer(); + ItemSetChangeListener addListener = createListenerMockFor(container); + container.addItem(); + Capture capturedEvent = captureAddEvent(addListener); + EasyMock.replay(addListener); + + Object itemId = container.addItemAt(1); + + assertEquals(1, capturedEvent.getValue().getFirstIndex()); + } + + @Test + public void testItemRemovedEvent() { + IndexedContainer container = new IndexedContainer(); + Object itemId = container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + removeListener + .containerItemSetChange(EasyMock.isA(ItemRemoveEvent.class)); + EasyMock.replay(removeListener); + + container.removeItem(itemId); + + EasyMock.verify(removeListener); + } + + @Test + public void testItemRemovedEvent_RemovedItem() { + IndexedContainer container = new IndexedContainer(); + Object itemId = container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeItem(itemId); + + assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); + } + + @Test + public void testItemRemovedEvent_indexOfRemovedItem() { + IndexedContainer container = new IndexedContainer(); + container.addItem(); + Object secondItemId = container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeItem(secondItemId); + + assertEquals(1, capturedEvent.getValue().getFirstIndex()); + } + + @Test + public void testItemRemovedEvent_amountOfRemovedItems() { + IndexedContainer container = new IndexedContainer(); + container.addItem(); + container.addItem(); + ItemSetChangeListener removeListener = createListenerMockFor(container); + Capture capturedEvent = captureRemoveEvent( + removeListener); + EasyMock.replay(removeListener); + + container.removeAllItems(); + + assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); + } + + private Capture captureAddEvent( + ItemSetChangeListener addListener) { + Capture capturedEvent = new Capture(); + addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); + return capturedEvent; + } + + private Capture captureRemoveEvent( + ItemSetChangeListener removeListener) { + Capture capturedEvent = new Capture(); + removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); + return capturedEvent; + } + + private ItemSetChangeListener createListenerMockFor( + IndexedContainer container) { + ItemSetChangeListener listener = EasyMock + .createNiceMock(ItemSetChangeListener.class); + container.addItemSetChangeListener(listener); + return listener; + } + + // Ticket 8028 + @Test(expected = IndexOutOfBoundsException.class) + public void testGetItemIdsRangeIndexOutOfBounds() { + IndexedContainer ic = new IndexedContainer(); + ic.getItemIds(-1, 10); + } + + // Ticket 8028 + @Test(expected = IndexOutOfBoundsException.class) + public void testGetItemIdsRangeIndexOutOfBounds2() { + IndexedContainer ic = new IndexedContainer(); + ic.addItem(new Object()); + ic.getItemIds(2, 1); + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeZeroRange() { + IndexedContainer ic = new IndexedContainer(); + ic.addItem(new Object()); + List itemIds = ic.getItemIds(1, 0); + + assertTrue( + "Container returned actual values when asking for 0 items...", + itemIds.isEmpty()); + } + + // Ticket 8028 + @Test(expected = IllegalArgumentException.class) + public void testGetItemIdsRangeNegativeRange() { + IndexedContainer ic = new IndexedContainer(); + ic.addItem(new Object()); + List itemIds = ic.getItemIds(1, -1); + + assertTrue( + "Container returned actual values when asking for -1 items...", + itemIds.isEmpty()); + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeIndexOutOfBoundsDueToSizeChange() { + IndexedContainer ic = new IndexedContainer(); + ic.addItem(new Object()); + Assert.assertEquals( + "Container returned too many items when the range was >> container size", + 1, ic.getItemIds(0, 10).size()); + } + + // Ticket 8028 + @Test + public void testGetItemIdsRangeBaseCase() { + IndexedContainer ic = new IndexedContainer(); + String object1 = new String("Obj1"); + String object2 = new String("Obj2"); + String object3 = new String("Obj3"); + String object4 = new String("Obj4"); + String object5 = new String("Obj5"); + + ic.addItem(object1); + ic.addItem(object2); + ic.addItem(object3); + ic.addItem(object4); + ic.addItem(object5); + + List itemIds = ic.getItemIds(1, 2); + + assertTrue(itemIds.contains(object2)); + assertTrue(itemIds.contains(object3)); + assertEquals(2, itemIds.size()); + } + + // test getting non-existing property (#10445) + @Test + public void testNonExistingProperty() { + IndexedContainer ic = new IndexedContainer(); + String object1 = new String("Obj1"); + ic.addItem(object1); + assertNull(ic.getContainerProperty(object1, "xyz")); + } + + // test getting null property id (#10445) + @Test + public void testNullPropertyId() { + IndexedContainer ic = new IndexedContainer(); + String object1 = new String("Obj1"); + ic.addItem(object1); + assertNull(ic.getContainerProperty(object1, null)); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java new file mode 100644 index 0000000000..6776021c24 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java @@ -0,0 +1,150 @@ +/* + * 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.data.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Field; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Test for MethodProperty: don't allocate unnecessary Object arrays. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class MethodPropertyMemoryConsumptionTest { + + @Test + public void testSetArguments() + throws NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + TestBean bean = new TestBean(); + TestMethodProperty property = new TestMethodProperty( + bean, "name"); + Object[] getArgs = property.getGetArgs(); + Object[] setArgs = property.getSetArgs(); + + Field getArgsField = TestMethodProperty.class + .getDeclaredField("getArgs"); + getArgsField.setAccessible(true); + + Field setArgsField = TestMethodProperty.class + .getDeclaredField("setArgs"); + setArgsField.setAccessible(true); + + Assert.assertSame( + "setArguments method sets non-default instance" + + " of empty Object array for getArgs", + getArgsField.get(property), getArgs); + + Assert.assertSame( + "setArguments method sets non-default instance" + + " of empty Object array for setArgs", + setArgsField.get(property), setArgs); + } + + @Test + public void testDefaultCtor() { + TestBean bean = new TestBean(); + TestMethodProperty property = new TestMethodProperty( + bean, "name"); + + Object[] getArgs = property.getGetArgs(); + Object[] setArgs = property.getSetArgs(); + + TestBean otherBean = new TestBean(); + TestMethodProperty otherProperty = new TestMethodProperty( + otherBean, "name"); + Assert.assertSame( + "setArguments method uses different instance" + + " of empty Object array for getArgs", + getArgs, otherProperty.getGetArgs()); + Assert.assertSame( + "setArguments method uses different instance" + + " of empty Object array for setArgs", + setArgs, otherProperty.getSetArgs()); + } + + @Test + public void testDefaultArgsSerialization() + throws IOException, ClassNotFoundException { + TestBean bean = new TestBean(); + TestMethodProperty property = new TestMethodProperty( + bean, "name"); + + ByteArrayOutputStream sourceOutStream = new ByteArrayOutputStream(); + ObjectOutputStream outStream = new ObjectOutputStream(sourceOutStream); + outStream.writeObject(property); + + ObjectInputStream inputStream = new ObjectInputStream( + new ByteArrayInputStream(sourceOutStream.toByteArray())); + Object red = inputStream.readObject(); + TestMethodProperty deserialized = (TestMethodProperty) red; + + Assert.assertNotNull("Deseriliation doesn't call setArguments method", + deserialized.getGetArgs()); + Assert.assertNotNull("Deseriliation doesn't call setArguments method", + deserialized.getSetArgs()); + + } + + public static class TestMethodProperty extends MethodProperty { + + public TestMethodProperty(Object instance, String beanPropertyName) { + super(instance, beanPropertyName); + } + + @Override + public void setArguments(Object[] getArgs, Object[] setArgs, + int setArgumentIndex) { + super.setArguments(getArgs, setArgs, setArgumentIndex); + this.getArgs = getArgs; + this.setArgs = setArgs; + } + + Object[] getGetArgs() { + return getArgs; + } + + Object[] getSetArgs() { + return setArgs; + } + + private transient Object[] getArgs; + private transient Object[] setArgs; + } + + public static class TestBean implements Serializable { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java new file mode 100644 index 0000000000..4a1e2a1784 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java @@ -0,0 +1,351 @@ +package com.vaadin.data.util; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class NestedMethodPropertyTest { + + public static class Address implements Serializable { + private String street; + private int postalCodePrimitive; + private Integer postalCodeObject; + + public Address(String street, int postalCode) { + this.street = street; + postalCodePrimitive = postalCode; + postalCodeObject = postalCode; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getStreet() { + return street; + } + + public void setPostalCodePrimitive(int postalCodePrimitive) { + this.postalCodePrimitive = postalCodePrimitive; + } + + public int getPostalCodePrimitive() { + return postalCodePrimitive; + } + + public void setPostalCodeObject(Integer postalCodeObject) { + this.postalCodeObject = postalCodeObject; + } + + public Integer getPostalCodeObject() { + return postalCodeObject; + } + + // read-only boolean property + public boolean isBoolean() { + return true; + } + } + + public static class Person implements Serializable { + private String name; + private Address address; + private int age; + + public Person(String name, Address address) { + this.name = name; + this.address = address; + } + + public Person(String name, Address address, int age) { + this.name = name; + this.address = address; + this.age = age; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setAddress(Address address) { + this.address = address; + } + + public Address getAddress() { + return address; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + } + + public static class Team implements Serializable { + private String name; + private Person manager; + + public Team(String name, Person manager) { + this.name = name; + this.manager = manager; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setManager(Person manager) { + this.manager = manager; + } + + public Person getManager() { + return manager; + } + } + + private Address oldMill; + private Person joonas; + private Team vaadin; + + @Before + public void setUp() { + oldMill = new Address("Ruukinkatu 2-4", 20540); + joonas = new Person("Joonas", oldMill); + vaadin = new Team("Vaadin", joonas); + } + + @Test + public void testSingleLevelNestedSimpleProperty() { + NestedMethodProperty nameProperty = new NestedMethodProperty( + vaadin, "name"); + + Assert.assertEquals(String.class, nameProperty.getType()); + Assert.assertEquals("Vaadin", nameProperty.getValue()); + } + + @Test + public void testSingleLevelNestedObjectProperty() { + NestedMethodProperty managerProperty = new NestedMethodProperty( + vaadin, "manager"); + + Assert.assertEquals(Person.class, managerProperty.getType()); + Assert.assertEquals(joonas, managerProperty.getValue()); + } + + @Test + public void testMultiLevelNestedProperty() { + NestedMethodProperty managerNameProperty = new NestedMethodProperty( + vaadin, "manager.name"); + NestedMethodProperty
addressProperty = new NestedMethodProperty
( + vaadin, "manager.address"); + NestedMethodProperty streetProperty = new NestedMethodProperty( + vaadin, "manager.address.street"); + NestedMethodProperty postalCodePrimitiveProperty = new NestedMethodProperty( + vaadin, "manager.address.postalCodePrimitive"); + NestedMethodProperty postalCodeObjectProperty = new NestedMethodProperty( + vaadin, "manager.address.postalCodeObject"); + NestedMethodProperty booleanProperty = new NestedMethodProperty( + vaadin, "manager.address.boolean"); + + Assert.assertEquals(String.class, managerNameProperty.getType()); + Assert.assertEquals("Joonas", managerNameProperty.getValue()); + + Assert.assertEquals(Address.class, addressProperty.getType()); + Assert.assertEquals(oldMill, addressProperty.getValue()); + + Assert.assertEquals(String.class, streetProperty.getType()); + Assert.assertEquals("Ruukinkatu 2-4", streetProperty.getValue()); + + Assert.assertEquals(Integer.class, + postalCodePrimitiveProperty.getType()); + Assert.assertEquals(Integer.valueOf(20540), + postalCodePrimitiveProperty.getValue()); + + Assert.assertEquals(Integer.class, postalCodeObjectProperty.getType()); + Assert.assertEquals(Integer.valueOf(20540), + postalCodeObjectProperty.getValue()); + + Assert.assertEquals(Boolean.class, booleanProperty.getType()); + Assert.assertEquals(Boolean.TRUE, booleanProperty.getValue()); + } + + @Test + public void testEmptyPropertyName() { + try { + new NestedMethodProperty(vaadin, ""); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + + try { + new NestedMethodProperty(vaadin, " "); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + } + + @Test + public void testInvalidPropertyName() { + try { + new NestedMethodProperty(vaadin, "."); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + try { + new NestedMethodProperty(vaadin, ".manager"); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + try { + new NestedMethodProperty(vaadin, "manager."); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + try { + new NestedMethodProperty(vaadin, "manager..name"); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + } + + @Test + public void testInvalidNestedPropertyName() { + try { + new NestedMethodProperty(vaadin, "member"); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + + try { + new NestedMethodProperty(vaadin, "manager.pet"); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + + try { + new NestedMethodProperty(vaadin, "manager.address.city"); + fail(); + } catch (IllegalArgumentException e) { + // should get exception + } + } + + @Test + public void testNullNestedProperty() { + NestedMethodProperty managerNameProperty = new NestedMethodProperty( + vaadin, "manager.name"); + NestedMethodProperty streetProperty = new NestedMethodProperty( + vaadin, "manager.address.street"); + + joonas.setAddress(null); + assertNull(streetProperty.getValue()); + + vaadin.setManager(null); + assertNull(managerNameProperty.getValue()); + assertNull(streetProperty.getValue()); + + vaadin.setManager(joonas); + Assert.assertEquals("Joonas", managerNameProperty.getValue()); + Assert.assertNull(streetProperty.getValue()); + + } + + @Test + public void testMultiLevelNestedPropertySetValue() { + NestedMethodProperty managerNameProperty = new NestedMethodProperty( + vaadin, "manager.name"); + NestedMethodProperty
addressProperty = new NestedMethodProperty
( + vaadin, "manager.address"); + NestedMethodProperty streetProperty = new NestedMethodProperty( + vaadin, "manager.address.street"); + NestedMethodProperty postalCodePrimitiveProperty = new NestedMethodProperty( + vaadin, "manager.address.postalCodePrimitive"); + NestedMethodProperty postalCodeObjectProperty = new NestedMethodProperty( + vaadin, "manager.address.postalCodeObject"); + + managerNameProperty.setValue("Joonas L"); + Assert.assertEquals("Joonas L", joonas.getName()); + streetProperty.setValue("Ruukinkatu"); + Assert.assertEquals("Ruukinkatu", oldMill.getStreet()); + postalCodePrimitiveProperty.setValue(0); + postalCodeObjectProperty.setValue(1); + Assert.assertEquals(0, oldMill.getPostalCodePrimitive()); + Assert.assertEquals(Integer.valueOf(1), oldMill.getPostalCodeObject()); + + postalCodeObjectProperty.setValue(null); + Assert.assertNull(oldMill.getPostalCodeObject()); + + Address address2 = new Address("Other street", 12345); + addressProperty.setValue(address2); + Assert.assertEquals("Other street", streetProperty.getValue()); + } + + @Test + public void testSerialization() throws IOException, ClassNotFoundException { + NestedMethodProperty streetProperty = new NestedMethodProperty( + vaadin, "manager.address.street"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ObjectOutputStream(baos).writeObject(streetProperty); + @SuppressWarnings("unchecked") + NestedMethodProperty property2 = (NestedMethodProperty) new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray())).readObject(); + + Assert.assertEquals("Ruukinkatu 2-4", property2.getValue()); + } + + @Test + public void testSerializationWithIntermediateNull() + throws IOException, ClassNotFoundException { + vaadin.setManager(null); + NestedMethodProperty streetProperty = new NestedMethodProperty( + vaadin, "manager.address.street"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ObjectOutputStream(baos).writeObject(streetProperty); + @SuppressWarnings("unchecked") + NestedMethodProperty property2 = (NestedMethodProperty) new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray())).readObject(); + + Assert.assertNull(property2.getValue()); + } + + @Test + public void testIsReadOnly() { + NestedMethodProperty streetProperty = new NestedMethodProperty( + vaadin, "manager.address.street"); + NestedMethodProperty booleanProperty = new NestedMethodProperty( + vaadin, "manager.address.boolean"); + + Assert.assertFalse(streetProperty.isReadOnly()); + Assert.assertTrue(booleanProperty.isReadOnly()); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java new file mode 100644 index 0000000000..e0307034cf --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java @@ -0,0 +1,102 @@ +package com.vaadin.data.util; + +import org.junit.Assert; +import org.junit.Test; + +public class ObjectPropertyTest { + + public static class TestSuperClass { + private String name; + + public TestSuperClass(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return getName(); + } + } + + public static class TestSubClass extends TestSuperClass { + public TestSubClass(String name) { + super("Subclass: " + name); + } + } + + private TestSuperClass super1 = new TestSuperClass("super1"); + private TestSubClass sub1 = new TestSubClass("sub1"); + + @Test + public void testSimple() { + ObjectProperty prop1 = new ObjectProperty( + super1, TestSuperClass.class); + Assert.assertEquals("super1", prop1.getValue().getName()); + prop1 = new ObjectProperty(super1); + Assert.assertEquals("super1", prop1.getValue().getName()); + + ObjectProperty prop2 = new ObjectProperty( + sub1, TestSubClass.class); + Assert.assertEquals("Subclass: sub1", prop2.getValue().getName()); + prop2 = new ObjectProperty(sub1); + Assert.assertEquals("Subclass: sub1", prop2.getValue().getName()); + } + + @Test + public void testSetValueObjectSuper() { + ObjectProperty prop = new ObjectProperty( + super1, TestSuperClass.class); + Assert.assertEquals("super1", prop.getValue().getName()); + prop.setValue(new TestSuperClass("super2")); + Assert.assertEquals("super1", super1.getName()); + Assert.assertEquals("super2", prop.getValue().getName()); + } + + @Test + public void testSetValueObjectSub() { + ObjectProperty prop = new ObjectProperty( + sub1, TestSubClass.class); + Assert.assertEquals("Subclass: sub1", prop.getValue().getName()); + prop.setValue(new TestSubClass("sub2")); + Assert.assertEquals("Subclass: sub1", sub1.getName()); + Assert.assertEquals("Subclass: sub2", prop.getValue().getName()); + } + + @Test + public void testSetValueStringSuper() { + ObjectProperty prop = new ObjectProperty( + super1, TestSuperClass.class); + Assert.assertEquals("super1", prop.getValue().getName()); + prop.setValue(new TestSuperClass("super2")); + Assert.assertEquals("super1", super1.getName()); + Assert.assertEquals("super2", prop.getValue().getName()); + } + + @Test + public void testSetValueStringSub() { + ObjectProperty prop = new ObjectProperty( + sub1, TestSubClass.class); + Assert.assertEquals("Subclass: sub1", prop.getValue().getName()); + prop.setValue(new TestSubClass("sub2")); + Assert.assertEquals("Subclass: sub1", sub1.getName()); + Assert.assertEquals("Subclass: sub2", prop.getValue().getName()); + } + + @Test + public void testMixedGenerics() { + ObjectProperty prop = new ObjectProperty( + sub1); + Assert.assertEquals("Subclass: sub1", prop.getValue().getName()); + Assert.assertEquals(prop.getType(), TestSubClass.class); + // create correct subclass based on the runtime type of the instance + // given to ObjectProperty constructor, which is a subclass of the type + // parameter + prop.setValue(new TestSubClass("sub2")); + Assert.assertEquals("Subclass: sub2", prop.getValue().getName()); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java new file mode 100644 index 0000000000..5f64c0e8d8 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java @@ -0,0 +1,120 @@ +package com.vaadin.data.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.junit.Assert; +import org.junit.Test; + +public class PerformanceTestIndexedContainerTest { + + private static final int REPEATS = 10; + private final static int ITEMS = 50000; + private static final long ADD_ITEM_FAIL_THRESHOLD = 200; + // TODO should improve performance of these methods + private static final long ADD_ITEM_AT_FAIL_THRESHOLD = 5000; + private static final long ADD_ITEM_AFTER_FAIL_THRESHOLD = 5000; + private static final long ADD_ITEM_AFTER_LAST_FAIL_THRESHOLD = 5000; + private static final long ADD_ITEMS_CONSTRUCTOR_FAIL_THRESHOLD = 200; + + @Test + public void testAddItemPerformance() { + Collection times = new ArrayList(); + for (int j = 0; j < REPEATS; ++j) { + IndexedContainer c = new IndexedContainer(); + long start = System.currentTimeMillis(); + for (int i = 0; i < ITEMS; i++) { + c.addItem(); + } + times.add(System.currentTimeMillis() - start); + } + checkMedian(ITEMS, times, "IndexedContainer.addItem()", + ADD_ITEM_FAIL_THRESHOLD); + } + + @Test + public void testAddItemAtPerformance() { + Collection times = new ArrayList(); + for (int j = 0; j < REPEATS; ++j) { + IndexedContainer c = new IndexedContainer(); + long start = System.currentTimeMillis(); + for (int i = 0; i < ITEMS; i++) { + c.addItemAt(0); + } + times.add(System.currentTimeMillis() - start); + } + checkMedian(ITEMS, times, "IndexedContainer.addItemAt()", + ADD_ITEM_AT_FAIL_THRESHOLD); + } + + @Test + public void testAddItemAfterPerformance() { + Object initialId = "Item0"; + Collection times = new ArrayList(); + for (int j = 0; j < REPEATS; ++j) { + IndexedContainer c = new IndexedContainer(); + c.addItem(initialId); + long start = System.currentTimeMillis(); + for (int i = 0; i < ITEMS; i++) { + c.addItemAfter(initialId); + } + times.add(System.currentTimeMillis() - start); + } + checkMedian(ITEMS, times, "IndexedContainer.addItemAfter()", + ADD_ITEM_AFTER_FAIL_THRESHOLD); + } + + @Test + public void testAddItemAfterLastPerformance() { + // TODO running with less items because slow otherwise + Collection times = new ArrayList(); + for (int j = 0; j < REPEATS; ++j) { + IndexedContainer c = new IndexedContainer(); + c.addItem(); + long start = System.currentTimeMillis(); + for (int i = 0; i < ITEMS / 3; i++) { + c.addItemAfter(c.lastItemId()); + } + times.add(System.currentTimeMillis() - start); + } + checkMedian(ITEMS / 3, times, "IndexedContainer.addItemAfter(lastId)", + ADD_ITEM_AFTER_LAST_FAIL_THRESHOLD); + } + + @Test + public void testAddItemsConstructorPerformance() { + Collection items = new ArrayList(50000); + for (int i = 0; i < ITEMS; ++i) { + items.add(new Object()); + } + + SortedSet times = new TreeSet(); + for (int j = 0; j < REPEATS; ++j) { + long start = System.currentTimeMillis(); + new IndexedContainer(items); + times.add(System.currentTimeMillis() - start); + } + checkMedian(ITEMS, times, "IndexedContainer(Collection)", + ADD_ITEMS_CONSTRUCTOR_FAIL_THRESHOLD); + } + + private void checkMedian(int items, Collection times, + String methodName, long threshold) { + long median = median(times); + System.out.println( + methodName + " timings (ms) for " + items + " items: " + times); + Assert.assertTrue(methodName + " too slow, median time " + median + + "ms for " + items + " items", median <= threshold); + } + + private Long median(Collection times) { + ArrayList list = new ArrayList(times); + Collections.sort(list); + // not exact median in some cases, but good enough + return list.get(list.size() / 2); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java new file mode 100644 index 0000000000..af9db229c5 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java @@ -0,0 +1,84 @@ +package com.vaadin.data.util; + +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Property; +import com.vaadin.data.util.NestedMethodPropertyTest.Person; + +public class PropertyDescriptorTest { + + @Test + public void testMethodPropertyDescriptorSerialization() throws Exception { + PropertyDescriptor[] pds = Introspector.getBeanInfo(Person.class) + .getPropertyDescriptors(); + + MethodPropertyDescriptor descriptor = null; + + for (PropertyDescriptor pd : pds) { + if ("name".equals(pd.getName())) { + descriptor = new MethodPropertyDescriptor(pd.getName(), + String.class, pd.getReadMethod(), pd.getWriteMethod()); + break; + } + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ObjectOutputStream(baos).writeObject(descriptor); + @SuppressWarnings("unchecked") + VaadinPropertyDescriptor descriptor2 = (VaadinPropertyDescriptor) new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray())).readObject(); + + Property property = descriptor2 + .createProperty(new Person("John", null)); + Assert.assertEquals("John", property.getValue()); + } + + @Test + public void testSimpleNestedPropertyDescriptorSerialization() + throws Exception { + NestedPropertyDescriptor pd = new NestedPropertyDescriptor( + "name", Person.class); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ObjectOutputStream(baos).writeObject(pd); + @SuppressWarnings("unchecked") + VaadinPropertyDescriptor pd2 = (VaadinPropertyDescriptor) new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray())).readObject(); + + Property property = pd2.createProperty(new Person("John", null)); + Assert.assertEquals("John", property.getValue()); + } + + @Test + public void testNestedPropertyDescriptorSerialization() throws Exception { + NestedPropertyDescriptor pd = new NestedPropertyDescriptor( + "address.street", Person.class); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ObjectOutputStream(baos).writeObject(pd); + @SuppressWarnings("unchecked") + VaadinPropertyDescriptor pd2 = (VaadinPropertyDescriptor) new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray())).readObject(); + + Property property = pd2.createProperty(new Person("John", null)); + Assert.assertNull(property.getValue()); + } + + @Test + public void testMethodPropertyDescriptorWithPrimitivePropertyType() + throws Exception { + MethodPropertyDescriptor pd = new MethodPropertyDescriptor( + "age", int.class, Person.class.getMethod("getAge"), + Person.class.getMethod("setAge", int.class)); + + Assert.assertEquals(Integer.class, pd.getPropertyType()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java new file mode 100644 index 0000000000..fc91a20dd0 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java @@ -0,0 +1,432 @@ +package com.vaadin.data.util; + +import java.util.Iterator; + +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Item.PropertySetChangeEvent; +import com.vaadin.data.Item.PropertySetChangeListener; + +public class PropertySetItemTest { + + private static final String ID1 = "id1"; + private static final String ID2 = "id2"; + private static final String ID3 = "id3"; + + private static final String VALUE1 = "value1"; + private static final String VALUE2 = "value2"; + private static final String VALUE3 = "value3"; + + private ObjectProperty prop1; + private ObjectProperty prop2; + private ObjectProperty prop3; + + private PropertySetChangeListener propertySetListenerMock; + private PropertySetChangeListener propertySetListenerMock2; + + @Before + public void setUp() { + prop1 = new ObjectProperty(VALUE1, String.class); + prop2 = new ObjectProperty(VALUE2, String.class); + prop3 = new ObjectProperty(VALUE3, String.class); + + propertySetListenerMock = EasyMock + .createStrictMock(PropertySetChangeListener.class); + propertySetListenerMock2 = EasyMock + .createMock(PropertySetChangeListener.class); + } + + @After + public void tearDown() { + prop1 = null; + prop2 = null; + prop3 = null; + + propertySetListenerMock = null; + propertySetListenerMock2 = null; + } + + private PropertysetItem createPropertySetItem() { + return new PropertysetItem(); + } + + @Test + public void testEmptyItem() { + PropertysetItem item = createPropertySetItem(); + Assert.assertNotNull(item.getItemPropertyIds()); + Assert.assertEquals(0, item.getItemPropertyIds().size()); + } + + @Test + public void testGetProperty() { + PropertysetItem item = createPropertySetItem(); + + Assert.assertNull(item.getItemProperty(ID1)); + + item.addItemProperty(ID1, prop1); + + Assert.assertEquals(prop1, item.getItemProperty(ID1)); + Assert.assertNull(item.getItemProperty(ID2)); + } + + @Test + public void testAddSingleProperty() { + PropertysetItem item = createPropertySetItem(); + + item.addItemProperty(ID1, prop1); + Assert.assertEquals(1, item.getItemPropertyIds().size()); + Object firstValue = item.getItemPropertyIds().iterator().next(); + Assert.assertEquals(ID1, firstValue); + Assert.assertEquals(prop1, item.getItemProperty(ID1)); + } + + @Test + public void testAddMultipleProperties() { + PropertysetItem item = createPropertySetItem(); + + item.addItemProperty(ID1, prop1); + Assert.assertEquals(1, item.getItemPropertyIds().size()); + Assert.assertEquals(prop1, item.getItemProperty(ID1)); + + item.addItemProperty(ID2, prop2); + Assert.assertEquals(2, item.getItemPropertyIds().size()); + Assert.assertEquals(prop1, item.getItemProperty(ID1)); + Assert.assertEquals(prop2, item.getItemProperty(ID2)); + + item.addItemProperty(ID3, prop3); + Assert.assertEquals(3, item.getItemPropertyIds().size()); + } + + @Test + public void testAddedPropertyOrder() { + PropertysetItem item = createPropertySetItem(); + item.addItemProperty(ID1, prop1); + item.addItemProperty(ID2, prop2); + item.addItemProperty(ID3, prop3); + + Iterator it = item.getItemPropertyIds().iterator(); + Assert.assertEquals(ID1, it.next()); + Assert.assertEquals(ID2, it.next()); + Assert.assertEquals(ID3, it.next()); + } + + @Test + public void testAddPropertyTwice() { + PropertysetItem item = createPropertySetItem(); + Assert.assertTrue(item.addItemProperty(ID1, prop1)); + Assert.assertFalse(item.addItemProperty(ID1, prop1)); + + Assert.assertEquals(1, item.getItemPropertyIds().size()); + Assert.assertEquals(prop1, item.getItemProperty(ID1)); + } + + @Test + public void testCannotChangeProperty() { + PropertysetItem item = createPropertySetItem(); + Assert.assertTrue(item.addItemProperty(ID1, prop1)); + + Assert.assertEquals(prop1, item.getItemProperty(ID1)); + + Assert.assertFalse(item.addItemProperty(ID1, prop2)); + + Assert.assertEquals(1, item.getItemPropertyIds().size()); + Assert.assertEquals(prop1, item.getItemProperty(ID1)); + } + + @Test + public void testRemoveProperty() { + PropertysetItem item = createPropertySetItem(); + item.addItemProperty(ID1, prop1); + item.removeItemProperty(ID1); + + Assert.assertEquals(0, item.getItemPropertyIds().size()); + Assert.assertNull(item.getItemProperty(ID1)); + } + + @Test + public void testRemovePropertyOrder() { + PropertysetItem item = createPropertySetItem(); + item.addItemProperty(ID1, prop1); + item.addItemProperty(ID2, prop2); + item.addItemProperty(ID3, prop3); + + item.removeItemProperty(ID2); + + Iterator it = item.getItemPropertyIds().iterator(); + Assert.assertEquals(ID1, it.next()); + Assert.assertEquals(ID3, it.next()); + } + + @Test + public void testRemoveNonExistentListener() { + PropertysetItem item = createPropertySetItem(); + item.removeListener(propertySetListenerMock); + } + + @Test + public void testRemoveListenerTwice() { + PropertysetItem item = createPropertySetItem(); + item.addListener(propertySetListenerMock); + item.removeListener(propertySetListenerMock); + item.removeListener(propertySetListenerMock); + } + + @Test + public void testAddPropertyNotification() { + // exactly one notification each time + PropertysetItem item = createPropertySetItem(); + + // Expectations and start test + propertySetListenerMock.itemPropertySetChange( + EasyMock.isA(PropertySetChangeEvent.class)); + EasyMock.replay(propertySetListenerMock); + + // Add listener and add a property -> should end up in listener once + item.addListener(propertySetListenerMock); + item.addItemProperty(ID1, prop1); + + // Ensure listener was called once + EasyMock.verify(propertySetListenerMock); + + // Remove the listener -> should not end up in listener when adding a + // property + item.removeListener(propertySetListenerMock); + item.addItemProperty(ID2, prop2); + + // Ensure listener still has been called only once + EasyMock.verify(propertySetListenerMock); + } + + @Test + public void testRemovePropertyNotification() { + // exactly one notification each time + PropertysetItem item = createPropertySetItem(); + item.addItemProperty(ID1, prop1); + item.addItemProperty(ID2, prop2); + + // Expectations and start test + propertySetListenerMock.itemPropertySetChange( + EasyMock.isA(PropertySetChangeEvent.class)); + EasyMock.replay(propertySetListenerMock); + + // Add listener and add a property -> should end up in listener once + item.addListener(propertySetListenerMock); + item.removeItemProperty(ID1); + + // Ensure listener was called once + EasyMock.verify(propertySetListenerMock); + + // Remove the listener -> should not end up in listener + item.removeListener(propertySetListenerMock); + item.removeItemProperty(ID2); + + // Ensure listener still has been called only once + EasyMock.verify(propertySetListenerMock); + } + + @Test + public void testItemEqualsNull() { + PropertysetItem item = createPropertySetItem(); + + Assert.assertFalse(item.equals(null)); + } + + @Test + public void testEmptyItemEquals() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + Assert.assertTrue(item1.equals(item2)); + } + + @Test + public void testItemEqualsSingleProperty() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + item2.addItemProperty(ID1, prop1); + PropertysetItem item3 = createPropertySetItem(); + item3.addItemProperty(ID1, prop1); + PropertysetItem item4 = createPropertySetItem(); + item4.addItemProperty(ID1, prop2); + PropertysetItem item5 = createPropertySetItem(); + item5.addItemProperty(ID2, prop2); + + Assert.assertFalse(item1.equals(item2)); + Assert.assertFalse(item1.equals(item3)); + Assert.assertFalse(item1.equals(item4)); + Assert.assertFalse(item1.equals(item5)); + + Assert.assertTrue(item2.equals(item3)); + Assert.assertFalse(item2.equals(item4)); + Assert.assertFalse(item2.equals(item5)); + + Assert.assertFalse(item3.equals(item4)); + Assert.assertFalse(item3.equals(item5)); + + Assert.assertFalse(item4.equals(item5)); + + Assert.assertFalse(item2.equals(item1)); + } + + @Test + public void testItemEqualsMultipleProperties() { + PropertysetItem item1 = createPropertySetItem(); + item1.addItemProperty(ID1, prop1); + + PropertysetItem item2 = createPropertySetItem(); + item2.addItemProperty(ID1, prop1); + item2.addItemProperty(ID2, prop2); + + PropertysetItem item3 = createPropertySetItem(); + item3.addItemProperty(ID1, prop1); + item3.addItemProperty(ID2, prop2); + + Assert.assertFalse(item1.equals(item2)); + + Assert.assertTrue(item2.equals(item3)); + } + + @Test + public void testItemEqualsPropertyOrder() { + PropertysetItem item1 = createPropertySetItem(); + item1.addItemProperty(ID1, prop1); + item1.addItemProperty(ID2, prop2); + + PropertysetItem item2 = createPropertySetItem(); + item2.addItemProperty(ID2, prop2); + item2.addItemProperty(ID1, prop1); + + Assert.assertFalse(item1.equals(item2)); + } + + @Test + public void testEqualsSingleListener() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + item1.addListener(propertySetListenerMock); + + Assert.assertFalse(item1.equals(item2)); + Assert.assertFalse(item2.equals(item1)); + + item2.addListener(propertySetListenerMock); + + Assert.assertTrue(item1.equals(item2)); + Assert.assertTrue(item2.equals(item1)); + } + + @Test + public void testEqualsMultipleListeners() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + item1.addListener(propertySetListenerMock); + item1.addListener(propertySetListenerMock2); + + item2.addListener(propertySetListenerMock); + + Assert.assertFalse(item1.equals(item2)); + Assert.assertFalse(item2.equals(item1)); + + item2.addListener(propertySetListenerMock2); + + Assert.assertTrue(item1.equals(item2)); + Assert.assertTrue(item2.equals(item1)); + } + + @Test + public void testEqualsAddRemoveListener() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + item1.addListener(propertySetListenerMock); + item1.removeListener(propertySetListenerMock); + + Assert.assertTrue(item1.equals(item2)); + Assert.assertTrue(item2.equals(item1)); + } + + @Test + public void testItemHashCodeEmpty() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + Assert.assertEquals(item1.hashCode(), item2.hashCode()); + } + + @Test + public void testItemHashCodeAddProperties() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + Assert.assertEquals(item1.hashCode(), item2.hashCode()); + + item1.addItemProperty(ID1, prop1); + item1.addItemProperty(ID2, prop2); + // hashCodes can be equal even if items are different + + item2.addItemProperty(ID1, prop1); + item2.addItemProperty(ID2, prop2); + // but here hashCodes must be equal + Assert.assertEquals(item1.hashCode(), item2.hashCode()); + } + + @Test + public void testItemHashCodeAddListeners() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + Assert.assertEquals(item1.hashCode(), item2.hashCode()); + + item1.addListener(propertySetListenerMock); + // hashCodes can be equal even if items are different + + item2.addListener(propertySetListenerMock); + // but here hashCodes must be equal + Assert.assertEquals(item1.hashCode(), item2.hashCode()); + } + + @Test + public void testItemHashCodeAddRemoveProperty() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + item1.addItemProperty(ID1, prop1); + item1.removeItemProperty(ID1); + + Assert.assertEquals(item1.hashCode(), item2.hashCode()); + } + + @Test + public void testItemHashCodeAddRemoveListener() { + PropertysetItem item1 = createPropertySetItem(); + PropertysetItem item2 = createPropertySetItem(); + + item1.addListener(propertySetListenerMock); + item1.removeListener(propertySetListenerMock); + + Assert.assertEquals(item1.hashCode(), item2.hashCode()); + } + + @Test + public void testToString() { + // toString() behavior is specified in the class javadoc + PropertysetItem item = createPropertySetItem(); + + Assert.assertEquals("", item.toString()); + + item.addItemProperty(ID1, prop1); + + Assert.assertEquals(String.valueOf(prop1.getValue()), item.toString()); + + item.addItemProperty(ID2, prop2); + + Assert.assertEquals(String.valueOf(prop1.getValue()) + " " + + String.valueOf(prop2.getValue()), item.toString()); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java new file mode 100644 index 0000000000..df4258f316 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java @@ -0,0 +1,35 @@ +package com.vaadin.data.util; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.PropertyId; +import com.vaadin.v7.ui.LegacyTextField; + +public class ReflectToolsGetSuperFieldTest { + + @Test + public void getFieldFromSuperClass() { + class MyClass { + @PropertyId("testProperty") + LegacyTextField test = new LegacyTextField("This is a test"); + } + class MySubClass extends MyClass { + // no fields here + } + + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("testProperty", + new ObjectProperty("Value of testProperty")); + + MySubClass form = new MySubClass(); + + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue("Value of testProperty".equals(form.test.getValue())); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java new file mode 100644 index 0000000000..ef3e416f96 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java @@ -0,0 +1,117 @@ +/* + * 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.data.util; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.v7.ui.LegacyTextField; + +/** + * Test verifying that TransactionalPropertyWrapper removes it's listener from + * wrapped Property + * + * @since 7.1.15 + * @author Vaadin Ltd + */ +public class TransactionalPropertyWrapperTest { + + @SuppressWarnings("serial") + public class TestingProperty + extends ObjectProperty { + + private List listeners = new ArrayList(); + + public TestingProperty(Object value) { + super(value); + } + + @Override + public void addValueChangeListener(ValueChangeListener listener) { + super.addValueChangeListener(listener); + listeners.add(listener); + } + + @Override + public void removeValueChangeListener(ValueChangeListener listener) { + super.removeValueChangeListener(listener); + if (listeners.contains(listener)) { + listeners.remove(listener); + } + } + + public boolean hasListeners() { + return !listeners.isEmpty(); + } + } + + private final LegacyTextField nameField = new LegacyTextField("Name"); + private final LegacyTextField ageField = new LegacyTextField("Age"); + private final LegacyTextField unboundField = new LegacyTextField( + "No FieldGroup"); + private final TestingProperty unboundProp = new TestingProperty( + "Hello World"); + private final PropertysetItem item = new PropertysetItem(); + + @Test + public void fieldGroupBindAndUnbind() { + item.addItemProperty("name", + new TestingProperty("Just some text")); + item.addItemProperty("age", new TestingProperty("42")); + + final FieldGroup binder = new FieldGroup(item); + binder.setBuffered(false); + + for (int i = 0; i < 2; ++i) { + binder.bind(nameField, "name"); + binder.bind(ageField, "age"); + unboundField.setPropertyDataSource(unboundProp); + + assertTrue("No listeners in Properties", fieldsHaveListeners(true)); + + binder.unbind(nameField); + binder.unbind(ageField); + unboundField.setPropertyDataSource(null); + + assertTrue("Listeners in Properties after unbinding", + fieldsHaveListeners(false)); + } + } + + /** + * Check that all listeners have same hasListeners() response + * + * @param expected + * expected response + * @return true if all are the same as expected. false if not + */ + private boolean fieldsHaveListeners(boolean expected) { + for (Object id : item.getItemPropertyIds()) { + TestingProperty itemProperty = (TestingProperty) item + .getItemProperty(id); + + if (itemProperty.hasListeners() != expected) { + return false; + } + } + return unboundProp.hasListeners() == expected; + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java new file mode 100644 index 0000000000..1b93a3063d --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java @@ -0,0 +1,163 @@ +/* + * 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.tests.server; + +import java.util.EventObject; + +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.event.ContextClickEvent; +import com.vaadin.event.ContextClickEvent.ContextClickListener; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.LegacyGrid.GridContextClickEvent; +import com.vaadin.ui.Table.TableContextClickEvent; + +/** + * Server-side unit tests to see that context click events are sent to listeners + * correctly. + * + * If a listener is listening to a super type of an event, it should get the + * event. i.e. Listening to ContextClickEvent, it should get the specialized + * GridContextClickEvent as well. + * + * If a listener is listening to a sub-type of an event, it should not get the + * super version. i.e. Listening to GridContextClickEvent, it should not get a + * plain ContextClickEvent. + */ +public class ContextClickListenerTest extends AbstractComponent { + + private final static ContextClickEvent contextClickEvent = EasyMock + .createMock(ContextClickEvent.class); + private final static GridContextClickEvent gridContextClickEvent = EasyMock + .createMock(GridContextClickEvent.class); + private final static TableContextClickEvent tableContextClickEvent = EasyMock + .createMock(TableContextClickEvent.class); + + private final AssertListener contextListener = new AssertListener(); + private final AssertListener ctxtListener2 = new AssertListener(); + + public static class AssertListener implements ContextClickListener { + + private Class expected = null; + private String error = null; + + @Override + public void contextClick(ContextClickEvent event) { + if (expected == null) { + error = "Unexpected context click event."; + return; + } + + if (!expected.isAssignableFrom(event.getClass())) { + error = "Expected event type did not match the actual event."; + } + + expected = null; + } + + public void expect(Class clazz) { + validate(); + expected = clazz; + } + + public void validate() { + if (expected != null) { + Assert.fail("Expected context click never happened."); + } else if (error != null) { + Assert.fail(error); + } + } + } + + @Test + public void testListenerGetsASubClass() { + addContextClickListener(contextListener); + contextListener.expect(GridContextClickEvent.class); + fireEvent(gridContextClickEvent); + } + + @Test + public void testListenerGetsExactClass() { + addContextClickListener(contextListener); + contextListener.expect(ContextClickEvent.class); + fireEvent(contextClickEvent); + } + + /** + * Multiple listeners should get fitting events. + */ + @Test + public void testMultipleListenerGetEvents() { + addContextClickListener(ctxtListener2); + addContextClickListener(contextListener); + + ctxtListener2.expect(GridContextClickEvent.class); + contextListener.expect(GridContextClickEvent.class); + + fireEvent(gridContextClickEvent); + } + + @Test + public void testAddAndRemoveListener() { + addContextClickListener(contextListener); + contextListener.expect(ContextClickEvent.class); + + fireEvent(contextClickEvent); + + removeContextClickListener(contextListener); + + fireEvent(contextClickEvent); + } + + @Test + public void testAddAndRemoveMultipleListeners() { + addContextClickListener(ctxtListener2); + addContextClickListener(contextListener); + + ctxtListener2.expect(GridContextClickEvent.class); + contextListener.expect(GridContextClickEvent.class); + fireEvent(gridContextClickEvent); + + removeContextClickListener(ctxtListener2); + + contextListener.expect(GridContextClickEvent.class); + fireEvent(gridContextClickEvent); + } + + @Test(expected = AssertionError.class) + public void testExpectedEventNotReceived() { + addContextClickListener(contextListener); + contextListener.expect(GridContextClickEvent.class); + fireEvent(contextClickEvent); + } + + @Test(expected = AssertionError.class) + public void testUnexpectedEventReceived() { + addContextClickListener(contextListener); + fireEvent(gridContextClickEvent); + } + + @Override + protected void fireEvent(EventObject event) { + super.fireEvent(event); + + // Validate listeners automatically. + ctxtListener2.validate(); + contextListener.validate(); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java new file mode 100644 index 0000000000..f5c315b440 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java @@ -0,0 +1,172 @@ +package com.vaadin.tests.server.component.fieldgroup; + +import static org.junit.Assert.assertEquals; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Item; +import com.vaadin.data.fieldgroup.BeanFieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.fieldgroup.PropertyId; +import com.vaadin.data.util.BeanItem; +import com.vaadin.ui.RichTextArea; +import com.vaadin.v7.ui.LegacyField; +import com.vaadin.v7.ui.LegacyTextField; + +public class BeanFieldGroupTest { + + private static final String DEFAULT_FOR_BASIC_FIELD = "default"; + + public static class MyBean { + + private String basicField = DEFAULT_FOR_BASIC_FIELD; + + private String anotherField; + + private MyNestedBean nestedBean = new MyNestedBean(); + + public MyNestedBean getNestedBean() { + return nestedBean; + } + + /** + * @return the basicField + */ + public String getBasicField() { + return basicField; + } + + /** + * @param basicField + * the basicField to set + */ + public void setBasicField(String basicField) { + this.basicField = basicField; + } + + /** + * @return the anotherField + */ + public String getAnotherField() { + return anotherField; + } + + /** + * @param anotherField + * the anotherField to set + */ + public void setAnotherField(String anotherField) { + this.anotherField = anotherField; + } + } + + public static class MyNestedBean { + + private String hello = "Hello world"; + + public String getHello() { + return hello; + } + } + + public static class ViewStub { + + LegacyTextField basicField = new LegacyTextField(); + + @PropertyId("anotherField") + LegacyTextField boundWithAnnotation = new LegacyTextField(); + } + + @SuppressWarnings("unchecked") + @Test + public void testStaticBindingHelper() { + MyBean myBean = new MyBean(); + + ViewStub viewStub = new ViewStub(); + BeanFieldGroup bindFields = BeanFieldGroup + .bindFieldsUnbuffered(myBean, viewStub); + + LegacyField field = (LegacyField) bindFields + .getField("basicField"); + Assert.assertEquals(DEFAULT_FOR_BASIC_FIELD, myBean.basicField); + field.setValue("Foo"); + Assert.assertEquals("Foo", myBean.basicField); + + field = (LegacyField) bindFields.getField("anotherField"); + field.setValue("Foo"); + Assert.assertEquals("Foo", myBean.anotherField); + } + + @SuppressWarnings("unchecked") + @Test + public void testStaticBufferedBindingHelper() throws CommitException { + MyBean myBean = new MyBean(); + + ViewStub viewStub = new ViewStub(); + BeanFieldGroup bindFields = BeanFieldGroup + .bindFieldsBuffered(myBean, viewStub); + + LegacyField basicField = (LegacyField) bindFields + .getField("basicField"); + basicField.setValue("Foo"); + Assert.assertEquals(DEFAULT_FOR_BASIC_FIELD, myBean.basicField); + + LegacyField anotherField = (LegacyField) bindFields + .getField("anotherField"); + anotherField.setValue("Foo"); + Assert.assertNull(myBean.anotherField); + + bindFields.commit(); + + Assert.assertEquals("Foo", myBean.basicField); + Assert.assertEquals("Foo", myBean.anotherField); + + } + + @Test + public void buildAndBindNestedProperty() { + + MyBean bean = new MyBean(); + + BeanFieldGroup bfg = new BeanFieldGroup(MyBean.class); + bfg.setItemDataSource(bean); + + com.vaadin.v7.ui.LegacyField helloField = bfg + .buildAndBind("Hello string", "nestedBean.hello"); + assertEquals(bean.nestedBean.hello, helloField.getValue().toString()); + } + + @Test + public void buildAndBindNestedRichTextAreaProperty() { + + MyBean bean = new MyBean(); + + BeanFieldGroup bfg = new BeanFieldGroup(MyBean.class); + bfg.setItemDataSource(bean); + + RichTextArea helloField = bfg.buildAndBind("Hello string", + "nestedBean.hello", RichTextArea.class); + assertEquals(bean.nestedBean.hello, helloField.getValue().toString()); + } + + @Test + public void setDataSource_nullBean_nullBeanIsSetInDataSource() { + BeanFieldGroup group = new BeanFieldGroup(MyBean.class); + + group.setItemDataSource((MyBean) null); + + BeanItem dataSource = group.getItemDataSource(); + Assert.assertNull("Data source is null for null bean", dataSource); + } + + @Test + public void setDataSource_nullItem_nullDataSourceIsSet() { + BeanFieldGroup group = new BeanFieldGroup(MyBean.class); + + group.setItemDataSource((Item) null); + BeanItem dataSource = group.getItemDataSource(); + Assert.assertNull("Group returns not null data source", dataSource); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java new file mode 100644 index 0000000000..a324c02cfc --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java @@ -0,0 +1,85 @@ +package com.vaadin.tests.server.component.fieldgroup; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.util.ObjectProperty; +import com.vaadin.data.util.PropertysetItem; +import com.vaadin.ui.FormLayout; +import com.vaadin.v7.ui.LegacyTextField; + +public class CaseInsensitiveBindingTest { + + @Test + public void caseInsensitivityAndUnderscoreRemoval() { + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("LastName", new ObjectProperty("Sparrow")); + + class MyForm extends FormLayout { + LegacyTextField lastName = new LegacyTextField("Last name"); + + public MyForm() { + + // Should bind to the LastName property + addComponent(lastName); + } + } + + MyForm form = new MyForm(); + + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue("Sparrow".equals(form.lastName.getValue())); + } + + @Test + public void UnderscoreRemoval() { + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("first_name", new ObjectProperty("Jack")); + + class MyForm extends FormLayout { + LegacyTextField firstName = new LegacyTextField("First name"); + + public MyForm() { + // Should bind to the first_name property + addComponent(firstName); + } + } + + MyForm form = new MyForm(); + + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue("Jack".equals(form.firstName.getValue())); + } + + @Test + public void perfectMatchPriority() { + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("first_name", + new ObjectProperty("Not this")); + item.addItemProperty("firstName", new ObjectProperty("This")); + + class MyForm extends FormLayout { + LegacyTextField firstName = new LegacyTextField("First name"); + + public MyForm() { + // should bind to the firstName property, not first_name + // property + addComponent(firstName); + } + } + + MyForm form = new MyForm(); + + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue("This".equals(form.firstName.getValue())); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java new file mode 100644 index 0000000000..43d2bf8a6a --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java @@ -0,0 +1,149 @@ +/* + * 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.tests.server.component.fieldgroup; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.util.AbstractProperty; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.ui.LegacyField; +import com.vaadin.v7.ui.LegacyTextField; + +/** + * + * Tests for {@link FieldGroup}. + * + * @author Vaadin Ltd + */ +public class FieldGroupTest { + + @Test + public void setReadOnly_readOnlyAndNoDataSource_fieldIsReadOnly() { + FieldGroup fieldGroup = new FieldGroup(); + + LegacyTextField field = new LegacyTextField(); + fieldGroup.bind(field, "property"); + + fieldGroup.setReadOnly(true); + + Assert.assertTrue("Field is not read only", field.isReadOnly()); + } + + @Test + public void setReadOnly_writableAndNoDataSource_fieldIsWritable() { + FieldGroup fieldGroup = new FieldGroup(); + + LegacyTextField field = new LegacyTextField(); + fieldGroup.bind(field, "property"); + + fieldGroup.setReadOnly(false); + + Assert.assertFalse("Field is not writable", field.isReadOnly()); + } + + @Test + public void commit_validationFailed_allValidationFailuresAvailable() + throws CommitException { + FieldGroup fieldGroup = new FieldGroup(); + + fieldGroup.setItemDataSource(new TestItem()); + + LegacyTextField field1 = new LegacyTextField(); + field1.setRequired(true); + fieldGroup.bind(field1, "prop1"); + + LegacyTextField field2 = new LegacyTextField(); + field2.setRequired(true); + fieldGroup.bind(field2, "prop2"); + + Set set = new HashSet<>(Arrays.asList(field1, field2)); + + try { + fieldGroup.commit(); + Assert.fail("No commit exception is thrown"); + } catch (CommitException exception) { + Map, ? extends InvalidValueException> invalidFields = exception + .getInvalidFields(); + for (Entry, ? extends InvalidValueException> entry : invalidFields + .entrySet()) { + set.remove(entry.getKey()); + } + Assert.assertEquals( + "Some fields are not found in the invalid fields map", 0, + set.size()); + Assert.assertEquals( + "Invalid value exception should be thrown for each field", + 2, invalidFields.size()); + } + } + + private static class TestItem implements Item { + + @Override + public Property getItemProperty(Object id) { + return new StringProperty(); + } + + @Override + public Collection getItemPropertyIds() { + return Arrays.asList("prop1", "prop2"); + } + + @Override + public boolean addItemProperty(Object id, Property property) + throws UnsupportedOperationException { + return false; + } + + @Override + public boolean removeItemProperty(Object id) + throws UnsupportedOperationException { + return false; + } + + } + + private static class StringProperty extends AbstractProperty { + + @Override + public String getValue() { + return null; + } + + @Override + public void setValue(String newValue) + throws Property.ReadOnlyException { + } + + @Override + public Class getType() { + return String.class; + } + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java new file mode 100644 index 0000000000..f14b966a2d --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java @@ -0,0 +1,51 @@ +package com.vaadin.tests.server.component.fieldgroup; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.util.BeanItem; +import com.vaadin.tests.data.bean.BeanWithReadOnlyField; +import com.vaadin.v7.ui.LegacyTextField; + +public class FieldGroupWithReadOnlyPropertiesTest { + + private LegacyTextField readOnlyField = new LegacyTextField(); + private LegacyTextField writableField = new LegacyTextField(); + + @Test + public void bindReadOnlyPropertyToFieldGroup() { + BeanWithReadOnlyField bean = new BeanWithReadOnlyField(); + BeanItem beanItem = new BeanItem( + bean); + beanItem.getItemProperty("readOnlyField").setReadOnly(true); + + FieldGroup fieldGroup = new FieldGroup(beanItem); + fieldGroup.bindMemberFields(this); + + assertTrue(readOnlyField.isReadOnly()); + assertFalse(writableField.isReadOnly()); + } + + @Test + public void fieldGroupSetReadOnlyTest() { + BeanWithReadOnlyField bean = new BeanWithReadOnlyField(); + BeanItem beanItem = new BeanItem( + bean); + beanItem.getItemProperty("readOnlyField").setReadOnly(true); + + FieldGroup fieldGroup = new FieldGroup(beanItem); + fieldGroup.bindMemberFields(this); + + fieldGroup.setReadOnly(true); + assertTrue(readOnlyField.isReadOnly()); + assertTrue(writableField.isReadOnly()); + + fieldGroup.setReadOnly(false); + assertTrue(readOnlyField.isReadOnly()); + assertFalse(writableField.isReadOnly()); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java new file mode 100644 index 0000000000..0ef30316ac --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java @@ -0,0 +1,53 @@ +package com.vaadin.tests.server.component.fieldgroup; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.PropertyId; +import com.vaadin.data.util.ObjectProperty; +import com.vaadin.data.util.PropertysetItem; +import com.vaadin.ui.FormLayout; +import com.vaadin.v7.ui.LegacyTextField; + +public class FieldNamedDescriptionTest { + + @Test + public void bindReadOnlyPropertyToFieldGroup() { + // Create an item + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("name", new ObjectProperty("Zaphod")); + item.addItemProperty("description", + new ObjectProperty("This is a description")); + + // Define a form as a class that extends some layout + class MyForm extends FormLayout { + // Member that will bind to the "name" property + LegacyTextField name = new LegacyTextField("Name"); + + // This member will not bind to the desctiptionProperty as the name + // description conflicts with something in the binding process + @PropertyId("description") + LegacyTextField description = new LegacyTextField("Description"); + + public MyForm() { + + // Add the fields + addComponent(name); + addComponent(description); + } + } + + // Create one + MyForm form = new MyForm(); + + // Now create a binder that can also creates the fields + // using the default field factory + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue(form.description.getValue().equals("This is a description")); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java new file mode 100644 index 0000000000..3bff93c042 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java @@ -0,0 +1,219 @@ +/* + * 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.tests.server.component.grid; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import com.vaadin.data.util.BeanItem; +import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.data.util.MethodProperty.MethodException; +import com.vaadin.tests.data.bean.Person; +import com.vaadin.ui.LegacyGrid; + +public class GridAddRowBuiltinContainerTest { + LegacyGrid grid = new LegacyGrid(); + Container.Indexed container; + + @Before + public void setUp() { + container = grid.getContainerDataSource(); + + grid.addColumn("myColumn"); + } + + @Test + public void testSimpleCase() { + Object itemId = grid.addRow("Hello"); + + Assert.assertEquals(Integer.valueOf(1), itemId); + + Assert.assertEquals("There should be one item in the container", 1, + container.size()); + + Assert.assertEquals("Hello", container.getItem(itemId) + .getItemProperty("myColumn").getValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testNullParameter() { + // cast to Object[] to distinguish from one null varargs value + grid.addRow((Object[]) null); + } + + @Test + public void testNullValue() { + // cast to Object to distinguish from a null varargs array + Object itemId = grid.addRow((Object) null); + + Assert.assertEquals(null, container.getItem(itemId) + .getItemProperty("myColumn").getValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddInvalidType() { + grid.addRow(Integer.valueOf(5)); + } + + @Test + public void testMultipleProperties() { + grid.addColumn("myOther", Integer.class); + + Object itemId = grid.addRow("Hello", Integer.valueOf(3)); + + Item item = container.getItem(itemId); + Assert.assertEquals("Hello", + item.getItemProperty("myColumn").getValue()); + Assert.assertEquals(Integer.valueOf(3), + item.getItemProperty("myOther").getValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidPropertyAmount() { + grid.addRow("Hello", Integer.valueOf(3)); + } + + @Test + public void testRemovedColumn() { + grid.addColumn("myOther", Integer.class); + grid.removeColumn("myColumn"); + + grid.addRow(Integer.valueOf(3)); + + Item item = container.getItem(Integer.valueOf(1)); + Assert.assertEquals("Default value should be used for removed column", + "", item.getItemProperty("myColumn").getValue()); + Assert.assertEquals(Integer.valueOf(3), + item.getItemProperty("myOther").getValue()); + } + + @Test + public void testMultiplePropertiesAfterReorder() { + grid.addColumn("myOther", Integer.class); + + grid.setColumnOrder("myOther", "myColumn"); + + grid.addRow(Integer.valueOf(3), "Hello"); + + Item item = container.getItem(Integer.valueOf(1)); + Assert.assertEquals("Hello", + item.getItemProperty("myColumn").getValue()); + Assert.assertEquals(Integer.valueOf(3), + item.getItemProperty("myOther").getValue()); + } + + @Test + public void testInvalidType_NothingAdded() { + try { + grid.addRow(Integer.valueOf(5)); + + // Can't use @Test(expect = Foo.class) since we also want to verify + // state after exception was thrown + Assert.fail("Adding wrong type should throw ClassCastException"); + } catch (IllegalArgumentException e) { + Assert.assertEquals("No row should have been added", 0, + container.size()); + } + } + + @Test + public void testUnsupportingContainer() { + setContainerRemoveColumns(new BeanItemContainer(Person.class)); + try { + + grid.addRow("name"); + + // Can't use @Test(expect = Foo.class) since we also want to verify + // state after exception was thrown + Assert.fail( + "Adding to BeanItemContainer container should throw UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + Assert.assertEquals("No row should have been added", 0, + container.size()); + } + } + + @Test + public void testCustomContainer() { + BeanItemContainer container = new BeanItemContainer( + Person.class) { + @Override + public Object addItem() { + BeanItem item = addBean(new Person()); + return getBeanIdResolver().getIdForBean(item.getBean()); + } + }; + + setContainerRemoveColumns(container); + + grid.addRow("name"); + + Assert.assertEquals(1, container.size()); + + Assert.assertEquals("name", container.getIdByIndex(0).getFirstName()); + } + + @Test + public void testSetterThrowing() { + BeanItemContainer container = new BeanItemContainer( + Person.class) { + @Override + public Object addItem() { + BeanItem item = addBean(new Person() { + @Override + public void setFirstName(String firstName) { + if ("name".equals(firstName)) { + throw new RuntimeException(firstName); + } else { + super.setFirstName(firstName); + } + } + }); + return getBeanIdResolver().getIdForBean(item.getBean()); + } + }; + + setContainerRemoveColumns(container); + + try { + + grid.addRow("name"); + + // Can't use @Test(expect = Foo.class) since we also want to verify + // state after exception was thrown + Assert.fail("Adding row should throw MethodException"); + } catch (MethodException e) { + Assert.assertEquals("Got the wrong exception", "name", + e.getCause().getMessage()); + + Assert.assertEquals("There should be no rows in the container", 0, + container.size()); + } + } + + private void setContainerRemoveColumns( + BeanItemContainer container) { + // Remove predefined column so we can change container + grid.removeAllColumns(); + grid.setContainerDataSource(container); + grid.removeAllColumns(); + grid.addColumn("firstName"); + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java new file mode 100644 index 0000000000..f126e636ba --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java @@ -0,0 +1,59 @@ +/* + * 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.tests.server.component.grid; + +import java.util.Iterator; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.ui.Component; +import com.vaadin.ui.LegacyGrid.FooterCell; +import com.vaadin.ui.LegacyGrid.HeaderCell; +import com.vaadin.ui.Label; + +public class GridChildrenTest { + + @Test + public void componentsInMergedHeader() { + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("foo"); + grid.addColumn("bar"); + grid.addColumn("baz"); + HeaderCell merged = grid.getDefaultHeaderRow().join("foo", "bar", + "baz"); + Label label = new Label(); + merged.setComponent(label); + Iterator i = grid.iterator(); + Assert.assertEquals(label, i.next()); + Assert.assertFalse(i.hasNext()); + } + + @Test + public void componentsInMergedFooter() { + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("foo"); + grid.addColumn("bar"); + grid.addColumn("baz"); + FooterCell merged = grid.addFooterRowAt(0).join("foo", "bar", "baz"); + Label label = new Label(); + merged.setComponent(label); + Iterator i = grid.iterator(); + Assert.assertEquals(label, i.next()); + Assert.assertFalse(i.hasNext()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java new file mode 100644 index 0000000000..1af2577fa3 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java @@ -0,0 +1,134 @@ +/* + * 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.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Property; +import com.vaadin.data.util.IndexedContainer; + +public class GridColumnAddingAndRemovingTest { + + LegacyGrid grid = new LegacyGrid(); + Container.Indexed container; + + @Before + public void setUp() { + container = grid.getContainerDataSource(); + container.addItem(); + } + + @Test + public void testAddColumn() { + grid.addColumn("foo"); + + Property property = container + .getContainerProperty(container.firstItemId(), "foo"); + assertEquals(property.getType(), String.class); + } + + @Test(expected = IllegalStateException.class) + public void testAddColumnTwice() { + grid.addColumn("foo"); + grid.addColumn("foo"); + } + + @Test + public void testAddRemoveAndAddAgainColumn() { + grid.addColumn("foo"); + grid.removeColumn("foo"); + + // Removing a column, doesn't remove the property + Property property = container + .getContainerProperty(container.firstItemId(), "foo"); + assertEquals(property.getType(), String.class); + grid.addColumn("foo"); + } + + @Test + public void testAddNumberColumns() { + grid.addColumn("bar", Integer.class); + grid.addColumn("baz", Double.class); + + Property property = container + .getContainerProperty(container.firstItemId(), "bar"); + assertEquals(property.getType(), Integer.class); + assertEquals(null, property.getValue()); + property = container.getContainerProperty(container.firstItemId(), + "baz"); + assertEquals(property.getType(), Double.class); + assertEquals(null, property.getValue()); + } + + @Test(expected = IllegalStateException.class) + public void testAddDifferentTypeColumn() { + grid.addColumn("foo"); + grid.removeColumn("foo"); + grid.addColumn("foo", Integer.class); + } + + @Test(expected = IllegalStateException.class) + public void testAddColumnToNonDefaultContainer() { + grid.setContainerDataSource(new IndexedContainer()); + grid.addColumn("foo"); + } + + @Test + public void testAddColumnForExistingProperty() { + grid.addColumn("bar"); + IndexedContainer container2 = new IndexedContainer(); + container2.addContainerProperty("foo", Integer.class, 0); + container2.addContainerProperty("bar", String.class, ""); + grid.setContainerDataSource(container2); + assertNull("Grid should not have a column for property foo", + grid.getColumn("foo")); + assertNotNull("Grid did should have a column for property bar", + grid.getColumn("bar")); + for (LegacyGrid.Column column : grid.getColumns()) { + assertNotNull("Grid getColumns returned a null value", column); + } + + grid.removeAllColumns(); + grid.addColumn("foo"); + assertNotNull("Grid should now have a column for property foo", + grid.getColumn("foo")); + assertNull("Grid should not have a column for property bar anymore", + grid.getColumn("bar")); + } + + @Test(expected = IllegalStateException.class) + public void testAddIncompatibleColumnProperty() { + grid.addColumn("bar"); + grid.removeAllColumns(); + grid.addColumn("bar", Integer.class); + } + + @Test + public void testAddBooleanColumnProperty() { + grid.addColumn("foo", Boolean.class); + Property property = container + .getContainerProperty(container.firstItemId(), "foo"); + assertEquals(property.getType(), Boolean.class); + assertEquals(property.getValue(), null); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java new file mode 100644 index 0000000000..570e9b92fc --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java @@ -0,0 +1,413 @@ +/* + * 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.tests.server.component.grid; + +import static org.easymock.EasyMock.and; +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.isA; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +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.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import com.vaadin.ui.LegacyGrid; +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.KeyMapper; +import com.vaadin.shared.ui.grid.GridColumnState; +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.util.SharedUtil; +import com.vaadin.ui.LegacyGrid.Column; +import com.vaadin.ui.LegacyGrid.ColumnResizeEvent; +import com.vaadin.ui.LegacyGrid.ColumnResizeListener; +import com.vaadin.v7.ui.LegacyTextField; + +public class GridColumnsTest { + + private LegacyGrid grid; + + private GridState state; + + private Method getStateMethod; + + private Field columnIdGeneratorField; + + private KeyMapper columnIdMapper; + + @Before + @SuppressWarnings("unchecked") + public void setup() throws Exception { + IndexedContainer ds = new IndexedContainer(); + for (int c = 0; c < 10; c++) { + ds.addContainerProperty("column" + c, String.class, ""); + } + ds.addContainerProperty("noSort", Object.class, null); + grid = new LegacyGrid(ds); + + getStateMethod = LegacyGrid.class.getDeclaredMethod("getState"); + getStateMethod.setAccessible(true); + + state = (GridState) getStateMethod.invoke(grid); + + columnIdGeneratorField = LegacyGrid.class.getDeclaredField("columnKeys"); + columnIdGeneratorField.setAccessible(true); + + columnIdMapper = (KeyMapper) columnIdGeneratorField.get(grid); + } + + @Test + public void testColumnGeneration() throws Exception { + + for (Object propertyId : grid.getContainerDataSource() + .getContainerPropertyIds()) { + + // All property ids should get a column + Column column = grid.getColumn(propertyId); + assertNotNull(column); + + // Humanized property id should be the column header by default + assertEquals( + SharedUtil.camelCaseToHumanFriendly(propertyId.toString()), + grid.getDefaultHeaderRow().getCell(propertyId).getText()); + } + } + + @Test + public void testModifyingColumnProperties() throws Exception { + + // Modify first column + Column column = grid.getColumn("column1"); + assertNotNull(column); + + column.setHeaderCaption("CustomHeader"); + assertEquals("CustomHeader", column.getHeaderCaption()); + assertEquals(column.getHeaderCaption(), + grid.getDefaultHeaderRow().getCell("column1").getText()); + + column.setWidth(100); + assertEquals(100, column.getWidth(), 0.49d); + assertEquals(column.getWidth(), getColumnState("column1").width, 0.49d); + + try { + column.setWidth(-1); + fail("Setting width to -1 should throw exception"); + } catch (IllegalArgumentException iae) { + // expected + } + + assertEquals(100, column.getWidth(), 0.49d); + assertEquals(100, getColumnState("column1").width, 0.49d); + } + + @Test + public void testRemovingColumnByRemovingPropertyFromContainer() + throws Exception { + + Column column = grid.getColumn("column1"); + assertNotNull(column); + + // Remove column + grid.getContainerDataSource().removeContainerProperty("column1"); + + try { + column.setHeaderCaption("asd"); + + fail("Succeeded in modifying a detached column"); + } catch (IllegalStateException ise) { + // Detached state should throw exception + } + + try { + column.setWidth(123); + fail("Succeeded in modifying a detached column"); + } catch (IllegalStateException ise) { + // Detached state should throw exception + } + + assertNull(grid.getColumn("column1")); + assertNull(getColumnState("column1")); + } + + @Test + public void testAddingColumnByAddingPropertyToContainer() throws Exception { + grid.getContainerDataSource().addContainerProperty("columnX", + String.class, ""); + Column column = grid.getColumn("columnX"); + assertNotNull(column); + } + + @Test + public void testHeaderVisiblility() throws Exception { + + assertTrue(grid.isHeaderVisible()); + assertTrue(state.header.visible); + + grid.setHeaderVisible(false); + assertFalse(grid.isHeaderVisible()); + assertFalse(state.header.visible); + + grid.setHeaderVisible(true); + assertTrue(grid.isHeaderVisible()); + assertTrue(state.header.visible); + } + + @Test + public void testFooterVisibility() throws Exception { + + assertTrue(grid.isFooterVisible()); + assertTrue(state.footer.visible); + + grid.setFooterVisible(false); + assertFalse(grid.isFooterVisible()); + assertFalse(state.footer.visible); + + grid.setFooterVisible(true); + assertTrue(grid.isFooterVisible()); + assertTrue(state.footer.visible); + } + + @Test + public void testSetFrozenColumnCount() { + assertEquals("Grid should not start with a frozen column", 0, + grid.getFrozenColumnCount()); + grid.setFrozenColumnCount(2); + assertEquals("Freezing two columns should freeze two columns", 2, + grid.getFrozenColumnCount()); + } + + @Test + public void testSetFrozenColumnCountThroughColumn() { + assertEquals("Grid should not start with a frozen column", 0, + grid.getFrozenColumnCount()); + grid.getColumns().get(2).setLastFrozenColumn(); + assertEquals( + "Setting the third column as last frozen should freeze three columns", + 3, grid.getFrozenColumnCount()); + } + + @Test + public void testFrozenColumnRemoveColumn() { + assertEquals("Grid should not start with a frozen column", 0, + grid.getFrozenColumnCount()); + + int containerSize = grid.getContainerDataSource() + .getContainerPropertyIds().size(); + grid.setFrozenColumnCount(containerSize); + + Object propertyId = grid.getContainerDataSource() + .getContainerPropertyIds().iterator().next(); + + grid.getContainerDataSource().removeContainerProperty(propertyId); + assertEquals("Frozen column count should update when removing last row", + containerSize - 1, grid.getFrozenColumnCount()); + } + + @Test + public void testReorderColumns() { + Set containerProperties = new LinkedHashSet( + grid.getContainerDataSource().getContainerPropertyIds()); + Object[] properties = new Object[] { "column3", "column2", "column6" }; + grid.setColumnOrder(properties); + + int i = 0; + // Test sorted columns are first in order + for (Object property : properties) { + containerProperties.remove(property); + assertEquals(columnIdMapper.key(property), + state.columnOrder.get(i++)); + } + + // Test remaining columns are in original order + for (Object property : containerProperties) { + assertEquals(columnIdMapper.key(property), + state.columnOrder.get(i++)); + } + + try { + grid.setColumnOrder("foo", "bar", "baz"); + fail("Grid allowed sorting with non-existent properties"); + } catch (IllegalArgumentException e) { + // All ok + } + } + + @Test(expected = IllegalArgumentException.class) + public void testRemoveColumnThatDoesNotExist() { + grid.removeColumn("banana phone"); + } + + @Test(expected = IllegalStateException.class) + public void testSetNonSortableColumnSortable() { + Column noSortColumn = grid.getColumn("noSort"); + assertFalse("Object property column should not be sortable.", + noSortColumn.isSortable()); + noSortColumn.setSortable(true); + } + + @Test + public void testColumnsEditableByDefault() { + for (Column c : grid.getColumns()) { + assertTrue(c + " should be editable", c.isEditable()); + } + } + + @Test + public void testPropertyAndColumnEditorFieldsMatch() { + Column column1 = grid.getColumn("column1"); + column1.setEditorField(new LegacyTextField()); + assertSame(column1.getEditorField(), + grid.getColumn("column1").getEditorField()); + + Column column2 = grid.getColumn("column2"); + column2.setEditorField(new LegacyTextField()); + assertSame(column2.getEditorField(), column2.getEditorField()); + } + + @Test + public void testUneditableColumnHasNoField() { + Column col = grid.getColumn("column1"); + + col.setEditable(false); + + assertFalse("Column should be uneditable", col.isEditable()); + assertNull("Uneditable column should not be auto-assigned a Field", + col.getEditorField()); + } + + private GridColumnState getColumnState(Object propertyId) { + String columnId = columnIdMapper.key(propertyId); + for (GridColumnState columnState : state.columns) { + if (columnState.id.equals(columnId)) { + return columnState; + } + } + return null; + } + + @Test + public void testAddAndRemoveSortableColumn() { + boolean sortable = grid.getColumn("column1").isSortable(); + grid.removeColumn("column1"); + grid.addColumn("column1"); + assertEquals("Column sortability changed when re-adding", sortable, + grid.getColumn("column1").isSortable()); + } + + @Test + public void testSetColumns() { + grid.setColumns("column7", "column0", "column9"); + Iterator it = grid.getColumns().iterator(); + assertEquals(it.next().getPropertyId(), "column7"); + assertEquals(it.next().getPropertyId(), "column0"); + assertEquals(it.next().getPropertyId(), "column9"); + assertFalse(it.hasNext()); + } + + @Test + public void testAddingColumnsWithSetColumns() { + LegacyGrid g = new LegacyGrid(); + g.setColumns("c1", "c2", "c3"); + Iterator it = g.getColumns().iterator(); + assertEquals(it.next().getPropertyId(), "c1"); + assertEquals(it.next().getPropertyId(), "c2"); + assertEquals(it.next().getPropertyId(), "c3"); + assertFalse(it.hasNext()); + } + + @Test(expected = IllegalStateException.class) + public void testAddingColumnsWithSetColumnsNonDefaultContainer() { + grid.setColumns("column1", "column2", "column50"); + } + + @Test + public void testDefaultColumnHidingToggleCaption() { + Column firstColumn = grid.getColumns().get(0); + firstColumn.setHeaderCaption("headerCaption"); + assertEquals(null, firstColumn.getHidingToggleCaption()); + } + + @Test + public void testOverriddenColumnHidingToggleCaption() { + Column firstColumn = grid.getColumns().get(0); + firstColumn.setHidingToggleCaption("hidingToggleCaption"); + firstColumn.setHeaderCaption("headerCaption"); + assertEquals("hidingToggleCaption", + firstColumn.getHidingToggleCaption()); + } + + @Test + public void testColumnSetWidthFiresResizeEvent() { + final Column firstColumn = grid.getColumns().get(0); + + // prepare a listener mock that captures the argument + ColumnResizeListener mock = EasyMock + .createMock(ColumnResizeListener.class); + Capture capturedEvent = new Capture(); + mock.columnResize( + and(capture(capturedEvent), isA(ColumnResizeEvent.class))); + EasyMock.expectLastCall().once(); + + // Tell it to wait for the call + EasyMock.replay(mock); + + // Cause a resize event + grid.addColumnResizeListener(mock); + firstColumn.setWidth(firstColumn.getWidth() + 10); + + // Verify the method was called + EasyMock.verify(mock); + + // Asserts on the captured event + ColumnResizeEvent event = capturedEvent.getValue(); + assertEquals("Event column was not first column.", firstColumn, + event.getColumn()); + assertFalse("Event should not be userOriginated", + event.isUserOriginated()); + } + + @Test + public void textHeaderCaptionIsReturned() { + Column firstColumn = grid.getColumns().get(0); + + firstColumn.setHeaderCaption("text"); + + assertThat(firstColumn.getHeaderCaption(), is("text")); + } + + @Test + public void defaultCaptionIsReturnedForHtml() { + Column firstColumn = grid.getColumns().get(0); + + grid.getDefaultHeaderRow().getCell("column0").setHtml("html"); + + assertThat(firstColumn.getHeaderCaption(), is("Column0")); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java new file mode 100644 index 0000000000..fa6c57df93 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java @@ -0,0 +1,103 @@ +/* + * 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.tests.server.component.grid; + +import static org.junit.Assert.assertFalse; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Test; + +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.util.AbstractInMemoryContainer; +import com.vaadin.ui.LegacyGrid.Column; + +public class GridContainerNotSortableTest { + + final AbstractInMemoryContainer notSortableDataSource = new AbstractInMemoryContainer() { + + private Map> properties = new LinkedHashMap>(); + + { + properties.put("Foo", new Property() { + + @Override + public String getValue() { + return "foo"; + } + + @Override + public void setValue(String newValue) throws ReadOnlyException { + throw new ReadOnlyException(); + } + + @Override + public Class getType() { + return String.class; + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Override + public void setReadOnly(boolean newStatus) { + throw new UnsupportedOperationException(); + } + }); + } + + @Override + public Collection getContainerPropertyIds() { + return properties.keySet(); + } + + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + return properties.get(propertyId); + } + + @Override + public Class getType(Object propertyId) { + return properties.get(propertyId).getType(); + } + + @Override + protected Item getUnfilteredItem(Object itemId) { + return null; + } + }; + + @Test + public void testGridWithNotSortableContainer() { + new LegacyGrid(notSortableDataSource); + } + + @Test(expected = IllegalStateException.class) + public void testNotSortableGridSetColumnSortable() { + LegacyGrid grid = new LegacyGrid(); + grid.setContainerDataSource(notSortableDataSource); + Column column = grid.getColumn("Foo"); + assertFalse("Column should not be sortable initially.", + column.isSortable()); + column.setSortable(true); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java new file mode 100644 index 0000000000..7b07aefcb2 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java @@ -0,0 +1,159 @@ +/* + * 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.tests.server.component.grid; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.ui.Component; +import com.vaadin.ui.LegacyGrid.DetailsGenerator; +import com.vaadin.ui.LegacyGrid.RowReference; +import com.vaadin.ui.Label; + +public class GridContainerTest { + + /** + * Null Stream used with serialization tests + */ + protected static OutputStream NULLSTREAM = new OutputStream() { + @Override + public void write(int b) { + } + }; + + @Test + public void testDetailsGeneratorDoesNotResetOnContainerChange() { + LegacyGrid grid = new LegacyGrid(); + DetailsGenerator detGen = new DetailsGenerator() { + + @Override + public Component getDetails(RowReference rowReference) { + return new Label("Empty details"); + } + }; + grid.setDetailsGenerator(detGen); + + grid.setContainerDataSource(createContainer()); + + Assert.assertEquals("DetailsGenerator changed", detGen, + grid.getDetailsGenerator()); + } + + @Test + public void testSetContainerTwice() throws Exception { + + TestGrid grid = new TestGrid(); + + grid.setContainerDataSource(createContainer()); + + // Simulate initial response to ensure "lazy" state changes are done + // before resetting the datasource + grid.beforeClientResponse(true); + grid.getDataProvider().beforeClientResponse(true); + + grid.setContainerDataSource(createContainer()); + } + + @SuppressWarnings("unchecked") + private IndexedContainer createContainer() { + IndexedContainer container = new IndexedContainer(); + container.addContainerProperty("x", String.class, null); + container.addItem(0).getItemProperty("x").setValue("y"); + return container; + } + + @Test + public void setColumnsOrder() { + LegacyGrid grid = new LegacyGrid(); + IndexedContainer ic = new IndexedContainer(); + ic.addContainerProperty("foo", String.class, ""); + ic.addContainerProperty("baz", String.class, ""); + ic.addContainerProperty("bar", String.class, ""); + grid.setContainerDataSource(ic); + grid.setColumns("foo", "baz", "bar"); + + Assert.assertEquals("foo", grid.getColumns().get(0).getPropertyId()); + Assert.assertEquals("baz", grid.getColumns().get(1).getPropertyId()); + Assert.assertEquals("bar", grid.getColumns().get(2).getPropertyId()); + } + + @Test + public void addColumnNotInContainer() { + LegacyGrid grid = new LegacyGrid(); + grid.setContainerDataSource(new IndexedContainer()); + try { + grid.addColumn("notInContainer"); + Assert.fail( + "Adding a property id not in the container should throw an exception"); + } catch (IllegalStateException e) { + Assert.assertTrue(e.getMessage().contains("notInContainer")); + Assert.assertTrue( + e.getMessage().contains("does not exist in the container")); + } + } + + @Test + public void setColumnsForPropertyIdNotInContainer() { + LegacyGrid grid = new LegacyGrid(); + grid.setContainerDataSource(new IndexedContainer()); + try { + grid.setColumns("notInContainer", "notThereEither"); + Assert.fail( + "Setting columns for property ids not in the container should throw an exception"); + } catch (IllegalStateException e) { + // addColumn is run in random order.. + Assert.assertTrue(e.getMessage().contains("notInContainer") + || e.getMessage().contains("notThereEither")); + Assert.assertTrue( + e.getMessage().contains("does not exist in the container")); + } + } + + @Test(expected = IllegalStateException.class) + public void multipleAddColumnsForDefaultContainer() { + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("foo"); + grid.addColumn("foo"); + } + + @Test + public void testSerializeRpcDataProviderWithRowChanges() + throws IOException { + LegacyGrid grid = new LegacyGrid(); + IndexedContainer container = new IndexedContainer(); + grid.setContainerDataSource(container); + container.addItem(); + serializeComponent(grid); + } + + protected void serializeComponent(Component component) throws IOException { + ObjectOutputStream stream = null; + try { + stream = new ObjectOutputStream(NULLSTREAM); + stream.writeObject(component); + } finally { + if (stream != null) { + stream.close(); + } + } + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java new file mode 100644 index 0000000000..15daa264d9 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java @@ -0,0 +1,296 @@ +/* + * 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.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.assertSame; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Method; + +import com.vaadin.ui.LegacyGrid; +import com.vaadin.v7.ui.LegacyField; +import com.vaadin.v7.ui.LegacyTextField; + +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.MockVaadinSession; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; + +public class GridEditorTest { + + private static final Object PROPERTY_NAME = "name"; + private static final Object PROPERTY_AGE = "age"; + private static final String DEFAULT_NAME = "Some Valid Name"; + private static final Integer DEFAULT_AGE = 25; + private static final Object ITEM_ID = new Object(); + + // Explicit field for the test session to save it from GC + private VaadinSession session; + + private final LegacyGrid grid = new LegacyGrid(); + private Method doEditMethod; + + @Before + @SuppressWarnings("unchecked") + public void setup() throws SecurityException, NoSuchMethodException { + IndexedContainer container = new IndexedContainer(); + container.addContainerProperty(PROPERTY_NAME, String.class, "[name]"); + container.addContainerProperty(PROPERTY_AGE, Integer.class, + Integer.valueOf(-1)); + + Item item = container.addItem(ITEM_ID); + item.getItemProperty(PROPERTY_NAME).setValue(DEFAULT_NAME); + item.getItemProperty(PROPERTY_AGE).setValue(DEFAULT_AGE); + grid.setContainerDataSource(container); + + // VaadinSession needed for ConverterFactory + VaadinService mockService = EasyMock + .createNiceMock(VaadinService.class); + session = new MockVaadinSession(mockService); + VaadinSession.setCurrent(session); + session.lock(); + + // Access to method for actual editing. + doEditMethod = LegacyGrid.class.getDeclaredMethod("doEditItem"); + doEditMethod.setAccessible(true); + } + + @After + public void tearDown() { + session.unlock(); + session = null; + VaadinSession.setCurrent(null); + } + + @Test + public void testInitAssumptions() throws Exception { + assertFalse(grid.isEditorEnabled()); + assertNull(grid.getEditedItemId()); + assertNotNull(grid.getEditorFieldGroup()); + } + + @Test + public void testSetEnabled() throws Exception { + assertFalse(grid.isEditorEnabled()); + grid.setEditorEnabled(true); + assertTrue(grid.isEditorEnabled()); + } + + @Test + public void testSetDisabled() throws Exception { + assertFalse(grid.isEditorEnabled()); + grid.setEditorEnabled(true); + grid.setEditorEnabled(false); + assertFalse(grid.isEditorEnabled()); + } + + @Test + public void testSetReEnabled() throws Exception { + assertFalse(grid.isEditorEnabled()); + grid.setEditorEnabled(true); + grid.setEditorEnabled(false); + grid.setEditorEnabled(true); + assertTrue(grid.isEditorEnabled()); + } + + @Test + public void testDetached() throws Exception { + FieldGroup oldFieldGroup = grid.getEditorFieldGroup(); + grid.removeAllColumns(); + grid.setContainerDataSource(new IndexedContainer()); + assertFalse(oldFieldGroup == grid.getEditorFieldGroup()); + } + + @Test(expected = IllegalStateException.class) + public void testDisabledEditItem() throws Exception { + grid.editItem(ITEM_ID); + } + + @Test + public void testEditItem() throws Exception { + startEdit(); + assertEquals(ITEM_ID, grid.getEditedItemId()); + assertEquals(getEditedItem(), + grid.getEditorFieldGroup().getItemDataSource()); + + assertEquals(DEFAULT_NAME, + grid.getColumn(PROPERTY_NAME).getEditorField().getValue()); + assertEquals(String.valueOf(DEFAULT_AGE), + grid.getColumn(PROPERTY_AGE).getEditorField().getValue()); + } + + @Test + public void testSaveEditor() throws Exception { + startEdit(); + LegacyTextField field = (LegacyTextField) grid.getColumn(PROPERTY_NAME) + .getEditorField(); + + field.setValue("New Name"); + assertEquals(DEFAULT_NAME, field.getPropertyDataSource().getValue()); + + grid.saveEditor(); + assertTrue(grid.isEditorActive()); + assertFalse(field.isModified()); + assertEquals("New Name", field.getValue()); + assertEquals("New Name", getEditedProperty(PROPERTY_NAME).getValue()); + } + + @Test + public void testSaveEditorCommitFail() throws Exception { + startEdit(); + + ((LegacyTextField) grid.getColumn(PROPERTY_AGE).getEditorField()) + .setValue("Invalid"); + try { + // Manual fail instead of @Test(expected=...) to check it is + // saveEditor that fails and not setValue + grid.saveEditor(); + Assert.fail( + "CommitException expected when saving an invalid field value"); + } catch (CommitException e) { + // expected + } + } + + @Test + public void testCancelEditor() throws Exception { + startEdit(); + LegacyTextField field = (LegacyTextField) grid.getColumn(PROPERTY_NAME) + .getEditorField(); + field.setValue("New Name"); + + Property datasource = field.getPropertyDataSource(); + + grid.cancelEditor(); + assertFalse(grid.isEditorActive()); + assertNull(grid.getEditedItemId()); + assertFalse(field.isModified()); + assertEquals("", field.getValue()); + assertEquals(DEFAULT_NAME, datasource.getValue()); + assertNull(field.getPropertyDataSource()); + assertNull(grid.getEditorFieldGroup().getItemDataSource()); + } + + @Test(expected = IllegalArgumentException.class) + public void testNonexistentEditItem() throws Exception { + grid.setEditorEnabled(true); + grid.editItem(new Object()); + } + + @Test + public void testGetField() throws Exception { + startEdit(); + + assertNotNull(grid.getColumn(PROPERTY_NAME).getEditorField()); + } + + @Test + public void testGetFieldWithoutItem() throws Exception { + grid.setEditorEnabled(true); + assertNotNull(grid.getColumn(PROPERTY_NAME).getEditorField()); + } + + @Test + public void testCustomBinding() { + LegacyTextField textField = new LegacyTextField(); + grid.getColumn(PROPERTY_NAME).setEditorField(textField); + + startEdit(); + + assertSame(textField, grid.getColumn(PROPERTY_NAME).getEditorField()); + } + + @Test(expected = IllegalStateException.class) + public void testDisableWhileEditing() { + startEdit(); + grid.setEditorEnabled(false); + } + + @Test + public void testFieldIsNotReadonly() { + startEdit(); + + LegacyField field = grid.getColumn(PROPERTY_NAME).getEditorField(); + assertFalse(field.isReadOnly()); + } + + @Test + public void testFieldIsReadonlyWhenFieldGroupIsReadonly() { + startEdit(); + + grid.getEditorFieldGroup().setReadOnly(true); + LegacyField field = grid.getColumn(PROPERTY_NAME).getEditorField(); + assertTrue(field.isReadOnly()); + } + + @Test + public void testColumnRemoved() { + LegacyField field = grid.getColumn(PROPERTY_NAME).getEditorField(); + + assertSame("field should be attached to ", grid, field.getParent()); + + grid.removeColumn(PROPERTY_NAME); + + assertNull("field should be detached from ", field.getParent()); + } + + @Test + public void testSetFieldAgain() { + LegacyTextField field = new LegacyTextField(); + grid.getColumn(PROPERTY_NAME).setEditorField(field); + + field = new LegacyTextField(); + grid.getColumn(PROPERTY_NAME).setEditorField(field); + + assertSame("new field should be used.", field, + grid.getColumn(PROPERTY_NAME).getEditorField()); + } + + private void startEdit() { + grid.setEditorEnabled(true); + grid.editItem(ITEM_ID); + // Simulate succesful client response to actually start the editing. + try { + doEditMethod.invoke(grid); + } catch (Exception e) { + Assert.fail("Editing item " + ITEM_ID + " failed. Cause: " + + e.getCause().toString()); + } + } + + private Item getEditedItem() { + assertNotNull(grid.getEditedItemId()); + return grid.getContainerDataSource().getItem(grid.getEditedItemId()); + } + + private Property getEditedProperty(Object propertyId) { + return getEditedItem().getItemProperty(PROPERTY_NAME); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java new file mode 100644 index 0000000000..452d2713a4 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java @@ -0,0 +1,41 @@ +/* + * 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.tests.server.component.grid; + +import static org.junit.Assert.assertTrue; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Test; + +import com.vaadin.ui.LegacyGrid.AbstractGridExtension; + +public class GridExtensionTest { + + public static class DummyGridExtension extends AbstractGridExtension { + + public DummyGridExtension(LegacyGrid grid) { + super(grid); + } + } + + @Test + public void testCreateExtension() { + LegacyGrid grid = new LegacyGrid(); + DummyGridExtension dummy = new DummyGridExtension(grid); + assertTrue("DummyGridExtension never made it to Grid", + grid.getExtensions().contains(dummy)); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java new file mode 100644 index 0000000000..7e910d2428 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java @@ -0,0 +1,377 @@ +/* + * Copyright 2000-2013 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.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.event.SelectionEvent; +import com.vaadin.event.SelectionEvent.SelectionListener; +import com.vaadin.ui.LegacyGrid.SelectionMode; +import com.vaadin.ui.LegacyGrid.SelectionModel; + +public class GridSelectionTest { + + private static class MockSelectionChangeListener + implements SelectionListener { + private SelectionEvent event; + + @Override + public void select(final SelectionEvent event) { + this.event = event; + } + + public Collection getAdded() { + return event.getAdded(); + } + + public Collection getRemoved() { + return event.getRemoved(); + } + + public void clearEvent() { + /* + * This method is not strictly needed as the event will simply be + * overridden, but it's good practice, and makes the code more + * obvious. + */ + event = null; + } + + public boolean eventHasHappened() { + return event != null; + } + } + + private LegacyGrid grid; + private MockSelectionChangeListener mockListener; + + private final Object itemId1Present = "itemId1Present"; + private final Object itemId2Present = "itemId2Present"; + + private final Object itemId1NotPresent = "itemId1NotPresent"; + private final Object itemId2NotPresent = "itemId2NotPresent"; + + @Before + public void setup() { + final IndexedContainer container = new IndexedContainer(); + container.addItem(itemId1Present); + container.addItem(itemId2Present); + for (int i = 2; i < 10; i++) { + container.addItem(new Object()); + } + + assertEquals("init size", 10, container.size()); + assertTrue("itemId1Present", container.containsId(itemId1Present)); + assertTrue("itemId2Present", container.containsId(itemId2Present)); + assertFalse("itemId1NotPresent", + container.containsId(itemId1NotPresent)); + assertFalse("itemId2NotPresent", + container.containsId(itemId2NotPresent)); + + grid = new LegacyGrid(container); + + mockListener = new MockSelectionChangeListener(); + grid.addSelectionListener(mockListener); + + assertFalse("eventHasHappened", mockListener.eventHasHappened()); + } + + @Test + public void defaultSelectionModeIsSingle() { + assertTrue(grid.getSelectionModel() instanceof SelectionModel.Single); + } + + @Test(expected = IllegalStateException.class) + public void getSelectedRowThrowsExceptionMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + grid.getSelectedRow(); + } + + @Test(expected = IllegalStateException.class) + public void getSelectedRowThrowsExceptionNone() { + grid.setSelectionMode(SelectionMode.NONE); + grid.getSelectedRow(); + } + + @Test(expected = IllegalStateException.class) + public void selectThrowsExceptionNone() { + grid.setSelectionMode(SelectionMode.NONE); + grid.select(itemId1Present); + } + + @Test(expected = IllegalStateException.class) + public void deselectRowThrowsExceptionNone() { + grid.setSelectionMode(SelectionMode.NONE); + grid.deselect(itemId1Present); + } + + @Test + public void selectionModeMapsToMulti() { + assertTrue(grid.setSelectionMode( + SelectionMode.MULTI) instanceof SelectionModel.Multi); + } + + @Test + public void selectionModeMapsToSingle() { + assertTrue(grid.setSelectionMode( + SelectionMode.SINGLE) instanceof SelectionModel.Single); + } + + @Test + public void selectionModeMapsToNone() { + assertTrue(grid.setSelectionMode( + SelectionMode.NONE) instanceof SelectionModel.None); + } + + @Test(expected = IllegalArgumentException.class) + public void selectionModeNullThrowsException() { + grid.setSelectionMode(null); + } + + @Test + public void noSelectModel_isSelected() { + grid.setSelectionMode(SelectionMode.NONE); + assertFalse("itemId1Present", grid.isSelected(itemId1Present)); + assertFalse("itemId1NotPresent", grid.isSelected(itemId1NotPresent)); + } + + @Test(expected = IllegalStateException.class) + public void noSelectModel_getSelectedRow() { + grid.setSelectionMode(SelectionMode.NONE); + grid.getSelectedRow(); + } + + @Test + public void noSelectModel_getSelectedRows() { + grid.setSelectionMode(SelectionMode.NONE); + assertTrue(grid.getSelectedRows().isEmpty()); + } + + @Test + public void selectionCallsListenerMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + selectionCallsListener(); + } + + @Test + public void selectionCallsListenerSingle() { + grid.setSelectionMode(SelectionMode.SINGLE); + selectionCallsListener(); + } + + private void selectionCallsListener() { + grid.select(itemId1Present); + assertEquals("added size", 1, mockListener.getAdded().size()); + assertEquals("added item", itemId1Present, + mockListener.getAdded().iterator().next()); + assertEquals("removed size", 0, mockListener.getRemoved().size()); + } + + @Test + public void deselectionCallsListenerMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + deselectionCallsListener(); + } + + @Test + public void deselectionCallsListenerSingle() { + grid.setSelectionMode(SelectionMode.SINGLE); + deselectionCallsListener(); + } + + private void deselectionCallsListener() { + grid.select(itemId1Present); + mockListener.clearEvent(); + + grid.deselect(itemId1Present); + assertEquals("removed size", 1, mockListener.getRemoved().size()); + assertEquals("removed item", itemId1Present, + mockListener.getRemoved().iterator().next()); + assertEquals("removed size", 0, mockListener.getAdded().size()); + } + + @Test + public void deselectPresentButNotSelectedItemIdShouldntFireListenerMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + deselectPresentButNotSelectedItemIdShouldntFireListener(); + } + + @Test + public void deselectPresentButNotSelectedItemIdShouldntFireListenerSingle() { + grid.setSelectionMode(SelectionMode.SINGLE); + deselectPresentButNotSelectedItemIdShouldntFireListener(); + } + + private void deselectPresentButNotSelectedItemIdShouldntFireListener() { + grid.deselect(itemId1Present); + assertFalse(mockListener.eventHasHappened()); + } + + @Test + public void deselectNotPresentItemIdShouldNotThrowExceptionMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + grid.deselect(itemId1NotPresent); + } + + @Test + public void deselectNotPresentItemIdShouldNotThrowExceptionSingle() { + grid.setSelectionMode(SelectionMode.SINGLE); + grid.deselect(itemId1NotPresent); + } + + @Test(expected = IllegalArgumentException.class) + public void selectNotPresentItemIdShouldThrowExceptionMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + grid.select(itemId1NotPresent); + } + + @Test(expected = IllegalArgumentException.class) + public void selectNotPresentItemIdShouldThrowExceptionSingle() { + grid.setSelectionMode(SelectionMode.SINGLE); + grid.select(itemId1NotPresent); + } + + @Test + public void selectAllMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + final SelectionModel.Multi select = (SelectionModel.Multi) grid + .getSelectionModel(); + select.selectAll(); + assertEquals("added size", 10, mockListener.getAdded().size()); + assertEquals("removed size", 0, mockListener.getRemoved().size()); + assertTrue("itemId1Present", + mockListener.getAdded().contains(itemId1Present)); + assertTrue("itemId2Present", + mockListener.getAdded().contains(itemId2Present)); + } + + @Test + public void deselectAllMulti() { + grid.setSelectionMode(SelectionMode.MULTI); + final SelectionModel.Multi select = (SelectionModel.Multi) grid + .getSelectionModel(); + select.selectAll(); + mockListener.clearEvent(); + + select.deselectAll(); + assertEquals("removed size", 10, mockListener.getRemoved().size()); + assertEquals("added size", 0, mockListener.getAdded().size()); + assertTrue("itemId1Present", + mockListener.getRemoved().contains(itemId1Present)); + assertTrue("itemId2Present", + mockListener.getRemoved().contains(itemId2Present)); + assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); + } + + @Test + public void gridDeselectAllMultiAllSelected() { + grid.setSelectionMode(SelectionMode.MULTI); + final SelectionModel.Multi select = (SelectionModel.Multi) grid + .getSelectionModel(); + select.selectAll(); + mockListener.clearEvent(); + + assertTrue(grid.deselectAll()); + assertEquals("removed size", 10, mockListener.getRemoved().size()); + assertEquals("added size", 0, mockListener.getAdded().size()); + assertTrue("itemId1Present", + mockListener.getRemoved().contains(itemId1Present)); + assertTrue("itemId2Present", + mockListener.getRemoved().contains(itemId2Present)); + assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); + + } + + @Test + public void gridDeselectAllMultiOneSelected() { + grid.setSelectionMode(SelectionMode.MULTI); + final SelectionModel.Multi select = (SelectionModel.Multi) grid + .getSelectionModel(); + select.select(itemId2Present); + mockListener.clearEvent(); + + assertTrue(grid.deselectAll()); + assertEquals("removed size", 1, mockListener.getRemoved().size()); + assertEquals("added size", 0, mockListener.getAdded().size()); + assertFalse("itemId1Present", + mockListener.getRemoved().contains(itemId1Present)); + assertTrue("itemId2Present", + mockListener.getRemoved().contains(itemId2Present)); + assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); + + } + + @Test + public void gridDeselectAllSingleNoneSelected() { + grid.setSelectionMode(SelectionMode.SINGLE); + assertFalse(grid.deselectAll()); + assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); + } + + @Test + public void gridDeselectAllSingleOneSelected() { + grid.setSelectionMode(SelectionMode.SINGLE); + final SelectionModel.Single select = (SelectionModel.Single) grid + .getSelectionModel(); + select.select(itemId2Present); + mockListener.clearEvent(); + + assertTrue(grid.deselectAll()); + assertEquals("removed size", 1, mockListener.getRemoved().size()); + assertEquals("added size", 0, mockListener.getAdded().size()); + assertFalse("itemId1Present", + mockListener.getRemoved().contains(itemId1Present)); + assertTrue("itemId2Present", + mockListener.getRemoved().contains(itemId2Present)); + assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); + + } + + @Test + public void gridDeselectAllMultiNoneSelected() { + grid.setSelectionMode(SelectionMode.MULTI); + + assertFalse(grid.deselectAll()); + assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); + + } + + @Test + public void reselectionDeselectsPreviousSingle() { + grid.setSelectionMode(SelectionMode.SINGLE); + grid.select(itemId1Present); + mockListener.clearEvent(); + + grid.select(itemId2Present); + assertEquals("added size", 1, mockListener.getAdded().size()); + assertEquals("removed size", 1, mockListener.getRemoved().size()); + assertEquals("added item", itemId2Present, + mockListener.getAdded().iterator().next()); + assertEquals("removed item", itemId1Present, + mockListener.getRemoved().iterator().next()); + assertEquals("selectedRows is correct", itemId2Present, + grid.getSelectedRow()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java new file mode 100644 index 0000000000..9f5f67d8be --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java @@ -0,0 +1,44 @@ +/* + * 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.tests.server.component.grid; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.ui.LegacyGrid; + +/** + * Tests for Grid State. + * + */ +public class GridStateTest { + + @Test + public void getPrimaryStyleName_gridHasCustomPrimaryStyleName() { + LegacyGrid grid = new LegacyGrid(); + GridState state = new GridState(); + Assert.assertEquals("Unexpected primary style name", + state.primaryStyleName, grid.getPrimaryStyleName()); + } + + @Test + public void gridStateHasCustomPrimaryStyleName() { + GridState state = new GridState(); + Assert.assertEquals("Unexpected primary style name", "v-grid", + state.primaryStyleName); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java new file mode 100644 index 0000000000..7a8209d50b --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java @@ -0,0 +1,132 @@ +/* + * 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.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.lang.reflect.Method; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.util.IndexedContainer; + +public class GridStaticSectionTest extends LegacyGrid { + + private Indexed dataSource = new IndexedContainer(); + + @Before + public void setUp() { + dataSource.addContainerProperty("firstName", String.class, ""); + dataSource.addContainerProperty("lastName", String.class, ""); + dataSource.addContainerProperty("streetAddress", String.class, ""); + dataSource.addContainerProperty("zipCode", Integer.class, null); + setContainerDataSource(dataSource); + } + + @Test + public void testAddAndRemoveHeaders() { + assertEquals(1, getHeaderRowCount()); + prependHeaderRow(); + assertEquals(2, getHeaderRowCount()); + removeHeaderRow(0); + assertEquals(1, getHeaderRowCount()); + removeHeaderRow(0); + assertEquals(0, getHeaderRowCount()); + assertEquals(null, getDefaultHeaderRow()); + HeaderRow row = appendHeaderRow(); + assertEquals(1, getHeaderRowCount()); + assertEquals(null, getDefaultHeaderRow()); + setDefaultHeaderRow(row); + assertEquals(row, getDefaultHeaderRow()); + } + + @Test + public void testAddAndRemoveFooters() { + // By default there are no footer rows + assertEquals(0, getFooterRowCount()); + FooterRow row = appendFooterRow(); + + assertEquals(1, getFooterRowCount()); + prependFooterRow(); + assertEquals(2, getFooterRowCount()); + assertEquals(row, getFooterRow(1)); + removeFooterRow(0); + assertEquals(1, getFooterRowCount()); + removeFooterRow(0); + assertEquals(0, getFooterRowCount()); + } + + @Test + public void testUnusedPropertyNotInCells() { + removeColumn("firstName"); + assertNull("firstName cell was not removed from existing row", + getDefaultHeaderRow().getCell("firstName")); + HeaderRow newRow = appendHeaderRow(); + assertNull("firstName cell was created when it should not.", + newRow.getCell("firstName")); + addColumn("firstName"); + assertNotNull( + "firstName cell was not created for default row when added again", + getDefaultHeaderRow().getCell("firstName")); + assertNotNull( + "firstName cell was not created for new row when added again", + newRow.getCell("firstName")); + + } + + @Test + public void testJoinHeaderCells() { + HeaderRow mergeRow = prependHeaderRow(); + mergeRow.join("firstName", "lastName").setText("Name"); + mergeRow.join(mergeRow.getCell("streetAddress"), + mergeRow.getCell("zipCode")); + } + + @Test(expected = IllegalStateException.class) + public void testJoinHeaderCellsIncorrectly() throws Throwable { + HeaderRow mergeRow = prependHeaderRow(); + mergeRow.join("firstName", "zipCode").setText("Name"); + sanityCheck(); + } + + @Test + public void testJoinAllFooterCells() { + FooterRow mergeRow = prependFooterRow(); + mergeRow.join(dataSource.getContainerPropertyIds().toArray()) + .setText("All the stuff."); + } + + private void sanityCheck() throws Throwable { + Method sanityCheckHeader; + try { + sanityCheckHeader = LegacyGrid.Header.class + .getDeclaredMethod("sanityCheck"); + sanityCheckHeader.setAccessible(true); + Method sanityCheckFooter = LegacyGrid.Footer.class + .getDeclaredMethod("sanityCheck"); + sanityCheckFooter.setAccessible(true); + sanityCheckHeader.invoke(getHeader()); + sanityCheckFooter.invoke(getFooter()); + } catch (Exception e) { + throw e.getCause(); + } + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java new file mode 100644 index 0000000000..1199486742 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java @@ -0,0 +1,171 @@ +/* + * 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.tests.server.component.grid; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.vaadin.ui.LegacyGrid; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.event.SelectionEvent; +import com.vaadin.event.SelectionEvent.SelectionListener; +import com.vaadin.ui.LegacyGrid.MultiSelectionModel; +import com.vaadin.ui.LegacyGrid.SelectionMode; + +public class MultiSelectionModelTest { + + private Object itemId1Present = "itemId1Present"; + private Object itemId2Present = "itemId2Present"; + private Object itemId3Present = "itemId3Present"; + + private Object itemIdNotPresent = "itemIdNotPresent"; + private Container.Indexed dataSource; + private MultiSelectionModel model; + private LegacyGrid grid; + + private boolean expectingEvent = false; + private boolean expectingDeselectEvent; + private List select = new ArrayList(); + private List deselect = new ArrayList(); + + @Before + public void setUp() { + dataSource = createDataSource(); + grid = new LegacyGrid(dataSource); + grid.setSelectionMode(SelectionMode.MULTI); + model = (MultiSelectionModel) grid.getSelectionModel(); + } + + @After + public void tearDown() { + Assert.assertFalse("Some expected select event did not happen.", + expectingEvent); + Assert.assertFalse("Some expected deselect event did not happen.", + expectingDeselectEvent); + } + + private IndexedContainer createDataSource() { + final IndexedContainer container = new IndexedContainer(); + container.addItem(itemId1Present); + container.addItem(itemId2Present); + container.addItem(itemId3Present); + for (int i = 3; i < 10; i++) { + container.addItem(new Object()); + } + + return container; + } + + @Test + public void testSelectAndDeselectRow() throws Throwable { + try { + expectSelectEvent(itemId1Present); + model.select(itemId1Present); + expectDeselectEvent(itemId1Present); + model.deselect(itemId1Present); + } catch (Exception e) { + throw e.getCause(); + } + + verifyCurrentSelection(); + } + + @Test + public void testAddSelection() throws Throwable { + try { + expectSelectEvent(itemId1Present); + model.select(itemId1Present); + expectSelectEvent(itemId2Present); + model.select(itemId2Present); + } catch (Exception e) { + throw e.getCause(); + } + + verifyCurrentSelection(itemId1Present, itemId2Present); + } + + @Test + public void testSettingSelection() throws Throwable { + try { + expectSelectEvent(itemId2Present, itemId1Present); + model.setSelected(Arrays + .asList(new Object[] { itemId1Present, itemId2Present })); + verifyCurrentSelection(itemId1Present, itemId2Present); + + expectDeselectEvent(itemId1Present); + expectSelectEvent(itemId3Present); + model.setSelected(Arrays + .asList(new Object[] { itemId3Present, itemId2Present })); + verifyCurrentSelection(itemId3Present, itemId2Present); + } catch (Exception e) { + throw e.getCause(); + } + } + + private void expectSelectEvent(Object... selectArray) { + select = Arrays.asList(selectArray); + addListener(); + } + + private void expectDeselectEvent(Object... deselectArray) { + deselect = Arrays.asList(deselectArray); + addListener(); + } + + private void addListener() { + if (expectingEvent) { + return; + } + + expectingEvent = true; + grid.addSelectionListener(new SelectionListener() { + + @Override + public void select(SelectionEvent event) { + Assert.assertTrue("Selection did not contain expected items", + event.getAdded().containsAll(select)); + Assert.assertTrue("Selection contained unexpected items", + select.containsAll(event.getAdded())); + select = new ArrayList(); + + Assert.assertTrue("Deselection did not contain expected items", + event.getRemoved().containsAll(deselect)); + Assert.assertTrue("Deselection contained unexpected items", + deselect.containsAll(event.getRemoved())); + deselect = new ArrayList(); + + grid.removeSelectionListener(this); + expectingEvent = false; + } + }); + } + + private void verifyCurrentSelection(Object... selection) { + final List selected = Arrays.asList(selection); + if (model.getSelectedRows().containsAll(selected) + && selected.containsAll(model.getSelectedRows())) { + return; + } + Assert.fail("Not all items were correctly selected"); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java new file mode 100644 index 0000000000..3183ad9021 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java @@ -0,0 +1,153 @@ +/* + * 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.tests.server.component.grid; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.event.SelectionEvent; +import com.vaadin.event.SelectionEvent.SelectionListener; +import com.vaadin.ui.LegacyGrid; +import com.vaadin.ui.LegacyGrid.SelectionMode; +import com.vaadin.ui.LegacyGrid.SingleSelectionModel; + +public class SingleSelectionModelTest { + + private Object itemId1Present = "itemId1Present"; + private Object itemId2Present = "itemId2Present"; + + private Object itemIdNotPresent = "itemIdNotPresent"; + private Container.Indexed dataSource; + private SingleSelectionModel model; + private LegacyGrid grid; + + private boolean expectingEvent = false; + + @Before + public void setUp() { + dataSource = createDataSource(); + grid = new LegacyGrid(dataSource); + grid.setSelectionMode(SelectionMode.SINGLE); + model = (SingleSelectionModel) grid.getSelectionModel(); + } + + @After + public void tearDown() { + Assert.assertFalse("Some expected event did not happen.", + expectingEvent); + } + + private IndexedContainer createDataSource() { + final IndexedContainer container = new IndexedContainer(); + container.addItem(itemId1Present); + container.addItem(itemId2Present); + for (int i = 2; i < 10; i++) { + container.addItem(new Object()); + } + + return container; + } + + @Test + public void testSelectAndDeselctRow() throws Throwable { + try { + expectEvent(itemId1Present, null); + model.select(itemId1Present); + expectEvent(null, itemId1Present); + model.select(null); + } catch (Exception e) { + throw e.getCause(); + } + } + + @Test + public void testSelectAndChangeSelectedRow() throws Throwable { + try { + expectEvent(itemId1Present, null); + model.select(itemId1Present); + expectEvent(itemId2Present, itemId1Present); + model.select(itemId2Present); + } catch (Exception e) { + throw e.getCause(); + } + } + + @Test + public void testRemovingSelectedRowAndThenDeselecting() throws Throwable { + try { + expectEvent(itemId2Present, null); + model.select(itemId2Present); + dataSource.removeItem(itemId2Present); + expectEvent(null, itemId2Present); + model.select(null); + } catch (Exception e) { + throw e.getCause(); + } + } + + @Test + public void testSelectAndReSelectRow() throws Throwable { + try { + expectEvent(itemId1Present, null); + model.select(itemId1Present); + expectEvent(null, null); + // This is no-op. Nothing should happen. + model.select(itemId1Present); + } catch (Exception e) { + throw e.getCause(); + } + Assert.assertTrue("Should still wait for event", expectingEvent); + expectingEvent = false; + } + + @Test(expected = IllegalArgumentException.class) + public void testSelectNonExistentRow() { + model.select(itemIdNotPresent); + } + + private void expectEvent(final Object selected, final Object deselected) { + expectingEvent = true; + grid.addSelectionListener(new SelectionListener() { + + @Override + public void select(SelectionEvent event) { + if (selected != null) { + Assert.assertTrue("Selection did not contain expected item", + event.getAdded().contains(selected)); + } else { + Assert.assertTrue("Unexpected selection", + event.getAdded().isEmpty()); + } + + if (deselected != null) { + Assert.assertTrue( + "DeSelection did not contain expected item", + event.getRemoved().contains(deselected)); + } else { + Assert.assertTrue("Unexpected selection", + event.getRemoved().isEmpty()); + } + + grid.removeSelectionListener(this); + expectingEvent = false; + } + }); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java new file mode 100644 index 0000000000..9b2dde4d24 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java @@ -0,0 +1,62 @@ +/* + * 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.tests.server.component.grid; + +import java.lang.reflect.Field; + +import org.easymock.EasyMock; + +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.communication.data.RpcDataProviderExtension; +import com.vaadin.ui.ConnectorTracker; +import com.vaadin.ui.LegacyGrid; +import com.vaadin.ui.UI; + +/** + * A Grid attached to a mock UI with a mock ConnectorTracker. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class TestGrid extends LegacyGrid { + + public TestGrid() { + super(); + init(); + } + + public TestGrid(IndexedContainer c) { + super(c); + init(); + } + + public RpcDataProviderExtension getDataProvider() throws Exception { + Field dseField = LegacyGrid.class.getDeclaredField("datasourceExtension"); + dseField.setAccessible(true); + return (RpcDataProviderExtension) dseField.get(this); + } + + private void init() { + UI mockUI = EasyMock.createNiceMock(UI.class); + ConnectorTracker mockCT = EasyMock + .createNiceMock(ConnectorTracker.class); + EasyMock.expect(mockUI.getConnectorTracker()).andReturn(mockCT) + .anyTimes(); + EasyMock.replay(mockUI, mockCT); + + setParent(mockUI); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java new file mode 100644 index 0000000000..71d24a2d8e --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java @@ -0,0 +1,101 @@ +/* + * 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.tests.server.component.grid.declarative; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Test; + +public class GridColumnDeclarativeTest extends GridDeclarativeTestBase { + + @Test + public void testSimpleGridColumns() { + String design = ""// + + "" + + " " + + " " + + " " + + " " + + " " + + "" // + + "" // + + "
"; + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class).setWidth(100); + grid.addColumn("Column2", String.class).setMaximumWidth(200) + .setExpandRatio(2).setSortable(false); + grid.addColumn("Column3", String.class).setMinimumWidth(15) + .setExpandRatio(1).setEditable(false).setResizable(false); + grid.addColumn("Column4", String.class).setHidable(true) + .setHidingToggleCaption("col 4").setResizable(true); + grid.addColumn("Column5", String.class).setHidden(true); + + // Remove the default header + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + // Use the read grid component to do another pass on write. + testRead(design, grid, true); + testWrite(design, grid); + } + + @Test + public void testReadColumnsWithoutPropertyId() { + String design = ""// + + "" + + " " + + " " // property-id="property-1" + + " " + + " " // property-id="property-3" + + "" // + + "
"; + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class).setWidth(100); + grid.addColumn("property-1", String.class).setMaximumWidth(200) + .setExpandRatio(2); + grid.addColumn("Column3", String.class).setMinimumWidth(15) + .setExpandRatio(1); + grid.addColumn("property-3", String.class).setHidable(true) + .setHidden(true).setHidingToggleCaption("col 4"); + + testRead(design, grid); + } + + @Test + public void testReadEmptyExpand() { + String design = ""// + + "" + " " + + "" // + + "
"; + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("property-0", String.class).setExpandRatio(1); + + testRead(design, grid); + } + + @Test + public void testReadColumnWithNoAttributes() { + String design = ""// + + "" // + + " " // + + "" // + + "
"; + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("property-0", String.class); + + testRead(design, grid); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java new file mode 100644 index 0000000000..8ca99327dc --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java @@ -0,0 +1,84 @@ +/* + * 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.tests.server.component.grid.declarative; + +import static org.junit.Assert.assertSame; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Test; + +import com.vaadin.shared.ui.grid.HeightMode; +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.LegacyGrid.MultiSelectionModel; +import com.vaadin.ui.LegacyGrid.NoSelectionModel; +import com.vaadin.ui.LegacyGrid.SingleSelectionModel; + +/** + * Tests declarative support for {@link Grid} properties. + * + * @since + * @author Vaadin Ltd + */ +public class GridDeclarativeAttributeTest extends DeclarativeTestBase { + + @Test + public void testBasicAttributes() { + + String design = ""; + + LegacyGrid grid = new LegacyGrid(); + grid.setEditorEnabled(true); + grid.setHeightMode(HeightMode.ROW); + grid.setHeightByRows(20); + grid.setFrozenColumnCount(-1); + grid.setEditorSaveCaption("Tallenna"); + grid.setEditorCancelCaption("Peruuta"); + grid.setColumnReorderingAllowed(true); + + testRead(design, grid); + testWrite(design, grid); + } + + @Test + public void testFrozenColumnsAttributes() { + String design = "" // + + "
"; + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("property-0", String.class); + grid.addColumn("property-1", String.class); + grid.addColumn("property-2", String.class); + grid.setFrozenColumnCount(2); + + testRead(design, grid); + } + + @Test + public void testSelectionMode() { + String design = ""; + assertSame(NoSelectionModel.class, + read(design).getSelectionModel().getClass()); + + design = ""; + assertSame(SingleSelectionModel.class, + read(design).getSelectionModel().getClass()); + + design = ""; + assertSame(MultiSelectionModel.class, + read(design).getSelectionModel().getClass()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java new file mode 100644 index 0000000000..3e97910be7 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java @@ -0,0 +1,159 @@ +/* + * 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.tests.server.component.grid.declarative; + +import java.util.List; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Assert; + +import com.vaadin.tests.design.DeclarativeTestBase; +import com.vaadin.ui.LegacyGrid.Column; +import com.vaadin.ui.LegacyGrid.FooterCell; +import com.vaadin.ui.LegacyGrid.FooterRow; +import com.vaadin.ui.LegacyGrid.HeaderCell; +import com.vaadin.ui.LegacyGrid.HeaderRow; + +public class GridDeclarativeTestBase extends DeclarativeTestBase { + + @Override + public LegacyGrid testRead(String design, LegacyGrid expected) { + return testRead(design, expected, false); + } + + public LegacyGrid testRead(String design, LegacyGrid expected, boolean retestWrite) { + return testRead(design, expected, retestWrite, false); + } + + public LegacyGrid testRead(String design, LegacyGrid expected, boolean retestWrite, + boolean writeData) { + LegacyGrid actual = super.testRead(design, expected); + + compareGridColumns(expected, actual); + compareHeaders(expected, actual); + compareFooters(expected, actual); + + if (retestWrite) { + testWrite(design, actual, writeData); + } + + return actual; + } + + private void compareHeaders(LegacyGrid expected, LegacyGrid actual) { + Assert.assertEquals("Different header row count", + expected.getHeaderRowCount(), actual.getHeaderRowCount()); + for (int i = 0; i < expected.getHeaderRowCount(); ++i) { + HeaderRow expectedRow = expected.getHeaderRow(i); + HeaderRow actualRow = actual.getHeaderRow(i); + + if (expectedRow.equals(expected.getDefaultHeaderRow())) { + Assert.assertEquals("Different index for default header row", + actual.getDefaultHeaderRow(), actualRow); + } + + for (Column c : expected.getColumns()) { + String baseError = "Difference when comparing cell for " + + c.toString() + " on header row " + i + ": "; + Object propertyId = c.getPropertyId(); + HeaderCell expectedCell = expectedRow.getCell(propertyId); + HeaderCell actualCell = actualRow.getCell(propertyId); + + switch (expectedCell.getCellType()) { + case TEXT: + Assert.assertEquals(baseError + "Text content", + expectedCell.getText(), actualCell.getText()); + break; + case HTML: + Assert.assertEquals(baseError + "HTML content", + expectedCell.getHtml(), actualCell.getHtml()); + break; + case WIDGET: + assertEquals(baseError + "Component content", + expectedCell.getComponent(), + actualCell.getComponent()); + break; + } + } + } + } + + private void compareFooters(LegacyGrid expected, LegacyGrid actual) { + Assert.assertEquals("Different footer row count", + expected.getFooterRowCount(), actual.getFooterRowCount()); + for (int i = 0; i < expected.getFooterRowCount(); ++i) { + FooterRow expectedRow = expected.getFooterRow(i); + FooterRow actualRow = actual.getFooterRow(i); + + for (Column c : expected.getColumns()) { + String baseError = "Difference when comparing cell for " + + c.toString() + " on footer row " + i + ": "; + Object propertyId = c.getPropertyId(); + FooterCell expectedCell = expectedRow.getCell(propertyId); + FooterCell actualCell = actualRow.getCell(propertyId); + + switch (expectedCell.getCellType()) { + case TEXT: + Assert.assertEquals(baseError + "Text content", + expectedCell.getText(), actualCell.getText()); + break; + case HTML: + Assert.assertEquals(baseError + "HTML content", + expectedCell.getHtml(), actualCell.getHtml()); + break; + case WIDGET: + assertEquals(baseError + "Component content", + expectedCell.getComponent(), + actualCell.getComponent()); + break; + } + } + } + } + + private void compareGridColumns(LegacyGrid expected, LegacyGrid actual) { + List columns = expected.getColumns(); + List actualColumns = actual.getColumns(); + Assert.assertEquals("Different amount of columns", columns.size(), + actualColumns.size()); + for (int i = 0; i < columns.size(); ++i) { + Column col1 = columns.get(i); + Column col2 = actualColumns.get(i); + String baseError = "Error when comparing columns for property " + + col1.getPropertyId() + ": "; + assertEquals(baseError + "Property id", col1.getPropertyId(), + col2.getPropertyId()); + assertEquals(baseError + "Width", col1.getWidth(), col2.getWidth()); + assertEquals(baseError + "Maximum width", col1.getMaximumWidth(), + col2.getMaximumWidth()); + assertEquals(baseError + "Minimum width", col1.getMinimumWidth(), + col2.getMinimumWidth()); + assertEquals(baseError + "Expand ratio", col1.getExpandRatio(), + col2.getExpandRatio()); + assertEquals(baseError + "Sortable", col1.isSortable(), + col2.isSortable()); + assertEquals(baseError + "Editable", col1.isEditable(), + col2.isEditable()); + assertEquals(baseError + "Hidable", col1.isHidable(), + col2.isHidable()); + assertEquals(baseError + "Hidden", col1.isHidden(), + col2.isHidden()); + assertEquals(baseError + "HidingToggleCaption", + col1.getHidingToggleCaption(), + col2.getHidingToggleCaption()); + } + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java new file mode 100644 index 0000000000..1207063c16 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java @@ -0,0 +1,356 @@ +/* + * 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.tests.server.component.grid.declarative; + +import com.vaadin.ui.LegacyGrid; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.ui.LegacyGrid.Column; +import com.vaadin.ui.LegacyGrid.FooterRow; +import com.vaadin.ui.LegacyGrid.HeaderRow; +import com.vaadin.ui.Label; +import com.vaadin.ui.declarative.DesignContext; + +public class GridHeaderFooterDeclarativeTest extends GridDeclarativeTestBase { + + @Test + public void testSingleDefaultHeader() { + //@formatter:off + String design = "" + + "" + + " " + + " " + + " " + + "" + + "" + + " " + + "" + + "
Column1Column2Column3
"; + //@formatter:on + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.addColumn("Column2", String.class); + grid.addColumn("Column3", String.class); + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testSingleDefaultHTMLHeader() { + //@formatter:off + String design = "" + + "" + + " " + + " " + + " " + "" + + "" + + " " + + "" + + "
Column1Column2Column3
"; + //@formatter:on + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.addColumn("Column2", String.class); + grid.addColumn("Column3", String.class); + + HeaderRow row = grid.getDefaultHeaderRow(); + for (Column c : grid.getColumns()) { + row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); + } + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testNoHeaderRows() { + //@formatter:off + String design = "" + + "" + + " " + + "" + + "" + + "
"; + //@formatter:on + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testMultipleHeadersWithColSpans() { + //@formatter:off + String design = "" + + "" + + " " + + " " + + " " + + "" + + "" + + " " + + " " + + " " + + "" + + "
Baz
Column1Column2Column3
FooBar
"; + //@formatter:on + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.addColumn("Column2", String.class); + grid.addColumn("Column3", String.class); + + HeaderRow row = grid.getDefaultHeaderRow(); + for (Column c : grid.getColumns()) { + row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); + } + + grid.prependHeaderRow().join("Column1", "Column2", "Column3") + .setHtml("Baz"); + row = grid.appendHeaderRow(); + row.getCell("Column1").setHtml("Foo"); + row.join("Column2", "Column3").setHtml("Bar"); + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testSingleDefaultFooter() { + //@formatter:off + String design = "" + + "" + + " " + + " " + + " " + + "" + + "" // No headers read or written + + "" + + " " + + "" + + "
Column1Column2Column3
"; + //@formatter:on + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.addColumn("Column2", String.class); + grid.addColumn("Column3", String.class); + + FooterRow row = grid.appendFooterRow(); + for (Column c : grid.getColumns()) { + row.getCell(c.getPropertyId()).setText(c.getHeaderCaption()); + } + + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testSingleDefaultHTMLFooter() { + //@formatter:off + String design = "" + + "" + + " " + + " " + + " " + "" + + "" // No headers read or written + + "" + + " " + + "" + + "
Column1Column2Column3
"; + //@formatter:on + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.addColumn("Column2", String.class); + grid.addColumn("Column3", String.class); + + FooterRow row = grid.appendFooterRow(); + for (Column c : grid.getColumns()) { + row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); + } + + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testMultipleFootersWithColSpans() { + //@formatter:off + String design = "" + + "" + + " " + + " " + + " " + + "" + + "" // No headers read or written. + + "" + + " " + + " " + + " " + + "" + + "
Baz
Column1Column2Column3
FooBar
"; + //@formatter:on + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.addColumn("Column2", String.class); + grid.addColumn("Column3", String.class); + + FooterRow row = grid.appendFooterRow(); + for (Column c : grid.getColumns()) { + row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); + } + + grid.prependFooterRow().join("Column1", "Column2", "Column3") + .setHtml("Baz"); + row = grid.appendFooterRow(); + row.getCell("Column1").setHtml("Foo"); + row.join("Column2", "Column3").setHtml("Bar"); + + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testComponentInGridHeader() { + //@formatter:off + String design = "" + + "" + + " " + + "" + + "" + + "" + + "" + + "
Foo
"; + //@formatter:on + Label component = new Label("Foo"); + component.setContentMode(ContentMode.HTML); + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.getDefaultHeaderRow().getCell("Column1").setComponent(component); + + testRead(design, grid, true); + testWrite(design, grid); + } + + @Test + public void testComponentInGridFooter() { + //@formatter:off + String design = "" + + "" + + " " + + "" + + "" // No headers read or written + + "" + + "" + + "" + + "
Foo
"; + //@formatter:on + + Label component = new Label("Foo"); + component.setContentMode(ContentMode.HTML); + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Column1", String.class); + grid.prependFooterRow().getCell("Column1").setComponent(component); + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testRead(design, grid, true); + testWrite(design, grid); + } + + @Test + public void testHtmlEntitiesinGridHeaderFooter() { + //@formatter:off + String design = "" + + "" + + " test\">" + + "" + + "" + + " " + + "" + + "" + + " " + + "" + + "" + + "
> Test
> Test
"; + //@formatter:on + + LegacyGrid grid = read(design); + String actualHeader = grid.getHeaderRow(0).getCell("> test").getText(); + String actualFooter = grid.getFooterRow(0).getCell("> test").getText(); + String expected = "> Test"; + + Assert.assertEquals(expected, actualHeader); + Assert.assertEquals(expected, actualFooter); + + design = design.replace("plain-text=\"true\"", ""); + grid = read(design); + actualHeader = grid.getHeaderRow(0).getCell("> test").getHtml(); + actualFooter = grid.getFooterRow(0).getCell("> test").getHtml(); + expected = "> Test"; + + Assert.assertEquals(expected, actualHeader); + Assert.assertEquals(expected, actualFooter); + + grid = new LegacyGrid(); + grid.setColumns("test"); + HeaderRow header = grid.addHeaderRowAt(0); + FooterRow footer = grid.addFooterRowAt(0); + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + // entities should be encoded when writing back, not interpreted as HTML + header.getCell("test").setText("& Test"); + footer.getCell("test").setText("& Test"); + + Element root = new Element(Tag.valueOf("vaadin-legacy-grid"), ""); + grid.writeDesign(root, new DesignContext()); + + Assert.assertEquals("&amp; Test", + root.getElementsByTag("th").get(0).html()); + Assert.assertEquals("&amp; Test", + root.getElementsByTag("td").get(0).html()); + + header = grid.addHeaderRowAt(0); + footer = grid.addFooterRowAt(0); + + // entities should not be encoded, this is already given as HTML + header.getCell("test").setHtml("& Test"); + footer.getCell("test").setHtml("& Test"); + + root = new Element(Tag.valueOf("vaadin-legacy-grid"), ""); + grid.writeDesign(root, new DesignContext()); + + Assert.assertEquals("& Test", + root.getElementsByTag("th").get(0).html()); + Assert.assertEquals("& Test", + root.getElementsByTag("td").get(0).html()); + + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java new file mode 100644 index 0000000000..f5b98824cd --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java @@ -0,0 +1,124 @@ +/* + * 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.tests.server.component.grid.declarative; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Container; + +public class GridInlineDataDeclarativeTest extends GridDeclarativeTestBase { + + @Test + public void testSimpleInlineData() { + String design = ""// + + "" + " " + + "" // + + "" // No headers read or written + + "" // + + "" // + + "" // + + "" // + + "" // + + "
Foo
Bar
Baz
"; + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Col1", String.class); + grid.addRow("Foo"); + grid.addRow("Bar"); + grid.addRow("Baz"); + + // Remove default header + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid, true); + testRead(design, grid, true, true); + } + + @Test + public void testMultipleColumnsInlineData() { + String design = ""// + + "" + " " + + " " + + " " // + + "" // + + "" // No headers read or written + + "" // + + "" // + + "" // + + "" // + + "
FooBarBaz
MySummerCar
"; + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Col1", String.class); + grid.addColumn("Col2", String.class); + grid.addColumn("Col3", String.class); + grid.addRow("Foo", "Bar", "Baz"); + grid.addRow("My", "Summer", "Car"); + + // Remove default header + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid, true); + testRead(design, grid, true, true); + } + + @Test + public void testMultipleColumnsInlineDataReordered() { + String design = ""// + + "" + " " + + " " + + " " // + + "" // + + "" // No headers read or written + + "" // + + "" // + + "" // + + "" // + + "
BarBazFoo
SummerCarMy
"; + + LegacyGrid grid = new LegacyGrid(); + grid.addColumn("Col1", String.class); + grid.addColumn("Col2", String.class); + grid.addColumn("Col3", String.class); + grid.addRow("Foo", "Bar", "Baz"); + grid.addRow("My", "Summer", "Car"); + grid.setColumnOrder("Col2", "Col3", "Col1"); + + // Remove default header + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid, true); + testRead(design, grid, true, true); + } + + @Test + public void testHtmlEntities() { + String design = ""// + + "" + " " + "" // + + "" // No headers read or written + + "" // + + " " + "" + + "
&Test
"; + + LegacyGrid read = read(design); + Container cds = read.getContainerDataSource(); + Assert.assertEquals("&Test", + cds.getItem(cds.getItemIds().iterator().next()) + .getItemProperty("test").getValue()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java new file mode 100644 index 0000000000..b55b0815f8 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java @@ -0,0 +1,50 @@ +/* + * 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.tests.server.component.grid.declarative; + +import com.vaadin.ui.LegacyGrid; +import org.junit.Test; + +import com.vaadin.ui.declarative.DesignException; + +public class GridStructureDeclarativeTest extends GridDeclarativeTestBase { + + @Test + public void testReadEmptyGrid() { + String design = ""; + testRead(design, new LegacyGrid(), false); + } + + @Test + public void testEmptyGrid() { + String design = ""; + LegacyGrid expected = new LegacyGrid(); + testWrite(design, expected); + testRead(design, expected, true); + } + + @Test(expected = DesignException.class) + public void testMalformedGrid() { + String design = ""; + testRead(design, new LegacyGrid()); + } + + @Test(expected = DesignException.class) + public void testGridWithNoColGroup() { + String design = "
Foo
"; + testRead(design, new LegacyGrid()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java new file mode 100644 index 0000000000..beb774528f --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java @@ -0,0 +1,205 @@ +/* + * 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.tests.server.component.grid.sort; + +import java.util.Arrays; +import java.util.List; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.sort.Sort; +import com.vaadin.data.sort.SortOrder; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.event.SortEvent; +import com.vaadin.event.SortEvent.SortListener; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.ui.LegacyGrid; + +public class SortTest { + + class DummySortingIndexedContainer extends IndexedContainer { + + private Object[] expectedProperties; + private boolean[] expectedAscending; + private boolean sorted = true; + + @Override + public void sort(Object[] propertyId, boolean[] ascending) { + Assert.assertEquals( + "Different amount of expected and actual properties,", + expectedProperties.length, propertyId.length); + Assert.assertEquals( + "Different amount of expected and actual directions", + expectedAscending.length, ascending.length); + for (int i = 0; i < propertyId.length; ++i) { + Assert.assertEquals("Sorting properties differ", + expectedProperties[i], propertyId[i]); + Assert.assertEquals("Sorting directions differ", + expectedAscending[i], ascending[i]); + } + sorted = true; + } + + public void expectedSort(Object[] properties, + SortDirection[] directions) { + assert directions.length == properties.length : "Array dimensions differ"; + expectedProperties = properties; + expectedAscending = new boolean[directions.length]; + for (int i = 0; i < directions.length; ++i) { + expectedAscending[i] = (directions[i] == SortDirection.ASCENDING); + } + sorted = false; + } + + public boolean isSorted() { + return sorted; + } + } + + class RegisteringSortChangeListener implements SortListener { + private List order; + + @Override + public void sort(SortEvent event) { + assert order == null : "The same listener was notified multipe times without checking"; + + order = event.getSortOrder(); + } + + public void assertEventFired(SortOrder... expectedOrder) { + Assert.assertEquals(Arrays.asList(expectedOrder), order); + + // Reset for nest test + order = null; + } + + } + + private DummySortingIndexedContainer container; + private RegisteringSortChangeListener listener; + private LegacyGrid grid; + + @Before + public void setUp() { + container = createContainer(); + container.expectedSort(new Object[] {}, new SortDirection[] {}); + + listener = new RegisteringSortChangeListener(); + + grid = new LegacyGrid(container); + grid.addSortListener(listener); + } + + @After + public void tearDown() { + Assert.assertTrue("Container was not sorted after the test.", + container.isSorted()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidSortDirection() { + Sort.by("foo", null); + } + + @Test(expected = IllegalStateException.class) + public void testSortOneColumnMultipleTimes() { + Sort.by("foo").then("bar").then("foo"); + } + + @Test(expected = IllegalArgumentException.class) + public void testSortingByUnexistingProperty() { + grid.sort("foobar"); + } + + @Test(expected = IllegalArgumentException.class) + public void testSortingByUnsortableProperty() { + container.addContainerProperty("foobar", Object.class, null); + grid.sort("foobar"); + } + + @Test + public void testGridDirectSortAscending() { + container.expectedSort(new Object[] { "foo" }, + new SortDirection[] { SortDirection.ASCENDING }); + grid.sort("foo"); + + listener.assertEventFired( + new SortOrder("foo", SortDirection.ASCENDING)); + } + + @Test + public void testGridDirectSortDescending() { + container.expectedSort(new Object[] { "foo" }, + new SortDirection[] { SortDirection.DESCENDING }); + grid.sort("foo", SortDirection.DESCENDING); + + listener.assertEventFired( + new SortOrder("foo", SortDirection.DESCENDING)); + } + + @Test + public void testGridSortBy() { + container.expectedSort(new Object[] { "foo", "bar", "baz" }, + new SortDirection[] { SortDirection.ASCENDING, + SortDirection.ASCENDING, SortDirection.DESCENDING }); + grid.sort(Sort.by("foo").then("bar").then("baz", + SortDirection.DESCENDING)); + + listener.assertEventFired(new SortOrder("foo", SortDirection.ASCENDING), + new SortOrder("bar", SortDirection.ASCENDING), + new SortOrder("baz", SortDirection.DESCENDING)); + + } + + @Test + public void testChangeContainerAfterSorting() { + class Person { + } + + container.expectedSort(new Object[] { "foo", "bar", "baz" }, + new SortDirection[] { SortDirection.ASCENDING, + SortDirection.ASCENDING, SortDirection.DESCENDING }); + grid.sort(Sort.by("foo").then("bar").then("baz", + SortDirection.DESCENDING)); + + listener.assertEventFired(new SortOrder("foo", SortDirection.ASCENDING), + new SortOrder("bar", SortDirection.ASCENDING), + new SortOrder("baz", SortDirection.DESCENDING)); + + container = new DummySortingIndexedContainer(); + container.addContainerProperty("foo", Person.class, null); + container.addContainerProperty("baz", String.class, ""); + container.addContainerProperty("bar", Person.class, null); + container.expectedSort(new Object[] { "baz" }, + new SortDirection[] { SortDirection.DESCENDING }); + grid.setContainerDataSource(container); + + listener.assertEventFired( + new SortOrder("baz", SortDirection.DESCENDING)); + + } + + private DummySortingIndexedContainer createContainer() { + DummySortingIndexedContainer container = new DummySortingIndexedContainer(); + container.addContainerProperty("foo", Integer.class, 0); + container.addContainerProperty("bar", Integer.class, 0); + container.addContainerProperty("baz", Integer.class, 0); + return container; + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java new file mode 100644 index 0000000000..822a2353ac --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java @@ -0,0 +1,85 @@ +/* + * 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.tests.server.renderer; + +import static org.junit.Assert.assertEquals; + +import java.io.File; + +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.server.ClassResource; +import com.vaadin.server.ExternalResource; +import com.vaadin.server.FileResource; +import com.vaadin.server.FontAwesome; +import com.vaadin.server.ThemeResource; +import com.vaadin.ui.LegacyGrid; +import com.vaadin.ui.UI; +import com.vaadin.ui.renderers.ImageRenderer; + +import elemental.json.JsonObject; +import elemental.json.JsonValue; + +public class ImageRendererTest { + + private ImageRenderer renderer; + + @Before + public void setUp() { + UI mockUI = EasyMock.createNiceMock(UI.class); + EasyMock.replay(mockUI); + + LegacyGrid grid = new LegacyGrid(); + grid.setParent(mockUI); + + renderer = new ImageRenderer(); + renderer.setParent(grid); + } + + @Test + public void testThemeResource() { + JsonValue v = renderer.encode(new ThemeResource("foo.png")); + assertEquals("theme://foo.png", getUrl(v)); + } + + @Test + public void testExternalResource() { + JsonValue v = renderer + .encode(new ExternalResource("http://example.com/foo.png")); + assertEquals("http://example.com/foo.png", getUrl(v)); + } + + @Test(expected = IllegalArgumentException.class) + public void testFileResource() { + renderer.encode(new FileResource(new File("/tmp/foo.png"))); + } + + @Test(expected = IllegalArgumentException.class) + public void testClassResource() { + renderer.encode(new ClassResource("img/foo.png")); + } + + @Test(expected = IllegalArgumentException.class) + public void testFontIcon() { + renderer.encode(FontAwesome.AMBULANCE); + } + + private String getUrl(JsonValue v) { + return ((JsonObject) v).get("uRL").asString(); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java new file mode 100644 index 0000000000..5eec5b6ea8 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java @@ -0,0 +1,268 @@ +/* + * 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.tests.server.renderer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.util.Date; +import java.util.Locale; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Item; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.VaadinSession; +import com.vaadin.tests.server.component.grid.TestGrid; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; +import com.vaadin.ui.LegacyGrid; +import com.vaadin.ui.LegacyGrid.AbstractRenderer; +import com.vaadin.ui.LegacyGrid.Column; +import com.vaadin.ui.renderers.ButtonRenderer; +import com.vaadin.ui.renderers.DateRenderer; +import com.vaadin.ui.renderers.HtmlRenderer; +import com.vaadin.ui.renderers.NumberRenderer; +import com.vaadin.ui.renderers.TextRenderer; +import com.vaadin.v7.data.util.converter.LegacyConverter; +import com.vaadin.v7.data.util.converter.LegacyStringToIntegerConverter; + +import elemental.json.JsonValue; + +public class RendererTest { + + private static class TestBean { + int i = 42; + + @Override + public String toString() { + return "TestBean [" + i + "]"; + } + } + + private static class ExtendedBean extends TestBean { + float f = 3.14f; + } + + private static class TestRenderer extends TextRenderer { + @Override + public JsonValue encode(String value) { + return super.encode("renderer(" + value + ")"); + } + } + + private static class TestConverter + implements LegacyConverter { + + @Override + public TestBean convertToModel(String value, + Class targetType, Locale locale) + throws ConversionException { + return null; + } + + @Override + public String convertToPresentation(TestBean value, + Class targetType, Locale locale) + throws ConversionException { + if (value instanceof ExtendedBean) { + return "ExtendedBean(" + value.i + ", " + + ((ExtendedBean) value).f + ")"; + } else { + return "TestBean(" + value.i + ")"; + } + } + + @Override + public Class getModelType() { + return TestBean.class; + } + + @Override + public Class getPresentationType() { + return String.class; + } + } + + private LegacyGrid grid; + + private Column intColumn; + private Column textColumn; + private Column beanColumn; + private Column htmlColumn; + private Column numberColumn; + private Column dateColumn; + private Column extendedBeanColumn; + private Column buttonColumn; + + @Before + @SuppressWarnings("unchecked") + public void setUp() { + VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); + + IndexedContainer c = new IndexedContainer(); + + c.addContainerProperty("int", Integer.class, 0); + c.addContainerProperty("text", String.class, ""); + c.addContainerProperty("html", String.class, ""); + c.addContainerProperty("number", Number.class, null); + c.addContainerProperty("date", Date.class, null); + c.addContainerProperty("bean", TestBean.class, null); + c.addContainerProperty("button", String.class, null); + c.addContainerProperty("extendedBean", ExtendedBean.class, null); + + Object id = c.addItem(); + Item item = c.getItem(id); + item.getItemProperty("int").setValue(123); + item.getItemProperty("text").setValue("321"); + item.getItemProperty("html").setValue("html"); + item.getItemProperty("number").setValue(3.14); + item.getItemProperty("date").setValue(new Date(123456789)); + item.getItemProperty("bean").setValue(new TestBean()); + item.getItemProperty("extendedBean").setValue(new ExtendedBean()); + + grid = new TestGrid(c); + + intColumn = grid.getColumn("int"); + textColumn = grid.getColumn("text"); + htmlColumn = grid.getColumn("html"); + numberColumn = grid.getColumn("number"); + dateColumn = grid.getColumn("date"); + beanColumn = grid.getColumn("bean"); + extendedBeanColumn = grid.getColumn("extendedBean"); + buttonColumn = grid.getColumn("button"); + + } + + @Test + public void testDefaultRendererAndConverter() throws Exception { + assertSame(TextRenderer.class, intColumn.getRenderer().getClass()); + assertSame(LegacyStringToIntegerConverter.class, + intColumn.getConverter().getClass()); + + assertSame(TextRenderer.class, textColumn.getRenderer().getClass()); + // String->String; converter not needed + assertNull(textColumn.getConverter()); + + assertSame(TextRenderer.class, beanColumn.getRenderer().getClass()); + // MyBean->String; converter not found + assertNull(beanColumn.getConverter()); + } + + @Test + public void testFindCompatibleConverter() throws Exception { + intColumn.setRenderer(renderer()); + assertSame(LegacyStringToIntegerConverter.class, + intColumn.getConverter().getClass()); + + textColumn.setRenderer(renderer()); + assertNull(textColumn.getConverter()); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotFindConverter() { + beanColumn.setRenderer(renderer()); + } + + @Test + public void testExplicitConverter() throws Exception { + beanColumn.setRenderer(renderer(), converter()); + extendedBeanColumn.setRenderer(renderer(), converter()); + } + + @Test + public void testEncoding() throws Exception { + assertEquals("42", render(intColumn, 42).asString()); + intColumn.setRenderer(renderer()); + assertEquals("renderer(42)", render(intColumn, 42).asString()); + + assertEquals("2.72", render(textColumn, "2.72").asString()); + textColumn.setRenderer(new TestRenderer()); + assertEquals("renderer(2.72)", render(textColumn, "2.72").asString()); + } + + @Test + public void testEncodingWithoutConverter() throws Exception { + assertEquals("TestBean [42]", + render(beanColumn, new TestBean()).asString()); + } + + @Test + public void testBeanEncoding() throws Exception { + beanColumn.setRenderer(renderer(), converter()); + extendedBeanColumn.setRenderer(renderer(), converter()); + + assertEquals("renderer(TestBean(42))", + render(beanColumn, new TestBean()).asString()); + assertEquals("renderer(ExtendedBean(42, 3.14))", + render(beanColumn, new ExtendedBean()).asString()); + + assertEquals("renderer(ExtendedBean(42, 3.14))", + render(extendedBeanColumn, new ExtendedBean()).asString()); + } + + @Test + public void testNullEncoding() { + + textColumn.setRenderer(new TextRenderer()); + htmlColumn.setRenderer(new HtmlRenderer()); + numberColumn.setRenderer(new NumberRenderer()); + dateColumn.setRenderer(new DateRenderer()); + buttonColumn.setRenderer(new ButtonRenderer()); + + assertEquals("", textColumn.getRenderer().encode(null).asString()); + assertEquals("", htmlColumn.getRenderer().encode(null).asString()); + assertEquals("", numberColumn.getRenderer().encode(null).asString()); + assertEquals("", dateColumn.getRenderer().encode(null).asString()); + assertEquals("", buttonColumn.getRenderer().encode(null).asString()); + } + + @Test + public void testNullEncodingWithDefault() { + + textColumn.setRenderer(new TextRenderer("default value")); + htmlColumn.setRenderer(new HtmlRenderer("default value")); + numberColumn.setRenderer( + new NumberRenderer("%s", Locale.getDefault(), "default value")); + dateColumn.setRenderer(new DateRenderer("%s", "default value")); + buttonColumn.setRenderer(new ButtonRenderer("default value")); + + assertEquals("default value", + textColumn.getRenderer().encode(null).asString()); + assertEquals("default value", + htmlColumn.getRenderer().encode(null).asString()); + assertEquals("default value", + numberColumn.getRenderer().encode(null).asString()); + assertEquals("default value", + dateColumn.getRenderer().encode(null).asString()); + assertEquals("default value", + buttonColumn.getRenderer().encode(null).asString()); + } + + private TestConverter converter() { + return new TestConverter(); + } + + private TestRenderer renderer() { + return new TestRenderer(); + } + + private JsonValue render(Column column, Object value) { + return AbstractRenderer.encodeValue(value, column.getRenderer(), + column.getConverter(), grid.getLocale()); + } +} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java new file mode 100644 index 0000000000..a8e6238d4b --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java @@ -0,0 +1,129 @@ +package com.vaadin.tests.server.validation; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.fieldgroup.BeanFieldGroup; +import com.vaadin.tests.data.bean.BeanToValidate; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.data.validator.LegacyBeanValidator; +import com.vaadin.v7.ui.LegacyField; + +public class BeanValidationTest { + @Test(expected = InvalidValueException.class) + public void testBeanValidationNull() { + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "firstname"); + validator.validate(null); + } + + @Test(expected = InvalidValueException.class) + public void testBeanValidationStringTooShort() { + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "firstname"); + validator.validate("aa"); + } + + @Test + public void testBeanValidationStringOk() { + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "firstname"); + validator.validate("aaa"); + } + + @Test(expected = InvalidValueException.class) + public void testBeanValidationIntegerTooSmall() { + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "age"); + validator.validate(17); + } + + @Test + public void testBeanValidationIntegerOk() { + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "age"); + validator.validate(18); + } + + @Test(expected = InvalidValueException.class) + public void testBeanValidationTooManyDigits() { + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "decimals"); + validator.validate("1234.567"); + } + + @Test + public void testBeanValidationDigitsOk() { + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "decimals"); + validator.validate("123.45"); + } + + @Test + public void testBeanValidationException_OneValidationError() { + InvalidValueException[] causes = null; + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "lastname"); + try { + validator.validate(null); + } catch (InvalidValueException e) { + causes = e.getCauses(); + } + + Assert.assertEquals(1, causes.length); + } + + @Test + public void testBeanValidationsException_TwoValidationErrors() { + InvalidValueException[] causes = null; + LegacyBeanValidator validator = new LegacyBeanValidator( + BeanToValidate.class, "nickname"); + try { + validator.validate("A"); + } catch (InvalidValueException e) { + causes = e.getCauses(); + } + + Assert.assertEquals(2, causes.length); + } + + @Test + public void testBeanValidationNotAddedTwice() { + // See ticket #11045 + BeanFieldGroup fieldGroup = new BeanFieldGroup( + BeanToValidate.class); + + BeanToValidate beanToValidate = new BeanToValidate(); + beanToValidate.setFirstname("a"); + fieldGroup.setItemDataSource(beanToValidate); + + LegacyField nameField = fieldGroup.buildAndBind("firstname"); + Assert.assertEquals(1, nameField.getValidators().size()); + + try { + nameField.validate(); + } catch (InvalidValueException e) { + // The 1 cause is from BeanValidator, where it tells what failed + // 1 validation exception never gets wrapped. + Assert.assertEquals(1, e.getCauses().length); + } + + // Create new, identical bean to cause duplicate validator unless #11045 + // is fixed + beanToValidate = new BeanToValidate(); + beanToValidate.setFirstname("a"); + fieldGroup.setItemDataSource(beanToValidate); + + Assert.assertEquals(1, nameField.getValidators().size()); + + try { + nameField.validate(); + } catch (InvalidValueException e) { + // The 1 cause is from BeanValidator, where it tells what failed + // 1 validation exception never gets wrapped. + Assert.assertEquals(1, e.getCauses().length); + } + + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/ui/TableTest.java b/compatibility-server/src/test/java/com/vaadin/ui/TableTest.java new file mode 100644 index 0000000000..f09313f685 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/ui/TableTest.java @@ -0,0 +1,78 @@ +/* + * 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.ui; + +import java.util.Collection; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.BeanItemContainerGenerator; + +public class TableTest { + + Table table; + + @Before + public void init() { + table = new Table(); + } + + @Test + public void initiallyEmpty() { + Assert.assertTrue(table.isEmpty()); + } + + @Test + public void emptyAfterClearSingleSelect() { + table.setContainerDataSource( + BeanItemContainerGenerator.createContainer(100)); + Assert.assertTrue(table.isEmpty()); + Object first = table.getContainerDataSource().getItemIds().iterator() + .next(); + table.setValue(first); + Assert.assertEquals(first, table.getValue()); + Assert.assertFalse(table.isEmpty()); + table.clear(); + Assert.assertEquals(null, table.getValue()); + Assert.assertTrue(table.isEmpty()); + } + + @Test + public void emptyAfterClearMultiSelect() { + table.setMultiSelect(true); + table.setContainerDataSource( + BeanItemContainerGenerator.createContainer(100)); + + Assert.assertTrue(table.isEmpty()); + Assert.assertArrayEquals(new Object[] {}, + ((Collection) table.getValue()).toArray()); + + Object first = table.getContainerDataSource().getItemIds().iterator() + .next(); + table.select(first); + Assert.assertArrayEquals(new Object[] { first }, + ((Collection) table.getValue()).toArray()); + Assert.assertFalse(table.isEmpty()); + + table.clear(); + Assert.assertArrayEquals(new Object[] {}, + ((Collection) table.getValue()).toArray()); + Assert.assertTrue(table.isEmpty()); + } + +} diff --git a/ivysettings.xml b/ivysettings.xml index 6cb125ddd3..4a0c3f5ff8 100644 --- a/ivysettings.xml +++ b/ivysettings.xml @@ -41,14 +41,22 @@ resolver="vaadin-maven" /> + + + + + + + + + test-jar + + + diff --git a/server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java deleted file mode 100644 index 96e4621761..0000000000 --- a/server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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.data.fieldgroup; - -import java.beans.IntrospectionException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - -import com.vaadin.data.Item; -import com.vaadin.data.util.BeanItem; -import com.vaadin.data.util.BeanUtil; -import com.vaadin.v7.data.validator.LegacyBeanValidator; -import com.vaadin.v7.ui.LegacyField; - -public class BeanFieldGroup extends FieldGroup { - - private final Class beanType; - - private static Boolean beanValidationImplementationAvailable = null; - private final Map, LegacyBeanValidator> defaultValidators; - - public BeanFieldGroup(Class beanType) { - this.beanType = beanType; - this.defaultValidators = new HashMap, LegacyBeanValidator>(); - } - - @Override - protected Class getPropertyType(Object propertyId) { - if (getItemDataSource() != null) { - return super.getPropertyType(propertyId); - } else { - // Data source not set so we need to figure out the type manually - /* - * toString should never really be needed as propertyId should be of - * form "fieldName" or "fieldName.subField[.subField2]" but the - * method declaration comes from parent. - */ - try { - Class type = BeanUtil.getPropertyType(beanType, - propertyId.toString()); - if (type == null) { - throw new BindException( - "Cannot determine type of propertyId '" + propertyId - + "'. The propertyId was not found in " - + beanType.getName()); - } - return type; - } catch (IntrospectionException e) { - throw new BindException("Cannot determine type of propertyId '" - + propertyId + "'. Unable to introspect " + beanType, - e); - } - } - } - - @Override - protected Object findPropertyId(java.lang.reflect.Field memberField) { - String fieldName = memberField.getName(); - Item dataSource = getItemDataSource(); - if (dataSource != null - && dataSource.getItemProperty(fieldName) != null) { - return fieldName; - } else { - String minifiedFieldName = minifyFieldName(fieldName); - try { - return getFieldName(beanType, minifiedFieldName); - } catch (SecurityException e) { - } catch (NoSuchFieldException e) { - } - } - return null; - } - - private static String getFieldName(Class cls, String propertyId) - throws SecurityException, NoSuchFieldException { - for (java.lang.reflect.Field field1 : cls.getDeclaredFields()) { - if (propertyId.equals(minifyFieldName(field1.getName()))) { - return field1.getName(); - } - } - // Try super classes until we reach Object - Class superClass = cls.getSuperclass(); - if (superClass != null && superClass != Object.class) { - return getFieldName(superClass, propertyId); - } else { - throw new NoSuchFieldException(); - } - } - - /** - * Helper method for setting the data source directly using a bean. This - * method wraps the bean in a {@link BeanItem} and calls - * {@link #setItemDataSource(Item)}. - *

- * For null values, a null item is passed to - * {@link #setItemDataSource(Item)} to be properly clear fields. - * - * @param bean - * The bean to use as data source. - */ - public void setItemDataSource(T bean) { - if (bean == null) { - setItemDataSource((Item) null); - } else { - setItemDataSource(new BeanItem(bean, beanType)); - } - } - - @Override - public void setItemDataSource(Item item) { - if (item == null || (item instanceof BeanItem)) { - super.setItemDataSource(item); - } else { - throw new RuntimeException(getClass().getSimpleName() - + " only supports BeanItems as item data source"); - } - } - - @Override - public BeanItem getItemDataSource() { - return (BeanItem) super.getItemDataSource(); - } - - private void ensureNestedPropertyAdded(Object propertyId) { - if (getItemDataSource() != null) { - // The data source is set so the property must be found in the item. - // If it is not we try to add it. - try { - getItemProperty(propertyId); - } catch (BindException e) { - // Not found, try to add a nested property; - // BeanItem property ids are always strings so this is safe - getItemDataSource().addNestedProperty((String) propertyId); - } - } - } - - @Override - public void bind(LegacyField field, Object propertyId) { - ensureNestedPropertyAdded(propertyId); - super.bind(field, propertyId); - } - - @Override - public T buildAndBind(String caption, - Object propertyId, Class fieldType) throws BindException { - ensureNestedPropertyAdded(propertyId); - return super.buildAndBind(caption, propertyId, fieldType); - } - - @Override - public void unbind(LegacyField field) throws BindException { - super.unbind(field); - - LegacyBeanValidator removed = defaultValidators.remove(field); - if (removed != null) { - field.removeValidator(removed); - } - } - - @Override - protected void configureField(LegacyField field) { - super.configureField(field); - // Add Bean validators if there are annotations - if (isBeanValidationImplementationAvailable() - && !defaultValidators.containsKey(field)) { - LegacyBeanValidator validator = new LegacyBeanValidator(beanType, - getPropertyId(field).toString()); - field.addValidator(validator); - if (field.getLocale() != null) { - validator.setLocale(field.getLocale()); - } - defaultValidators.put(field, validator); - } - } - - /** - * Checks whether a bean validation implementation (e.g. Hibernate Validator - * or Apache Bean Validation) is available. - * - * TODO move this method to some more generic location - * - * @return true if a JSR-303 bean validation implementation is available - */ - protected static boolean isBeanValidationImplementationAvailable() { - if (beanValidationImplementationAvailable != null) { - return beanValidationImplementationAvailable; - } - try { - Class validationClass = Class - .forName("javax.validation.Validation"); - Method buildFactoryMethod = validationClass - .getMethod("buildDefaultValidatorFactory"); - Object factory = buildFactoryMethod.invoke(null); - beanValidationImplementationAvailable = (factory != null); - } catch (Exception e) { - // no bean validation implementation available - beanValidationImplementationAvailable = false; - } - return beanValidationImplementationAvailable; - } - - /** - * Convenience method to bind Fields from a given "field container" to a - * given bean with buffering disabled. - *

- * The returned {@link BeanFieldGroup} can be used for further - * configuration. - * - * @see #bindFieldsBuffered(Object, Object) - * @see #bindMemberFields(Object) - * @since 7.2 - * @param bean - * the bean to be bound - * @param objectWithMemberFields - * the class that contains {@link LegacyField}s for bean - * properties - * @return the bean field group used to make binding - */ - public static BeanFieldGroup bindFieldsUnbuffered(T bean, - Object objectWithMemberFields) { - return createAndBindFields(bean, objectWithMemberFields, false); - } - - /** - * Convenience method to bind Fields from a given "field container" to a - * given bean with buffering enabled. - *

- * The returned {@link BeanFieldGroup} can be used for further - * configuration. - * - * @see #bindFieldsUnbuffered(Object, Object) - * @see #bindMemberFields(Object) - * @since 7.2 - * @param bean - * the bean to be bound - * @param objectWithMemberFields - * the class that contains {@link LegacyField}s for bean - * properties - * @return the bean field group used to make binding - */ - public static BeanFieldGroup bindFieldsBuffered(T bean, - Object objectWithMemberFields) { - return createAndBindFields(bean, objectWithMemberFields, true); - } - - private static BeanFieldGroup createAndBindFields(T bean, - Object objectWithMemberFields, boolean buffered) { - @SuppressWarnings("unchecked") - BeanFieldGroup beanFieldGroup = new BeanFieldGroup( - (Class) bean.getClass()); - beanFieldGroup.setItemDataSource(bean); - beanFieldGroup.setBuffered(buffered); - beanFieldGroup.bindMemberFields(objectWithMemberFields); - return beanFieldGroup; - } - -} diff --git a/server/src/main/java/com/vaadin/data/fieldgroup/Caption.java b/server/src/main/java/com/vaadin/data/fieldgroup/Caption.java deleted file mode 100644 index d752aa78d2..0000000000 --- a/server/src/main/java/com/vaadin/data/fieldgroup/Caption.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.data.fieldgroup; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Caption { - String value(); -} diff --git a/server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java deleted file mode 100644 index 24c97eedc5..0000000000 --- a/server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * 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.data.fieldgroup; - -import java.util.Date; -import java.util.EnumSet; - -import com.vaadin.data.Item; -import com.vaadin.data.fieldgroup.FieldGroup.BindException; -import com.vaadin.ui.AbstractSelect; -import com.vaadin.ui.ComboBox; -import com.vaadin.ui.ListSelect; -import com.vaadin.ui.NativeSelect; -import com.vaadin.ui.OptionGroup; -import com.vaadin.ui.RichTextArea; -import com.vaadin.ui.Table; -import com.vaadin.v7.ui.LegacyAbstractField; -import com.vaadin.v7.ui.LegacyAbstractTextField; -import com.vaadin.v7.ui.LegacyCheckBox; -import com.vaadin.v7.ui.LegacyDateField; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyInlineDateField; -import com.vaadin.v7.ui.LegacyPopupDateField; -import com.vaadin.v7.ui.LegacyTextField; - -/** - * This class contains a basic implementation for {@link FieldGroupFieldFactory} - * .The class is singleton, use {@link #get()} method to get reference to the - * instance. - * - * @author Vaadin Ltd - */ -public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory { - - private static final DefaultFieldGroupFieldFactory INSTANCE = new DefaultFieldGroupFieldFactory(); - - public static final Object CAPTION_PROPERTY_ID = "Caption"; - - protected DefaultFieldGroupFieldFactory() { - } - - /** - * Gets the singleton instance. - * - * @since 7.4 - * - * @return the singleton instance - */ - public static DefaultFieldGroupFieldFactory get() { - return INSTANCE; - } - - @Override - public T createField(Class type, - Class fieldType) { - if (Enum.class.isAssignableFrom(type)) { - return createEnumField(type, fieldType); - } else if (Date.class.isAssignableFrom(type)) { - return createDateField(type, fieldType); - } else if (Boolean.class.isAssignableFrom(type) - || boolean.class.isAssignableFrom(type)) { - return createBooleanField(fieldType); - } - if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { - return fieldType.cast(createAbstractTextField( - fieldType.asSubclass(LegacyAbstractTextField.class))); - } else if (fieldType == RichTextArea.class) { - return fieldType.cast(createRichTextArea()); - } - return createDefaultField(type, fieldType); - } - - protected RichTextArea createRichTextArea() { - RichTextArea rta = new RichTextArea(); - rta.setImmediate(true); - - return rta; - } - - private T createEnumField(Class type, - Class fieldType) { - // Determine first if we should (or can) create a select for the enum - Class selectClass = null; - if (AbstractSelect.class.isAssignableFrom(fieldType)) { - selectClass = (Class) fieldType; - } else if (anySelect(fieldType)) { - selectClass = AbstractSelect.class; - } - - if (selectClass != null) { - AbstractSelect s = createCompatibleSelect(selectClass); - populateWithEnumData(s, (Class) type); - return (T) s; - } else if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { - return (T) createAbstractTextField( - (Class) fieldType); - } - - return null; - } - - private T createDateField(Class type, - Class fieldType) { - LegacyAbstractField field; - - if (LegacyInlineDateField.class.isAssignableFrom(fieldType)) { - field = new LegacyInlineDateField(); - } else if (anyField(fieldType) - || LegacyDateField.class.isAssignableFrom(fieldType)) { - field = new LegacyPopupDateField(); - } else if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { - field = createAbstractTextField( - (Class) fieldType); - } else { - return null; - } - - field.setImmediate(true); - return (T) field; - } - - protected AbstractSelect createCompatibleSelect( - Class fieldType) { - AbstractSelect select; - if (fieldType.isAssignableFrom(ListSelect.class)) { - select = new ListSelect(); - select.setMultiSelect(false); - } else if (fieldType.isAssignableFrom(NativeSelect.class)) { - select = new NativeSelect(); - } else if (fieldType.isAssignableFrom(OptionGroup.class)) { - select = new OptionGroup(); - select.setMultiSelect(false); - } else if (fieldType.isAssignableFrom(Table.class)) { - Table t = new Table(); - t.setSelectable(true); - select = t; - } else { - select = new ComboBox(null); - } - select.setImmediate(true); - select.setNullSelectionAllowed(false); - - return select; - } - - /** - * @since 7.4 - * @param fieldType - * the type of the field - * @return true if any LegacyAbstractField can be assigned to the field - */ - protected boolean anyField(Class fieldType) { - return fieldType == LegacyField.class - || fieldType == LegacyAbstractField.class; - } - - /** - * @since 7.4 - * @param fieldType - * the type of the field - * @return true if any AbstractSelect can be assigned to the field - */ - protected boolean anySelect(Class fieldType) { - return anyField(fieldType) || fieldType == AbstractSelect.class; - } - - protected T createBooleanField(Class fieldType) { - if (fieldType.isAssignableFrom(LegacyCheckBox.class)) { - LegacyCheckBox cb = new LegacyCheckBox(null); - cb.setImmediate(true); - return (T) cb; - } else if (LegacyAbstractTextField.class.isAssignableFrom(fieldType)) { - return (T) createAbstractTextField( - (Class) fieldType); - } - - return null; - } - - protected T createAbstractTextField( - Class fieldType) { - if (fieldType == LegacyAbstractTextField.class) { - fieldType = (Class) LegacyTextField.class; - } - try { - T field = fieldType.newInstance(); - field.setImmediate(true); - return field; - } catch (Exception e) { - throw new BindException( - "Could not create a field of type " + fieldType, e); - } - } - - /** - * Fallback when no specific field has been created. Typically returns a - * TextField. - * - * @param - * The type of field to create - * @param type - * The type of data that should be edited - * @param fieldType - * The type of field to create - * @return A field capable of editing the data or null if no field could be - * created - */ - protected T createDefaultField(Class type, - Class fieldType) { - if (fieldType.isAssignableFrom(LegacyTextField.class)) { - return fieldType - .cast(createAbstractTextField(LegacyTextField.class)); - } - return null; - } - - /** - * Populates the given select with all the enums in the given {@link Enum} - * class. Uses {@link Enum}.toString() for caption. - * - * @param select - * The select to populate - * @param enumClass - * The Enum class to use - */ - protected void populateWithEnumData(AbstractSelect select, - Class enumClass) { - select.removeAllItems(); - for (Object p : select.getContainerPropertyIds()) { - select.removeContainerProperty(p); - } - select.addContainerProperty(CAPTION_PROPERTY_ID, String.class, ""); - select.setItemCaptionPropertyId(CAPTION_PROPERTY_ID); - @SuppressWarnings("unchecked") - EnumSet enumSet = EnumSet.allOf(enumClass); - for (Object r : enumSet) { - Item newItem = select.addItem(r); - newItem.getItemProperty(CAPTION_PROPERTY_ID).setValue(r.toString()); - } - } -} diff --git a/server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java deleted file mode 100644 index 1254009cfc..0000000000 --- a/server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java +++ /dev/null @@ -1,1258 +0,0 @@ -/* - * 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.data.fieldgroup; - -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.TransactionalPropertyWrapper; -import com.vaadin.ui.DefaultFieldFactory; -import com.vaadin.util.ReflectTools; -import com.vaadin.v7.data.Validator.InvalidValueException; -import com.vaadin.v7.ui.LegacyAbstractField; -import com.vaadin.v7.ui.LegacyField; - -/** - * FieldGroup provides an easy way of binding fields to data and handling - * commits of these fields. - *

- * The typical use case is to create a layout outside the FieldGroup and then - * use FieldGroup to bind the fields to a data source. - *

- *

- * {@link FieldGroup} is not a UI component so it cannot be added to a layout. - * Using the buildAndBind methods {@link FieldGroup} can create fields for you - * using a FieldGroupFieldFactory but you still have to add them to the correct - * position in your layout. - *

- * - * @author Vaadin Ltd - * @since 7.0 - */ -public class FieldGroup implements Serializable { - - private Item itemDataSource; - private boolean buffered = true; - - private boolean enabled = true; - private boolean readOnly = false; - - private HashMap> propertyIdToField = new HashMap>(); - private LinkedHashMap, Object> fieldToPropertyId = new LinkedHashMap, Object>(); - private List commitHandlers = new ArrayList(); - - /** - * The field factory used by builder methods. - */ - private FieldGroupFieldFactory fieldFactory = DefaultFieldGroupFieldFactory - .get(); - - /** - * Constructs a field binder. Use {@link #setItemDataSource(Item)} to set a - * data source for the field binder. - * - */ - public FieldGroup() { - } - - /** - * Constructs a field binder that uses the given data source. - * - * @param itemDataSource - * The data source to bind the fields to - */ - public FieldGroup(Item itemDataSource) { - setItemDataSource(itemDataSource); - } - - /** - * Updates the item that is used by this FieldBinder. Rebinds all fields to - * the properties in the new item. - * - * @param itemDataSource - * The new item to use - */ - public void setItemDataSource(Item itemDataSource) { - this.itemDataSource = itemDataSource; - - for (LegacyField f : fieldToPropertyId.keySet()) { - bind(f, fieldToPropertyId.get(f)); - } - } - - /** - * Gets the item used by this FieldBinder. Note that you must call - * {@link #commit()} for the item to be updated unless buffered mode has - * been switched off. - * - * @see #setBuffered(boolean) - * @see #commit() - * - * @return The item used by this FieldBinder - */ - public Item getItemDataSource() { - return itemDataSource; - } - - /** - * Checks the buffered mode for the bound fields. - *

- * - * @see #setBuffered(boolean) for more details on buffered mode - * - * @see LegacyField#isBuffered() - * @return true if buffered mode is on, false otherwise - * - */ - public boolean isBuffered() { - return buffered; - } - - /** - * Sets the buffered mode for the bound fields. - *

- * When buffered mode is on the item will not be updated until - * {@link #commit()} is called. If buffered mode is off the item will be - * updated once the fields are updated. - *

- *

- * The default is to use buffered mode. - *

- * - * @see LegacyField#setBuffered(boolean) - * @param buffered - * true to turn on buffered mode, false otherwise - */ - public void setBuffered(boolean buffered) { - if (buffered == this.buffered) { - return; - } - - this.buffered = buffered; - for (LegacyField field : getFields()) { - field.setBuffered(buffered); - } - } - - /** - * Returns the enabled status for the fields. - *

- * Note that this will not accurately represent the enabled status of all - * fields if you change the enabled status of the fields through some other - * method than {@link #setEnabled(boolean)}. - * - * @return true if the fields are enabled, false otherwise - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Updates the enabled state of all bound fields. - * - * @param fieldsEnabled - * true to enable all bound fields, false to disable them - */ - public void setEnabled(boolean fieldsEnabled) { - enabled = fieldsEnabled; - for (LegacyField field : getFields()) { - field.setEnabled(fieldsEnabled); - } - } - - /** - * Returns the read only status that is used by default with all fields that - * have a writable data source. - *

- * Note that this will not accurately represent the read only status of all - * fields if you change the read only status of the fields through some - * other method than {@link #setReadOnly(boolean)}. - * - * @return true if the fields are set to read only, false otherwise - */ - public boolean isReadOnly() { - return readOnly; - } - - /** - * Sets the read only state to the given value for all fields with writable - * data source. Fields with read only data source will always be set to read - * only. - * - * @param fieldsReadOnly - * true to set the fields with writable data source to read only, - * false to set them to read write - */ - public void setReadOnly(boolean fieldsReadOnly) { - readOnly = fieldsReadOnly; - for (LegacyField field : getFields()) { - if (field.getPropertyDataSource() == null - || !field.getPropertyDataSource().isReadOnly()) { - field.setReadOnly(fieldsReadOnly); - } else { - field.setReadOnly(true); - } - } - } - - /** - * Returns a collection of all fields that have been bound. - *

- * The fields are not returned in any specific order. - *

- * - * @return A collection with all bound Fields - */ - public Collection> getFields() { - return fieldToPropertyId.keySet(); - } - - /** - * Binds the field with the given propertyId from the current item. If an - * item has not been set then the binding is postponed until the item is set - * using {@link #setItemDataSource(Item)}. - *

- * This method also adds validators when applicable. - *

- * - * @param field - * The field to bind - * @param propertyId - * The propertyId to bind to the field - * @throws BindException - * If the field is null or the property id is already bound to - * another field by this field binder - */ - public void bind(LegacyField field, Object propertyId) - throws BindException { - throwIfFieldIsNull(field, propertyId); - throwIfPropertyIdAlreadyBound(field, propertyId); - - 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); - } - - // Will be bound when data source is set - return; - } - - field.setPropertyDataSource( - wrapInTransactionalProperty(getItemProperty(propertyId))); - configureField(field); - } - - /** - * Wrap property to transactional property. - */ - protected Property.Transactional wrapInTransactionalProperty( - Property itemProperty) { - return new TransactionalPropertyWrapper(itemProperty); - } - - private void throwIfFieldIsNull(LegacyField field, Object propertyId) { - if (field == null) { - throw new BindException(String.format( - "Cannot bind property id '%s' to a null field.", - propertyId)); - } - } - - private void throwIfPropertyIdAlreadyBound(LegacyField field, - Object propertyId) { - if (propertyIdToField.containsKey(propertyId) - && propertyIdToField.get(propertyId) != field) { - throw new BindException("Property id " + propertyId - + " is already bound to another field"); - } - } - - /** - * Gets the property with the given property id from the item. - * - * @param propertyId - * The id if the property to find - * @return The property with the given id from the item - * @throws BindException - * If the property was not found in the item or no item has been - * set - */ - protected Property getItemProperty(Object propertyId) throws BindException { - Item item = getItemDataSource(); - if (item == null) { - throw new BindException("Could not lookup property with id " - + propertyId + " as no item has been set"); - } - Property p = item.getItemProperty(propertyId); - if (p == null) { - throw new BindException("A property with id " + propertyId - + " was not found in the item"); - } - return p; - } - - /** - * Detaches the field from its property id and removes it from this - * FieldBinder. - *

- * Note that the field is not detached from its property data source if it - * is no longer connected to the same property id it was bound to using this - * FieldBinder. - * - * @param field - * The field to detach - * @throws BindException - * If the field is not bound by this field binder or not bound - * to the correct property id - */ - public void unbind(LegacyField field) throws BindException { - Object propertyId = fieldToPropertyId.get(field); - if (propertyId == null) { - throw new BindException( - "The given field is not part of this FieldBinder"); - } - - TransactionalPropertyWrapper wrapper = null; - Property fieldDataSource = field.getPropertyDataSource(); - if (fieldDataSource instanceof TransactionalPropertyWrapper) { - wrapper = (TransactionalPropertyWrapper) fieldDataSource; - fieldDataSource = ((TransactionalPropertyWrapper) fieldDataSource) - .getWrappedProperty(); - - } - if (getItemDataSource() != null - && fieldDataSource == getItemProperty(propertyId)) { - if (null != wrapper) { - wrapper.detachFromProperty(); - } - field.setPropertyDataSource(null); - } - fieldToPropertyId.remove(field); - propertyIdToField.remove(propertyId); - } - - /** - * Configures a field with the settings set for this FieldBinder. - *

- * By default this updates the buffered, read only and enabled state of the - * field. Also adds validators when applicable. Fields with read only data - * source are always configured as read only. - * - * @param field - * The field to update - */ - protected void configureField(LegacyField field) { - field.setBuffered(isBuffered()); - - field.setEnabled(isEnabled()); - - if (field.getPropertyDataSource().isReadOnly()) { - field.setReadOnly(true); - } else { - field.setReadOnly(isReadOnly()); - } - } - - /** - * Gets the type of the property with the given property id. - * - * @param propertyId - * The propertyId. Must be find - * @return The type of the property - */ - protected Class getPropertyType(Object propertyId) throws BindException { - if (getItemDataSource() == null) { - throw new BindException("Property type for '" + propertyId - + "' could not be determined. No item data source has been set."); - } - Property p = getItemDataSource().getItemProperty(propertyId); - if (p == null) { - throw new BindException("Property type for '" + propertyId - + "' could not be determined. No property with that id was found."); - } - - return p.getType(); - } - - /** - * Returns a collection of all property ids that have been bound to fields. - *

- * Note that this will return property ids even before the item has been - * set. In that case it returns the property ids that will be bound once the - * item is set. - *

- *

- * No guarantee is given for the order of the property ids - *

- * - * @return A collection of bound property ids - */ - public Collection getBoundPropertyIds() { - return Collections.unmodifiableCollection(propertyIdToField.keySet()); - } - - /** - * Returns a collection of all property ids that exist in the item set using - * {@link #setItemDataSource(Item)} but have not been bound to fields. - *

- * Will always return an empty collection before an item has been set using - * {@link #setItemDataSource(Item)}. - *

- *

- * No guarantee is given for the order of the property ids - *

- * - * @return A collection of property ids that have not been bound to fields - */ - public Collection getUnboundPropertyIds() { - if (getItemDataSource() == null) { - return new ArrayList(); - } - List unboundPropertyIds = new ArrayList(); - unboundPropertyIds.addAll(getItemDataSource().getItemPropertyIds()); - unboundPropertyIds.removeAll(propertyIdToField.keySet()); - return unboundPropertyIds; - } - - /** - * Commits all changes done to the bound fields. - *

- * Calls all {@link CommitHandler}s before and after committing the field - * changes to the item data source. The whole commit is aborted and state is - * restored to what it was before commit was called if any - * {@link CommitHandler} throws a CommitException or there is a problem - * committing the fields - * - * @throws CommitException - * If the commit was aborted - */ - public void commit() throws CommitException { - if (!isBuffered()) { - // Not using buffered mode, nothing to do - return; - } - - startTransactions(); - - try { - firePreCommitEvent(); - - Map, InvalidValueException> invalidValueExceptions = commitFields(); - - if (invalidValueExceptions.isEmpty()) { - firePostCommitEvent(); - commitTransactions(); - } else { - throw new FieldGroupInvalidValueException( - invalidValueExceptions); - } - } catch (Exception e) { - rollbackTransactions(); - throw new CommitException("Commit failed", this, e); - } - - } - - /** - * Tries to commit all bound fields one by one and gathers any validation - * exceptions in a map, which is returned to the caller - * - * @return a propertyId to validation exception map which is empty if all - * commits succeeded - */ - private Map, InvalidValueException> commitFields() { - Map, InvalidValueException> invalidValueExceptions = new HashMap, InvalidValueException>(); - - for (LegacyField f : fieldToPropertyId.keySet()) { - try { - f.commit(); - } catch (InvalidValueException e) { - invalidValueExceptions.put(f, e); - } - } - - return invalidValueExceptions; - } - - /** - * Exception which wraps InvalidValueExceptions from all invalid fields in a - * FieldGroup - * - * @since 7.4 - */ - public static class FieldGroupInvalidValueException - extends InvalidValueException { - private Map, InvalidValueException> invalidValueExceptions; - - /** - * Constructs a new exception with the specified validation exceptions. - * - * @param invalidValueExceptions - * a property id to exception map - */ - public FieldGroupInvalidValueException( - Map, InvalidValueException> invalidValueExceptions) { - super(null, invalidValueExceptions.values().toArray( - new InvalidValueException[invalidValueExceptions.size()])); - this.invalidValueExceptions = invalidValueExceptions; - } - - /** - * Returns a map containing fields which failed validation and the - * exceptions the corresponding validators threw. - * - * @return a map with all the invalid value exceptions - */ - public Map, InvalidValueException> getInvalidFields() { - return invalidValueExceptions; - } - } - - private void startTransactions() throws CommitException { - for (LegacyField f : fieldToPropertyId.keySet()) { - Property.Transactional property = (Property.Transactional) f - .getPropertyDataSource(); - if (property == null) { - throw new CommitException( - "Property \"" + fieldToPropertyId.get(f) - + "\" not bound to datasource."); - } - property.startTransaction(); - } - } - - private void commitTransactions() { - for (LegacyField f : fieldToPropertyId.keySet()) { - ((Property.Transactional) f.getPropertyDataSource()).commit(); - } - } - - private void rollbackTransactions() { - for (LegacyField f : fieldToPropertyId.keySet()) { - try { - ((Property.Transactional) f.getPropertyDataSource()) - .rollback(); - } catch (Exception rollbackException) { - // FIXME: What to do ? - } - } - } - - /** - * Sends a preCommit event to all registered commit handlers - * - * @throws CommitException - * If the commit should be aborted - */ - private void firePreCommitEvent() throws CommitException { - CommitHandler[] handlers = commitHandlers - .toArray(new CommitHandler[commitHandlers.size()]); - - for (CommitHandler handler : handlers) { - handler.preCommit(new CommitEvent(this)); - } - } - - /** - * Sends a postCommit event to all registered commit handlers - * - * @throws CommitException - * If the commit should be aborted - */ - private void firePostCommitEvent() throws CommitException { - CommitHandler[] handlers = commitHandlers - .toArray(new CommitHandler[commitHandlers.size()]); - - for (CommitHandler handler : handlers) { - handler.postCommit(new CommitEvent(this)); - } - } - - /** - * Discards all changes done to the bound fields. - *

- * Only has effect if buffered mode is used. - * - */ - public void discard() { - for (LegacyField f : fieldToPropertyId.keySet()) { - try { - f.discard(); - } catch (Exception e) { - // TODO: handle exception - // What can we do if discard fails other than try to discard all - // other fields? - } - } - } - - /** - * Returns the field that is bound to the given property id - * - * @param propertyId - * The property id to use to lookup the field - * @return The field that is bound to the property id or null if no field is - * bound to that property id - */ - public LegacyField getField(Object propertyId) { - return propertyIdToField.get(propertyId); - } - - /** - * Returns the property id that is bound to the given field - * - * @param field - * The field to use to lookup the property id - * @return The property id that is bound to the field or null if the field - * is not bound to any property id by this FieldBinder - */ - public Object getPropertyId(LegacyField field) { - return fieldToPropertyId.get(field); - } - - /** - * Adds a commit handler. - *

- * The commit handler is called before the field values are committed to the - * item ( {@link CommitHandler#preCommit(CommitEvent)}) and after the item - * has been updated ({@link CommitHandler#postCommit(CommitEvent)}). If a - * {@link CommitHandler} throws a CommitException the whole commit is - * aborted and the fields retain their old values. - * - * @param commitHandler - * The commit handler to add - */ - public void addCommitHandler(CommitHandler commitHandler) { - commitHandlers.add(commitHandler); - } - - /** - * Removes the given commit handler. - * - * @see #addCommitHandler(CommitHandler) - * - * @param commitHandler - * The commit handler to remove - */ - public void removeCommitHandler(CommitHandler commitHandler) { - commitHandlers.remove(commitHandler); - } - - /** - * Returns a list of all commit handlers for this {@link FieldGroup}. - *

- * Use {@link #addCommitHandler(CommitHandler)} and - * {@link #removeCommitHandler(CommitHandler)} to register or unregister a - * commit handler. - * - * @return A collection of commit handlers - */ - protected Collection getCommitHandlers() { - return Collections.unmodifiableCollection(commitHandlers); - } - - /** - * CommitHandlers are used by {@link FieldGroup#commit()} as part of the - * commit transactions. CommitHandlers can perform custom operations as part - * of the commit and cause the commit to be aborted by throwing a - * {@link CommitException}. - */ - public interface CommitHandler extends Serializable { - /** - * Called before changes are committed to the field and the item is - * updated. - *

- * Throw a {@link CommitException} to abort the commit. - * - * @param commitEvent - * An event containing information regarding the commit - * @throws CommitException - * if the commit should be aborted - */ - public void preCommit(CommitEvent commitEvent) throws CommitException; - - /** - * Called after changes are committed to the fields and the item is - * updated. - *

- * Throw a {@link CommitException} to abort the commit. - * - * @param commitEvent - * An event containing information regarding the commit - * @throws CommitException - * if the commit should be aborted - */ - public void postCommit(CommitEvent commitEvent) throws CommitException; - } - - /** - * FIXME javadoc - * - */ - public static class CommitEvent implements Serializable { - private FieldGroup fieldBinder; - - private CommitEvent(FieldGroup fieldBinder) { - this.fieldBinder = fieldBinder; - } - - /** - * Returns the field binder that this commit relates to - * - * @return The FieldBinder that is being committed. - */ - public FieldGroup getFieldBinder() { - return fieldBinder; - } - - } - - /** - * Checks the validity of the bound fields. - *

- * Call the {@link LegacyField#validate()} for the fields to get the - * individual error messages. - * - * @return true if all bound fields are valid, false otherwise. - */ - public boolean isValid() { - try { - for (LegacyField field : getFields()) { - field.validate(); - } - return true; - } catch (InvalidValueException e) { - return false; - } - } - - /** - * Checks if any bound field has been modified. - * - * @return true if at least one field has been modified, false otherwise - */ - public boolean isModified() { - for (LegacyField field : getFields()) { - if (field.isModified()) { - return true; - } - } - return false; - } - - /** - * Gets the field factory for the {@link FieldGroup}. The field factory is - * only used when {@link FieldGroup} creates a new field. - * - * @return The field factory in use - * - */ - public FieldGroupFieldFactory getFieldFactory() { - return fieldFactory; - } - - /** - * Sets the field factory for the {@link FieldGroup}. The field factory is - * only used when {@link FieldGroup} creates a new field. - * - * @param fieldFactory - * The field factory to use - */ - public void setFieldFactory(FieldGroupFieldFactory fieldFactory) { - this.fieldFactory = fieldFactory; - } - - /** - * Binds member fields found in the given object. - *

- * This method processes all (Java) member fields whose type extends - * {@link LegacyField} and that can be mapped to a property id. Property id - * mapping is done based on the field name or on a @{@link PropertyId} - * annotation on the field. All non-null fields for which a property id can - * be determined are bound to the property id. - *

- *

- * For example: - * - *

-     * public class MyForm extends VerticalLayout {
-     * private TextField firstName = new TextField("First name");
-     * @PropertyId("last")
-     * private TextField lastName = new TextField("Last name");
-     * private TextField age = new TextField("Age"); ... }
-     *
-     * MyForm myForm = new MyForm();
-     * ...
-     * fieldGroup.bindMemberFields(myForm);
-     * 
- * - *

- * This binds the firstName TextField to a "firstName" property in the item, - * lastName TextField to a "last" property and the age TextField to a "age" - * property. - * - * @param objectWithMemberFields - * The object that contains (Java) member fields to bind - * @throws BindException - * If there is a problem binding a field - */ - public void bindMemberFields(Object objectWithMemberFields) - throws BindException { - buildAndBindMemberFields(objectWithMemberFields, false); - } - - /** - * Binds member fields found in the given object and builds member fields - * that have not been initialized. - *

- * This method processes all (Java) member fields whose type extends - * {@link LegacyField} and that can be mapped to a property id. Property ids - * are searched in the following order: @{@link PropertyId} annotations, - * exact field name matches and the case-insensitive matching that ignores - * underscores. Fields that are not initialized (null) are built using the - * field factory. All non-null fields for which a property id can be - * determined are bound to the property id. - *

- *

- * For example: - * - *

-     * public class MyForm extends VerticalLayout {
-     * private TextField firstName = new TextField("First name");
-     * @PropertyId("last")
-     * private TextField lastName = new TextField("Last name");
-     * private TextField age;
-     *
-     * MyForm myForm = new MyForm();
-     * ...
-     * fieldGroup.buildAndBindMemberFields(myForm);
-     * 
- * - *

- *

- * This binds the firstName TextField to a "firstName" property in the item, - * lastName TextField to a "last" property and builds an age TextField using - * the field factory and then binds it to the "age" property. - *

- * - * @param objectWithMemberFields - * The object that contains (Java) member fields to build and - * bind - * @throws BindException - * If there is a problem binding or building a field - */ - public void buildAndBindMemberFields(Object objectWithMemberFields) - throws BindException { - buildAndBindMemberFields(objectWithMemberFields, true); - } - - /** - * Binds member fields found in the given object and optionally builds - * member fields that have not been initialized. - *

- * This method processes all (Java) member fields whose type extends - * {@link LegacyField} and that can be mapped to a property id. Property ids - * are searched in the following order: @{@link PropertyId} annotations, - * exact field name matches and the case-insensitive matching that ignores - * underscores. Fields that are not initialized (null) are built using the - * field factory is buildFields is true. All non-null fields for which a - * property id can be determined are bound to the property id. - *

- * - * @param objectWithMemberFields - * The object that contains (Java) member fields to build and - * bind - * @throws BindException - * If there is a problem binding or building a field - */ - protected void buildAndBindMemberFields(Object objectWithMemberFields, - boolean buildFields) throws BindException { - Class objectClass = objectWithMemberFields.getClass(); - - for (java.lang.reflect.Field memberField : getFieldsInDeclareOrder( - objectClass)) { - - if (!LegacyField.class.isAssignableFrom(memberField.getType())) { - // Process next field - continue; - } - - PropertyId propertyIdAnnotation = memberField - .getAnnotation(PropertyId.class); - - Class fieldType = (Class) memberField - .getType(); - - Object propertyId = null; - if (propertyIdAnnotation != null) { - // @PropertyId(propertyId) always overrides property id - propertyId = propertyIdAnnotation.value(); - } else { - try { - propertyId = findPropertyId(memberField); - } catch (SearchException e) { - // Property id was not found, skip this field - continue; - } - if (propertyId == null) { - // Property id was not found, skip this field - continue; - } - } - - // Ensure that the property id exists - Class propertyType; - - try { - propertyType = getPropertyType(propertyId); - } catch (BindException e) { - // Property id was not found, skip this field - continue; - } - - LegacyField field; - try { - // Get the field from the object - field = (LegacyField) ReflectTools.getJavaFieldValue( - objectWithMemberFields, memberField, LegacyField.class); - } catch (Exception e) { - // If we cannot determine the value, just skip the field and try - // the next one - continue; - } - - if (field == null && buildFields) { - Caption captionAnnotation = memberField - .getAnnotation(Caption.class); - String caption; - if (captionAnnotation != null) { - caption = captionAnnotation.value(); - } else { - caption = DefaultFieldFactory - .createCaptionByPropertyId(propertyId); - } - - // Create the component (LegacyField) - field = build(caption, propertyType, fieldType); - - // Store it in the field - try { - ReflectTools.setJavaFieldValue(objectWithMemberFields, - memberField, field); - } catch (IllegalArgumentException e) { - throw new BindException("Could not assign value to field '" - + memberField.getName() + "'", e); - } catch (IllegalAccessException e) { - throw new BindException("Could not assign value to field '" - + memberField.getName() + "'", e); - } catch (InvocationTargetException e) { - throw new BindException("Could not assign value to field '" - + memberField.getName() + "'", e); - } - } - - if (field != null) { - // Bind it to the property id - bind(field, propertyId); - } - } - } - - /** - * Searches for a property id from the current itemDataSource that matches - * the given memberField. - *

- * If perfect match is not found, uses a case insensitive search that also - * ignores underscores. Returns null if no match is found. Throws a - * SearchException if no item data source has been set. - *

- *

- * The propertyId search logic used by - * {@link #buildAndBindMemberFields(Object, boolean) - * buildAndBindMemberFields} can easily be customized by overriding this - * method. No other changes are needed. - *

- * - * @param memberField - * The field an object id is searched for - * @return - */ - protected Object findPropertyId(java.lang.reflect.Field memberField) { - String fieldName = memberField.getName(); - if (getItemDataSource() == null) { - throw new SearchException("Property id type for field '" + fieldName - + "' could not be determined. No item data source has been set."); - } - Item dataSource = getItemDataSource(); - if (dataSource.getItemProperty(fieldName) != null) { - return fieldName; - } else { - String minifiedFieldName = minifyFieldName(fieldName); - for (Object itemPropertyId : dataSource.getItemPropertyIds()) { - if (itemPropertyId instanceof String) { - String itemPropertyName = (String) itemPropertyId; - if (minifiedFieldName - .equals(minifyFieldName(itemPropertyName))) { - return itemPropertyName; - } - } - } - } - return null; - } - - protected static String minifyFieldName(String fieldName) { - return fieldName.toLowerCase().replace("_", ""); - } - - /** - * Exception thrown by a FieldGroup when the commit operation fails. - * - * Provides information about validation errors through - * {@link #getInvalidFields()} if the cause of the failure is that all bound - * fields did not pass validation - * - */ - public static class CommitException extends Exception { - - private FieldGroup fieldGroup; - - public CommitException() { - super(); - } - - public CommitException(String message, FieldGroup fieldGroup, - Throwable cause) { - super(message, cause); - this.fieldGroup = fieldGroup; - } - - public CommitException(String message, Throwable cause) { - super(message, cause); - } - - public CommitException(String message) { - super(message); - } - - public CommitException(Throwable cause) { - super(cause); - } - - /** - * Returns a map containing the fields which failed validation and the - * exceptions the corresponding validators threw. - * - * @since 7.4 - * @return a map with all the invalid value exceptions. Can be empty but - * not null - */ - public Map, InvalidValueException> getInvalidFields() { - if (getCause() instanceof FieldGroupInvalidValueException) { - return ((FieldGroupInvalidValueException) getCause()) - .getInvalidFields(); - } - return new HashMap, InvalidValueException>(); - } - - /** - * Returns the field group where the exception occurred - * - * @since 7.4 - * @return the field group - */ - public FieldGroup getFieldGroup() { - return fieldGroup; - } - - } - - public static class BindException extends RuntimeException { - - public BindException(String message) { - super(message); - } - - public BindException(String message, Throwable t) { - super(message, t); - } - - } - - public static class SearchException extends RuntimeException { - - public SearchException(String message) { - super(message); - } - - public SearchException(String message, Throwable t) { - super(message, t); - } - - } - - /** - * Builds a field and binds it to the given property id using the field - * binder. - * - * @param propertyId - * The property id to bind to. Must be present in the field - * finder. - * @throws BindException - * If there is a problem while building or binding - * @return The created and bound field - */ - public LegacyField buildAndBind(Object propertyId) throws BindException { - String caption = DefaultFieldFactory - .createCaptionByPropertyId(propertyId); - return buildAndBind(caption, propertyId); - } - - /** - * Builds a field using the given caption and binds it to the given property - * id using the field binder. - * - * @param caption - * The caption for the field - * @param propertyId - * The property id to bind to. Must be present in the field - * finder. - * @throws BindException - * If there is a problem while building or binding - * @return The created and bound field. Can be any type of - * {@link LegacyField}. - */ - public LegacyField buildAndBind(String caption, Object propertyId) - throws BindException { - return buildAndBind(caption, propertyId, LegacyField.class); - } - - /** - * Builds a field using the given caption and binds it to the given property - * id using the field binder. Ensures the new field is of the given type. - * - * @param caption - * The caption for the field - * @param propertyId - * The property id to bind to. Must be present in the field - * finder. - * @throws BindException - * If the field could not be created - * @return The created and bound field. Can be any type of - * {@link LegacyField}. - */ - - public T buildAndBind(String caption, - Object propertyId, Class fieldType) throws BindException { - Class type = getPropertyType(propertyId); - - T field = build(caption, type, fieldType); - bind(field, propertyId); - - return field; - } - - /** - * Creates a field based on the given data type. - *

- * The data type is the type that we want to edit using the field. The field - * type is the type of field we want to create, can be {@link LegacyField} - * if any LegacyField is good. - *

- * - * @param caption - * The caption for the new field - * @param dataType - * The data model type that we want to edit using the field - * @param fieldType - * The type of field that we want to create - * @return A LegacyField capable of editing the given type - * @throws BindException - * If the field could not be created - */ - protected T build(String caption, Class dataType, - Class fieldType) throws BindException { - T field = getFieldFactory().createField(dataType, fieldType); - if (field == null) { - throw new BindException( - "Unable to build a field of type " + fieldType.getName() - + " for editing " + dataType.getName()); - } - - field.setCaption(caption); - return field; - } - - /** - * Returns an array containing LegacyField objects reflecting all the fields - * of the class or interface represented by this Class object. The elements - * in the array returned are sorted in declare order from sub class to super - * class. - * - * @param searchClass - * @return - */ - protected static List getFieldsInDeclareOrder( - Class searchClass) { - ArrayList memberFieldInOrder = new ArrayList(); - - while (searchClass != null) { - for (java.lang.reflect.Field memberField : searchClass - .getDeclaredFields()) { - memberFieldInOrder.add(memberField); - } - searchClass = searchClass.getSuperclass(); - } - return memberFieldInOrder; - } - - /** - * Clears the value of all fields. - * - * @since 7.4 - */ - public void clear() { - for (LegacyField f : getFields()) { - if (f instanceof LegacyAbstractField) { - ((LegacyAbstractField) f).clear(); - } - } - - } - -} \ No newline at end of file diff --git a/server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java b/server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java deleted file mode 100644 index a80d51c6df..0000000000 --- a/server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.data.fieldgroup; - -import java.io.Serializable; - -import com.vaadin.v7.ui.LegacyField; - -/** - * Factory interface for creating new LegacyField-instances based on the data - * type that should be edited. - * - * @author Vaadin Ltd. - * @since 7.0 - */ -public interface FieldGroupFieldFactory extends Serializable { - /** - * Creates a field based on the data type that we want to edit - * - * @param dataType - * The type that we want to edit using the field - * @param fieldType - * The type of field we want to create. If set to - * {@link LegacyField} then any type of field is accepted - * @return A field that can be assigned to the given fieldType and that is - * capable of editing the given type of data - */ - T createField(Class dataType, - Class fieldType); -} diff --git a/server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java b/server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java deleted file mode 100644 index a2a8aa27af..0000000000 --- a/server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.data.fieldgroup; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Defines the custom property name to be bound to a {@link LegacyField} using - * {@link FieldGroup} or {@link BeanFieldGroup}. - *

- * The automatic data binding in FieldGroup and BeanFieldGroup relies on a - * naming convention by default: properties of an item are bound to similarly - * named field components in given a editor object. If you want to map a - * property with a different name (ID) to a - * {@link com.vaadin.client.ui.LegacyField}, you can use this annotation for the - * member fields, with the name (ID) of the desired property as the parameter. - *

- * In following usage example, the text field would be bound to property "foo" - * in the Entity class. - *

- *    class Editor extends FormLayout {
-        @PropertyId("foo")
-        TextField myField = new TextField();
-    }
-
-    class Entity {
-        String foo;
-    }
-
-    {
-        Editor e = new Editor();
-        BeanFieldGroup.bindFieldsUnbuffered(new Entity(), e);
-    }
-   
- * - * - * @since 7.0 - * @author Vaadin Ltd - */ -@Target({ ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface PropertyId { - String value(); -} diff --git a/server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java b/server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java deleted file mode 100644 index f7459a7dd3..0000000000 --- a/server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.server.communication.data; - -import java.io.Serializable; - -import com.vaadin.data.Item; -import com.vaadin.ui.LegacyGrid.AbstractGridExtension; - -import elemental.json.JsonObject; - -/** - * Interface for {@link AbstractGridExtension}s that allows adding data to row - * objects being sent to client by the {@link RpcDataProviderExtension}. - *

- * This class also provides a way to remove any unneeded data once the data - * object is no longer used on the client-side. - * - * @since 7.6 - * @author Vaadin Ltd - */ -public interface DataGenerator extends Serializable { - - /** - * Adds data to row object for given item and item id being sent to client. - * - * @param itemId - * item id of item - * @param item - * item being sent to client - * @param rowData - * row object being sent to client - */ - public void generateData(Object itemId, Item item, JsonObject rowData); - - /** - * Informs the DataGenerator that an item id has been dropped and is no - * longer needed. This method should clean up any unneeded stored data - * related to the item. - * - * @param itemId - * removed item id - */ - public void destroyData(Object itemId); -} diff --git a/server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java b/server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java deleted file mode 100644 index 341c3f1a45..0000000000 --- a/server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java +++ /dev/null @@ -1,632 +0,0 @@ -/* - * 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.server.communication.data; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Container.ItemSetChangeNotifier; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.data.Property.ValueChangeNotifier; -import com.vaadin.server.AbstractExtension; -import com.vaadin.server.ClientConnector; -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.data.DataProviderRpc; -import com.vaadin.shared.data.DataRequestRpc; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.Range; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.Column; - -import elemental.json.Json; -import elemental.json.JsonArray; -import elemental.json.JsonObject; - -/** - * Provides Vaadin server-side container data source to a - * {@link com.vaadin.client.ui.grid.GridConnector}. This is currently - * implemented as an Extension hardcoded to support a specific connector type. - * This will be changed once framework support for something more flexible has - * been implemented. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class RpcDataProviderExtension extends AbstractExtension { - - /** - * Class for keeping track of current items and ValueChangeListeners. - * - * @since 7.6 - */ - private class ActiveItemHandler implements Serializable, DataGenerator { - - private final Map activeItemMap = new HashMap(); - private final KeyMapper keyMapper = new KeyMapper(); - private final Set droppedItems = new HashSet(); - - /** - * Registers ValueChangeListeners for given item ids. - *

- * Note: This method will clean up any unneeded listeners and key - * mappings - * - * @param itemIds - * collection of new active item ids - */ - public void addActiveItems(Collection itemIds) { - for (Object itemId : itemIds) { - if (!activeItemMap.containsKey(itemId)) { - activeItemMap.put(itemId, new GridValueChangeListener( - itemId, container.getItem(itemId))); - } - } - - // Remove still active rows that were "dropped" - droppedItems.removeAll(itemIds); - internalDropItems(droppedItems); - droppedItems.clear(); - } - - /** - * Marks given item id as dropped. Dropped items are cleared when adding - * new active items. - * - * @param itemId - * dropped item id - */ - public void dropActiveItem(Object itemId) { - if (activeItemMap.containsKey(itemId)) { - droppedItems.add(itemId); - } - } - - /** - * Gets a collection copy of currently active item ids. - * - * @return collection of item ids - */ - public Collection getActiveItemIds() { - return new HashSet(activeItemMap.keySet()); - } - - /** - * Gets a collection copy of currently active ValueChangeListeners. - * - * @return collection of value change listeners - */ - public Collection getValueChangeListeners() { - return new HashSet(activeItemMap.values()); - } - - @Override - public void generateData(Object itemId, Item item, JsonObject rowData) { - rowData.put(GridState.JSONKEY_ROWKEY, keyMapper.key(itemId)); - } - - @Override - public void destroyData(Object itemId) { - keyMapper.remove(itemId); - removeListener(itemId); - } - - private void removeListener(Object itemId) { - GridValueChangeListener removed = activeItemMap.remove(itemId); - - if (removed != null) { - removed.removeListener(); - } - } - } - - /** - * A class to listen to changes in property values in the Container added - * with {@link LegacyGrid#setContainerDatasource(Container.Indexed)}, and notifies - * the data source to update the client-side representation of the modified - * item. - *

- * One instance of this class can (and should) be reused for all the - * properties in an item, since this class will inform that the entire row - * needs to be re-evaluated (in contrast to a property-based change - * management) - *

- * Since there's no Container-wide possibility to listen to any kind of - * value changes, an instance of this class needs to be attached to each and - * every Item's Property in the container. - * - * @see LegacyGrid#addValueChangeListener(Container, Object, Object) - * @see LegacyGrid#valueChangeListeners - */ - private class GridValueChangeListener implements ValueChangeListener { - private final Object itemId; - private final Item item; - - public GridValueChangeListener(Object itemId, Item item) { - /* - * Using an assert instead of an exception throw, just to optimize - * prematurely - */ - assert itemId != null : "null itemId not accepted"; - this.itemId = itemId; - this.item = item; - - internalAddColumns(getGrid().getColumns()); - } - - @Override - public void valueChange(ValueChangeEvent event) { - updateRowData(itemId); - } - - public void removeListener() { - removeColumns(getGrid().getColumns()); - } - - public void addColumns(Collection addedColumns) { - internalAddColumns(addedColumns); - updateRowData(itemId); - } - - private void internalAddColumns(Collection addedColumns) { - for (final Column column : addedColumns) { - final Property property = item - .getItemProperty(column.getPropertyId()); - if (property instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) property) - .addValueChangeListener(this); - } - } - } - - public void removeColumns(Collection removedColumns) { - for (final Column column : removedColumns) { - final Property property = item - .getItemProperty(column.getPropertyId()); - if (property instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) property) - .removeValueChangeListener(this); - } - } - } - } - - private final Indexed container; - - private DataProviderRpc rpc; - - private final ItemSetChangeListener itemListener = new ItemSetChangeListener() { - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - - if (event instanceof ItemAddEvent) { - ItemAddEvent addEvent = (ItemAddEvent) event; - int firstIndex = addEvent.getFirstIndex(); - int count = addEvent.getAddedItemsCount(); - insertRowData(firstIndex, count); - } - - else if (event instanceof ItemRemoveEvent) { - ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; - int firstIndex = removeEvent.getFirstIndex(); - int count = removeEvent.getRemovedItemsCount(); - removeRowData(firstIndex, count); - } - - else { - // Remove obsolete value change listeners. - Set keySet = new HashSet( - activeItemHandler.activeItemMap.keySet()); - for (Object itemId : keySet) { - activeItemHandler.removeListener(itemId); - } - - /* Mark as dirty to push changes in beforeClientResponse */ - bareItemSetTriggeredSizeChange = true; - markAsDirty(); - } - } - }; - - /** RpcDataProvider should send the current cache again. */ - private boolean refreshCache = false; - - /** Set of updated item ids */ - private transient Set updatedItemIds; - - /** - * Queued RPC calls for adding and removing rows. Queue will be handled in - * {@link beforeClientResponse} - */ - private transient List rowChanges; - - /** Size possibly changed with a bare ItemSetChangeEvent */ - private boolean bareItemSetTriggeredSizeChange = false; - - private final Set dataGenerators = new LinkedHashSet(); - - private final ActiveItemHandler activeItemHandler = new ActiveItemHandler(); - - /** - * Creates a new data provider using the given container. - * - * @param container - * the container to make available - */ - public RpcDataProviderExtension(Indexed container) { - this.container = container; - rpc = getRpcProxy(DataProviderRpc.class); - - registerRpc(new DataRequestRpc() { - @Override - public void requestRows(int firstRow, int numberOfRows, - int firstCachedRowIndex, int cacheSize) { - pushRowData(firstRow, numberOfRows, firstCachedRowIndex, - cacheSize); - } - - @Override - public void dropRows(JsonArray rowKeys) { - for (int i = 0; i < rowKeys.length(); ++i) { - activeItemHandler.dropActiveItem( - getKeyMapper().get(rowKeys.getString(i))); - } - } - }); - - if (container instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) container) - .addItemSetChangeListener(itemListener); - } - - addDataGenerator(activeItemHandler); - } - - /** - * {@inheritDoc} - *

- * RpcDataProviderExtension makes all actual RPC calls from this function - * based on changes in the container. - */ - @Override - public void beforeClientResponse(boolean initial) { - if (initial || bareItemSetTriggeredSizeChange) { - /* - * Push initial set of rows, assuming Grid will initially be - * rendered scrolled to the top and with a decent amount of rows - * visible. If this guess is right, initial data can be shown - * without a round-trip and if it's wrong, the data will simply be - * discarded. - */ - int size = container.size(); - rpc.resetDataAndSize(size); - - int numberOfRows = Math.min(40, size); - pushRowData(0, numberOfRows, 0, 0); - } else { - // Only do row changes if not initial response. - if (rowChanges != null) { - for (Runnable r : rowChanges) { - r.run(); - } - } - - // Send current rows again if needed. - if (refreshCache) { - for (Object itemId : activeItemHandler.getActiveItemIds()) { - updateRowData(itemId); - } - } - } - - internalUpdateRows(updatedItemIds); - - // Clear all changes. - if (rowChanges != null) { - rowChanges.clear(); - } - if (updatedItemIds != null) { - updatedItemIds.clear(); - } - refreshCache = false; - bareItemSetTriggeredSizeChange = false; - - super.beforeClientResponse(initial); - } - - private void pushRowData(int firstRowToPush, int numberOfRows, - int firstCachedRowIndex, int cacheSize) { - Range newRange = Range.withLength(firstRowToPush, numberOfRows); - Range cached = Range.withLength(firstCachedRowIndex, cacheSize); - Range fullRange = newRange; - if (!cached.isEmpty()) { - fullRange = newRange.combineWith(cached); - } - - List itemIds = container.getItemIds(fullRange.getStart(), - fullRange.length()); - - JsonArray rows = Json.createArray(); - - // Offset the index to match the wanted range. - int diff = 0; - if (!cached.isEmpty() && newRange.getStart() > cached.getStart()) { - diff = cached.length(); - } - - for (int i = 0; i < newRange.length() - && i + diff < itemIds.size(); ++i) { - Object itemId = itemIds.get(i + diff); - - Item item = container.getItem(itemId); - - rows.set(i, getRowData(getGrid().getColumns(), itemId, item)); - } - rpc.setRowData(firstRowToPush, rows); - - activeItemHandler.addActiveItems(itemIds); - } - - private JsonObject getRowData(Collection columns, Object itemId, - Item item) { - - final JsonObject rowObject = Json.createObject(); - for (DataGenerator dg : dataGenerators) { - dg.generateData(itemId, item, rowObject); - } - - return rowObject; - } - - /** - * Makes the data source available to the given {@link LegacyGrid} component. - * - * @param component - * the remote data grid component to extend - * @param columnKeys - * the key mapper for columns - */ - public void extend(LegacyGrid component) { - super.extend(component); - } - - /** - * Adds a {@link DataGenerator} for this {@code RpcDataProviderExtension}. - * DataGenerators are called when sending row data to client. If given - * DataGenerator is already added, this method does nothing. - * - * @since 7.6 - * @param generator - * generator to add - */ - public void addDataGenerator(DataGenerator generator) { - dataGenerators.add(generator); - } - - /** - * Removes a {@link DataGenerator} from this - * {@code RpcDataProviderExtension}. If given DataGenerator is not added to - * this data provider, this method does nothing. - * - * @since 7.6 - * @param generator - * generator to remove - */ - public void removeDataGenerator(DataGenerator generator) { - dataGenerators.remove(generator); - } - - /** - * Informs the client side that new rows have been inserted into the data - * source. - * - * @param index - * the index at which new rows have been inserted - * @param count - * the number of rows inserted at index - */ - private void insertRowData(final int index, final int count) { - if (rowChanges == null) { - rowChanges = new ArrayList(); - } - - if (rowChanges.isEmpty()) { - markAsDirty(); - } - - /* - * Since all changes should be processed in a consistent order, we don't - * send the RPC call immediately. beforeClientResponse will decide - * whether to send these or not. Valid situation to not send these is - * initial response or bare ItemSetChange event. - */ - rowChanges.add(new Runnable() { - @Override - public void run() { - rpc.insertRowData(index, count); - } - }); - } - - /** - * Informs the client side that rows have been removed from the data source. - * - * @param index - * the index of the first row removed - * @param count - * the number of rows removed - * @param firstItemId - * the item id of the first removed item - */ - private void removeRowData(final int index, final int count) { - if (rowChanges == null) { - rowChanges = new ArrayList(); - } - - if (rowChanges.isEmpty()) { - markAsDirty(); - } - - /* See comment in insertRowData */ - rowChanges.add(new Runnable() { - @Override - public void run() { - rpc.removeRowData(index, count); - } - }); - } - - /** - * Informs the client side that data of a row has been modified in the data - * source. - * - * @param itemId - * the item Id the row that was updated - */ - public void updateRowData(Object itemId) { - if (updatedItemIds == null) { - updatedItemIds = new LinkedHashSet(); - } - - if (updatedItemIds.isEmpty()) { - // At least one new item will be updated. Mark as dirty to actually - // update before response to client. - markAsDirty(); - } - - updatedItemIds.add(itemId); - } - - private void internalUpdateRows(Set itemIds) { - if (itemIds == null || itemIds.isEmpty()) { - return; - } - - Collection activeItemIds = activeItemHandler.getActiveItemIds(); - List columns = getGrid().getColumns(); - JsonArray rowData = Json.createArray(); - int i = 0; - for (Object itemId : itemIds) { - if (activeItemIds.contains(itemId)) { - Item item = container.getItem(itemId); - if (item != null) { - JsonObject row = getRowData(columns, itemId, item); - rowData.set(i++, row); - } - } - } - rpc.updateRowData(rowData); - } - - /** - * Pushes a new version of all the rows in the active cache range. - */ - public void refreshCache() { - if (!refreshCache) { - refreshCache = true; - markAsDirty(); - } - } - - @Override - public void setParent(ClientConnector parent) { - if (parent == null) { - // We're being detached, release various listeners - internalDropItems(activeItemHandler.getActiveItemIds()); - - if (container instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) container) - .removeItemSetChangeListener(itemListener); - } - - } else if (!(parent instanceof LegacyGrid)) { - throw new IllegalStateException( - "Grid is the only accepted parent type"); - } - super.setParent(parent); - } - - /** - * Informs all DataGenerators than an item id has been dropped. - * - * @param droppedItemIds - * collection of dropped item ids - */ - private void internalDropItems(Collection droppedItemIds) { - for (Object itemId : droppedItemIds) { - for (DataGenerator generator : dataGenerators) { - generator.destroyData(itemId); - } - } - } - - /** - * Informs this data provider that given columns have been removed from - * grid. - * - * @param removedColumns - * a list of removed columns - */ - public void columnsRemoved(List removedColumns) { - for (GridValueChangeListener l : activeItemHandler - .getValueChangeListeners()) { - l.removeColumns(removedColumns); - } - - // No need to resend unchanged data. Client will remember the old - // columns until next set of rows is sent. - } - - /** - * Informs this data provider that given columns have been added to grid. - * - * @param addedColumns - * a list of added columns - */ - public void columnsAdded(List addedColumns) { - for (GridValueChangeListener l : activeItemHandler - .getValueChangeListeners()) { - l.addColumns(addedColumns); - } - - // Resend all rows to contain new data. - refreshCache(); - } - - public KeyMapper getKeyMapper() { - return activeItemHandler.keyMapper; - } - - protected LegacyGrid getGrid() { - return (LegacyGrid) getParent(); - } -} diff --git a/server/src/main/java/com/vaadin/ui/LegacyGrid.java b/server/src/main/java/com/vaadin/ui/LegacyGrid.java deleted file mode 100644 index 9a98593b83..0000000000 --- a/server/src/main/java/com/vaadin/ui/LegacyGrid.java +++ /dev/null @@ -1,7352 +0,0 @@ -/* - * 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.ui; - -import java.io.Serializable; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jsoup.nodes.Attributes; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Container.ItemSetChangeNotifier; -import com.vaadin.data.Container.PropertySetChangeEvent; -import com.vaadin.data.Container.PropertySetChangeListener; -import com.vaadin.data.Container.PropertySetChangeNotifier; -import com.vaadin.data.Container.Sortable; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.fieldgroup.FieldGroup.CommitException; -import com.vaadin.data.fieldgroup.FieldGroupFieldFactory; -import com.vaadin.data.sort.Sort; -import com.vaadin.data.sort.SortOrder; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.ContextClickEvent; -import com.vaadin.event.ItemClickEvent; -import com.vaadin.event.ItemClickEvent.ItemClickListener; -import com.vaadin.event.ItemClickEvent.ItemClickNotifier; -import com.vaadin.event.SelectionEvent; -import com.vaadin.event.SelectionEvent.SelectionListener; -import com.vaadin.event.SelectionEvent.SelectionNotifier; -import com.vaadin.event.SortEvent; -import com.vaadin.event.SortEvent.SortListener; -import com.vaadin.event.SortEvent.SortNotifier; -import com.vaadin.server.AbstractClientConnector; -import com.vaadin.server.AbstractExtension; -import com.vaadin.server.EncodeResult; -import com.vaadin.server.ErrorMessage; -import com.vaadin.server.Extension; -import com.vaadin.server.JsonCodec; -import com.vaadin.server.KeyMapper; -import com.vaadin.server.VaadinSession; -import com.vaadin.server.communication.data.DataGenerator; -import com.vaadin.server.communication.data.RpcDataProviderExtension; -import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.data.sort.SortDirection; -import com.vaadin.shared.ui.grid.EditorClientRpc; -import com.vaadin.shared.ui.grid.EditorServerRpc; -import com.vaadin.shared.ui.grid.GridClientRpc; -import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridConstants; -import com.vaadin.shared.ui.grid.GridConstants.Section; -import com.vaadin.shared.ui.grid.GridServerRpc; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.ui.grid.GridStaticCellType; -import com.vaadin.shared.ui.grid.GridStaticSectionState; -import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; -import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; -import com.vaadin.shared.ui.grid.HeightMode; -import com.vaadin.shared.ui.grid.ScrollDestination; -import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; -import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; -import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; -import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; -import com.vaadin.shared.util.SharedUtil; -import com.vaadin.ui.declarative.DesignAttributeHandler; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.ui.declarative.DesignException; -import com.vaadin.ui.declarative.DesignFormatter; -import com.vaadin.ui.renderers.HtmlRenderer; -import com.vaadin.ui.renderers.Renderer; -import com.vaadin.ui.renderers.TextRenderer; -import com.vaadin.util.ReflectTools; -import com.vaadin.v7.data.Validator.InvalidValueException; -import com.vaadin.v7.data.util.converter.LegacyConverter; -import com.vaadin.v7.data.util.converter.LegacyConverterUtil; -import com.vaadin.v7.ui.LegacyCheckBox; -import com.vaadin.v7.ui.LegacyField; - -import elemental.json.Json; -import elemental.json.JsonObject; -import elemental.json.JsonValue; - -/** - * A grid component for displaying tabular data. - *

- * Grid is always bound to a {@link Container.Indexed}, but is not a - * {@code Container} of any kind in of itself. The contents of the given - * Container is displayed with the help of {@link Renderer Renderers}. - * - *

Headers and Footers

- *

- * - * - *

Converters and Renderers

- *

- * Each column has its own {@link Renderer} that displays data into something - * that can be displayed in the browser. That data is first converted with a - * {@link com.vaadin.v7.data.util.converter.LegacyConverter Converter} into - * something that the Renderer can process. This can also be an implicit step - - * if a column has a simple data type, like a String, no explicit assignment is - * needed. - *

- * Usually a renderer takes some kind of object, and converts it into a - * HTML-formatted string. - *

- *

- * Grid grid = new Grid(myContainer);
- * Column column = grid.getColumn(STRING_DATE_PROPERTY);
- * column.setConverter(new StringToDateConverter());
- * column.setRenderer(new MyColorfulDateRenderer());
- * 
- * - *

Lazy Loading

- *

- * The data is accessed as it is needed by Grid and not any sooner. In other - * words, if the given Container is huge, but only the first few rows are - * displayed to the user, only those (and a few more, for caching purposes) are - * accessed. - * - *

Selection Modes and Models

- *

- * Grid supports three selection {@link SelectionMode modes} (single, - * multi, none), and comes bundled with one {@link SelectionModel - * model} for each of the modes. The distinction between a selection mode - * and selection model is as follows: a mode essentially says whether - * you can have one, many or no rows selected. The model, however, has the - * behavioral details of each. A single selection model may require that the - * user deselects one row before selecting another one. A variant of a - * multiselect might have a configurable maximum of rows that may be selected. - * And so on. - *

- *

- * Grid grid = new Grid(myContainer);
- *
- * // uses the bundled SingleSelectionModel class
- * grid.setSelectionMode(SelectionMode.SINGLE);
- *
- * // changes the behavior to a custom selection model
- * grid.setSelectionModel(new MyTwoSelectionModel());
- * 
- * - * @since 7.4 - * @author Vaadin Ltd - */ -public class LegacyGrid extends AbstractFocusable implements SelectionNotifier, - SortNotifier, SelectiveRenderer, ItemClickNotifier { - - /** - * An event listener for column visibility change events in the Grid. - * - * @since 7.5.0 - */ - public interface ColumnVisibilityChangeListener extends Serializable { - /** - * Called when a column has become hidden or unhidden. - * - * @param event - */ - void columnVisibilityChanged(ColumnVisibilityChangeEvent event); - } - - /** - * An event that is fired when a column's visibility changes. - * - * @since 7.5.0 - */ - public static class ColumnVisibilityChangeEvent extends Component.Event { - - private final Column column; - private final boolean userOriginated; - private final boolean hidden; - - /** - * Constructor for a column visibility change event. - * - * @param source - * the grid from which this event originates - * @param column - * the column that changed its visibility - * @param hidden - * true if the column was hidden, - * false if it became visible - * @param isUserOriginated - * true iff the event was triggered by an UI - * interaction - */ - public ColumnVisibilityChangeEvent(LegacyGrid source, Column column, - boolean hidden, boolean isUserOriginated) { - super(source); - this.column = column; - this.hidden = hidden; - userOriginated = isUserOriginated; - } - - /** - * Gets the column that became hidden or visible. - * - * @return the column that became hidden or visible. - * @see Column#isHidden() - */ - public Column getColumn() { - return column; - } - - /** - * Was the column set hidden or visible. - * - * @return true if the column was hidden false - * if it was set visible - */ - public boolean isHidden() { - return hidden; - } - - /** - * Returns true if the column reorder was done by the user, - * false if not and it was triggered by server side code. - * - * @return true if event is a result of user interaction - */ - public boolean isUserOriginated() { - return userOriginated; - } - } - - /** - * A callback interface for generating details for a particular row in Grid. - * - * @since 7.5.0 - * @author Vaadin Ltd - * @see DetailsGenerator#NULL - */ - public interface DetailsGenerator extends Serializable { - - /** A details generator that provides no details */ - public DetailsGenerator NULL = new DetailsGenerator() { - @Override - public Component getDetails(RowReference rowReference) { - return null; - } - }; - - /** - * This method is called for whenever a details row needs to be shown on - * the client. Grid removes all of its references to details components - * when they are no longer displayed on the client-side and will - * re-request once needed again. - *

- * Note: If a component gets generated, it may not be manually - * attached anywhere. The same details component can not be displayed - * for multiple different rows. - * - * @param rowReference - * the reference for the row for which to generate details - * @return the details for the given row, or null to leave - * the details empty. - */ - Component getDetails(RowReference rowReference); - } - - /** - * A class that manages details components by calling - * {@link DetailsGenerator} as needed. Details components are attached by - * this class when the {@link RpcDataProviderExtension} is sending data to - * the client. Details components are detached and forgotten when client - * informs that it has dropped the corresponding item. - * - * @since 7.6.1 - */ - public final static class DetailComponentManager - extends AbstractGridExtension implements DataGenerator { - - /** - * The user-defined details generator. - * - * @see #setDetailsGenerator(DetailsGenerator) - */ - private DetailsGenerator detailsGenerator; - - /** - * This map represents all details that are currently visible on the - * client. Details components get destroyed once they scroll out of - * view. - */ - private final Map itemIdToDetailsComponent = new HashMap<>(); - - /** - * Set of item ids that got null from DetailsGenerator when - * {@link DetailsGenerator#getDetails(RowReference)} was called. - */ - private final Set emptyDetails = new HashSet<>(); - - /** - * Set of item IDs for all open details rows. Contains even the ones - * that are not currently visible on the client. - */ - private final Set openDetails = new HashSet<>(); - - public DetailComponentManager(LegacyGrid grid) { - this(grid, DetailsGenerator.NULL); - } - - public DetailComponentManager(LegacyGrid grid, - DetailsGenerator detailsGenerator) { - super(grid); - setDetailsGenerator(detailsGenerator); - } - - /** - * Creates a details component with the help of the user-defined - * {@link DetailsGenerator}. - *

- * This method attaches created components to the parent {@link LegacyGrid}. - * - * @param itemId - * the item id for which to create the details component. - * @throws IllegalStateException - * if the current details generator provides a component - * that was manually attached. - */ - private void createDetails(Object itemId) throws IllegalStateException { - assert itemId != null : "itemId was null"; - - if (itemIdToDetailsComponent.containsKey(itemId) - || emptyDetails.contains(itemId)) { - // Don't overwrite existing components - return; - } - - RowReference rowReference = new RowReference(getParentGrid()); - rowReference.set(itemId); - - DetailsGenerator detailsGenerator = getParentGrid() - .getDetailsGenerator(); - Component details = detailsGenerator.getDetails(rowReference); - if (details != null) { - if (details.getParent() != null) { - String name = detailsGenerator.getClass().getName(); - throw new IllegalStateException( - name + " generated a details component that already " - + "was attached. (itemId: " + itemId - + ", component: " + details + ")"); - } - - itemIdToDetailsComponent.put(itemId, details); - - addComponentToGrid(details); - - assert !emptyDetails.contains(itemId) : "Bookeeping thinks " - + "itemId is empty even though we just created a " - + "component for it (" + itemId + ")"; - } else { - emptyDetails.add(itemId); - } - - } - - /** - * Destroys a details component correctly. - *

- * This method will detach the component from parent {@link LegacyGrid}. - * - * @param itemId - * the item id for which to destroy the details component - */ - private void destroyDetails(Object itemId) { - emptyDetails.remove(itemId); - - Component removedComponent = itemIdToDetailsComponent - .remove(itemId); - if (removedComponent == null) { - return; - } - - removeComponentFromGrid(removedComponent); - } - - /** - * Recreates all visible details components. - */ - public void refreshDetails() { - Set visibleItemIds = new HashSet<>( - itemIdToDetailsComponent.keySet()); - for (Object itemId : visibleItemIds) { - destroyDetails(itemId); - createDetails(itemId); - refreshRow(itemId); - } - } - - /** - * Sets details visiblity status of given item id. - * - * @param itemId - * item id to set - * @param visible - * true if visible; false if not - */ - public void setDetailsVisible(Object itemId, boolean visible) { - if ((visible && openDetails.contains(itemId)) - || (!visible && !openDetails.contains(itemId))) { - return; - } - - if (visible) { - openDetails.add(itemId); - refreshRow(itemId); - } else { - openDetails.remove(itemId); - destroyDetails(itemId); - refreshRow(itemId); - } - } - - @Override - public void generateData(Object itemId, Item item, JsonObject rowData) { - // DetailComponentManager should not send anything if details - // generator is the default null version. - if (openDetails.contains(itemId) - && !detailsGenerator.equals(DetailsGenerator.NULL)) { - // Double check to be sure details component exists. - createDetails(itemId); - - Component detailsComponent = itemIdToDetailsComponent - .get(itemId); - rowData.put(GridState.JSONKEY_DETAILS_VISIBLE, - (detailsComponent != null - ? detailsComponent.getConnectorId() : "")); - } - } - - @Override - public void destroyData(Object itemId) { - if (openDetails.contains(itemId)) { - destroyDetails(itemId); - } - } - - /** - * Sets a new details generator for row details. - *

- * The currently opened row details will be re-rendered. - * - * @param detailsGenerator - * the details generator to set - * @throws IllegalArgumentException - * if detailsGenerator is null; - */ - public void setDetailsGenerator(DetailsGenerator detailsGenerator) - throws IllegalArgumentException { - if (detailsGenerator == null) { - throw new IllegalArgumentException( - "Details generator may not be null"); - } else if (detailsGenerator == this.detailsGenerator) { - return; - } - - this.detailsGenerator = detailsGenerator; - - refreshDetails(); - } - - /** - * Gets the current details generator for row details. - * - * @return the detailsGenerator the current details generator - */ - public DetailsGenerator getDetailsGenerator() { - return detailsGenerator; - } - - /** - * Checks whether details are visible for the given item. - * - * @param itemId - * the id of the item for which to check details visibility - * @return true iff the details are visible - */ - public boolean isDetailsVisible(Object itemId) { - return openDetails.contains(itemId); - } - } - - /** - * Custom field group that allows finding property types before an item has - * been bound. - */ - private final class CustomFieldGroup extends FieldGroup { - - public CustomFieldGroup() { - setFieldFactory(EditorFieldFactory.get()); - } - - @Override - protected Class getPropertyType(Object propertyId) - throws BindException { - if (getItemDataSource() == null) { - return datasource.getType(propertyId); - } else { - return super.getPropertyType(propertyId); - } - } - - @Override - protected T build(String caption, - Class dataType, Class fieldType) throws BindException { - T field = super.build(caption, dataType, fieldType); - if (field instanceof LegacyCheckBox) { - field.setCaption(null); - } - return field; - } - } - - /** - * Field factory used by default in the editor. - * - * Aims to fields of suitable type and with suitable size for use in the - * editor row. - */ - public static class EditorFieldFactory - extends DefaultFieldGroupFieldFactory { - private static final EditorFieldFactory INSTANCE = new EditorFieldFactory(); - - protected EditorFieldFactory() { - } - - /** - * Returns the singleton instance - * - * @return the singleton instance - */ - public static EditorFieldFactory get() { - return INSTANCE; - } - - @Override - public T createField(Class type, - Class fieldType) { - T f = super.createField(type, fieldType); - if (f != null) { - f.setWidth("100%"); - } - return f; - } - - @Override - protected AbstractSelect createCompatibleSelect( - Class fieldType) { - if (anySelect(fieldType)) { - return super.createCompatibleSelect(ComboBox.class); - } - return super.createCompatibleSelect(fieldType); - } - - @Override - protected void populateWithEnumData(AbstractSelect select, - Class enumClass) { - // Use enums directly and the EnumToStringConverter to be consistent - // with what is shown in the Grid - @SuppressWarnings("unchecked") - EnumSet enumSet = EnumSet.allOf(enumClass); - for (Object r : enumSet) { - select.addItem(r); - } - } - } - - /** - * Error handler for the editor - */ - public interface EditorErrorHandler extends Serializable { - - /** - * Called when an exception occurs while the editor row is being saved - * - * @param event - * An event providing more information about the error - */ - void commitError(CommitErrorEvent event); - } - - /** - * ContextClickEvent for the Grid Component. - * - * @since 7.6 - */ - public static class GridContextClickEvent extends ContextClickEvent { - - private final Object itemId; - private final int rowIndex; - private final Object propertyId; - private final Section section; - - public GridContextClickEvent(LegacyGrid source, - MouseEventDetails mouseEventDetails, Section section, - int rowIndex, Object itemId, Object propertyId) { - super(source, mouseEventDetails); - this.itemId = itemId; - this.propertyId = propertyId; - this.section = section; - this.rowIndex = rowIndex; - } - - /** - * Returns the item id of context clicked row. - * - * @return item id of clicked row; null if header or footer - */ - public Object getItemId() { - return itemId; - } - - /** - * Returns property id of clicked column. - * - * @return property id - */ - public Object getPropertyId() { - return propertyId; - } - - /** - * Return the clicked section of Grid. - * - * @return section of grid - */ - public Section getSection() { - return section; - } - - /** - * Returns the clicked row index relative to Grid section. In the body - * of the Grid the index is the item index in the Container. Header and - * Footer rows for index can be fetched with - * {@link LegacyGrid#getHeaderRow(int)} and {@link LegacyGrid#getFooterRow(int)}. - * - * @return row index in section - */ - public int getRowIndex() { - return rowIndex; - } - - @Override - public LegacyGrid getComponent() { - return (LegacyGrid) super.getComponent(); - } - } - - /** - * An event which is fired when saving the editor fails - */ - public static class CommitErrorEvent extends Component.Event { - - private CommitException cause; - - private Set errorColumns = new HashSet<>(); - - private String userErrorMessage; - - public CommitErrorEvent(LegacyGrid grid, CommitException cause) { - super(grid); - this.cause = cause; - userErrorMessage = cause.getLocalizedMessage(); - } - - /** - * Retrieves the cause of the failure - * - * @return the cause of the failure - */ - public CommitException getCause() { - return cause; - } - - @Override - public LegacyGrid getComponent() { - return (LegacyGrid) super.getComponent(); - } - - /** - * Checks if validation exceptions caused this error - * - * @return true if the problem was caused by a validation error - */ - public boolean isValidationFailure() { - return cause.getCause() instanceof InvalidValueException; - } - - /** - * Marks that an error indicator should be shown for the editor of a - * column. - * - * @param column - * the column to show an error for - */ - public void addErrorColumn(Column column) { - errorColumns.add(column); - } - - /** - * Gets all the columns that have been marked as erroneous. - * - * @return an umodifiable collection of erroneous columns - */ - public Collection getErrorColumns() { - return Collections.unmodifiableCollection(errorColumns); - } - - /** - * Gets the error message to show to the user. - * - * @return error message to show - */ - public String getUserErrorMessage() { - return userErrorMessage; - } - - /** - * Sets the error message to show to the user. - * - * @param userErrorMessage - * the user error message to set - */ - public void setUserErrorMessage(String userErrorMessage) { - this.userErrorMessage = userErrorMessage; - } - - } - - /** - * An event listener for column reorder events in the Grid. - * - * @since 7.5.0 - */ - public interface ColumnReorderListener extends Serializable { - - /** - * Called when the columns of the grid have been reordered. - * - * @param event - * An event providing more information - */ - void columnReorder(ColumnReorderEvent event); - } - - /** - * An event that is fired when the columns are reordered. - * - * @since 7.5.0 - */ - public static class ColumnReorderEvent extends Component.Event { - - private final boolean userOriginated; - - /** - * - * @param source - * the grid where the event originated from - * @param userOriginated - * true if event is a result of user - * interaction, false if from API call - */ - public ColumnReorderEvent(LegacyGrid source, boolean userOriginated) { - super(source); - this.userOriginated = userOriginated; - } - - /** - * Returns true if the column reorder was done by the user, - * false if not and it was triggered by server side code. - * - * @return true if event is a result of user interaction - */ - public boolean isUserOriginated() { - return userOriginated; - } - - } - - /** - * An event listener for column resize events in the Grid. - * - * @since 7.6 - */ - public interface ColumnResizeListener extends Serializable { - - /** - * Called when the columns of the grid have been resized. - * - * @param event - * An event providing more information - */ - void columnResize(ColumnResizeEvent event); - } - - /** - * An event that is fired when a column is resized, either programmatically - * or by the user. - * - * @since 7.6 - */ - public static class ColumnResizeEvent extends Component.Event { - - private final Column column; - private final boolean userOriginated; - - /** - * - * @param source - * the grid where the event originated from - * @param userOriginated - * true if event is a result of user - * interaction, false if from API call - */ - public ColumnResizeEvent(LegacyGrid source, Column column, - boolean userOriginated) { - super(source); - this.column = column; - this.userOriginated = userOriginated; - } - - /** - * Returns the column that was resized. - * - * @return the resized column. - */ - public Column getColumn() { - return column; - } - - /** - * Returns true if the column resize was done by the user, - * false if not and it was triggered by server side code. - * - * @return true if event is a result of user interaction - */ - public boolean isUserOriginated() { - return userOriginated; - } - - } - - /** - * Interface for an editor event listener - */ - public interface EditorListener extends Serializable { - - public static final Method EDITOR_OPEN_METHOD = ReflectTools.findMethod( - EditorListener.class, "editorOpened", EditorOpenEvent.class); - public static final Method EDITOR_MOVE_METHOD = ReflectTools.findMethod( - EditorListener.class, "editorMoved", EditorMoveEvent.class); - public static final Method EDITOR_CLOSE_METHOD = ReflectTools - .findMethod(EditorListener.class, "editorClosed", - EditorCloseEvent.class); - - /** - * Called when an editor is opened - * - * @param e - * an editor open event object - */ - public void editorOpened(EditorOpenEvent e); - - /** - * Called when an editor is reopened without closing it first - * - * @param e - * an editor move event object - */ - public void editorMoved(EditorMoveEvent e); - - /** - * Called when an editor is closed - * - * @param e - * an editor close event object - */ - public void editorClosed(EditorCloseEvent e); - - } - - /** - * Base class for editor related events - */ - public static abstract class EditorEvent extends Component.Event { - - private Object itemID; - - protected EditorEvent(LegacyGrid source, Object itemID) { - super(source); - this.itemID = itemID; - } - - /** - * Get the item (row) for which this editor was opened - */ - public Object getItem() { - return itemID; - } - - } - - /** - * This event gets fired when an editor is opened - */ - public static class EditorOpenEvent extends EditorEvent { - - public EditorOpenEvent(LegacyGrid source, Object itemID) { - super(source, itemID); - } - } - - /** - * This event gets fired when an editor is opened while another row is being - * edited (i.e. editor focus moves elsewhere) - */ - public static class EditorMoveEvent extends EditorEvent { - - public EditorMoveEvent(LegacyGrid source, Object itemID) { - super(source, itemID); - } - } - - /** - * This event gets fired when an editor is dismissed or closed by other - * means. - */ - public static class EditorCloseEvent extends EditorEvent { - - public EditorCloseEvent(LegacyGrid source, Object itemID) { - super(source, itemID); - } - } - - /** - * Default error handler for the editor - * - */ - public class DefaultEditorErrorHandler implements EditorErrorHandler { - - @Override - public void commitError(CommitErrorEvent event) { - Map, InvalidValueException> invalidFields = event - .getCause().getInvalidFields(); - - if (!invalidFields.isEmpty()) { - Object firstErrorPropertyId = null; - LegacyField firstErrorField = null; - - FieldGroup fieldGroup = event.getCause().getFieldGroup(); - for (Column column : getColumns()) { - Object propertyId = column.getPropertyId(); - LegacyField field = fieldGroup.getField(propertyId); - if (invalidFields.keySet().contains(field)) { - event.addErrorColumn(column); - - if (firstErrorPropertyId == null) { - firstErrorPropertyId = propertyId; - firstErrorField = field; - } - } - } - - /* - * Validation error, show first failure as - * ": " - */ - String caption = getColumn(firstErrorPropertyId) - .getHeaderCaption(); - String message = invalidFields.get(firstErrorField) - .getLocalizedMessage(); - - event.setUserErrorMessage(caption + ": " + message); - } else { - com.vaadin.server.ErrorEvent.findErrorHandler(LegacyGrid.this).error( - new ConnectorErrorEvent(LegacyGrid.this, event.getCause())); - } - } - - private Object getFirstPropertyId(FieldGroup fieldGroup, - Set> keySet) { - for (Column c : getColumns()) { - Object propertyId = c.getPropertyId(); - LegacyField f = fieldGroup.getField(propertyId); - if (keySet.contains(f)) { - return propertyId; - } - } - return null; - } - } - - /** - * Selection modes representing built-in {@link SelectionModel - * SelectionModels} that come bundled with {@link LegacyGrid}. - *

- * Passing one of these enums into - * {@link LegacyGrid#setSelectionMode(SelectionMode)} is equivalent to calling - * {@link LegacyGrid#setSelectionModel(SelectionModel)} with one of the built-in - * implementations of {@link SelectionModel}. - * - * @see LegacyGrid#setSelectionMode(SelectionMode) - * @see LegacyGrid#setSelectionModel(SelectionModel) - */ - public enum SelectionMode { - /** A SelectionMode that maps to {@link SingleSelectionModel} */ - SINGLE { - @Override - protected SelectionModel createModel() { - return new SingleSelectionModel(); - } - - }, - - /** A SelectionMode that maps to {@link MultiSelectionModel} */ - MULTI { - @Override - protected SelectionModel createModel() { - return new MultiSelectionModel(); - } - }, - - /** A SelectionMode that maps to {@link NoSelectionModel} */ - NONE { - @Override - protected SelectionModel createModel() { - return new NoSelectionModel(); - } - }; - - protected abstract SelectionModel createModel(); - } - - /** - * The server-side interface that controls Grid's selection state. - * SelectionModel should extend {@link AbstractGridExtension}. - */ - public interface SelectionModel extends Serializable, Extension { - /** - * Checks whether an item is selected or not. - * - * @param itemId - * the item id to check for - * @return true iff the item is selected - */ - boolean isSelected(Object itemId); - - /** - * Returns a collection of all the currently selected itemIds. - * - * @return a collection of all the currently selected itemIds - */ - Collection getSelectedRows(); - - /** - * Injects the current {@link LegacyGrid} instance into the SelectionModel. - * This method should usually call the extend method of - * {@link AbstractExtension}. - *

- * Note: This method should not be called manually. - * - * @param grid - * the Grid in which the SelectionModel currently is, or - * null when a selection model is being detached - * from a Grid. - */ - void setGrid(LegacyGrid grid); - - /** - * Resets the SelectiomModel to an initial state. - *

- * Most often this means that the selection state is cleared, but - * implementations are free to interpret the "initial state" as they - * wish. Some, for example, may want to keep the first selected item as - * selected. - */ - void reset(); - - /** - * A SelectionModel that supports multiple selections to be made. - *

- * This interface has a contract of having the same behavior, no matter - * how the selection model is interacted with. In other words, if - * something is forbidden to do in e.g. the user interface, it must also - * be forbidden to do in the server-side and client-side APIs. - */ - public interface Multi extends SelectionModel { - - /** - * Marks items as selected. - *

- * This method does not clear any previous selection state, only - * adds to it. - * - * @param itemIds - * the itemId(s) to mark as selected - * @return true if the selection state changed. - * false if all the given itemIds already were - * selected - * @throws IllegalArgumentException - * if the itemIds varargs array is - * null or given itemIds don't exist in the - * container of Grid - * @see #deselect(Object...) - */ - boolean select(Object... itemIds) throws IllegalArgumentException; - - /** - * Marks items as selected. - *

- * This method does not clear any previous selection state, only - * adds to it. - * - * @param itemIds - * the itemIds to mark as selected - * @return true if the selection state changed. - * false if all the given itemIds already were - * selected - * @throws IllegalArgumentException - * if itemIds is null or given - * itemIds don't exist in the container of Grid - * @see #deselect(Collection) - */ - boolean select(Collection itemIds) - throws IllegalArgumentException; - - /** - * Marks items as deselected. - * - * @param itemIds - * the itemId(s) to remove from being selected - * @return true if the selection state changed. - * false if none the given itemIds were - * selected previously - * @throws IllegalArgumentException - * if the itemIds varargs array is - * null - * @see #select(Object...) - */ - boolean deselect(Object... itemIds) throws IllegalArgumentException; - - /** - * Marks items as deselected. - * - * @param itemIds - * the itemId(s) to remove from being selected - * @return true if the selection state changed. - * false if none the given itemIds were - * selected previously - * @throws IllegalArgumentException - * if itemIds is null - * @see #select(Collection) - */ - boolean deselect(Collection itemIds) - throws IllegalArgumentException; - - /** - * Marks all the items in the current Container as selected - * - * @return true iff some items were previously not - * selected - * @see #deselectAll() - */ - boolean selectAll(); - - /** - * Marks all the items in the current Container as deselected - * - * @return true iff some items were previously selected - * @see #selectAll() - */ - boolean deselectAll(); - - /** - * Marks items as selected while deselecting all items not in the - * given Collection. - * - * @param itemIds - * the itemIds to mark as selected - * @return true if the selection state changed. - * false if all the given itemIds already were - * selected - * @throws IllegalArgumentException - * if itemIds is null or given - * itemIds don't exist in the container of Grid - */ - boolean setSelected(Collection itemIds) - throws IllegalArgumentException; - - /** - * Marks items as selected while deselecting all items not in the - * varargs array. - * - * @param itemIds - * the itemIds to mark as selected - * @return true if the selection state changed. - * false if all the given itemIds already were - * selected - * @throws IllegalArgumentException - * if the itemIds varargs array is - * null or given itemIds don't exist in the - * container of Grid - */ - boolean setSelected(Object... itemIds) - throws IllegalArgumentException; - } - - /** - * A SelectionModel that supports for only single rows to be selected at - * a time. - *

- * This interface has a contract of having the same behavior, no matter - * how the selection model is interacted with. In other words, if - * something is forbidden to do in e.g. the user interface, it must also - * be forbidden to do in the server-side and client-side APIs. - */ - public interface Single extends SelectionModel { - - /** - * Marks an item as selected. - * - * @param itemId - * the itemId to mark as selected; null for - * deselect - * @return true if the selection state changed. - * false if the itemId already was selected - * @throws IllegalStateException - * if the selection was illegal. One such reason might - * be that the given id was null, indicating a deselect, - * but implementation doesn't allow deselecting. - * re-selecting something - * @throws IllegalArgumentException - * if given itemId does not exist in the container of - * Grid - */ - boolean select(Object itemId) - throws IllegalStateException, IllegalArgumentException; - - /** - * Gets the item id of the currently selected item. - * - * @return the item id of the currently selected item, or - * null if nothing is selected - */ - Object getSelectedRow(); - - /** - * Sets whether it's allowed to deselect the selected row through - * the UI. Deselection is allowed by default. - * - * @param deselectAllowed - * true if the selected row can be - * deselected without selecting another row instead; - * otherwise false. - */ - public void setDeselectAllowed(boolean deselectAllowed); - - /** - * Sets whether it's allowed to deselect the selected row through - * the UI. - * - * @return true if deselection is allowed; otherwise - * false - */ - public boolean isDeselectAllowed(); - } - - /** - * A SelectionModel that does not allow for rows to be selected. - *

- * This interface has a contract of having the same behavior, no matter - * how the selection model is interacted with. In other words, if the - * developer is unable to select something programmatically, it is not - * allowed for the end-user to select anything, either. - */ - public interface None extends SelectionModel { - - /** - * {@inheritDoc} - * - * @return always false. - */ - @Override - public boolean isSelected(Object itemId); - - /** - * {@inheritDoc} - * - * @return always an empty collection. - */ - @Override - public Collection getSelectedRows(); - } - } - - /** - * A base class for SelectionModels that contains some of the logic that is - * reusable. - */ - public static abstract class AbstractSelectionModel extends - AbstractGridExtension implements SelectionModel, DataGenerator { - protected final LinkedHashSet selection = new LinkedHashSet<>(); - - @Override - public boolean isSelected(final Object itemId) { - return selection.contains(itemId); - } - - @Override - public Collection getSelectedRows() { - return new ArrayList<>(selection); - } - - @Override - public void setGrid(final LegacyGrid grid) { - if (grid != null) { - extend(grid); - } - } - - /** - * Sanity check for existence of item id. - * - * @param itemId - * item id to be selected / deselected - * - * @throws IllegalArgumentException - * if item Id doesn't exist in the container of Grid - */ - protected void checkItemIdExists(Object itemId) - throws IllegalArgumentException { - if (!getParentGrid().getContainerDataSource().containsId(itemId)) { - throw new IllegalArgumentException("Given item id (" + itemId - + ") does not exist in the container"); - } - } - - /** - * Sanity check for existence of item ids in given collection. - * - * @param itemIds - * item id collection to be selected / deselected - * - * @throws IllegalArgumentException - * if at least one item id doesn't exist in the container of - * Grid - */ - protected void checkItemIdsExist(Collection itemIds) - throws IllegalArgumentException { - for (Object itemId : itemIds) { - checkItemIdExists(itemId); - } - } - - /** - * Fires a {@link SelectionEvent} to all the {@link SelectionListener - * SelectionListeners} currently added to the Grid in which this - * SelectionModel is. - *

- * Note that this is only a helper method, and routes the call all the - * way to Grid. A {@link SelectionModel} is not a - * {@link SelectionNotifier} - * - * @param oldSelection - * the complete {@link Collection} of the itemIds that were - * selected before this event happened - * @param newSelection - * the complete {@link Collection} of the itemIds that are - * selected after this event happened - */ - protected void fireSelectionEvent(final Collection oldSelection, - final Collection newSelection) { - getParentGrid().fireSelectionEvent(oldSelection, newSelection); - } - - @Override - public void generateData(Object itemId, Item item, JsonObject rowData) { - if (isSelected(itemId)) { - rowData.put(GridState.JSONKEY_SELECTED, true); - } - } - - @Override - public void destroyData(Object itemId) { - // NO-OP - } - - @Override - protected Object getItemId(String rowKey) { - return rowKey != null ? super.getItemId(rowKey) : null; - } - } - - /** - * A default implementation of a {@link SelectionModel.Single} - */ - public static class SingleSelectionModel extends AbstractSelectionModel - implements SelectionModel.Single { - - @Override - protected void extend(AbstractClientConnector target) { - super.extend(target); - registerRpc(new SingleSelectionModelServerRpc() { - - @Override - public void select(String rowKey) { - SingleSelectionModel.this.select(getItemId(rowKey), false); - } - }); - } - - @Override - public boolean select(final Object itemId) { - return select(itemId, true); - } - - protected boolean select(final Object itemId, boolean refresh) { - if (itemId == null) { - return deselect(getSelectedRow()); - } - - checkItemIdExists(itemId); - - final Object selectedRow = getSelectedRow(); - final boolean modified = selection.add(itemId); - if (modified) { - final Collection deselected; - if (selectedRow != null) { - deselectInternal(selectedRow, false, true); - deselected = Collections.singleton(selectedRow); - } else { - deselected = Collections.emptySet(); - } - - fireSelectionEvent(deselected, selection); - } - - if (refresh) { - refreshRow(itemId); - } - - return modified; - } - - private boolean deselect(final Object itemId) { - return deselectInternal(itemId, true, true); - } - - private boolean deselectInternal(final Object itemId, - boolean fireEventIfNeeded, boolean refresh) { - final boolean modified = selection.remove(itemId); - if (modified) { - if (refresh) { - refreshRow(itemId); - } - if (fireEventIfNeeded) { - fireSelectionEvent(Collections.singleton(itemId), - Collections.emptySet()); - } - } - return modified; - } - - @Override - public Object getSelectedRow() { - if (selection.isEmpty()) { - return null; - } else { - return selection.iterator().next(); - } - } - - /** - * Resets the selection state. - *

- * If an item is selected, it will become deselected. - */ - @Override - public void reset() { - deselect(getSelectedRow()); - } - - @Override - public void setDeselectAllowed(boolean deselectAllowed) { - getState().deselectAllowed = deselectAllowed; - } - - @Override - public boolean isDeselectAllowed() { - return getState().deselectAllowed; - } - - @Override - protected SingleSelectionModelState getState() { - return (SingleSelectionModelState) super.getState(); - } - } - - /** - * A default implementation for a {@link SelectionModel.None} - */ - public static class NoSelectionModel extends AbstractSelectionModel - implements SelectionModel.None { - - @Override - public boolean isSelected(final Object itemId) { - return false; - } - - @Override - public Collection getSelectedRows() { - return Collections.emptyList(); - } - - /** - * Semantically resets the selection model. - *

- * Effectively a no-op. - */ - @Override - public void reset() { - // NOOP - } - } - - /** - * A default implementation of a {@link SelectionModel.Multi} - */ - public static class MultiSelectionModel extends AbstractSelectionModel - implements SelectionModel.Multi { - - /** - * The default selection size limit. - * - * @see #setSelectionLimit(int) - */ - public static final int DEFAULT_MAX_SELECTIONS = 1000; - - private int selectionLimit = DEFAULT_MAX_SELECTIONS; - - @Override - protected void extend(AbstractClientConnector target) { - super.extend(target); - registerRpc(new MultiSelectionModelServerRpc() { - - @Override - public void select(List rowKeys) { - List items = new ArrayList<>(); - for (String rowKey : rowKeys) { - items.add(getItemId(rowKey)); - } - MultiSelectionModel.this.select(items, false); - } - - @Override - public void deselect(List rowKeys) { - List items = new ArrayList<>(); - for (String rowKey : rowKeys) { - items.add(getItemId(rowKey)); - } - MultiSelectionModel.this.deselect(items, false); - } - - @Override - public void selectAll() { - MultiSelectionModel.this.selectAll(false); - } - - @Override - public void deselectAll() { - MultiSelectionModel.this.deselectAll(false); - } - }); - } - - @Override - public boolean select(final Object... itemIds) - throws IllegalArgumentException { - if (itemIds != null) { - // select will fire the event - return select(Arrays.asList(itemIds)); - } else { - throw new IllegalArgumentException( - "Vararg array of itemIds may not be null"); - } - } - - /** - * {@inheritDoc} - *

- * All items might not be selected if the limit set using - * {@link #setSelectionLimit(int)} is exceeded. - */ - @Override - public boolean select(final Collection itemIds) - throws IllegalArgumentException { - return select(itemIds, true); - } - - protected boolean select(final Collection itemIds, boolean refresh) { - if (itemIds == null) { - throw new IllegalArgumentException("itemIds may not be null"); - } - - // Sanity check - checkItemIdsExist(itemIds); - - final boolean selectionWillChange = !selection.containsAll(itemIds) - && selection.size() < selectionLimit; - if (selectionWillChange) { - final HashSet oldSelection = new HashSet<>(selection); - if (selection.size() + itemIds.size() >= selectionLimit) { - // Add one at a time if there's a risk of overflow - Iterator iterator = itemIds.iterator(); - while (iterator.hasNext() - && selection.size() < selectionLimit) { - selection.add(iterator.next()); - } - } else { - selection.addAll(itemIds); - } - fireSelectionEvent(oldSelection, selection); - } - - updateAllSelectedState(); - - if (refresh) { - for (Object itemId : itemIds) { - refreshRow(itemId); - } - } - - return selectionWillChange; - } - - /** - * Sets the maximum number of rows that can be selected at once. This is - * a mechanism to prevent exhausting server memory in situations where - * users select lots of rows. If the limit is reached, newly selected - * rows will not become recorded. - *

- * Old selections are not discarded if the current number of selected - * row exceeds the new limit. - *

- * The default limit is {@value #DEFAULT_MAX_SELECTIONS} rows. - * - * @param selectionLimit - * the non-negative selection limit to set - * @throws IllegalArgumentException - * if the limit is negative - */ - public void setSelectionLimit(int selectionLimit) { - if (selectionLimit < 0) { - throw new IllegalArgumentException( - "The selection limit must be non-negative"); - } - this.selectionLimit = selectionLimit; - } - - /** - * Gets the selection limit. - * - * @see #setSelectionLimit(int) - * - * @return the selection limit - */ - public int getSelectionLimit() { - return selectionLimit; - } - - @Override - public boolean deselect(final Object... itemIds) - throws IllegalArgumentException { - if (itemIds != null) { - // deselect will fire the event - return deselect(Arrays.asList(itemIds)); - } else { - throw new IllegalArgumentException( - "Vararg array of itemIds may not be null"); - } - } - - @Override - public boolean deselect(final Collection itemIds) - throws IllegalArgumentException { - return deselect(itemIds, true); - } - - protected boolean deselect(final Collection itemIds, - boolean refresh) { - if (itemIds == null) { - throw new IllegalArgumentException("itemIds may not be null"); - } - - final boolean hasCommonElements = !Collections.disjoint(itemIds, - selection); - if (hasCommonElements) { - final HashSet oldSelection = new HashSet<>(selection); - selection.removeAll(itemIds); - fireSelectionEvent(oldSelection, selection); - } - - updateAllSelectedState(); - - if (refresh) { - for (Object itemId : itemIds) { - refreshRow(itemId); - } - } - - return hasCommonElements; - } - - @Override - public boolean selectAll() { - return selectAll(true); - } - - protected boolean selectAll(boolean refresh) { - // select will fire the event - final Indexed container = getParentGrid().getContainerDataSource(); - if (container != null) { - return select(container.getItemIds(), refresh); - } else if (selection.isEmpty()) { - return false; - } else { - /* - * this should never happen (no container but has a selection), - * but I guess the only theoretically correct course of - * action... - */ - return deselectAll(false); - } - } - - @Override - public boolean deselectAll() { - return deselectAll(true); - } - - protected boolean deselectAll(boolean refresh) { - // deselect will fire the event - return deselect(getSelectedRows(), refresh); - } - - /** - * {@inheritDoc} - *

- * The returned Collection is in order of selection - * – the item that was first selected will be first in the - * collection, and so on. Should an item have been selected twice - * without being deselected in between, it will have remained in its - * original position. - */ - @Override - public Collection getSelectedRows() { - // overridden only for JavaDoc - return super.getSelectedRows(); - } - - /** - * Resets the selection model. - *

- * Equivalent to calling {@link #deselectAll()} - */ - @Override - public void reset() { - deselectAll(); - } - - @Override - public boolean setSelected(Collection itemIds) - throws IllegalArgumentException { - if (itemIds == null) { - throw new IllegalArgumentException("itemIds may not be null"); - } - - checkItemIdsExist(itemIds); - - boolean changed = false; - Set selectedRows = new HashSet<>(itemIds); - final Collection oldSelection = getSelectedRows(); - Set added = getDifference(selectedRows, selection); - if (!added.isEmpty()) { - changed = true; - selection.addAll(added); - for (Object id : added) { - refreshRow(id); - } - } - - Set removed = getDifference(selection, selectedRows); - if (!removed.isEmpty()) { - changed = true; - selection.removeAll(removed); - for (Object id : removed) { - refreshRow(id); - } - } - - if (changed) { - fireSelectionEvent(oldSelection, selection); - } - - updateAllSelectedState(); - - return changed; - } - - /** - * Compares two sets and returns a set containing all values that are - * present in the first, but not in the second. - * - * @param set1 - * first item set - * @param set2 - * second item set - * @return all values from set1 which are not present in set2 - */ - private static Set getDifference(Set set1, - Set set2) { - Set diff = new HashSet<>(set1); - diff.removeAll(set2); - return diff; - } - - @Override - public boolean setSelected(Object... itemIds) - throws IllegalArgumentException { - if (itemIds != null) { - return setSelected(Arrays.asList(itemIds)); - } else { - throw new IllegalArgumentException( - "Vararg array of itemIds may not be null"); - } - } - - private void updateAllSelectedState() { - int totalRowCount = getParentGrid().datasource.size(); - int rows = Math.min(totalRowCount, selectionLimit); - if (getState().allSelected != selection.size() >= rows) { - getState().allSelected = selection.size() >= rows; - } - } - - @Override - protected MultiSelectionModelState getState() { - return (MultiSelectionModelState) super.getState(); - } - } - - /** - * A data class which contains information which identifies a row in a - * {@link LegacyGrid}. - *

- * Since this class follows the Flyweight-pattern any instance - * of this object is subject to change without the user knowing it and so - * should not be stored anywhere outside of the method providing these - * instances. - */ - public static class RowReference implements Serializable { - private final LegacyGrid grid; - - private Object itemId; - - /** - * Creates a new row reference for the given grid. - * - * @param grid - * the grid that the row belongs to - */ - public RowReference(LegacyGrid grid) { - this.grid = grid; - } - - /** - * Sets the identifying information for this row - * - * @param itemId - * the item id of the row - */ - public void set(Object itemId) { - this.itemId = itemId; - } - - /** - * Gets the grid that contains the referenced row. - * - * @return the grid that contains referenced row - */ - public LegacyGrid getGrid() { - return grid; - } - - /** - * Gets the item id of the row. - * - * @return the item id of the row - */ - public Object getItemId() { - return itemId; - } - - /** - * Gets the item for the row. - * - * @return the item for the row - */ - public Item getItem() { - return grid.getContainerDataSource().getItem(itemId); - } - } - - /** - * A data class which contains information which identifies a cell in a - * {@link LegacyGrid}. - *

- * Since this class follows the Flyweight-pattern any instance - * of this object is subject to change without the user knowing it and so - * should not be stored anywhere outside of the method providing these - * instances. - */ - public static class CellReference implements Serializable { - private final RowReference rowReference; - - private Object propertyId; - - public CellReference(RowReference rowReference) { - this.rowReference = rowReference; - } - - /** - * Sets the identifying information for this cell - * - * @param propertyId - * the property id of the column - */ - public void set(Object propertyId) { - this.propertyId = propertyId; - } - - /** - * Gets the grid that contains the referenced cell. - * - * @return the grid that contains referenced cell - */ - public LegacyGrid getGrid() { - return rowReference.getGrid(); - } - - /** - * @return the property id of the column - */ - public Object getPropertyId() { - return propertyId; - } - - /** - * @return the property for the cell - */ - public Property getProperty() { - return getItem().getItemProperty(propertyId); - } - - /** - * Gets the item id of the row of the cell. - * - * @return the item id of the row - */ - public Object getItemId() { - return rowReference.getItemId(); - } - - /** - * Gets the item for the row of the cell. - * - * @return the item for the row - */ - public Item getItem() { - return rowReference.getItem(); - } - - /** - * Gets the value of the cell. - * - * @return the value of the cell - */ - public Object getValue() { - return getProperty().getValue(); - } - } - - /** - * A callback interface for generating custom style names for Grid rows. - * - * @see LegacyGrid#setRowStyleGenerator(RowStyleGenerator) - */ - public interface RowStyleGenerator extends Serializable { - - /** - * Called by Grid to generate a style name for a row. - * - * @param row - * the row to generate a style for - * @return the style name to add to this row, or {@code null} to not set - * any style - */ - public String getStyle(RowReference row); - } - - /** - * A callback interface for generating custom style names for Grid cells. - * - * @see LegacyGrid#setCellStyleGenerator(CellStyleGenerator) - */ - public interface CellStyleGenerator extends Serializable { - - /** - * Called by Grid to generate a style name for a column. - * - * @param cell - * the cell to generate a style for - * @return the style name to add to this cell, or {@code null} to not - * set any style - */ - public String getStyle(CellReference cell); - } - - /** - * A callback interface for generating optional descriptions (tooltips) for - * Grid rows. If a description is generated for a row, it is used for all - * the cells in the row for which a {@link CellDescriptionGenerator cell - * description} is not generated. - * - * @see LegacyGrid#setRowDescriptionGenerator - * - * @since 7.6 - */ - public interface RowDescriptionGenerator extends Serializable { - - /** - * Called by Grid to generate a description (tooltip) for a row. The - * description may contain HTML which is rendered directly; if this is - * not desired the returned string must be escaped by the implementing - * method. - * - * @param row - * the row to generate a description for - * @return the row description or {@code null} for no description - */ - public String getDescription(RowReference row); - } - - /** - * A callback interface for generating optional descriptions (tooltips) for - * Grid cells. If a cell has both a {@link RowDescriptionGenerator row - * description} and a cell description, the latter has precedence. - * - * @see LegacyGrid#setCellDescriptionGenerator(CellDescriptionGenerator) - * - * @since 7.6 - */ - public interface CellDescriptionGenerator extends Serializable { - - /** - * Called by Grid to generate a description (tooltip) for a cell. The - * description may contain HTML which is rendered directly; if this is - * not desired the returned string must be escaped by the implementing - * method. - * - * @param cell - * the cell to generate a description for - * @return the cell description or {@code null} for no description - */ - public String getDescription(CellReference cell); - } - - /** - * Class for generating all row and cell related data for the essential - * parts of Grid. - */ - private class RowDataGenerator implements DataGenerator { - - private void put(String key, String value, JsonObject object) { - if (value != null && !value.isEmpty()) { - object.put(key, value); - } - } - - @Override - public void generateData(Object itemId, Item item, JsonObject rowData) { - RowReference row = new RowReference(LegacyGrid.this); - row.set(itemId); - - if (rowStyleGenerator != null) { - String style = rowStyleGenerator.getStyle(row); - put(GridState.JSONKEY_ROWSTYLE, style, rowData); - } - - if (rowDescriptionGenerator != null) { - String description = rowDescriptionGenerator - .getDescription(row); - put(GridState.JSONKEY_ROWDESCRIPTION, description, rowData); - - } - - JsonObject cellStyles = Json.createObject(); - JsonObject cellData = Json.createObject(); - JsonObject cellDescriptions = Json.createObject(); - - CellReference cell = new CellReference(row); - - for (Column column : getColumns()) { - cell.set(column.getPropertyId()); - - writeData(cell, cellData); - writeStyles(cell, cellStyles); - writeDescriptions(cell, cellDescriptions); - } - - if (cellDescriptionGenerator != null - && cellDescriptions.keys().length > 0) { - rowData.put(GridState.JSONKEY_CELLDESCRIPTION, - cellDescriptions); - } - - if (cellStyleGenerator != null && cellStyles.keys().length > 0) { - rowData.put(GridState.JSONKEY_CELLSTYLES, cellStyles); - } - - rowData.put(GridState.JSONKEY_DATA, cellData); - } - - private void writeStyles(CellReference cell, JsonObject styles) { - if (cellStyleGenerator != null) { - String style = cellStyleGenerator.getStyle(cell); - put(columnKeys.key(cell.getPropertyId()), style, styles); - } - } - - private void writeDescriptions(CellReference cell, - JsonObject descriptions) { - if (cellDescriptionGenerator != null) { - String description = cellDescriptionGenerator - .getDescription(cell); - put(columnKeys.key(cell.getPropertyId()), description, - descriptions); - } - } - - private void writeData(CellReference cell, JsonObject data) { - Column column = getColumn(cell.getPropertyId()); - LegacyConverter converter = column.getConverter(); - Renderer renderer = column.getRenderer(); - - Item item = cell.getItem(); - Object modelValue = item.getItemProperty(cell.getPropertyId()) - .getValue(); - - data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer - .encodeValue(modelValue, renderer, converter, getLocale())); - } - - @Override - public void destroyData(Object itemId) { - // NO-OP - } - } - - /** - * Abstract base class for Grid header and footer sections. - * - * @since 7.6 - * @param - * the type of the rows in the section - */ - public abstract static class StaticSection> - implements Serializable { - - /** - * Abstract base class for Grid header and footer rows. - * - * @param - * the type of the cells in the row - */ - public abstract static class StaticRow - implements Serializable { - - private RowState rowState = new RowState(); - protected StaticSection section; - private Map cells = new LinkedHashMap<>(); - private Map, CELLTYPE> cellGroups = new HashMap<>(); - - protected StaticRow(StaticSection section) { - this.section = section; - } - - protected void addCell(Object propertyId) { - CELLTYPE cell = createCell(); - cell.setColumnId( - section.grid.getColumn(propertyId).getState().id); - cells.put(propertyId, cell); - rowState.cells.add(cell.getCellState()); - } - - protected void removeCell(Object propertyId) { - CELLTYPE cell = cells.remove(propertyId); - if (cell != null) { - Set cellGroupForCell = getCellGroupForCell(cell); - if (cellGroupForCell != null) { - removeCellFromGroup(cell, cellGroupForCell); - } - rowState.cells.remove(cell.getCellState()); - } - } - - private void removeCellFromGroup(CELLTYPE cell, - Set cellGroup) { - String columnId = cell.getColumnId(); - for (Set group : rowState.cellGroups.keySet()) { - if (group.contains(columnId)) { - if (group.size() > 2) { - // Update map key correctly - CELLTYPE mergedCell = cellGroups.remove(cellGroup); - cellGroup.remove(cell); - cellGroups.put(cellGroup, mergedCell); - - group.remove(columnId); - } else { - rowState.cellGroups.remove(group); - cellGroups.remove(cellGroup); - } - return; - } - } - } - - /** - * Creates and returns a new instance of the cell type. - * - * @return the created cell - */ - protected abstract CELLTYPE createCell(); - - protected RowState getRowState() { - return rowState; - } - - /** - * Returns the cell for the given property id on this row. If the - * column is merged returned cell is the cell for the whole group. - * - * @param propertyId - * the property id of the column - * @return the cell for the given property, merged cell for merged - * properties, null if not found - */ - public CELLTYPE getCell(Object propertyId) { - CELLTYPE cell = cells.get(propertyId); - Set cellGroup = getCellGroupForCell(cell); - if (cellGroup != null) { - cell = cellGroups.get(cellGroup); - } - return cell; - } - - /** - * Merges columns cells in a row - * - * @param propertyIds - * The property ids of columns to merge - * @return The remaining visible cell after the merge - */ - public CELLTYPE join(Object... propertyIds) { - assert propertyIds.length > 1 : "You need to merge at least 2 properties"; - - Set cells = new HashSet<>(); - for (int i = 0; i < propertyIds.length; ++i) { - cells.add(getCell(propertyIds[i])); - } - - return join(cells); - } - - /** - * Merges columns cells in a row - * - * @param cells - * The cells to merge. Must be from the same row. - * @return The remaining visible cell after the merge - */ - public CELLTYPE join(CELLTYPE... cells) { - assert cells.length > 1 : "You need to merge at least 2 cells"; - - return join(new HashSet<>(Arrays.asList(cells))); - } - - protected CELLTYPE join(Set cells) { - for (CELLTYPE cell : cells) { - if (getCellGroupForCell(cell) != null) { - throw new IllegalArgumentException( - "Cell already merged"); - } else if (!this.cells.containsValue(cell)) { - throw new IllegalArgumentException( - "Cell does not exist on this row"); - } - } - - // Create new cell data for the group - CELLTYPE newCell = createCell(); - - Set columnGroup = new HashSet<>(); - for (CELLTYPE cell : cells) { - columnGroup.add(cell.getColumnId()); - } - rowState.cellGroups.put(columnGroup, newCell.getCellState()); - cellGroups.put(cells, newCell); - return newCell; - } - - private Set getCellGroupForCell(CELLTYPE cell) { - for (Set group : cellGroups.keySet()) { - if (group.contains(cell)) { - return group; - } - } - return null; - } - - /** - * Returns the custom style name for this row. - * - * @return the style name or null if no style name has been set - */ - public String getStyleName() { - return getRowState().styleName; - } - - /** - * Sets a custom style name for this row. - * - * @param styleName - * the style name to set or null to not use any style - * name - */ - public void setStyleName(String styleName) { - getRowState().styleName = styleName; - } - - /** - * Writes the declarative design to the given table row element. - * - * @since 7.5.0 - * @param trElement - * Element to write design to - * @param designContext - * the design context - */ - protected void writeDesign(Element trElement, - DesignContext designContext) { - Set visited = new HashSet<>(); - for (LegacyGrid.Column column : section.grid.getColumns()) { - CELLTYPE cell = getCell(column.getPropertyId()); - if (visited.contains(cell)) { - continue; - } - visited.add(cell); - - Element cellElement = trElement - .appendElement(getCellTagName()); - cell.writeDesign(cellElement, designContext); - - for (Entry, CELLTYPE> entry : cellGroups - .entrySet()) { - if (entry.getValue() == cell) { - cellElement.attr("colspan", - "" + entry.getKey().size()); - break; - } - } - } - } - - /** - * Reads the declarative design from the given table row element. - * - * @since 7.5.0 - * @param trElement - * Element to read design from - * @param designContext - * the design context - * @throws DesignException - * if the given table row contains unexpected children - */ - protected void readDesign(Element trElement, - DesignContext designContext) throws DesignException { - Elements cellElements = trElement.children(); - int totalColSpans = 0; - for (int i = 0; i < cellElements.size(); ++i) { - Element element = cellElements.get(i); - if (!element.tagName().equals(getCellTagName())) { - throw new DesignException( - "Unexpected element in tr while expecting " - + getCellTagName() + ": " - + element.tagName()); - } - - int columnIndex = i + totalColSpans; - - int colspan = DesignAttributeHandler.readAttribute( - "colspan", element.attributes(), 1, int.class); - - Set cells = new HashSet<>(); - for (int c = 0; c < colspan; ++c) { - cells.add(getCell(section.grid.getColumns() - .get(columnIndex + c).getPropertyId())); - } - - if (colspan > 1) { - totalColSpans += colspan - 1; - join(cells).readDesign(element, designContext); - } else { - cells.iterator().next().readDesign(element, - designContext); - } - } - } - - abstract protected String getCellTagName(); - - void detach() { - for (CELLTYPE cell : cells.values()) { - cell.detach(); - } - } - } - - /** - * A header or footer cell. Has a simple textual caption. - */ - abstract static class StaticCell implements Serializable { - - private CellState cellState = new CellState(); - private StaticRow row; - - protected StaticCell(StaticRow row) { - this.row = row; - } - - void setColumnId(String id) { - cellState.columnId = id; - } - - String getColumnId() { - return cellState.columnId; - } - - /** - * Gets the row where this cell is. - * - * @return row for this cell - */ - public StaticRow getRow() { - return row; - } - - protected CellState getCellState() { - return cellState; - } - - /** - * Sets the text displayed in this cell. - * - * @param text - * a plain text caption - */ - public void setText(String text) { - removeComponentIfPresent(); - cellState.text = text; - cellState.type = GridStaticCellType.TEXT; - row.section.markAsDirty(); - } - - /** - * Returns the text displayed in this cell. - * - * @return the plain text caption - */ - public String getText() { - if (cellState.type != GridStaticCellType.TEXT) { - throw new IllegalStateException( - "Cannot fetch Text from a cell with type " - + cellState.type); - } - return cellState.text; - } - - /** - * Returns the HTML content displayed in this cell. - * - * @return the html - * - */ - public String getHtml() { - if (cellState.type != GridStaticCellType.HTML) { - throw new IllegalStateException( - "Cannot fetch HTML from a cell with type " - + cellState.type); - } - return cellState.html; - } - - /** - * Sets the HTML content displayed in this cell. - * - * @param html - * the html to set - */ - public void setHtml(String html) { - removeComponentIfPresent(); - cellState.html = html; - cellState.type = GridStaticCellType.HTML; - row.section.markAsDirty(); - } - - /** - * Returns the component displayed in this cell. - * - * @return the component - */ - public Component getComponent() { - if (cellState.type != GridStaticCellType.WIDGET) { - throw new IllegalStateException( - "Cannot fetch Component from a cell with type " - + cellState.type); - } - return (Component) cellState.connector; - } - - /** - * Sets the component displayed in this cell. - * - * @param component - * the component to set - */ - public void setComponent(Component component) { - removeComponentIfPresent(); - component.setParent(row.section.grid); - cellState.connector = component; - cellState.type = GridStaticCellType.WIDGET; - row.section.markAsDirty(); - } - - /** - * Returns the type of content stored in this cell. - * - * @return cell content type - */ - public GridStaticCellType getCellType() { - return cellState.type; - } - - /** - * Returns the custom style name for this cell. - * - * @return the style name or null if no style name has been set - */ - public String getStyleName() { - return cellState.styleName; - } - - /** - * Sets a custom style name for this cell. - * - * @param styleName - * the style name to set or null to not use any style - * name - */ - public void setStyleName(String styleName) { - cellState.styleName = styleName; - row.section.markAsDirty(); - } - - private void removeComponentIfPresent() { - Component component = (Component) cellState.connector; - if (component != null) { - component.setParent(null); - cellState.connector = null; - } - } - - /** - * Writes the declarative design to the given table cell element. - * - * @since 7.5.0 - * @param cellElement - * Element to write design to - * @param designContext - * the design context - */ - protected void writeDesign(Element cellElement, - DesignContext designContext) { - switch (cellState.type) { - case TEXT: - cellElement.attr("plain-text", true); - cellElement.appendText(getText()); - break; - case HTML: - cellElement.append(getHtml()); - break; - case WIDGET: - cellElement.appendChild( - designContext.createElement(getComponent())); - break; - } - } - - /** - * Reads the declarative design from the given table cell element. - * - * @since 7.5.0 - * @param cellElement - * Element to read design from - * @param designContext - * the design context - */ - protected void readDesign(Element cellElement, - DesignContext designContext) { - if (!cellElement.hasAttr("plain-text")) { - if (cellElement.children().size() > 0 - && cellElement.child(0).tagName().contains("-")) { - setComponent( - designContext.readDesign(cellElement.child(0))); - } else { - setHtml(cellElement.html()); - } - } else { - // text – need to unescape HTML entities - setText(DesignFormatter - .decodeFromTextNode(cellElement.html())); - } - } - - void detach() { - removeComponentIfPresent(); - } - } - - protected LegacyGrid grid; - protected List rows = new ArrayList<>(); - - /** - * Sets the visibility of the whole section. - * - * @param visible - * true to show this section, false to hide - */ - public void setVisible(boolean visible) { - if (getSectionState().visible != visible) { - getSectionState().visible = visible; - markAsDirty(); - } - } - - /** - * Returns the visibility of this section. - * - * @return true if visible, false otherwise. - */ - public boolean isVisible() { - return getSectionState().visible; - } - - /** - * Removes the row at the given position. - * - * @param rowIndex - * the position of the row - * - * @throws IllegalArgumentException - * if no row exists at given index - * @see #removeRow(StaticRow) - * @see #addRowAt(int) - * @see #appendRow() - * @see #prependRow() - */ - public ROWTYPE removeRow(int rowIndex) { - if (rowIndex >= rows.size() || rowIndex < 0) { - throw new IllegalArgumentException( - "No row at given index " + rowIndex); - } - ROWTYPE row = rows.remove(rowIndex); - row.detach(); - getSectionState().rows.remove(rowIndex); - - markAsDirty(); - return row; - } - - /** - * Removes the given row from the section. - * - * @param row - * the row to be removed - * - * @throws IllegalArgumentException - * if the row does not exist in this section - * @see #removeRow(int) - * @see #addRowAt(int) - * @see #appendRow() - * @see #prependRow() - */ - public void removeRow(ROWTYPE row) { - try { - removeRow(rows.indexOf(row)); - } catch (IndexOutOfBoundsException e) { - throw new IllegalArgumentException( - "Section does not contain the given row"); - } - } - - /** - * Gets row at given index. - * - * @param rowIndex - * 0 based index for row. Counted from top to bottom - * @return row at given index - */ - public ROWTYPE getRow(int rowIndex) { - if (rowIndex >= rows.size() || rowIndex < 0) { - throw new IllegalArgumentException( - "No row at given index " + rowIndex); - } - return rows.get(rowIndex); - } - - /** - * Adds a new row at the top of this section. - * - * @return the new row - * @see #appendRow() - * @see #addRowAt(int) - * @see #removeRow(StaticRow) - * @see #removeRow(int) - */ - public ROWTYPE prependRow() { - return addRowAt(0); - } - - /** - * Adds a new row at the bottom of this section. - * - * @return the new row - * @see #prependRow() - * @see #addRowAt(int) - * @see #removeRow(StaticRow) - * @see #removeRow(int) - */ - public ROWTYPE appendRow() { - return addRowAt(rows.size()); - } - - /** - * Inserts a new row at the given position. - * - * @param index - * the position at which to insert the row - * @return the new row - * - * @throws IndexOutOfBoundsException - * if the index is out of bounds - * @see #appendRow() - * @see #prependRow() - * @see #removeRow(StaticRow) - * @see #removeRow(int) - */ - public ROWTYPE addRowAt(int index) { - if (index > rows.size() || index < 0) { - throw new IllegalArgumentException( - "Unable to add row at index " + index); - } - ROWTYPE row = createRow(); - rows.add(index, row); - getSectionState().rows.add(index, row.getRowState()); - - for (Object id : grid.columns.keySet()) { - row.addCell(id); - } - - markAsDirty(); - return row; - } - - /** - * Gets the amount of rows in this section. - * - * @return row count - */ - public int getRowCount() { - return rows.size(); - } - - protected abstract GridStaticSectionState getSectionState(); - - protected abstract ROWTYPE createRow(); - - /** - * Informs the grid that state has changed and it should be redrawn. - */ - protected void markAsDirty() { - grid.markAsDirty(); - } - - /** - * Removes a column for given property id from the section. - * - * @param propertyId - * property to be removed - */ - protected void removeColumn(Object propertyId) { - for (ROWTYPE row : rows) { - row.removeCell(propertyId); - } - } - - /** - * Adds a column for given property id to the section. - * - * @param propertyId - * property to be added - */ - protected void addColumn(Object propertyId) { - for (ROWTYPE row : rows) { - row.addCell(propertyId); - } - } - - /** - * Performs a sanity check that section is in correct state. - * - * @throws IllegalStateException - * if merged cells are not i n continuous range - */ - protected void sanityCheck() throws IllegalStateException { - List columnOrder = grid.getState().columnOrder; - for (ROWTYPE row : rows) { - for (Set cellGroup : row.getRowState().cellGroups - .keySet()) { - if (!checkCellGroupAndOrder(columnOrder, cellGroup)) { - throw new IllegalStateException( - "Not all merged cells were in a continuous range."); - } - } - } - } - - private boolean checkCellGroupAndOrder(List columnOrder, - Set cellGroup) { - if (!columnOrder.containsAll(cellGroup)) { - return false; - } - - for (int i = 0; i < columnOrder.size(); ++i) { - if (!cellGroup.contains(columnOrder.get(i))) { - continue; - } - - for (int j = 1; j < cellGroup.size(); ++j) { - if (!cellGroup.contains(columnOrder.get(i + j))) { - return false; - } - } - return true; - } - return false; - } - - /** - * Writes the declarative design to the given table section element. - * - * @since 7.5.0 - * @param tableSectionElement - * Element to write design to - * @param designContext - * the design context - */ - protected void writeDesign(Element tableSectionElement, - DesignContext designContext) { - for (ROWTYPE row : rows) { - row.writeDesign(tableSectionElement.appendElement("tr"), - designContext); - } - } - - /** - * Writes the declarative design from the given table section element. - * - * @since 7.5.0 - * @param tableSectionElement - * Element to read design from - * @param designContext - * the design context - * @throws DesignException - * if the table section contains unexpected children - */ - protected void readDesign(Element tableSectionElement, - DesignContext designContext) throws DesignException { - while (rows.size() > 0) { - removeRow(0); - } - - for (Element row : tableSectionElement.children()) { - if (!row.tagName().equals("tr")) { - throw new DesignException("Unexpected element in " - + tableSectionElement.tagName() + ": " - + row.tagName()); - } - appendRow().readDesign(row, designContext); - } - } - } - - /** - * Represents the header section of a Grid. - */ - protected static class Header extends StaticSection { - - private HeaderRow defaultRow = null; - private final GridStaticSectionState headerState = new GridStaticSectionState(); - - protected Header(LegacyGrid grid) { - this.grid = grid; - grid.getState(true).header = headerState; - HeaderRow row = createRow(); - rows.add(row); - setDefaultRow(row); - getSectionState().rows.add(row.getRowState()); - } - - /** - * Sets the default row of this header. The default row is a special - * header row providing a user interface for sorting columns. - * - * @param row - * the new default row, or null for no default row - * - * @throws IllegalArgumentException - * this header does not contain the row - */ - public void setDefaultRow(HeaderRow row) { - if (row == defaultRow) { - return; - } - - if (row != null && !rows.contains(row)) { - throw new IllegalArgumentException( - "Cannot set a default row that does not exist in the section"); - } - - if (defaultRow != null) { - defaultRow.setDefaultRow(false); - } - - if (row != null) { - row.setDefaultRow(true); - } - - defaultRow = row; - markAsDirty(); - } - - /** - * Returns the current default row of this header. The default row is a - * special header row providing a user interface for sorting columns. - * - * @return the default row or null if no default row set - */ - public HeaderRow getDefaultRow() { - return defaultRow; - } - - @Override - protected GridStaticSectionState getSectionState() { - return headerState; - } - - @Override - protected HeaderRow createRow() { - return new HeaderRow(this); - } - - @Override - public HeaderRow removeRow(int rowIndex) { - HeaderRow row = super.removeRow(rowIndex); - if (row == defaultRow) { - // Default Header Row was just removed. - setDefaultRow(null); - } - return row; - } - - @Override - protected void sanityCheck() throws IllegalStateException { - super.sanityCheck(); - - boolean hasDefaultRow = false; - for (HeaderRow row : rows) { - if (row.getRowState().defaultRow) { - if (!hasDefaultRow) { - hasDefaultRow = true; - } else { - throw new IllegalStateException( - "Multiple default rows in header"); - } - } - } - } - - @Override - protected void readDesign(Element tableSectionElement, - DesignContext designContext) { - super.readDesign(tableSectionElement, designContext); - - if (defaultRow == null && !rows.isEmpty()) { - grid.setDefaultHeaderRow(rows.get(0)); - } - } - } - - /** - * Represents a header row in Grid. - */ - public static class HeaderRow extends StaticSection.StaticRow { - - protected HeaderRow(StaticSection section) { - super(section); - } - - private void setDefaultRow(boolean value) { - getRowState().defaultRow = value; - } - - private boolean isDefaultRow() { - return getRowState().defaultRow; - } - - @Override - protected HeaderCell createCell() { - return new HeaderCell(this); - } - - @Override - protected String getCellTagName() { - return "th"; - } - - @Override - protected void writeDesign(Element trElement, - DesignContext designContext) { - super.writeDesign(trElement, designContext); - - if (section.grid.getDefaultHeaderRow() == this) { - DesignAttributeHandler.writeAttribute("default", - trElement.attributes(), true, null, boolean.class); - } - } - - @Override - protected void readDesign(Element trElement, - DesignContext designContext) { - super.readDesign(trElement, designContext); - - boolean defaultRow = DesignAttributeHandler.readAttribute("default", - trElement.attributes(), false, boolean.class); - if (defaultRow) { - section.grid.setDefaultHeaderRow(this); - } - } - } - - /** - * Represents a header cell in Grid. Can be a merged cell for multiple - * columns. - */ - public static class HeaderCell extends StaticSection.StaticCell { - - protected HeaderCell(HeaderRow row) { - super(row); - } - } - - /** - * Represents the footer section of a Grid. By default Footer is not - * visible. - */ - protected static class Footer extends StaticSection { - - private final GridStaticSectionState footerState = new GridStaticSectionState(); - - protected Footer(LegacyGrid grid) { - this.grid = grid; - grid.getState(true).footer = footerState; - } - - @Override - protected GridStaticSectionState getSectionState() { - return footerState; - } - - @Override - protected FooterRow createRow() { - return new FooterRow(this); - } - - @Override - protected void sanityCheck() throws IllegalStateException { - super.sanityCheck(); - } - } - - /** - * Represents a footer row in Grid. - */ - public static class FooterRow extends StaticSection.StaticRow { - - protected FooterRow(StaticSection section) { - super(section); - } - - @Override - protected FooterCell createCell() { - return new FooterCell(this); - } - - @Override - protected String getCellTagName() { - return "td"; - } - - } - - /** - * Represents a footer cell in Grid. - */ - public static class FooterCell extends StaticSection.StaticCell { - - protected FooterCell(FooterRow row) { - super(row); - } - } - - /** - * A column in the grid. Can be obtained by calling - * {@link LegacyGrid#getColumn(Object propertyId)}. - */ - public static class Column implements Serializable { - - /** - * The state of the column shared to the client - */ - private final GridColumnState state; - - /** - * The grid this column is associated with - */ - private final LegacyGrid grid; - - /** - * Backing property for column - */ - private final Object propertyId; - - private LegacyConverter converter; - - /** - * A check for allowing the - * {@link #Column(LegacyGrid, GridColumnState, Object) constructor} to call - * {@link #setConverter(LegacyConverter)} with a null, even - * if model and renderer aren't compatible. - */ - private boolean isFirstConverterAssignment = true; - - /** - * Internally used constructor. - * - * @param grid - * The grid this column belongs to. Should not be null. - * @param state - * the shared state of this column - * @param propertyId - * the backing property id for this column - */ - Column(LegacyGrid grid, GridColumnState state, Object propertyId) { - this.grid = grid; - this.state = state; - this.propertyId = propertyId; - internalSetRenderer(new TextRenderer()); - } - - /** - * Returns the serializable state of this column that is sent to the - * client side connector. - * - * @return the internal state of the column - */ - GridColumnState getState() { - return state; - } - - /** - * Returns the property id for the backing property of this Column - * - * @return property id - */ - public Object getPropertyId() { - return propertyId; - } - - /** - * Returns the caption of the header. By default the header caption is - * the property id of the column. - * - * @return the text in the default row of header. - * - * @throws IllegalStateException - * if the column no longer is attached to the grid - */ - public String getHeaderCaption() throws IllegalStateException { - checkColumnIsAttached(); - - return state.headerCaption; - } - - /** - * Sets the caption of the header. This caption is also used as the - * hiding toggle caption, unless it is explicitly set via - * {@link #setHidingToggleCaption(String)}. - * - * @param caption - * the text to show in the caption - * @return the column itself - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public Column setHeaderCaption(String caption) - throws IllegalStateException { - checkColumnIsAttached(); - if (caption == null) { - caption = ""; // Render null as empty - } - state.headerCaption = caption; - - HeaderRow row = grid.getHeader().getDefaultRow(); - if (row != null) { - row.getCell(grid.getPropertyIdByColumnId(state.id)) - .setText(caption); - } - return this; - } - - /** - * Gets the caption of the hiding toggle for this column. - * - * @since 7.5.0 - * @see #setHidingToggleCaption(String) - * @return the caption for the hiding toggle for this column - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public String getHidingToggleCaption() throws IllegalStateException { - checkColumnIsAttached(); - return state.hidingToggleCaption; - } - - /** - * Sets the caption of the hiding toggle for this column. Shown in the - * toggle for this column in the grid's sidebar when the column is - * {@link #isHidable() hidable}. - *

- * The default value is null, and in that case the column's - * {@link #getHeaderCaption() header caption} is used. - *

- * NOTE: setting this to empty string might cause the hiding - * toggle to not render correctly. - * - * @since 7.5.0 - * @param hidingToggleCaption - * the text to show in the column hiding toggle - * @return the column itself - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public Column setHidingToggleCaption(String hidingToggleCaption) - throws IllegalStateException { - checkColumnIsAttached(); - state.hidingToggleCaption = hidingToggleCaption; - grid.markAsDirty(); - return this; - } - - /** - * Returns the width (in pixels). By default a column is 100px wide. - * - * @return the width in pixels of the column - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public double getWidth() throws IllegalStateException { - checkColumnIsAttached(); - return state.width; - } - - /** - * Sets the width (in pixels). - *

- * This overrides any configuration set by any of - * {@link #setExpandRatio(int)}, {@link #setMinimumWidth(double)} or - * {@link #setMaximumWidth(double)}. - * - * @param pixelWidth - * the new pixel width of the column - * @return the column itself - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - * @throws IllegalArgumentException - * thrown if pixel width is less than zero - */ - public Column setWidth(double pixelWidth) - throws IllegalStateException, IllegalArgumentException { - checkColumnIsAttached(); - if (pixelWidth < 0) { - throw new IllegalArgumentException( - "Pixel width should be greated than 0 (in " + toString() - + ")"); - } - if (state.width != pixelWidth) { - state.width = pixelWidth; - grid.markAsDirty(); - grid.fireColumnResizeEvent(this, false); - } - return this; - } - - /** - * Returns whether this column has an undefined width. - * - * @since 7.6 - * @return whether the width is undefined - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public boolean isWidthUndefined() { - checkColumnIsAttached(); - return state.width < 0; - } - - /** - * Marks the column width as undefined. An undefined width means the - * grid is free to resize the column based on the cell contents and - * available space in the grid. - * - * @return the column itself - */ - public Column setWidthUndefined() { - checkColumnIsAttached(); - if (!isWidthUndefined()) { - state.width = -1; - grid.markAsDirty(); - grid.fireColumnResizeEvent(this, false); - } - return this; - } - - /** - * Checks if column is attached and throws an - * {@link IllegalStateException} if it is not - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - protected void checkColumnIsAttached() throws IllegalStateException { - if (grid.getColumnByColumnId(state.id) == null) { - throw new IllegalStateException("Column no longer exists."); - } - } - - /** - * Sets this column as the last frozen column in its grid. - * - * @return the column itself - * - * @throws IllegalArgumentException - * if the column is no longer attached to any grid - * @see LegacyGrid#setFrozenColumnCount(int) - */ - public Column setLastFrozenColumn() { - checkColumnIsAttached(); - grid.setFrozenColumnCount( - grid.getState(false).columnOrder.indexOf(getState().id) - + 1); - return this; - } - - /** - * Sets the renderer for this column. - *

- * If a suitable converter isn't defined explicitly, the session - * converter factory is used to find a compatible converter. - * - * @param renderer - * the renderer to use - * @return the column itself - * - * @throws IllegalArgumentException - * if no compatible converter could be found - * - * @see VaadinSession#getConverterFactory() - * @see LegacyConverterUtil#getConverter(Class, Class, VaadinSession) - * @see #setConverter(LegacyConverter) - */ - public Column setRenderer(Renderer renderer) { - if (!internalSetRenderer(renderer)) { - throw new IllegalArgumentException( - "Could not find a converter for converting from the model type " - + getModelType() - + " to the renderer presentation type " - + renderer.getPresentationType() + " (in " - + toString() + ")"); - } - return this; - } - - /** - * Sets the renderer for this column and the converter used to convert - * from the property value type to the renderer presentation type. - * - * @param renderer - * the renderer to use, cannot be null - * @param converter - * the converter to use - * @return the column itself - * - * @throws IllegalArgumentException - * if the renderer is already associated with a grid column - */ - public Column setRenderer(Renderer renderer, - LegacyConverter converter) { - if (renderer.getParent() != null) { - throw new IllegalArgumentException( - "Cannot set a renderer that is already connected to a grid column (in " - + toString() + ")"); - } - - if (getRenderer() != null) { - grid.removeExtension(getRenderer()); - } - - grid.addRenderer(renderer); - state.rendererConnector = renderer; - setConverter(converter); - return this; - } - - /** - * Sets the converter used to convert from the property value type to - * the renderer presentation type. - * - * @param converter - * the converter to use, or {@code null} to not use any - * converters - * @return the column itself - * - * @throws IllegalArgumentException - * if the types are not compatible - */ - public Column setConverter(LegacyConverter converter) - throws IllegalArgumentException { - Class modelType = getModelType(); - if (converter != null) { - if (!converter.getModelType().isAssignableFrom(modelType)) { - throw new IllegalArgumentException( - "The converter model type " - + converter.getModelType() - + " is not compatible with the property type " - + modelType + " (in " + toString() + ")"); - - } else if (!getRenderer().getPresentationType() - .isAssignableFrom(converter.getPresentationType())) { - throw new IllegalArgumentException( - "The converter presentation type " - + converter.getPresentationType() - + " is not compatible with the renderer presentation type " - + getRenderer().getPresentationType() - + " (in " + toString() + ")"); - } - } - - else { - /* - * Since the converter is null (i.e. will be removed), we need - * to know that the renderer and model are compatible. If not, - * we can't allow for this to happen. - * - * The constructor is allowed to call this method with null - * without any compatibility checks, therefore we have a special - * case for it. - */ - - Class rendererPresentationType = getRenderer() - .getPresentationType(); - if (!isFirstConverterAssignment && !rendererPresentationType - .isAssignableFrom(modelType)) { - throw new IllegalArgumentException( - "Cannot remove converter, " - + "as renderer's presentation type " - + rendererPresentationType.getName() - + " and column's " + "model " - + modelType.getName() + " type aren't " - + "directly compatible with each other (in " - + toString() + ")"); - } - } - - isFirstConverterAssignment = false; - - @SuppressWarnings("unchecked") - LegacyConverter castConverter = (LegacyConverter) converter; - this.converter = castConverter; - - return this; - } - - /** - * Returns the renderer instance used by this column. - * - * @return the renderer - */ - public Renderer getRenderer() { - return (Renderer) getState().rendererConnector; - } - - /** - * Returns the converter instance used by this column. - * - * @return the converter - */ - public LegacyConverter getConverter() { - return converter; - } - - private boolean internalSetRenderer(Renderer renderer) { - - LegacyConverter converter; - if (isCompatibleWithProperty(renderer, getConverter())) { - // Use the existing converter (possibly none) if types - // compatible - converter = (LegacyConverter) getConverter(); - } else { - converter = LegacyConverterUtil.getConverter( - renderer.getPresentationType(), getModelType(), - getSession()); - } - setRenderer(renderer, converter); - return isCompatibleWithProperty(renderer, converter); - } - - private VaadinSession getSession() { - UI ui = grid.getUI(); - return ui != null ? ui.getSession() : null; - } - - private boolean isCompatibleWithProperty(Renderer renderer, - LegacyConverter converter) { - Class type; - if (converter == null) { - type = getModelType(); - } else { - type = converter.getPresentationType(); - } - return renderer.getPresentationType().isAssignableFrom(type); - } - - private Class getModelType() { - return grid.getContainerDataSource() - .getType(grid.getPropertyIdByColumnId(state.id)); - } - - /** - * Sets whether this column is sortable by the user. The grid can be - * sorted by a sortable column by clicking or tapping the column's - * default header. Programmatic sorting using the Grid#sort methods is - * not affected by this setting. - * - * @param sortable - * {@code true} if the user should be able to sort the - * column, {@code false} otherwise - * @return the column itself - * - * @throws IllegalStateException - * if the data source of the Grid does not implement - * {@link Sortable} - * @throws IllegalStateException - * if the data source does not support sorting by the - * property associated with this column - */ - public Column setSortable(boolean sortable) { - checkColumnIsAttached(); - - if (sortable) { - if (!(grid.datasource instanceof Sortable)) { - throw new IllegalStateException("Can't set column " - + toString() - + " sortable. The Container of Grid does not implement Sortable"); - } else if (!((Sortable) grid.datasource) - .getSortableContainerPropertyIds() - .contains(propertyId)) { - throw new IllegalStateException( - "Can't set column " + toString() - + " sortable. Container doesn't support sorting by property " - + propertyId); - } - } - - state.sortable = sortable; - grid.markAsDirty(); - return this; - } - - /** - * Returns whether the user can sort the grid by this column. - *

- * Note: it is possible to sort by this column programmatically - * using the Grid#sort methods regardless of the returned value. - * - * @return {@code true} if the column is sortable by the user, - * {@code false} otherwise - */ - public boolean isSortable() { - return state.sortable; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[propertyId:" - + grid.getPropertyIdByColumnId(state.id) + "]"; - } - - /** - * Sets the ratio with which the column expands. - *

- * By default, all columns expand equally (treated as if all of them had - * an expand ratio of 1). Once at least one column gets a defined expand - * ratio, the implicit expand ratio is removed, and only the defined - * expand ratios are taken into account. - *

- * If a column has a defined width ({@link #setWidth(double)}), it - * overrides this method's effects. - *

- * Example: A grid with three columns, with expand ratios 0, 1 - * and 2, respectively. The column with a ratio of 0 is exactly - * as wide as its contents requires. The column with a ratio of - * 1 is as wide as it needs, plus a third of any excess - * space, because we have 3 parts total, and this column - * reserves only one of those. The column with a ratio of 2, is as wide - * as it needs to be, plus two thirds of the excess - * width. - * - * @param expandRatio - * the expand ratio of this column. {@code 0} to not have it - * expand at all. A negative number to clear the expand - * value. - * @throws IllegalStateException - * if the column is no longer attached to any grid - * @see #setWidth(double) - */ - public Column setExpandRatio(int expandRatio) - throws IllegalStateException { - checkColumnIsAttached(); - - getState().expandRatio = expandRatio; - grid.markAsDirty(); - return this; - } - - /** - * Returns the column's expand ratio. - * - * @return the column's expand ratio - * @see #setExpandRatio(int) - */ - public int getExpandRatio() { - return getState().expandRatio; - } - - /** - * Clears the expand ratio for this column. - *

- * Equal to calling {@link #setExpandRatio(int) setExpandRatio(-1)} - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - public Column clearExpandRatio() throws IllegalStateException { - return setExpandRatio(-1); - } - - /** - * Sets the minimum width for this column. - *

- * This defines the minimum guaranteed pixel width of the column - * when it is set to expand. - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - * @see #setExpandRatio(int) - */ - public Column setMinimumWidth(double pixels) - throws IllegalStateException { - checkColumnIsAttached(); - - final double maxwidth = getMaximumWidth(); - if (pixels >= 0 && pixels > maxwidth && maxwidth >= 0) { - throw new IllegalArgumentException("New minimum width (" - + pixels + ") was greater than maximum width (" - + maxwidth + ")"); - } - getState().minWidth = pixels; - grid.markAsDirty(); - return this; - } - - /** - * Return the minimum width for this column. - * - * @return the minimum width for this column - * @see #setMinimumWidth(double) - */ - public double getMinimumWidth() { - return getState().minWidth; - } - - /** - * Sets the maximum width for this column. - *

- * This defines the maximum allowed pixel width of the column when - * it is set to expand. - * - * @param pixels - * the maximum width - * @throws IllegalStateException - * if the column is no longer attached to any grid - * @see #setExpandRatio(int) - */ - public Column setMaximumWidth(double pixels) { - checkColumnIsAttached(); - - final double minwidth = getMinimumWidth(); - if (pixels >= 0 && pixels < minwidth && minwidth >= 0) { - throw new IllegalArgumentException("New maximum width (" - + pixels + ") was less than minimum width (" + minwidth - + ")"); - } - - getState().maxWidth = pixels; - grid.markAsDirty(); - return this; - } - - /** - * Returns the maximum width for this column. - * - * @return the maximum width for this column - * @see #setMaximumWidth(double) - */ - public double getMaximumWidth() { - return getState().maxWidth; - } - - /** - * Sets whether the properties corresponding to this column should be - * editable when the item editor is active. By default columns are - * editable. - *

- * Values in non-editable columns are currently not displayed when the - * editor is active, but this will probably change in the future. They - * are not automatically assigned an editor field and, if one is - * manually assigned, it is not used. Columns that cannot (or should - * not) be edited even in principle should be set non-editable. - * - * @param editable - * {@code true} if this column should be editable, - * {@code false} otherwise - * @return this column - * - * @throws IllegalStateException - * if the editor is currently active - * - * @see LegacyGrid#editItem(Object) - * @see LegacyGrid#isEditorActive() - */ - public Column setEditable(boolean editable) { - checkColumnIsAttached(); - if (grid.isEditorActive()) { - throw new IllegalStateException( - "Cannot change column editable status while the editor is active"); - } - getState().editable = editable; - grid.markAsDirty(); - return this; - } - - /** - * Returns whether the properties corresponding to this column should be - * editable when the item editor is active. - * - * @return {@code true} if this column is editable, {@code false} - * otherwise - * - * @see LegacyGrid#editItem(Object) - * @see #setEditable(boolean) - */ - - public boolean isEditable() { - return getState().editable; - } - - /** - * Sets the field component used to edit the properties in this column - * when the item editor is active. If an item has not been set, then the - * binding is postponed until the item is set using - * {@link #editItem(Object)}. - *

- * Setting the field to null clears any previously set - * field, causing a new field to be created the next time the item - * editor is opened. - * - * @param editor - * the editor field - * @return this column - */ - public Column setEditorField(LegacyField editor) { - grid.setEditorField(getPropertyId(), editor); - return this; - } - - /** - * Returns the editor field used to edit the properties in this column - * when the item editor is active. Returns null if the column is not - * {@link Column#isEditable() editable}. - *

- * When {@link #editItem(Object) editItem} is called, fields are - * automatically created and bound for any unbound properties. - *

- * Getting a field before the editor has been opened depends on special - * support from the {@link FieldGroup} in use. Using this method with a - * user-provided FieldGroup might cause - * {@link com.vaadin.data.fieldgroup.FieldGroup.BindException - * BindException} to be thrown. - * - * @return the bound field; or null if the respective - * column is not editable - * - * @throws IllegalArgumentException - * if there is no column for the provided property id - * @throws FieldGroup.BindException - * if no field has been configured and there is a problem - * building or binding - */ - public LegacyField getEditorField() { - return grid.getEditorField(getPropertyId()); - } - - /** - * Hides or shows the column. By default columns are visible before - * explicitly hiding them. - * - * @since 7.5.0 - * @param hidden - * true to hide the column, false - * to show - * @return this column - */ - public Column setHidden(boolean hidden) { - if (hidden != getState().hidden) { - getState().hidden = hidden; - grid.markAsDirty(); - grid.fireColumnVisibilityChangeEvent(this, hidden, false); - } - return this; - } - - /** - * Returns whether this column is hidden. Default is {@code false}. - * - * @since 7.5.0 - * @return true if the column is currently hidden, - * false otherwise - */ - public boolean isHidden() { - return getState().hidden; - } - - /** - * Sets whether this column can be hidden by the user. Hidable columns - * can be hidden and shown via the sidebar menu. - * - * @since 7.5.0 - * @param hidable - * true iff the column may be hidable by the - * user via UI interaction - * @return this column - */ - public Column setHidable(boolean hidable) { - if (hidable != getState().hidable) { - getState().hidable = hidable; - grid.markAsDirty(); - } - return this; - } - - /** - * Returns whether this column can be hidden by the user. Default is - * {@code false}. - *

- * Note: the column can be programmatically hidden using - * {@link #setHidden(boolean)} regardless of the returned value. - * - * @since 7.5.0 - * @return true if the user can hide the column, - * false if not - */ - public boolean isHidable() { - return getState().hidable; - } - - /** - * Sets whether this column can be resized by the user. - * - * @since 7.6 - * @param resizable - * {@code true} if this column should be resizable, - * {@code false} otherwise - */ - public Column setResizable(boolean resizable) { - if (resizable != getState().resizable) { - getState().resizable = resizable; - grid.markAsDirty(); - } - return this; - } - - /** - * Returns whether this column can be resized by the user. Default is - * {@code true}. - *

- * Note: the column can be programmatically resized using - * {@link #setWidth(double)} and {@link #setWidthUndefined()} regardless - * of the returned value. - * - * @since 7.6 - * @return {@code true} if this column is resizable, {@code false} - * otherwise - */ - public boolean isResizable() { - return getState().resizable; - } - - /** - * Writes the design attributes for this column into given element. - * - * @since 7.5.0 - * - * @param design - * Element to write attributes into - * - * @param designContext - * the design context - */ - protected void writeDesign(Element design, - DesignContext designContext) { - Attributes attributes = design.attributes(); - GridColumnState def = new GridColumnState(); - - DesignAttributeHandler.writeAttribute("property-id", attributes, - getPropertyId(), null, Object.class); - - // Sortable is a special attribute that depends on the container. - DesignAttributeHandler.writeAttribute("sortable", attributes, - isSortable(), null, boolean.class); - DesignAttributeHandler.writeAttribute("editable", attributes, - isEditable(), def.editable, boolean.class); - DesignAttributeHandler.writeAttribute("resizable", attributes, - isResizable(), def.resizable, boolean.class); - - DesignAttributeHandler.writeAttribute("hidable", attributes, - isHidable(), def.hidable, boolean.class); - DesignAttributeHandler.writeAttribute("hidden", attributes, - isHidden(), def.hidden, boolean.class); - DesignAttributeHandler.writeAttribute("hiding-toggle-caption", - attributes, getHidingToggleCaption(), null, String.class); - - DesignAttributeHandler.writeAttribute("width", attributes, - getWidth(), def.width, Double.class); - DesignAttributeHandler.writeAttribute("min-width", attributes, - getMinimumWidth(), def.minWidth, Double.class); - DesignAttributeHandler.writeAttribute("max-width", attributes, - getMaximumWidth(), def.maxWidth, Double.class); - DesignAttributeHandler.writeAttribute("expand", attributes, - getExpandRatio(), def.expandRatio, Integer.class); - } - - /** - * Reads the design attributes for this column from given element. - * - * @since 7.5.0 - * @param design - * Element to read attributes from - * @param designContext - * the design context - */ - protected void readDesign(Element design, DesignContext designContext) { - Attributes attributes = design.attributes(); - - if (design.hasAttr("sortable")) { - setSortable(DesignAttributeHandler.readAttribute("sortable", - attributes, boolean.class)); - } - if (design.hasAttr("editable")) { - setEditable(DesignAttributeHandler.readAttribute("editable", - attributes, boolean.class)); - } - if (design.hasAttr("resizable")) { - setResizable(DesignAttributeHandler.readAttribute("resizable", - attributes, boolean.class)); - } - - if (design.hasAttr("hidable")) { - setHidable(DesignAttributeHandler.readAttribute("hidable", - attributes, boolean.class)); - } - if (design.hasAttr("hidden")) { - setHidden(DesignAttributeHandler.readAttribute("hidden", - attributes, boolean.class)); - } - if (design.hasAttr("hiding-toggle-caption")) { - setHidingToggleCaption(DesignAttributeHandler.readAttribute( - "hiding-toggle-caption", attributes, String.class)); - } - - // Read size info where necessary. - if (design.hasAttr("width")) { - setWidth(DesignAttributeHandler.readAttribute("width", - attributes, Double.class)); - } - if (design.hasAttr("min-width")) { - setMinimumWidth(DesignAttributeHandler - .readAttribute("min-width", attributes, Double.class)); - } - if (design.hasAttr("max-width")) { - setMaximumWidth(DesignAttributeHandler - .readAttribute("max-width", attributes, Double.class)); - } - if (design.hasAttr("expand")) { - if (design.attr("expand").isEmpty()) { - setExpandRatio(1); - } else { - setExpandRatio(DesignAttributeHandler.readAttribute( - "expand", attributes, Integer.class)); - } - } - } - } - - /** - * An abstract base class for server-side - * {@link com.vaadin.ui.renderers.Renderer Grid renderers}. This class - * currently extends the AbstractExtension superclass, but this fact should - * be regarded as an implementation detail and subject to change in a future - * major or minor Vaadin revision. - * - * @param - * the type this renderer knows how to present - */ - public static abstract class AbstractRenderer - extends AbstractGridExtension implements Renderer { - - private final Class presentationType; - - private final String nullRepresentation; - - protected AbstractRenderer(Class presentationType, - String nullRepresentation) { - this.presentationType = presentationType; - this.nullRepresentation = nullRepresentation; - } - - protected AbstractRenderer(Class presentationType) { - this(presentationType, null); - } - - /** - * This method is inherited from AbstractExtension but should never be - * called directly with an AbstractRenderer. - */ - @Deprecated - @Override - protected Class getSupportedParentType() { - return LegacyGrid.class; - } - - /** - * This method is inherited from AbstractExtension but should never be - * called directly with an AbstractRenderer. - */ - @Deprecated - @Override - protected void extend(AbstractClientConnector target) { - super.extend(target); - } - - @Override - public Class getPresentationType() { - return presentationType; - } - - @Override - public JsonValue encode(T value) { - if (value == null) { - return encode(getNullRepresentation(), String.class); - } else { - return encode(value, getPresentationType()); - } - } - - /** - * Null representation for the renderer - * - * @return a textual representation of {@code null} - */ - protected String getNullRepresentation() { - return nullRepresentation; - } - - /** - * Encodes the given value to JSON. - *

- * This is a helper method that can be invoked by an - * {@link #encode(Object) encode(T)} override if serializing a value of - * type other than {@link #getPresentationType() the presentation type} - * is desired. For instance, a {@code Renderer} could first turn a - * date value into a formatted string and return - * {@code encode(dateString, String.class)}. - * - * @param value - * the value to be encoded - * @param type - * the type of the value - * @return a JSON representation of the given value - */ - protected JsonValue encode(U value, Class type) { - return JsonCodec - .encode(value, null, type, getUI().getConnectorTracker()) - .getEncodedValue(); - } - - /** - * Converts and encodes the given data model property value using the - * given converter and renderer. This method is public only for testing - * purposes. - * - * @since 7.6 - * @param renderer - * the renderer to use - * @param converter - * the converter to use - * @param modelValue - * the value to convert and encode - * @param locale - * the locale to use in conversion - * @return an encoded value ready to be sent to the client - */ - public static JsonValue encodeValue(Object modelValue, - Renderer renderer, LegacyConverter converter, - Locale locale) { - Class presentationType = renderer.getPresentationType(); - T presentationValue; - - if (converter == null) { - try { - presentationValue = presentationType.cast(modelValue); - } catch (ClassCastException e) { - if (presentationType == String.class) { - // If there is no converter, just fallback to using - // toString(). modelValue can't be null as - // Class.cast(null) will always succeed - presentationValue = (T) modelValue.toString(); - } else { - throw new LegacyConverter.ConversionException( - "Unable to convert value of type " - + modelValue.getClass().getName() - + " to presentation type " - + presentationType.getName() - + ". No converter is set and the types are not compatible."); - } - } - } else { - assert presentationType - .isAssignableFrom(converter.getPresentationType()); - @SuppressWarnings("unchecked") - LegacyConverter safeConverter = (LegacyConverter) converter; - presentationValue = safeConverter.convertToPresentation( - modelValue, safeConverter.getPresentationType(), - locale); - } - - JsonValue encodedValue; - try { - encodedValue = renderer.encode(presentationValue); - } catch (Exception e) { - getLogger().log(Level.SEVERE, "Unable to encode data", e); - encodedValue = renderer.encode(null); - } - - return encodedValue; - } - - private static Logger getLogger() { - return Logger.getLogger(AbstractRenderer.class.getName()); - } - - } - - /** - * An abstract base class for server-side Grid extensions. - *

- * Note: If the extension is an instance of {@link DataGenerator} it will - * automatically register itself to {@link RpcDataProviderExtension} of - * extended Grid. On remove this registration is automatically removed. - * - * @since 7.5 - */ - public static abstract class AbstractGridExtension - extends AbstractExtension { - - /** - * Constructs a new Grid extension. - */ - public AbstractGridExtension() { - super(); - } - - /** - * Constructs a new Grid extension and extends given Grid. - * - * @param grid - * a grid instance - */ - public AbstractGridExtension(LegacyGrid grid) { - super(); - extend(grid); - } - - @Override - protected void extend(AbstractClientConnector target) { - super.extend(target); - - if (this instanceof DataGenerator) { - getParentGrid().datasourceExtension - .addDataGenerator((DataGenerator) this); - } - } - - @Override - public void remove() { - if (this instanceof DataGenerator) { - getParentGrid().datasourceExtension - .removeDataGenerator((DataGenerator) this); - } - - super.remove(); - } - - /** - * Gets the item id for a row key. - *

- * A key is used to identify a particular row on both a server and a - * client. This method can be used to get the item id for the row key - * that the client has sent. - * - * @param rowKey - * the row key for which to retrieve an item id - * @return the item id corresponding to {@code key} - */ - protected Object getItemId(String rowKey) { - return getParentGrid().getKeyMapper().get(rowKey); - } - - /** - * Gets the column for a column id. - *

- * An id is used to identify a particular column on both a server and a - * client. This method can be used to get the column for the column id - * that the client has sent. - * - * @param columnId - * the column id for which to retrieve a column - * @return the column corresponding to {@code columnId} - */ - protected Column getColumn(String columnId) { - return getParentGrid().getColumnByColumnId(columnId); - } - - /** - * Gets the parent Grid of the renderer. - * - * @return parent grid - * @throws IllegalStateException - * if parent is not Grid - */ - protected LegacyGrid getParentGrid() { - if (getParent() instanceof LegacyGrid) { - LegacyGrid grid = (LegacyGrid) getParent(); - return grid; - } else if (getParent() == null) { - throw new IllegalStateException( - "Renderer is not attached to any parent"); - } else { - throw new IllegalStateException( - "Renderers can be used only with Grid. Extended " - + getParent().getClass().getSimpleName() - + " instead"); - } - } - - /** - * Resends the row data for given item id to the client. - * - * @since 7.6 - * @param itemId - * row to refresh - */ - protected void refreshRow(Object itemId) { - getParentGrid().datasourceExtension.updateRowData(itemId); - } - - /** - * Informs the parent Grid that this Extension wants to add a child - * component to it. - * - * @since 7.6 - * @param c - * component - */ - protected void addComponentToGrid(Component c) { - getParentGrid().addComponent(c); - } - - /** - * Informs the parent Grid that this Extension wants to remove a child - * component from it. - * - * @since 7.6 - * @param c - * component - */ - protected void removeComponentFromGrid(Component c) { - getParentGrid().removeComponent(c); - } - } - - /** - * The data source attached to the grid - */ - private Container.Indexed datasource; - - /** - * Property id to column instance mapping - */ - private final Map columns = new HashMap<>(); - - /** - * Key generator for column server-to-client communication - */ - private final KeyMapper columnKeys = new KeyMapper<>(); - - /** - * The current sort order - */ - private final List sortOrder = new ArrayList<>(); - - /** - * Property listener for listening to changes in data source properties. - */ - private final PropertySetChangeListener propertyListener = new PropertySetChangeListener() { - - @Override - public void containerPropertySetChange(PropertySetChangeEvent event) { - Collection properties = new HashSet( - event.getContainer().getContainerPropertyIds()); - - // Find columns that need to be removed. - List removedColumns = new LinkedList<>(); - for (Object propertyId : columns.keySet()) { - if (!properties.contains(propertyId)) { - removedColumns.add(getColumn(propertyId)); - } - } - - // Actually remove columns. - for (Column column : removedColumns) { - Object propertyId = column.getPropertyId(); - internalRemoveColumn(propertyId); - columnKeys.remove(propertyId); - } - datasourceExtension.columnsRemoved(removedColumns); - - // Add new columns - List addedColumns = new LinkedList<>(); - for (Object propertyId : properties) { - if (!columns.containsKey(propertyId)) { - addedColumns.add(appendColumn(propertyId)); - } - } - datasourceExtension.columnsAdded(addedColumns); - - if (getFrozenColumnCount() > columns.size()) { - setFrozenColumnCount(columns.size()); - } - - // Unset sortable for non-sortable columns. - if (datasource instanceof Sortable) { - Collection sortables = ((Sortable) datasource) - .getSortableContainerPropertyIds(); - for (Object propertyId : columns.keySet()) { - Column column = columns.get(propertyId); - if (!sortables.contains(propertyId) - && column.isSortable()) { - column.setSortable(false); - } - } - } - } - }; - - private final ItemSetChangeListener editorClosingItemSetListener = new ItemSetChangeListener() { - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - cancelEditor(); - } - }; - - private RpcDataProviderExtension datasourceExtension; - - /** - * The selection model that is currently in use. Never null - * after the constructor has been run. - */ - private SelectionModel selectionModel; - - /** - * Used to know whether selection change events originate from the server or - * the client so the selection change handler knows whether the changes - * should be sent to the client. - */ - private boolean applyingSelectionFromClient; - - private final Header header = new Header(this); - private final Footer footer = new Footer(this); - - private Object editedItemId = null; - private boolean editorActive = false; - private FieldGroup editorFieldGroup = new CustomFieldGroup(); - - private CellStyleGenerator cellStyleGenerator; - private RowStyleGenerator rowStyleGenerator; - - private CellDescriptionGenerator cellDescriptionGenerator; - private RowDescriptionGenerator rowDescriptionGenerator; - - /** - * true if Grid is using the internal IndexedContainer created - * in Grid() constructor, or false if the user has set their - * own Container. - * - * @see #setContainerDataSource(Indexed) - * @see #LegacyGrid() - */ - private boolean defaultContainer = true; - - private EditorErrorHandler editorErrorHandler = new DefaultEditorErrorHandler(); - - private DetailComponentManager detailComponentManager = null; - - private Set extensionComponents = new HashSet<>(); - - private static final Method SELECTION_CHANGE_METHOD = ReflectTools - .findMethod(SelectionListener.class, "select", - SelectionEvent.class); - - private static final Method SORT_ORDER_CHANGE_METHOD = ReflectTools - .findMethod(SortListener.class, "sort", SortEvent.class); - - private static final Method COLUMN_REORDER_METHOD = ReflectTools.findMethod( - ColumnReorderListener.class, "columnReorder", - ColumnReorderEvent.class); - - private static final Method COLUMN_RESIZE_METHOD = ReflectTools.findMethod( - ColumnResizeListener.class, "columnResize", - ColumnResizeEvent.class); - - private static final Method COLUMN_VISIBILITY_METHOD = ReflectTools - .findMethod(ColumnVisibilityChangeListener.class, - "columnVisibilityChanged", - ColumnVisibilityChangeEvent.class); - - /** - * Creates a new Grid with a new {@link IndexedContainer} as the data - * source. - */ - public LegacyGrid() { - this(null, null); - } - - /** - * Creates a new Grid using the given data source. - * - * @param dataSource - * the indexed container to use as a data source - */ - public LegacyGrid(final Container.Indexed dataSource) { - this(null, dataSource); - } - - /** - * Creates a new Grid with the given caption and a new - * {@link IndexedContainer} data source. - * - * @param caption - * the caption of the grid - */ - public LegacyGrid(String caption) { - this(caption, null); - } - - /** - * Creates a new Grid with the given caption and data source. If the data - * source is null, a new {@link IndexedContainer} will be used. - * - * @param caption - * the caption of the grid - * @param dataSource - * the indexed container to use as a data source - */ - public LegacyGrid(String caption, Container.Indexed dataSource) { - if (dataSource == null) { - internalSetContainerDataSource(new IndexedContainer()); - } else { - setContainerDataSource(dataSource); - } - setCaption(caption); - initGrid(); - } - - /** - * Grid initial setup - */ - private void initGrid() { - setSelectionMode(getDefaultSelectionMode()); - - registerRpc(new GridServerRpc() { - - @Override - public void sort(String[] columnIds, SortDirection[] directions, - boolean userOriginated) { - assert columnIds.length == directions.length; - - List order = new ArrayList<>(columnIds.length); - for (int i = 0; i < columnIds.length; i++) { - Object propertyId = getPropertyIdByColumnId(columnIds[i]); - order.add(new SortOrder(propertyId, directions[i])); - } - setSortOrder(order, userOriginated); - if (!order.equals(getSortOrder())) { - /* - * Actual sort order is not what the client expects. Make - * sure the client gets a state change event by clearing the - * diffstate and marking as dirty - */ - ConnectorTracker connectorTracker = getUI() - .getConnectorTracker(); - JsonObject diffState = connectorTracker - .getDiffState(LegacyGrid.this); - diffState.remove("sortColumns"); - diffState.remove("sortDirs"); - markAsDirty(); - } - } - - @Override - public void itemClick(String rowKey, String columnId, - MouseEventDetails details) { - Object itemId = getKeyMapper().get(rowKey); - Item item = datasource.getItem(itemId); - Object propertyId = getPropertyIdByColumnId(columnId); - fireEvent(new ItemClickEvent(LegacyGrid.this, item, itemId, - propertyId, details)); - } - - @Override - public void columnsReordered(List newColumnOrder, - List oldColumnOrder) { - final String diffStateKey = "columnOrder"; - ConnectorTracker connectorTracker = getUI() - .getConnectorTracker(); - JsonObject diffState = connectorTracker.getDiffState(LegacyGrid.this); - // discard the change if the columns have been reordered from - // the server side, as the server side is always right - if (getState(false).columnOrder.equals(oldColumnOrder)) { - // Don't mark as dirty since client has the state already - getState(false).columnOrder = newColumnOrder; - // write changes to diffState so that possible reverting the - // column order is sent to client - assert diffState - .hasKey(diffStateKey) : "Field name has changed"; - Type type = null; - try { - type = (getState(false).getClass() - .getDeclaredField(diffStateKey) - .getGenericType()); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (SecurityException e) { - e.printStackTrace(); - } - EncodeResult encodeResult = JsonCodec.encode( - getState(false).columnOrder, diffState, type, - connectorTracker); - - diffState.put(diffStateKey, encodeResult.getEncodedValue()); - fireColumnReorderEvent(true); - } else { - // make sure the client is reverted to the order that the - // server thinks it is - diffState.remove(diffStateKey); - markAsDirty(); - } - } - - @Override - public void columnVisibilityChanged(String id, boolean hidden, - boolean userOriginated) { - final Column column = getColumnByColumnId(id); - final GridColumnState columnState = column.getState(); - - if (columnState.hidden != hidden) { - columnState.hidden = hidden; - - final String diffStateKey = "columns"; - ConnectorTracker connectorTracker = getUI() - .getConnectorTracker(); - JsonObject diffState = connectorTracker - .getDiffState(LegacyGrid.this); - - assert diffState - .hasKey(diffStateKey) : "Field name has changed"; - Type type = null; - try { - type = (getState(false).getClass() - .getDeclaredField(diffStateKey) - .getGenericType()); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (SecurityException e) { - e.printStackTrace(); - } - EncodeResult encodeResult = JsonCodec.encode( - getState(false).columns, diffState, type, - connectorTracker); - - diffState.put(diffStateKey, encodeResult.getEncodedValue()); - - fireColumnVisibilityChangeEvent(column, hidden, - userOriginated); - } - } - - @Override - public void contextClick(int rowIndex, String rowKey, - String columnId, Section section, - MouseEventDetails details) { - Object itemId = null; - if (rowKey != null) { - itemId = getKeyMapper().get(rowKey); - } - fireEvent(new GridContextClickEvent(LegacyGrid.this, details, section, - rowIndex, itemId, getPropertyIdByColumnId(columnId))); - } - - @Override - public void columnResized(String id, double pixels) { - final Column column = getColumnByColumnId(id); - if (column != null && column.isResizable()) { - column.getState().width = pixels; - fireColumnResizeEvent(column, true); - markAsDirty(); - } - } - }); - - registerRpc(new EditorServerRpc() { - - @Override - public void bind(int rowIndex) { - try { - Object id = getContainerDataSource().getIdByIndex(rowIndex); - - final boolean opening = editedItemId == null; - - final boolean moving = !opening && !editedItemId.equals(id); - - final boolean allowMove = !isEditorBuffered() - && getEditorFieldGroup().isValid(); - - if (opening || !moving || allowMove) { - doBind(id); - } else { - failBind(null); - } - } catch (Exception e) { - failBind(e); - } - } - - private void doBind(Object id) { - editedItemId = id; - doEditItem(); - getEditorRpc().confirmBind(true); - } - - private void failBind(Exception e) { - if (e != null) { - handleError(e); - } - getEditorRpc().confirmBind(false); - } - - @Override - public void cancel(int rowIndex) { - try { - // For future proofing even though cannot currently fail - doCancelEditor(); - } catch (Exception e) { - handleError(e); - } - } - - @Override - public void save(int rowIndex) { - List errorColumnIds = null; - String errorMessage = null; - boolean success = false; - try { - saveEditor(); - success = true; - } catch (CommitException e) { - try { - CommitErrorEvent event = new CommitErrorEvent(LegacyGrid.this, - e); - getEditorErrorHandler().commitError(event); - - errorMessage = event.getUserErrorMessage(); - - errorColumnIds = new ArrayList<>(); - for (Column column : event.getErrorColumns()) { - errorColumnIds.add(column.state.id); - } - } catch (Exception ee) { - // A badly written error handler can throw an exception, - // which would lock up the Grid - handleError(ee); - } - } catch (Exception e) { - handleError(e); - } - getEditorRpc().confirmSave(success, errorMessage, - errorColumnIds); - } - - private void handleError(Exception e) { - com.vaadin.server.ErrorEvent.findErrorHandler(LegacyGrid.this) - .error(new ConnectorErrorEvent(LegacyGrid.this, e)); - } - }); - } - - @Override - public void beforeClientResponse(boolean initial) { - try { - header.sanityCheck(); - footer.sanityCheck(); - } catch (Exception e) { - e.printStackTrace(); - setComponentError(new ErrorMessage() { - - @Override - public ErrorLevel getErrorLevel() { - return ErrorLevel.CRITICAL; - } - - @Override - public String getFormattedHtmlMessage() { - return "Incorrectly merged cells"; - } - - }); - } - - super.beforeClientResponse(initial); - } - - /** - * Sets the grid data source. - *

- * - * Note Grid columns are based on properties and try to - * detect a correct converter for the data type. The columns are not - * reinitialized automatically if the container is changed, and if the same - * properties are present after container change, the columns are reused. - * Properties with same names, but different data types will lead to - * unpredictable behaviour. - * - * @param container - * The container data source. Cannot be null. - * @throws IllegalArgumentException - * if the data source is null - */ - public void setContainerDataSource(Container.Indexed container) { - defaultContainer = false; - internalSetContainerDataSource(container); - } - - private void internalSetContainerDataSource(Container.Indexed container) { - if (container == null) { - throw new IllegalArgumentException( - "Cannot set the datasource to null"); - } - if (datasource == container) { - return; - } - - // Remove old listeners - if (datasource instanceof PropertySetChangeNotifier) { - ((PropertySetChangeNotifier) datasource) - .removePropertySetChangeListener(propertyListener); - } - - if (datasourceExtension != null) { - removeExtension(datasourceExtension); - } - - // Remove old DetailComponentManager - if (detailComponentManager != null) { - detailComponentManager.remove(); - } - - resetEditor(); - - datasource = container; - - // - // Adjust sort order - // - - if (container instanceof Container.Sortable) { - - // If the container is sortable, go through the current sort order - // and match each item to the sortable properties of the new - // container. If the new container does not support an item in the - // current sort order, that item is removed from the current sort - // order list. - Collection sortableProps = ((Container.Sortable) getContainerDataSource()) - .getSortableContainerPropertyIds(); - - Iterator i = sortOrder.iterator(); - while (i.hasNext()) { - if (!sortableProps.contains(i.next().getPropertyId())) { - i.remove(); - } - } - - sort(false); - } else { - // Clear sorting order. Don't sort. - sortOrder.clear(); - } - - datasourceExtension = new RpcDataProviderExtension(container); - datasourceExtension.extend(this); - datasourceExtension.addDataGenerator(new RowDataGenerator()); - for (Extension e : getExtensions()) { - if (e instanceof DataGenerator) { - datasourceExtension.addDataGenerator((DataGenerator) e); - } - } - - if (detailComponentManager != null) { - detailComponentManager = new DetailComponentManager(this, - detailComponentManager.getDetailsGenerator()); - } else { - detailComponentManager = new DetailComponentManager(this); - } - - /* - * selectionModel == null when the invocation comes from the - * constructor. - */ - if (selectionModel != null) { - selectionModel.reset(); - } - - // Listen to changes in properties and remove columns if needed - if (datasource instanceof PropertySetChangeNotifier) { - ((PropertySetChangeNotifier) datasource) - .addPropertySetChangeListener(propertyListener); - } - - /* - * activeRowHandler will be updated by the client-side request that - * occurs on container change - no need to actively re-insert any - * ValueChangeListeners at this point. - */ - - setFrozenColumnCount(0); - - if (columns.isEmpty()) { - // Add columns - for (Object propertyId : datasource.getContainerPropertyIds()) { - Column column = appendColumn(propertyId); - - // Initial sorting is defined by container - if (datasource instanceof Sortable) { - column.setSortable(((Sortable) datasource) - .getSortableContainerPropertyIds() - .contains(propertyId)); - } else { - column.setSortable(false); - } - } - } else { - Collection properties = datasource.getContainerPropertyIds(); - for (Object property : columns.keySet()) { - if (!properties.contains(property)) { - throw new IllegalStateException( - "Found at least one column in Grid that does not exist in the given container: " - + property + " with the header \"" - + getColumn(property).getHeaderCaption() - + "\". " - + "Call removeAllColumns() before setContainerDataSource() if you want to reconfigure the columns based on the new container."); - } - - if (!(datasource instanceof Sortable) - || !((Sortable) datasource) - .getSortableContainerPropertyIds() - .contains(property)) { - columns.get(property).setSortable(false); - } - } - } - } - - /** - * Returns the grid data source. - * - * @return the container data source of the grid - */ - public Container.Indexed getContainerDataSource() { - return datasource; - } - - /** - * Returns a column based on the property id - * - * @param propertyId - * the property id of the column - * @return the column or null if not found - */ - public Column getColumn(Object propertyId) { - return columns.get(propertyId); - } - - /** - * Returns a copy of currently configures columns in their current visual - * order in this Grid. - * - * @return unmodifiable copy of current columns in visual order - */ - public List getColumns() { - List columns = new ArrayList<>(); - for (String columnId : getState(false).columnOrder) { - columns.add(getColumnByColumnId(columnId)); - } - return Collections.unmodifiableList(columns); - } - - /** - * Adds a new Column to Grid. Also adds the property to container with data - * type String, if property for column does not exist in it. Default value - * for the new property is an empty String. - *

- * Note that adding a new property is only done for the default container - * that Grid sets up with the default constructor. - * - * @param propertyId - * the property id of the new column - * @return the new column - * - * @throws IllegalStateException - * if column for given property already exists in this grid - */ - - public Column addColumn(Object propertyId) throws IllegalStateException { - if (datasource.getContainerPropertyIds().contains(propertyId) - && !columns.containsKey(propertyId)) { - appendColumn(propertyId); - } else if (defaultContainer) { - addColumnProperty(propertyId, String.class, ""); - } else { - if (columns.containsKey(propertyId)) { - throw new IllegalStateException( - "A column for property id '" + propertyId.toString() - + "' already exists in this grid"); - } else { - throw new IllegalStateException( - "Property id '" + propertyId.toString() - + "' does not exist in the container"); - } - } - - // Inform the data provider of this new column. - Column column = getColumn(propertyId); - List addedColumns = new ArrayList<>(); - addedColumns.add(column); - datasourceExtension.columnsAdded(addedColumns); - - return column; - } - - /** - * Adds a new Column to Grid. This function makes sure that the property - * with the given id and data type exists in the container. If property does - * not exists, it will be created. - *

- * Default value for the new property is 0 if type is Integer, Double and - * Float. If type is String, default value is an empty string. For all other - * types the default value is null. - *

- * Note that adding a new property is only done for the default container - * that Grid sets up with the default constructor. - * - * @param propertyId - * the property id of the new column - * @param type - * the data type for the new property - * @return the new column - * - * @throws IllegalStateException - * if column for given property already exists in this grid or - * property already exists in the container with wrong type - */ - public Column addColumn(Object propertyId, Class type) { - addColumnProperty(propertyId, type, null); - return getColumn(propertyId); - } - - protected void addColumnProperty(Object propertyId, Class type, - Object defaultValue) throws IllegalStateException { - if (!defaultContainer) { - throw new IllegalStateException( - "Container for this Grid is not a default container from Grid() constructor"); - } - - if (!columns.containsKey(propertyId)) { - if (!datasource.getContainerPropertyIds().contains(propertyId)) { - datasource.addContainerProperty(propertyId, type, defaultValue); - } else { - Property containerProperty = datasource.getContainerProperty( - datasource.firstItemId(), propertyId); - if (containerProperty.getType() == type) { - appendColumn(propertyId); - } else { - throw new IllegalStateException( - "DataSource already has the given property " - + propertyId + " with a different type"); - } - } - } else { - throw new IllegalStateException( - "Grid already has a column for property " + propertyId); - } - } - - /** - * Removes all columns from this Grid. - */ - public void removeAllColumns() { - List removed = new ArrayList<>(columns.values()); - Set properties = new HashSet<>(columns.keySet()); - for (Object propertyId : properties) { - removeColumn(propertyId); - } - datasourceExtension.columnsRemoved(removed); - } - - /** - * Used internally by the {@link LegacyGrid} to get a {@link Column} by - * referencing its generated state id. Also used by {@link Column} to verify - * if it has been detached from the {@link LegacyGrid}. - * - * @param columnId - * the client id generated for the column when the column is - * added to the grid - * @return the column with the id or null if not found - */ - Column getColumnByColumnId(String columnId) { - Object propertyId = getPropertyIdByColumnId(columnId); - return getColumn(propertyId); - } - - /** - * Used internally by the {@link LegacyGrid} to get a property id by referencing - * the columns generated state id. - * - * @param columnId - * The state id of the column - * @return The column instance or null if not found - */ - Object getPropertyIdByColumnId(String columnId) { - return columnKeys.get(columnId); - } - - /** - * Returns whether column reordering is allowed. Default value is - * false. - * - * @since 7.5.0 - * @return true if reordering is allowed - */ - public boolean isColumnReorderingAllowed() { - return getState(false).columnReorderingAllowed; - } - - /** - * Sets whether or not column reordering is allowed. Default value is - * false. - * - * @since 7.5.0 - * @param columnReorderingAllowed - * specifies whether column reordering is allowed - */ - public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { - if (isColumnReorderingAllowed() != columnReorderingAllowed) { - getState().columnReorderingAllowed = columnReorderingAllowed; - } - } - - @Override - protected GridState getState() { - return (GridState) super.getState(); - } - - @Override - protected GridState getState(boolean markAsDirty) { - return (GridState) super.getState(markAsDirty); - } - - /** - * Creates a new column based on a property id and appends it as the last - * column. - * - * @param datasourcePropertyId - * The property id of a property in the datasource - */ - private Column appendColumn(Object datasourcePropertyId) { - if (datasourcePropertyId == null) { - throw new IllegalArgumentException("Property id cannot be null"); - } - assert datasource.getContainerPropertyIds().contains( - datasourcePropertyId) : "Datasource should contain the property id"; - - GridColumnState columnState = new GridColumnState(); - columnState.id = columnKeys.key(datasourcePropertyId); - - Column column = new Column(this, columnState, datasourcePropertyId); - columns.put(datasourcePropertyId, column); - - getState().columns.add(columnState); - getState().columnOrder.add(columnState.id); - header.addColumn(datasourcePropertyId); - footer.addColumn(datasourcePropertyId); - - String humanFriendlyPropertyId = SharedUtil.propertyIdToHumanFriendly( - String.valueOf(datasourcePropertyId)); - column.setHeaderCaption(humanFriendlyPropertyId); - - if (datasource instanceof Sortable - && ((Sortable) datasource).getSortableContainerPropertyIds() - .contains(datasourcePropertyId)) { - column.setSortable(true); - } - - return column; - } - - /** - * Removes a column from Grid based on a property id. - * - * @param propertyId - * The property id of column to be removed - * - * @throws IllegalArgumentException - * if there is no column for given property id in this grid - */ - public void removeColumn(Object propertyId) - throws IllegalArgumentException { - if (!columns.keySet().contains(propertyId)) { - throw new IllegalArgumentException( - "There is no column for given property id " + propertyId); - } - - List removed = new ArrayList<>(); - removed.add(getColumn(propertyId)); - internalRemoveColumn(propertyId); - datasourceExtension.columnsRemoved(removed); - } - - private void internalRemoveColumn(Object propertyId) { - setEditorField(propertyId, null); - header.removeColumn(propertyId); - footer.removeColumn(propertyId); - Column column = columns.remove(propertyId); - getState().columnOrder.remove(columnKeys.key(propertyId)); - getState().columns.remove(column.getState()); - removeExtension(column.getRenderer()); - } - - /** - * Sets the columns and their order for the grid. Current columns whose - * property id is not in propertyIds are removed. Similarly, a column is - * added for any property id in propertyIds that has no corresponding column - * in this Grid. - * - * @since 7.5.0 - * - * @param propertyIds - * properties in the desired column order - */ - public void setColumns(Object... propertyIds) { - Set removePids = new HashSet<>(columns.keySet()); - removePids.removeAll(Arrays.asList(propertyIds)); - for (Object removePid : removePids) { - removeColumn(removePid); - } - Set addPids = new HashSet<>(Arrays.asList(propertyIds)); - addPids.removeAll(columns.keySet()); - for (Object propertyId : addPids) { - addColumn(propertyId); - } - setColumnOrder(propertyIds); - } - - /** - * Sets a new column order for the grid. All columns which are not ordered - * here will remain in the order they were before as the last columns of - * grid. - * - * @param propertyIds - * properties in the order columns should be - */ - public void setColumnOrder(Object... propertyIds) { - List columnOrder = new ArrayList<>(); - for (Object propertyId : propertyIds) { - if (columns.containsKey(propertyId)) { - columnOrder.add(columnKeys.key(propertyId)); - } else { - throw new IllegalArgumentException( - "Grid does not contain column for property " - + String.valueOf(propertyId)); - } - } - - List stateColumnOrder = getState().columnOrder; - if (stateColumnOrder.size() != columnOrder.size()) { - stateColumnOrder.removeAll(columnOrder); - columnOrder.addAll(stateColumnOrder); - } - getState().columnOrder = columnOrder; - fireColumnReorderEvent(false); - } - - /** - * Sets the number of frozen columns in this grid. Setting the count to 0 - * means that no data columns will be frozen, but the built-in selection - * checkbox column will still be frozen if it's in use. Setting the count to - * -1 will also disable the selection column. - *

- * The default value is 0. - * - * @param numberOfColumns - * the number of columns that should be frozen - * - * @throws IllegalArgumentException - * if the column count is < 0 or > the number of visible columns - */ - public void setFrozenColumnCount(int numberOfColumns) { - if (numberOfColumns < -1 || numberOfColumns > columns.size()) { - throw new IllegalArgumentException( - "count must be between -1 and the current number of columns (" - + columns.size() + "): " + numberOfColumns); - } - - getState().frozenColumnCount = numberOfColumns; - } - - /** - * Gets the number of frozen columns in this grid. 0 means that no data - * columns will be frozen, but the built-in selection checkbox column will - * still be frozen if it's in use. -1 means that not even the selection - * column is frozen. - *

- * NOTE: this count includes {@link Column#isHidden() hidden - * columns} in the count. - * - * @see #setFrozenColumnCount(int) - * - * @return the number of frozen columns - */ - public int getFrozenColumnCount() { - return getState(false).frozenColumnCount; - } - - /** - * Scrolls to a certain item, using {@link ScrollDestination#ANY}. - *

- * If the item has visible details, its size will also be taken into - * account. - * - * @param itemId - * id of item to scroll to. - * @throws IllegalArgumentException - * if the provided id is not recognized by the data source. - */ - public void scrollTo(Object itemId) throws IllegalArgumentException { - scrollTo(itemId, ScrollDestination.ANY); - } - - /** - * Scrolls to a certain item, using user-specified scroll destination. - *

- * If the item has visible details, its size will also be taken into - * account. - * - * @param itemId - * id of item to scroll to. - * @param destination - * value specifying desired position of scrolled-to row. - * @throws IllegalArgumentException - * if the provided id is not recognized by the data source. - */ - public void scrollTo(Object itemId, ScrollDestination destination) - throws IllegalArgumentException { - - int row = datasource.indexOfId(itemId); - - if (row == -1) { - throw new IllegalArgumentException( - "Item with specified ID does not exist in data source"); - } - - GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); - clientRPC.scrollToRow(row, destination); - } - - /** - * Scrolls to the beginning of the first data row. - */ - public void scrollToStart() { - GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); - clientRPC.scrollToStart(); - } - - /** - * Scrolls to the end of the last data row. - */ - public void scrollToEnd() { - GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class); - clientRPC.scrollToEnd(); - } - - /** - * Sets the number of rows that should be visible in Grid's body, while - * {@link #getHeightMode()} is {@link HeightMode#ROW}. - *

- * If Grid is currently not in {@link HeightMode#ROW}, the given value is - * remembered, and applied once the mode is applied. - * - * @param rows - * The height in terms of number of rows displayed in Grid's - * body. If Grid doesn't contain enough rows, white space is - * displayed instead. If null is given, then Grid's - * height is undefined - * @throws IllegalArgumentException - * if {@code rows} is zero or less - * @throws IllegalArgumentException - * if {@code rows} is {@link Double#isInfinite(double) infinite} - * @throws IllegalArgumentException - * if {@code rows} is {@link Double#isNaN(double) NaN} - */ - public void setHeightByRows(double rows) { - if (rows <= 0.0d) { - throw new IllegalArgumentException( - "More than zero rows must be shown."); - } else if (Double.isInfinite(rows)) { - throw new IllegalArgumentException( - "Grid doesn't support infinite heights"); - } else if (Double.isNaN(rows)) { - throw new IllegalArgumentException("NaN is not a valid row count"); - } - - getState().heightByRows = rows; - } - - /** - * Gets the amount of rows in Grid's body that are shown, while - * {@link #getHeightMode()} is {@link HeightMode#ROW}. - * - * @return the amount of rows that are being shown in Grid's body - * @see #setHeightByRows(double) - */ - public double getHeightByRows() { - return getState(false).heightByRows; - } - - /** - * {@inheritDoc} - *

- * Note: This method will change the widget's size in the browser - * only if {@link #getHeightMode()} returns {@link HeightMode#CSS}. - * - * @see #setHeightMode(HeightMode) - */ - @Override - public void setHeight(float height, Unit unit) { - super.setHeight(height, unit); - } - - /** - * Defines the mode in which the Grid widget's height is calculated. - *

- * If {@link HeightMode#CSS} is given, Grid will respect the values given - * via a {@code setHeight}-method, and behave as a traditional Component. - *

- * If {@link HeightMode#ROW} is given, Grid will make sure that the body - * will display as many rows as {@link #getHeightByRows()} defines. - * Note: If headers/footers are inserted or removed, the widget - * will resize itself to still display the required amount of rows in its - * body. It also takes the horizontal scrollbar into account. - * - * @param heightMode - * the mode in to which Grid should be set - */ - public void setHeightMode(HeightMode heightMode) { - /* - * This method is a workaround for the fact that Vaadin re-applies - * widget dimensions (height/width) on each state change event. The - * original design was to have setHeight and setHeightByRow be equals, - * and whichever was called the latest was considered in effect. - * - * But, because of Vaadin always calling setHeight on the widget, this - * approach doesn't work. - */ - - getState().heightMode = heightMode; - } - - /** - * Returns the current {@link HeightMode} the Grid is in. - *

- * Defaults to {@link HeightMode#CSS}. - * - * @return the current HeightMode - */ - public HeightMode getHeightMode() { - return getState(false).heightMode; - } - - /* Selection related methods: */ - - /** - * Takes a new {@link SelectionModel} into use. - *

- * The SelectionModel that is previously in use will have all its items - * deselected. - *

- * If the given SelectionModel is already in use, this method does nothing. - * - * @param selectionModel - * the new SelectionModel to use - * @throws IllegalArgumentException - * if {@code selectionModel} is null - */ - public void setSelectionModel(SelectionModel selectionModel) - throws IllegalArgumentException { - if (selectionModel == null) { - throw new IllegalArgumentException( - "Selection model may not be null"); - } - - if (this.selectionModel != selectionModel) { - // this.selectionModel is null on init - if (this.selectionModel != null) { - this.selectionModel.remove(); - } - - this.selectionModel = selectionModel; - selectionModel.setGrid(this); - } - } - - /** - * Returns the currently used {@link SelectionModel}. - * - * @return the currently used SelectionModel - */ - public SelectionModel getSelectionModel() { - return selectionModel; - } - - /** - * Sets the Grid's selection mode. - *

- * Grid supports three selection modes: multiselect, single select and no - * selection, and this is a convenience method for choosing between one of - * them. - *

- * Technically, this method is a shortcut that can be used instead of - * calling {@code setSelectionModel} with a specific SelectionModel - * instance. Grid comes with three built-in SelectionModel classes, and the - * {@link SelectionMode} enum represents each of them. - *

- * Essentially, the two following method calls are equivalent: - *

- *

-     * grid.setSelectionMode(SelectionMode.MULTI);
-     * grid.setSelectionModel(new MultiSelectionMode());
-     * 
- * - * - * @param selectionMode - * the selection mode to switch to - * @return The {@link SelectionModel} instance that was taken into use - * @throws IllegalArgumentException - * if {@code selectionMode} is null - * @see SelectionModel - */ - public SelectionModel setSelectionMode(final SelectionMode selectionMode) - throws IllegalArgumentException { - if (selectionMode == null) { - throw new IllegalArgumentException( - "selection mode may not be null"); - } - final SelectionModel newSelectionModel = selectionMode.createModel(); - setSelectionModel(newSelectionModel); - return newSelectionModel; - } - - /** - * Checks whether an item is selected or not. - * - * @param itemId - * the item id to check for - * @return true iff the item is selected - */ - // keep this javadoc in sync with SelectionModel.isSelected - public boolean isSelected(Object itemId) { - return selectionModel.isSelected(itemId); - } - - /** - * Returns a collection of all the currently selected itemIds. - *

- * This method is a shorthand that delegates to the - * {@link #getSelectionModel() selection model}. - * - * @return a collection of all the currently selected itemIds - */ - // keep this javadoc in sync with SelectionModel.getSelectedRows - public Collection getSelectedRows() { - return getSelectionModel().getSelectedRows(); - } - - /** - * Gets the item id of the currently selected item. - *

- * This method is a shorthand that delegates to the - * {@link #getSelectionModel() selection model}. Only - * {@link SelectionModel.Single} is supported. - * - * @return the item id of the currently selected item, or null - * if nothing is selected - * @throws IllegalStateException - * if the selection model does not implement - * {@code SelectionModel.Single} - */ - // keep this javadoc in sync with SelectionModel.Single.getSelectedRow - public Object getSelectedRow() throws IllegalStateException { - if (selectionModel instanceof SelectionModel.Single) { - return ((SelectionModel.Single) selectionModel).getSelectedRow(); - } else if (selectionModel instanceof SelectionModel.Multi) { - throw new IllegalStateException("Cannot get unique selected row: " - + "Grid is in multiselect mode " - + "(the current selection model is " - + selectionModel.getClass().getName() + ")."); - } else if (selectionModel instanceof SelectionModel.None) { - throw new IllegalStateException( - "Cannot get selected row: " + "Grid selection is disabled " - + "(the current selection model is " - + selectionModel.getClass().getName() + ")."); - } else { - throw new IllegalStateException("Cannot get selected row: " - + "Grid selection model does not implement " - + SelectionModel.Single.class.getName() + " or " - + SelectionModel.Multi.class.getName() - + "(the current model is " - + selectionModel.getClass().getName() + ")."); - } - } - - /** - * Marks an item as selected. - *

- * This method is a shorthand that delegates to the - * {@link #getSelectionModel() selection model}. Only - * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are - * supported. - * - * @param itemId - * the itemId to mark as selected - * @return true if the selection state changed, - * false if the itemId already was selected - * @throws IllegalArgumentException - * if the {@code itemId} doesn't exist in the currently active - * Container - * @throws IllegalStateException - * if the selection was illegal. One such reason might be that - * the implementation already had an item selected, and that - * needs to be explicitly deselected before re-selecting - * something. - * @throws IllegalStateException - * if the selection model does not implement - * {@code SelectionModel.Single} or {@code SelectionModel.Multi} - */ - // keep this javadoc in sync with SelectionModel.Single.select - public boolean select(Object itemId) - throws IllegalArgumentException, IllegalStateException { - if (selectionModel instanceof SelectionModel.Single) { - return ((SelectionModel.Single) selectionModel).select(itemId); - } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel).select(itemId); - } else if (selectionModel instanceof SelectionModel.None) { - throw new IllegalStateException("Cannot select row '" + itemId - + "': Grid selection is disabled " - + "(the current selection model is " - + selectionModel.getClass().getName() + ")."); - } else { - throw new IllegalStateException("Cannot select row '" + itemId - + "': Grid selection model does not implement " - + SelectionModel.Single.class.getName() + " or " - + SelectionModel.Multi.class.getName() - + "(the current model is " - + selectionModel.getClass().getName() + ")."); - } - } - - /** - * Marks an item as unselected. - *

- * This method is a shorthand that delegates to the - * {@link #getSelectionModel() selection model}. Only - * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are - * supported. - * - * @param itemId - * the itemId to remove from being selected - * @return true if the selection state changed, - * false if the itemId was already selected - * @throws IllegalArgumentException - * if the {@code itemId} doesn't exist in the currently active - * Container - * @throws IllegalStateException - * if the deselection was illegal. One such reason might be that - * the implementation requires one or more items to be selected - * at all times. - * @throws IllegalStateException - * if the selection model does not implement - * {@code SelectionModel.Single} or {code SelectionModel.Multi} - */ - // keep this javadoc in sync with SelectionModel.Single.deselect - public boolean deselect(Object itemId) throws IllegalStateException { - if (selectionModel instanceof SelectionModel.Single) { - if (isSelected(itemId)) { - return ((SelectionModel.Single) selectionModel).select(null); - } - return false; - } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel).deselect(itemId); - } else if (selectionModel instanceof SelectionModel.None) { - throw new IllegalStateException("Cannot deselect row '" + itemId - + "': Grid selection is disabled " - + "(the current selection model is " - + selectionModel.getClass().getName() + ")."); - } else { - throw new IllegalStateException("Cannot deselect row '" + itemId - + "': Grid selection model does not implement " - + SelectionModel.Single.class.getName() + " or " - + SelectionModel.Multi.class.getName() - + "(the current model is " - + selectionModel.getClass().getName() + ")."); - } - } - - /** - * Marks all items as unselected. - *

- * This method is a shorthand that delegates to the - * {@link #getSelectionModel() selection model}. Only - * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are - * supported. - * - * @return true if the selection state changed, - * false if the itemId was already selected - * @throws IllegalStateException - * if the deselection was illegal. One such reason might be that - * the implementation requires one or more items to be selected - * at all times. - * @throws IllegalStateException - * if the selection model does not implement - * {@code SelectionModel.Single} or {code SelectionModel.Multi} - */ - public boolean deselectAll() throws IllegalStateException { - if (selectionModel instanceof SelectionModel.Single) { - if (getSelectedRow() != null) { - return deselect(getSelectedRow()); - } - return false; - } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel).deselectAll(); - } else if (selectionModel instanceof SelectionModel.None) { - throw new IllegalStateException( - "Cannot deselect all rows" + ": Grid selection is disabled " - + "(the current selection model is " - + selectionModel.getClass().getName() + ")."); - } else { - throw new IllegalStateException("Cannot deselect all rows:" - + " Grid selection model does not implement " - + SelectionModel.Single.class.getName() + " or " - + SelectionModel.Multi.class.getName() - + "(the current model is " - + selectionModel.getClass().getName() + ")."); - } - } - - /** - * Fires a selection change event. - *

- * Note: This is not a method that should be called by - * application logic. This method is publicly accessible only so that - * {@link SelectionModel SelectionModels} would be able to inform Grid of - * these events. - * - * @param newSelection - * the selection that was added by this event - * @param oldSelection - * the selection that was removed by this event - */ - public void fireSelectionEvent(Collection oldSelection, - Collection newSelection) { - fireEvent(new SelectionEvent(this, oldSelection, newSelection)); - } - - @Override - public void addSelectionListener(SelectionListener listener) { - addListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD); - } - - @Override - public void removeSelectionListener(SelectionListener listener) { - removeListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD); - } - - private void fireColumnReorderEvent(boolean userOriginated) { - fireEvent(new ColumnReorderEvent(this, userOriginated)); - } - - /** - * Registers a new column reorder listener. - * - * @since 7.5.0 - * @param listener - * the listener to register - */ - public void addColumnReorderListener(ColumnReorderListener listener) { - addListener(ColumnReorderEvent.class, listener, COLUMN_REORDER_METHOD); - } - - /** - * Removes a previously registered column reorder listener. - * - * @since 7.5.0 - * @param listener - * the listener to remove - */ - public void removeColumnReorderListener(ColumnReorderListener listener) { - removeListener(ColumnReorderEvent.class, listener, - COLUMN_REORDER_METHOD); - } - - private void fireColumnResizeEvent(Column column, boolean userOriginated) { - fireEvent(new ColumnResizeEvent(this, column, userOriginated)); - } - - /** - * Registers a new column resize listener. - * - * @param listener - * the listener to register - */ - public void addColumnResizeListener(ColumnResizeListener listener) { - addListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD); - } - - /** - * Removes a previously registered column resize listener. - * - * @param listener - * the listener to remove - */ - public void removeColumnResizeListener(ColumnResizeListener listener) { - removeListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD); - } - - /** - * Gets the {@link KeyMapper } being used by the data source. - * - * @return the key mapper being used by the data source - */ - KeyMapper getKeyMapper() { - return datasourceExtension.getKeyMapper(); - } - - /** - * Adds a renderer to this grid's connector hierarchy. - * - * @param renderer - * the renderer to add - */ - void addRenderer(Renderer renderer) { - addExtension(renderer); - } - - /** - * Sets the current sort order using the fluid Sort API. Read the - * documentation for {@link Sort} for more information. - *

- * Note: Sorting by a property that has no column in Grid will hide - * all possible sorting indicators. - * - * @param s - * a sort instance - * - * @throws IllegalStateException - * if container is not sortable (does not implement - * Container.Sortable) - * @throws IllegalArgumentException - * if trying to sort by non-existing property - */ - public void sort(Sort s) { - setSortOrder(s.build()); - } - - /** - * Sort this Grid in ascending order by a specified property. - *

- * Note: Sorting by a property that has no column in Grid will hide - * all possible sorting indicators. - * - * @param propertyId - * a property ID - * - * @throws IllegalStateException - * if container is not sortable (does not implement - * Container.Sortable) - * @throws IllegalArgumentException - * if trying to sort by non-existing property - */ - public void sort(Object propertyId) { - sort(propertyId, SortDirection.ASCENDING); - } - - /** - * Sort this Grid in user-specified {@link SortOrder} by a property. - *

- * Note: Sorting by a property that has no column in Grid will hide - * all possible sorting indicators. - * - * @param propertyId - * a property ID - * @param direction - * a sort order value (ascending/descending) - * - * @throws IllegalStateException - * if container is not sortable (does not implement - * Container.Sortable) - * @throws IllegalArgumentException - * if trying to sort by non-existing property - */ - public void sort(Object propertyId, SortDirection direction) { - sort(Sort.by(propertyId, direction)); - } - - /** - * Clear the current sort order, and re-sort the grid. - */ - public void clearSortOrder() { - sortOrder.clear(); - sort(false); - } - - /** - * Sets the sort order to use. - *

- * Note: Sorting by a property that has no column in Grid will hide - * all possible sorting indicators. - * - * @param order - * a sort order list. - * - * @throws IllegalStateException - * if container is not sortable (does not implement - * Container.Sortable) - * @throws IllegalArgumentException - * if order is null or trying to sort by non-existing property - */ - public void setSortOrder(List order) { - setSortOrder(order, false); - } - - private void setSortOrder(List order, boolean userOriginated) - throws IllegalStateException, IllegalArgumentException { - if (!(getContainerDataSource() instanceof Container.Sortable)) { - throw new IllegalStateException( - "Attached container is not sortable (does not implement Container.Sortable)"); - } - - if (order == null) { - throw new IllegalArgumentException("Order list may not be null!"); - } - - sortOrder.clear(); - - Collection sortableProps = ((Container.Sortable) getContainerDataSource()) - .getSortableContainerPropertyIds(); - - for (SortOrder o : order) { - if (!sortableProps.contains(o.getPropertyId())) { - throw new IllegalArgumentException("Property " - + o.getPropertyId() - + " does not exist or is not sortable in the current container"); - } - } - - sortOrder.addAll(order); - sort(userOriginated); - } - - /** - * Get the current sort order list. - * - * @return a sort order list - */ - public List getSortOrder() { - return Collections.unmodifiableList(sortOrder); - } - - /** - * Apply sorting to data source. - */ - private void sort(boolean userOriginated) { - - Container c = getContainerDataSource(); - if (c instanceof Container.Sortable) { - Container.Sortable cs = (Container.Sortable) c; - - final int items = sortOrder.size(); - Object[] propertyIds = new Object[items]; - boolean[] directions = new boolean[items]; - - SortDirection[] stateDirs = new SortDirection[items]; - - for (int i = 0; i < items; ++i) { - SortOrder order = sortOrder.get(i); - - stateDirs[i] = order.getDirection(); - propertyIds[i] = order.getPropertyId(); - switch (order.getDirection()) { - case ASCENDING: - directions[i] = true; - break; - case DESCENDING: - directions[i] = false; - break; - default: - throw new IllegalArgumentException("getDirection() of " - + order + " returned an unexpected value"); - } - } - - cs.sort(propertyIds, directions); - - if (columns.keySet().containsAll(Arrays.asList(propertyIds))) { - String[] columnKeys = new String[items]; - for (int i = 0; i < items; ++i) { - columnKeys[i] = this.columnKeys.key(propertyIds[i]); - } - getState().sortColumns = columnKeys; - getState(false).sortDirs = stateDirs; - } else { - // Not all sorted properties are in Grid. Remove any indicators. - getState().sortColumns = new String[] {}; - getState(false).sortDirs = new SortDirection[] {}; - } - fireEvent(new SortEvent(this, new ArrayList<>(sortOrder), - userOriginated)); - } else { - throw new IllegalStateException( - "Container is not sortable (does not implement Container.Sortable)"); - } - } - - /** - * Adds a sort order change listener that gets notified when the sort order - * changes. - * - * @param listener - * the sort order change listener to add - */ - @Override - public void addSortListener(SortListener listener) { - addListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD); - } - - /** - * Removes a sort order change listener previously added using - * {@link #addSortListener(SortListener)}. - * - * @param listener - * the sort order change listener to remove - */ - @Override - public void removeSortListener(SortListener listener) { - removeListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD); - } - - /* Grid Headers */ - - /** - * Returns the header section of this grid. The default header contains a - * single row displaying the column captions. - * - * @return the header - */ - protected Header getHeader() { - return header; - } - - /** - * Gets the header row at given index. - * - * @param rowIndex - * 0 based index for row. Counted from top to bottom - * @return header row at given index - * @throws IllegalArgumentException - * if no row exists at given index - */ - public HeaderRow getHeaderRow(int rowIndex) { - return header.getRow(rowIndex); - } - - /** - * Inserts a new row at the given position to the header section. Shifts the - * row currently at that position and any subsequent rows down (adds one to - * their indices). - * - * @param index - * the position at which to insert the row - * @return the new row - * - * @throws IllegalArgumentException - * if the index is less than 0 or greater than row count - * @see #appendHeaderRow() - * @see #prependHeaderRow() - * @see #removeHeaderRow(HeaderRow) - * @see #removeHeaderRow(int) - */ - public HeaderRow addHeaderRowAt(int index) { - return header.addRowAt(index); - } - - /** - * Adds a new row at the bottom of the header section. - * - * @return the new row - * @see #prependHeaderRow() - * @see #addHeaderRowAt(int) - * @see #removeHeaderRow(HeaderRow) - * @see #removeHeaderRow(int) - */ - public HeaderRow appendHeaderRow() { - return header.appendRow(); - } - - /** - * Returns the current default row of the header section. The default row is - * a special header row providing a user interface for sorting columns. - * Setting a header text for column updates cells in the default header. - * - * @return the default row or null if no default row set - */ - public HeaderRow getDefaultHeaderRow() { - return header.getDefaultRow(); - } - - /** - * Gets the row count for the header section. - * - * @return row count - */ - public int getHeaderRowCount() { - return header.getRowCount(); - } - - /** - * Adds a new row at the top of the header section. - * - * @return the new row - * @see #appendHeaderRow() - * @see #addHeaderRowAt(int) - * @see #removeHeaderRow(HeaderRow) - * @see #removeHeaderRow(int) - */ - public HeaderRow prependHeaderRow() { - return header.prependRow(); - } - - /** - * Removes the given row from the header section. - * - * @param row - * the row to be removed - * - * @throws IllegalArgumentException - * if the row does not exist in this section - * @see #removeHeaderRow(int) - * @see #addHeaderRowAt(int) - * @see #appendHeaderRow() - * @see #prependHeaderRow() - */ - public void removeHeaderRow(HeaderRow row) { - header.removeRow(row); - } - - /** - * Removes the row at the given position from the header section. - * - * @param rowIndex - * the position of the row - * - * @throws IllegalArgumentException - * if no row exists at given index - * @see #removeHeaderRow(HeaderRow) - * @see #addHeaderRowAt(int) - * @see #appendHeaderRow() - * @see #prependHeaderRow() - */ - public void removeHeaderRow(int rowIndex) { - header.removeRow(rowIndex); - } - - /** - * Sets the default row of the header. The default row is a special header - * row providing a user interface for sorting columns. - * - * @param row - * the new default row, or null for no default row - * - * @throws IllegalArgumentException - * header does not contain the row - */ - public void setDefaultHeaderRow(HeaderRow row) { - header.setDefaultRow(row); - } - - /** - * Sets the visibility of the header section. - * - * @param visible - * true to show header section, false to hide - */ - public void setHeaderVisible(boolean visible) { - header.setVisible(visible); - } - - /** - * Returns the visibility of the header section. - * - * @return true if visible, false otherwise. - */ - public boolean isHeaderVisible() { - return header.isVisible(); - } - - /* Grid Footers */ - - /** - * Returns the footer section of this grid. The default header contains a - * single row displaying the column captions. - * - * @return the footer - */ - protected Footer getFooter() { - return footer; - } - - /** - * Gets the footer row at given index. - * - * @param rowIndex - * 0 based index for row. Counted from top to bottom - * @return footer row at given index - * @throws IllegalArgumentException - * if no row exists at given index - */ - public FooterRow getFooterRow(int rowIndex) { - return footer.getRow(rowIndex); - } - - /** - * Inserts a new row at the given position to the footer section. Shifts the - * row currently at that position and any subsequent rows down (adds one to - * their indices). - * - * @param index - * the position at which to insert the row - * @return the new row - * - * @throws IllegalArgumentException - * if the index is less than 0 or greater than row count - * @see #appendFooterRow() - * @see #prependFooterRow() - * @see #removeFooterRow(FooterRow) - * @see #removeFooterRow(int) - */ - public FooterRow addFooterRowAt(int index) { - return footer.addRowAt(index); - } - - /** - * Adds a new row at the bottom of the footer section. - * - * @return the new row - * @see #prependFooterRow() - * @see #addFooterRowAt(int) - * @see #removeFooterRow(FooterRow) - * @see #removeFooterRow(int) - */ - public FooterRow appendFooterRow() { - return footer.appendRow(); - } - - /** - * Gets the row count for the footer. - * - * @return row count - */ - public int getFooterRowCount() { - return footer.getRowCount(); - } - - /** - * Adds a new row at the top of the footer section. - * - * @return the new row - * @see #appendFooterRow() - * @see #addFooterRowAt(int) - * @see #removeFooterRow(FooterRow) - * @see #removeFooterRow(int) - */ - public FooterRow prependFooterRow() { - return footer.prependRow(); - } - - /** - * Removes the given row from the footer section. - * - * @param row - * the row to be removed - * - * @throws IllegalArgumentException - * if the row does not exist in this section - * @see #removeFooterRow(int) - * @see #addFooterRowAt(int) - * @see #appendFooterRow() - * @see #prependFooterRow() - */ - public void removeFooterRow(FooterRow row) { - footer.removeRow(row); - } - - /** - * Removes the row at the given position from the footer section. - * - * @param rowIndex - * the position of the row - * - * @throws IllegalArgumentException - * if no row exists at given index - * @see #removeFooterRow(FooterRow) - * @see #addFooterRowAt(int) - * @see #appendFooterRow() - * @see #prependFooterRow() - */ - public void removeFooterRow(int rowIndex) { - footer.removeRow(rowIndex); - } - - /** - * Sets the visibility of the footer section. - * - * @param visible - * true to show footer section, false to hide - */ - public void setFooterVisible(boolean visible) { - footer.setVisible(visible); - } - - /** - * Returns the visibility of the footer section. - * - * @return true if visible, false otherwise. - */ - public boolean isFooterVisible() { - return footer.isVisible(); - } - - private void addComponent(Component c) { - extensionComponents.add(c); - c.setParent(this); - markAsDirty(); - } - - private void removeComponent(Component c) { - extensionComponents.remove(c); - c.setParent(null); - markAsDirty(); - } - - @Override - public Iterator iterator() { - // This is a hash set to avoid adding header/footer components inside - // merged cells multiple times - LinkedHashSet componentList = new LinkedHashSet<>(); - - Header header = getHeader(); - for (int i = 0; i < header.getRowCount(); ++i) { - HeaderRow row = header.getRow(i); - for (Object propId : columns.keySet()) { - HeaderCell cell = row.getCell(propId); - if (cell.getCellState().type == GridStaticCellType.WIDGET) { - componentList.add(cell.getComponent()); - } - } - } - - Footer footer = getFooter(); - for (int i = 0; i < footer.getRowCount(); ++i) { - FooterRow row = footer.getRow(i); - for (Object propId : columns.keySet()) { - FooterCell cell = row.getCell(propId); - if (cell.getCellState().type == GridStaticCellType.WIDGET) { - componentList.add(cell.getComponent()); - } - } - } - - componentList.addAll(getEditorFields()); - - componentList.addAll(extensionComponents); - - return componentList.iterator(); - } - - @Override - public boolean isRendered(Component childComponent) { - if (getEditorFields().contains(childComponent)) { - // Only render editor fields if the editor is open - return isEditorActive(); - } else { - // TODO Header and footer components should also only be rendered if - // the header/footer is visible - return true; - } - } - - EditorClientRpc getEditorRpc() { - return getRpcProxy(EditorClientRpc.class); - } - - /** - * Sets the {@code CellDescriptionGenerator} instance for generating - * optional descriptions (tooltips) for individual Grid cells. If a - * {@link RowDescriptionGenerator} is also set, the row description it - * generates is displayed for cells for which {@code generator} returns - * null. - * - * @param generator - * the description generator to use or {@code null} to remove a - * previously set generator if any - * - * @see #setRowDescriptionGenerator(RowDescriptionGenerator) - * - * @since 7.6 - */ - public void setCellDescriptionGenerator( - CellDescriptionGenerator generator) { - cellDescriptionGenerator = generator; - getState().hasDescriptions = (generator != null - || rowDescriptionGenerator != null); - datasourceExtension.refreshCache(); - } - - /** - * Returns the {@code CellDescriptionGenerator} instance used to generate - * descriptions (tooltips) for Grid cells. - * - * @return the description generator or {@code null} if no generator is set - * - * @since 7.6 - */ - public CellDescriptionGenerator getCellDescriptionGenerator() { - return cellDescriptionGenerator; - } - - /** - * Sets the {@code RowDescriptionGenerator} instance for generating optional - * descriptions (tooltips) for Grid rows. If a - * {@link CellDescriptionGenerator} is also set, the row description - * generated by {@code generator} is used for cells for which the cell - * description generator returns null. - * - * - * @param generator - * the description generator to use or {@code null} to remove a - * previously set generator if any - * - * @see #setCellDescriptionGenerator(CellDescriptionGenerator) - * - * @since 7.6 - */ - public void setRowDescriptionGenerator(RowDescriptionGenerator generator) { - rowDescriptionGenerator = generator; - getState().hasDescriptions = (generator != null - || cellDescriptionGenerator != null); - datasourceExtension.refreshCache(); - } - - /** - * Returns the {@code RowDescriptionGenerator} instance used to generate - * descriptions (tooltips) for Grid rows - * - * @return the description generator or {@code} null if no generator is set - * - * @since 7.6 - */ - public RowDescriptionGenerator getRowDescriptionGenerator() { - return rowDescriptionGenerator; - } - - /** - * Sets the style generator that is used for generating styles for cells - * - * @param cellStyleGenerator - * the cell style generator to set, or null to - * remove a previously set generator - */ - public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) { - this.cellStyleGenerator = cellStyleGenerator; - datasourceExtension.refreshCache(); - } - - /** - * Gets the style generator that is used for generating styles for cells - * - * @return the cell style generator, or null if no generator is - * set - */ - public CellStyleGenerator getCellStyleGenerator() { - return cellStyleGenerator; - } - - /** - * Sets the style generator that is used for generating styles for rows - * - * @param rowStyleGenerator - * the row style generator to set, or null to remove - * a previously set generator - */ - public void setRowStyleGenerator(RowStyleGenerator rowStyleGenerator) { - this.rowStyleGenerator = rowStyleGenerator; - datasourceExtension.refreshCache(); - } - - /** - * Gets the style generator that is used for generating styles for rows - * - * @return the row style generator, or null if no generator is - * set - */ - public RowStyleGenerator getRowStyleGenerator() { - return rowStyleGenerator; - } - - /** - * Adds a row to the underlying container. The order of the parameters - * should match the current visible column order. - *

- * Please note that it's generally only safe to use this method during - * initialization. After Grid has been initialized and the visible column - * order might have been changed, it's better to instead add items directly - * to the underlying container and use {@link Item#getItemProperty(Object)} - * to make sure each value is assigned to the intended property. - * - * @param values - * the cell values of the new row, in the same order as the - * visible column order, not null. - * @return the item id of the new row - * @throws IllegalArgumentException - * if values is null - * @throws IllegalArgumentException - * if its length does not match the number of visible columns - * @throws IllegalArgumentException - * if a parameter value is not an instance of the corresponding - * property type - * @throws UnsupportedOperationException - * if the container does not support adding new items - */ - public Object addRow(Object... values) { - if (values == null) { - throw new IllegalArgumentException("Values cannot be null"); - } - - Indexed dataSource = getContainerDataSource(); - List columnOrder = getState(false).columnOrder; - - if (values.length != columnOrder.size()) { - throw new IllegalArgumentException( - "There are " + columnOrder.size() + " visible columns, but " - + values.length + " cell values were provided."); - } - - // First verify all parameter types - for (int i = 0; i < columnOrder.size(); i++) { - Object propertyId = getPropertyIdByColumnId(columnOrder.get(i)); - - Class propertyType = dataSource.getType(propertyId); - if (values[i] != null && !propertyType.isInstance(values[i])) { - throw new IllegalArgumentException("Parameter " + i + "(" - + values[i] + ") is not an instance of " - + propertyType.getCanonicalName()); - } - } - - Object itemId = dataSource.addItem(); - try { - Item item = dataSource.getItem(itemId); - for (int i = 0; i < columnOrder.size(); i++) { - Object propertyId = getPropertyIdByColumnId(columnOrder.get(i)); - Property property = item.getItemProperty(propertyId); - property.setValue(values[i]); - } - } catch (RuntimeException e) { - try { - dataSource.removeItem(itemId); - } catch (Exception e2) { - getLogger().log(Level.SEVERE, - "Error recovering from exception in addRow", e); - } - throw e; - } - - return itemId; - } - - private static Logger getLogger() { - return Logger.getLogger(LegacyGrid.class.getName()); - } - - /** - * Sets whether or not the item editor UI is enabled for this grid. When the - * editor is enabled, the user can open it by double-clicking a row or - * hitting enter when a row is focused. The editor can also be opened - * programmatically using the {@link #editItem(Object)} method. - * - * @param isEnabled - * true to enable the feature, false - * otherwise - * @throws IllegalStateException - * if an item is currently being edited - * - * @see #getEditedItemId() - */ - public void setEditorEnabled(boolean isEnabled) - throws IllegalStateException { - if (isEditorActive()) { - throw new IllegalStateException( - "Cannot disable the editor while an item (" - + getEditedItemId() + ") is being edited"); - } - if (isEditorEnabled() != isEnabled) { - getState().editorEnabled = isEnabled; - } - } - - /** - * Checks whether the item editor UI is enabled for this grid. - * - * @return true iff the editor is enabled for this grid - * - * @see #setEditorEnabled(boolean) - * @see #getEditedItemId() - */ - public boolean isEditorEnabled() { - return getState(false).editorEnabled; - } - - /** - * Gets the id of the item that is currently being edited. - * - * @return the id of the item that is currently being edited, or - * null if no item is being edited at the moment - */ - public Object getEditedItemId() { - return editedItemId; - } - - /** - * Gets the field group that is backing the item editor of this grid. - * - * @return the backing field group - */ - public FieldGroup getEditorFieldGroup() { - return editorFieldGroup; - } - - /** - * Sets the field group that is backing the item editor of this grid. - * - * @param fieldGroup - * the backing field group - * - * @throws IllegalStateException - * if the editor is currently active - */ - public void setEditorFieldGroup(FieldGroup fieldGroup) { - if (isEditorActive()) { - throw new IllegalStateException( - "Cannot change field group while an item (" - + getEditedItemId() + ") is being edited"); - } - editorFieldGroup = fieldGroup; - } - - /** - * Returns whether an item is currently being edited in the editor. - * - * @return true iff the editor is open - */ - public boolean isEditorActive() { - return editorActive; - } - - private void checkColumnExists(Object propertyId) { - if (getColumn(propertyId) == null) { - throw new IllegalArgumentException( - "There is no column with the property id " + propertyId); - } - } - - private LegacyField getEditorField(Object propertyId) { - checkColumnExists(propertyId); - - if (!getColumn(propertyId).isEditable()) { - return null; - } - - LegacyField editor = editorFieldGroup.getField(propertyId); - - try { - if (editor == null) { - editor = editorFieldGroup.buildAndBind(propertyId); - } - } finally { - if (editor == null) { - editor = editorFieldGroup.getField(propertyId); - } - - if (editor != null && editor.getParent() != LegacyGrid.this) { - assert editor.getParent() == null; - editor.setParent(this); - } - } - return editor; - } - - /** - * Opens the editor interface for the provided item. Scrolls the Grid to - * bring the item to view if it is not already visible. - * - * Note that any cell content rendered by a WidgetRenderer will not be - * visible in the editor row. - * - * @param itemId - * the id of the item to edit - * @throws IllegalStateException - * if the editor is not enabled or already editing an item in - * buffered mode - * @throws IllegalArgumentException - * if the {@code itemId} is not in the backing container - * @see #setEditorEnabled(boolean) - */ - public void editItem(Object itemId) - throws IllegalStateException, IllegalArgumentException { - if (!isEditorEnabled()) { - throw new IllegalStateException("Item editor is not enabled"); - } else if (isEditorBuffered() && editedItemId != null) { - throw new IllegalStateException("Editing item " + itemId - + " failed. Item editor is already editing item " - + editedItemId); - } else if (!getContainerDataSource().containsId(itemId)) { - throw new IllegalArgumentException("Item with id " + itemId - + " not found in current container"); - } - editedItemId = itemId; - getEditorRpc().bind(getContainerDataSource().indexOfId(itemId)); - } - - protected void doEditItem() { - Item item = getContainerDataSource().getItem(editedItemId); - - editorFieldGroup.setItemDataSource(item); - - for (Column column : getColumns()) { - column.getState().editorConnector = getEditorField( - column.getPropertyId()); - } - - editorActive = true; - // Must ensure that all fields, recursively, are sent to the client - // This is needed because the fields are hidden using isRendered - for (LegacyField f : getEditorFields()) { - f.markAsDirtyRecursive(); - } - - if (datasource instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) datasource) - .addItemSetChangeListener(editorClosingItemSetListener); - } - } - - private void setEditorField(Object propertyId, LegacyField field) { - checkColumnExists(propertyId); - - LegacyField oldField = editorFieldGroup.getField(propertyId); - if (oldField != null) { - editorFieldGroup.unbind(oldField); - oldField.setParent(null); - } - - if (field != null) { - field.setParent(this); - editorFieldGroup.bind(field, propertyId); - } - } - - /** - * Saves all changes done to the bound fields. - *

- * Note: This is a pass-through call to the backing field group. - * - * @throws CommitException - * If the commit was aborted - * - * @see FieldGroup#commit() - */ - public void saveEditor() throws CommitException { - editorFieldGroup.commit(); - } - - /** - * Cancels the currently active edit if any. Hides the editor and discards - * possible unsaved changes in the editor fields. - */ - public void cancelEditor() { - if (isEditorActive()) { - getEditorRpc() - .cancel(getContainerDataSource().indexOfId(editedItemId)); - doCancelEditor(); - } - } - - protected void doCancelEditor() { - editedItemId = null; - editorActive = false; - editorFieldGroup.discard(); - editorFieldGroup.setItemDataSource(null); - - if (datasource instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) datasource) - .removeItemSetChangeListener(editorClosingItemSetListener); - } - - // Mark Grid as dirty so the client side gets to know that the editors - // are no longer attached - markAsDirty(); - } - - void resetEditor() { - if (isEditorActive()) { - /* - * Simply force cancel the editing; throwing here would just make - * Grid.setContainerDataSource semantics more complicated. - */ - cancelEditor(); - } - for (LegacyField editor : getEditorFields()) { - editor.setParent(null); - } - - editedItemId = null; - editorActive = false; - editorFieldGroup = new CustomFieldGroup(); - } - - /** - * Gets a collection of all fields bound to the item editor of this grid. - *

- * When {@link #editItem(Object) editItem} is called, fields are - * automatically created and bound to any unbound properties. - * - * @return a collection of all the fields bound to the item editor - */ - Collection> getEditorFields() { - Collection> fields = editorFieldGroup.getFields(); - assert allAttached(fields); - return fields; - } - - private boolean allAttached(Collection components) { - for (Component component : components) { - if (component.getParent() != this) { - return false; - } - } - return true; - } - - /** - * Sets the field factory for the {@link FieldGroup}. The field factory is - * only used when {@link FieldGroup} creates a new field. - *

- * Note: This is a pass-through call to the backing field group. - * - * @param fieldFactory - * The field factory to use - */ - public void setEditorFieldFactory(FieldGroupFieldFactory fieldFactory) { - editorFieldGroup.setFieldFactory(fieldFactory); - } - - /** - * Sets the error handler for the editor. - * - * The error handler is called whenever there is an exception in the editor. - * - * @param editorErrorHandler - * The editor error handler to use - * @throws IllegalArgumentException - * if the error handler is null - */ - public void setEditorErrorHandler(EditorErrorHandler editorErrorHandler) - throws IllegalArgumentException { - if (editorErrorHandler == null) { - throw new IllegalArgumentException( - "The error handler cannot be null"); - } - this.editorErrorHandler = editorErrorHandler; - } - - /** - * Gets the error handler used for the editor - * - * @see #setErrorHandler(com.vaadin.server.ErrorHandler) - * @return the editor error handler, never null - */ - public EditorErrorHandler getEditorErrorHandler() { - return editorErrorHandler; - } - - /** - * Gets the field factory for the {@link FieldGroup}. The field factory is - * only used when {@link FieldGroup} creates a new field. - *

- * Note: This is a pass-through call to the backing field group. - * - * @return The field factory in use - */ - public FieldGroupFieldFactory getEditorFieldFactory() { - return editorFieldGroup.getFieldFactory(); - } - - /** - * Sets the caption on the save button in the Grid editor. - * - * @param saveCaption - * the caption to set - * @throws IllegalArgumentException - * if {@code saveCaption} is {@code null} - */ - public void setEditorSaveCaption(String saveCaption) - throws IllegalArgumentException { - if (saveCaption == null) { - throw new IllegalArgumentException("Save caption cannot be null"); - } - getState().editorSaveCaption = saveCaption; - } - - /** - * Gets the current caption of the save button in the Grid editor. - * - * @return the current caption of the save button - */ - public String getEditorSaveCaption() { - return getState(false).editorSaveCaption; - } - - /** - * Sets the caption on the cancel button in the Grid editor. - * - * @param cancelCaption - * the caption to set - * @throws IllegalArgumentException - * if {@code cancelCaption} is {@code null} - */ - public void setEditorCancelCaption(String cancelCaption) - throws IllegalArgumentException { - if (cancelCaption == null) { - throw new IllegalArgumentException("Cancel caption cannot be null"); - } - getState().editorCancelCaption = cancelCaption; - } - - /** - * Gets the current caption of the cancel button in the Grid editor. - * - * @return the current caption of the cancel button - */ - public String getEditorCancelCaption() { - return getState(false).editorCancelCaption; - } - - /** - * Sets the buffered editor mode. The default mode is buffered ( - * true). - * - * @since 7.6 - * @param editorBuffered - * true to enable buffered editor, - * false to disable it - * @throws IllegalStateException - * If editor is active while attempting to change the buffered - * mode. - */ - public void setEditorBuffered(boolean editorBuffered) - throws IllegalStateException { - if (isEditorActive()) { - throw new IllegalStateException( - "Can't change editor unbuffered mode while editor is active."); - } - getState().editorBuffered = editorBuffered; - editorFieldGroup.setBuffered(editorBuffered); - } - - /** - * Gets the buffered editor mode. - * - * @since 7.6 - * @return true if buffered editor is enabled, - * false otherwise - */ - public boolean isEditorBuffered() { - return getState(false).editorBuffered; - } - - @Override - public void addItemClickListener(ItemClickListener listener) { - addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, - listener, ItemClickEvent.ITEM_CLICK_METHOD); - } - - @Override - @Deprecated - public void addListener(ItemClickListener listener) { - addItemClickListener(listener); - } - - @Override - public void removeItemClickListener(ItemClickListener listener) { - removeListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, - listener); - } - - @Override - @Deprecated - public void removeListener(ItemClickListener listener) { - removeItemClickListener(listener); - } - - /** - * Requests that the column widths should be recalculated. - *

- * In most cases Grid will know when column widths need to be recalculated - * but this method can be used to force recalculation in situations when - * grid does not recalculate automatically. - * - * @since 7.4.1 - */ - public void recalculateColumnWidths() { - getRpcProxy(GridClientRpc.class).recalculateColumnWidths(); - } - - /** - * Registers a new column visibility change listener - * - * @since 7.5.0 - * @param listener - * the listener to register - */ - public void addColumnVisibilityChangeListener( - ColumnVisibilityChangeListener listener) { - addListener(ColumnVisibilityChangeEvent.class, listener, - COLUMN_VISIBILITY_METHOD); - } - - /** - * Removes a previously registered column visibility change listener - * - * @since 7.5.0 - * @param listener - * the listener to remove - */ - public void removeColumnVisibilityChangeListener( - ColumnVisibilityChangeListener listener) { - removeListener(ColumnVisibilityChangeEvent.class, listener, - COLUMN_VISIBILITY_METHOD); - } - - private void fireColumnVisibilityChangeEvent(Column column, boolean hidden, - boolean isUserOriginated) { - fireEvent(new ColumnVisibilityChangeEvent(this, column, hidden, - isUserOriginated)); - } - - /** - * Sets a new details generator for row details. - *

- * The currently opened row details will be re-rendered. - * - * @since 7.5.0 - * @param detailsGenerator - * the details generator to set - * @throws IllegalArgumentException - * if detailsGenerator is null; - */ - public void setDetailsGenerator(DetailsGenerator detailsGenerator) - throws IllegalArgumentException { - detailComponentManager.setDetailsGenerator(detailsGenerator); - } - - /** - * Gets the current details generator for row details. - * - * @since 7.5.0 - * @return the detailsGenerator the current details generator - */ - public DetailsGenerator getDetailsGenerator() { - return detailComponentManager.getDetailsGenerator(); - } - - /** - * Shows or hides the details for a specific item. - * - * @since 7.5.0 - * @param itemId - * the id of the item for which to set details visibility - * @param visible - * true to show the details, or false - * to hide them - */ - public void setDetailsVisible(Object itemId, boolean visible) { - detailComponentManager.setDetailsVisible(itemId, visible); - } - - /** - * Checks whether details are visible for the given item. - * - * @since 7.5.0 - * @param itemId - * the id of the item for which to check details visibility - * @return true iff the details are visible - */ - public boolean isDetailsVisible(Object itemId) { - return detailComponentManager.isDetailsVisible(itemId); - } - - private static SelectionMode getDefaultSelectionMode() { - return SelectionMode.SINGLE; - } - - @Override - public void readDesign(Element design, DesignContext context) { - super.readDesign(design, context); - - Attributes attrs = design.attributes(); - if (attrs.hasKey("editable")) { - setEditorEnabled(DesignAttributeHandler.readAttribute("editable", - attrs, boolean.class)); - } - if (attrs.hasKey("rows")) { - setHeightByRows(DesignAttributeHandler.readAttribute("rows", attrs, - double.class)); - setHeightMode(HeightMode.ROW); - } - if (attrs.hasKey("selection-mode")) { - setSelectionMode(DesignAttributeHandler.readAttribute( - "selection-mode", attrs, SelectionMode.class)); - } - - if (design.children().size() > 0) { - if (design.children().size() > 1 - || !design.child(0).tagName().equals("table")) { - throw new DesignException( - "Grid needs to have a table element as its only child"); - } - Element table = design.child(0); - - Elements colgroups = table.getElementsByTag("colgroup"); - if (colgroups.size() != 1) { - throw new DesignException( - "Table element in declarative Grid needs to have a" - + " colgroup defining the columns used in Grid"); - } - - int i = 0; - for (Element col : colgroups.get(0).getElementsByTag("col")) { - String propertyId = DesignAttributeHandler.readAttribute( - "property-id", col.attributes(), "property-" + i, - String.class); - addColumn(propertyId, String.class).readDesign(col, context); - ++i; - } - - for (Element child : table.children()) { - if (child.tagName().equals("thead")) { - header.readDesign(child, context); - } else if (child.tagName().equals("tbody")) { - for (Element row : child.children()) { - Elements cells = row.children(); - Object[] data = new String[cells.size()]; - for (int c = 0; c < cells.size(); ++c) { - data[c] = cells.get(c).html(); - } - addRow(data); - } - - // Since inline data is used, set HTML renderer for columns - for (Column c : getColumns()) { - c.setRenderer(new HtmlRenderer()); - } - } else if (child.tagName().equals("tfoot")) { - footer.readDesign(child, context); - } - } - } - - // Read frozen columns after columns are read. - if (attrs.hasKey("frozen-columns")) { - setFrozenColumnCount(DesignAttributeHandler - .readAttribute("frozen-columns", attrs, int.class)); - } - } - - @Override - public void writeDesign(Element design, DesignContext context) { - super.writeDesign(design, context); - - Attributes attrs = design.attributes(); - LegacyGrid def = context.getDefaultInstance(this); - - DesignAttributeHandler.writeAttribute("editable", attrs, - isEditorEnabled(), def.isEditorEnabled(), boolean.class); - - DesignAttributeHandler.writeAttribute("frozen-columns", attrs, - getFrozenColumnCount(), def.getFrozenColumnCount(), int.class); - - if (getHeightMode() == HeightMode.ROW) { - DesignAttributeHandler.writeAttribute("rows", attrs, - getHeightByRows(), def.getHeightByRows(), double.class); - } - - SelectionMode selectionMode = null; - - if (selectionModel.getClass().equals(SingleSelectionModel.class)) { - selectionMode = SelectionMode.SINGLE; - } else if (selectionModel.getClass() - .equals(MultiSelectionModel.class)) { - selectionMode = SelectionMode.MULTI; - } else if (selectionModel.getClass().equals(NoSelectionModel.class)) { - selectionMode = SelectionMode.NONE; - } - - assert selectionMode != null : "Unexpected selection model " - + selectionModel.getClass().getName(); - - DesignAttributeHandler.writeAttribute("selection-mode", attrs, - selectionMode, getDefaultSelectionMode(), SelectionMode.class); - - if (columns.isEmpty()) { - // Empty grid. Structure not needed. - return; - } - - // Do structure. - Element tableElement = design.appendElement("table"); - Element colGroup = tableElement.appendElement("colgroup"); - - List columnOrder = getColumns(); - for (int i = 0; i < columnOrder.size(); ++i) { - Column column = columnOrder.get(i); - Element colElement = colGroup.appendElement("col"); - column.writeDesign(colElement, context); - } - - // Always write thead. Reads correctly when there no header rows - header.writeDesign(tableElement.appendElement("thead"), context); - - if (context.shouldWriteData(this)) { - Element bodyElement = tableElement.appendElement("tbody"); - for (Object itemId : datasource.getItemIds()) { - Element tableRow = bodyElement.appendElement("tr"); - for (Column c : getColumns()) { - Object value = datasource.getItem(itemId) - .getItemProperty(c.getPropertyId()).getValue(); - tableRow.appendElement("td") - .append((value != null ? DesignFormatter - .encodeForTextNode(value.toString()) : "")); - } - } - } - - if (footer.getRowCount() > 0) { - footer.writeDesign(tableElement.appendElement("tfoot"), context); - } - } - - @Override - protected Collection getCustomAttributes() { - Collection result = super.getCustomAttributes(); - result.add("editor-enabled"); - result.add("editable"); - result.add("frozen-column-count"); - result.add("frozen-columns"); - result.add("height-by-rows"); - result.add("rows"); - result.add("selection-mode"); - result.add("header-visible"); - result.add("footer-visible"); - result.add("editor-error-handler"); - result.add("height-mode"); - - return result; - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java deleted file mode 100644 index 63c6f5aeaf..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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.ui.renderers; - -import com.vaadin.server.AbstractJavaScriptExtension; -import com.vaadin.server.JavaScriptCallbackHelper; -import com.vaadin.server.JsonCodec; -import com.vaadin.shared.JavaScriptExtensionState; -import com.vaadin.shared.communication.ServerRpc; -import com.vaadin.ui.LegacyGrid.AbstractRenderer; -import com.vaadin.ui.JavaScriptFunction; - -import elemental.json.Json; -import elemental.json.JsonValue; - -/** - * Base class for Renderers with all client-side logic implemented using - * JavaScript. - *

- * When a new JavaScript renderer is initialized in the browser, the framework - * will look for a globally defined JavaScript function that will initialize the - * renderer. The name of the initialization function is formed by replacing . - * with _ in the name of the server-side class. If no such function is defined, - * each super class is used in turn until a match is found. The framework will - * thus first attempt with com_example_MyRenderer for the - * server-side - * com.example.MyRenderer extends AbstractJavaScriptRenderer class. - * If MyRenderer instead extends com.example.SuperRenderer , then - * com_example_SuperRenderer will also be attempted if - * com_example_MyRenderer has not been defined. - *

- * - * In addition to the general JavaScript extension functionality explained in - * {@link AbstractJavaScriptExtension}, this class also provides some - * functionality specific for renderers. - *

- * The initialization function will be called with this pointing to - * a connector wrapper object providing integration to Vaadin. Please note that - * in JavaScript, this is not necessarily defined inside callback - * functions and it might therefore be necessary to assign the reference to a - * separate variable, e.g. var self = this;. In addition to the - * extension functions described for {@link AbstractJavaScriptExtension}, the - * connector wrapper object also provides this function: - *

    - *
  • getRowKey(rowIndex) - Gets a unique identifier for the row - * at the given index. This identifier can be used on the server to retrieve the - * corresponding ItemId using {@link #getItemId(String)}.
  • - *
- * The connector wrapper also supports these special functions that can be - * implemented by the connector: - *
    - *
  • render(cell, data) - Callback for rendering the given data - * into the given cell. The structure of cell and data are described in separate - * sections below. The renderer is required to implement this function. - * Corresponds to - * {@link com.vaadin.client.renderers.Renderer#render(com.vaadin.client.widget.grid.RendererCellReference, Object)} - * .
  • - *
  • init(cell) - Prepares a cell for rendering. Corresponds to - * {@link com.vaadin.client.renderers.ComplexRenderer#init(com.vaadin.client.widget.grid.RendererCellReference)} - * .
  • - *
  • destory(cell) - Allows the renderer to release resources - * allocate for a cell that will no longer be used. Corresponds to - * {@link com.vaadin.client.renderers.ComplexRenderer#destroy(com.vaadin.client.widget.grid.RendererCellReference)} - * .
  • - *
  • onActivate(cell) - Called when the cell is activated by the - * user e.g. by double clicking on the cell or pressing enter with the cell - * focused. Corresponds to - * {@link com.vaadin.client.renderers.ComplexRenderer#onActivate(com.vaadin.client.widget.grid.CellReference)} - * .
  • - *
  • getConsumedEvents() - Returns a JavaScript array of event - * names that should cause onBrowserEvent to be invoked whenever an event is - * fired for a cell managed by this renderer. Corresponds to - * {@link com.vaadin.client.renderers.ComplexRenderer#getConsumedEvents()}.
  • - *
  • onBrowserEvent(cell, event) - Called by Grid when an event - * of a type returned by getConsumedEvents is fired for a cell managed by this - * renderer. Corresponds to - * {@link com.vaadin.client.renderers.ComplexRenderer#onBrowserEvent(com.vaadin.client.widget.grid.CellReference, com.google.gwt.dom.client.NativeEvent)} - * .
  • - *
- * - *

- * The cell object passed to functions defined by the renderer has these - * properties: - *

    - *
  • element - The DOM element corresponding to this cell. - * Readonly.
  • - *
  • rowIndex - The current index of the row of this cell. - * Readonly.
  • - *
  • columnIndex - The current index of the column of this cell. - * Readonly.
  • - *
  • colSpan - The number of columns spanned by this cell. Only - * supported in the object passed to the render function - other - * functions should not use the property. Readable and writable. - *
- * - * @author Vaadin Ltd - * @since 7.4 - */ -public abstract class AbstractJavaScriptRenderer - extends AbstractRenderer { - private JavaScriptCallbackHelper callbackHelper = new JavaScriptCallbackHelper( - this); - - protected AbstractJavaScriptRenderer(Class presentationType, - String nullRepresentation) { - super(presentationType, nullRepresentation); - } - - protected AbstractJavaScriptRenderer(Class presentationType) { - super(presentationType, null); - } - - @Override - protected void registerRpc(R implementation, - Class rpcInterfaceType) { - super.registerRpc(implementation, rpcInterfaceType); - callbackHelper.registerRpc(rpcInterfaceType); - } - - /** - * Register a {@link JavaScriptFunction} that can be called from the - * JavaScript using the provided name. A JavaScript function with the - * provided name will be added to the connector wrapper object (initially - * available as this). Calling that JavaScript function will - * cause the call method in the registered {@link JavaScriptFunction} to be - * invoked with the same arguments. - * - * @param functionName - * the name that should be used for client-side callback - * @param function - * the {@link JavaScriptFunction} object that will be invoked - * when the JavaScript function is called - */ - protected void addFunction(String functionName, - JavaScriptFunction function) { - callbackHelper.registerCallback(functionName, function); - } - - /** - * Invoke a named function that the connector JavaScript has added to the - * JavaScript connector wrapper object. The arguments can be any boxed - * primitive type, String, {@link JsonValue} or arrays of any other - * supported type. Complex types (e.g. List, Set, Map, Connector or any - * JavaBean type) must be explicitly serialized to a {@link JsonValue} - * before sending. This can be done either with - * {@link JsonCodec#encode(Object, JsonValue, java.lang.reflect.Type, com.vaadin.ui.ConnectorTracker)} - * or using the factory methods in {@link Json}. - * - * @param name - * the name of the function - * @param arguments - * function arguments - */ - protected void callFunction(String name, Object... arguments) { - callbackHelper.invokeCallback(name, arguments); - } - - @Override - protected JavaScriptExtensionState getState() { - return (JavaScriptExtensionState) super.getState(); - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java deleted file mode 100644 index 7a87f8f4c1..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.ui.renderers; - -import elemental.json.JsonValue; - -/** - * A Renderer that displays a button with a textual caption. The value of the - * corresponding property is used as the caption. Click listeners can be added - * to the renderer, invoked when any of the rendered buttons is clicked. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class ButtonRenderer extends ClickableRenderer { - - /** - * Creates a new button renderer. - * - * @param nullRepresentation - * the textual representation of {@code null} value - */ - public ButtonRenderer(String nullRepresentation) { - super(String.class, nullRepresentation); - } - - /** - * Creates a new button renderer and adds the given click listener to it. - * - * @param listener - * the click listener to register - * @param nullRepresentation - * the textual representation of {@code null} value - */ - public ButtonRenderer(RendererClickListener listener, - String nullRepresentation) { - this(nullRepresentation); - addClickListener(listener); - } - - /** - * Creates a new button renderer. - */ - public ButtonRenderer() { - this(""); - } - - /** - * Creates a new button renderer and adds the given click listener to it. - * - * @param listener - * the click listener to register - */ - public ButtonRenderer(RendererClickListener listener) { - this(listener, ""); - } - - @Override - public String getNullRepresentation() { - return super.getNullRepresentation(); - } - -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java deleted file mode 100644 index d8fcdc3b4e..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.ui.renderers; - -import java.lang.reflect.Method; - -import com.vaadin.event.ConnectorEventListener; -import com.vaadin.event.MouseEvents.ClickEvent; -import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.ui.grid.renderers.RendererClickRpc; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.AbstractRenderer; -import com.vaadin.ui.LegacyGrid.Column; -import com.vaadin.util.ReflectTools; - -/** - * An abstract superclass for Renderers that render clickable items. Click - * listeners can be added to a renderer to be notified when any of the rendered - * items is clicked. - * - * @param - * the type presented by the renderer - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class ClickableRenderer extends AbstractRenderer { - - /** - * An interface for listening to {@link RendererClickEvent renderer click - * events}. - * - * @see {@link ButtonRenderer#addClickListener(RendererClickListener)} - */ - public interface RendererClickListener extends ConnectorEventListener { - - static final Method CLICK_METHOD = ReflectTools.findMethod( - RendererClickListener.class, "click", RendererClickEvent.class); - - /** - * Called when a rendered button is clicked. - * - * @param event - * the event representing the click - */ - void click(RendererClickEvent event); - } - - /** - * An event fired when a button rendered by a ButtonRenderer is clicked. - */ - public static class RendererClickEvent extends ClickEvent { - - private Object itemId; - private Column column; - - protected RendererClickEvent(LegacyGrid source, Object itemId, Column column, - MouseEventDetails mouseEventDetails) { - super(source, mouseEventDetails); - this.itemId = itemId; - this.column = column; - } - - /** - * Returns the item ID of the row where the click event originated. - * - * @return the item ID of the clicked row - */ - public Object getItemId() { - return itemId; - } - - /** - * Returns the {@link Column} where the click event originated. - * - * @return the column of the click event - */ - public Column getColumn() { - return column; - } - - /** - * Returns the property ID where the click event originated. - * - * @return the property ID of the clicked cell - */ - public Object getPropertyId() { - return column.getPropertyId(); - } - } - - protected ClickableRenderer(Class presentationType) { - this(presentationType, null); - } - - protected ClickableRenderer(Class presentationType, - String nullRepresentation) { - super(presentationType, nullRepresentation); - registerRpc(new RendererClickRpc() { - @Override - public void click(String rowKey, String columnId, - MouseEventDetails mouseDetails) { - fireEvent(new RendererClickEvent(getParentGrid(), - getItemId(rowKey), getColumn(columnId), mouseDetails)); - } - }); - } - - /** - * Adds a click listener to this button renderer. The listener is invoked - * every time one of the buttons rendered by this renderer is clicked. - * - * @param listener - * the click listener to be added - */ - public void addClickListener(RendererClickListener listener) { - addListener(RendererClickEvent.class, listener, - RendererClickListener.CLICK_METHOD); - } - - /** - * Removes the given click listener from this renderer. - * - * @param listener - * the click listener to be removed - */ - public void removeClickListener(RendererClickListener listener) { - removeListener(RendererClickEvent.class, listener); - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java deleted file mode 100644 index eea18e7445..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.ui.renderers; - -import java.text.DateFormat; -import java.util.Date; -import java.util.Locale; - -import com.vaadin.ui.LegacyGrid.AbstractRenderer; - -import elemental.json.JsonValue; - -/** - * A renderer for presenting date values. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class DateRenderer extends AbstractRenderer { - private final Locale locale; - private final String formatString; - private final DateFormat dateFormat; - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with the {@link Date#toString()} - * representation for the default locale. - */ - public DateRenderer() { - this(Locale.getDefault(), ""); - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with the {@link Date#toString()} - * representation for the given locale. - * - * @param locale - * the locale in which to present dates - * @throws IllegalArgumentException - * if {@code locale} is {@code null} - */ - public DateRenderer(Locale locale) throws IllegalArgumentException { - this("%s", locale, ""); - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with the {@link Date#toString()} - * representation for the given locale. - * - * @param locale - * the locale in which to present dates - * @param nullRepresentation - * the textual representation of {@code null} value - * @throws IllegalArgumentException - * if {@code locale} is {@code null} - */ - public DateRenderer(Locale locale, String nullRepresentation) - throws IllegalArgumentException { - this("%s", locale, nullRepresentation); - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with the given string format, as - * displayed in the default locale. - * - * @param formatString - * the format string with which to format the date - * @throws IllegalArgumentException - * if {@code formatString} is {@code null} - * @see Format - * String Syntax - */ - public DateRenderer(String formatString) throws IllegalArgumentException { - this(formatString, ""); - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with the given string format, as - * displayed in the default locale. - * - * @param formatString - * the format string with which to format the date - * @param nullRepresentation - * the textual representation of {@code null} value - * @throws IllegalArgumentException - * if {@code formatString} is {@code null} - * @see Format - * String Syntax - */ - public DateRenderer(String formatString, String nullRepresentation) - throws IllegalArgumentException { - this(formatString, Locale.getDefault(), nullRepresentation); - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with the given string format, as - * displayed in the given locale. - * - * @param formatString - * the format string to format the date with - * @param locale - * the locale to use - * @throws IllegalArgumentException - * if either argument is {@code null} - * @see Format - * String Syntax - */ - public DateRenderer(String formatString, Locale locale) - throws IllegalArgumentException { - this(formatString, locale, ""); - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with the given string format, as - * displayed in the given locale. - * - * @param formatString - * the format string to format the date with - * @param locale - * the locale to use - * @param nullRepresentation - * the textual representation of {@code null} value - * @throws IllegalArgumentException - * if either argument is {@code null} - * @see Format - * String Syntax - */ - public DateRenderer(String formatString, Locale locale, - String nullRepresentation) throws IllegalArgumentException { - super(Date.class, nullRepresentation); - - if (formatString == null) { - throw new IllegalArgumentException("format string may not be null"); - } - - if (locale == null) { - throw new IllegalArgumentException("locale may not be null"); - } - - this.locale = locale; - this.formatString = formatString; - dateFormat = null; - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with he given date format. - * - * @param dateFormat - * the date format to use when rendering dates - * @throws IllegalArgumentException - * if {@code dateFormat} is {@code null} - */ - public DateRenderer(DateFormat dateFormat) throws IllegalArgumentException { - this(dateFormat, ""); - } - - /** - * Creates a new date renderer. - *

- * The renderer is configured to render with he given date format. - * - * @param dateFormat - * the date format to use when rendering dates - * @throws IllegalArgumentException - * if {@code dateFormat} is {@code null} - */ - public DateRenderer(DateFormat dateFormat, String nullRepresentation) - throws IllegalArgumentException { - super(Date.class, nullRepresentation); - if (dateFormat == null) { - throw new IllegalArgumentException("date format may not be null"); - } - - locale = null; - formatString = null; - this.dateFormat = dateFormat; - } - - @Override - public String getNullRepresentation() { - return super.getNullRepresentation(); - } - - @Override - public JsonValue encode(Date value) { - String dateString; - if (value == null) { - dateString = getNullRepresentation(); - } else if (dateFormat != null) { - dateString = dateFormat.format(value); - } else { - dateString = String.format(locale, formatString, value); - } - return encode(dateString, String.class); - } - - @Override - public String toString() { - final String fieldInfo; - if (dateFormat != null) { - fieldInfo = "dateFormat: " + dateFormat.toString(); - } else { - fieldInfo = "locale: " + locale + ", formatString: " + formatString; - } - - return String.format("%s [%s]", getClass().getSimpleName(), fieldInfo); - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java deleted file mode 100644 index c197f7415a..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.ui.renderers; - -import com.vaadin.ui.LegacyGrid.AbstractRenderer; -import elemental.json.JsonValue; - -/** - * A renderer for presenting HTML content. - * - * @author Vaadin Ltd - * @since 7.4 - */ -public class HtmlRenderer extends AbstractRenderer { - /** - * Creates a new HTML renderer. - * - * @param nullRepresentation - * the html representation of {@code null} value - */ - public HtmlRenderer(String nullRepresentation) { - super(String.class, nullRepresentation); - } - - /** - * Creates a new HTML renderer. - */ - public HtmlRenderer() { - this(""); - } - - @Override - public String getNullRepresentation() { - return super.getNullRepresentation(); - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java deleted file mode 100644 index 56e319b47d..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.ui.renderers; - -import com.vaadin.server.ExternalResource; -import com.vaadin.server.Resource; -import com.vaadin.server.ResourceReference; -import com.vaadin.server.ThemeResource; -import com.vaadin.shared.communication.URLReference; - -import elemental.json.JsonValue; - -/** - * A renderer for presenting images. - *

- * The image for each rendered cell is read from a Resource-typed property in - * the data source. Only {@link ExternalResource}s and {@link ThemeResource}s - * are currently supported. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class ImageRenderer extends ClickableRenderer { - - /** - * Creates a new image renderer. - */ - public ImageRenderer() { - super(Resource.class, null); - } - - /** - * Creates a new image renderer and adds the given click listener to it. - * - * @param listener - * the click listener to register - */ - public ImageRenderer(RendererClickListener listener) { - this(); - addClickListener(listener); - } - - @Override - public JsonValue encode(Resource resource) { - if (!(resource == null || resource instanceof ExternalResource - || resource instanceof ThemeResource)) { - throw new IllegalArgumentException( - "ImageRenderer only supports ExternalResource and ThemeResource (" - + resource.getClass().getSimpleName() + " given)"); - } - - return encode(ResourceReference.create(resource, this, null), - URLReference.class); - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java deleted file mode 100644 index e54eecc6ef..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.ui.renderers; - -import java.text.NumberFormat; -import java.util.Locale; - -import com.vaadin.ui.LegacyGrid.AbstractRenderer; - -import elemental.json.JsonValue; - -/** - * A renderer for presenting number values. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class NumberRenderer extends AbstractRenderer { - private final Locale locale; - private final NumberFormat numberFormat; - private final String formatString; - - /** - * Creates a new number renderer. - *

- * The renderer is configured to render with the number's natural string - * representation in the default locale. - */ - public NumberRenderer() { - this(Locale.getDefault()); - } - - /** - * Creates a new number renderer. - *

- * The renderer is configured to render the number as defined with the given - * number format. - * - * @param numberFormat - * the number format with which to display numbers - * @throws IllegalArgumentException - * if {@code numberFormat} is {@code null} - */ - public NumberRenderer(NumberFormat numberFormat) { - this(numberFormat, ""); - } - - /** - * Creates a new number renderer. - *

- * The renderer is configured to render the number as defined with the given - * number format. - * - * @param numberFormat - * the number format with which to display numbers - * @param nullRepresentation - * the textual representation of {@code null} value - * @throws IllegalArgumentException - * if {@code numberFormat} is {@code null} - */ - public NumberRenderer(NumberFormat numberFormat, String nullRepresentation) - throws IllegalArgumentException { - super(Number.class, nullRepresentation); - - if (numberFormat == null) { - throw new IllegalArgumentException("Number format may not be null"); - } - - locale = null; - this.numberFormat = numberFormat; - formatString = null; - } - - /** - * Creates a new number renderer. - *

- * The renderer is configured to render with the number's natural string - * representation in the given locale. - * - * @param locale - * the locale in which to display numbers - * @throws IllegalArgumentException - * if {@code locale} is {@code null} - */ - public NumberRenderer(Locale locale) throws IllegalArgumentException { - this("%s", locale); - } - - /** - * Creates a new number renderer. - *

- * The renderer is configured to render with the number's natural string - * representation in the given locale. - * - * @param formatString - * the format string with which to format the number - * @param locale - * the locale in which to display numbers - * @throws IllegalArgumentException - * if {@code locale} is {@code null} - */ - public NumberRenderer(String formatString, Locale locale) - throws IllegalArgumentException { - this(formatString, locale, ""); // This will call #toString() during - // formatting - } - - /** - * Creates a new number renderer. - *

- * The renderer is configured to render with the given format string in the - * default locale. - * - * @param formatString - * the format string with which to format the number - * @throws IllegalArgumentException - * if {@code formatString} is {@code null} - * @see Format - * String Syntax - */ - public NumberRenderer(String formatString) throws IllegalArgumentException { - this(formatString, Locale.getDefault(), ""); - } - - /** - * Creates a new number renderer. - *

- * The renderer is configured to render with the given format string in the - * given locale. - * - * @param formatString - * the format string with which to format the number - * @param locale - * the locale in which to present numbers - * @throws IllegalArgumentException - * if either argument is {@code null} - * @see Format - * String Syntax - */ - public NumberRenderer(String formatString, Locale locale, - String nullRepresentation) { - super(Number.class, nullRepresentation); - - if (formatString == null) { - throw new IllegalArgumentException("Format string may not be null"); - } - - if (locale == null) { - throw new IllegalArgumentException("Locale may not be null"); - } - - this.locale = locale; - numberFormat = null; - this.formatString = formatString; - } - - @Override - public JsonValue encode(Number value) { - String stringValue; - if (value == null) { - stringValue = getNullRepresentation(); - } else if (formatString != null && locale != null) { - stringValue = String.format(locale, formatString, value); - } else if (numberFormat != null) { - stringValue = numberFormat.format(value); - } else { - throw new IllegalStateException(String.format( - "Internal bug: " + "%s is in an illegal state: " - + "[locale: %s, numberFormat: %s, formatString: %s]", - getClass().getSimpleName(), locale, numberFormat, - formatString)); - } - return encode(stringValue, String.class); - } - - @Override - public String toString() { - final String fieldInfo; - if (numberFormat != null) { - fieldInfo = "numberFormat: " + numberFormat.toString(); - } else { - fieldInfo = "locale: " + locale + ", formatString: " + formatString; - } - - return String.format("%s [%s]", getClass().getSimpleName(), fieldInfo); - } - - @Override - public String getNullRepresentation() { - return super.getNullRepresentation(); - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java deleted file mode 100644 index fe90dfdee0..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.ui.renderers; - -import com.vaadin.ui.LegacyGrid.AbstractRenderer; - -import elemental.json.JsonValue; - -/** - * A renderer that represents a double values as a graphical progress bar. - * - * @author Vaadin Ltd - * @since 7.4 - */ -public class ProgressBarRenderer extends AbstractRenderer { - - /** - * Creates a new text renderer - */ - public ProgressBarRenderer() { - super(Double.class, null); - } - - @Override - public JsonValue encode(Double value) { - if (value != null) { - value = Math.max(Math.min(value, 1), 0); - } else { - value = 0d; - } - return super.encode(value); - } -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/Renderer.java b/server/src/main/java/com/vaadin/ui/renderers/Renderer.java deleted file mode 100644 index d42e299ea8..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/Renderer.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.ui.renderers; - -import com.vaadin.server.ClientConnector; -import com.vaadin.server.Extension; - -import elemental.json.JsonValue; - -/** - * A ClientConnector for controlling client-side - * {@link com.vaadin.client.widget.grid.Renderer Grid renderers}. Renderers - * currently extend the Extension interface, but this fact should be regarded as - * an implementation detail and subject to change in a future major or minor - * Vaadin revision. - * - * @param - * the type this renderer knows how to present - * - * @since 7.4 - * @author Vaadin Ltd - */ -public interface Renderer extends Extension { - - /** - * Returns the class literal corresponding to the presentation type T. - * - * @return the class literal of T - */ - Class getPresentationType(); - - /** - * Encodes the given value into a {@link JsonValue}. - * - * @param value - * the value to encode - * @return a JSON representation of the given value - */ - JsonValue encode(T value); - - /** - * This method is inherited from Extension but should never be called - * directly with a Renderer. - */ - @Override - @Deprecated - void remove(); - - /** - * This method is inherited from Extension but should never be called - * directly with a Renderer. - */ - @Override - @Deprecated - void setParent(ClientConnector parent); -} diff --git a/server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java b/server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java deleted file mode 100644 index ac73615272..0000000000 --- a/server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.ui.renderers; - -import com.vaadin.ui.LegacyGrid.AbstractRenderer; -import elemental.json.JsonValue; - -/** - * A renderer for presenting simple plain-text string values. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class TextRenderer extends AbstractRenderer { - - /** - * Creates a new text renderer - */ - public TextRenderer() { - this(""); - } - - /** - * Creates a new text renderer - * - * @param nullRepresentation - * the textual representation of {@code null} value - */ - public TextRenderer(String nullRepresentation) { - super(String.class, nullRepresentation); - } - - @Override - public String getNullRepresentation() { - return super.getNullRepresentation(); - } -} diff --git a/server/src/test/java/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java b/server/src/test/java/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java deleted file mode 100644 index 013d14c27d..0000000000 --- a/server/src/test/java/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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.data; - -import java.lang.reflect.Constructor; -import java.util.Date; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; -import com.vaadin.ui.AbstractSelect; -import com.vaadin.ui.ComboBox; -import com.vaadin.ui.ListSelect; -import com.vaadin.v7.ui.LegacyDateField; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyInlineDateField; -import com.vaadin.v7.ui.LegacyPopupDateField; -import com.vaadin.v7.ui.LegacyTextField; - -public class DefaultFieldGroupFieldFactoryTest { - - private DefaultFieldGroupFieldFactory fieldFactory; - - @Before - public void setupFieldFactory() { - fieldFactory = DefaultFieldGroupFieldFactory.get(); - } - - @Test - public void noPublicConstructor() { - Class clazz = DefaultFieldGroupFieldFactory.class; - Constructor[] constructors = clazz.getConstructors(); - Assert.assertEquals( - "DefaultFieldGroupFieldFactory contains public constructors", 0, - constructors.length); - } - - @Test - public void testSameInstance() { - DefaultFieldGroupFieldFactory factory1 = DefaultFieldGroupFieldFactory - .get(); - DefaultFieldGroupFieldFactory factory2 = DefaultFieldGroupFieldFactory - .get(); - Assert.assertTrue( - "DefaultFieldGroupFieldFactory.get() method returns different instances", - factory1 == factory2); - Assert.assertNotNull( - "DefaultFieldGroupFieldFactory.get() method returns null", - factory1); - } - - @Test - public void testDateGenerationForPopupDateField() { - LegacyField f = fieldFactory.createField(Date.class, - LegacyDateField.class); - Assert.assertNotNull(f); - Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); - } - - @Test - public void testDateGenerationForInlineDateField() { - LegacyField f = fieldFactory.createField(Date.class, - LegacyInlineDateField.class); - Assert.assertNotNull(f); - Assert.assertEquals(LegacyInlineDateField.class, f.getClass()); - } - - @Test - public void testDateGenerationForTextField() { - LegacyField f = fieldFactory.createField(Date.class, - LegacyTextField.class); - Assert.assertNotNull(f); - Assert.assertEquals(LegacyTextField.class, f.getClass()); - } - - @Test - public void testDateGenerationForField() { - LegacyField f = fieldFactory.createField(Date.class, LegacyField.class); - Assert.assertNotNull(f); - Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); - } - - public enum SomeEnum { - FOO, BAR; - } - - @Test - public void testEnumComboBox() { - LegacyField f = fieldFactory.createField(SomeEnum.class, - ComboBox.class); - Assert.assertNotNull(f); - Assert.assertEquals(ComboBox.class, f.getClass()); - } - - @Test - public void testEnumAnySelect() { - LegacyField f = fieldFactory.createField(SomeEnum.class, - AbstractSelect.class); - Assert.assertNotNull(f); - Assert.assertEquals(ListSelect.class, f.getClass()); - } - - @Test - public void testEnumAnyField() { - LegacyField f = fieldFactory.createField(SomeEnum.class, - LegacyField.class); - Assert.assertNotNull(f); - Assert.assertEquals(ListSelect.class, f.getClass()); - } -} diff --git a/server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java b/server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java deleted file mode 100644 index 3333cd7744..0000000000 --- a/server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012 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.data.fieldgroup; - -import org.junit.Assert; -import org.junit.Test; - -public class BeanFieldGroupTest { - - class Main { - private String mainField; - - public String getMainField() { - return mainField; - } - - public void setMainField(String mainField) { - this.mainField = mainField; - } - - } - - class Sub1 extends Main { - private Integer sub1Field; - - public Integer getSub1Field() { - return sub1Field; - } - - public void setSub1Field(Integer sub1Field) { - this.sub1Field = sub1Field; - } - - } - - class Sub2 extends Sub1 { - private boolean sub2field; - - public boolean isSub2field() { - return sub2field; - } - - public void setSub2field(boolean sub2field) { - this.sub2field = sub2field; - } - - } - - @Test - public void propertyTypeWithoutItem() { - BeanFieldGroup s = new BeanFieldGroup( - Sub2.class); - Assert.assertEquals(boolean.class, s.getPropertyType("sub2field")); - Assert.assertEquals(Integer.class, s.getPropertyType("sub1Field")); - Assert.assertEquals(String.class, s.getPropertyType("mainField")); - } -} diff --git a/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java b/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java deleted file mode 100644 index e270211abf..0000000000 --- a/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.data.fieldgroup; - -import java.util.Date; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.BeanItem; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyPopupDateField; - -public class FieldGroupDateTest { - - private FieldGroup fieldGroup; - - public class TestBean { - private Date javaDate; - private java.sql.Date sqlDate; - - public TestBean(Date javaDate, java.sql.Date sqlDate) { - super(); - this.javaDate = javaDate; - this.sqlDate = sqlDate; - } - - public java.sql.Date getSqlDate() { - return sqlDate; - } - - public void setSqlDate(java.sql.Date sqlDate) { - this.sqlDate = sqlDate; - } - - public Date getJavaDate() { - return javaDate; - } - - public void setJavaDate(Date date) { - javaDate = date; - } - } - - @SuppressWarnings("deprecation") - @Before - public void setup() { - fieldGroup = new FieldGroup(); - fieldGroup.setItemDataSource(new BeanItem(new TestBean( - new Date(2010, 5, 7), new java.sql.Date(2011, 6, 8)))); - } - - @Test - public void testBuildAndBindDate() { - LegacyField f = fieldGroup.buildAndBind("javaDate"); - Assert.assertNotNull(f); - Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); - } - - @Test - public void testBuildAndBindSqlDate() { - LegacyField f = fieldGroup.buildAndBind("sqlDate"); - Assert.assertNotNull(f); - Assert.assertEquals(LegacyPopupDateField.class, f.getClass()); - } - - @Test - public void clearFields() { - LegacyPopupDateField sqlDate = new LegacyPopupDateField(); - LegacyPopupDateField javaDate = new LegacyPopupDateField(); - fieldGroup.bind(sqlDate, "sqlDate"); - fieldGroup.bind(javaDate, "javaDate"); - - Assert.assertEquals(new Date(2010, 5, 7), javaDate.getValue()); - Assert.assertEquals(new Date(2011, 6, 8), sqlDate.getValue()); - - fieldGroup.clear(); - Assert.assertEquals(null, javaDate.getValue()); - Assert.assertEquals(null, sqlDate.getValue()); - - } - -} diff --git a/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java b/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java deleted file mode 100644 index 4622f0960e..0000000000 --- a/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.data.fieldgroup; - -import org.junit.Test; - -import com.vaadin.data.fieldgroup.FieldGroup.CommitException; -import com.vaadin.v7.ui.LegacyPopupDateField; - -public class FieldGroupExceptionTest { - - @Test(expected = CommitException.class) - public void testUnboundCommitException() throws CommitException { - FieldGroup fieldGroup = new FieldGroup(); - LegacyPopupDateField dateField = new LegacyPopupDateField(); - fieldGroup.bind(dateField, "date"); - fieldGroup.commit(); - } - -} diff --git a/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java b/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java deleted file mode 100644 index c0039fc4fb..0000000000 --- a/server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.vaadin.data.fieldgroup; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsNull.nullValue; -import static org.mockito.Mockito.mock; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Property; -import com.vaadin.data.Property.Transactional; -import com.vaadin.data.util.BeanItem; -import com.vaadin.data.util.TransactionalPropertyWrapper; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyTextField; - -public class FieldGroupTest { - - private FieldGroup sut; - private LegacyField field; - - @Before - public void setup() { - sut = new FieldGroup(); - field = mock(LegacyField.class); - } - - @Test - public void fieldIsBound() { - sut.bind(field, "foobar"); - - assertThat(sut.getField("foobar"), is(field)); - } - - @Test(expected = FieldGroup.BindException.class) - public void cannotBindToAlreadyBoundProperty() { - sut.bind(field, "foobar"); - sut.bind(mock(LegacyField.class), "foobar"); - } - - @Test(expected = FieldGroup.BindException.class) - public void cannotBindNullField() { - sut.bind(null, "foobar"); - } - - public void canUnbindWithoutItem() { - sut.bind(field, "foobar"); - - sut.unbind(field); - assertThat(sut.getField("foobar"), is(nullValue())); - } - - @Test - public void wrapInTransactionalProperty_provideCustomImpl_customTransactionalWrapperIsUsed() { - Bean bean = new Bean(); - FieldGroup group = new FieldGroup() { - @Override - protected Transactional wrapInTransactionalProperty( - Property itemProperty) { - return new TransactionalPropertyImpl(itemProperty); - } - }; - group.setItemDataSource(new BeanItem(bean)); - LegacyTextField field = new LegacyTextField(); - group.bind(field, "name"); - - Property propertyDataSource = field.getPropertyDataSource(); - Assert.assertTrue( - "Custom implementation of transactional property " - + "has not been used", - propertyDataSource instanceof TransactionalPropertyImpl); - } - - public static class TransactionalPropertyImpl - extends TransactionalPropertyWrapper { - - public TransactionalPropertyImpl(Property wrappedProperty) { - super(wrappedProperty); - } - - } - - public static class Bean { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } -} diff --git a/server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java b/server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java deleted file mode 100644 index 4a9e4f9891..0000000000 --- a/server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.vaadin.data.util; - -/** - * Automated test for {@link AbstractBeanContainer}. - * - * Only a limited subset of the functionality is tested here, the rest in tests - * of subclasses including {@link BeanItemContainer} and {@link BeanContainer}. - */ -public abstract class AbstractBeanContainerTestBase - extends AbstractInMemoryContainerTestBase { - - public static class Person { - private String name; - - public Person(String name) { - setName(name); - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - - public static class ClassName { - // field names match constants in parent test class - private String fullyQualifiedName; - private String simpleName; - private String reverseFullyQualifiedName; - private Integer idNumber; - - public ClassName(String fullyQualifiedName, Integer idNumber) { - this.fullyQualifiedName = fullyQualifiedName; - simpleName = AbstractContainerTestBase - .getSimpleName(fullyQualifiedName); - reverseFullyQualifiedName = reverse(fullyQualifiedName); - this.idNumber = idNumber; - } - - public String getFullyQualifiedName() { - return fullyQualifiedName; - } - - public void setFullyQualifiedName(String fullyQualifiedName) { - this.fullyQualifiedName = fullyQualifiedName; - } - - public String getSimpleName() { - return simpleName; - } - - public void setSimpleName(String simpleName) { - this.simpleName = simpleName; - } - - public String getReverseFullyQualifiedName() { - return reverseFullyQualifiedName; - } - - public void setReverseFullyQualifiedName( - String reverseFullyQualifiedName) { - this.reverseFullyQualifiedName = reverseFullyQualifiedName; - } - - public Integer getIdNumber() { - return idNumber; - } - - public void setIdNumber(Integer idNumber) { - this.idNumber = idNumber; - } - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java b/server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java deleted file mode 100644 index 955b609735..0000000000 --- a/server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java +++ /dev/null @@ -1,866 +0,0 @@ -package com.vaadin.data.util; - -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 java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.junit.Assert; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Filterable; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Container.Ordered; -import com.vaadin.data.Container.Sortable; -import com.vaadin.data.Item; -import com.vaadin.data.util.filter.SimpleStringFilter; - -public abstract class AbstractContainerTestBase { - - /** - * Helper class for testing e.g. listeners expecting events to be fired. - */ - protected abstract static class AbstractEventCounter { - private int eventCount = 0; - private int lastAssertedEventCount = 0; - - /** - * Increment the event count. To be called by subclasses e.g. from a - * listener method. - */ - protected void increment() { - ++eventCount; - } - - /** - * Check that no one event has occurred since the previous assert call. - */ - public void assertNone() { - Assert.assertEquals(lastAssertedEventCount, eventCount); - } - - /** - * Check that exactly one event has occurred since the previous assert - * call. - */ - public void assertOnce() { - Assert.assertEquals(++lastAssertedEventCount, eventCount); - } - - /** - * Reset the counter and the expected count. - */ - public void reset() { - eventCount = 0; - lastAssertedEventCount = 0; - } - } - - /** - * Test class for counting item set change events and verifying they have - * been received. - */ - protected static class ItemSetChangeCounter extends AbstractEventCounter - implements ItemSetChangeListener { - - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - increment(); - } - - } - - // #6043: for items that have been filtered out, Container interface does - // not specify what to return from getItem() and getContainerProperty(), so - // need checkGetItemNull parameter for the test to be usable for most - // current containers - protected void validateContainer(Container container, - Object expectedFirstItemId, Object expectedLastItemId, - Object itemIdInSet, Object itemIdNotInSet, boolean checkGetItemNull, - int expectedSize) { - Container.Indexed indexed = null; - if (container instanceof Container.Indexed) { - indexed = (Container.Indexed) container; - } - - List itemIdList = new ArrayList(container.getItemIds()); - - // size() - assertEquals(expectedSize, container.size()); - assertEquals(expectedSize, itemIdList.size()); - - // first item, last item - Object first = itemIdList.get(0); - Object last = itemIdList.get(itemIdList.size() - 1); - - assertEquals(expectedFirstItemId, first); - assertEquals(expectedLastItemId, last); - - // containsId - assertFalse(container.containsId(itemIdNotInSet)); - assertTrue(container.containsId(itemIdInSet)); - - // getItem - if (checkGetItemNull) { - assertNull(container.getItem(itemIdNotInSet)); - } - assertNotNull(container.getItem(itemIdInSet)); - - // getContainerProperty - for (Object propId : container.getContainerPropertyIds()) { - if (checkGetItemNull) { - assertNull( - container.getContainerProperty(itemIdNotInSet, propId)); - } - assertNotNull(container.getContainerProperty(itemIdInSet, propId)); - } - - if (indexed != null) { - // firstItemId - assertEquals(first, indexed.firstItemId()); - - // lastItemId - assertEquals(last, indexed.lastItemId()); - - // nextItemId - assertEquals(itemIdList.get(1), indexed.nextItemId(first)); - - // prevItemId - assertEquals(itemIdList.get(itemIdList.size() - 2), - indexed.prevItemId(last)); - - // isFirstId - assertTrue(indexed.isFirstId(first)); - assertFalse(indexed.isFirstId(last)); - - // isLastId - assertTrue(indexed.isLastId(last)); - assertFalse(indexed.isLastId(first)); - - // indexOfId - assertEquals(0, indexed.indexOfId(first)); - assertEquals(expectedSize - 1, indexed.indexOfId(last)); - - // getIdByIndex - assertEquals(indexed.getIdByIndex(0), first); - assertEquals(indexed.getIdByIndex(expectedSize - 1), last); - - } - - // getItemProperty - Assert.assertNull( - container.getItem(itemIdInSet).getItemProperty("notinset")); - - } - - protected static final Object FULLY_QUALIFIED_NAME = "fullyQualifiedName"; - protected static final Object SIMPLE_NAME = "simpleName"; - protected static final Object REVERSE_FULLY_QUALIFIED_NAME = "reverseFullyQualifiedName"; - protected static final Object ID_NUMBER = "idNumber"; - - protected void testBasicContainerOperations(Container container) { - initializeContainer(container); - - // Basic container - validateContainer(container, sampleData[0], - sampleData[sampleData.length - 1], sampleData[10], "abc", true, - sampleData.length); - - validateRemovingItems(container); - validateAddItem(container); - if (container instanceof Container.Indexed) { - validateAddItemAt((Container.Indexed) container); - } - if (container instanceof Container.Ordered) { - validateAddItemAfter((Container.Ordered) container); - } - - } - - protected void validateRemovingItems(Container container) { - int sizeBeforeRemoving = container.size(); - - List itemIdList = new ArrayList(container.getItemIds()); - // There should be at least four items in the list - Object first = itemIdList.get(0); - Object middle = itemIdList.get(2); - Object last = itemIdList.get(itemIdList.size() - 1); - - container.removeItem(first); - container.removeItem(middle); // Middle now that first has been removed - container.removeItem(last); - - assertEquals(sizeBeforeRemoving - 3, container.size()); - - container.removeAllItems(); - - assertEquals(0, container.size()); - } - - protected void validateAddItem(Container container) { - try { - container.removeAllItems(); - - Object id = container.addItem(); - Assert.assertTrue(container.containsId(id)); - Assert.assertNotNull(container.getItem(id)); - - Item item = container.addItem("foo"); - Assert.assertNotNull(item); - Assert.assertTrue(container.containsId("foo")); - Assert.assertEquals(item, container.getItem("foo")); - - // Add again - Item item2 = container.addItem("foo"); - Assert.assertNull(item2); - - // Null is not a valid itemId - Assert.assertNull(container.addItem(null)); - } catch (UnsupportedOperationException e) { - // Ignore contains which do not support addItem* - } - } - - protected void validateAddItemAt(Container.Indexed container) { - try { - container.removeAllItems(); - - Object id = container.addItemAt(0); - Assert.assertTrue(container.containsId(id)); - Assert.assertEquals(id, container.getIdByIndex(0)); - Assert.assertNotNull(container.getItem(id)); - - Item item = container.addItemAt(0, "foo"); - Assert.assertNotNull(item); - Assert.assertTrue(container.containsId("foo")); - Assert.assertEquals(item, container.getItem("foo")); - Assert.assertEquals("foo", container.getIdByIndex(0)); - - Item itemAtEnd = container.addItemAt(2, "atend"); - Assert.assertNotNull(itemAtEnd); - Assert.assertTrue(container.containsId("atend")); - Assert.assertEquals(itemAtEnd, container.getItem("atend")); - Assert.assertEquals("atend", container.getIdByIndex(2)); - - // Add again - Item item2 = container.addItemAt(0, "foo"); - Assert.assertNull(item2); - } catch (UnsupportedOperationException e) { - // Ignore contains which do not support addItem* - } - } - - protected void validateAddItemAfter(Container.Ordered container) { - if (container instanceof AbstractBeanContainer) { - // Doesn't work as bean container requires beans - return; - } - - try { - container.removeAllItems(); - - Assert.assertNotNull(container.addItem(0)); - - Item item = container.addItemAfter(null, "foo"); - Assert.assertNotNull(item); - Assert.assertTrue(container.containsId("foo")); - Assert.assertEquals(item, container.getItem("foo")); - Assert.assertEquals("foo", - container.getItemIds().iterator().next()); - - Item itemAtEnd = container.addItemAfter(0, "atend"); - Assert.assertNotNull(itemAtEnd); - Assert.assertTrue(container.containsId("atend")); - Assert.assertEquals(itemAtEnd, container.getItem("atend")); - Iterator i = container.getItemIds().iterator(); - i.next(); - i.next(); - Assert.assertEquals("atend", i.next()); - - // Add again - Assert.assertNull(container.addItemAfter(null, "foo")); - Assert.assertNull(container.addItemAfter("atend", "foo")); - Assert.assertNull(container.addItemAfter("nonexistant", "123123")); - } catch (UnsupportedOperationException e) { - // Ignore contains which do not support addItem* - } - } - - protected void testContainerOrdered(Container.Ordered container) { - // addItem with empty container - Object id = container.addItem(); - assertOrderedContents(container, id); - Item item = container.getItem(id); - assertNotNull(item); - - // addItemAfter with empty container - container.removeAllItems(); - assertOrderedContents(container); - id = container.addItemAfter(null); - assertOrderedContents(container, id); - item = container.getItem(id); - assertNotNull(item); - - // Add a new item before the first - // addItemAfter - Object newFirstId = container.addItemAfter(null); - assertOrderedContents(container, newFirstId, id); - - // addItemAfter(Object) - Object newSecondItemId = container.addItemAfter(newFirstId); - // order is now: newFirstId, newSecondItemId, id - assertOrderedContents(container, newFirstId, newSecondItemId, id); - - // addItemAfter(Object,Object) - String fourthId = "id of the fourth item"; - Item fourth = container.addItemAfter(newFirstId, fourthId); - // order is now: newFirstId, fourthId, newSecondItemId, id - assertNotNull(fourth); - assertEquals(fourth, container.getItem(fourthId)); - assertOrderedContents(container, newFirstId, fourthId, newSecondItemId, - id); - - // addItemAfter(Object,Object) - Object fifthId = new Object(); - Item fifth = container.addItemAfter(null, fifthId); - // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id - assertNotNull(fifth); - assertEquals(fifth, container.getItem(fifthId)); - assertOrderedContents(container, fifthId, newFirstId, fourthId, - newSecondItemId, id); - - // addItemAfter(Object,Object) - Object sixthId = new Object(); - Item sixth = container.addItemAfter(id, sixthId); - // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id, - // sixthId - assertNotNull(sixth); - assertEquals(sixth, container.getItem(sixthId)); - assertOrderedContents(container, fifthId, newFirstId, fourthId, - newSecondItemId, id, sixthId); - - // Test order after removing first item 'fifthId' - container.removeItem(fifthId); - // order is now: newFirstId, fourthId, newSecondItemId, id, sixthId - assertOrderedContents(container, newFirstId, fourthId, newSecondItemId, - id, sixthId); - - // Test order after removing last item 'sixthId' - container.removeItem(sixthId); - // order is now: newFirstId, fourthId, newSecondItemId, id - assertOrderedContents(container, newFirstId, fourthId, newSecondItemId, - id); - - // Test order after removing item from the middle 'fourthId' - container.removeItem(fourthId); - // order is now: newFirstId, newSecondItemId, id - assertOrderedContents(container, newFirstId, newSecondItemId, id); - - // Delete remaining items - container.removeItem(newFirstId); - container.removeItem(newSecondItemId); - container.removeItem(id); - assertOrderedContents(container); - - Object finalItem = container.addItem(); - assertOrderedContents(container, finalItem); - } - - private void assertOrderedContents(Ordered container, Object... ids) { - assertEquals(ids.length, container.size()); - for (int i = 0; i < ids.length - 1; i++) { - assertNotNull("The item id should not be null", ids[i]); - } - if (ids.length == 0) { - assertNull("The first id is wrong", container.firstItemId()); - assertNull("The last id is wrong", container.lastItemId()); - return; - } - - assertEquals("The first id is wrong", ids[0], container.firstItemId()); - assertEquals("The last id is wrong", ids[ids.length - 1], - container.lastItemId()); - - // isFirstId & isLastId - assertTrue(container.isFirstId(container.firstItemId())); - assertTrue(container.isLastId(container.lastItemId())); - - // nextId - Object ref = container.firstItemId(); - for (int i = 1; i < ids.length; i++) { - Object next = container.nextItemId(ref); - assertEquals("The id after " + ref + " is wrong", ids[i], next); - ref = next; - } - assertNull("The last id should not have a next id", - container.nextItemId(ids[ids.length - 1])); - assertNull(container.nextItemId("not-in-container")); - - // prevId - ref = container.lastItemId(); - for (int i = ids.length - 2; i >= 0; i--) { - Object prev = container.prevItemId(ref); - assertEquals("The id before " + ref + " is wrong", ids[i], prev); - ref = prev; - } - assertNull("The first id should not have a prev id", - container.prevItemId(ids[0])); - assertNull(container.prevItemId("not-in-container")); - - } - - protected void testContainerIndexed(Container.Indexed container, - Object itemId, int itemPosition, boolean testAddEmptyItemAt, - Object newItemId, boolean testAddItemAtWithId) { - initializeContainer(container); - - // indexOfId - Assert.assertEquals(itemPosition, container.indexOfId(itemId)); - - // getIdByIndex - Assert.assertEquals(itemId, container.getIdByIndex(itemPosition)); - - // addItemAt - if (testAddEmptyItemAt) { - Object addedId = container.addItemAt(itemPosition); - Assert.assertEquals(itemPosition, container.indexOfId(addedId)); - Assert.assertEquals(itemPosition + 1, container.indexOfId(itemId)); - Assert.assertEquals(addedId, container.getIdByIndex(itemPosition)); - Assert.assertEquals(itemId, - container.getIdByIndex(itemPosition + 1)); - - Object newFirstId = container.addItemAt(0); - Assert.assertEquals(0, container.indexOfId(newFirstId)); - Assert.assertEquals(itemPosition + 2, container.indexOfId(itemId)); - Assert.assertEquals(newFirstId, container.firstItemId()); - Assert.assertEquals(newFirstId, container.getIdByIndex(0)); - Assert.assertEquals(itemId, - container.getIdByIndex(itemPosition + 2)); - - Object newLastId = container.addItemAt(container.size()); - Assert.assertEquals(container.size() - 1, - container.indexOfId(newLastId)); - Assert.assertEquals(itemPosition + 2, container.indexOfId(itemId)); - Assert.assertEquals(newLastId, container.lastItemId()); - Assert.assertEquals(newLastId, - container.getIdByIndex(container.size() - 1)); - Assert.assertEquals(itemId, - container.getIdByIndex(itemPosition + 2)); - - Assert.assertTrue(container.removeItem(addedId)); - Assert.assertTrue(container.removeItem(newFirstId)); - Assert.assertTrue(container.removeItem(newLastId)); - - Assert.assertFalse( - "Removing non-existing item should indicate failure", - container.removeItem(addedId)); - } - - // addItemAt - if (testAddItemAtWithId) { - container.addItemAt(itemPosition, newItemId); - Assert.assertEquals(itemPosition, container.indexOfId(newItemId)); - Assert.assertEquals(itemPosition + 1, container.indexOfId(itemId)); - Assert.assertEquals(newItemId, - container.getIdByIndex(itemPosition)); - Assert.assertEquals(itemId, - container.getIdByIndex(itemPosition + 1)); - Assert.assertTrue(container.removeItem(newItemId)); - Assert.assertFalse(container.containsId(newItemId)); - - container.addItemAt(0, newItemId); - Assert.assertEquals(0, container.indexOfId(newItemId)); - Assert.assertEquals(itemPosition + 1, container.indexOfId(itemId)); - Assert.assertEquals(newItemId, container.firstItemId()); - Assert.assertEquals(newItemId, container.getIdByIndex(0)); - Assert.assertEquals(itemId, - container.getIdByIndex(itemPosition + 1)); - Assert.assertTrue(container.removeItem(newItemId)); - Assert.assertFalse(container.containsId(newItemId)); - - container.addItemAt(container.size(), newItemId); - Assert.assertEquals(container.size() - 1, - container.indexOfId(newItemId)); - Assert.assertEquals(itemPosition, container.indexOfId(itemId)); - Assert.assertEquals(newItemId, container.lastItemId()); - Assert.assertEquals(newItemId, - container.getIdByIndex(container.size() - 1)); - Assert.assertEquals(itemId, container.getIdByIndex(itemPosition)); - Assert.assertTrue(container.removeItem(newItemId)); - Assert.assertFalse(container.containsId(newItemId)); - } - } - - protected void testContainerFiltering(Container.Filterable container) { - initializeContainer(container); - - // Filter by "contains ab" - SimpleStringFilter filter1 = new SimpleStringFilter( - FULLY_QUALIFIED_NAME, "ab", false, false); - container.addContainerFilter(filter1); - - assertTrue(container.getContainerFilters().size() == 1); - assertEquals(filter1, - container.getContainerFilters().iterator().next()); - - validateContainer(container, "com.vaadin.data.BufferedValidatable", - "com.vaadin.ui.TabSheet", - "com.vaadin.terminal.gwt.client.Focusable", - "com.vaadin.data.Buffered", isFilteredOutItemNull(), 20); - - // Filter by "contains da" (reversed as ad here) - container.removeAllContainerFilters(); - - assertTrue(container.getContainerFilters().isEmpty()); - - SimpleStringFilter filter2 = new SimpleStringFilter( - REVERSE_FULLY_QUALIFIED_NAME, "ad", false, false); - container.addContainerFilter(filter2); - - assertTrue(container.getContainerFilters().size() == 1); - assertEquals(filter2, - container.getContainerFilters().iterator().next()); - - validateContainer(container, "com.vaadin.data.Buffered", - "com.vaadin.server.ComponentSizeValidator", - "com.vaadin.data.util.IndexedContainer", - "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", - isFilteredOutItemNull(), 37); - } - - /** - * Override in subclasses to return false if the container getItem() method - * returns a non-null value for an item that has been filtered out. - * - * @return - */ - protected boolean isFilteredOutItemNull() { - return true; - } - - protected void testContainerSortingAndFiltering( - Container.Sortable sortable) { - Filterable filterable = (Filterable) sortable; - - initializeContainer(sortable); - - // Filter by "contains ab" - filterable.addContainerFilter(new SimpleStringFilter( - FULLY_QUALIFIED_NAME, "ab", false, false)); - - // Must be able to sort based on PROP1 for this test - assertTrue(sortable.getSortableContainerPropertyIds() - .contains(FULLY_QUALIFIED_NAME)); - - sortable.sort(new Object[] { FULLY_QUALIFIED_NAME }, - new boolean[] { true }); - - validateContainer(sortable, "com.vaadin.data.BufferedValidatable", - "com.vaadin.ui.TableFieldFactory", - "com.vaadin.ui.TableFieldFactory", - "com.vaadin.data.util.BeanItem", isFilteredOutItemNull(), 20); - } - - protected void testContainerSorting(Container.Filterable container) { - Container.Sortable sortable = (Sortable) container; - - initializeContainer(container); - - // Must be able to sort based on PROP1 for this test - assertTrue(sortable.getSortableContainerPropertyIds() - .contains(FULLY_QUALIFIED_NAME)); - assertTrue(sortable.getSortableContainerPropertyIds() - .contains(REVERSE_FULLY_QUALIFIED_NAME)); - - sortable.sort(new Object[] { FULLY_QUALIFIED_NAME }, - new boolean[] { true }); - - validateContainer(container, "com.vaadin.Application", - "org.vaadin.test.LastClass", - "com.vaadin.server.ApplicationResource", "blah", true, - sampleData.length); - - sortable.sort(new Object[] { REVERSE_FULLY_QUALIFIED_NAME }, - new boolean[] { true }); - - validateContainer(container, "com.vaadin.server.ApplicationPortlet2", - "com.vaadin.data.util.ObjectProperty", - "com.vaadin.ui.BaseFieldFactory", "blah", true, - sampleData.length); - - } - - protected void initializeContainer(Container container) { - Assert.assertTrue(container.removeAllItems()); - Object[] propertyIds = container.getContainerPropertyIds().toArray(); - for (Object propertyId : propertyIds) { - container.removeContainerProperty(propertyId); - } - - container.addContainerProperty(FULLY_QUALIFIED_NAME, String.class, ""); - container.addContainerProperty(SIMPLE_NAME, String.class, ""); - container.addContainerProperty(REVERSE_FULLY_QUALIFIED_NAME, - String.class, null); - container.addContainerProperty(ID_NUMBER, Integer.class, null); - - for (int i = 0; i < sampleData.length; i++) { - String id = sampleData[i]; - Item item = container.addItem(id); - - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(sampleData[i]); - item.getItemProperty(SIMPLE_NAME) - .setValue(getSimpleName(sampleData[i])); - item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) - .setValue(reverse(sampleData[i])); - item.getItemProperty(ID_NUMBER).setValue(i); - } - } - - protected static String getSimpleName(String name) { - if (name.contains(".")) { - return name.substring(name.lastIndexOf('.') + 1); - } else { - return name; - } - } - - protected static String reverse(String string) { - return new StringBuilder(string).reverse().toString(); - } - - protected final String[] sampleData = { - "com.vaadin.annotations.AutoGenerated", "com.vaadin.Application", - "com.vaadin.data.Buffered", "com.vaadin.data.BufferedValidatable", - "com.vaadin.data.Container", "com.vaadin.data.Item", - "com.vaadin.data.Property", "com.vaadin.data.util.BeanItem", - "com.vaadin.data.util.BeanItemContainer", - "com.vaadin.data.util.ContainerHierarchicalWrapper", - "com.vaadin.data.util.ContainerOrderedWrapper", - "com.vaadin.data.util.DefaultItemSorter", - "com.vaadin.data.util.FilesystemContainer", - "com.vaadin.data.util.Filter", - "com.vaadin.data.util.HierarchicalContainer", - "com.vaadin.data.util.IndexedContainer", - "com.vaadin.data.util.ItemSorter", - "com.vaadin.data.util.MethodProperty", - "com.vaadin.data.util.ObjectProperty", - "com.vaadin.data.util.PropertyFormatter", - "com.vaadin.data.util.PropertysetItem", - "com.vaadin.data.util.QueryContainer", - "com.vaadin.data.util.TextFileProperty", - "com.vaadin.data.Validatable", - "com.vaadin.data.validator.AbstractStringValidator", - "com.vaadin.data.validator.AbstractValidator", - "com.vaadin.data.validator.CompositeValidator", - "com.vaadin.data.validator.DoubleValidator", - "com.vaadin.data.validator.EmailValidator", - "com.vaadin.data.validator.IntegerValidator", - "com.vaadin.data.validator.NullValidator", - "com.vaadin.data.validator.RegexpValidator", - "com.vaadin.data.validator.StringLengthValidator", - "com.vaadin.data.Validator", "com.vaadin.event.Action", - "com.vaadin.event.ComponentEventListener", - "com.vaadin.event.EventRouter", "com.vaadin.event.FieldEvents", - "com.vaadin.event.ItemClickEvent", "com.vaadin.event.LayoutEvents", - "com.vaadin.event.ListenerMethod", - "com.vaadin.event.MethodEventSource", - "com.vaadin.event.MouseEvents", "com.vaadin.event.ShortcutAction", - "com.vaadin.launcher.DemoLauncher", - "com.vaadin.launcher.DevelopmentServerLauncher", - "com.vaadin.launcher.util.BrowserLauncher", - "com.vaadin.service.ApplicationContext", - "com.vaadin.service.FileTypeResolver", - "com.vaadin.server.ApplicationResource", - "com.vaadin.server.ClassResource", - "com.vaadin.server.CompositeErrorMessage", - "com.vaadin.server.DownloadStream", - "com.vaadin.server.ErrorMessage", - "com.vaadin.server.ExternalResource", - "com.vaadin.server.FileResource", - "com.vaadin.terminal.gwt.client.ApplicationConfiguration", - "com.vaadin.terminal.gwt.client.ApplicationConnection", - "com.vaadin.terminal.gwt.client.BrowserInfo", - "com.vaadin.terminal.gwt.client.ClientExceptionHandler", - "com.vaadin.terminal.gwt.client.ComponentDetail", - "com.vaadin.terminal.gwt.client.ComponentDetailMap", - "com.vaadin.terminal.gwt.client.ComponentLocator", - "com.vaadin.terminal.gwt.client.Console", - "com.vaadin.terminal.gwt.client.Container", - "com.vaadin.terminal.gwt.client.ContainerResizedListener", - "com.vaadin.terminal.gwt.client.CSSRule", - "com.vaadin.terminal.gwt.client.DateTimeService", - "com.vaadin.terminal.gwt.client.DefaultWidgetSet", - "com.vaadin.terminal.gwt.client.Focusable", - "com.vaadin.terminal.gwt.client.HistoryImplIEVaadin", - "com.vaadin.terminal.gwt.client.LocaleNotLoadedException", - "com.vaadin.terminal.gwt.client.LocaleService", - "com.vaadin.terminal.gwt.client.MouseEventDetails", - "com.vaadin.terminal.gwt.client.NullConsole", - "com.vaadin.terminal.gwt.client.Paintable", - "com.vaadin.terminal.gwt.client.RenderInformation", - "com.vaadin.terminal.gwt.client.RenderSpace", - "com.vaadin.terminal.gwt.client.StyleConstants", - "com.vaadin.terminal.gwt.client.TooltipInfo", - "com.vaadin.terminal.gwt.client.ui.Action", - "com.vaadin.terminal.gwt.client.ui.ActionOwner", - "com.vaadin.terminal.gwt.client.ui.AlignmentInfo", - "com.vaadin.terminal.gwt.client.ui.CalendarEntry", - "com.vaadin.terminal.gwt.client.ui.ClickEventHandler", - "com.vaadin.terminal.gwt.client.ui.Field", - "com.vaadin.terminal.gwt.client.ui.Icon", - "com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout", - "com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer", - "com.vaadin.terminal.gwt.client.ui.layout.Margins", - "com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler", - "com.vaadin.terminal.gwt.client.ui.MenuBar", - "com.vaadin.terminal.gwt.client.ui.MenuItem", - "com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextToolbar", - "com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler", - "com.vaadin.terminal.gwt.client.ui.SubPartAware", - "com.vaadin.terminal.gwt.client.ui.Table", - "com.vaadin.terminal.gwt.client.ui.TreeAction", - "com.vaadin.terminal.gwt.client.ui.TreeImages", - "com.vaadin.terminal.gwt.client.ui.VAbsoluteLayout", - "com.vaadin.terminal.gwt.client.ui.VAccordion", - "com.vaadin.terminal.gwt.client.ui.VButton", - "com.vaadin.terminal.gwt.client.ui.VCalendarPanel", - "com.vaadin.terminal.gwt.client.ui.VCheckBox", - "com.vaadin.terminal.gwt.client.ui.VContextMenu", - "com.vaadin.terminal.gwt.client.ui.VCssLayout", - "com.vaadin.terminal.gwt.client.ui.VCustomComponent", - "com.vaadin.terminal.gwt.client.ui.VCustomLayout", - "com.vaadin.terminal.gwt.client.ui.VDateField", - "com.vaadin.terminal.gwt.client.ui.VDateFieldCalendar", - "com.vaadin.terminal.gwt.client.ui.VEmbedded", - "com.vaadin.terminal.gwt.client.ui.VFilterSelect", - "com.vaadin.terminal.gwt.client.ui.VForm", - "com.vaadin.terminal.gwt.client.ui.VFormLayout", - "com.vaadin.terminal.gwt.client.ui.VGridLayout", - "com.vaadin.terminal.gwt.client.ui.VHorizontalLayout", - "com.vaadin.terminal.gwt.client.ui.VLabel", - "com.vaadin.terminal.gwt.client.ui.VLink", - "com.vaadin.terminal.gwt.client.ui.VListSelect", - "com.vaadin.terminal.gwt.client.ui.VMarginInfo", - "com.vaadin.terminal.gwt.client.ui.VMenuBar", - "com.vaadin.terminal.gwt.client.ui.VNativeButton", - "com.vaadin.terminal.gwt.client.ui.VNativeSelect", - "com.vaadin.terminal.gwt.client.ui.VNotification", - "com.vaadin.terminal.gwt.client.ui.VOptionGroup", - "com.vaadin.terminal.gwt.client.ui.VOptionGroupBase", - "com.vaadin.terminal.gwt.client.ui.VOrderedLayout", - "com.vaadin.terminal.gwt.client.ui.VOverlay", - "com.vaadin.terminal.gwt.client.ui.VPanel", - "com.vaadin.terminal.gwt.client.ui.VPasswordField", - "com.vaadin.terminal.gwt.client.ui.VPopupCalendar", - "com.vaadin.terminal.gwt.client.ui.VPopupView", - "com.vaadin.terminal.gwt.client.ui.VProgressIndicator", - "com.vaadin.terminal.gwt.client.ui.VRichTextArea", - "com.vaadin.terminal.gwt.client.ui.VScrollTable", - "com.vaadin.terminal.gwt.client.ui.VSlider", - "com.vaadin.terminal.gwt.client.ui.VSplitPanel", - "com.vaadin.terminal.gwt.client.ui.VSplitPanelHorizontal", - "com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical", - "com.vaadin.terminal.gwt.client.ui.VTablePaging", - "com.vaadin.terminal.gwt.client.ui.VTabsheet", - "com.vaadin.terminal.gwt.client.ui.VTabsheetBase", - "com.vaadin.terminal.gwt.client.ui.VTabsheetPanel", - "com.vaadin.terminal.gwt.client.ui.VTextArea", - "com.vaadin.terminal.gwt.client.ui.VTextField", - "com.vaadin.terminal.gwt.client.ui.VTextualDate", - "com.vaadin.terminal.gwt.client.ui.VTime", - "com.vaadin.terminal.gwt.client.ui.VTree", - "com.vaadin.terminal.gwt.client.ui.VTwinColSelect", - "com.vaadin.terminal.gwt.client.ui.VUnknownComponent", - "com.vaadin.terminal.gwt.client.ui.VUpload", - "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", - "com.vaadin.terminal.gwt.client.ui.VVerticalLayout", - "com.vaadin.terminal.gwt.client.ui.VView", - "com.vaadin.terminal.gwt.client.ui.VWindow", - "com.vaadin.terminal.gwt.client.UIDL", - "com.vaadin.terminal.gwt.client.Util", - "com.vaadin.terminal.gwt.client.ValueMap", - "com.vaadin.terminal.gwt.client.VCaption", - "com.vaadin.terminal.gwt.client.VCaptionWrapper", - "com.vaadin.terminal.gwt.client.VDebugConsole", - "com.vaadin.terminal.gwt.client.VErrorMessage", - "com.vaadin.terminal.gwt.client.VTooltip", - "com.vaadin.terminal.gwt.client.VUIDLBrowser", - "com.vaadin.terminal.gwt.client.WidgetMap", - "com.vaadin.terminal.gwt.client.WidgetSet", - "com.vaadin.server.AbstractApplicationPortlet", - "com.vaadin.server.AbstractApplicationServlet", - "com.vaadin.server.AbstractCommunicationManager", - "com.vaadin.server.AbstractWebApplicationContext", - "com.vaadin.server.ApplicationPortlet", - "com.vaadin.server.ApplicationPortlet2", - "com.vaadin.server.ApplicationRunnerServlet", - "com.vaadin.server.ApplicationServlet", - "com.vaadin.server.ChangeVariablesErrorEvent", - "com.vaadin.server.CommunicationManager", - "com.vaadin.server.ComponentSizeValidator", - "com.vaadin.server.Constants", - "com.vaadin.server.GAEApplicationServlet", - "com.vaadin.server.HttpServletRequestListener", - "com.vaadin.server.HttpUploadStream", - "com.vaadin.server.JsonPaintTarget", - "com.vaadin.server.PortletApplicationContext", - "com.vaadin.server.PortletApplicationContext2", - "com.vaadin.server.PortletCommunicationManager", - "com.vaadin.server.PortletRequestListener", - "com.vaadin.server.RestrictedRenderResponse", - "com.vaadin.server.SessionExpiredException", - "com.vaadin.server.SystemMessageException", - "com.vaadin.server.WebApplicationContext", - "com.vaadin.server.WebBrowser", - "com.vaadin.server.widgetsetutils.ClassPathExplorer", - "com.vaadin.server.widgetsetutils.WidgetMapGenerator", - "com.vaadin.server.widgetsetutils.WidgetSetBuilder", - "com.vaadin.server.KeyMapper", "com.vaadin.server.Paintable", - "com.vaadin.server.PaintException", "com.vaadin.server.PaintTarget", - "com.vaadin.server.ParameterHandler", "com.vaadin.server.Resource", - "com.vaadin.server.Scrollable", "com.vaadin.server.Sizeable", - "com.vaadin.server.StreamResource", "com.vaadin.server.SystemError", - "com.vaadin.server.Terminal", "com.vaadin.server.ThemeResource", - "com.vaadin.server.UploadStream", "com.vaadin.server.URIHandler", - "com.vaadin.server.UserError", "com.vaadin.server.VariableOwner", - "com.vaadin.tools.ReflectTools", - "com.vaadin.tools.WidgetsetCompiler", - "com.vaadin.ui.AbsoluteLayout", "com.vaadin.ui.AbstractComponent", - "com.vaadin.ui.AbstractComponentContainer", - "com.vaadin.ui.AbstractField", "com.vaadin.ui.AbstractLayout", - "com.vaadin.ui.AbstractOrderedLayout", - "com.vaadin.ui.AbstractSelect", "com.vaadin.ui.Accordion", - "com.vaadin.ui.Alignment", "com.vaadin.ui.AlignmentUtils", - "com.vaadin.ui.BaseFieldFactory", "com.vaadin.ui.Button", - "com.vaadin.ui.CheckBox", "com.vaadin.ui.ClientWidget", - "com.vaadin.ui.ComboBox", "com.vaadin.ui.Component", - "com.vaadin.ui.ComponentContainer", "com.vaadin.ui.CssLayout", - "com.vaadin.ui.CustomComponent", "com.vaadin.ui.CustomLayout", - "com.vaadin.ui.DateField", "com.vaadin.ui.DefaultFieldFactory", - "com.vaadin.ui.Embedded", "com.vaadin.ui.ExpandLayout", - "com.vaadin.ui.Field", "com.vaadin.ui.FieldFactory", - "com.vaadin.ui.Form", "com.vaadin.ui.FormFieldFactory", - "com.vaadin.ui.FormLayout", "com.vaadin.ui.GridLayout", - "com.vaadin.ui.HorizontalLayout", "com.vaadin.ui.InlineDateField", - "com.vaadin.ui.Label", "com.vaadin.ui.Layout", "com.vaadin.ui.Link", - "com.vaadin.ui.ListSelect", "com.vaadin.ui.LoginForm", - "com.vaadin.ui.MenuBar", "com.vaadin.ui.NativeButton", - "com.vaadin.ui.NativeSelect", "com.vaadin.ui.OptionGroup", - "com.vaadin.ui.OrderedLayout", "com.vaadin.ui.Panel", - "com.vaadin.ui.PopupDateField", "com.vaadin.ui.PopupView", - "com.vaadin.ui.ProgressIndicator", "com.vaadin.ui.RichTextArea", - "com.vaadin.ui.Select", "com.vaadin.ui.Slider", - "com.vaadin.ui.SplitPanel", "com.vaadin.ui.Table", - "com.vaadin.ui.TableFieldFactory", "com.vaadin.ui.TabSheet", - "com.vaadin.ui.TextField", "com.vaadin.ui.Tree", - "com.vaadin.ui.TwinColSelect", "com.vaadin.ui.Upload", - "com.vaadin.ui.UriFragmentUtility", "com.vaadin.ui.VerticalLayout", - "com.vaadin.ui.Window", "com.vaadin.util.SerializerHelper", - "org.vaadin.test.LastClass" }; -} diff --git a/server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java b/server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java deleted file mode 100644 index f3eda74100..0000000000 --- a/server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java +++ /dev/null @@ -1,289 +0,0 @@ -package com.vaadin.data.util; - -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 java.util.Collection; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Hierarchical; -import com.vaadin.data.Container.Sortable; -import com.vaadin.data.Item; - -public abstract class AbstractHierarchicalContainerTestBase - extends AbstractContainerTestBase { - - /** - * @param container - * The container to validate - * @param expectedFirstItemId - * Expected first item id - * @param expectedLastItemId - * Expected last item id - * @param itemIdInSet - * An item id that is in the container - * @param itemIdNotInSet - * An item id that is not in the container - * @param checkGetItemNull - * true if getItem() should return null for itemIdNotInSet, false - * to skip the check (container.containsId() is checked in any - * case) - * @param expectedSize - * Expected number of items in the container. Not related to - * hierarchy. - * @param expectedTraversalSize - * Expected number of items found when traversing from the roots - * down to all available nodes. - * @param expectedRootSize - * Expected number of root items - * @param rootsHaveChildren - * true if all roots have children, false otherwise (skips some - * asserts) - */ - protected void validateHierarchicalContainer(Hierarchical container, - Object expectedFirstItemId, Object expectedLastItemId, - Object itemIdInSet, Object itemIdNotInSet, boolean checkGetItemNull, - int expectedSize, int expectedRootSize, boolean rootsHaveChildren) { - - validateContainer(container, expectedFirstItemId, expectedLastItemId, - itemIdInSet, itemIdNotInSet, checkGetItemNull, expectedSize); - - // rootItemIds - Collection rootIds = container.rootItemIds(); - assertEquals(expectedRootSize, rootIds.size()); - - for (Object rootId : rootIds) { - // All roots must be in container - assertTrue(container.containsId(rootId)); - - // All roots must have no parent - assertNull(container.getParent(rootId)); - - // all roots must be roots - assertTrue(container.isRoot(rootId)); - - if (rootsHaveChildren) { - // all roots have children allowed in this case - assertTrue(container.areChildrenAllowed(rootId)); - - // all roots have children in this case - Collection children = container.getChildren(rootId); - assertNotNull(rootId + " should have children", children); - assertTrue(rootId + " should have children", - (children.size() > 0)); - // getParent - for (Object childId : children) { - assertEquals(container.getParent(childId), rootId); - } - - } - } - - // isRoot should return false for unknown items - assertFalse(container.isRoot(itemIdNotInSet)); - - // hasChildren should return false for unknown items - assertFalse(container.hasChildren(itemIdNotInSet)); - - // areChildrenAllowed should return false for unknown items - assertFalse(container.areChildrenAllowed(itemIdNotInSet)); - - // removeItem of unknown items should return false - assertFalse(container.removeItem(itemIdNotInSet)); - - assertEquals(expectedSize, countNodes(container)); - - validateHierarchy(container); - } - - private int countNodes(Hierarchical container) { - int totalNodes = 0; - for (Object rootId : container.rootItemIds()) { - totalNodes += countNodes(container, rootId); - } - - return totalNodes; - } - - private int countNodes(Hierarchical container, Object itemId) { - int nodes = 1; // This - Collection children = container.getChildren(itemId); - if (children != null) { - for (Object id : children) { - nodes += countNodes(container, id); - } - } - - return nodes; - } - - private void validateHierarchy(Hierarchical container) { - for (Object rootId : container.rootItemIds()) { - validateHierarchy(container, rootId, null); - } - } - - private void validateHierarchy(Hierarchical container, Object itemId, - Object parentId) { - Collection children = container.getChildren(itemId); - - // getParent - assertEquals(container.getParent(itemId), parentId); - - if (!container.areChildrenAllowed(itemId)) { - // If no children is allowed the item should have no children - assertFalse(container.hasChildren(itemId)); - assertTrue(children == null || children.size() == 0); - - return; - } - if (children != null) { - for (Object id : children) { - validateHierarchy(container, id, itemId); - } - } - } - - protected void testHierarchicalContainer(Container.Hierarchical container) { - initializeContainer(container); - - int packages = 21 + 3; - int expectedSize = sampleData.length + packages; - validateHierarchicalContainer(container, "com", - "org.vaadin.test.LastClass", - "com.vaadin.server.ApplicationResource", "blah", true, - expectedSize, 2, true); - - } - - protected void testHierarchicalSorting(Container.Hierarchical container) { - Container.Sortable sortable = (Sortable) container; - - initializeContainer(container); - - // Must be able to sort based on PROP1 and PROP2 for this test - assertTrue(sortable.getSortableContainerPropertyIds() - .contains(FULLY_QUALIFIED_NAME)); - assertTrue(sortable.getSortableContainerPropertyIds() - .contains(REVERSE_FULLY_QUALIFIED_NAME)); - - sortable.sort(new Object[] { FULLY_QUALIFIED_NAME }, - new boolean[] { true }); - - int packages = 21 + 3; - int expectedSize = sampleData.length + packages; - validateHierarchicalContainer(container, "com", - "org.vaadin.test.LastClass", - "com.vaadin.server.ApplicationResource", "blah", true, - expectedSize, 2, true); - - sortable.sort(new Object[] { REVERSE_FULLY_QUALIFIED_NAME }, - new boolean[] { true }); - - validateHierarchicalContainer(container, - "com.vaadin.server.ApplicationPortlet2", - "com.vaadin.data.util.ObjectProperty", - "com.vaadin.server.ApplicationResource", "blah", true, - expectedSize, 2, true); - - } - - protected void initializeContainer(Container.Hierarchical container) { - container.removeAllItems(); - Object[] propertyIds = container.getContainerPropertyIds().toArray(); - for (Object propertyId : propertyIds) { - container.removeContainerProperty(propertyId); - } - - container.addContainerProperty(FULLY_QUALIFIED_NAME, String.class, ""); - container.addContainerProperty(SIMPLE_NAME, String.class, ""); - container.addContainerProperty(REVERSE_FULLY_QUALIFIED_NAME, - String.class, null); - container.addContainerProperty(ID_NUMBER, Integer.class, null); - - for (int i = 0; i < sampleData.length; i++) { - String id = sampleData[i]; - - // Add path as parent - String paths[] = id.split("\\."); - String path = paths[0]; - // Adds "com" and other items multiple times so should return null - // for all but the first time - if (container.addItem(path) != null) { - assertTrue(container.setChildrenAllowed(path, false)); - Item item = container.getItem(path); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(path); - item.getItemProperty(SIMPLE_NAME).setValue(getSimpleName(path)); - item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) - .setValue(reverse(path)); - item.getItemProperty(ID_NUMBER).setValue(1); - } - for (int j = 1; j < paths.length; j++) { - String parent = path; - path = path + "." + paths[j]; - - // Adds "com" and other items multiple times so should return - // null for all but the first time - if (container.addItem(path) != null) { - assertTrue(container.setChildrenAllowed(path, false)); - - Item item = container.getItem(path); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(path); - item.getItemProperty(SIMPLE_NAME) - .setValue(getSimpleName(path)); - item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) - .setValue(reverse(path)); - item.getItemProperty(ID_NUMBER).setValue(1); - - } - assertTrue(container.setChildrenAllowed(parent, true)); - assertTrue("Failed to set " + parent + " as parent for " + path, - container.setParent(path, parent)); - } - - Item item = container.getItem(id); - assertNotNull(item); - String parent = id.substring(0, id.lastIndexOf('.')); - assertTrue(container.setParent(id, parent)); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(sampleData[i]); - item.getItemProperty(SIMPLE_NAME) - .setValue(getSimpleName(sampleData[i])); - item.getItemProperty(REVERSE_FULLY_QUALIFIED_NAME) - .setValue(reverse(sampleData[i])); - item.getItemProperty(ID_NUMBER).setValue(i % 2); - } - } - - protected void testRemoveHierarchicalWrapperSubtree( - Container.Hierarchical container) { - initializeContainer(container); - - // remove root item - removeItemRecursively(container, "org"); - - int packages = 21 + 3 - 3; - int expectedSize = sampleData.length + packages - 1; - - validateContainer(container, "com", "com.vaadin.util.SerializerHelper", - "com.vaadin.server.ApplicationResource", "blah", true, - expectedSize); - - // rootItemIds - Collection rootIds = container.rootItemIds(); - assertEquals(1, rootIds.size()); - } - - private void removeItemRecursively(Container.Hierarchical container, - Object itemId) { - if (container instanceof ContainerHierarchicalWrapper) { - ((ContainerHierarchicalWrapper) container) - .removeItemRecursively("org"); - } else { - HierarchicalContainer.removeItemRecursively(container, itemId); - } - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java b/server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java deleted file mode 100644 index 3858504bc7..0000000000 --- a/server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.vaadin.data.util; - -public abstract class AbstractInMemoryContainerTestBase - extends AbstractContainerTestBase { - -} diff --git a/server/src/test/java/com/vaadin/data/util/BeanContainerTest.java b/server/src/test/java/com/vaadin/data/util/BeanContainerTest.java deleted file mode 100644 index bdf6ba1958..0000000000 --- a/server/src/test/java/com/vaadin/data/util/BeanContainerTest.java +++ /dev/null @@ -1,516 +0,0 @@ -package com.vaadin.data.util; - -import static org.junit.Assert.assertEquals; -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.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.util.AbstractBeanContainer.BeanIdResolver; - -public class BeanContainerTest extends AbstractBeanContainerTestBase { - - protected static class PersonNameResolver - implements BeanIdResolver { - - @Override - public String getIdForBean(Person bean) { - return bean != null ? bean.getName() : null; - } - - } - - protected static class NullResolver - implements BeanIdResolver { - - @Override - public String getIdForBean(Person bean) { - return null; - } - - } - - private Map nameToBean = new LinkedHashMap(); - - private BeanContainer getContainer() { - return new BeanContainer(ClassName.class); - } - - @Before - public void setUp() { - nameToBean.clear(); - - for (int i = 0; i < sampleData.length; i++) { - ClassName className = new ClassName(sampleData[i], i); - nameToBean.put(sampleData[i], className); - } - } - - @Override - @SuppressWarnings("unchecked") - protected void initializeContainer(Container container) { - BeanContainer beanItemContainer = (BeanContainer) container; - - beanItemContainer.removeAllItems(); - - for (Entry entry : nameToBean.entrySet()) { - beanItemContainer.addItem(entry.getKey(), entry.getValue()); - } - } - - @Override - protected boolean isFilteredOutItemNull() { - return false; - } - - @Test - public void testGetType_existingProperty_typeReturned() { - BeanContainer container = getContainer(); - Assert.assertEquals( - "Unexpected type is returned for property 'simpleName'", - String.class, container.getType("simpleName")); - } - - @Test - public void testGetType_notExistingProperty_nullReturned() { - BeanContainer container = getContainer(); - Assert.assertNull("Not null type is returned for property ''", - container.getType("")); - } - - @Test - public void testBasicOperations() { - testBasicContainerOperations(getContainer()); - } - - @Test - public void testFiltering() { - testContainerFiltering(getContainer()); - } - - @Test - public void testSorting() { - testContainerSorting(getContainer()); - } - - @Test - public void testSortingAndFiltering() { - testContainerSortingAndFiltering(getContainer()); - } - - // duplicated from parent class and modified - adding items to - // BeanContainer differs from other containers - @Test - public void testContainerOrdered() { - BeanContainer container = new BeanContainer( - String.class); - - String id = "test1"; - - Item item = container.addItem(id, "value"); - assertNotNull(item); - - assertEquals(id, container.firstItemId()); - assertEquals(id, container.lastItemId()); - - // isFirstId - assertTrue(container.isFirstId(id)); - assertTrue(container.isFirstId(container.firstItemId())); - // isLastId - assertTrue(container.isLastId(id)); - assertTrue(container.isLastId(container.lastItemId())); - - // Add a new item before the first - // addItemAfter - String newFirstId = "newFirst"; - item = container.addItemAfter(null, newFirstId, "newFirstValue"); - assertNotNull(item); - assertNotNull(container.getItem(newFirstId)); - - // isFirstId - assertTrue(container.isFirstId(newFirstId)); - assertTrue(container.isFirstId(container.firstItemId())); - // isLastId - assertTrue(container.isLastId(id)); - assertTrue(container.isLastId(container.lastItemId())); - - // nextItemId - assertEquals(id, container.nextItemId(newFirstId)); - assertNull(container.nextItemId(id)); - assertNull(container.nextItemId("not-in-container")); - - // prevItemId - assertEquals(newFirstId, container.prevItemId(id)); - assertNull(container.prevItemId(newFirstId)); - assertNull(container.prevItemId("not-in-container")); - - // addItemAfter(IDTYPE, IDTYPE, BT) - String newSecondItemId = "newSecond"; - item = container.addItemAfter(newFirstId, newSecondItemId, - "newSecondValue"); - // order is now: newFirstId, newSecondItemId, id - assertNotNull(item); - assertNotNull(container.getItem(newSecondItemId)); - assertEquals(id, container.nextItemId(newSecondItemId)); - assertEquals(newFirstId, container.prevItemId(newSecondItemId)); - - // addItemAfter(IDTYPE, IDTYPE, BT) - String fourthId = "id of the fourth item"; - Item fourth = container.addItemAfter(newFirstId, fourthId, - "fourthValue"); - // order is now: newFirstId, fourthId, newSecondItemId, id - assertNotNull(fourth); - assertEquals(fourth, container.getItem(fourthId)); - assertEquals(newSecondItemId, container.nextItemId(fourthId)); - assertEquals(newFirstId, container.prevItemId(fourthId)); - - // addItemAfter(IDTYPE, IDTYPE, BT) - String fifthId = "fifth"; - Item fifth = container.addItemAfter(null, fifthId, "fifthValue"); - // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id - assertNotNull(fifth); - assertEquals(fifth, container.getItem(fifthId)); - assertEquals(newFirstId, container.nextItemId(fifthId)); - assertNull(container.prevItemId(fifthId)); - - } - - // TODO test Container.Indexed interface operation - testContainerIndexed()? - - @Test - public void testAddItemAt() { - BeanContainer container = new BeanContainer( - String.class); - - container.addItem("id1", "value1"); - // id1 - container.addItemAt(0, "id2", "value2"); - // id2, id1 - container.addItemAt(1, "id3", "value3"); - // id2, id3, id1 - container.addItemAt(container.size(), "id4", "value4"); - // id2, id3, id1, id4 - - assertNull(container.addItemAt(-1, "id5", "value5")); - assertNull(container.addItemAt(container.size() + 1, "id6", "value6")); - - assertEquals(4, container.size()); - assertEquals("id2", container.getIdByIndex(0)); - assertEquals("id3", container.getIdByIndex(1)); - assertEquals("id1", container.getIdByIndex(2)); - assertEquals("id4", container.getIdByIndex(3)); - } - - @Test - public void testUnsupportedMethods() { - BeanContainer container = new BeanContainer( - Person.class); - container.addItem("John", new Person("John")); - - try { - container.addItem(); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addItem(null); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addItemAfter(null, null); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addItemAfter(new Person("Jane")); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addItemAt(0); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addItemAt(0, new Person("Jane")); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addContainerProperty("lastName", String.class, ""); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - assertEquals(1, container.size()); - } - - @Test - public void testRemoveContainerProperty() { - BeanContainer container = new BeanContainer( - Person.class); - container.setBeanIdResolver(new PersonNameResolver()); - container.addBean(new Person("John")); - - Assert.assertEquals("John", - container.getContainerProperty("John", "name").getValue()); - Assert.assertTrue(container.removeContainerProperty("name")); - Assert.assertNull(container.getContainerProperty("John", "name")); - - Assert.assertNotNull(container.getItem("John")); - // property removed also from item - Assert.assertNull(container.getItem("John").getItemProperty("name")); - } - - @Test - public void testAddNullBeans() { - BeanContainer container = new BeanContainer( - Person.class); - - assertNull(container.addItem("id1", null)); - assertNull(container.addItemAfter(null, "id2", null)); - assertNull(container.addItemAt(0, "id3", null)); - - assertEquals(0, container.size()); - } - - @Test - public void testAddNullId() { - BeanContainer container = new BeanContainer( - Person.class); - - Person john = new Person("John"); - - assertNull(container.addItem(null, john)); - assertNull(container.addItemAfter(null, null, john)); - assertNull(container.addItemAt(0, null, john)); - - assertEquals(0, container.size()); - } - - @Test - public void testEmptyContainer() { - BeanContainer container = new BeanContainer( - Person.class); - - assertNull(container.firstItemId()); - assertNull(container.lastItemId()); - - assertEquals(0, container.size()); - - // could test more about empty container - } - - @Test - public void testAddBeanWithoutResolver() { - BeanContainer container = new BeanContainer( - Person.class); - - try { - container.addBean(new Person("John")); - Assert.fail(); - } catch (IllegalStateException e) { - // should get exception - } - try { - container.addBeanAfter(null, new Person("Jane")); - Assert.fail(); - } catch (IllegalStateException e) { - // should get exception - } - try { - container.addBeanAt(0, new Person("Jack")); - Assert.fail(); - } catch (IllegalStateException e) { - // should get exception - } - try { - container - .addAll(Arrays.asList(new Person[] { new Person("Jack") })); - Assert.fail(); - } catch (IllegalStateException e) { - // should get exception - } - - assertEquals(0, container.size()); - } - - @Test - public void testAddAllWithNullItemId() { - BeanContainer container = new BeanContainer( - Person.class); - // resolver that returns null as item id - container.setBeanIdResolver( - new BeanIdResolver() { - - @Override - public String getIdForBean(Person bean) { - return bean.getName(); - } - }); - - List persons = new ArrayList(); - persons.add(new Person("John")); - persons.add(new Person("Marc")); - persons.add(new Person(null)); - persons.add(new Person("foo")); - - try { - container.addAll(persons); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - - container.removeAllItems(); - persons.remove(2); - container.addAll(persons); - assertEquals(3, container.size()); - } - - @Test - public void testAddBeanWithNullResolver() { - BeanContainer container = new BeanContainer( - Person.class); - // resolver that returns null as item id - container.setBeanIdResolver(new NullResolver()); - - try { - container.addBean(new Person("John")); - Assert.fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - try { - container.addBeanAfter(null, new Person("Jane")); - Assert.fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - try { - container.addBeanAt(0, new Person("Jack")); - Assert.fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - - assertEquals(0, container.size()); - } - - @Test - public void testAddBeanWithResolver() { - BeanContainer container = new BeanContainer( - Person.class); - container.setBeanIdResolver(new PersonNameResolver()); - - assertNotNull(container.addBean(new Person("John"))); - assertNotNull(container.addBeanAfter(null, new Person("Jane"))); - assertNotNull(container.addBeanAt(0, new Person("Jack"))); - - container.addAll(Arrays.asList( - new Person[] { new Person("Jill"), new Person("Joe") })); - - assertTrue(container.containsId("John")); - assertTrue(container.containsId("Jane")); - assertTrue(container.containsId("Jack")); - assertTrue(container.containsId("Jill")); - assertTrue(container.containsId("Joe")); - assertEquals(3, container.indexOfId("Jill")); - assertEquals(4, container.indexOfId("Joe")); - assertEquals(5, container.size()); - } - - @Test - public void testAddNullBeansWithResolver() { - BeanContainer container = new BeanContainer( - Person.class); - container.setBeanIdResolver(new PersonNameResolver()); - - assertNull(container.addBean(null)); - assertNull(container.addBeanAfter(null, null)); - assertNull(container.addBeanAt(0, null)); - - assertEquals(0, container.size()); - } - - @Test - public void testAddBeanWithPropertyResolver() { - BeanContainer container = new BeanContainer( - Person.class); - container.setBeanIdProperty("name"); - - assertNotNull(container.addBean(new Person("John"))); - assertNotNull(container.addBeanAfter(null, new Person("Jane"))); - assertNotNull(container.addBeanAt(0, new Person("Jack"))); - - container.addAll(Arrays.asList( - new Person[] { new Person("Jill"), new Person("Joe") })); - - assertTrue(container.containsId("John")); - assertTrue(container.containsId("Jane")); - assertTrue(container.containsId("Jack")); - assertTrue(container.containsId("Jill")); - assertTrue(container.containsId("Joe")); - assertEquals(3, container.indexOfId("Jill")); - assertEquals(4, container.indexOfId("Joe")); - assertEquals(5, container.size()); - } - - @Test - public void testAddNestedContainerProperty() { - BeanContainer container = new BeanContainer( - NestedMethodPropertyTest.Person.class); - container.setBeanIdProperty("name"); - - container.addBean(new NestedMethodPropertyTest.Person("John", - new NestedMethodPropertyTest.Address("Ruukinkatu 2-4", 20540))); - - assertTrue(container.addNestedContainerProperty("address.street")); - assertEquals("Ruukinkatu 2-4", container - .getContainerProperty("John", "address.street").getValue()); - } - - @Test - public void testNestedContainerPropertyWithNullBean() { - BeanContainer container = new BeanContainer( - NestedMethodPropertyTest.Person.class); - container.setBeanIdProperty("name"); - - container.addBean(new NestedMethodPropertyTest.Person("John", null)); - assertTrue(container - .addNestedContainerProperty("address.postalCodeObject")); - assertTrue(container.addNestedContainerProperty("address.street")); - // the nested properties added with allowNullBean setting should return - // null - assertNull(container.getContainerProperty("John", "address.street") - .getValue()); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java b/server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java deleted file mode 100644 index a5bdcc7cf9..0000000000 --- a/server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.vaadin.data.util; - -import java.util.Date; -import java.util.concurrent.atomic.AtomicLong; - -public class BeanItemContainerGenerator { - - public static class PortableRandom { - private final static long multiplier = 0x5DEECE66DL; - private final static long addend = 0xBL; - private final static long mask = (1L << 48) - 1; - private AtomicLong seed; - - public PortableRandom(long seed) { - this.seed = new AtomicLong(0L); - setSeed(seed); - } - - synchronized public void setSeed(long seed) { - seed = (seed ^ multiplier) & mask; - this.seed.set(seed); - } - - public int nextInt(int n) { - if (n <= 0) { - throw new IllegalArgumentException("n must be positive"); - } - - if ((n & -n) == n) { - return (int) ((n * (long) next(31)) >> 31); - } - - int bits, val; - do { - bits = next(31); - val = bits % n; - } while (bits - val + (n - 1) < 0); - return val; - } - - protected int next(int bits) { - long oldseed, nextseed; - AtomicLong seed = this.seed; - do { - oldseed = seed.get(); - nextseed = (oldseed * multiplier + addend) & mask; - } while (!seed.compareAndSet(oldseed, nextseed)); - return (int) (nextseed >>> (48 - bits)); - } - - public boolean nextBoolean() { - return next(1) != 0; - } - - } - - public static BeanItemContainer createContainer(int size) { - return createContainer(size, new Date().getTime()); - } - - public static BeanItemContainer createContainer(int size, - long seed) { - - BeanItemContainer container = new BeanItemContainer( - TestBean.class); - PortableRandom r = new PortableRandom(seed); - for (int i = 0; i < size; i++) { - container.addBean(new TestBean(r)); - } - - return container; - - } - - public static class TestBean { - private String name, address, city, country; - private int age, shoesize; - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public int getShoesize() { - return shoesize; - } - - public void setShoesize(int shoesize) { - this.shoesize = shoesize; - } - - public TestBean(PortableRandom r) { - age = r.nextInt(100) + 5; - shoesize = r.nextInt(10) + 35; - name = createRandomString(r, r.nextInt(5) + 5); - address = createRandomString(r, r.nextInt(15) + 5) + " " - + r.nextInt(100) + 1; - city = createRandomString(r, r.nextInt(7) + 3); - if (r.nextBoolean()) { - country = createRandomString(r, r.nextInt(4) + 4); - } - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getCountry() { - return country; - } - - public void setCountry(String country) { - this.country = country; - } - - } - - public static String createRandomString(PortableRandom r, int len) { - StringBuilder b = new StringBuilder(); - for (int i = 0; i < len; i++) { - b.append((char) (r.nextInt('z' - 'a') + 'a')); - } - - return b.toString(); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java b/server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java deleted file mode 100644 index 4f4e35258f..0000000000 --- a/server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.vaadin.data.util; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.Assert; -import org.junit.Test; - -public class BeanItemContainerSortTest { - public class Person { - private String name; - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - private int age; - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - - public class Parent extends Person { - private Set children = new HashSet(); - - public void setChildren(Set children) { - this.children = children; - } - - public Set getChildren() { - return children; - } - } - - String[] names = new String[] { "Antti", "Ville", "Sirkka", "Jaakko", - "Pekka", "John" }; - int[] ages = new int[] { 10, 20, 50, 12, 64, 67 }; - String[] sortedByAge = new String[] { names[0], names[3], names[1], - names[2], names[4], names[5] }; - - public BeanItemContainer getContainer() { - BeanItemContainer bc = new BeanItemContainer( - Person.class); - for (int i = 0; i < names.length; i++) { - Person p = new Person(); - p.setName(names[i]); - p.setAge(ages[i]); - bc.addBean(p); - } - return bc; - - } - - public BeanItemContainer getParentContainer() { - BeanItemContainer bc = new BeanItemContainer( - Parent.class); - for (int i = 0; i < names.length; i++) { - Parent p = new Parent(); - p.setName(names[i]); - p.setAge(ages[i]); - bc.addBean(p); - } - return bc; - } - - @Test - public void testSort() { - testSort(true); - } - - public void testSort(boolean b) { - BeanItemContainer container = getContainer(); - container.sort(new Object[] { "name" }, new boolean[] { b }); - - List asList = Arrays.asList(names); - Collections.sort(asList); - if (!b) { - Collections.reverse(asList); - } - - int i = 0; - for (String string : asList) { - Person idByIndex = container.getIdByIndex(i++); - Assert.assertTrue(container.containsId(idByIndex)); - Assert.assertEquals(string, idByIndex.getName()); - } - } - - @Test - public void testReverseSort() { - testSort(false); - } - - @Test - public void primitiveSorting() { - BeanItemContainer container = getContainer(); - container.sort(new Object[] { "age" }, new boolean[] { true }); - - int i = 0; - for (String string : sortedByAge) { - Person idByIndex = container.getIdByIndex(i++); - Assert.assertTrue(container.containsId(idByIndex)); - Assert.assertEquals(string, idByIndex.getName()); - } - } - - @Test - public void customSorting() { - BeanItemContainer container = getContainer(); - - // custom sorter using the reverse order - container.setItemSorter(new DefaultItemSorter() { - @Override - public int compare(Object o1, Object o2) { - return -super.compare(o1, o2); - } - }); - - container.sort(new Object[] { "age" }, new boolean[] { true }); - - int i = container.size() - 1; - for (String string : sortedByAge) { - Person idByIndex = container.getIdByIndex(i--); - Assert.assertTrue(container.containsId(idByIndex)); - Assert.assertEquals(string, idByIndex.getName()); - } - } - - @Test - public void testGetSortableProperties() { - BeanItemContainer container = getContainer(); - - Collection sortablePropertyIds = container - .getSortableContainerPropertyIds(); - Assert.assertEquals(2, sortablePropertyIds.size()); - Assert.assertTrue(sortablePropertyIds.contains("name")); - Assert.assertTrue(sortablePropertyIds.contains("age")); - } - - @Test - public void testGetNonSortableProperties() { - BeanItemContainer container = getParentContainer(); - - Assert.assertEquals(3, container.getContainerPropertyIds().size()); - - Collection sortablePropertyIds = container - .getSortableContainerPropertyIds(); - Assert.assertEquals(2, sortablePropertyIds.size()); - Assert.assertTrue(sortablePropertyIds.contains("name")); - Assert.assertTrue(sortablePropertyIds.contains("age")); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java b/server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java deleted file mode 100644 index 19b0835fd6..0000000000 --- a/server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java +++ /dev/null @@ -1,997 +0,0 @@ -package com.vaadin.data.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Item; -import com.vaadin.data.util.NestedMethodPropertyTest.Address; -import com.vaadin.data.util.filter.Compare; - -/** - * Test basic functionality of BeanItemContainer. - * - * Most sorting related tests are in {@link BeanItemContainerSortTest}. - */ -public class BeanItemContainerTest extends AbstractBeanContainerTestBase { - - // basics from the common container test - - private Map nameToBean = new LinkedHashMap(); - - private BeanItemContainer getContainer() { - return new BeanItemContainer(ClassName.class); - } - - @Before - public void setUp() { - nameToBean.clear(); - - for (int i = 0; i < sampleData.length; i++) { - ClassName className = new ClassName(sampleData[i], i); - nameToBean.put(sampleData[i], className); - } - } - - @Override - @SuppressWarnings("unchecked") - protected void initializeContainer(Container container) { - BeanItemContainer beanItemContainer = (BeanItemContainer) container; - - beanItemContainer.removeAllItems(); - - Iterator it = nameToBean.values().iterator(); - while (it.hasNext()) { - beanItemContainer.addBean(it.next()); - } - } - - @Override - protected void validateContainer(Container container, - Object expectedFirstItemId, Object expectedLastItemId, - Object itemIdInSet, Object itemIdNotInSet, boolean checkGetItemNull, - int expectedSize) { - Object notInSet = nameToBean.get(itemIdNotInSet); - if (notInSet == null && itemIdNotInSet != null) { - notInSet = new ClassName(String.valueOf(itemIdNotInSet), 9999); - } - super.validateContainer(container, nameToBean.get(expectedFirstItemId), - nameToBean.get(expectedLastItemId), nameToBean.get(itemIdInSet), - notInSet, checkGetItemNull, expectedSize); - } - - @Override - protected boolean isFilteredOutItemNull() { - return false; - } - - @Test - public void testGetType_existingProperty_typeReturned() { - BeanItemContainer container = getContainer(); - Assert.assertEquals( - "Unexpected type is returned for property 'simpleName'", - String.class, container.getType("simpleName")); - } - - @Test - public void testGetType_notExistingProperty_nullReturned() { - BeanItemContainer container = getContainer(); - Assert.assertNull("Not null type is returned for property ''", - container.getType("")); - } - - @Test - public void testBasicOperations() { - testBasicContainerOperations(getContainer()); - } - - @Test - public void testFiltering() { - testContainerFiltering(getContainer()); - } - - @Test - public void testSorting() { - testContainerSorting(getContainer()); - } - - @Test - public void testSortingAndFiltering() { - testContainerSortingAndFiltering(getContainer()); - } - - // duplicated from parent class and modified - adding items to - // BeanItemContainer differs from other containers - @Test - public void testContainerOrdered() { - BeanItemContainer container = new BeanItemContainer( - String.class); - - String id = "test1"; - - Item item = container.addBean(id); - assertNotNull(item); - - assertEquals(id, container.firstItemId()); - assertEquals(id, container.lastItemId()); - - // isFirstId - assertTrue(container.isFirstId(id)); - assertTrue(container.isFirstId(container.firstItemId())); - // isLastId - assertTrue(container.isLastId(id)); - assertTrue(container.isLastId(container.lastItemId())); - - // Add a new item before the first - // addItemAfter - String newFirstId = "newFirst"; - item = container.addItemAfter(null, newFirstId); - assertNotNull(item); - assertNotNull(container.getItem(newFirstId)); - - // isFirstId - assertTrue(container.isFirstId(newFirstId)); - assertTrue(container.isFirstId(container.firstItemId())); - // isLastId - assertTrue(container.isLastId(id)); - assertTrue(container.isLastId(container.lastItemId())); - - // nextItemId - assertEquals(id, container.nextItemId(newFirstId)); - assertNull(container.nextItemId(id)); - assertNull(container.nextItemId("not-in-container")); - - // prevItemId - assertEquals(newFirstId, container.prevItemId(id)); - assertNull(container.prevItemId(newFirstId)); - assertNull(container.prevItemId("not-in-container")); - - // addItemAfter(Object) - String newSecondItemId = "newSecond"; - item = container.addItemAfter(newFirstId, newSecondItemId); - // order is now: newFirstId, newSecondItemId, id - assertNotNull(item); - assertNotNull(container.getItem(newSecondItemId)); - assertEquals(id, container.nextItemId(newSecondItemId)); - assertEquals(newFirstId, container.prevItemId(newSecondItemId)); - - // addItemAfter(Object,Object) - String fourthId = "id of the fourth item"; - Item fourth = container.addItemAfter(newFirstId, fourthId); - // order is now: newFirstId, fourthId, newSecondItemId, id - assertNotNull(fourth); - assertEquals(fourth, container.getItem(fourthId)); - assertEquals(newSecondItemId, container.nextItemId(fourthId)); - assertEquals(newFirstId, container.prevItemId(fourthId)); - - // addItemAfter(Object,Object) - Object fifthId = "fifth"; - Item fifth = container.addItemAfter(null, fifthId); - // order is now: fifthId, newFirstId, fourthId, newSecondItemId, id - assertNotNull(fifth); - assertEquals(fifth, container.getItem(fifthId)); - assertEquals(newFirstId, container.nextItemId(fifthId)); - assertNull(container.prevItemId(fifthId)); - - } - - @Test - public void testContainerIndexed() { - testContainerIndexed(getContainer(), nameToBean.get(sampleData[2]), 2, - false, new ClassName("org.vaadin.test.Test", 8888), true); - } - - @SuppressWarnings("deprecation") - @Test - public void testCollectionConstructors() { - List classNames = new ArrayList(); - classNames.add(new ClassName("a.b.c.Def", 1)); - classNames.add(new ClassName("a.b.c.Fed", 2)); - classNames.add(new ClassName("b.c.d.Def", 3)); - - // note that this constructor is problematic, users should use the - // version that - // takes the bean class as a parameter - BeanItemContainer container = new BeanItemContainer( - classNames); - - Assert.assertEquals(3, container.size()); - Assert.assertEquals(classNames.get(0), container.firstItemId()); - Assert.assertEquals(classNames.get(1), container.getIdByIndex(1)); - Assert.assertEquals(classNames.get(2), container.lastItemId()); - - BeanItemContainer container2 = new BeanItemContainer( - ClassName.class, classNames); - - Assert.assertEquals(3, container2.size()); - Assert.assertEquals(classNames.get(0), container2.firstItemId()); - Assert.assertEquals(classNames.get(1), container2.getIdByIndex(1)); - Assert.assertEquals(classNames.get(2), container2.lastItemId()); - } - - // this only applies to the collection constructor with no type parameter - @SuppressWarnings("deprecation") - @Test - public void testEmptyCollectionConstructor() { - try { - new BeanItemContainer((Collection) null); - Assert.fail( - "Initializing BeanItemContainer from a null collection should not work!"); - } catch (IllegalArgumentException e) { - // success - } - try { - new BeanItemContainer(new ArrayList()); - Assert.fail( - "Initializing BeanItemContainer from an empty collection should not work!"); - } catch (IllegalArgumentException e) { - // success - } - } - - @Test - public void testItemSetChangeListeners() { - BeanItemContainer container = getContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - container.addListener(counter); - - ClassName cn1 = new ClassName("com.example.Test", 1111); - ClassName cn2 = new ClassName("com.example.Test2", 2222); - - initializeContainer(container); - counter.reset(); - container.addBean(cn1); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - container.addItem(cn1); - counter.assertOnce(); - // no notification if already in container - container.addItem(cn1); - counter.assertNone(); - container.addItem(cn2); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(null, cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.firstItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(container.firstItemId(), cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.getIdByIndex(1), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(container.lastItemId(), cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.lastItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAt(0, cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.firstItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAt(1, cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.getIdByIndex(1), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAt(container.size(), cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.lastItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.removeItem(nameToBean.get(sampleData[0])); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - // no notification for removing a non-existing item - container.removeItem(cn1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.removeAllItems(); - counter.assertOnce(); - // already empty - container.removeAllItems(); - counter.assertNone(); - - } - - @Test - public void testItemSetChangeListenersFiltering() { - BeanItemContainer container = getContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - container.addListener(counter); - - ClassName cn1 = new ClassName("com.example.Test", 1111); - ClassName cn2 = new ClassName("com.example.Test2", 2222); - ClassName other = new ClassName("com.example.Other", 3333); - - // simply adding or removing container filters should cause event - // (content changes) - - initializeContainer(container); - counter.reset(); - container.addContainerFilter(SIMPLE_NAME, "a", true, false); - counter.assertOnce(); - container.removeContainerFilters(SIMPLE_NAME); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - container.addContainerFilter(SIMPLE_NAME, "a", true, false); - counter.assertOnce(); - container.removeAllContainerFilters(); - counter.assertOnce(); - - // perform operations while filtering container - - initializeContainer(container); - counter.reset(); - container.addContainerFilter(FULLY_QUALIFIED_NAME, "Test", true, false); - counter.assertOnce(); - - // passes filter - container.addBean(cn1); - counter.assertOnce(); - - // passes filter but already in the container - container.addBean(cn1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - - // passes filter - container.addItem(cn1); - counter.assertOnce(); - // already in the container - container.addItem(cn1); - counter.assertNone(); - container.addItem(cn2); - counter.assertOnce(); - // does not pass filter - container.addItem(other); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(null, cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.firstItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(container.firstItemId(), cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.getIdByIndex(1), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(container.lastItemId(), cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.lastItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAt(0, cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.firstItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAt(1, cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.getIdByIndex(1), - FULLY_QUALIFIED_NAME).getValue()); - - initializeContainer(container); - counter.reset(); - container.addItemAt(container.size(), cn1); - counter.assertOnce(); - Assert.assertEquals("com.example.Test", - container.getContainerProperty(container.lastItemId(), - FULLY_QUALIFIED_NAME).getValue()); - - // does not pass filter - // note: testAddRemoveWhileFiltering() checks position for these after - // removing filter etc, here concentrating on listeners - - initializeContainer(container); - counter.reset(); - container.addItemAfter(null, other); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(container.firstItemId(), other); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(container.lastItemId(), other); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAt(0, other); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAt(1, other); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAt(container.size(), other); - counter.assertNone(); - - // passes filter - - initializeContainer(container); - counter.reset(); - container.addItem(cn1); - counter.assertOnce(); - container.removeItem(cn1); - counter.assertOnce(); - - // does not pass filter - - initializeContainer(container); - counter.reset(); - // not visible - container.removeItem(nameToBean.get(sampleData[0])); - counter.assertNone(); - - container.removeAllItems(); - counter.assertOnce(); - // no visible items - container.removeAllItems(); - counter.assertNone(); - } - - @Test - public void testAddRemoveWhileFiltering() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - - Person john = new Person("John"); - Person jane = new Person("Jane"); - Person matthew = new Person("Matthew"); - - Person jack = new Person("Jack"); - Person michael = new Person("Michael"); - Person william = new Person("William"); - Person julia = new Person("Julia"); - Person george = new Person("George"); - Person mark = new Person("Mark"); - - container.addBean(john); - container.addBean(jane); - container.addBean(matthew); - - assertEquals(3, container.size()); - // john, jane, matthew - - container.addContainerFilter("name", "j", true, true); - - assertEquals(2, container.size()); - // john, jane, (matthew) - - // add a bean that passes the filter - container.addBean(jack); - assertEquals(3, container.size()); - assertEquals(jack, container.lastItemId()); - // john, jane, (matthew), jack - - // add beans that do not pass the filter - container.addBean(michael); - // john, jane, (matthew), jack, (michael) - container.addItemAfter(null, william); - // (william), john, jane, (matthew), jack, (michael) - - // add after an item that is shown - container.addItemAfter(john, george); - // (william), john, (george), jane, (matthew), jack, (michael) - assertEquals(3, container.size()); - assertEquals(john, container.firstItemId()); - - // add after an item that is not shown does nothing - container.addItemAfter(william, julia); - // (william), john, (george), jane, (matthew), jack, (michael) - assertEquals(3, container.size()); - assertEquals(john, container.firstItemId()); - - container.addItemAt(1, julia); - // (william), john, julia, (george), jane, (matthew), jack, (michael) - - container.addItemAt(2, mark); - // (william), john, julia, (mark), (george), jane, (matthew), jack, - // (michael) - - container.removeItem(matthew); - // (william), john, julia, (mark), (george), jane, jack, (michael) - - assertEquals(4, container.size()); - assertEquals(jack, container.lastItemId()); - - container.removeContainerFilters("name"); - - assertEquals(8, container.size()); - assertEquals(william, container.firstItemId()); - assertEquals(john, container.nextItemId(william)); - assertEquals(julia, container.nextItemId(john)); - assertEquals(mark, container.nextItemId(julia)); - assertEquals(george, container.nextItemId(mark)); - assertEquals(jane, container.nextItemId(george)); - assertEquals(jack, container.nextItemId(jane)); - assertEquals(michael, container.lastItemId()); - } - - @Test - public void testRefilterOnPropertyModification() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - - Person john = new Person("John"); - Person jane = new Person("Jane"); - Person matthew = new Person("Matthew"); - - container.addBean(john); - container.addBean(jane); - container.addBean(matthew); - - assertEquals(3, container.size()); - // john, jane, matthew - - container.addContainerFilter("name", "j", true, true); - - assertEquals(2, container.size()); - // john, jane, (matthew) - - // #6053 currently, modification of an item that is not visible does not - // trigger refiltering - should it? - // matthew.setName("Julia"); - // assertEquals(3, container.size()); - // john, jane, julia - - john.setName("Mark"); - assertEquals(2, container.size()); - // (mark), jane, julia - - container.removeAllContainerFilters(); - - assertEquals(3, container.size()); - } - - @Test - public void testAddAll() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - - Person john = new Person("John"); - Person jane = new Person("Jane"); - Person matthew = new Person("Matthew"); - - container.addBean(john); - container.addBean(jane); - container.addBean(matthew); - - assertEquals(3, container.size()); - // john, jane, matthew - - Person jack = new Person("Jack"); - Person michael = new Person("Michael"); - - // addAll - container.addAll(Arrays.asList(jack, michael)); - // john, jane, matthew, jack, michael - - assertEquals(5, container.size()); - assertEquals(jane, container.nextItemId(john)); - assertEquals(matthew, container.nextItemId(jane)); - assertEquals(jack, container.nextItemId(matthew)); - assertEquals(michael, container.nextItemId(jack)); - } - - @Test - public void testUnsupportedMethods() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - container.addBean(new Person("John")); - - try { - container.addItem(); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addItemAfter(new Person("Jane")); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addItemAt(0); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - try { - container.addContainerProperty("lastName", String.class, ""); - Assert.fail(); - } catch (UnsupportedOperationException e) { - // should get exception - } - - assertEquals(1, container.size()); - } - - @Test - public void testRemoveContainerProperty() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person john = new Person("John"); - container.addBean(john); - - Assert.assertEquals("John", - container.getContainerProperty(john, "name").getValue()); - Assert.assertTrue(container.removeContainerProperty("name")); - Assert.assertNull(container.getContainerProperty(john, "name")); - - Assert.assertNotNull(container.getItem(john)); - // property removed also from item - Assert.assertNull(container.getItem(john).getItemProperty("name")); - } - - @Test - public void testAddNullBean() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person john = new Person("John"); - container.addBean(john); - - assertNull(container.addItem(null)); - assertNull(container.addItemAfter(null, null)); - assertNull(container.addItemAfter(john, null)); - assertNull(container.addItemAt(0, null)); - - assertEquals(1, container.size()); - } - - @Test - public void testBeanIdResolver() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person john = new Person("John"); - - assertSame(john, container.getBeanIdResolver().getIdForBean(john)); - } - - @Test(expected = IllegalArgumentException.class) - public void testNullBeanClass() { - new BeanItemContainer((Class) null); - } - - @Test - public void testAddNestedContainerProperty() { - BeanItemContainer container = new BeanItemContainer( - NestedMethodPropertyTest.Person.class); - - NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( - "John", - new NestedMethodPropertyTest.Address("Ruukinkatu 2-4", 20540)); - container.addBean(john); - - assertTrue(container.addNestedContainerProperty("address.street")); - assertEquals("Ruukinkatu 2-4", container - .getContainerProperty(john, "address.street").getValue()); - } - - @Test - public void testNestedContainerPropertyWithNullBean() { - BeanItemContainer container = new BeanItemContainer( - NestedMethodPropertyTest.Person.class); - NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( - "John", null); - assertNotNull(container.addBean(john)); - assertTrue(container - .addNestedContainerProperty("address.postalCodeObject")); - assertTrue(container.addNestedContainerProperty("address.street")); - // the nested properties should return null - assertNull(container.getContainerProperty(john, "address.street") - .getValue()); - } - - @Test - public void testItemAddedEvent() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - ItemSetChangeListener addListener = createListenerMockFor(container); - addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); - EasyMock.replay(addListener); - - container.addItem(bean); - - EasyMock.verify(addListener); - } - - @Test - public void testItemAddedEvent_AddedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - container.addItem(bean); - - assertEquals(bean, capturedEvent.getValue().getFirstItemId()); - } - - @Test - public void testItemAddedEvent_addItemAt_IndexOfAddedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - container.addItemAt(1, new Person("")); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - @Test - public void testItemAddedEvent_addItemAfter_IndexOfAddedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - container.addItemAfter(bean, new Person("")); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - @Test - public void testItemAddedEvent_amountOfAddedItems() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - List beans = Arrays.asList(new Person("Jack"), - new Person("John")); - - container.addAll(beans); - - assertEquals(2, capturedEvent.getValue().getAddedItemsCount()); - } - - @Test - public void testItemAddedEvent_someItemsAreFiltered_amountOfAddedItemsIsReducedByAmountOfFilteredItems() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - List beans = Arrays.asList(new Person("Jack"), - new Person("John")); - container.addFilter(new Compare.Equal("name", "John")); - - container.addAll(beans); - - assertEquals(1, capturedEvent.getValue().getAddedItemsCount()); - } - - @Test - public void testItemAddedEvent_someItemsAreFiltered_addedItemIsTheFirstVisibleItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - List beans = Arrays.asList(new Person("Jack"), bean); - container.addFilter(new Compare.Equal("name", "John")); - - container.addAll(beans); - - assertEquals(bean, capturedEvent.getValue().getFirstItemId()); - } - - @Test - public void testItemRemovedEvent() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener removeListener = createListenerMockFor(container); - removeListener - .containerItemSetChange(EasyMock.isA(ItemRemoveEvent.class)); - EasyMock.replay(removeListener); - - container.removeItem(bean); - - EasyMock.verify(removeListener); - } - - @Test - public void testItemRemovedEvent_RemovedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeItem(bean); - - assertEquals(bean, capturedEvent.getValue().getFirstItemId()); - } - - @Test - public void testItemRemovedEvent_indexOfRemovedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - container.addItem(new Person("Jack")); - Person secondBean = new Person("John"); - container.addItem(secondBean); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeItem(secondBean); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - @Test - public void testItemRemovedEvent_amountOfRemovedItems() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - container.addItem(new Person("Jack")); - container.addItem(new Person("John")); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeAllItems(); - - assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); - } - - private Capture captureAddEvent( - ItemSetChangeListener addListener) { - Capture capturedEvent = new Capture(); - addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private Capture captureRemoveEvent( - ItemSetChangeListener removeListener) { - Capture capturedEvent = new Capture(); - removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private ItemSetChangeListener createListenerMockFor( - BeanItemContainer container) { - ItemSetChangeListener listener = EasyMock - .createNiceMock(ItemSetChangeListener.class); - container.addItemSetChangeListener(listener); - return listener; - } - - @Test - public void testAddNestedContainerBeanBeforeData() { - BeanItemContainer container = new BeanItemContainer( - NestedMethodPropertyTest.Person.class); - - container.addNestedContainerBean("address"); - - assertTrue( - container.getContainerPropertyIds().contains("address.street")); - - NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( - "John", new Address("streetname", 12345)); - container.addBean(john); - - assertTrue(container.getItem(john).getItemPropertyIds() - .contains("address.street")); - assertEquals("streetname", container.getItem(john) - .getItemProperty("address.street").getValue()); - - } - - @Test - public void testAddNestedContainerBeanAfterData() { - BeanItemContainer container = new BeanItemContainer( - NestedMethodPropertyTest.Person.class); - - NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( - "John", new Address("streetname", 12345)); - container.addBean(john); - - container.addNestedContainerBean("address"); - - assertTrue( - container.getContainerPropertyIds().contains("address.street")); - assertTrue(container.getItem(john).getItemPropertyIds() - .contains("address.street")); - assertEquals("streetname", container.getItem(john) - .getItemProperty("address.street").getValue()); - - } -} diff --git a/server/src/test/java/com/vaadin/data/util/BeanItemTest.java b/server/src/test/java/com/vaadin/data/util/BeanItemTest.java deleted file mode 100644 index 1bbe74818c..0000000000 --- a/server/src/test/java/com/vaadin/data/util/BeanItemTest.java +++ /dev/null @@ -1,389 +0,0 @@ -package com.vaadin.data.util; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Property; - -/** - * Test BeanItem specific features. - * - * Only public API is tested, not the methods with package visibility. - * - * See also {@link PropertySetItemTest}, which tests the base class. - */ -public class BeanItemTest { - - @SuppressWarnings("unused") - protected static class MySuperClass { - private int superPrivate = 1; - private int superPrivate2 = 2; - protected double superProtected = 3.0; - private double superProtected2 = 4.0; - public boolean superPublic = true; - private boolean superPublic2 = true; - - public int getSuperPrivate() { - return superPrivate; - } - - public void setSuperPrivate(int superPrivate) { - this.superPrivate = superPrivate; - } - - public double getSuperProtected() { - return superProtected; - } - - public void setSuperProtected(double superProtected) { - this.superProtected = superProtected; - } - - public boolean isSuperPublic() { - return superPublic; - } - - public void setSuperPublic(boolean superPublic) { - this.superPublic = superPublic; - } - - } - - protected static class MyClass extends MySuperClass { - private String name; - public int value = 123; - - public MyClass(String name) { - this.name = name; - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setNoField(String name) { - } - - public String getNoField() { - return "no field backing this setter"; - } - - public String getName2() { - return name; - } - } - - protected static class MyClass2 extends MyClass { - public MyClass2(String name) { - super(name); - } - - @Override - public void setName(String name) { - super.setName(name + "2"); - } - - @Override - public String getName() { - return super.getName() + "2"; - } - - @Override - public String getName2() { - return super.getName(); - } - - public void setName2(String name) { - super.setName(name); - } - } - - protected static interface MySuperInterface { - public int getSuper1(); - - public void setSuper1(int i); - - public int getOverride(); - } - - protected static interface MySuperInterface2 { - public int getSuper2(); - } - - protected static class Generic { - - public T getProperty() { - return null; - } - - public void setProperty(T t) { - throw new UnsupportedOperationException(); - } - } - - protected static class SubClass extends Generic { - - @Override - // Has a bridged method - public String getProperty() { - return ""; - } - - @Override - // Has a bridged method - public void setProperty(String t) { - } - } - - protected static interface MySubInterface - extends MySuperInterface, MySuperInterface2 { - public int getSub(); - - public void setSub(int i); - - @Override - public int getOverride(); - - public void setOverride(int i); - } - - @Test - public void testGetProperties() { - BeanItem item = new BeanItem( - new MySuperClass()); - - Collection itemPropertyIds = item.getItemPropertyIds(); - Assert.assertEquals(3, itemPropertyIds.size()); - Assert.assertTrue(itemPropertyIds.contains("superPrivate")); - Assert.assertTrue(itemPropertyIds.contains("superProtected")); - Assert.assertTrue(itemPropertyIds.contains("superPublic")); - } - - @Test - public void testGetSuperClassProperties() { - BeanItem item = new BeanItem(new MyClass("bean1")); - - Collection itemPropertyIds = item.getItemPropertyIds(); - Assert.assertEquals(6, itemPropertyIds.size()); - Assert.assertTrue(itemPropertyIds.contains("superPrivate")); - Assert.assertTrue(itemPropertyIds.contains("superProtected")); - Assert.assertTrue(itemPropertyIds.contains("superPublic")); - Assert.assertTrue(itemPropertyIds.contains("name")); - Assert.assertTrue(itemPropertyIds.contains("noField")); - Assert.assertTrue(itemPropertyIds.contains("name2")); - } - - @Test - public void testOverridingProperties() { - BeanItem item = new BeanItem(new MyClass2("bean2")); - - Collection itemPropertyIds = item.getItemPropertyIds(); - Assert.assertEquals(6, itemPropertyIds.size()); - - Assert.assertTrue(MyClass2.class.equals(item.getBean().getClass())); - - // check that name2 accessed via MyClass2, not MyClass - Assert.assertFalse(item.getItemProperty("name2").isReadOnly()); - } - - @Test - public void testGetInterfaceProperties() throws SecurityException, - NoSuchMethodException, IllegalArgumentException, - IllegalAccessException, InvocationTargetException { - Method method = BeanItem.class - .getDeclaredMethod("getPropertyDescriptors", Class.class); - method.setAccessible(true); - LinkedHashMap> propertyDescriptors = (LinkedHashMap>) method - .invoke(null, MySuperInterface.class); - - Assert.assertEquals(2, propertyDescriptors.size()); - Assert.assertTrue(propertyDescriptors.containsKey("super1")); - Assert.assertTrue(propertyDescriptors.containsKey("override")); - - MethodProperty property = (MethodProperty) propertyDescriptors - .get("override").createProperty(getClass()); - Assert.assertTrue(property.isReadOnly()); - } - - @Test - public void testGetSuperInterfaceProperties() throws SecurityException, - NoSuchMethodException, IllegalArgumentException, - IllegalAccessException, InvocationTargetException { - Method method = BeanItem.class - .getDeclaredMethod("getPropertyDescriptors", Class.class); - method.setAccessible(true); - LinkedHashMap> propertyDescriptors = (LinkedHashMap>) method - .invoke(null, MySubInterface.class); - - Assert.assertEquals(4, propertyDescriptors.size()); - Assert.assertTrue(propertyDescriptors.containsKey("sub")); - Assert.assertTrue(propertyDescriptors.containsKey("super1")); - Assert.assertTrue(propertyDescriptors.containsKey("super2")); - Assert.assertTrue(propertyDescriptors.containsKey("override")); - - MethodProperty property = (MethodProperty) propertyDescriptors - .get("override").createProperty(getClass()); - Assert.assertFalse(property.isReadOnly()); - } - - @Test - public void testPropertyExplicitOrder() { - Collection ids = new ArrayList(); - ids.add("name"); - ids.add("superPublic"); - ids.add("name2"); - ids.add("noField"); - - BeanItem item = new BeanItem(new MyClass("bean1"), - ids); - - Iterator it = item.getItemPropertyIds().iterator(); - Assert.assertEquals("name", it.next()); - Assert.assertEquals("superPublic", it.next()); - Assert.assertEquals("name2", it.next()); - Assert.assertEquals("noField", it.next()); - Assert.assertFalse(it.hasNext()); - } - - @Test - public void testPropertyExplicitOrder2() { - BeanItem item = new BeanItem(new MyClass("bean1"), - new String[] { "name", "superPublic", "name2", "noField" }); - - Iterator it = item.getItemPropertyIds().iterator(); - Assert.assertEquals("name", it.next()); - Assert.assertEquals("superPublic", it.next()); - Assert.assertEquals("name2", it.next()); - Assert.assertEquals("noField", it.next()); - Assert.assertFalse(it.hasNext()); - } - - @Test - public void testPropertyBadPropertyName() { - Collection ids = new ArrayList(); - ids.add("name3"); - ids.add("name"); - - // currently silently ignores non-existent properties - BeanItem item = new BeanItem(new MyClass("bean1"), - ids); - - Iterator it = item.getItemPropertyIds().iterator(); - Assert.assertEquals("name", it.next()); - Assert.assertFalse(it.hasNext()); - } - - @Test - public void testRemoveProperty() { - BeanItem item = new BeanItem(new MyClass("bean1")); - - Collection itemPropertyIds = item.getItemPropertyIds(); - Assert.assertEquals(6, itemPropertyIds.size()); - - item.removeItemProperty("name2"); - Assert.assertEquals(5, itemPropertyIds.size()); - Assert.assertFalse(itemPropertyIds.contains("name2")); - } - - @Test - public void testRemoveSuperProperty() { - BeanItem item = new BeanItem(new MyClass("bean1")); - - Collection itemPropertyIds = item.getItemPropertyIds(); - Assert.assertEquals(6, itemPropertyIds.size()); - - item.removeItemProperty("superPrivate"); - Assert.assertEquals(5, itemPropertyIds.size()); - Assert.assertFalse(itemPropertyIds.contains("superPrivate")); - } - - @Test - public void testPropertyTypes() { - BeanItem item = new BeanItem(new MyClass("bean1")); - - Assert.assertTrue(Integer.class - .equals(item.getItemProperty("superPrivate").getType())); - Assert.assertTrue(Double.class - .equals(item.getItemProperty("superProtected").getType())); - Assert.assertTrue(Boolean.class - .equals(item.getItemProperty("superPublic").getType())); - Assert.assertTrue( - String.class.equals(item.getItemProperty("name").getType())); - } - - @Test - public void testPropertyReadOnly() { - BeanItem item = new BeanItem(new MyClass("bean1")); - - Assert.assertFalse(item.getItemProperty("name").isReadOnly()); - Assert.assertTrue(item.getItemProperty("name2").isReadOnly()); - } - - @Test - public void testCustomProperties() throws Exception { - LinkedHashMap> propertyDescriptors = new LinkedHashMap>(); - propertyDescriptors.put("myname", - new MethodPropertyDescriptor("myname", - MyClass.class, - MyClass.class.getDeclaredMethod("getName"), - MyClass.class.getDeclaredMethod("setName", - String.class))); - MyClass instance = new MyClass("bean1"); - Constructor constructor = BeanItem.class - .getDeclaredConstructor(Object.class, Map.class); - constructor.setAccessible(true); - BeanItem item = constructor.newInstance(instance, - propertyDescriptors); - - Assert.assertEquals(1, item.getItemPropertyIds().size()); - Assert.assertEquals("bean1", item.getItemProperty("myname").getValue()); - } - - @Test - public void testAddRemoveProperty() throws Exception { - MethodPropertyDescriptor pd = new MethodPropertyDescriptor( - "myname", MyClass.class, - MyClass.class.getDeclaredMethod("getName"), - MyClass.class.getDeclaredMethod("setName", String.class)); - - BeanItem item = new BeanItem(new MyClass("bean1")); - - Assert.assertEquals(6, item.getItemPropertyIds().size()); - Assert.assertEquals(null, item.getItemProperty("myname")); - - item.addItemProperty("myname", pd.createProperty(item.getBean())); - Assert.assertEquals(7, item.getItemPropertyIds().size()); - Assert.assertEquals("bean1", item.getItemProperty("myname").getValue()); - item.removeItemProperty("myname"); - Assert.assertEquals(6, item.getItemPropertyIds().size()); - Assert.assertEquals(null, item.getItemProperty("myname")); - } - - @Test - public void testOverridenGenericMethods() { - BeanItem item = new BeanItem(new SubClass()); - - Property property = item.getItemProperty("property"); - Assert.assertEquals("Unexpected class for property type", String.class, - property.getType()); - - Assert.assertEquals("Unexpected property value", "", - property.getValue()); - - // Should not be exception - property.setValue(null); - } -} diff --git a/server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java b/server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java deleted file mode 100644 index e41f751bfd..0000000000 --- a/server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.vaadin.data.util; - -import org.junit.Test; - -public class ContainerHierarchicalWrapperTest - extends AbstractHierarchicalContainerTestBase { - - @Test - public void testBasicOperations() { - testBasicContainerOperations( - new ContainerHierarchicalWrapper(new IndexedContainer())); - } - - @Test - public void testHierarchicalContainer() { - testHierarchicalContainer( - new ContainerHierarchicalWrapper(new IndexedContainer())); - } - - @Test - public void testRemoveSubtree() { - testRemoveHierarchicalWrapperSubtree( - new ContainerHierarchicalWrapper(new IndexedContainer())); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java b/server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java deleted file mode 100644 index c3a8b2c4f9..0000000000 --- a/server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.vaadin.data.util; - -import java.util.Collection; - -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; - -public class ContainerOrderedWrapperTest extends AbstractContainerTestBase { - - // This class is needed to get an implementation of container - // which is not an implementation of Ordered interface. - private class NotOrderedContainer implements Container { - - private IndexedContainer container; - - public NotOrderedContainer() { - container = new IndexedContainer(); - } - - @Override - public Item getItem(Object itemId) { - return container.getItem(itemId); - } - - @Override - public Collection getContainerPropertyIds() { - return container.getContainerPropertyIds(); - } - - @Override - public Collection getItemIds() { - return container.getItemIds(); - } - - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - return container.getContainerProperty(itemId, propertyId); - } - - @Override - public Class getType(Object propertyId) { - return container.getType(propertyId); - } - - @Override - public int size() { - return container.size(); - } - - @Override - public boolean containsId(Object itemId) { - return container.containsId(itemId); - } - - @Override - public Item addItem(Object itemId) - throws UnsupportedOperationException { - return container.addItem(itemId); - } - - @Override - public Object addItem() throws UnsupportedOperationException { - return container.addItem(); - } - - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - return container.removeItem(itemId); - } - - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - return container.addContainerProperty(propertyId, type, - defaultValue); - } - - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - return container.removeContainerProperty(propertyId); - } - - @Override - public boolean removeAllItems() throws UnsupportedOperationException { - return container.removeAllItems(); - } - - } - - @Test - public void testBasicOperations() { - testBasicContainerOperations( - new ContainerOrderedWrapper(new NotOrderedContainer())); - } - - @Test - public void testOrdered() { - testContainerOrdered( - new ContainerOrderedWrapper(new NotOrderedContainer())); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java b/server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java deleted file mode 100644 index 58cb2b1d27..0000000000 --- a/server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.data.util; - -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.ui.Table; - -public class ContainerSizeAssertTest { - - @Test(expected = AssertionError.class) - public void testNegativeSizeAssert() { - Table table = createAttachedTable(); - - table.setContainerDataSource(createNegativeSizeContainer()); - } - - @Test - public void testZeroSizeNoAssert() { - Table table = createAttachedTable(); - - table.setContainerDataSource(new IndexedContainer()); - } - - private Container createNegativeSizeContainer() { - return new IndexedContainer() { - @Override - public int size() { - return -1; - } - }; - } - - private Table createAttachedTable() { - return new Table() { - private boolean initialized = true; - - @Override - public boolean isAttached() { - // This returns false until the super constructor has finished - return initialized; - } - }; - } -} diff --git a/server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java b/server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java deleted file mode 100644 index 1c4dc1de5e..0000000000 --- a/server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.vaadin.data.util; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.tests.util.TestUtil; - -public class ContainerSortingTest { - - private static final String ITEM_DATA_MINUS2_NULL = "Data -2 null"; - private static final String ITEM_DATA_MINUS2 = "Data -2"; - private static final String ITEM_DATA_MINUS1 = "Data -1"; - private static final String ITEM_DATA_MINUS1_NULL = "Data -1 null"; - private static final String ITEM_ANOTHER_NULL = "Another null"; - private static final String ITEM_STRING_2 = "String 2"; - private static final String ITEM_STRING_NULL2 = "String null"; - private static final String ITEM_STRING_1 = "String 1"; - - private static final String PROPERTY_INTEGER_NULL2 = "integer-null"; - private static final String PROPERTY_INTEGER_NOT_NULL = "integer-not-null"; - private static final String PROPERTY_STRING_NULL = "string-null"; - private static final String PROPERTY_STRING_ID = "string-not-null"; - - @Test - public void testEmptyFilteredIndexedContainer() { - IndexedContainer ic = new IndexedContainer(); - - addProperties(ic); - populate(ic); - - ic.addContainerFilter(PROPERTY_STRING_ID, "aasdfasdfasdf", true, false); - ic.sort(new Object[] { PROPERTY_STRING_ID }, new boolean[] { true }); - - } - - @Test - public void testFilteredIndexedContainer() { - IndexedContainer ic = new IndexedContainer(); - - addProperties(ic); - populate(ic); - - ic.addContainerFilter(PROPERTY_STRING_ID, "a", true, false); - ic.sort(new Object[] { PROPERTY_STRING_ID }, new boolean[] { true }); - verifyOrder(ic, - new String[] { ITEM_ANOTHER_NULL, ITEM_DATA_MINUS1, - ITEM_DATA_MINUS1_NULL, ITEM_DATA_MINUS2, - ITEM_DATA_MINUS2_NULL, }); - } - - @Test - public void testIndexedContainer() { - IndexedContainer ic = new IndexedContainer(); - - addProperties(ic); - populate(ic); - - ic.sort(new Object[] { PROPERTY_STRING_ID }, new boolean[] { true }); - verifyOrder(ic, new String[] { ITEM_ANOTHER_NULL, ITEM_DATA_MINUS1, - ITEM_DATA_MINUS1_NULL, ITEM_DATA_MINUS2, ITEM_DATA_MINUS2_NULL, - ITEM_STRING_1, ITEM_STRING_2, ITEM_STRING_NULL2 }); - - ic.sort(new Object[] { PROPERTY_INTEGER_NOT_NULL, - PROPERTY_INTEGER_NULL2, PROPERTY_STRING_ID }, - new boolean[] { true, false, true }); - verifyOrder(ic, new String[] { ITEM_DATA_MINUS2, ITEM_DATA_MINUS2_NULL, - ITEM_DATA_MINUS1, ITEM_DATA_MINUS1_NULL, ITEM_ANOTHER_NULL, - ITEM_STRING_NULL2, ITEM_STRING_1, ITEM_STRING_2 }); - - ic.sort(new Object[] { PROPERTY_INTEGER_NOT_NULL, - PROPERTY_INTEGER_NULL2, PROPERTY_STRING_ID }, - new boolean[] { true, true, true }); - verifyOrder(ic, new String[] { ITEM_DATA_MINUS2_NULL, ITEM_DATA_MINUS2, - ITEM_DATA_MINUS1_NULL, ITEM_DATA_MINUS1, ITEM_ANOTHER_NULL, - ITEM_STRING_NULL2, ITEM_STRING_1, ITEM_STRING_2 }); - - } - - @Test - public void testHierarchicalContainer() { - HierarchicalContainer hc = new HierarchicalContainer(); - populateContainer(hc); - hc.sort(new Object[] { "name" }, new boolean[] { true }); - verifyOrder(hc, - new String[] { "Audi", "C++", "Call of Duty", "Cars", "English", - "Fallout", "Finnish", "Ford", "Games", "Java", - "Might and Magic", "Natural languages", "PHP", - "Programming languages", "Python", "Red Alert", - "Swedish", "Toyota", "Volvo" }); - TestUtil.assertArrays(hc.rootItemIds().toArray(), - new Integer[] { nameToId.get("Cars"), nameToId.get("Games"), - nameToId.get("Natural languages"), - nameToId.get("Programming languages") }); - TestUtil.assertArrays(hc.getChildren(nameToId.get("Games")).toArray(), - new Integer[] { nameToId.get("Call of Duty"), - nameToId.get("Fallout"), - nameToId.get("Might and Magic"), - nameToId.get("Red Alert") }); - } - - private static void populateContainer(HierarchicalContainer container) { - container.addContainerProperty("name", String.class, null); - - addItem(container, "Games", null); - addItem(container, "Call of Duty", "Games"); - addItem(container, "Might and Magic", "Games"); - addItem(container, "Fallout", "Games"); - addItem(container, "Red Alert", "Games"); - - addItem(container, "Cars", null); - addItem(container, "Toyota", "Cars"); - addItem(container, "Volvo", "Cars"); - addItem(container, "Audi", "Cars"); - addItem(container, "Ford", "Cars"); - - addItem(container, "Natural languages", null); - addItem(container, "Swedish", "Natural languages"); - addItem(container, "English", "Natural languages"); - addItem(container, "Finnish", "Natural languages"); - - addItem(container, "Programming languages", null); - addItem(container, "C++", "Programming languages"); - addItem(container, "PHP", "Programming languages"); - addItem(container, "Java", "Programming languages"); - addItem(container, "Python", "Programming languages"); - - } - - private static int index = 0; - private static Map nameToId = new HashMap(); - private static Map idToName = new HashMap(); - - public static void addItem(IndexedContainer container, String string, - String parent) { - nameToId.put(string, index); - idToName.put(index, string); - - Item item = container.addItem(index); - item.getItemProperty("name").setValue(string); - - if (parent != null && container instanceof HierarchicalContainer) { - ((HierarchicalContainer) container).setParent(index, - nameToId.get(parent)); - } - - index++; - } - - private void verifyOrder(Container.Sortable ic, Object[] idOrder) { - int size = ic.size(); - Object[] actual = new Object[size]; - Iterator i = ic.getItemIds().iterator(); - int index = 0; - while (i.hasNext()) { - Object o = i.next(); - if (o.getClass() == Integer.class - && idOrder[index].getClass() == String.class) { - o = idToName.get(o); - } - actual[index++] = o; - } - - TestUtil.assertArrays(actual, idOrder); - - } - - private void populate(IndexedContainer ic) { - addItem(ic, ITEM_STRING_1, ITEM_STRING_1, 1, 1); - addItem(ic, ITEM_STRING_NULL2, null, 0, null); - addItem(ic, ITEM_STRING_2, ITEM_STRING_2, 2, 2); - addItem(ic, ITEM_ANOTHER_NULL, null, 0, null); - addItem(ic, ITEM_DATA_MINUS1, ITEM_DATA_MINUS1, -1, -1); - addItem(ic, ITEM_DATA_MINUS1_NULL, null, -1, null); - addItem(ic, ITEM_DATA_MINUS2, ITEM_DATA_MINUS2, -2, -2); - addItem(ic, ITEM_DATA_MINUS2_NULL, null, -2, null); - } - - private Item addItem(Container ic, String id, String string_null, - int integer, Integer integer_null) { - Item i = ic.addItem(id); - i.getItemProperty(PROPERTY_STRING_ID).setValue(id); - i.getItemProperty(PROPERTY_STRING_NULL).setValue(string_null); - i.getItemProperty(PROPERTY_INTEGER_NOT_NULL).setValue(integer); - i.getItemProperty(PROPERTY_INTEGER_NULL2).setValue(integer_null); - - return i; - } - - private void addProperties(IndexedContainer ic) { - ic.addContainerProperty("id", String.class, null); - ic.addContainerProperty(PROPERTY_STRING_ID, String.class, ""); - ic.addContainerProperty(PROPERTY_STRING_NULL, String.class, null); - ic.addContainerProperty(PROPERTY_INTEGER_NULL2, Integer.class, null); - ic.addContainerProperty(PROPERTY_INTEGER_NOT_NULL, Integer.class, 0); - ic.addContainerProperty("comparable-null", Integer.class, 0); - } - - public class MyObject implements Comparable { - private String data; - - @Override - public int compareTo(MyObject o) { - if (o == null) { - return 1; - } - - if (o.data == null) { - return data == null ? 0 : 1; - } else if (data == null) { - return -1; - } else { - return data.compareTo(o.data); - } - } - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java b/server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java deleted file mode 100644 index 992b265702..0000000000 --- a/server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.vaadin.data.util; - -import java.io.File; - -import org.junit.Assert; -import org.junit.Test; - -public class FileSystemContainerTest { - - @Test - public void nonExistingDirectory() { - FilesystemContainer fsc = new FilesystemContainer( - new File("/non/existing")); - Assert.assertTrue(fsc.getItemIds().isEmpty()); - } -} diff --git a/server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java b/server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java deleted file mode 100644 index 11335314f0..0000000000 --- a/server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java +++ /dev/null @@ -1,596 +0,0 @@ -package com.vaadin.data.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.List; - -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Container.ItemSetChangeNotifier; -import com.vaadin.data.Item; -import com.vaadin.data.util.filter.SimpleStringFilter; - -public class GeneratedPropertyContainerBasicTest - extends AbstractInMemoryContainerTestBase { - - @Test - public void testBasicOperations() { - testBasicContainerOperations(createContainer()); - } - - private GeneratedPropertyContainer createContainer() { - return new GeneratedPropertyContainer(new IndexedContainer()); - } - - @Test - public void testFiltering() { - testContainerFiltering(createContainer()); - } - - @Test - public void testSorting() { - testContainerSorting(createContainer()); - } - - @Test - public void testSortingAndFiltering() { - testContainerSortingAndFiltering(createContainer()); - } - - @Test - public void testContainerOrdered() { - testContainerOrdered(createContainer()); - } - - @Test - public void testContainerIndexed() { - testContainerIndexed(createContainer(), sampleData[2], 2, true, - "newItemId", true); - } - - @Test - public void testItemSetChangeListeners() { - GeneratedPropertyContainer container = createContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - container.addListener(counter); - - String id1 = "id1"; - String id2 = "id2"; - String id3 = "id3"; - - initializeContainer(container); - counter.reset(); - container.addItem(); - counter.assertOnce(); - container.addItem(id1); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - container.addItemAt(0); - counter.assertOnce(); - container.addItemAt(0, id1); - counter.assertOnce(); - container.addItemAt(0, id2); - counter.assertOnce(); - container.addItemAt(container.size(), id3); - counter.assertOnce(); - // no notification if already in container - container.addItemAt(0, id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(null); - counter.assertOnce(); - container.addItemAfter(null, id1); - counter.assertOnce(); - container.addItemAfter(id1); - counter.assertOnce(); - container.addItemAfter(id1, id2); - counter.assertOnce(); - container.addItemAfter(container.firstItemId()); - counter.assertOnce(); - container.addItemAfter(container.lastItemId()); - counter.assertOnce(); - container.addItemAfter(container.lastItemId(), id3); - counter.assertOnce(); - // no notification if already in container - container.addItemAfter(0, id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.removeItem(sampleData[0]); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - // no notification for removing a non-existing item - container.removeItem(id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.removeAllItems(); - counter.assertOnce(); - // already empty - container.removeAllItems(); - counter.assertNone(); - - } - - @Test - public void testAddRemoveContainerFilter() { - GeneratedPropertyContainer container = createContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - container.addListener(counter); - - // simply adding or removing container filters should cause events - // (content changes) - - initializeContainer(container); - counter.reset(); - SimpleStringFilter filter = new SimpleStringFilter(SIMPLE_NAME, "a", - true, false); - container.addContainerFilter(filter); - counter.assertOnce(); - container.removeContainerFilter(filter); - counter.assertOnce(); - container.addContainerFilter( - new SimpleStringFilter(SIMPLE_NAME, "a", true, false)); - counter.assertOnce(); - container.removeAllContainerFilters(); - counter.assertOnce(); - } - - // TODO other tests should check positions after removing filter etc, - // here concentrating on listeners - @Test - public void testItemSetChangeListenersFiltering() { - Container.Indexed container = createContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - ((GeneratedPropertyContainer) container).addListener(counter); - - counter.reset(); - ((Container.Filterable) container) - .addContainerFilter(new SimpleStringFilter(FULLY_QUALIFIED_NAME, - "Test", true, false)); - // no real change, so no notification required - counter.assertNone(); - - String id1 = "com.example.Test1"; - String id2 = "com.example.Test2"; - String id3 = "com.example.Other"; - - // perform operations while filtering container - - Item item; - - initializeContainer(container); - counter.reset(); - // passes filter - item = container.addItem(id1); - // no event if filtered out - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - // passes filter but already in the container - item = container.addItem(id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - // passes filter after change - item = container.addItemAt(0, id1); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - item = container.addItemAt(container.size(), id2); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); - counter.assertOnce(); - // passes filter but already in the container - item = container.addItemAt(0, id1); - counter.assertNone(); - item = container.addItemAt(container.size(), id2); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - // passes filter - item = container.addItemAfter(null, id1); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - item = container.addItemAfter(container.lastItemId(), id2); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); - counter.assertOnce(); - // passes filter but already in the container - item = container.addItemAfter(null, id1); - counter.assertNone(); - item = container.addItemAfter(container.lastItemId(), id2); - counter.assertNone(); - - // does not pass filter - - // TODO implement rest - - initializeContainer(container); - counter.reset(); - item = container.addItemAfter(null, id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAfter(container.firstItemId(), id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAfter(container.lastItemId(), id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAt(0, id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAt(1, id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAt(container.size(), id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - // passes filter - - initializeContainer(container); - counter.reset(); - item = container.addItem(id1); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - container.removeItem(id1); - counter.assertOnce(); - // already removed - container.removeItem(id1); - counter.assertNone(); - - item = container.addItem(id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - // not visible - container.removeItem(id3); - counter.assertNone(); - - // remove all - - initializeContainer(container); - item = container.addItem(id1); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.reset(); - container.removeAllItems(); - counter.assertOnce(); - // no visible items - container.removeAllItems(); - counter.assertNone(); - } - - @Test - public void testItemAdd_idSequence() { - GeneratedPropertyContainer container = createContainer(); - Object itemId; - - itemId = container.addItem(); - assertEquals(Integer.valueOf(1), itemId); - - itemId = container.addItem(); - assertEquals(Integer.valueOf(2), itemId); - - itemId = container.addItemAfter(null); - assertEquals(Integer.valueOf(3), itemId); - - itemId = container.addItemAt(2); - assertEquals(Integer.valueOf(4), itemId); - } - - @Test - public void testItemAddRemove_idSequence() { - GeneratedPropertyContainer container = createContainer(); - Object itemId; - - itemId = container.addItem(); - assertEquals(Integer.valueOf(1), itemId); - - container.removeItem(itemId); - - itemId = container.addItem(); - assertEquals( - "Id sequence should continue from the previous value even if an item is removed", - Integer.valueOf(2), itemId); - } - - @Test - public void testItemAddedEvent() { - GeneratedPropertyContainer container = createContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); - EasyMock.replay(addListener); - - container.addItem(); - - EasyMock.verify(addListener); - } - - @Test - public void testItemAddedEvent_AddedItem() { - GeneratedPropertyContainer container = createContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - Object itemId = container.addItem(); - - assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); - } - - @Test - public void testItemAddedEvent_IndexOfAddedItem() { - GeneratedPropertyContainer container = createContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - container.addItem(); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - Object itemId = container.addItemAt(1); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - @Test - public void testItemRemovedEvent() { - GeneratedPropertyContainer container = createContainer(); - Object itemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - removeListener - .containerItemSetChange(EasyMock.isA(ItemRemoveEvent.class)); - EasyMock.replay(removeListener); - - container.removeItem(itemId); - - EasyMock.verify(removeListener); - } - - @Test - public void testItemRemovedEvent_RemovedItem() { - GeneratedPropertyContainer container = createContainer(); - Object itemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeItem(itemId); - - assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); - } - - @Test - public void testItemRemovedEvent_indexOfRemovedItem() { - GeneratedPropertyContainer container = createContainer(); - container.addItem(); - Object secondItemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeItem(secondItemId); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - @Test - public void testItemRemovedEvent_amountOfRemovedItems() { - GeneratedPropertyContainer container = createContainer(); - container.addItem(); - container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeAllItems(); - - assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); - } - - private Capture captureAddEvent( - ItemSetChangeListener addListener) { - Capture capturedEvent = new Capture(); - addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private Capture captureRemoveEvent( - ItemSetChangeListener removeListener) { - Capture capturedEvent = new Capture(); - removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private ItemSetChangeListener createListenerMockFor( - ItemSetChangeNotifier container) { - ItemSetChangeListener listener = EasyMock - .createNiceMock(ItemSetChangeListener.class); - container.addItemSetChangeListener(listener); - return listener; - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeIndexOutOfBounds() { - GeneratedPropertyContainer ic = createContainer(); - try { - ic.getItemIds(-1, 10); - fail("Container returned items starting from index -1, something very wrong here!"); - } catch (IndexOutOfBoundsException e) { - // This is expected... - } catch (Exception e) { - // Should not happen! - fail("Container threw unspecified exception when fetching a range of items and the range started from -1"); - } - - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeIndexOutOfBounds2() { - GeneratedPropertyContainer ic = createContainer(); - ic.addItem(new Object()); - try { - ic.getItemIds(2, 1); - fail("Container returned items starting from index -1, something very wrong here!"); - } catch (IndexOutOfBoundsException e) { - // This is expected... - } catch (Exception e) { - // Should not happen! - fail("Container threw unspecified exception when fetching a out of bounds range of items"); - } - - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeZeroRange() { - GeneratedPropertyContainer ic = createContainer(); - ic.addItem(new Object()); - try { - List itemIds = (List) ic.getItemIds(1, 0); - - assertTrue( - "Container returned actual values when asking for 0 items...", - itemIds.isEmpty()); - } catch (Exception e) { - // Should not happen! - fail("Container threw unspecified exception when fetching 0 items..."); - } - - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeNegativeRange() { - GeneratedPropertyContainer ic = createContainer(); - ic.addItem(new Object()); - try { - List itemIds = (List) ic.getItemIds(1, -1); - - assertTrue( - "Container returned actual values when asking for -1 items...", - itemIds.isEmpty()); - } catch (IllegalArgumentException e) { - // this is expected - - } catch (Exception e) { - // Should not happen! - fail("Container threw unspecified exception when fetching -1 items..."); - } - - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeIndexOutOfBoundsDueToSizeChange() { - GeneratedPropertyContainer ic = createContainer(); - ic.addItem(new Object()); - Assert.assertEquals( - "Container returned too many items when the range was >> container size", - 1, ic.getItemIds(0, 10).size()); - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeBaseCase() { - GeneratedPropertyContainer ic = createContainer(); - String object1 = new String("Obj1"); - String object2 = new String("Obj2"); - String object3 = new String("Obj3"); - String object4 = new String("Obj4"); - String object5 = new String("Obj5"); - - ic.addItem(object1); - ic.addItem(object2); - ic.addItem(object3); - ic.addItem(object4); - ic.addItem(object5); - - try { - List itemIds = (List) ic.getItemIds(1, 2); - - assertTrue(itemIds.contains(object2)); - assertTrue(itemIds.contains(object3)); - assertEquals(2, itemIds.size()); - - } catch (Exception e) { - // Should not happen! - fail("Container threw exception when fetching a range of items "); - } - } - - // test getting non-existing property (#10445) - @Test - public void testNonExistingProperty() { - Container ic = createContainer(); - String object1 = new String("Obj1"); - ic.addItem(object1); - assertNull(ic.getContainerProperty(object1, "xyz")); - } - - // test getting null property id (#10445) - @Test - public void testNullPropertyId() { - Container ic = createContainer(); - String object1 = new String("Obj1"); - ic.addItem(object1); - assertNull(ic.getContainerProperty(object1, null)); - } - - @Override - protected void initializeContainer(Container container) { - if (container instanceof GeneratedPropertyContainer) { - super.initializeContainer(((GeneratedPropertyContainer) container) - .getWrappedContainer()); - } else { - super.initializeContainer(container); - } - } -} diff --git a/server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java b/server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java deleted file mode 100644 index 1e34567439..0000000000 --- a/server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * 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.data.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Container.PropertySetChangeEvent; -import com.vaadin.data.Container.PropertySetChangeListener; -import com.vaadin.data.Item; -import com.vaadin.data.sort.SortOrder; -import com.vaadin.data.util.GeneratedPropertyContainer.GeneratedPropertyItem; -import com.vaadin.data.util.filter.Compare; -import com.vaadin.data.util.filter.UnsupportedFilterException; - -public class GeneratedPropertyContainerTest { - - GeneratedPropertyContainer container; - Indexed wrappedContainer; - private static double MILES_CONVERSION = 0.6214d; - - private class GeneratedPropertyListener - implements PropertySetChangeListener { - - private int callCount = 0; - - public int getCallCount() { - return callCount; - } - - @Override - public void containerPropertySetChange(PropertySetChangeEvent event) { - ++callCount; - assertEquals( - "Container for event was not GeneratedPropertyContainer", - event.getContainer(), container); - } - } - - private class GeneratedItemSetListener implements ItemSetChangeListener { - - private int callCount = 0; - - public int getCallCount() { - return callCount; - } - - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - ++callCount; - assertEquals( - "Container for event was not GeneratedPropertyContainer", - event.getContainer(), container); - } - } - - @Before - public void setUp() { - container = new GeneratedPropertyContainer(createContainer()); - } - - @Test - public void testSimpleGeneratedProperty() { - container.addGeneratedProperty("hello", - new PropertyValueGenerator() { - - @Override - public String getValue(Item item, Object itemId, - Object propertyId) { - return "Hello World!"; - } - - @Override - public Class getType() { - return String.class; - } - }); - - Object itemId = container.addItem(); - assertEquals("Expected value not in item.", - container.getItem(itemId).getItemProperty("hello").getValue(), - "Hello World!"); - } - - @Test - public void testSortableProperties() { - container.addGeneratedProperty("baz", - new PropertyValueGenerator() { - - @Override - public String getValue(Item item, Object itemId, - Object propertyId) { - return item.getItemProperty("foo").getValue() + " " - + item.getItemProperty("bar").getValue(); - } - - @Override - public Class getType() { - return String.class; - } - - @Override - public SortOrder[] getSortProperties(SortOrder order) { - SortOrder[] sortOrder = new SortOrder[1]; - sortOrder[0] = new SortOrder("bar", - order.getDirection()); - return sortOrder; - } - }); - - container.sort(new Object[] { "baz" }, new boolean[] { true }); - assertEquals("foo 0", container.getItem(container.getIdByIndex(0)) - .getItemProperty("baz").getValue()); - - container.sort(new Object[] { "baz" }, new boolean[] { false }); - assertEquals("foo 10", container.getItem(container.getIdByIndex(0)) - .getItemProperty("baz").getValue()); - } - - @Test - public void testOverrideSortableProperties() { - - assertTrue(container.getSortableContainerPropertyIds().contains("bar")); - - container.addGeneratedProperty("bar", - new PropertyValueGenerator() { - - @Override - public String getValue(Item item, Object itemId, - Object propertyId) { - return item.getItemProperty("foo").getValue() + " " - + item.getItemProperty("bar").getValue(); - } - - @Override - public Class getType() { - return String.class; - } - }); - - assertFalse( - container.getSortableContainerPropertyIds().contains("bar")); - } - - @Test - public void testFilterByMiles() { - container.addGeneratedProperty("miles", - new PropertyValueGenerator() { - - @Override - public Double getValue(Item item, Object itemId, - Object propertyId) { - return (Double) item.getItemProperty("km").getValue() - * MILES_CONVERSION; - } - - @Override - public Class getType() { - return Double.class; - } - - @Override - public Filter modifyFilter(Filter filter) - throws UnsupportedFilterException { - if (filter instanceof Compare.LessOrEqual) { - Double value = (Double) ((Compare.LessOrEqual) filter) - .getValue(); - value = value / MILES_CONVERSION; - return new Compare.LessOrEqual("km", value); - } - return super.modifyFilter(filter); - } - }); - - for (Object itemId : container.getItemIds()) { - Item item = container.getItem(itemId); - Double km = (Double) item.getItemProperty("km").getValue(); - Double miles = (Double) item.getItemProperty("miles").getValue(); - assertTrue(miles.equals(km * MILES_CONVERSION)); - } - - Filter filter = new Compare.LessOrEqual("miles", MILES_CONVERSION); - container.addContainerFilter(filter); - for (Object itemId : container.getItemIds()) { - Item item = container.getItem(itemId); - assertTrue("Item did not pass original filter.", - filter.passesFilter(itemId, item)); - } - - assertTrue(container.getContainerFilters().contains(filter)); - container.removeContainerFilter(filter); - assertFalse(container.getContainerFilters().contains(filter)); - - boolean allPass = true; - for (Object itemId : container.getItemIds()) { - Item item = container.getItem(itemId); - if (!filter.passesFilter(itemId, item)) { - allPass = false; - } - } - - if (allPass) { - fail("Removing filter did not introduce any previous filtered items"); - } - } - - @Test - public void testPropertySetChangeNotifier() { - GeneratedPropertyListener listener = new GeneratedPropertyListener(); - GeneratedPropertyListener removedListener = new GeneratedPropertyListener(); - container.addPropertySetChangeListener(listener); - container.addPropertySetChangeListener(removedListener); - - container.addGeneratedProperty("foo", - new PropertyValueGenerator() { - - @Override - public String getValue(Item item, Object itemId, - Object propertyId) { - return ""; - } - - @Override - public Class getType() { - return String.class; - } - }); - - // Adding property to wrapped container should cause an event - wrappedContainer.addContainerProperty("baz", String.class, ""); - container.removePropertySetChangeListener(removedListener); - container.removeGeneratedProperty("foo"); - - assertEquals("Listener was not called correctly.", 3, - listener.getCallCount()); - assertEquals("Removed listener was not called correctly.", 2, - removedListener.getCallCount()); - } - - @Test - public void testItemSetChangeNotifier() { - GeneratedItemSetListener listener = new GeneratedItemSetListener(); - container.addItemSetChangeListener(listener); - - container.sort(new Object[] { "foo" }, new boolean[] { true }); - container.sort(new Object[] { "foo" }, new boolean[] { false }); - - assertEquals("Listener was not called correctly.", 2, - listener.getCallCount()); - - } - - @Test - public void testRemoveProperty() { - container.removeContainerProperty("foo"); - assertFalse("Container contained removed property", - container.getContainerPropertyIds().contains("foo")); - assertTrue("Wrapped container did not contain removed property", - wrappedContainer.getContainerPropertyIds().contains("foo")); - - assertFalse(container.getItem(container.firstItemId()) - .getItemPropertyIds().contains("foo")); - - container.addContainerProperty("foo", null, null); - assertTrue("Container did not contain returned property", - container.getContainerPropertyIds().contains("foo")); - } - - @Test - public void testGetWrappedItem() { - Object itemId = wrappedContainer.getItemIds().iterator().next(); - Item wrappedItem = wrappedContainer.getItem(itemId); - GeneratedPropertyItem generatedPropertyItem = (GeneratedPropertyItem) container - .getItem(itemId); - assertEquals(wrappedItem, generatedPropertyItem.getWrappedItem()); - } - - private Indexed createContainer() { - wrappedContainer = new IndexedContainer(); - wrappedContainer.addContainerProperty("foo", String.class, "foo"); - wrappedContainer.addContainerProperty("bar", Integer.class, 0); - // km contains double values from 0.0 to 2.0 - wrappedContainer.addContainerProperty("km", Double.class, 0); - - for (int i = 0; i <= 10; ++i) { - Object itemId = wrappedContainer.addItem(); - Item item = wrappedContainer.getItem(itemId); - item.getItemProperty("foo").setValue("foo"); - item.getItemProperty("bar").setValue(i); - item.getItemProperty("km").setValue(i / 5.0d); - } - - return wrappedContainer; - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java b/server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java deleted file mode 100644 index 54984f07ad..0000000000 --- a/server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.vaadin.data.util; - -import org.junit.Test; - -public class HierarchicalContainerOrderedWrapperTest - extends AbstractHierarchicalContainerTestBase { - - private HierarchicalContainerOrderedWrapper createContainer() { - return new HierarchicalContainerOrderedWrapper( - new ContainerHierarchicalWrapper(new IndexedContainer())); - } - - @Test - public void testBasicOperations() { - testBasicContainerOperations(createContainer()); - } - - @Test - public void testHierarchicalContainer() { - testHierarchicalContainer(createContainer()); - } - - @Test - public void testContainerOrdered() { - testContainerOrdered(createContainer()); - } - - @Test - public void testRemoveSubtree() { - testRemoveHierarchicalWrapperSubtree(createContainer()); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java b/server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java deleted file mode 100644 index 7ab20ca3dd..0000000000 --- a/server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java +++ /dev/null @@ -1,286 +0,0 @@ -package com.vaadin.data.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; - -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; - -public class HierarchicalContainerTest - extends AbstractHierarchicalContainerTestBase { - - @Test - public void testBasicOperations() { - testBasicContainerOperations(new HierarchicalContainer()); - } - - @Test - public void testFiltering() { - testContainerFiltering(new HierarchicalContainer()); - } - - @Test - public void testSorting() { - testContainerSorting(new HierarchicalContainer()); - } - - @Test - public void testOrdered() { - testContainerOrdered(new HierarchicalContainer()); - } - - @Test - public void testHierarchicalSorting() { - testHierarchicalSorting(new HierarchicalContainer()); - } - - @Test - public void testSortingAndFiltering() { - testContainerSortingAndFiltering(new HierarchicalContainer()); - } - - @Test - public void testRemovingItemsFromFilteredContainer() { - HierarchicalContainer container = new HierarchicalContainer(); - initializeContainer(container); - container.setIncludeParentsWhenFiltering(true); - container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); - Object p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertEquals("com.vaadin.ui", p1); - - container.removeItem("com.vaadin.ui.TabSheet"); - // Parent for the removed item must be null because the item is no - // longer in the container - p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertNull("Parent should be null, is " + p1, p1); - - container.removeAllItems(); - p1 = container.getParent("com.vaadin.terminal.gwt.client.Focusable"); - assertNull("Parent should be null, is " + p1, p1); - - } - - @Test - public void testParentWhenRemovingFilterFromContainer() { - HierarchicalContainer container = new HierarchicalContainer(); - initializeContainer(container); - container.setIncludeParentsWhenFiltering(true); - container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); - Object p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertEquals("com.vaadin.ui", p1); - p1 = container - .getParent("com.vaadin.terminal.gwt.client.ui.VPopupCalendar"); - assertNull(p1); - container.removeAllContainerFilters(); - p1 = container - .getParent("com.vaadin.terminal.gwt.client.ui.VPopupCalendar"); - assertEquals("com.vaadin.terminal.gwt.client.ui", p1); - - } - - @Test - public void testChangeParentInFilteredContainer() { - HierarchicalContainer container = new HierarchicalContainer(); - initializeContainer(container); - container.setIncludeParentsWhenFiltering(true); - container.addContainerFilter(FULLY_QUALIFIED_NAME, "Tab", false, false); - - // Change parent of filtered item - Object p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertEquals("com.vaadin.ui", p1); - container.setParent("com.vaadin.ui.TabSheet", "com.vaadin"); - p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertEquals("com.vaadin", p1); - container.setParent("com.vaadin.ui.TabSheet", "com"); - p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertEquals("com", p1); - container.setParent("com.vaadin.ui.TabSheet", null); - p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertNull(p1); - - // root -> non-root - container.setParent("com.vaadin.ui.TabSheet", "com"); - p1 = container.getParent("com.vaadin.ui.TabSheet"); - assertEquals("com", p1); - - } - - @Test - public void testHierarchicalFilteringWithParents() { - HierarchicalContainer container = new HierarchicalContainer(); - initializeContainer(container); - container.setIncludeParentsWhenFiltering(true); - - // Filter by "contains ab" - container.addContainerFilter(FULLY_QUALIFIED_NAME, "ab", false, false); - - // 20 items match the filters and the have 8 parents that should also be - // included - // only one root "com" should exist - // filtered - int expectedSize = 29; - int expectedRoots = 1; - - validateHierarchicalContainer(container, "com", - "com.vaadin.ui.TabSheet", - "com.vaadin.terminal.gwt.client.Focusable", "blah", true, - expectedSize, expectedRoots, true); - - // only include .gwt.client classes - container.removeAllContainerFilters(); - container.addContainerFilter(FULLY_QUALIFIED_NAME, ".gwt.client.", - false, false); - - int packages = 6; - int classes = 112; - - expectedSize = packages + classes; - expectedRoots = 1; - - validateHierarchicalContainer(container, "com", - "com.vaadin.terminal.gwt.client.WidgetSet", - "com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical", "blah", - true, expectedSize, expectedRoots, true); - - // Additionally remove all without 'm' in the simple name. - container.addContainerFilter(SIMPLE_NAME, "m", false, false); - - expectedSize = 7 + 18; - expectedRoots = 1; - - validateHierarchicalContainer(container, "com", - "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", - "com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer", - "blah", true, expectedSize, expectedRoots, true); - - } - - @Test - public void testRemoveLastChild() { - HierarchicalContainer c = new HierarchicalContainer(); - - c.addItem("root"); - assertEquals(false, c.hasChildren("root")); - - c.addItem("child"); - c.setParent("child", "root"); - assertEquals(true, c.hasChildren("root")); - - c.removeItem("child"); - assertFalse(c.containsId("child")); - assertNull(c.getChildren("root")); - assertNull(c.getChildren("child")); - assertFalse(c.hasChildren("child")); - assertFalse(c.hasChildren("root")); - } - - @Test - public void testRemoveLastChildFromFiltered() { - HierarchicalContainer c = new HierarchicalContainer(); - - c.addItem("root"); - assertEquals(false, c.hasChildren("root")); - - c.addItem("child"); - c.setParent("child", "root"); - assertEquals(true, c.hasChildren("root")); - - // Dummy filter that does not remove any items - c.addContainerFilter(new Filter() { - - @Override - public boolean passesFilter(Object itemId, Item item) - throws UnsupportedOperationException { - return true; - } - - @Override - public boolean appliesToProperty(Object propertyId) { - return true; - } - }); - c.removeItem("child"); - - assertFalse(c.containsId("child")); - assertNull(c.getChildren("root")); - assertNull(c.getChildren("child")); - assertFalse(c.hasChildren("child")); - assertFalse(c.hasChildren("root")); - } - - @Test - public void testHierarchicalFilteringWithoutParents() { - HierarchicalContainer container = new HierarchicalContainer(); - - initializeContainer(container); - container.setIncludeParentsWhenFiltering(false); - - // Filter by "contains ab" - container.addContainerFilter(SIMPLE_NAME, "ab", false, false); - - // 20 items match the filter. - // com.vaadin.data.BufferedValidatable - // com.vaadin.data.Validatable - // com.vaadin.terminal.gwt.client.Focusable - // com.vaadin.terminal.gwt.client.Paintable - // com.vaadin.terminal.gwt.client.ui.Table - // com.vaadin.terminal.gwt.client.ui.VLabel - // com.vaadin.terminal.gwt.client.ui.VScrollTable - // com.vaadin.terminal.gwt.client.ui.VTablePaging - // com.vaadin.terminal.gwt.client.ui.VTabsheet - // com.vaadin.terminal.gwt.client.ui.VTabsheetBase - // com.vaadin.terminal.gwt.client.ui.VTabsheetPanel - // com.vaadin.server.ChangeVariablesErrorEvent - // com.vaadin.server.Paintable - // com.vaadin.server.Scrollable - // com.vaadin.server.Sizeable - // com.vaadin.server.VariableOwner - // com.vaadin.ui.Label - // com.vaadin.ui.Table - // com.vaadin.ui.TableFieldFactory - // com.vaadin.ui.TabSheet - // all become roots. - int expectedSize = 20; - int expectedRoots = 20; - - validateHierarchicalContainer(container, - "com.vaadin.data.BufferedValidatable", "com.vaadin.ui.TabSheet", - "com.vaadin.terminal.gwt.client.ui.VTabsheetBase", "blah", true, - expectedSize, expectedRoots, false); - - // only include .gwt.client classes - container.removeAllContainerFilters(); - container.addContainerFilter(FULLY_QUALIFIED_NAME, ".gwt.client.", - false, false); - - int packages = 3; - int classes = 110; - - expectedSize = packages + classes; - expectedRoots = 35 + 1; // com.vaadin.terminal.gwt.client.ui + - // com.vaadin.terminal.gwt.client.* - - // Sorting is case insensitive - validateHierarchicalContainer(container, - "com.vaadin.terminal.gwt.client.ApplicationConfiguration", - "com.vaadin.terminal.gwt.client.WidgetSet", - "com.vaadin.terminal.gwt.client.ui.VOptionGroup", "blah", true, - expectedSize, expectedRoots, false); - - // Additionally remove all without 'P' in the simple name. - container.addContainerFilter(SIMPLE_NAME, "P", false, false); - - expectedSize = 13; - expectedRoots = expectedSize; - - validateHierarchicalContainer(container, - "com.vaadin.terminal.gwt.client.Paintable", - "com.vaadin.terminal.gwt.client.ui.VTabsheetPanel", - "com.vaadin.terminal.gwt.client.ui.VPopupCalendar", "blah", - true, expectedSize, expectedRoots, false); - - } -} diff --git a/server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java b/server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java deleted file mode 100644 index b19a425518..0000000000 --- a/server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java +++ /dev/null @@ -1,533 +0,0 @@ -package com.vaadin.data.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Item; - -public class IndexedContainerTest extends AbstractInMemoryContainerTestBase { - - @Test - public void testBasicOperations() { - testBasicContainerOperations(new IndexedContainer()); - } - - @Test - public void testFiltering() { - testContainerFiltering(new IndexedContainer()); - } - - @Test - public void testSorting() { - testContainerSorting(new IndexedContainer()); - } - - @Test - public void testSortingAndFiltering() { - testContainerSortingAndFiltering(new IndexedContainer()); - } - - @Test - public void testContainerOrdered() { - testContainerOrdered(new IndexedContainer()); - } - - @Test - public void testContainerIndexed() { - testContainerIndexed(new IndexedContainer(), sampleData[2], 2, true, - "newItemId", true); - } - - @Test - public void testItemSetChangeListeners() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - container.addListener(counter); - - String id1 = "id1"; - String id2 = "id2"; - String id3 = "id3"; - - initializeContainer(container); - counter.reset(); - container.addItem(); - counter.assertOnce(); - container.addItem(id1); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - container.addItemAt(0); - counter.assertOnce(); - container.addItemAt(0, id1); - counter.assertOnce(); - container.addItemAt(0, id2); - counter.assertOnce(); - container.addItemAt(container.size(), id3); - counter.assertOnce(); - // no notification if already in container - container.addItemAt(0, id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.addItemAfter(null); - counter.assertOnce(); - container.addItemAfter(null, id1); - counter.assertOnce(); - container.addItemAfter(id1); - counter.assertOnce(); - container.addItemAfter(id1, id2); - counter.assertOnce(); - container.addItemAfter(container.firstItemId()); - counter.assertOnce(); - container.addItemAfter(container.lastItemId()); - counter.assertOnce(); - container.addItemAfter(container.lastItemId(), id3); - counter.assertOnce(); - // no notification if already in container - container.addItemAfter(0, id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.removeItem(sampleData[0]); - counter.assertOnce(); - - initializeContainer(container); - counter.reset(); - // no notification for removing a non-existing item - container.removeItem(id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - container.removeAllItems(); - counter.assertOnce(); - // already empty - container.removeAllItems(); - counter.assertNone(); - - } - - @Test - public void testAddRemoveContainerFilter() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - container.addListener(counter); - - // simply adding or removing container filters should cause events - // (content changes) - - initializeContainer(container); - counter.reset(); - container.addContainerFilter(SIMPLE_NAME, "a", true, false); - counter.assertOnce(); - container.removeContainerFilters(SIMPLE_NAME); - counter.assertOnce(); - container.addContainerFilter(SIMPLE_NAME, "a", true, false); - counter.assertOnce(); - container.removeAllContainerFilters(); - counter.assertOnce(); - } - - // TODO other tests should check positions after removing filter etc, - // here concentrating on listeners - @Test - public void testItemSetChangeListenersFiltering() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeCounter counter = new ItemSetChangeCounter(); - container.addListener(counter); - - counter.reset(); - container.addContainerFilter(FULLY_QUALIFIED_NAME, "Test", true, false); - // no real change, so no notification required - counter.assertNone(); - - String id1 = "com.example.Test1"; - String id2 = "com.example.Test2"; - String id3 = "com.example.Other"; - - // perform operations while filtering container - - Item item; - - initializeContainer(container); - counter.reset(); - // passes filter - item = container.addItem(id1); - // no event if filtered out - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - // passes filter but already in the container - item = container.addItem(id1); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - // passes filter after change - item = container.addItemAt(0, id1); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - item = container.addItemAt(container.size(), id2); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); - counter.assertOnce(); - // passes filter but already in the container - item = container.addItemAt(0, id1); - counter.assertNone(); - item = container.addItemAt(container.size(), id2); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - // passes filter - item = container.addItemAfter(null, id1); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - item = container.addItemAfter(container.lastItemId(), id2); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id2); - counter.assertOnce(); - // passes filter but already in the container - item = container.addItemAfter(null, id1); - counter.assertNone(); - item = container.addItemAfter(container.lastItemId(), id2); - counter.assertNone(); - - // does not pass filter - - // TODO implement rest - - initializeContainer(container); - counter.reset(); - item = container.addItemAfter(null, id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAfter(container.firstItemId(), id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAfter(container.lastItemId(), id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAt(0, id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAt(1, id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - initializeContainer(container); - counter.reset(); - item = container.addItemAt(container.size(), id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - - // passes filter - - initializeContainer(container); - counter.reset(); - item = container.addItem(id1); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.assertOnce(); - container.removeItem(id1); - counter.assertOnce(); - // already removed - container.removeItem(id1); - counter.assertNone(); - - item = container.addItem(id3); - counter.assertNone(); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id3); - counter.assertNone(); - // not visible - container.removeItem(id3); - counter.assertNone(); - - // remove all - - initializeContainer(container); - item = container.addItem(id1); - item.getItemProperty(FULLY_QUALIFIED_NAME).setValue(id1); - counter.reset(); - container.removeAllItems(); - counter.assertOnce(); - // no visible items - container.removeAllItems(); - counter.assertNone(); - } - - @Test - public void testItemAdd_idSequence() { - IndexedContainer container = new IndexedContainer(); - Object itemId; - - itemId = container.addItem(); - assertEquals(Integer.valueOf(1), itemId); - - itemId = container.addItem(); - assertEquals(Integer.valueOf(2), itemId); - - itemId = container.addItemAfter(null); - assertEquals(Integer.valueOf(3), itemId); - - itemId = container.addItemAt(2); - assertEquals(Integer.valueOf(4), itemId); - } - - @Test - public void testItemAddRemove_idSequence() { - IndexedContainer container = new IndexedContainer(); - Object itemId; - - itemId = container.addItem(); - assertEquals(Integer.valueOf(1), itemId); - - container.removeItem(itemId); - - itemId = container.addItem(); - assertEquals( - "Id sequence should continue from the previous value even if an item is removed", - Integer.valueOf(2), itemId); - } - - @Test - public void testItemAddedEvent() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); - EasyMock.replay(addListener); - - container.addItem(); - - EasyMock.verify(addListener); - } - - @Test - public void testItemAddedEvent_AddedItem() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - Object itemId = container.addItem(); - - assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); - } - - @Test - public void testItemAddedEvent_IndexOfAddedItem() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - container.addItem(); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - Object itemId = container.addItemAt(1); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - @Test - public void testItemRemovedEvent() { - IndexedContainer container = new IndexedContainer(); - Object itemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - removeListener - .containerItemSetChange(EasyMock.isA(ItemRemoveEvent.class)); - EasyMock.replay(removeListener); - - container.removeItem(itemId); - - EasyMock.verify(removeListener); - } - - @Test - public void testItemRemovedEvent_RemovedItem() { - IndexedContainer container = new IndexedContainer(); - Object itemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeItem(itemId); - - assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); - } - - @Test - public void testItemRemovedEvent_indexOfRemovedItem() { - IndexedContainer container = new IndexedContainer(); - container.addItem(); - Object secondItemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeItem(secondItemId); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - @Test - public void testItemRemovedEvent_amountOfRemovedItems() { - IndexedContainer container = new IndexedContainer(); - container.addItem(); - container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent( - removeListener); - EasyMock.replay(removeListener); - - container.removeAllItems(); - - assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); - } - - private Capture captureAddEvent( - ItemSetChangeListener addListener) { - Capture capturedEvent = new Capture(); - addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private Capture captureRemoveEvent( - ItemSetChangeListener removeListener) { - Capture capturedEvent = new Capture(); - removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private ItemSetChangeListener createListenerMockFor( - IndexedContainer container) { - ItemSetChangeListener listener = EasyMock - .createNiceMock(ItemSetChangeListener.class); - container.addItemSetChangeListener(listener); - return listener; - } - - // Ticket 8028 - @Test(expected = IndexOutOfBoundsException.class) - public void testGetItemIdsRangeIndexOutOfBounds() { - IndexedContainer ic = new IndexedContainer(); - ic.getItemIds(-1, 10); - } - - // Ticket 8028 - @Test(expected = IndexOutOfBoundsException.class) - public void testGetItemIdsRangeIndexOutOfBounds2() { - IndexedContainer ic = new IndexedContainer(); - ic.addItem(new Object()); - ic.getItemIds(2, 1); - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeZeroRange() { - IndexedContainer ic = new IndexedContainer(); - ic.addItem(new Object()); - List itemIds = ic.getItemIds(1, 0); - - assertTrue( - "Container returned actual values when asking for 0 items...", - itemIds.isEmpty()); - } - - // Ticket 8028 - @Test(expected = IllegalArgumentException.class) - public void testGetItemIdsRangeNegativeRange() { - IndexedContainer ic = new IndexedContainer(); - ic.addItem(new Object()); - List itemIds = ic.getItemIds(1, -1); - - assertTrue( - "Container returned actual values when asking for -1 items...", - itemIds.isEmpty()); - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeIndexOutOfBoundsDueToSizeChange() { - IndexedContainer ic = new IndexedContainer(); - ic.addItem(new Object()); - Assert.assertEquals( - "Container returned too many items when the range was >> container size", - 1, ic.getItemIds(0, 10).size()); - } - - // Ticket 8028 - @Test - public void testGetItemIdsRangeBaseCase() { - IndexedContainer ic = new IndexedContainer(); - String object1 = new String("Obj1"); - String object2 = new String("Obj2"); - String object3 = new String("Obj3"); - String object4 = new String("Obj4"); - String object5 = new String("Obj5"); - - ic.addItem(object1); - ic.addItem(object2); - ic.addItem(object3); - ic.addItem(object4); - ic.addItem(object5); - - List itemIds = ic.getItemIds(1, 2); - - assertTrue(itemIds.contains(object2)); - assertTrue(itemIds.contains(object3)); - assertEquals(2, itemIds.size()); - } - - // test getting non-existing property (#10445) - @Test - public void testNonExistingProperty() { - IndexedContainer ic = new IndexedContainer(); - String object1 = new String("Obj1"); - ic.addItem(object1); - assertNull(ic.getContainerProperty(object1, "xyz")); - } - - // test getting null property id (#10445) - @Test - public void testNullPropertyId() { - IndexedContainer ic = new IndexedContainer(); - String object1 = new String("Obj1"); - ic.addItem(object1); - assertNull(ic.getContainerProperty(object1, null)); - } -} diff --git a/server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java b/server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java deleted file mode 100644 index 6776021c24..0000000000 --- a/server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.data.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.reflect.Field; - -import org.junit.Assert; -import org.junit.Test; - -/** - * Test for MethodProperty: don't allocate unnecessary Object arrays. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class MethodPropertyMemoryConsumptionTest { - - @Test - public void testSetArguments() - throws NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException { - TestBean bean = new TestBean(); - TestMethodProperty property = new TestMethodProperty( - bean, "name"); - Object[] getArgs = property.getGetArgs(); - Object[] setArgs = property.getSetArgs(); - - Field getArgsField = TestMethodProperty.class - .getDeclaredField("getArgs"); - getArgsField.setAccessible(true); - - Field setArgsField = TestMethodProperty.class - .getDeclaredField("setArgs"); - setArgsField.setAccessible(true); - - Assert.assertSame( - "setArguments method sets non-default instance" - + " of empty Object array for getArgs", - getArgsField.get(property), getArgs); - - Assert.assertSame( - "setArguments method sets non-default instance" - + " of empty Object array for setArgs", - setArgsField.get(property), setArgs); - } - - @Test - public void testDefaultCtor() { - TestBean bean = new TestBean(); - TestMethodProperty property = new TestMethodProperty( - bean, "name"); - - Object[] getArgs = property.getGetArgs(); - Object[] setArgs = property.getSetArgs(); - - TestBean otherBean = new TestBean(); - TestMethodProperty otherProperty = new TestMethodProperty( - otherBean, "name"); - Assert.assertSame( - "setArguments method uses different instance" - + " of empty Object array for getArgs", - getArgs, otherProperty.getGetArgs()); - Assert.assertSame( - "setArguments method uses different instance" - + " of empty Object array for setArgs", - setArgs, otherProperty.getSetArgs()); - } - - @Test - public void testDefaultArgsSerialization() - throws IOException, ClassNotFoundException { - TestBean bean = new TestBean(); - TestMethodProperty property = new TestMethodProperty( - bean, "name"); - - ByteArrayOutputStream sourceOutStream = new ByteArrayOutputStream(); - ObjectOutputStream outStream = new ObjectOutputStream(sourceOutStream); - outStream.writeObject(property); - - ObjectInputStream inputStream = new ObjectInputStream( - new ByteArrayInputStream(sourceOutStream.toByteArray())); - Object red = inputStream.readObject(); - TestMethodProperty deserialized = (TestMethodProperty) red; - - Assert.assertNotNull("Deseriliation doesn't call setArguments method", - deserialized.getGetArgs()); - Assert.assertNotNull("Deseriliation doesn't call setArguments method", - deserialized.getSetArgs()); - - } - - public static class TestMethodProperty extends MethodProperty { - - public TestMethodProperty(Object instance, String beanPropertyName) { - super(instance, beanPropertyName); - } - - @Override - public void setArguments(Object[] getArgs, Object[] setArgs, - int setArgumentIndex) { - super.setArguments(getArgs, setArgs, setArgumentIndex); - this.getArgs = getArgs; - this.setArgs = setArgs; - } - - Object[] getGetArgs() { - return getArgs; - } - - Object[] getSetArgs() { - return setArgs; - } - - private transient Object[] getArgs; - private transient Object[] setArgs; - } - - public static class TestBean implements Serializable { - - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - } -} diff --git a/server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java b/server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java deleted file mode 100644 index 4a1e2a1784..0000000000 --- a/server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java +++ /dev/null @@ -1,351 +0,0 @@ -package com.vaadin.data.util; - -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class NestedMethodPropertyTest { - - public static class Address implements Serializable { - private String street; - private int postalCodePrimitive; - private Integer postalCodeObject; - - public Address(String street, int postalCode) { - this.street = street; - postalCodePrimitive = postalCode; - postalCodeObject = postalCode; - } - - public void setStreet(String street) { - this.street = street; - } - - public String getStreet() { - return street; - } - - public void setPostalCodePrimitive(int postalCodePrimitive) { - this.postalCodePrimitive = postalCodePrimitive; - } - - public int getPostalCodePrimitive() { - return postalCodePrimitive; - } - - public void setPostalCodeObject(Integer postalCodeObject) { - this.postalCodeObject = postalCodeObject; - } - - public Integer getPostalCodeObject() { - return postalCodeObject; - } - - // read-only boolean property - public boolean isBoolean() { - return true; - } - } - - public static class Person implements Serializable { - private String name; - private Address address; - private int age; - - public Person(String name, Address address) { - this.name = name; - this.address = address; - } - - public Person(String name, Address address, int age) { - this.name = name; - this.address = address; - this.age = age; - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setAddress(Address address) { - this.address = address; - } - - public Address getAddress() { - return address; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - } - - public static class Team implements Serializable { - private String name; - private Person manager; - - public Team(String name, Person manager) { - this.name = name; - this.manager = manager; - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setManager(Person manager) { - this.manager = manager; - } - - public Person getManager() { - return manager; - } - } - - private Address oldMill; - private Person joonas; - private Team vaadin; - - @Before - public void setUp() { - oldMill = new Address("Ruukinkatu 2-4", 20540); - joonas = new Person("Joonas", oldMill); - vaadin = new Team("Vaadin", joonas); - } - - @Test - public void testSingleLevelNestedSimpleProperty() { - NestedMethodProperty nameProperty = new NestedMethodProperty( - vaadin, "name"); - - Assert.assertEquals(String.class, nameProperty.getType()); - Assert.assertEquals("Vaadin", nameProperty.getValue()); - } - - @Test - public void testSingleLevelNestedObjectProperty() { - NestedMethodProperty managerProperty = new NestedMethodProperty( - vaadin, "manager"); - - Assert.assertEquals(Person.class, managerProperty.getType()); - Assert.assertEquals(joonas, managerProperty.getValue()); - } - - @Test - public void testMultiLevelNestedProperty() { - NestedMethodProperty managerNameProperty = new NestedMethodProperty( - vaadin, "manager.name"); - NestedMethodProperty
addressProperty = new NestedMethodProperty
( - vaadin, "manager.address"); - NestedMethodProperty streetProperty = new NestedMethodProperty( - vaadin, "manager.address.street"); - NestedMethodProperty postalCodePrimitiveProperty = new NestedMethodProperty( - vaadin, "manager.address.postalCodePrimitive"); - NestedMethodProperty postalCodeObjectProperty = new NestedMethodProperty( - vaadin, "manager.address.postalCodeObject"); - NestedMethodProperty booleanProperty = new NestedMethodProperty( - vaadin, "manager.address.boolean"); - - Assert.assertEquals(String.class, managerNameProperty.getType()); - Assert.assertEquals("Joonas", managerNameProperty.getValue()); - - Assert.assertEquals(Address.class, addressProperty.getType()); - Assert.assertEquals(oldMill, addressProperty.getValue()); - - Assert.assertEquals(String.class, streetProperty.getType()); - Assert.assertEquals("Ruukinkatu 2-4", streetProperty.getValue()); - - Assert.assertEquals(Integer.class, - postalCodePrimitiveProperty.getType()); - Assert.assertEquals(Integer.valueOf(20540), - postalCodePrimitiveProperty.getValue()); - - Assert.assertEquals(Integer.class, postalCodeObjectProperty.getType()); - Assert.assertEquals(Integer.valueOf(20540), - postalCodeObjectProperty.getValue()); - - Assert.assertEquals(Boolean.class, booleanProperty.getType()); - Assert.assertEquals(Boolean.TRUE, booleanProperty.getValue()); - } - - @Test - public void testEmptyPropertyName() { - try { - new NestedMethodProperty(vaadin, ""); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - - try { - new NestedMethodProperty(vaadin, " "); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - } - - @Test - public void testInvalidPropertyName() { - try { - new NestedMethodProperty(vaadin, "."); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - try { - new NestedMethodProperty(vaadin, ".manager"); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - try { - new NestedMethodProperty(vaadin, "manager."); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - try { - new NestedMethodProperty(vaadin, "manager..name"); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - } - - @Test - public void testInvalidNestedPropertyName() { - try { - new NestedMethodProperty(vaadin, "member"); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - - try { - new NestedMethodProperty(vaadin, "manager.pet"); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - - try { - new NestedMethodProperty(vaadin, "manager.address.city"); - fail(); - } catch (IllegalArgumentException e) { - // should get exception - } - } - - @Test - public void testNullNestedProperty() { - NestedMethodProperty managerNameProperty = new NestedMethodProperty( - vaadin, "manager.name"); - NestedMethodProperty streetProperty = new NestedMethodProperty( - vaadin, "manager.address.street"); - - joonas.setAddress(null); - assertNull(streetProperty.getValue()); - - vaadin.setManager(null); - assertNull(managerNameProperty.getValue()); - assertNull(streetProperty.getValue()); - - vaadin.setManager(joonas); - Assert.assertEquals("Joonas", managerNameProperty.getValue()); - Assert.assertNull(streetProperty.getValue()); - - } - - @Test - public void testMultiLevelNestedPropertySetValue() { - NestedMethodProperty managerNameProperty = new NestedMethodProperty( - vaadin, "manager.name"); - NestedMethodProperty
addressProperty = new NestedMethodProperty
( - vaadin, "manager.address"); - NestedMethodProperty streetProperty = new NestedMethodProperty( - vaadin, "manager.address.street"); - NestedMethodProperty postalCodePrimitiveProperty = new NestedMethodProperty( - vaadin, "manager.address.postalCodePrimitive"); - NestedMethodProperty postalCodeObjectProperty = new NestedMethodProperty( - vaadin, "manager.address.postalCodeObject"); - - managerNameProperty.setValue("Joonas L"); - Assert.assertEquals("Joonas L", joonas.getName()); - streetProperty.setValue("Ruukinkatu"); - Assert.assertEquals("Ruukinkatu", oldMill.getStreet()); - postalCodePrimitiveProperty.setValue(0); - postalCodeObjectProperty.setValue(1); - Assert.assertEquals(0, oldMill.getPostalCodePrimitive()); - Assert.assertEquals(Integer.valueOf(1), oldMill.getPostalCodeObject()); - - postalCodeObjectProperty.setValue(null); - Assert.assertNull(oldMill.getPostalCodeObject()); - - Address address2 = new Address("Other street", 12345); - addressProperty.setValue(address2); - Assert.assertEquals("Other street", streetProperty.getValue()); - } - - @Test - public void testSerialization() throws IOException, ClassNotFoundException { - NestedMethodProperty streetProperty = new NestedMethodProperty( - vaadin, "manager.address.street"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - new ObjectOutputStream(baos).writeObject(streetProperty); - @SuppressWarnings("unchecked") - NestedMethodProperty property2 = (NestedMethodProperty) new ObjectInputStream( - new ByteArrayInputStream(baos.toByteArray())).readObject(); - - Assert.assertEquals("Ruukinkatu 2-4", property2.getValue()); - } - - @Test - public void testSerializationWithIntermediateNull() - throws IOException, ClassNotFoundException { - vaadin.setManager(null); - NestedMethodProperty streetProperty = new NestedMethodProperty( - vaadin, "manager.address.street"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - new ObjectOutputStream(baos).writeObject(streetProperty); - @SuppressWarnings("unchecked") - NestedMethodProperty property2 = (NestedMethodProperty) new ObjectInputStream( - new ByteArrayInputStream(baos.toByteArray())).readObject(); - - Assert.assertNull(property2.getValue()); - } - - @Test - public void testIsReadOnly() { - NestedMethodProperty streetProperty = new NestedMethodProperty( - vaadin, "manager.address.street"); - NestedMethodProperty booleanProperty = new NestedMethodProperty( - vaadin, "manager.address.boolean"); - - Assert.assertFalse(streetProperty.isReadOnly()); - Assert.assertTrue(booleanProperty.isReadOnly()); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java b/server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java deleted file mode 100644 index e0307034cf..0000000000 --- a/server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.vaadin.data.util; - -import org.junit.Assert; -import org.junit.Test; - -public class ObjectPropertyTest { - - public static class TestSuperClass { - private String name; - - public TestSuperClass(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return getName(); - } - } - - public static class TestSubClass extends TestSuperClass { - public TestSubClass(String name) { - super("Subclass: " + name); - } - } - - private TestSuperClass super1 = new TestSuperClass("super1"); - private TestSubClass sub1 = new TestSubClass("sub1"); - - @Test - public void testSimple() { - ObjectProperty prop1 = new ObjectProperty( - super1, TestSuperClass.class); - Assert.assertEquals("super1", prop1.getValue().getName()); - prop1 = new ObjectProperty(super1); - Assert.assertEquals("super1", prop1.getValue().getName()); - - ObjectProperty prop2 = new ObjectProperty( - sub1, TestSubClass.class); - Assert.assertEquals("Subclass: sub1", prop2.getValue().getName()); - prop2 = new ObjectProperty(sub1); - Assert.assertEquals("Subclass: sub1", prop2.getValue().getName()); - } - - @Test - public void testSetValueObjectSuper() { - ObjectProperty prop = new ObjectProperty( - super1, TestSuperClass.class); - Assert.assertEquals("super1", prop.getValue().getName()); - prop.setValue(new TestSuperClass("super2")); - Assert.assertEquals("super1", super1.getName()); - Assert.assertEquals("super2", prop.getValue().getName()); - } - - @Test - public void testSetValueObjectSub() { - ObjectProperty prop = new ObjectProperty( - sub1, TestSubClass.class); - Assert.assertEquals("Subclass: sub1", prop.getValue().getName()); - prop.setValue(new TestSubClass("sub2")); - Assert.assertEquals("Subclass: sub1", sub1.getName()); - Assert.assertEquals("Subclass: sub2", prop.getValue().getName()); - } - - @Test - public void testSetValueStringSuper() { - ObjectProperty prop = new ObjectProperty( - super1, TestSuperClass.class); - Assert.assertEquals("super1", prop.getValue().getName()); - prop.setValue(new TestSuperClass("super2")); - Assert.assertEquals("super1", super1.getName()); - Assert.assertEquals("super2", prop.getValue().getName()); - } - - @Test - public void testSetValueStringSub() { - ObjectProperty prop = new ObjectProperty( - sub1, TestSubClass.class); - Assert.assertEquals("Subclass: sub1", prop.getValue().getName()); - prop.setValue(new TestSubClass("sub2")); - Assert.assertEquals("Subclass: sub1", sub1.getName()); - Assert.assertEquals("Subclass: sub2", prop.getValue().getName()); - } - - @Test - public void testMixedGenerics() { - ObjectProperty prop = new ObjectProperty( - sub1); - Assert.assertEquals("Subclass: sub1", prop.getValue().getName()); - Assert.assertEquals(prop.getType(), TestSubClass.class); - // create correct subclass based on the runtime type of the instance - // given to ObjectProperty constructor, which is a subclass of the type - // parameter - prop.setValue(new TestSubClass("sub2")); - Assert.assertEquals("Subclass: sub2", prop.getValue().getName()); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java b/server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java deleted file mode 100644 index 5f64c0e8d8..0000000000 --- a/server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.vaadin.data.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.SortedSet; -import java.util.TreeSet; - -import org.junit.Assert; -import org.junit.Test; - -public class PerformanceTestIndexedContainerTest { - - private static final int REPEATS = 10; - private final static int ITEMS = 50000; - private static final long ADD_ITEM_FAIL_THRESHOLD = 200; - // TODO should improve performance of these methods - private static final long ADD_ITEM_AT_FAIL_THRESHOLD = 5000; - private static final long ADD_ITEM_AFTER_FAIL_THRESHOLD = 5000; - private static final long ADD_ITEM_AFTER_LAST_FAIL_THRESHOLD = 5000; - private static final long ADD_ITEMS_CONSTRUCTOR_FAIL_THRESHOLD = 200; - - @Test - public void testAddItemPerformance() { - Collection times = new ArrayList(); - for (int j = 0; j < REPEATS; ++j) { - IndexedContainer c = new IndexedContainer(); - long start = System.currentTimeMillis(); - for (int i = 0; i < ITEMS; i++) { - c.addItem(); - } - times.add(System.currentTimeMillis() - start); - } - checkMedian(ITEMS, times, "IndexedContainer.addItem()", - ADD_ITEM_FAIL_THRESHOLD); - } - - @Test - public void testAddItemAtPerformance() { - Collection times = new ArrayList(); - for (int j = 0; j < REPEATS; ++j) { - IndexedContainer c = new IndexedContainer(); - long start = System.currentTimeMillis(); - for (int i = 0; i < ITEMS; i++) { - c.addItemAt(0); - } - times.add(System.currentTimeMillis() - start); - } - checkMedian(ITEMS, times, "IndexedContainer.addItemAt()", - ADD_ITEM_AT_FAIL_THRESHOLD); - } - - @Test - public void testAddItemAfterPerformance() { - Object initialId = "Item0"; - Collection times = new ArrayList(); - for (int j = 0; j < REPEATS; ++j) { - IndexedContainer c = new IndexedContainer(); - c.addItem(initialId); - long start = System.currentTimeMillis(); - for (int i = 0; i < ITEMS; i++) { - c.addItemAfter(initialId); - } - times.add(System.currentTimeMillis() - start); - } - checkMedian(ITEMS, times, "IndexedContainer.addItemAfter()", - ADD_ITEM_AFTER_FAIL_THRESHOLD); - } - - @Test - public void testAddItemAfterLastPerformance() { - // TODO running with less items because slow otherwise - Collection times = new ArrayList(); - for (int j = 0; j < REPEATS; ++j) { - IndexedContainer c = new IndexedContainer(); - c.addItem(); - long start = System.currentTimeMillis(); - for (int i = 0; i < ITEMS / 3; i++) { - c.addItemAfter(c.lastItemId()); - } - times.add(System.currentTimeMillis() - start); - } - checkMedian(ITEMS / 3, times, "IndexedContainer.addItemAfter(lastId)", - ADD_ITEM_AFTER_LAST_FAIL_THRESHOLD); - } - - @Test - public void testAddItemsConstructorPerformance() { - Collection items = new ArrayList(50000); - for (int i = 0; i < ITEMS; ++i) { - items.add(new Object()); - } - - SortedSet times = new TreeSet(); - for (int j = 0; j < REPEATS; ++j) { - long start = System.currentTimeMillis(); - new IndexedContainer(items); - times.add(System.currentTimeMillis() - start); - } - checkMedian(ITEMS, times, "IndexedContainer(Collection)", - ADD_ITEMS_CONSTRUCTOR_FAIL_THRESHOLD); - } - - private void checkMedian(int items, Collection times, - String methodName, long threshold) { - long median = median(times); - System.out.println( - methodName + " timings (ms) for " + items + " items: " + times); - Assert.assertTrue(methodName + " too slow, median time " + median - + "ms for " + items + " items", median <= threshold); - } - - private Long median(Collection times) { - ArrayList list = new ArrayList(times); - Collections.sort(list); - // not exact median in some cases, but good enough - return list.get(list.size() / 2); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java b/server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java deleted file mode 100644 index af9db229c5..0000000000 --- a/server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.vaadin.data.util; - -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Property; -import com.vaadin.data.util.NestedMethodPropertyTest.Person; - -public class PropertyDescriptorTest { - - @Test - public void testMethodPropertyDescriptorSerialization() throws Exception { - PropertyDescriptor[] pds = Introspector.getBeanInfo(Person.class) - .getPropertyDescriptors(); - - MethodPropertyDescriptor descriptor = null; - - for (PropertyDescriptor pd : pds) { - if ("name".equals(pd.getName())) { - descriptor = new MethodPropertyDescriptor(pd.getName(), - String.class, pd.getReadMethod(), pd.getWriteMethod()); - break; - } - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - new ObjectOutputStream(baos).writeObject(descriptor); - @SuppressWarnings("unchecked") - VaadinPropertyDescriptor descriptor2 = (VaadinPropertyDescriptor) new ObjectInputStream( - new ByteArrayInputStream(baos.toByteArray())).readObject(); - - Property property = descriptor2 - .createProperty(new Person("John", null)); - Assert.assertEquals("John", property.getValue()); - } - - @Test - public void testSimpleNestedPropertyDescriptorSerialization() - throws Exception { - NestedPropertyDescriptor pd = new NestedPropertyDescriptor( - "name", Person.class); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - new ObjectOutputStream(baos).writeObject(pd); - @SuppressWarnings("unchecked") - VaadinPropertyDescriptor pd2 = (VaadinPropertyDescriptor) new ObjectInputStream( - new ByteArrayInputStream(baos.toByteArray())).readObject(); - - Property property = pd2.createProperty(new Person("John", null)); - Assert.assertEquals("John", property.getValue()); - } - - @Test - public void testNestedPropertyDescriptorSerialization() throws Exception { - NestedPropertyDescriptor pd = new NestedPropertyDescriptor( - "address.street", Person.class); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - new ObjectOutputStream(baos).writeObject(pd); - @SuppressWarnings("unchecked") - VaadinPropertyDescriptor pd2 = (VaadinPropertyDescriptor) new ObjectInputStream( - new ByteArrayInputStream(baos.toByteArray())).readObject(); - - Property property = pd2.createProperty(new Person("John", null)); - Assert.assertNull(property.getValue()); - } - - @Test - public void testMethodPropertyDescriptorWithPrimitivePropertyType() - throws Exception { - MethodPropertyDescriptor pd = new MethodPropertyDescriptor( - "age", int.class, Person.class.getMethod("getAge"), - Person.class.getMethod("setAge", int.class)); - - Assert.assertEquals(Integer.class, pd.getPropertyType()); - } -} diff --git a/server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java b/server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java deleted file mode 100644 index fc91a20dd0..0000000000 --- a/server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java +++ /dev/null @@ -1,432 +0,0 @@ -package com.vaadin.data.util; - -import java.util.Iterator; - -import org.easymock.EasyMock; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Item.PropertySetChangeEvent; -import com.vaadin.data.Item.PropertySetChangeListener; - -public class PropertySetItemTest { - - private static final String ID1 = "id1"; - private static final String ID2 = "id2"; - private static final String ID3 = "id3"; - - private static final String VALUE1 = "value1"; - private static final String VALUE2 = "value2"; - private static final String VALUE3 = "value3"; - - private ObjectProperty prop1; - private ObjectProperty prop2; - private ObjectProperty prop3; - - private PropertySetChangeListener propertySetListenerMock; - private PropertySetChangeListener propertySetListenerMock2; - - @Before - public void setUp() { - prop1 = new ObjectProperty(VALUE1, String.class); - prop2 = new ObjectProperty(VALUE2, String.class); - prop3 = new ObjectProperty(VALUE3, String.class); - - propertySetListenerMock = EasyMock - .createStrictMock(PropertySetChangeListener.class); - propertySetListenerMock2 = EasyMock - .createMock(PropertySetChangeListener.class); - } - - @After - public void tearDown() { - prop1 = null; - prop2 = null; - prop3 = null; - - propertySetListenerMock = null; - propertySetListenerMock2 = null; - } - - private PropertysetItem createPropertySetItem() { - return new PropertysetItem(); - } - - @Test - public void testEmptyItem() { - PropertysetItem item = createPropertySetItem(); - Assert.assertNotNull(item.getItemPropertyIds()); - Assert.assertEquals(0, item.getItemPropertyIds().size()); - } - - @Test - public void testGetProperty() { - PropertysetItem item = createPropertySetItem(); - - Assert.assertNull(item.getItemProperty(ID1)); - - item.addItemProperty(ID1, prop1); - - Assert.assertEquals(prop1, item.getItemProperty(ID1)); - Assert.assertNull(item.getItemProperty(ID2)); - } - - @Test - public void testAddSingleProperty() { - PropertysetItem item = createPropertySetItem(); - - item.addItemProperty(ID1, prop1); - Assert.assertEquals(1, item.getItemPropertyIds().size()); - Object firstValue = item.getItemPropertyIds().iterator().next(); - Assert.assertEquals(ID1, firstValue); - Assert.assertEquals(prop1, item.getItemProperty(ID1)); - } - - @Test - public void testAddMultipleProperties() { - PropertysetItem item = createPropertySetItem(); - - item.addItemProperty(ID1, prop1); - Assert.assertEquals(1, item.getItemPropertyIds().size()); - Assert.assertEquals(prop1, item.getItemProperty(ID1)); - - item.addItemProperty(ID2, prop2); - Assert.assertEquals(2, item.getItemPropertyIds().size()); - Assert.assertEquals(prop1, item.getItemProperty(ID1)); - Assert.assertEquals(prop2, item.getItemProperty(ID2)); - - item.addItemProperty(ID3, prop3); - Assert.assertEquals(3, item.getItemPropertyIds().size()); - } - - @Test - public void testAddedPropertyOrder() { - PropertysetItem item = createPropertySetItem(); - item.addItemProperty(ID1, prop1); - item.addItemProperty(ID2, prop2); - item.addItemProperty(ID3, prop3); - - Iterator it = item.getItemPropertyIds().iterator(); - Assert.assertEquals(ID1, it.next()); - Assert.assertEquals(ID2, it.next()); - Assert.assertEquals(ID3, it.next()); - } - - @Test - public void testAddPropertyTwice() { - PropertysetItem item = createPropertySetItem(); - Assert.assertTrue(item.addItemProperty(ID1, prop1)); - Assert.assertFalse(item.addItemProperty(ID1, prop1)); - - Assert.assertEquals(1, item.getItemPropertyIds().size()); - Assert.assertEquals(prop1, item.getItemProperty(ID1)); - } - - @Test - public void testCannotChangeProperty() { - PropertysetItem item = createPropertySetItem(); - Assert.assertTrue(item.addItemProperty(ID1, prop1)); - - Assert.assertEquals(prop1, item.getItemProperty(ID1)); - - Assert.assertFalse(item.addItemProperty(ID1, prop2)); - - Assert.assertEquals(1, item.getItemPropertyIds().size()); - Assert.assertEquals(prop1, item.getItemProperty(ID1)); - } - - @Test - public void testRemoveProperty() { - PropertysetItem item = createPropertySetItem(); - item.addItemProperty(ID1, prop1); - item.removeItemProperty(ID1); - - Assert.assertEquals(0, item.getItemPropertyIds().size()); - Assert.assertNull(item.getItemProperty(ID1)); - } - - @Test - public void testRemovePropertyOrder() { - PropertysetItem item = createPropertySetItem(); - item.addItemProperty(ID1, prop1); - item.addItemProperty(ID2, prop2); - item.addItemProperty(ID3, prop3); - - item.removeItemProperty(ID2); - - Iterator it = item.getItemPropertyIds().iterator(); - Assert.assertEquals(ID1, it.next()); - Assert.assertEquals(ID3, it.next()); - } - - @Test - public void testRemoveNonExistentListener() { - PropertysetItem item = createPropertySetItem(); - item.removeListener(propertySetListenerMock); - } - - @Test - public void testRemoveListenerTwice() { - PropertysetItem item = createPropertySetItem(); - item.addListener(propertySetListenerMock); - item.removeListener(propertySetListenerMock); - item.removeListener(propertySetListenerMock); - } - - @Test - public void testAddPropertyNotification() { - // exactly one notification each time - PropertysetItem item = createPropertySetItem(); - - // Expectations and start test - propertySetListenerMock.itemPropertySetChange( - EasyMock.isA(PropertySetChangeEvent.class)); - EasyMock.replay(propertySetListenerMock); - - // Add listener and add a property -> should end up in listener once - item.addListener(propertySetListenerMock); - item.addItemProperty(ID1, prop1); - - // Ensure listener was called once - EasyMock.verify(propertySetListenerMock); - - // Remove the listener -> should not end up in listener when adding a - // property - item.removeListener(propertySetListenerMock); - item.addItemProperty(ID2, prop2); - - // Ensure listener still has been called only once - EasyMock.verify(propertySetListenerMock); - } - - @Test - public void testRemovePropertyNotification() { - // exactly one notification each time - PropertysetItem item = createPropertySetItem(); - item.addItemProperty(ID1, prop1); - item.addItemProperty(ID2, prop2); - - // Expectations and start test - propertySetListenerMock.itemPropertySetChange( - EasyMock.isA(PropertySetChangeEvent.class)); - EasyMock.replay(propertySetListenerMock); - - // Add listener and add a property -> should end up in listener once - item.addListener(propertySetListenerMock); - item.removeItemProperty(ID1); - - // Ensure listener was called once - EasyMock.verify(propertySetListenerMock); - - // Remove the listener -> should not end up in listener - item.removeListener(propertySetListenerMock); - item.removeItemProperty(ID2); - - // Ensure listener still has been called only once - EasyMock.verify(propertySetListenerMock); - } - - @Test - public void testItemEqualsNull() { - PropertysetItem item = createPropertySetItem(); - - Assert.assertFalse(item.equals(null)); - } - - @Test - public void testEmptyItemEquals() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - Assert.assertTrue(item1.equals(item2)); - } - - @Test - public void testItemEqualsSingleProperty() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - item2.addItemProperty(ID1, prop1); - PropertysetItem item3 = createPropertySetItem(); - item3.addItemProperty(ID1, prop1); - PropertysetItem item4 = createPropertySetItem(); - item4.addItemProperty(ID1, prop2); - PropertysetItem item5 = createPropertySetItem(); - item5.addItemProperty(ID2, prop2); - - Assert.assertFalse(item1.equals(item2)); - Assert.assertFalse(item1.equals(item3)); - Assert.assertFalse(item1.equals(item4)); - Assert.assertFalse(item1.equals(item5)); - - Assert.assertTrue(item2.equals(item3)); - Assert.assertFalse(item2.equals(item4)); - Assert.assertFalse(item2.equals(item5)); - - Assert.assertFalse(item3.equals(item4)); - Assert.assertFalse(item3.equals(item5)); - - Assert.assertFalse(item4.equals(item5)); - - Assert.assertFalse(item2.equals(item1)); - } - - @Test - public void testItemEqualsMultipleProperties() { - PropertysetItem item1 = createPropertySetItem(); - item1.addItemProperty(ID1, prop1); - - PropertysetItem item2 = createPropertySetItem(); - item2.addItemProperty(ID1, prop1); - item2.addItemProperty(ID2, prop2); - - PropertysetItem item3 = createPropertySetItem(); - item3.addItemProperty(ID1, prop1); - item3.addItemProperty(ID2, prop2); - - Assert.assertFalse(item1.equals(item2)); - - Assert.assertTrue(item2.equals(item3)); - } - - @Test - public void testItemEqualsPropertyOrder() { - PropertysetItem item1 = createPropertySetItem(); - item1.addItemProperty(ID1, prop1); - item1.addItemProperty(ID2, prop2); - - PropertysetItem item2 = createPropertySetItem(); - item2.addItemProperty(ID2, prop2); - item2.addItemProperty(ID1, prop1); - - Assert.assertFalse(item1.equals(item2)); - } - - @Test - public void testEqualsSingleListener() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - item1.addListener(propertySetListenerMock); - - Assert.assertFalse(item1.equals(item2)); - Assert.assertFalse(item2.equals(item1)); - - item2.addListener(propertySetListenerMock); - - Assert.assertTrue(item1.equals(item2)); - Assert.assertTrue(item2.equals(item1)); - } - - @Test - public void testEqualsMultipleListeners() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - item1.addListener(propertySetListenerMock); - item1.addListener(propertySetListenerMock2); - - item2.addListener(propertySetListenerMock); - - Assert.assertFalse(item1.equals(item2)); - Assert.assertFalse(item2.equals(item1)); - - item2.addListener(propertySetListenerMock2); - - Assert.assertTrue(item1.equals(item2)); - Assert.assertTrue(item2.equals(item1)); - } - - @Test - public void testEqualsAddRemoveListener() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - item1.addListener(propertySetListenerMock); - item1.removeListener(propertySetListenerMock); - - Assert.assertTrue(item1.equals(item2)); - Assert.assertTrue(item2.equals(item1)); - } - - @Test - public void testItemHashCodeEmpty() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - Assert.assertEquals(item1.hashCode(), item2.hashCode()); - } - - @Test - public void testItemHashCodeAddProperties() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - Assert.assertEquals(item1.hashCode(), item2.hashCode()); - - item1.addItemProperty(ID1, prop1); - item1.addItemProperty(ID2, prop2); - // hashCodes can be equal even if items are different - - item2.addItemProperty(ID1, prop1); - item2.addItemProperty(ID2, prop2); - // but here hashCodes must be equal - Assert.assertEquals(item1.hashCode(), item2.hashCode()); - } - - @Test - public void testItemHashCodeAddListeners() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - Assert.assertEquals(item1.hashCode(), item2.hashCode()); - - item1.addListener(propertySetListenerMock); - // hashCodes can be equal even if items are different - - item2.addListener(propertySetListenerMock); - // but here hashCodes must be equal - Assert.assertEquals(item1.hashCode(), item2.hashCode()); - } - - @Test - public void testItemHashCodeAddRemoveProperty() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - item1.addItemProperty(ID1, prop1); - item1.removeItemProperty(ID1); - - Assert.assertEquals(item1.hashCode(), item2.hashCode()); - } - - @Test - public void testItemHashCodeAddRemoveListener() { - PropertysetItem item1 = createPropertySetItem(); - PropertysetItem item2 = createPropertySetItem(); - - item1.addListener(propertySetListenerMock); - item1.removeListener(propertySetListenerMock); - - Assert.assertEquals(item1.hashCode(), item2.hashCode()); - } - - @Test - public void testToString() { - // toString() behavior is specified in the class javadoc - PropertysetItem item = createPropertySetItem(); - - Assert.assertEquals("", item.toString()); - - item.addItemProperty(ID1, prop1); - - Assert.assertEquals(String.valueOf(prop1.getValue()), item.toString()); - - item.addItemProperty(ID2, prop2); - - Assert.assertEquals(String.valueOf(prop1.getValue()) + " " - + String.valueOf(prop2.getValue()), item.toString()); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java b/server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java deleted file mode 100644 index df4258f316..0000000000 --- a/server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.vaadin.data.util; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.fieldgroup.PropertyId; -import com.vaadin.v7.ui.LegacyTextField; - -public class ReflectToolsGetSuperFieldTest { - - @Test - public void getFieldFromSuperClass() { - class MyClass { - @PropertyId("testProperty") - LegacyTextField test = new LegacyTextField("This is a test"); - } - class MySubClass extends MyClass { - // no fields here - } - - PropertysetItem item = new PropertysetItem(); - item.addItemProperty("testProperty", - new ObjectProperty("Value of testProperty")); - - MySubClass form = new MySubClass(); - - FieldGroup binder = new FieldGroup(item); - binder.bindMemberFields(form); - - assertTrue("Value of testProperty".equals(form.test.getValue())); - } - -} diff --git a/server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java b/server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java deleted file mode 100644 index ef3e416f96..0000000000 --- a/server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.data.util; - -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.v7.ui.LegacyTextField; - -/** - * Test verifying that TransactionalPropertyWrapper removes it's listener from - * wrapped Property - * - * @since 7.1.15 - * @author Vaadin Ltd - */ -public class TransactionalPropertyWrapperTest { - - @SuppressWarnings("serial") - public class TestingProperty - extends ObjectProperty { - - private List listeners = new ArrayList(); - - public TestingProperty(Object value) { - super(value); - } - - @Override - public void addValueChangeListener(ValueChangeListener listener) { - super.addValueChangeListener(listener); - listeners.add(listener); - } - - @Override - public void removeValueChangeListener(ValueChangeListener listener) { - super.removeValueChangeListener(listener); - if (listeners.contains(listener)) { - listeners.remove(listener); - } - } - - public boolean hasListeners() { - return !listeners.isEmpty(); - } - } - - private final LegacyTextField nameField = new LegacyTextField("Name"); - private final LegacyTextField ageField = new LegacyTextField("Age"); - private final LegacyTextField unboundField = new LegacyTextField( - "No FieldGroup"); - private final TestingProperty unboundProp = new TestingProperty( - "Hello World"); - private final PropertysetItem item = new PropertysetItem(); - - @Test - public void fieldGroupBindAndUnbind() { - item.addItemProperty("name", - new TestingProperty("Just some text")); - item.addItemProperty("age", new TestingProperty("42")); - - final FieldGroup binder = new FieldGroup(item); - binder.setBuffered(false); - - for (int i = 0; i < 2; ++i) { - binder.bind(nameField, "name"); - binder.bind(ageField, "age"); - unboundField.setPropertyDataSource(unboundProp); - - assertTrue("No listeners in Properties", fieldsHaveListeners(true)); - - binder.unbind(nameField); - binder.unbind(ageField); - unboundField.setPropertyDataSource(null); - - assertTrue("Listeners in Properties after unbinding", - fieldsHaveListeners(false)); - } - } - - /** - * Check that all listeners have same hasListeners() response - * - * @param expected - * expected response - * @return true if all are the same as expected. false if not - */ - private boolean fieldsHaveListeners(boolean expected) { - for (Object id : item.getItemPropertyIds()) { - TestingProperty itemProperty = (TestingProperty) item - .getItemProperty(id); - - if (itemProperty.hasListeners() != expected) { - return false; - } - } - return unboundProp.hasListeners() == expected; - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java b/server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java deleted file mode 100644 index 1b93a3063d..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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.tests.server; - -import java.util.EventObject; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.event.ContextClickEvent; -import com.vaadin.event.ContextClickEvent.ContextClickListener; -import com.vaadin.ui.AbstractComponent; -import com.vaadin.ui.LegacyGrid.GridContextClickEvent; -import com.vaadin.ui.Table.TableContextClickEvent; - -/** - * Server-side unit tests to see that context click events are sent to listeners - * correctly. - * - * If a listener is listening to a super type of an event, it should get the - * event. i.e. Listening to ContextClickEvent, it should get the specialized - * GridContextClickEvent as well. - * - * If a listener is listening to a sub-type of an event, it should not get the - * super version. i.e. Listening to GridContextClickEvent, it should not get a - * plain ContextClickEvent. - */ -public class ContextClickListenerTest extends AbstractComponent { - - private final static ContextClickEvent contextClickEvent = EasyMock - .createMock(ContextClickEvent.class); - private final static GridContextClickEvent gridContextClickEvent = EasyMock - .createMock(GridContextClickEvent.class); - private final static TableContextClickEvent tableContextClickEvent = EasyMock - .createMock(TableContextClickEvent.class); - - private final AssertListener contextListener = new AssertListener(); - private final AssertListener ctxtListener2 = new AssertListener(); - - public static class AssertListener implements ContextClickListener { - - private Class expected = null; - private String error = null; - - @Override - public void contextClick(ContextClickEvent event) { - if (expected == null) { - error = "Unexpected context click event."; - return; - } - - if (!expected.isAssignableFrom(event.getClass())) { - error = "Expected event type did not match the actual event."; - } - - expected = null; - } - - public void expect(Class clazz) { - validate(); - expected = clazz; - } - - public void validate() { - if (expected != null) { - Assert.fail("Expected context click never happened."); - } else if (error != null) { - Assert.fail(error); - } - } - } - - @Test - public void testListenerGetsASubClass() { - addContextClickListener(contextListener); - contextListener.expect(GridContextClickEvent.class); - fireEvent(gridContextClickEvent); - } - - @Test - public void testListenerGetsExactClass() { - addContextClickListener(contextListener); - contextListener.expect(ContextClickEvent.class); - fireEvent(contextClickEvent); - } - - /** - * Multiple listeners should get fitting events. - */ - @Test - public void testMultipleListenerGetEvents() { - addContextClickListener(ctxtListener2); - addContextClickListener(contextListener); - - ctxtListener2.expect(GridContextClickEvent.class); - contextListener.expect(GridContextClickEvent.class); - - fireEvent(gridContextClickEvent); - } - - @Test - public void testAddAndRemoveListener() { - addContextClickListener(contextListener); - contextListener.expect(ContextClickEvent.class); - - fireEvent(contextClickEvent); - - removeContextClickListener(contextListener); - - fireEvent(contextClickEvent); - } - - @Test - public void testAddAndRemoveMultipleListeners() { - addContextClickListener(ctxtListener2); - addContextClickListener(contextListener); - - ctxtListener2.expect(GridContextClickEvent.class); - contextListener.expect(GridContextClickEvent.class); - fireEvent(gridContextClickEvent); - - removeContextClickListener(ctxtListener2); - - contextListener.expect(GridContextClickEvent.class); - fireEvent(gridContextClickEvent); - } - - @Test(expected = AssertionError.class) - public void testExpectedEventNotReceived() { - addContextClickListener(contextListener); - contextListener.expect(GridContextClickEvent.class); - fireEvent(contextClickEvent); - } - - @Test(expected = AssertionError.class) - public void testUnexpectedEventReceived() { - addContextClickListener(contextListener); - fireEvent(gridContextClickEvent); - } - - @Override - protected void fireEvent(EventObject event) { - super.fireEvent(event); - - // Validate listeners automatically. - ctxtListener2.validate(); - contextListener.validate(); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java b/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java deleted file mode 100644 index f5c315b440..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.vaadin.tests.server.component.fieldgroup; - -import static org.junit.Assert.assertEquals; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.fieldgroup.BeanFieldGroup; -import com.vaadin.data.fieldgroup.FieldGroup.CommitException; -import com.vaadin.data.fieldgroup.PropertyId; -import com.vaadin.data.util.BeanItem; -import com.vaadin.ui.RichTextArea; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyTextField; - -public class BeanFieldGroupTest { - - private static final String DEFAULT_FOR_BASIC_FIELD = "default"; - - public static class MyBean { - - private String basicField = DEFAULT_FOR_BASIC_FIELD; - - private String anotherField; - - private MyNestedBean nestedBean = new MyNestedBean(); - - public MyNestedBean getNestedBean() { - return nestedBean; - } - - /** - * @return the basicField - */ - public String getBasicField() { - return basicField; - } - - /** - * @param basicField - * the basicField to set - */ - public void setBasicField(String basicField) { - this.basicField = basicField; - } - - /** - * @return the anotherField - */ - public String getAnotherField() { - return anotherField; - } - - /** - * @param anotherField - * the anotherField to set - */ - public void setAnotherField(String anotherField) { - this.anotherField = anotherField; - } - } - - public static class MyNestedBean { - - private String hello = "Hello world"; - - public String getHello() { - return hello; - } - } - - public static class ViewStub { - - LegacyTextField basicField = new LegacyTextField(); - - @PropertyId("anotherField") - LegacyTextField boundWithAnnotation = new LegacyTextField(); - } - - @SuppressWarnings("unchecked") - @Test - public void testStaticBindingHelper() { - MyBean myBean = new MyBean(); - - ViewStub viewStub = new ViewStub(); - BeanFieldGroup bindFields = BeanFieldGroup - .bindFieldsUnbuffered(myBean, viewStub); - - LegacyField field = (LegacyField) bindFields - .getField("basicField"); - Assert.assertEquals(DEFAULT_FOR_BASIC_FIELD, myBean.basicField); - field.setValue("Foo"); - Assert.assertEquals("Foo", myBean.basicField); - - field = (LegacyField) bindFields.getField("anotherField"); - field.setValue("Foo"); - Assert.assertEquals("Foo", myBean.anotherField); - } - - @SuppressWarnings("unchecked") - @Test - public void testStaticBufferedBindingHelper() throws CommitException { - MyBean myBean = new MyBean(); - - ViewStub viewStub = new ViewStub(); - BeanFieldGroup bindFields = BeanFieldGroup - .bindFieldsBuffered(myBean, viewStub); - - LegacyField basicField = (LegacyField) bindFields - .getField("basicField"); - basicField.setValue("Foo"); - Assert.assertEquals(DEFAULT_FOR_BASIC_FIELD, myBean.basicField); - - LegacyField anotherField = (LegacyField) bindFields - .getField("anotherField"); - anotherField.setValue("Foo"); - Assert.assertNull(myBean.anotherField); - - bindFields.commit(); - - Assert.assertEquals("Foo", myBean.basicField); - Assert.assertEquals("Foo", myBean.anotherField); - - } - - @Test - public void buildAndBindNestedProperty() { - - MyBean bean = new MyBean(); - - BeanFieldGroup bfg = new BeanFieldGroup(MyBean.class); - bfg.setItemDataSource(bean); - - com.vaadin.v7.ui.LegacyField helloField = bfg - .buildAndBind("Hello string", "nestedBean.hello"); - assertEquals(bean.nestedBean.hello, helloField.getValue().toString()); - } - - @Test - public void buildAndBindNestedRichTextAreaProperty() { - - MyBean bean = new MyBean(); - - BeanFieldGroup bfg = new BeanFieldGroup(MyBean.class); - bfg.setItemDataSource(bean); - - RichTextArea helloField = bfg.buildAndBind("Hello string", - "nestedBean.hello", RichTextArea.class); - assertEquals(bean.nestedBean.hello, helloField.getValue().toString()); - } - - @Test - public void setDataSource_nullBean_nullBeanIsSetInDataSource() { - BeanFieldGroup group = new BeanFieldGroup(MyBean.class); - - group.setItemDataSource((MyBean) null); - - BeanItem dataSource = group.getItemDataSource(); - Assert.assertNull("Data source is null for null bean", dataSource); - } - - @Test - public void setDataSource_nullItem_nullDataSourceIsSet() { - BeanFieldGroup group = new BeanFieldGroup(MyBean.class); - - group.setItemDataSource((Item) null); - BeanItem dataSource = group.getItemDataSource(); - Assert.assertNull("Group returns not null data source", dataSource); - } - -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java b/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java deleted file mode 100644 index a324c02cfc..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.vaadin.tests.server.component.fieldgroup; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; -import com.vaadin.ui.FormLayout; -import com.vaadin.v7.ui.LegacyTextField; - -public class CaseInsensitiveBindingTest { - - @Test - public void caseInsensitivityAndUnderscoreRemoval() { - PropertysetItem item = new PropertysetItem(); - item.addItemProperty("LastName", new ObjectProperty("Sparrow")); - - class MyForm extends FormLayout { - LegacyTextField lastName = new LegacyTextField("Last name"); - - public MyForm() { - - // Should bind to the LastName property - addComponent(lastName); - } - } - - MyForm form = new MyForm(); - - FieldGroup binder = new FieldGroup(item); - binder.bindMemberFields(form); - - assertTrue("Sparrow".equals(form.lastName.getValue())); - } - - @Test - public void UnderscoreRemoval() { - PropertysetItem item = new PropertysetItem(); - item.addItemProperty("first_name", new ObjectProperty("Jack")); - - class MyForm extends FormLayout { - LegacyTextField firstName = new LegacyTextField("First name"); - - public MyForm() { - // Should bind to the first_name property - addComponent(firstName); - } - } - - MyForm form = new MyForm(); - - FieldGroup binder = new FieldGroup(item); - binder.bindMemberFields(form); - - assertTrue("Jack".equals(form.firstName.getValue())); - } - - @Test - public void perfectMatchPriority() { - PropertysetItem item = new PropertysetItem(); - item.addItemProperty("first_name", - new ObjectProperty("Not this")); - item.addItemProperty("firstName", new ObjectProperty("This")); - - class MyForm extends FormLayout { - LegacyTextField firstName = new LegacyTextField("First name"); - - public MyForm() { - // should bind to the firstName property, not first_name - // property - addComponent(firstName); - } - } - - MyForm form = new MyForm(); - - FieldGroup binder = new FieldGroup(item); - binder.bindMemberFields(form); - - assertTrue("This".equals(form.firstName.getValue())); - } - -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java b/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java deleted file mode 100644 index 43d2bf8a6a..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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.tests.server.component.fieldgroup; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.fieldgroup.FieldGroup.CommitException; -import com.vaadin.data.util.AbstractProperty; -import com.vaadin.v7.data.Validator.InvalidValueException; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyTextField; - -/** - * - * Tests for {@link FieldGroup}. - * - * @author Vaadin Ltd - */ -public class FieldGroupTest { - - @Test - public void setReadOnly_readOnlyAndNoDataSource_fieldIsReadOnly() { - FieldGroup fieldGroup = new FieldGroup(); - - LegacyTextField field = new LegacyTextField(); - fieldGroup.bind(field, "property"); - - fieldGroup.setReadOnly(true); - - Assert.assertTrue("Field is not read only", field.isReadOnly()); - } - - @Test - public void setReadOnly_writableAndNoDataSource_fieldIsWritable() { - FieldGroup fieldGroup = new FieldGroup(); - - LegacyTextField field = new LegacyTextField(); - fieldGroup.bind(field, "property"); - - fieldGroup.setReadOnly(false); - - Assert.assertFalse("Field is not writable", field.isReadOnly()); - } - - @Test - public void commit_validationFailed_allValidationFailuresAvailable() - throws CommitException { - FieldGroup fieldGroup = new FieldGroup(); - - fieldGroup.setItemDataSource(new TestItem()); - - LegacyTextField field1 = new LegacyTextField(); - field1.setRequired(true); - fieldGroup.bind(field1, "prop1"); - - LegacyTextField field2 = new LegacyTextField(); - field2.setRequired(true); - fieldGroup.bind(field2, "prop2"); - - Set set = new HashSet<>(Arrays.asList(field1, field2)); - - try { - fieldGroup.commit(); - Assert.fail("No commit exception is thrown"); - } catch (CommitException exception) { - Map, ? extends InvalidValueException> invalidFields = exception - .getInvalidFields(); - for (Entry, ? extends InvalidValueException> entry : invalidFields - .entrySet()) { - set.remove(entry.getKey()); - } - Assert.assertEquals( - "Some fields are not found in the invalid fields map", 0, - set.size()); - Assert.assertEquals( - "Invalid value exception should be thrown for each field", - 2, invalidFields.size()); - } - } - - private static class TestItem implements Item { - - @Override - public Property getItemProperty(Object id) { - return new StringProperty(); - } - - @Override - public Collection getItemPropertyIds() { - return Arrays.asList("prop1", "prop2"); - } - - @Override - public boolean addItemProperty(Object id, Property property) - throws UnsupportedOperationException { - return false; - } - - @Override - public boolean removeItemProperty(Object id) - throws UnsupportedOperationException { - return false; - } - - } - - private static class StringProperty extends AbstractProperty { - - @Override - public String getValue() { - return null; - } - - @Override - public void setValue(String newValue) - throws Property.ReadOnlyException { - } - - @Override - public Class getType() { - return String.class; - } - } - -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java b/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java deleted file mode 100644 index f14b966a2d..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.vaadin.tests.server.component.fieldgroup; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.util.BeanItem; -import com.vaadin.tests.data.bean.BeanWithReadOnlyField; -import com.vaadin.v7.ui.LegacyTextField; - -public class FieldGroupWithReadOnlyPropertiesTest { - - private LegacyTextField readOnlyField = new LegacyTextField(); - private LegacyTextField writableField = new LegacyTextField(); - - @Test - public void bindReadOnlyPropertyToFieldGroup() { - BeanWithReadOnlyField bean = new BeanWithReadOnlyField(); - BeanItem beanItem = new BeanItem( - bean); - beanItem.getItemProperty("readOnlyField").setReadOnly(true); - - FieldGroup fieldGroup = new FieldGroup(beanItem); - fieldGroup.bindMemberFields(this); - - assertTrue(readOnlyField.isReadOnly()); - assertFalse(writableField.isReadOnly()); - } - - @Test - public void fieldGroupSetReadOnlyTest() { - BeanWithReadOnlyField bean = new BeanWithReadOnlyField(); - BeanItem beanItem = new BeanItem( - bean); - beanItem.getItemProperty("readOnlyField").setReadOnly(true); - - FieldGroup fieldGroup = new FieldGroup(beanItem); - fieldGroup.bindMemberFields(this); - - fieldGroup.setReadOnly(true); - assertTrue(readOnlyField.isReadOnly()); - assertTrue(writableField.isReadOnly()); - - fieldGroup.setReadOnly(false); - assertTrue(readOnlyField.isReadOnly()); - assertFalse(writableField.isReadOnly()); - } - -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java b/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java deleted file mode 100644 index 0ef30316ac..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.vaadin.tests.server.component.fieldgroup; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.fieldgroup.PropertyId; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; -import com.vaadin.ui.FormLayout; -import com.vaadin.v7.ui.LegacyTextField; - -public class FieldNamedDescriptionTest { - - @Test - public void bindReadOnlyPropertyToFieldGroup() { - // Create an item - PropertysetItem item = new PropertysetItem(); - item.addItemProperty("name", new ObjectProperty("Zaphod")); - item.addItemProperty("description", - new ObjectProperty("This is a description")); - - // Define a form as a class that extends some layout - class MyForm extends FormLayout { - // Member that will bind to the "name" property - LegacyTextField name = new LegacyTextField("Name"); - - // This member will not bind to the desctiptionProperty as the name - // description conflicts with something in the binding process - @PropertyId("description") - LegacyTextField description = new LegacyTextField("Description"); - - public MyForm() { - - // Add the fields - addComponent(name); - addComponent(description); - } - } - - // Create one - MyForm form = new MyForm(); - - // Now create a binder that can also creates the fields - // using the default field factory - FieldGroup binder = new FieldGroup(item); - binder.bindMemberFields(form); - - assertTrue(form.description.getValue().equals("This is a description")); - } - -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java deleted file mode 100644 index 3bff93c042..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.util.BeanItem; -import com.vaadin.data.util.BeanItemContainer; -import com.vaadin.data.util.MethodProperty.MethodException; -import com.vaadin.tests.data.bean.Person; -import com.vaadin.ui.LegacyGrid; - -public class GridAddRowBuiltinContainerTest { - LegacyGrid grid = new LegacyGrid(); - Container.Indexed container; - - @Before - public void setUp() { - container = grid.getContainerDataSource(); - - grid.addColumn("myColumn"); - } - - @Test - public void testSimpleCase() { - Object itemId = grid.addRow("Hello"); - - Assert.assertEquals(Integer.valueOf(1), itemId); - - Assert.assertEquals("There should be one item in the container", 1, - container.size()); - - Assert.assertEquals("Hello", container.getItem(itemId) - .getItemProperty("myColumn").getValue()); - } - - @Test(expected = IllegalArgumentException.class) - public void testNullParameter() { - // cast to Object[] to distinguish from one null varargs value - grid.addRow((Object[]) null); - } - - @Test - public void testNullValue() { - // cast to Object to distinguish from a null varargs array - Object itemId = grid.addRow((Object) null); - - Assert.assertEquals(null, container.getItem(itemId) - .getItemProperty("myColumn").getValue()); - } - - @Test(expected = IllegalArgumentException.class) - public void testAddInvalidType() { - grid.addRow(Integer.valueOf(5)); - } - - @Test - public void testMultipleProperties() { - grid.addColumn("myOther", Integer.class); - - Object itemId = grid.addRow("Hello", Integer.valueOf(3)); - - Item item = container.getItem(itemId); - Assert.assertEquals("Hello", - item.getItemProperty("myColumn").getValue()); - Assert.assertEquals(Integer.valueOf(3), - item.getItemProperty("myOther").getValue()); - } - - @Test(expected = IllegalArgumentException.class) - public void testInvalidPropertyAmount() { - grid.addRow("Hello", Integer.valueOf(3)); - } - - @Test - public void testRemovedColumn() { - grid.addColumn("myOther", Integer.class); - grid.removeColumn("myColumn"); - - grid.addRow(Integer.valueOf(3)); - - Item item = container.getItem(Integer.valueOf(1)); - Assert.assertEquals("Default value should be used for removed column", - "", item.getItemProperty("myColumn").getValue()); - Assert.assertEquals(Integer.valueOf(3), - item.getItemProperty("myOther").getValue()); - } - - @Test - public void testMultiplePropertiesAfterReorder() { - grid.addColumn("myOther", Integer.class); - - grid.setColumnOrder("myOther", "myColumn"); - - grid.addRow(Integer.valueOf(3), "Hello"); - - Item item = container.getItem(Integer.valueOf(1)); - Assert.assertEquals("Hello", - item.getItemProperty("myColumn").getValue()); - Assert.assertEquals(Integer.valueOf(3), - item.getItemProperty("myOther").getValue()); - } - - @Test - public void testInvalidType_NothingAdded() { - try { - grid.addRow(Integer.valueOf(5)); - - // Can't use @Test(expect = Foo.class) since we also want to verify - // state after exception was thrown - Assert.fail("Adding wrong type should throw ClassCastException"); - } catch (IllegalArgumentException e) { - Assert.assertEquals("No row should have been added", 0, - container.size()); - } - } - - @Test - public void testUnsupportingContainer() { - setContainerRemoveColumns(new BeanItemContainer(Person.class)); - try { - - grid.addRow("name"); - - // Can't use @Test(expect = Foo.class) since we also want to verify - // state after exception was thrown - Assert.fail( - "Adding to BeanItemContainer container should throw UnsupportedOperationException"); - } catch (UnsupportedOperationException e) { - Assert.assertEquals("No row should have been added", 0, - container.size()); - } - } - - @Test - public void testCustomContainer() { - BeanItemContainer container = new BeanItemContainer( - Person.class) { - @Override - public Object addItem() { - BeanItem item = addBean(new Person()); - return getBeanIdResolver().getIdForBean(item.getBean()); - } - }; - - setContainerRemoveColumns(container); - - grid.addRow("name"); - - Assert.assertEquals(1, container.size()); - - Assert.assertEquals("name", container.getIdByIndex(0).getFirstName()); - } - - @Test - public void testSetterThrowing() { - BeanItemContainer container = new BeanItemContainer( - Person.class) { - @Override - public Object addItem() { - BeanItem item = addBean(new Person() { - @Override - public void setFirstName(String firstName) { - if ("name".equals(firstName)) { - throw new RuntimeException(firstName); - } else { - super.setFirstName(firstName); - } - } - }); - return getBeanIdResolver().getIdForBean(item.getBean()); - } - }; - - setContainerRemoveColumns(container); - - try { - - grid.addRow("name"); - - // Can't use @Test(expect = Foo.class) since we also want to verify - // state after exception was thrown - Assert.fail("Adding row should throw MethodException"); - } catch (MethodException e) { - Assert.assertEquals("Got the wrong exception", "name", - e.getCause().getMessage()); - - Assert.assertEquals("There should be no rows in the container", 0, - container.size()); - } - } - - private void setContainerRemoveColumns( - BeanItemContainer container) { - // Remove predefined column so we can change container - grid.removeAllColumns(); - grid.setContainerDataSource(container); - grid.removeAllColumns(); - grid.addColumn("firstName"); - } - -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java deleted file mode 100644 index f126e636ba..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import java.util.Iterator; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.ui.Component; -import com.vaadin.ui.LegacyGrid.FooterCell; -import com.vaadin.ui.LegacyGrid.HeaderCell; -import com.vaadin.ui.Label; - -public class GridChildrenTest { - - @Test - public void componentsInMergedHeader() { - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("foo"); - grid.addColumn("bar"); - grid.addColumn("baz"); - HeaderCell merged = grid.getDefaultHeaderRow().join("foo", "bar", - "baz"); - Label label = new Label(); - merged.setComponent(label); - Iterator i = grid.iterator(); - Assert.assertEquals(label, i.next()); - Assert.assertFalse(i.hasNext()); - } - - @Test - public void componentsInMergedFooter() { - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("foo"); - grid.addColumn("bar"); - grid.addColumn("baz"); - FooterCell merged = grid.addFooterRowAt(0).join("foo", "bar", "baz"); - Label label = new Label(); - merged.setComponent(label); - Iterator i = grid.iterator(); - Assert.assertEquals(label, i.next()); - Assert.assertFalse(i.hasNext()); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java deleted file mode 100644 index 1af2577fa3..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Property; -import com.vaadin.data.util.IndexedContainer; - -public class GridColumnAddingAndRemovingTest { - - LegacyGrid grid = new LegacyGrid(); - Container.Indexed container; - - @Before - public void setUp() { - container = grid.getContainerDataSource(); - container.addItem(); - } - - @Test - public void testAddColumn() { - grid.addColumn("foo"); - - Property property = container - .getContainerProperty(container.firstItemId(), "foo"); - assertEquals(property.getType(), String.class); - } - - @Test(expected = IllegalStateException.class) - public void testAddColumnTwice() { - grid.addColumn("foo"); - grid.addColumn("foo"); - } - - @Test - public void testAddRemoveAndAddAgainColumn() { - grid.addColumn("foo"); - grid.removeColumn("foo"); - - // Removing a column, doesn't remove the property - Property property = container - .getContainerProperty(container.firstItemId(), "foo"); - assertEquals(property.getType(), String.class); - grid.addColumn("foo"); - } - - @Test - public void testAddNumberColumns() { - grid.addColumn("bar", Integer.class); - grid.addColumn("baz", Double.class); - - Property property = container - .getContainerProperty(container.firstItemId(), "bar"); - assertEquals(property.getType(), Integer.class); - assertEquals(null, property.getValue()); - property = container.getContainerProperty(container.firstItemId(), - "baz"); - assertEquals(property.getType(), Double.class); - assertEquals(null, property.getValue()); - } - - @Test(expected = IllegalStateException.class) - public void testAddDifferentTypeColumn() { - grid.addColumn("foo"); - grid.removeColumn("foo"); - grid.addColumn("foo", Integer.class); - } - - @Test(expected = IllegalStateException.class) - public void testAddColumnToNonDefaultContainer() { - grid.setContainerDataSource(new IndexedContainer()); - grid.addColumn("foo"); - } - - @Test - public void testAddColumnForExistingProperty() { - grid.addColumn("bar"); - IndexedContainer container2 = new IndexedContainer(); - container2.addContainerProperty("foo", Integer.class, 0); - container2.addContainerProperty("bar", String.class, ""); - grid.setContainerDataSource(container2); - assertNull("Grid should not have a column for property foo", - grid.getColumn("foo")); - assertNotNull("Grid did should have a column for property bar", - grid.getColumn("bar")); - for (LegacyGrid.Column column : grid.getColumns()) { - assertNotNull("Grid getColumns returned a null value", column); - } - - grid.removeAllColumns(); - grid.addColumn("foo"); - assertNotNull("Grid should now have a column for property foo", - grid.getColumn("foo")); - assertNull("Grid should not have a column for property bar anymore", - grid.getColumn("bar")); - } - - @Test(expected = IllegalStateException.class) - public void testAddIncompatibleColumnProperty() { - grid.addColumn("bar"); - grid.removeAllColumns(); - grid.addColumn("bar", Integer.class); - } - - @Test - public void testAddBooleanColumnProperty() { - grid.addColumn("foo", Boolean.class); - Property property = container - .getContainerProperty(container.firstItemId(), "foo"); - assertEquals(property.getType(), Boolean.class); - assertEquals(property.getValue(), null); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java deleted file mode 100644 index 570e9b92fc..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import static org.easymock.EasyMock.and; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.isA; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -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.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Set; - -import com.vaadin.ui.LegacyGrid; -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.shared.util.SharedUtil; -import com.vaadin.ui.LegacyGrid.Column; -import com.vaadin.ui.LegacyGrid.ColumnResizeEvent; -import com.vaadin.ui.LegacyGrid.ColumnResizeListener; -import com.vaadin.v7.ui.LegacyTextField; - -public class GridColumnsTest { - - private LegacyGrid grid; - - private GridState state; - - private Method getStateMethod; - - private Field columnIdGeneratorField; - - private KeyMapper columnIdMapper; - - @Before - @SuppressWarnings("unchecked") - public void setup() throws Exception { - IndexedContainer ds = new IndexedContainer(); - for (int c = 0; c < 10; c++) { - ds.addContainerProperty("column" + c, String.class, ""); - } - ds.addContainerProperty("noSort", Object.class, null); - grid = new LegacyGrid(ds); - - getStateMethod = LegacyGrid.class.getDeclaredMethod("getState"); - getStateMethod.setAccessible(true); - - state = (GridState) getStateMethod.invoke(grid); - - columnIdGeneratorField = LegacyGrid.class.getDeclaredField("columnKeys"); - columnIdGeneratorField.setAccessible(true); - - columnIdMapper = (KeyMapper) columnIdGeneratorField.get(grid); - } - - @Test - public void testColumnGeneration() throws Exception { - - for (Object propertyId : grid.getContainerDataSource() - .getContainerPropertyIds()) { - - // All property ids should get a column - Column column = grid.getColumn(propertyId); - assertNotNull(column); - - // Humanized property id should be the column header by default - assertEquals( - SharedUtil.camelCaseToHumanFriendly(propertyId.toString()), - grid.getDefaultHeaderRow().getCell(propertyId).getText()); - } - } - - @Test - public void testModifyingColumnProperties() throws Exception { - - // Modify first column - Column column = grid.getColumn("column1"); - assertNotNull(column); - - column.setHeaderCaption("CustomHeader"); - assertEquals("CustomHeader", column.getHeaderCaption()); - assertEquals(column.getHeaderCaption(), - grid.getDefaultHeaderRow().getCell("column1").getText()); - - column.setWidth(100); - assertEquals(100, column.getWidth(), 0.49d); - assertEquals(column.getWidth(), getColumnState("column1").width, 0.49d); - - try { - column.setWidth(-1); - fail("Setting width to -1 should throw exception"); - } catch (IllegalArgumentException iae) { - // expected - } - - assertEquals(100, column.getWidth(), 0.49d); - assertEquals(100, getColumnState("column1").width, 0.49d); - } - - @Test - public void testRemovingColumnByRemovingPropertyFromContainer() - throws Exception { - - Column column = grid.getColumn("column1"); - assertNotNull(column); - - // Remove column - grid.getContainerDataSource().removeContainerProperty("column1"); - - try { - column.setHeaderCaption("asd"); - - fail("Succeeded in modifying a detached column"); - } catch (IllegalStateException ise) { - // Detached state should throw exception - } - - try { - column.setWidth(123); - fail("Succeeded in modifying a detached column"); - } catch (IllegalStateException ise) { - // Detached state should throw exception - } - - assertNull(grid.getColumn("column1")); - assertNull(getColumnState("column1")); - } - - @Test - public void testAddingColumnByAddingPropertyToContainer() throws Exception { - grid.getContainerDataSource().addContainerProperty("columnX", - String.class, ""); - Column column = grid.getColumn("columnX"); - assertNotNull(column); - } - - @Test - public void testHeaderVisiblility() throws Exception { - - assertTrue(grid.isHeaderVisible()); - assertTrue(state.header.visible); - - grid.setHeaderVisible(false); - assertFalse(grid.isHeaderVisible()); - assertFalse(state.header.visible); - - grid.setHeaderVisible(true); - assertTrue(grid.isHeaderVisible()); - assertTrue(state.header.visible); - } - - @Test - public void testFooterVisibility() throws Exception { - - assertTrue(grid.isFooterVisible()); - assertTrue(state.footer.visible); - - grid.setFooterVisible(false); - assertFalse(grid.isFooterVisible()); - assertFalse(state.footer.visible); - - grid.setFooterVisible(true); - assertTrue(grid.isFooterVisible()); - assertTrue(state.footer.visible); - } - - @Test - public void testSetFrozenColumnCount() { - assertEquals("Grid should not start with a frozen column", 0, - grid.getFrozenColumnCount()); - grid.setFrozenColumnCount(2); - assertEquals("Freezing two columns should freeze two columns", 2, - grid.getFrozenColumnCount()); - } - - @Test - public void testSetFrozenColumnCountThroughColumn() { - assertEquals("Grid should not start with a frozen column", 0, - grid.getFrozenColumnCount()); - grid.getColumns().get(2).setLastFrozenColumn(); - assertEquals( - "Setting the third column as last frozen should freeze three columns", - 3, grid.getFrozenColumnCount()); - } - - @Test - public void testFrozenColumnRemoveColumn() { - assertEquals("Grid should not start with a frozen column", 0, - grid.getFrozenColumnCount()); - - int containerSize = grid.getContainerDataSource() - .getContainerPropertyIds().size(); - grid.setFrozenColumnCount(containerSize); - - Object propertyId = grid.getContainerDataSource() - .getContainerPropertyIds().iterator().next(); - - grid.getContainerDataSource().removeContainerProperty(propertyId); - assertEquals("Frozen column count should update when removing last row", - containerSize - 1, grid.getFrozenColumnCount()); - } - - @Test - public void testReorderColumns() { - Set containerProperties = new LinkedHashSet( - grid.getContainerDataSource().getContainerPropertyIds()); - Object[] properties = new Object[] { "column3", "column2", "column6" }; - grid.setColumnOrder(properties); - - int i = 0; - // Test sorted columns are first in order - for (Object property : properties) { - containerProperties.remove(property); - assertEquals(columnIdMapper.key(property), - state.columnOrder.get(i++)); - } - - // Test remaining columns are in original order - for (Object property : containerProperties) { - assertEquals(columnIdMapper.key(property), - state.columnOrder.get(i++)); - } - - try { - grid.setColumnOrder("foo", "bar", "baz"); - fail("Grid allowed sorting with non-existent properties"); - } catch (IllegalArgumentException e) { - // All ok - } - } - - @Test(expected = IllegalArgumentException.class) - public void testRemoveColumnThatDoesNotExist() { - grid.removeColumn("banana phone"); - } - - @Test(expected = IllegalStateException.class) - public void testSetNonSortableColumnSortable() { - Column noSortColumn = grid.getColumn("noSort"); - assertFalse("Object property column should not be sortable.", - noSortColumn.isSortable()); - noSortColumn.setSortable(true); - } - - @Test - public void testColumnsEditableByDefault() { - for (Column c : grid.getColumns()) { - assertTrue(c + " should be editable", c.isEditable()); - } - } - - @Test - public void testPropertyAndColumnEditorFieldsMatch() { - Column column1 = grid.getColumn("column1"); - column1.setEditorField(new LegacyTextField()); - assertSame(column1.getEditorField(), - grid.getColumn("column1").getEditorField()); - - Column column2 = grid.getColumn("column2"); - column2.setEditorField(new LegacyTextField()); - assertSame(column2.getEditorField(), column2.getEditorField()); - } - - @Test - public void testUneditableColumnHasNoField() { - Column col = grid.getColumn("column1"); - - col.setEditable(false); - - assertFalse("Column should be uneditable", col.isEditable()); - assertNull("Uneditable column should not be auto-assigned a Field", - col.getEditorField()); - } - - private GridColumnState getColumnState(Object propertyId) { - String columnId = columnIdMapper.key(propertyId); - for (GridColumnState columnState : state.columns) { - if (columnState.id.equals(columnId)) { - return columnState; - } - } - return null; - } - - @Test - public void testAddAndRemoveSortableColumn() { - boolean sortable = grid.getColumn("column1").isSortable(); - grid.removeColumn("column1"); - grid.addColumn("column1"); - assertEquals("Column sortability changed when re-adding", sortable, - grid.getColumn("column1").isSortable()); - } - - @Test - public void testSetColumns() { - grid.setColumns("column7", "column0", "column9"); - Iterator it = grid.getColumns().iterator(); - assertEquals(it.next().getPropertyId(), "column7"); - assertEquals(it.next().getPropertyId(), "column0"); - assertEquals(it.next().getPropertyId(), "column9"); - assertFalse(it.hasNext()); - } - - @Test - public void testAddingColumnsWithSetColumns() { - LegacyGrid g = new LegacyGrid(); - g.setColumns("c1", "c2", "c3"); - Iterator it = g.getColumns().iterator(); - assertEquals(it.next().getPropertyId(), "c1"); - assertEquals(it.next().getPropertyId(), "c2"); - assertEquals(it.next().getPropertyId(), "c3"); - assertFalse(it.hasNext()); - } - - @Test(expected = IllegalStateException.class) - public void testAddingColumnsWithSetColumnsNonDefaultContainer() { - grid.setColumns("column1", "column2", "column50"); - } - - @Test - public void testDefaultColumnHidingToggleCaption() { - Column firstColumn = grid.getColumns().get(0); - firstColumn.setHeaderCaption("headerCaption"); - assertEquals(null, firstColumn.getHidingToggleCaption()); - } - - @Test - public void testOverriddenColumnHidingToggleCaption() { - Column firstColumn = grid.getColumns().get(0); - firstColumn.setHidingToggleCaption("hidingToggleCaption"); - firstColumn.setHeaderCaption("headerCaption"); - assertEquals("hidingToggleCaption", - firstColumn.getHidingToggleCaption()); - } - - @Test - public void testColumnSetWidthFiresResizeEvent() { - final Column firstColumn = grid.getColumns().get(0); - - // prepare a listener mock that captures the argument - ColumnResizeListener mock = EasyMock - .createMock(ColumnResizeListener.class); - Capture capturedEvent = new Capture(); - mock.columnResize( - and(capture(capturedEvent), isA(ColumnResizeEvent.class))); - EasyMock.expectLastCall().once(); - - // Tell it to wait for the call - EasyMock.replay(mock); - - // Cause a resize event - grid.addColumnResizeListener(mock); - firstColumn.setWidth(firstColumn.getWidth() + 10); - - // Verify the method was called - EasyMock.verify(mock); - - // Asserts on the captured event - ColumnResizeEvent event = capturedEvent.getValue(); - assertEquals("Event column was not first column.", firstColumn, - event.getColumn()); - assertFalse("Event should not be userOriginated", - event.isUserOriginated()); - } - - @Test - public void textHeaderCaptionIsReturned() { - Column firstColumn = grid.getColumns().get(0); - - firstColumn.setHeaderCaption("text"); - - assertThat(firstColumn.getHeaderCaption(), is("text")); - } - - @Test - public void defaultCaptionIsReturnedForHtml() { - Column firstColumn = grid.getColumns().get(0); - - grid.getDefaultHeaderRow().getCell("column0").setHtml("html"); - - assertThat(firstColumn.getHeaderCaption(), is("Column0")); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java deleted file mode 100644 index fa6c57df93..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import static org.junit.Assert.assertFalse; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.AbstractInMemoryContainer; -import com.vaadin.ui.LegacyGrid.Column; - -public class GridContainerNotSortableTest { - - final AbstractInMemoryContainer notSortableDataSource = new AbstractInMemoryContainer() { - - private Map> properties = new LinkedHashMap>(); - - { - properties.put("Foo", new Property() { - - @Override - public String getValue() { - return "foo"; - } - - @Override - public void setValue(String newValue) throws ReadOnlyException { - throw new ReadOnlyException(); - } - - @Override - public Class getType() { - return String.class; - } - - @Override - public boolean isReadOnly() { - return true; - } - - @Override - public void setReadOnly(boolean newStatus) { - throw new UnsupportedOperationException(); - } - }); - } - - @Override - public Collection getContainerPropertyIds() { - return properties.keySet(); - } - - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - return properties.get(propertyId); - } - - @Override - public Class getType(Object propertyId) { - return properties.get(propertyId).getType(); - } - - @Override - protected Item getUnfilteredItem(Object itemId) { - return null; - } - }; - - @Test - public void testGridWithNotSortableContainer() { - new LegacyGrid(notSortableDataSource); - } - - @Test(expected = IllegalStateException.class) - public void testNotSortableGridSetColumnSortable() { - LegacyGrid grid = new LegacyGrid(); - grid.setContainerDataSource(notSortableDataSource); - Column column = grid.getColumn("Foo"); - assertFalse("Column should not be sortable initially.", - column.isSortable()); - column.setSortable(true); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java deleted file mode 100644 index 7b07aefcb2..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.OutputStream; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.ui.Component; -import com.vaadin.ui.LegacyGrid.DetailsGenerator; -import com.vaadin.ui.LegacyGrid.RowReference; -import com.vaadin.ui.Label; - -public class GridContainerTest { - - /** - * Null Stream used with serialization tests - */ - protected static OutputStream NULLSTREAM = new OutputStream() { - @Override - public void write(int b) { - } - }; - - @Test - public void testDetailsGeneratorDoesNotResetOnContainerChange() { - LegacyGrid grid = new LegacyGrid(); - DetailsGenerator detGen = new DetailsGenerator() { - - @Override - public Component getDetails(RowReference rowReference) { - return new Label("Empty details"); - } - }; - grid.setDetailsGenerator(detGen); - - grid.setContainerDataSource(createContainer()); - - Assert.assertEquals("DetailsGenerator changed", detGen, - grid.getDetailsGenerator()); - } - - @Test - public void testSetContainerTwice() throws Exception { - - TestGrid grid = new TestGrid(); - - grid.setContainerDataSource(createContainer()); - - // Simulate initial response to ensure "lazy" state changes are done - // before resetting the datasource - grid.beforeClientResponse(true); - grid.getDataProvider().beforeClientResponse(true); - - grid.setContainerDataSource(createContainer()); - } - - @SuppressWarnings("unchecked") - private IndexedContainer createContainer() { - IndexedContainer container = new IndexedContainer(); - container.addContainerProperty("x", String.class, null); - container.addItem(0).getItemProperty("x").setValue("y"); - return container; - } - - @Test - public void setColumnsOrder() { - LegacyGrid grid = new LegacyGrid(); - IndexedContainer ic = new IndexedContainer(); - ic.addContainerProperty("foo", String.class, ""); - ic.addContainerProperty("baz", String.class, ""); - ic.addContainerProperty("bar", String.class, ""); - grid.setContainerDataSource(ic); - grid.setColumns("foo", "baz", "bar"); - - Assert.assertEquals("foo", grid.getColumns().get(0).getPropertyId()); - Assert.assertEquals("baz", grid.getColumns().get(1).getPropertyId()); - Assert.assertEquals("bar", grid.getColumns().get(2).getPropertyId()); - } - - @Test - public void addColumnNotInContainer() { - LegacyGrid grid = new LegacyGrid(); - grid.setContainerDataSource(new IndexedContainer()); - try { - grid.addColumn("notInContainer"); - Assert.fail( - "Adding a property id not in the container should throw an exception"); - } catch (IllegalStateException e) { - Assert.assertTrue(e.getMessage().contains("notInContainer")); - Assert.assertTrue( - e.getMessage().contains("does not exist in the container")); - } - } - - @Test - public void setColumnsForPropertyIdNotInContainer() { - LegacyGrid grid = new LegacyGrid(); - grid.setContainerDataSource(new IndexedContainer()); - try { - grid.setColumns("notInContainer", "notThereEither"); - Assert.fail( - "Setting columns for property ids not in the container should throw an exception"); - } catch (IllegalStateException e) { - // addColumn is run in random order.. - Assert.assertTrue(e.getMessage().contains("notInContainer") - || e.getMessage().contains("notThereEither")); - Assert.assertTrue( - e.getMessage().contains("does not exist in the container")); - } - } - - @Test(expected = IllegalStateException.class) - public void multipleAddColumnsForDefaultContainer() { - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("foo"); - grid.addColumn("foo"); - } - - @Test - public void testSerializeRpcDataProviderWithRowChanges() - throws IOException { - LegacyGrid grid = new LegacyGrid(); - IndexedContainer container = new IndexedContainer(); - grid.setContainerDataSource(container); - container.addItem(); - serializeComponent(grid); - } - - protected void serializeComponent(Component component) throws IOException { - ObjectOutputStream stream = null; - try { - stream = new ObjectOutputStream(NULLSTREAM); - stream.writeObject(component); - } finally { - if (stream != null) { - stream.close(); - } - } - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java deleted file mode 100644 index 15daa264d9..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * 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.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.assertSame; -import static org.junit.Assert.assertTrue; - -import java.lang.reflect.Method; - -import com.vaadin.ui.LegacyGrid; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyTextField; - -import org.easymock.EasyMock; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.fieldgroup.FieldGroup; -import com.vaadin.data.fieldgroup.FieldGroup.CommitException; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.MockVaadinSession; -import com.vaadin.server.VaadinService; -import com.vaadin.server.VaadinSession; - -public class GridEditorTest { - - private static final Object PROPERTY_NAME = "name"; - private static final Object PROPERTY_AGE = "age"; - private static final String DEFAULT_NAME = "Some Valid Name"; - private static final Integer DEFAULT_AGE = 25; - private static final Object ITEM_ID = new Object(); - - // Explicit field for the test session to save it from GC - private VaadinSession session; - - private final LegacyGrid grid = new LegacyGrid(); - private Method doEditMethod; - - @Before - @SuppressWarnings("unchecked") - public void setup() throws SecurityException, NoSuchMethodException { - IndexedContainer container = new IndexedContainer(); - container.addContainerProperty(PROPERTY_NAME, String.class, "[name]"); - container.addContainerProperty(PROPERTY_AGE, Integer.class, - Integer.valueOf(-1)); - - Item item = container.addItem(ITEM_ID); - item.getItemProperty(PROPERTY_NAME).setValue(DEFAULT_NAME); - item.getItemProperty(PROPERTY_AGE).setValue(DEFAULT_AGE); - grid.setContainerDataSource(container); - - // VaadinSession needed for ConverterFactory - VaadinService mockService = EasyMock - .createNiceMock(VaadinService.class); - session = new MockVaadinSession(mockService); - VaadinSession.setCurrent(session); - session.lock(); - - // Access to method for actual editing. - doEditMethod = LegacyGrid.class.getDeclaredMethod("doEditItem"); - doEditMethod.setAccessible(true); - } - - @After - public void tearDown() { - session.unlock(); - session = null; - VaadinSession.setCurrent(null); - } - - @Test - public void testInitAssumptions() throws Exception { - assertFalse(grid.isEditorEnabled()); - assertNull(grid.getEditedItemId()); - assertNotNull(grid.getEditorFieldGroup()); - } - - @Test - public void testSetEnabled() throws Exception { - assertFalse(grid.isEditorEnabled()); - grid.setEditorEnabled(true); - assertTrue(grid.isEditorEnabled()); - } - - @Test - public void testSetDisabled() throws Exception { - assertFalse(grid.isEditorEnabled()); - grid.setEditorEnabled(true); - grid.setEditorEnabled(false); - assertFalse(grid.isEditorEnabled()); - } - - @Test - public void testSetReEnabled() throws Exception { - assertFalse(grid.isEditorEnabled()); - grid.setEditorEnabled(true); - grid.setEditorEnabled(false); - grid.setEditorEnabled(true); - assertTrue(grid.isEditorEnabled()); - } - - @Test - public void testDetached() throws Exception { - FieldGroup oldFieldGroup = grid.getEditorFieldGroup(); - grid.removeAllColumns(); - grid.setContainerDataSource(new IndexedContainer()); - assertFalse(oldFieldGroup == grid.getEditorFieldGroup()); - } - - @Test(expected = IllegalStateException.class) - public void testDisabledEditItem() throws Exception { - grid.editItem(ITEM_ID); - } - - @Test - public void testEditItem() throws Exception { - startEdit(); - assertEquals(ITEM_ID, grid.getEditedItemId()); - assertEquals(getEditedItem(), - grid.getEditorFieldGroup().getItemDataSource()); - - assertEquals(DEFAULT_NAME, - grid.getColumn(PROPERTY_NAME).getEditorField().getValue()); - assertEquals(String.valueOf(DEFAULT_AGE), - grid.getColumn(PROPERTY_AGE).getEditorField().getValue()); - } - - @Test - public void testSaveEditor() throws Exception { - startEdit(); - LegacyTextField field = (LegacyTextField) grid.getColumn(PROPERTY_NAME) - .getEditorField(); - - field.setValue("New Name"); - assertEquals(DEFAULT_NAME, field.getPropertyDataSource().getValue()); - - grid.saveEditor(); - assertTrue(grid.isEditorActive()); - assertFalse(field.isModified()); - assertEquals("New Name", field.getValue()); - assertEquals("New Name", getEditedProperty(PROPERTY_NAME).getValue()); - } - - @Test - public void testSaveEditorCommitFail() throws Exception { - startEdit(); - - ((LegacyTextField) grid.getColumn(PROPERTY_AGE).getEditorField()) - .setValue("Invalid"); - try { - // Manual fail instead of @Test(expected=...) to check it is - // saveEditor that fails and not setValue - grid.saveEditor(); - Assert.fail( - "CommitException expected when saving an invalid field value"); - } catch (CommitException e) { - // expected - } - } - - @Test - public void testCancelEditor() throws Exception { - startEdit(); - LegacyTextField field = (LegacyTextField) grid.getColumn(PROPERTY_NAME) - .getEditorField(); - field.setValue("New Name"); - - Property datasource = field.getPropertyDataSource(); - - grid.cancelEditor(); - assertFalse(grid.isEditorActive()); - assertNull(grid.getEditedItemId()); - assertFalse(field.isModified()); - assertEquals("", field.getValue()); - assertEquals(DEFAULT_NAME, datasource.getValue()); - assertNull(field.getPropertyDataSource()); - assertNull(grid.getEditorFieldGroup().getItemDataSource()); - } - - @Test(expected = IllegalArgumentException.class) - public void testNonexistentEditItem() throws Exception { - grid.setEditorEnabled(true); - grid.editItem(new Object()); - } - - @Test - public void testGetField() throws Exception { - startEdit(); - - assertNotNull(grid.getColumn(PROPERTY_NAME).getEditorField()); - } - - @Test - public void testGetFieldWithoutItem() throws Exception { - grid.setEditorEnabled(true); - assertNotNull(grid.getColumn(PROPERTY_NAME).getEditorField()); - } - - @Test - public void testCustomBinding() { - LegacyTextField textField = new LegacyTextField(); - grid.getColumn(PROPERTY_NAME).setEditorField(textField); - - startEdit(); - - assertSame(textField, grid.getColumn(PROPERTY_NAME).getEditorField()); - } - - @Test(expected = IllegalStateException.class) - public void testDisableWhileEditing() { - startEdit(); - grid.setEditorEnabled(false); - } - - @Test - public void testFieldIsNotReadonly() { - startEdit(); - - LegacyField field = grid.getColumn(PROPERTY_NAME).getEditorField(); - assertFalse(field.isReadOnly()); - } - - @Test - public void testFieldIsReadonlyWhenFieldGroupIsReadonly() { - startEdit(); - - grid.getEditorFieldGroup().setReadOnly(true); - LegacyField field = grid.getColumn(PROPERTY_NAME).getEditorField(); - assertTrue(field.isReadOnly()); - } - - @Test - public void testColumnRemoved() { - LegacyField field = grid.getColumn(PROPERTY_NAME).getEditorField(); - - assertSame("field should be attached to ", grid, field.getParent()); - - grid.removeColumn(PROPERTY_NAME); - - assertNull("field should be detached from ", field.getParent()); - } - - @Test - public void testSetFieldAgain() { - LegacyTextField field = new LegacyTextField(); - grid.getColumn(PROPERTY_NAME).setEditorField(field); - - field = new LegacyTextField(); - grid.getColumn(PROPERTY_NAME).setEditorField(field); - - assertSame("new field should be used.", field, - grid.getColumn(PROPERTY_NAME).getEditorField()); - } - - private void startEdit() { - grid.setEditorEnabled(true); - grid.editItem(ITEM_ID); - // Simulate succesful client response to actually start the editing. - try { - doEditMethod.invoke(grid); - } catch (Exception e) { - Assert.fail("Editing item " + ITEM_ID + " failed. Cause: " - + e.getCause().toString()); - } - } - - private Item getEditedItem() { - assertNotNull(grid.getEditedItemId()); - return grid.getContainerDataSource().getItem(grid.getEditedItemId()); - } - - private Property getEditedProperty(Object propertyId) { - return getEditedItem().getItemProperty(PROPERTY_NAME); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java deleted file mode 100644 index 452d2713a4..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import static org.junit.Assert.assertTrue; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Test; - -import com.vaadin.ui.LegacyGrid.AbstractGridExtension; - -public class GridExtensionTest { - - public static class DummyGridExtension extends AbstractGridExtension { - - public DummyGridExtension(LegacyGrid grid) { - super(grid); - } - } - - @Test - public void testCreateExtension() { - LegacyGrid grid = new LegacyGrid(); - DummyGridExtension dummy = new DummyGridExtension(grid); - assertTrue("DummyGridExtension never made it to Grid", - grid.getExtensions().contains(dummy)); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java deleted file mode 100644 index 7e910d2428..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright 2000-2013 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.tests.server.component.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Collection; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.SelectionEvent; -import com.vaadin.event.SelectionEvent.SelectionListener; -import com.vaadin.ui.LegacyGrid.SelectionMode; -import com.vaadin.ui.LegacyGrid.SelectionModel; - -public class GridSelectionTest { - - private static class MockSelectionChangeListener - implements SelectionListener { - private SelectionEvent event; - - @Override - public void select(final SelectionEvent event) { - this.event = event; - } - - public Collection getAdded() { - return event.getAdded(); - } - - public Collection getRemoved() { - return event.getRemoved(); - } - - public void clearEvent() { - /* - * This method is not strictly needed as the event will simply be - * overridden, but it's good practice, and makes the code more - * obvious. - */ - event = null; - } - - public boolean eventHasHappened() { - return event != null; - } - } - - private LegacyGrid grid; - private MockSelectionChangeListener mockListener; - - private final Object itemId1Present = "itemId1Present"; - private final Object itemId2Present = "itemId2Present"; - - private final Object itemId1NotPresent = "itemId1NotPresent"; - private final Object itemId2NotPresent = "itemId2NotPresent"; - - @Before - public void setup() { - final IndexedContainer container = new IndexedContainer(); - container.addItem(itemId1Present); - container.addItem(itemId2Present); - for (int i = 2; i < 10; i++) { - container.addItem(new Object()); - } - - assertEquals("init size", 10, container.size()); - assertTrue("itemId1Present", container.containsId(itemId1Present)); - assertTrue("itemId2Present", container.containsId(itemId2Present)); - assertFalse("itemId1NotPresent", - container.containsId(itemId1NotPresent)); - assertFalse("itemId2NotPresent", - container.containsId(itemId2NotPresent)); - - grid = new LegacyGrid(container); - - mockListener = new MockSelectionChangeListener(); - grid.addSelectionListener(mockListener); - - assertFalse("eventHasHappened", mockListener.eventHasHappened()); - } - - @Test - public void defaultSelectionModeIsSingle() { - assertTrue(grid.getSelectionModel() instanceof SelectionModel.Single); - } - - @Test(expected = IllegalStateException.class) - public void getSelectedRowThrowsExceptionMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - grid.getSelectedRow(); - } - - @Test(expected = IllegalStateException.class) - public void getSelectedRowThrowsExceptionNone() { - grid.setSelectionMode(SelectionMode.NONE); - grid.getSelectedRow(); - } - - @Test(expected = IllegalStateException.class) - public void selectThrowsExceptionNone() { - grid.setSelectionMode(SelectionMode.NONE); - grid.select(itemId1Present); - } - - @Test(expected = IllegalStateException.class) - public void deselectRowThrowsExceptionNone() { - grid.setSelectionMode(SelectionMode.NONE); - grid.deselect(itemId1Present); - } - - @Test - public void selectionModeMapsToMulti() { - assertTrue(grid.setSelectionMode( - SelectionMode.MULTI) instanceof SelectionModel.Multi); - } - - @Test - public void selectionModeMapsToSingle() { - assertTrue(grid.setSelectionMode( - SelectionMode.SINGLE) instanceof SelectionModel.Single); - } - - @Test - public void selectionModeMapsToNone() { - assertTrue(grid.setSelectionMode( - SelectionMode.NONE) instanceof SelectionModel.None); - } - - @Test(expected = IllegalArgumentException.class) - public void selectionModeNullThrowsException() { - grid.setSelectionMode(null); - } - - @Test - public void noSelectModel_isSelected() { - grid.setSelectionMode(SelectionMode.NONE); - assertFalse("itemId1Present", grid.isSelected(itemId1Present)); - assertFalse("itemId1NotPresent", grid.isSelected(itemId1NotPresent)); - } - - @Test(expected = IllegalStateException.class) - public void noSelectModel_getSelectedRow() { - grid.setSelectionMode(SelectionMode.NONE); - grid.getSelectedRow(); - } - - @Test - public void noSelectModel_getSelectedRows() { - grid.setSelectionMode(SelectionMode.NONE); - assertTrue(grid.getSelectedRows().isEmpty()); - } - - @Test - public void selectionCallsListenerMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - selectionCallsListener(); - } - - @Test - public void selectionCallsListenerSingle() { - grid.setSelectionMode(SelectionMode.SINGLE); - selectionCallsListener(); - } - - private void selectionCallsListener() { - grid.select(itemId1Present); - assertEquals("added size", 1, mockListener.getAdded().size()); - assertEquals("added item", itemId1Present, - mockListener.getAdded().iterator().next()); - assertEquals("removed size", 0, mockListener.getRemoved().size()); - } - - @Test - public void deselectionCallsListenerMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - deselectionCallsListener(); - } - - @Test - public void deselectionCallsListenerSingle() { - grid.setSelectionMode(SelectionMode.SINGLE); - deselectionCallsListener(); - } - - private void deselectionCallsListener() { - grid.select(itemId1Present); - mockListener.clearEvent(); - - grid.deselect(itemId1Present); - assertEquals("removed size", 1, mockListener.getRemoved().size()); - assertEquals("removed item", itemId1Present, - mockListener.getRemoved().iterator().next()); - assertEquals("removed size", 0, mockListener.getAdded().size()); - } - - @Test - public void deselectPresentButNotSelectedItemIdShouldntFireListenerMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - deselectPresentButNotSelectedItemIdShouldntFireListener(); - } - - @Test - public void deselectPresentButNotSelectedItemIdShouldntFireListenerSingle() { - grid.setSelectionMode(SelectionMode.SINGLE); - deselectPresentButNotSelectedItemIdShouldntFireListener(); - } - - private void deselectPresentButNotSelectedItemIdShouldntFireListener() { - grid.deselect(itemId1Present); - assertFalse(mockListener.eventHasHappened()); - } - - @Test - public void deselectNotPresentItemIdShouldNotThrowExceptionMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - grid.deselect(itemId1NotPresent); - } - - @Test - public void deselectNotPresentItemIdShouldNotThrowExceptionSingle() { - grid.setSelectionMode(SelectionMode.SINGLE); - grid.deselect(itemId1NotPresent); - } - - @Test(expected = IllegalArgumentException.class) - public void selectNotPresentItemIdShouldThrowExceptionMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - grid.select(itemId1NotPresent); - } - - @Test(expected = IllegalArgumentException.class) - public void selectNotPresentItemIdShouldThrowExceptionSingle() { - grid.setSelectionMode(SelectionMode.SINGLE); - grid.select(itemId1NotPresent); - } - - @Test - public void selectAllMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - final SelectionModel.Multi select = (SelectionModel.Multi) grid - .getSelectionModel(); - select.selectAll(); - assertEquals("added size", 10, mockListener.getAdded().size()); - assertEquals("removed size", 0, mockListener.getRemoved().size()); - assertTrue("itemId1Present", - mockListener.getAdded().contains(itemId1Present)); - assertTrue("itemId2Present", - mockListener.getAdded().contains(itemId2Present)); - } - - @Test - public void deselectAllMulti() { - grid.setSelectionMode(SelectionMode.MULTI); - final SelectionModel.Multi select = (SelectionModel.Multi) grid - .getSelectionModel(); - select.selectAll(); - mockListener.clearEvent(); - - select.deselectAll(); - assertEquals("removed size", 10, mockListener.getRemoved().size()); - assertEquals("added size", 0, mockListener.getAdded().size()); - assertTrue("itemId1Present", - mockListener.getRemoved().contains(itemId1Present)); - assertTrue("itemId2Present", - mockListener.getRemoved().contains(itemId2Present)); - assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); - } - - @Test - public void gridDeselectAllMultiAllSelected() { - grid.setSelectionMode(SelectionMode.MULTI); - final SelectionModel.Multi select = (SelectionModel.Multi) grid - .getSelectionModel(); - select.selectAll(); - mockListener.clearEvent(); - - assertTrue(grid.deselectAll()); - assertEquals("removed size", 10, mockListener.getRemoved().size()); - assertEquals("added size", 0, mockListener.getAdded().size()); - assertTrue("itemId1Present", - mockListener.getRemoved().contains(itemId1Present)); - assertTrue("itemId2Present", - mockListener.getRemoved().contains(itemId2Present)); - assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); - - } - - @Test - public void gridDeselectAllMultiOneSelected() { - grid.setSelectionMode(SelectionMode.MULTI); - final SelectionModel.Multi select = (SelectionModel.Multi) grid - .getSelectionModel(); - select.select(itemId2Present); - mockListener.clearEvent(); - - assertTrue(grid.deselectAll()); - assertEquals("removed size", 1, mockListener.getRemoved().size()); - assertEquals("added size", 0, mockListener.getAdded().size()); - assertFalse("itemId1Present", - mockListener.getRemoved().contains(itemId1Present)); - assertTrue("itemId2Present", - mockListener.getRemoved().contains(itemId2Present)); - assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); - - } - - @Test - public void gridDeselectAllSingleNoneSelected() { - grid.setSelectionMode(SelectionMode.SINGLE); - assertFalse(grid.deselectAll()); - assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); - } - - @Test - public void gridDeselectAllSingleOneSelected() { - grid.setSelectionMode(SelectionMode.SINGLE); - final SelectionModel.Single select = (SelectionModel.Single) grid - .getSelectionModel(); - select.select(itemId2Present); - mockListener.clearEvent(); - - assertTrue(grid.deselectAll()); - assertEquals("removed size", 1, mockListener.getRemoved().size()); - assertEquals("added size", 0, mockListener.getAdded().size()); - assertFalse("itemId1Present", - mockListener.getRemoved().contains(itemId1Present)); - assertTrue("itemId2Present", - mockListener.getRemoved().contains(itemId2Present)); - assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); - - } - - @Test - public void gridDeselectAllMultiNoneSelected() { - grid.setSelectionMode(SelectionMode.MULTI); - - assertFalse(grid.deselectAll()); - assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); - - } - - @Test - public void reselectionDeselectsPreviousSingle() { - grid.setSelectionMode(SelectionMode.SINGLE); - grid.select(itemId1Present); - mockListener.clearEvent(); - - grid.select(itemId2Present); - assertEquals("added size", 1, mockListener.getAdded().size()); - assertEquals("removed size", 1, mockListener.getRemoved().size()); - assertEquals("added item", itemId2Present, - mockListener.getAdded().iterator().next()); - assertEquals("removed item", itemId1Present, - mockListener.getRemoved().iterator().next()); - assertEquals("selectedRows is correct", itemId2Present, - grid.getSelectedRow()); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java deleted file mode 100644 index 9f5f67d8be..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.ui.LegacyGrid; - -/** - * Tests for Grid State. - * - */ -public class GridStateTest { - - @Test - public void getPrimaryStyleName_gridHasCustomPrimaryStyleName() { - LegacyGrid grid = new LegacyGrid(); - GridState state = new GridState(); - Assert.assertEquals("Unexpected primary style name", - state.primaryStyleName, grid.getPrimaryStyleName()); - } - - @Test - public void gridStateHasCustomPrimaryStyleName() { - GridState state = new GridState(); - Assert.assertEquals("Unexpected primary style name", "v-grid", - state.primaryStyleName); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java deleted file mode 100644 index 7a8209d50b..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.lang.reflect.Method; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.util.IndexedContainer; - -public class GridStaticSectionTest extends LegacyGrid { - - private Indexed dataSource = new IndexedContainer(); - - @Before - public void setUp() { - dataSource.addContainerProperty("firstName", String.class, ""); - dataSource.addContainerProperty("lastName", String.class, ""); - dataSource.addContainerProperty("streetAddress", String.class, ""); - dataSource.addContainerProperty("zipCode", Integer.class, null); - setContainerDataSource(dataSource); - } - - @Test - public void testAddAndRemoveHeaders() { - assertEquals(1, getHeaderRowCount()); - prependHeaderRow(); - assertEquals(2, getHeaderRowCount()); - removeHeaderRow(0); - assertEquals(1, getHeaderRowCount()); - removeHeaderRow(0); - assertEquals(0, getHeaderRowCount()); - assertEquals(null, getDefaultHeaderRow()); - HeaderRow row = appendHeaderRow(); - assertEquals(1, getHeaderRowCount()); - assertEquals(null, getDefaultHeaderRow()); - setDefaultHeaderRow(row); - assertEquals(row, getDefaultHeaderRow()); - } - - @Test - public void testAddAndRemoveFooters() { - // By default there are no footer rows - assertEquals(0, getFooterRowCount()); - FooterRow row = appendFooterRow(); - - assertEquals(1, getFooterRowCount()); - prependFooterRow(); - assertEquals(2, getFooterRowCount()); - assertEquals(row, getFooterRow(1)); - removeFooterRow(0); - assertEquals(1, getFooterRowCount()); - removeFooterRow(0); - assertEquals(0, getFooterRowCount()); - } - - @Test - public void testUnusedPropertyNotInCells() { - removeColumn("firstName"); - assertNull("firstName cell was not removed from existing row", - getDefaultHeaderRow().getCell("firstName")); - HeaderRow newRow = appendHeaderRow(); - assertNull("firstName cell was created when it should not.", - newRow.getCell("firstName")); - addColumn("firstName"); - assertNotNull( - "firstName cell was not created for default row when added again", - getDefaultHeaderRow().getCell("firstName")); - assertNotNull( - "firstName cell was not created for new row when added again", - newRow.getCell("firstName")); - - } - - @Test - public void testJoinHeaderCells() { - HeaderRow mergeRow = prependHeaderRow(); - mergeRow.join("firstName", "lastName").setText("Name"); - mergeRow.join(mergeRow.getCell("streetAddress"), - mergeRow.getCell("zipCode")); - } - - @Test(expected = IllegalStateException.class) - public void testJoinHeaderCellsIncorrectly() throws Throwable { - HeaderRow mergeRow = prependHeaderRow(); - mergeRow.join("firstName", "zipCode").setText("Name"); - sanityCheck(); - } - - @Test - public void testJoinAllFooterCells() { - FooterRow mergeRow = prependFooterRow(); - mergeRow.join(dataSource.getContainerPropertyIds().toArray()) - .setText("All the stuff."); - } - - private void sanityCheck() throws Throwable { - Method sanityCheckHeader; - try { - sanityCheckHeader = LegacyGrid.Header.class - .getDeclaredMethod("sanityCheck"); - sanityCheckHeader.setAccessible(true); - Method sanityCheckFooter = LegacyGrid.Footer.class - .getDeclaredMethod("sanityCheck"); - sanityCheckFooter.setAccessible(true); - sanityCheckHeader.invoke(getHeader()); - sanityCheckFooter.invoke(getFooter()); - } catch (Exception e) { - throw e.getCause(); - } - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java deleted file mode 100644 index 1199486742..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import com.vaadin.ui.LegacyGrid; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.SelectionEvent; -import com.vaadin.event.SelectionEvent.SelectionListener; -import com.vaadin.ui.LegacyGrid.MultiSelectionModel; -import com.vaadin.ui.LegacyGrid.SelectionMode; - -public class MultiSelectionModelTest { - - private Object itemId1Present = "itemId1Present"; - private Object itemId2Present = "itemId2Present"; - private Object itemId3Present = "itemId3Present"; - - private Object itemIdNotPresent = "itemIdNotPresent"; - private Container.Indexed dataSource; - private MultiSelectionModel model; - private LegacyGrid grid; - - private boolean expectingEvent = false; - private boolean expectingDeselectEvent; - private List select = new ArrayList(); - private List deselect = new ArrayList(); - - @Before - public void setUp() { - dataSource = createDataSource(); - grid = new LegacyGrid(dataSource); - grid.setSelectionMode(SelectionMode.MULTI); - model = (MultiSelectionModel) grid.getSelectionModel(); - } - - @After - public void tearDown() { - Assert.assertFalse("Some expected select event did not happen.", - expectingEvent); - Assert.assertFalse("Some expected deselect event did not happen.", - expectingDeselectEvent); - } - - private IndexedContainer createDataSource() { - final IndexedContainer container = new IndexedContainer(); - container.addItem(itemId1Present); - container.addItem(itemId2Present); - container.addItem(itemId3Present); - for (int i = 3; i < 10; i++) { - container.addItem(new Object()); - } - - return container; - } - - @Test - public void testSelectAndDeselectRow() throws Throwable { - try { - expectSelectEvent(itemId1Present); - model.select(itemId1Present); - expectDeselectEvent(itemId1Present); - model.deselect(itemId1Present); - } catch (Exception e) { - throw e.getCause(); - } - - verifyCurrentSelection(); - } - - @Test - public void testAddSelection() throws Throwable { - try { - expectSelectEvent(itemId1Present); - model.select(itemId1Present); - expectSelectEvent(itemId2Present); - model.select(itemId2Present); - } catch (Exception e) { - throw e.getCause(); - } - - verifyCurrentSelection(itemId1Present, itemId2Present); - } - - @Test - public void testSettingSelection() throws Throwable { - try { - expectSelectEvent(itemId2Present, itemId1Present); - model.setSelected(Arrays - .asList(new Object[] { itemId1Present, itemId2Present })); - verifyCurrentSelection(itemId1Present, itemId2Present); - - expectDeselectEvent(itemId1Present); - expectSelectEvent(itemId3Present); - model.setSelected(Arrays - .asList(new Object[] { itemId3Present, itemId2Present })); - verifyCurrentSelection(itemId3Present, itemId2Present); - } catch (Exception e) { - throw e.getCause(); - } - } - - private void expectSelectEvent(Object... selectArray) { - select = Arrays.asList(selectArray); - addListener(); - } - - private void expectDeselectEvent(Object... deselectArray) { - deselect = Arrays.asList(deselectArray); - addListener(); - } - - private void addListener() { - if (expectingEvent) { - return; - } - - expectingEvent = true; - grid.addSelectionListener(new SelectionListener() { - - @Override - public void select(SelectionEvent event) { - Assert.assertTrue("Selection did not contain expected items", - event.getAdded().containsAll(select)); - Assert.assertTrue("Selection contained unexpected items", - select.containsAll(event.getAdded())); - select = new ArrayList(); - - Assert.assertTrue("Deselection did not contain expected items", - event.getRemoved().containsAll(deselect)); - Assert.assertTrue("Deselection contained unexpected items", - deselect.containsAll(event.getRemoved())); - deselect = new ArrayList(); - - grid.removeSelectionListener(this); - expectingEvent = false; - } - }); - } - - private void verifyCurrentSelection(Object... selection) { - final List selected = Arrays.asList(selection); - if (model.getSelectedRows().containsAll(selected) - && selected.containsAll(model.getSelectedRows())) { - return; - } - Assert.fail("Not all items were correctly selected"); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java deleted file mode 100644 index 3183ad9021..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.SelectionEvent; -import com.vaadin.event.SelectionEvent.SelectionListener; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.SelectionMode; -import com.vaadin.ui.LegacyGrid.SingleSelectionModel; - -public class SingleSelectionModelTest { - - private Object itemId1Present = "itemId1Present"; - private Object itemId2Present = "itemId2Present"; - - private Object itemIdNotPresent = "itemIdNotPresent"; - private Container.Indexed dataSource; - private SingleSelectionModel model; - private LegacyGrid grid; - - private boolean expectingEvent = false; - - @Before - public void setUp() { - dataSource = createDataSource(); - grid = new LegacyGrid(dataSource); - grid.setSelectionMode(SelectionMode.SINGLE); - model = (SingleSelectionModel) grid.getSelectionModel(); - } - - @After - public void tearDown() { - Assert.assertFalse("Some expected event did not happen.", - expectingEvent); - } - - private IndexedContainer createDataSource() { - final IndexedContainer container = new IndexedContainer(); - container.addItem(itemId1Present); - container.addItem(itemId2Present); - for (int i = 2; i < 10; i++) { - container.addItem(new Object()); - } - - return container; - } - - @Test - public void testSelectAndDeselctRow() throws Throwable { - try { - expectEvent(itemId1Present, null); - model.select(itemId1Present); - expectEvent(null, itemId1Present); - model.select(null); - } catch (Exception e) { - throw e.getCause(); - } - } - - @Test - public void testSelectAndChangeSelectedRow() throws Throwable { - try { - expectEvent(itemId1Present, null); - model.select(itemId1Present); - expectEvent(itemId2Present, itemId1Present); - model.select(itemId2Present); - } catch (Exception e) { - throw e.getCause(); - } - } - - @Test - public void testRemovingSelectedRowAndThenDeselecting() throws Throwable { - try { - expectEvent(itemId2Present, null); - model.select(itemId2Present); - dataSource.removeItem(itemId2Present); - expectEvent(null, itemId2Present); - model.select(null); - } catch (Exception e) { - throw e.getCause(); - } - } - - @Test - public void testSelectAndReSelectRow() throws Throwable { - try { - expectEvent(itemId1Present, null); - model.select(itemId1Present); - expectEvent(null, null); - // This is no-op. Nothing should happen. - model.select(itemId1Present); - } catch (Exception e) { - throw e.getCause(); - } - Assert.assertTrue("Should still wait for event", expectingEvent); - expectingEvent = false; - } - - @Test(expected = IllegalArgumentException.class) - public void testSelectNonExistentRow() { - model.select(itemIdNotPresent); - } - - private void expectEvent(final Object selected, final Object deselected) { - expectingEvent = true; - grid.addSelectionListener(new SelectionListener() { - - @Override - public void select(SelectionEvent event) { - if (selected != null) { - Assert.assertTrue("Selection did not contain expected item", - event.getAdded().contains(selected)); - } else { - Assert.assertTrue("Unexpected selection", - event.getAdded().isEmpty()); - } - - if (deselected != null) { - Assert.assertTrue( - "DeSelection did not contain expected item", - event.getRemoved().contains(deselected)); - } else { - Assert.assertTrue("Unexpected selection", - event.getRemoved().isEmpty()); - } - - grid.removeSelectionListener(this); - expectingEvent = false; - } - }); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java b/server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java deleted file mode 100644 index 9b2dde4d24..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.tests.server.component.grid; - -import java.lang.reflect.Field; - -import org.easymock.EasyMock; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.communication.data.RpcDataProviderExtension; -import com.vaadin.ui.ConnectorTracker; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.UI; - -/** - * A Grid attached to a mock UI with a mock ConnectorTracker. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class TestGrid extends LegacyGrid { - - public TestGrid() { - super(); - init(); - } - - public TestGrid(IndexedContainer c) { - super(c); - init(); - } - - public RpcDataProviderExtension getDataProvider() throws Exception { - Field dseField = LegacyGrid.class.getDeclaredField("datasourceExtension"); - dseField.setAccessible(true); - return (RpcDataProviderExtension) dseField.get(this); - } - - private void init() { - UI mockUI = EasyMock.createNiceMock(UI.class); - ConnectorTracker mockCT = EasyMock - .createNiceMock(ConnectorTracker.class); - EasyMock.expect(mockUI.getConnectorTracker()).andReturn(mockCT) - .anyTimes(); - EasyMock.replay(mockUI, mockCT); - - setParent(mockUI); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java deleted file mode 100644 index 71d24a2d8e..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.tests.server.component.grid.declarative; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Test; - -public class GridColumnDeclarativeTest extends GridDeclarativeTestBase { - - @Test - public void testSimpleGridColumns() { - String design = ""// - + "" - + " " - + " " - + " " - + " " - + " " - + "" // - + "" // - + "
"; - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class).setWidth(100); - grid.addColumn("Column2", String.class).setMaximumWidth(200) - .setExpandRatio(2).setSortable(false); - grid.addColumn("Column3", String.class).setMinimumWidth(15) - .setExpandRatio(1).setEditable(false).setResizable(false); - grid.addColumn("Column4", String.class).setHidable(true) - .setHidingToggleCaption("col 4").setResizable(true); - grid.addColumn("Column5", String.class).setHidden(true); - - // Remove the default header - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - // Use the read grid component to do another pass on write. - testRead(design, grid, true); - testWrite(design, grid); - } - - @Test - public void testReadColumnsWithoutPropertyId() { - String design = ""// - + "" - + " " - + " " // property-id="property-1" - + " " - + " " // property-id="property-3" - + "" // - + "
"; - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class).setWidth(100); - grid.addColumn("property-1", String.class).setMaximumWidth(200) - .setExpandRatio(2); - grid.addColumn("Column3", String.class).setMinimumWidth(15) - .setExpandRatio(1); - grid.addColumn("property-3", String.class).setHidable(true) - .setHidden(true).setHidingToggleCaption("col 4"); - - testRead(design, grid); - } - - @Test - public void testReadEmptyExpand() { - String design = ""// - + "" + " " - + "" // - + "
"; - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("property-0", String.class).setExpandRatio(1); - - testRead(design, grid); - } - - @Test - public void testReadColumnWithNoAttributes() { - String design = ""// - + "" // - + " " // - + "" // - + "
"; - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("property-0", String.class); - - testRead(design, grid); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java deleted file mode 100644 index 8ca99327dc..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.tests.server.component.grid.declarative; - -import static org.junit.Assert.assertSame; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Test; - -import com.vaadin.shared.ui.grid.HeightMode; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.LegacyGrid.MultiSelectionModel; -import com.vaadin.ui.LegacyGrid.NoSelectionModel; -import com.vaadin.ui.LegacyGrid.SingleSelectionModel; - -/** - * Tests declarative support for {@link Grid} properties. - * - * @since - * @author Vaadin Ltd - */ -public class GridDeclarativeAttributeTest extends DeclarativeTestBase { - - @Test - public void testBasicAttributes() { - - String design = ""; - - LegacyGrid grid = new LegacyGrid(); - grid.setEditorEnabled(true); - grid.setHeightMode(HeightMode.ROW); - grid.setHeightByRows(20); - grid.setFrozenColumnCount(-1); - grid.setEditorSaveCaption("Tallenna"); - grid.setEditorCancelCaption("Peruuta"); - grid.setColumnReorderingAllowed(true); - - testRead(design, grid); - testWrite(design, grid); - } - - @Test - public void testFrozenColumnsAttributes() { - String design = "" // - + "
"; - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("property-0", String.class); - grid.addColumn("property-1", String.class); - grid.addColumn("property-2", String.class); - grid.setFrozenColumnCount(2); - - testRead(design, grid); - } - - @Test - public void testSelectionMode() { - String design = ""; - assertSame(NoSelectionModel.class, - read(design).getSelectionModel().getClass()); - - design = ""; - assertSame(SingleSelectionModel.class, - read(design).getSelectionModel().getClass()); - - design = ""; - assertSame(MultiSelectionModel.class, - read(design).getSelectionModel().getClass()); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java b/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java deleted file mode 100644 index 3e97910be7..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.tests.server.component.grid.declarative; - -import java.util.List; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Assert; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.LegacyGrid.Column; -import com.vaadin.ui.LegacyGrid.FooterCell; -import com.vaadin.ui.LegacyGrid.FooterRow; -import com.vaadin.ui.LegacyGrid.HeaderCell; -import com.vaadin.ui.LegacyGrid.HeaderRow; - -public class GridDeclarativeTestBase extends DeclarativeTestBase { - - @Override - public LegacyGrid testRead(String design, LegacyGrid expected) { - return testRead(design, expected, false); - } - - public LegacyGrid testRead(String design, LegacyGrid expected, boolean retestWrite) { - return testRead(design, expected, retestWrite, false); - } - - public LegacyGrid testRead(String design, LegacyGrid expected, boolean retestWrite, - boolean writeData) { - LegacyGrid actual = super.testRead(design, expected); - - compareGridColumns(expected, actual); - compareHeaders(expected, actual); - compareFooters(expected, actual); - - if (retestWrite) { - testWrite(design, actual, writeData); - } - - return actual; - } - - private void compareHeaders(LegacyGrid expected, LegacyGrid actual) { - Assert.assertEquals("Different header row count", - expected.getHeaderRowCount(), actual.getHeaderRowCount()); - for (int i = 0; i < expected.getHeaderRowCount(); ++i) { - HeaderRow expectedRow = expected.getHeaderRow(i); - HeaderRow actualRow = actual.getHeaderRow(i); - - if (expectedRow.equals(expected.getDefaultHeaderRow())) { - Assert.assertEquals("Different index for default header row", - actual.getDefaultHeaderRow(), actualRow); - } - - for (Column c : expected.getColumns()) { - String baseError = "Difference when comparing cell for " - + c.toString() + " on header row " + i + ": "; - Object propertyId = c.getPropertyId(); - HeaderCell expectedCell = expectedRow.getCell(propertyId); - HeaderCell actualCell = actualRow.getCell(propertyId); - - switch (expectedCell.getCellType()) { - case TEXT: - Assert.assertEquals(baseError + "Text content", - expectedCell.getText(), actualCell.getText()); - break; - case HTML: - Assert.assertEquals(baseError + "HTML content", - expectedCell.getHtml(), actualCell.getHtml()); - break; - case WIDGET: - assertEquals(baseError + "Component content", - expectedCell.getComponent(), - actualCell.getComponent()); - break; - } - } - } - } - - private void compareFooters(LegacyGrid expected, LegacyGrid actual) { - Assert.assertEquals("Different footer row count", - expected.getFooterRowCount(), actual.getFooterRowCount()); - for (int i = 0; i < expected.getFooterRowCount(); ++i) { - FooterRow expectedRow = expected.getFooterRow(i); - FooterRow actualRow = actual.getFooterRow(i); - - for (Column c : expected.getColumns()) { - String baseError = "Difference when comparing cell for " - + c.toString() + " on footer row " + i + ": "; - Object propertyId = c.getPropertyId(); - FooterCell expectedCell = expectedRow.getCell(propertyId); - FooterCell actualCell = actualRow.getCell(propertyId); - - switch (expectedCell.getCellType()) { - case TEXT: - Assert.assertEquals(baseError + "Text content", - expectedCell.getText(), actualCell.getText()); - break; - case HTML: - Assert.assertEquals(baseError + "HTML content", - expectedCell.getHtml(), actualCell.getHtml()); - break; - case WIDGET: - assertEquals(baseError + "Component content", - expectedCell.getComponent(), - actualCell.getComponent()); - break; - } - } - } - } - - private void compareGridColumns(LegacyGrid expected, LegacyGrid actual) { - List columns = expected.getColumns(); - List actualColumns = actual.getColumns(); - Assert.assertEquals("Different amount of columns", columns.size(), - actualColumns.size()); - for (int i = 0; i < columns.size(); ++i) { - Column col1 = columns.get(i); - Column col2 = actualColumns.get(i); - String baseError = "Error when comparing columns for property " - + col1.getPropertyId() + ": "; - assertEquals(baseError + "Property id", col1.getPropertyId(), - col2.getPropertyId()); - assertEquals(baseError + "Width", col1.getWidth(), col2.getWidth()); - assertEquals(baseError + "Maximum width", col1.getMaximumWidth(), - col2.getMaximumWidth()); - assertEquals(baseError + "Minimum width", col1.getMinimumWidth(), - col2.getMinimumWidth()); - assertEquals(baseError + "Expand ratio", col1.getExpandRatio(), - col2.getExpandRatio()); - assertEquals(baseError + "Sortable", col1.isSortable(), - col2.isSortable()); - assertEquals(baseError + "Editable", col1.isEditable(), - col2.isEditable()); - assertEquals(baseError + "Hidable", col1.isHidable(), - col2.isHidable()); - assertEquals(baseError + "Hidden", col1.isHidden(), - col2.isHidden()); - assertEquals(baseError + "HidingToggleCaption", - col1.getHidingToggleCaption(), - col2.getHidingToggleCaption()); - } - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java deleted file mode 100644 index 1207063c16..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * 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.tests.server.component.grid.declarative; - -import com.vaadin.ui.LegacyGrid; -import org.jsoup.nodes.Element; -import org.jsoup.parser.Tag; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.label.ContentMode; -import com.vaadin.ui.LegacyGrid.Column; -import com.vaadin.ui.LegacyGrid.FooterRow; -import com.vaadin.ui.LegacyGrid.HeaderRow; -import com.vaadin.ui.Label; -import com.vaadin.ui.declarative.DesignContext; - -public class GridHeaderFooterDeclarativeTest extends GridDeclarativeTestBase { - - @Test - public void testSingleDefaultHeader() { - //@formatter:off - String design = "" - + "" - + " " - + " " - + " " - + "" - + "" - + " " - + "" - + "
Column1Column2Column3
"; - //@formatter:on - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.addColumn("Column2", String.class); - grid.addColumn("Column3", String.class); - - testWrite(design, grid); - testRead(design, grid, true); - } - - @Test - public void testSingleDefaultHTMLHeader() { - //@formatter:off - String design = "" - + "" - + " " - + " " - + " " + "" - + "" - + " " - + "" - + "
Column1Column2Column3
"; - //@formatter:on - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.addColumn("Column2", String.class); - grid.addColumn("Column3", String.class); - - HeaderRow row = grid.getDefaultHeaderRow(); - for (Column c : grid.getColumns()) { - row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); - } - - testWrite(design, grid); - testRead(design, grid, true); - } - - @Test - public void testNoHeaderRows() { - //@formatter:off - String design = "" - + "" - + " " - + "" - + "" - + "
"; - //@formatter:on - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testWrite(design, grid); - testRead(design, grid, true); - } - - @Test - public void testMultipleHeadersWithColSpans() { - //@formatter:off - String design = "" - + "" - + " " - + " " - + " " - + "" - + "" - + " " - + " " - + " " - + "" - + "
Baz
Column1Column2Column3
FooBar
"; - //@formatter:on - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.addColumn("Column2", String.class); - grid.addColumn("Column3", String.class); - - HeaderRow row = grid.getDefaultHeaderRow(); - for (Column c : grid.getColumns()) { - row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); - } - - grid.prependHeaderRow().join("Column1", "Column2", "Column3") - .setHtml("Baz"); - row = grid.appendHeaderRow(); - row.getCell("Column1").setHtml("Foo"); - row.join("Column2", "Column3").setHtml("Bar"); - - testWrite(design, grid); - testRead(design, grid, true); - } - - @Test - public void testSingleDefaultFooter() { - //@formatter:off - String design = "" - + "" - + " " - + " " - + " " - + "" - + "" // No headers read or written - + "" - + " " - + "" - + "
Column1Column2Column3
"; - //@formatter:on - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.addColumn("Column2", String.class); - grid.addColumn("Column3", String.class); - - FooterRow row = grid.appendFooterRow(); - for (Column c : grid.getColumns()) { - row.getCell(c.getPropertyId()).setText(c.getHeaderCaption()); - } - - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testWrite(design, grid); - testRead(design, grid, true); - } - - @Test - public void testSingleDefaultHTMLFooter() { - //@formatter:off - String design = "" - + "" - + " " - + " " - + " " + "" - + "" // No headers read or written - + "" - + " " - + "" - + "
Column1Column2Column3
"; - //@formatter:on - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.addColumn("Column2", String.class); - grid.addColumn("Column3", String.class); - - FooterRow row = grid.appendFooterRow(); - for (Column c : grid.getColumns()) { - row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); - } - - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testWrite(design, grid); - testRead(design, grid, true); - } - - @Test - public void testMultipleFootersWithColSpans() { - //@formatter:off - String design = "" - + "" - + " " - + " " - + " " - + "" - + "" // No headers read or written. - + "" - + " " - + " " - + " " - + "" - + "
Baz
Column1Column2Column3
FooBar
"; - //@formatter:on - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.addColumn("Column2", String.class); - grid.addColumn("Column3", String.class); - - FooterRow row = grid.appendFooterRow(); - for (Column c : grid.getColumns()) { - row.getCell(c.getPropertyId()).setHtml(c.getHeaderCaption()); - } - - grid.prependFooterRow().join("Column1", "Column2", "Column3") - .setHtml("Baz"); - row = grid.appendFooterRow(); - row.getCell("Column1").setHtml("Foo"); - row.join("Column2", "Column3").setHtml("Bar"); - - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testWrite(design, grid); - testRead(design, grid, true); - } - - @Test - public void testComponentInGridHeader() { - //@formatter:off - String design = "" - + "" - + " " - + "" - + "" - + "" - + "" - + "
Foo
"; - //@formatter:on - Label component = new Label("Foo"); - component.setContentMode(ContentMode.HTML); - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.getDefaultHeaderRow().getCell("Column1").setComponent(component); - - testRead(design, grid, true); - testWrite(design, grid); - } - - @Test - public void testComponentInGridFooter() { - //@formatter:off - String design = "" - + "" - + " " - + "" - + "" // No headers read or written - + "" - + "" - + "" - + "
Foo
"; - //@formatter:on - - Label component = new Label("Foo"); - component.setContentMode(ContentMode.HTML); - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Column1", String.class); - grid.prependFooterRow().getCell("Column1").setComponent(component); - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testRead(design, grid, true); - testWrite(design, grid); - } - - @Test - public void testHtmlEntitiesinGridHeaderFooter() { - //@formatter:off - String design = "" - + "" - + " test\">" - + "" - + "" - + " " - + "" - + "" - + " " - + "" - + "" - + "
> Test
> Test
"; - //@formatter:on - - LegacyGrid grid = read(design); - String actualHeader = grid.getHeaderRow(0).getCell("> test").getText(); - String actualFooter = grid.getFooterRow(0).getCell("> test").getText(); - String expected = "> Test"; - - Assert.assertEquals(expected, actualHeader); - Assert.assertEquals(expected, actualFooter); - - design = design.replace("plain-text=\"true\"", ""); - grid = read(design); - actualHeader = grid.getHeaderRow(0).getCell("> test").getHtml(); - actualFooter = grid.getFooterRow(0).getCell("> test").getHtml(); - expected = "> Test"; - - Assert.assertEquals(expected, actualHeader); - Assert.assertEquals(expected, actualFooter); - - grid = new LegacyGrid(); - grid.setColumns("test"); - HeaderRow header = grid.addHeaderRowAt(0); - FooterRow footer = grid.addFooterRowAt(0); - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - // entities should be encoded when writing back, not interpreted as HTML - header.getCell("test").setText("& Test"); - footer.getCell("test").setText("& Test"); - - Element root = new Element(Tag.valueOf("vaadin-legacy-grid"), ""); - grid.writeDesign(root, new DesignContext()); - - Assert.assertEquals("&amp; Test", - root.getElementsByTag("th").get(0).html()); - Assert.assertEquals("&amp; Test", - root.getElementsByTag("td").get(0).html()); - - header = grid.addHeaderRowAt(0); - footer = grid.addFooterRowAt(0); - - // entities should not be encoded, this is already given as HTML - header.getCell("test").setHtml("& Test"); - footer.getCell("test").setHtml("& Test"); - - root = new Element(Tag.valueOf("vaadin-legacy-grid"), ""); - grid.writeDesign(root, new DesignContext()); - - Assert.assertEquals("& Test", - root.getElementsByTag("th").get(0).html()); - Assert.assertEquals("& Test", - root.getElementsByTag("td").get(0).html()); - - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java deleted file mode 100644 index f5b98824cd..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.tests.server.component.grid.declarative; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container; - -public class GridInlineDataDeclarativeTest extends GridDeclarativeTestBase { - - @Test - public void testSimpleInlineData() { - String design = ""// - + "" + " " - + "" // - + "" // No headers read or written - + "" // - + "" // - + "" // - + "" // - + "" // - + "
Foo
Bar
Baz
"; - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Col1", String.class); - grid.addRow("Foo"); - grid.addRow("Bar"); - grid.addRow("Baz"); - - // Remove default header - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testWrite(design, grid, true); - testRead(design, grid, true, true); - } - - @Test - public void testMultipleColumnsInlineData() { - String design = ""// - + "" + " " - + " " - + " " // - + "" // - + "" // No headers read or written - + "" // - + "" // - + "" // - + "" // - + "
FooBarBaz
MySummerCar
"; - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Col1", String.class); - grid.addColumn("Col2", String.class); - grid.addColumn("Col3", String.class); - grid.addRow("Foo", "Bar", "Baz"); - grid.addRow("My", "Summer", "Car"); - - // Remove default header - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testWrite(design, grid, true); - testRead(design, grid, true, true); - } - - @Test - public void testMultipleColumnsInlineDataReordered() { - String design = ""// - + "" + " " - + " " - + " " // - + "" // - + "" // No headers read or written - + "" // - + "" // - + "" // - + "" // - + "
BarBazFoo
SummerCarMy
"; - - LegacyGrid grid = new LegacyGrid(); - grid.addColumn("Col1", String.class); - grid.addColumn("Col2", String.class); - grid.addColumn("Col3", String.class); - grid.addRow("Foo", "Bar", "Baz"); - grid.addRow("My", "Summer", "Car"); - grid.setColumnOrder("Col2", "Col3", "Col1"); - - // Remove default header - grid.removeHeaderRow(grid.getDefaultHeaderRow()); - - testWrite(design, grid, true); - testRead(design, grid, true, true); - } - - @Test - public void testHtmlEntities() { - String design = ""// - + "" + " " + "" // - + "" // No headers read or written - + "" // - + " " + "" - + "
&Test
"; - - LegacyGrid read = read(design); - Container cds = read.getContainerDataSource(); - Assert.assertEquals("&Test", - cds.getItem(cds.getItemIds().iterator().next()) - .getItemProperty("test").getValue()); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java deleted file mode 100644 index b55b0815f8..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.tests.server.component.grid.declarative; - -import com.vaadin.ui.LegacyGrid; -import org.junit.Test; - -import com.vaadin.ui.declarative.DesignException; - -public class GridStructureDeclarativeTest extends GridDeclarativeTestBase { - - @Test - public void testReadEmptyGrid() { - String design = ""; - testRead(design, new LegacyGrid(), false); - } - - @Test - public void testEmptyGrid() { - String design = ""; - LegacyGrid expected = new LegacyGrid(); - testWrite(design, expected); - testRead(design, expected, true); - } - - @Test(expected = DesignException.class) - public void testMalformedGrid() { - String design = ""; - testRead(design, new LegacyGrid()); - } - - @Test(expected = DesignException.class) - public void testGridWithNoColGroup() { - String design = "
Foo
"; - testRead(design, new LegacyGrid()); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java deleted file mode 100644 index beb774528f..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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.tests.server.component.grid.sort; - -import java.util.Arrays; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.sort.Sort; -import com.vaadin.data.sort.SortOrder; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.SortEvent; -import com.vaadin.event.SortEvent.SortListener; -import com.vaadin.shared.data.sort.SortDirection; -import com.vaadin.ui.LegacyGrid; - -public class SortTest { - - class DummySortingIndexedContainer extends IndexedContainer { - - private Object[] expectedProperties; - private boolean[] expectedAscending; - private boolean sorted = true; - - @Override - public void sort(Object[] propertyId, boolean[] ascending) { - Assert.assertEquals( - "Different amount of expected and actual properties,", - expectedProperties.length, propertyId.length); - Assert.assertEquals( - "Different amount of expected and actual directions", - expectedAscending.length, ascending.length); - for (int i = 0; i < propertyId.length; ++i) { - Assert.assertEquals("Sorting properties differ", - expectedProperties[i], propertyId[i]); - Assert.assertEquals("Sorting directions differ", - expectedAscending[i], ascending[i]); - } - sorted = true; - } - - public void expectedSort(Object[] properties, - SortDirection[] directions) { - assert directions.length == properties.length : "Array dimensions differ"; - expectedProperties = properties; - expectedAscending = new boolean[directions.length]; - for (int i = 0; i < directions.length; ++i) { - expectedAscending[i] = (directions[i] == SortDirection.ASCENDING); - } - sorted = false; - } - - public boolean isSorted() { - return sorted; - } - } - - class RegisteringSortChangeListener implements SortListener { - private List order; - - @Override - public void sort(SortEvent event) { - assert order == null : "The same listener was notified multipe times without checking"; - - order = event.getSortOrder(); - } - - public void assertEventFired(SortOrder... expectedOrder) { - Assert.assertEquals(Arrays.asList(expectedOrder), order); - - // Reset for nest test - order = null; - } - - } - - private DummySortingIndexedContainer container; - private RegisteringSortChangeListener listener; - private LegacyGrid grid; - - @Before - public void setUp() { - container = createContainer(); - container.expectedSort(new Object[] {}, new SortDirection[] {}); - - listener = new RegisteringSortChangeListener(); - - grid = new LegacyGrid(container); - grid.addSortListener(listener); - } - - @After - public void tearDown() { - Assert.assertTrue("Container was not sorted after the test.", - container.isSorted()); - } - - @Test(expected = IllegalArgumentException.class) - public void testInvalidSortDirection() { - Sort.by("foo", null); - } - - @Test(expected = IllegalStateException.class) - public void testSortOneColumnMultipleTimes() { - Sort.by("foo").then("bar").then("foo"); - } - - @Test(expected = IllegalArgumentException.class) - public void testSortingByUnexistingProperty() { - grid.sort("foobar"); - } - - @Test(expected = IllegalArgumentException.class) - public void testSortingByUnsortableProperty() { - container.addContainerProperty("foobar", Object.class, null); - grid.sort("foobar"); - } - - @Test - public void testGridDirectSortAscending() { - container.expectedSort(new Object[] { "foo" }, - new SortDirection[] { SortDirection.ASCENDING }); - grid.sort("foo"); - - listener.assertEventFired( - new SortOrder("foo", SortDirection.ASCENDING)); - } - - @Test - public void testGridDirectSortDescending() { - container.expectedSort(new Object[] { "foo" }, - new SortDirection[] { SortDirection.DESCENDING }); - grid.sort("foo", SortDirection.DESCENDING); - - listener.assertEventFired( - new SortOrder("foo", SortDirection.DESCENDING)); - } - - @Test - public void testGridSortBy() { - container.expectedSort(new Object[] { "foo", "bar", "baz" }, - new SortDirection[] { SortDirection.ASCENDING, - SortDirection.ASCENDING, SortDirection.DESCENDING }); - grid.sort(Sort.by("foo").then("bar").then("baz", - SortDirection.DESCENDING)); - - listener.assertEventFired(new SortOrder("foo", SortDirection.ASCENDING), - new SortOrder("bar", SortDirection.ASCENDING), - new SortOrder("baz", SortDirection.DESCENDING)); - - } - - @Test - public void testChangeContainerAfterSorting() { - class Person { - } - - container.expectedSort(new Object[] { "foo", "bar", "baz" }, - new SortDirection[] { SortDirection.ASCENDING, - SortDirection.ASCENDING, SortDirection.DESCENDING }); - grid.sort(Sort.by("foo").then("bar").then("baz", - SortDirection.DESCENDING)); - - listener.assertEventFired(new SortOrder("foo", SortDirection.ASCENDING), - new SortOrder("bar", SortDirection.ASCENDING), - new SortOrder("baz", SortDirection.DESCENDING)); - - container = new DummySortingIndexedContainer(); - container.addContainerProperty("foo", Person.class, null); - container.addContainerProperty("baz", String.class, ""); - container.addContainerProperty("bar", Person.class, null); - container.expectedSort(new Object[] { "baz" }, - new SortDirection[] { SortDirection.DESCENDING }); - grid.setContainerDataSource(container); - - listener.assertEventFired( - new SortOrder("baz", SortDirection.DESCENDING)); - - } - - private DummySortingIndexedContainer createContainer() { - DummySortingIndexedContainer container = new DummySortingIndexedContainer(); - container.addContainerProperty("foo", Integer.class, 0); - container.addContainerProperty("bar", Integer.class, 0); - container.addContainerProperty("baz", Integer.class, 0); - return container; - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java b/server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java deleted file mode 100644 index 822a2353ac..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.tests.server.renderer; - -import static org.junit.Assert.assertEquals; - -import java.io.File; - -import org.easymock.EasyMock; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.server.ClassResource; -import com.vaadin.server.ExternalResource; -import com.vaadin.server.FileResource; -import com.vaadin.server.FontAwesome; -import com.vaadin.server.ThemeResource; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.UI; -import com.vaadin.ui.renderers.ImageRenderer; - -import elemental.json.JsonObject; -import elemental.json.JsonValue; - -public class ImageRendererTest { - - private ImageRenderer renderer; - - @Before - public void setUp() { - UI mockUI = EasyMock.createNiceMock(UI.class); - EasyMock.replay(mockUI); - - LegacyGrid grid = new LegacyGrid(); - grid.setParent(mockUI); - - renderer = new ImageRenderer(); - renderer.setParent(grid); - } - - @Test - public void testThemeResource() { - JsonValue v = renderer.encode(new ThemeResource("foo.png")); - assertEquals("theme://foo.png", getUrl(v)); - } - - @Test - public void testExternalResource() { - JsonValue v = renderer - .encode(new ExternalResource("http://example.com/foo.png")); - assertEquals("http://example.com/foo.png", getUrl(v)); - } - - @Test(expected = IllegalArgumentException.class) - public void testFileResource() { - renderer.encode(new FileResource(new File("/tmp/foo.png"))); - } - - @Test(expected = IllegalArgumentException.class) - public void testClassResource() { - renderer.encode(new ClassResource("img/foo.png")); - } - - @Test(expected = IllegalArgumentException.class) - public void testFontIcon() { - renderer.encode(FontAwesome.AMBULANCE); - } - - private String getUrl(JsonValue v) { - return ((JsonObject) v).get("uRL").asString(); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java b/server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java deleted file mode 100644 index 5eec5b6ea8..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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.tests.server.renderer; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; - -import java.util.Date; -import java.util.Locale; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.VaadinSession; -import com.vaadin.tests.server.component.grid.TestGrid; -import com.vaadin.tests.util.AlwaysLockedVaadinSession; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.AbstractRenderer; -import com.vaadin.ui.LegacyGrid.Column; -import com.vaadin.ui.renderers.ButtonRenderer; -import com.vaadin.ui.renderers.DateRenderer; -import com.vaadin.ui.renderers.HtmlRenderer; -import com.vaadin.ui.renderers.NumberRenderer; -import com.vaadin.ui.renderers.TextRenderer; -import com.vaadin.v7.data.util.converter.LegacyConverter; -import com.vaadin.v7.data.util.converter.LegacyStringToIntegerConverter; - -import elemental.json.JsonValue; - -public class RendererTest { - - private static class TestBean { - int i = 42; - - @Override - public String toString() { - return "TestBean [" + i + "]"; - } - } - - private static class ExtendedBean extends TestBean { - float f = 3.14f; - } - - private static class TestRenderer extends TextRenderer { - @Override - public JsonValue encode(String value) { - return super.encode("renderer(" + value + ")"); - } - } - - private static class TestConverter - implements LegacyConverter { - - @Override - public TestBean convertToModel(String value, - Class targetType, Locale locale) - throws ConversionException { - return null; - } - - @Override - public String convertToPresentation(TestBean value, - Class targetType, Locale locale) - throws ConversionException { - if (value instanceof ExtendedBean) { - return "ExtendedBean(" + value.i + ", " - + ((ExtendedBean) value).f + ")"; - } else { - return "TestBean(" + value.i + ")"; - } - } - - @Override - public Class getModelType() { - return TestBean.class; - } - - @Override - public Class getPresentationType() { - return String.class; - } - } - - private LegacyGrid grid; - - private Column intColumn; - private Column textColumn; - private Column beanColumn; - private Column htmlColumn; - private Column numberColumn; - private Column dateColumn; - private Column extendedBeanColumn; - private Column buttonColumn; - - @Before - @SuppressWarnings("unchecked") - public void setUp() { - VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); - - IndexedContainer c = new IndexedContainer(); - - c.addContainerProperty("int", Integer.class, 0); - c.addContainerProperty("text", String.class, ""); - c.addContainerProperty("html", String.class, ""); - c.addContainerProperty("number", Number.class, null); - c.addContainerProperty("date", Date.class, null); - c.addContainerProperty("bean", TestBean.class, null); - c.addContainerProperty("button", String.class, null); - c.addContainerProperty("extendedBean", ExtendedBean.class, null); - - Object id = c.addItem(); - Item item = c.getItem(id); - item.getItemProperty("int").setValue(123); - item.getItemProperty("text").setValue("321"); - item.getItemProperty("html").setValue("html"); - item.getItemProperty("number").setValue(3.14); - item.getItemProperty("date").setValue(new Date(123456789)); - item.getItemProperty("bean").setValue(new TestBean()); - item.getItemProperty("extendedBean").setValue(new ExtendedBean()); - - grid = new TestGrid(c); - - intColumn = grid.getColumn("int"); - textColumn = grid.getColumn("text"); - htmlColumn = grid.getColumn("html"); - numberColumn = grid.getColumn("number"); - dateColumn = grid.getColumn("date"); - beanColumn = grid.getColumn("bean"); - extendedBeanColumn = grid.getColumn("extendedBean"); - buttonColumn = grid.getColumn("button"); - - } - - @Test - public void testDefaultRendererAndConverter() throws Exception { - assertSame(TextRenderer.class, intColumn.getRenderer().getClass()); - assertSame(LegacyStringToIntegerConverter.class, - intColumn.getConverter().getClass()); - - assertSame(TextRenderer.class, textColumn.getRenderer().getClass()); - // String->String; converter not needed - assertNull(textColumn.getConverter()); - - assertSame(TextRenderer.class, beanColumn.getRenderer().getClass()); - // MyBean->String; converter not found - assertNull(beanColumn.getConverter()); - } - - @Test - public void testFindCompatibleConverter() throws Exception { - intColumn.setRenderer(renderer()); - assertSame(LegacyStringToIntegerConverter.class, - intColumn.getConverter().getClass()); - - textColumn.setRenderer(renderer()); - assertNull(textColumn.getConverter()); - } - - @Test(expected = IllegalArgumentException.class) - public void testCannotFindConverter() { - beanColumn.setRenderer(renderer()); - } - - @Test - public void testExplicitConverter() throws Exception { - beanColumn.setRenderer(renderer(), converter()); - extendedBeanColumn.setRenderer(renderer(), converter()); - } - - @Test - public void testEncoding() throws Exception { - assertEquals("42", render(intColumn, 42).asString()); - intColumn.setRenderer(renderer()); - assertEquals("renderer(42)", render(intColumn, 42).asString()); - - assertEquals("2.72", render(textColumn, "2.72").asString()); - textColumn.setRenderer(new TestRenderer()); - assertEquals("renderer(2.72)", render(textColumn, "2.72").asString()); - } - - @Test - public void testEncodingWithoutConverter() throws Exception { - assertEquals("TestBean [42]", - render(beanColumn, new TestBean()).asString()); - } - - @Test - public void testBeanEncoding() throws Exception { - beanColumn.setRenderer(renderer(), converter()); - extendedBeanColumn.setRenderer(renderer(), converter()); - - assertEquals("renderer(TestBean(42))", - render(beanColumn, new TestBean()).asString()); - assertEquals("renderer(ExtendedBean(42, 3.14))", - render(beanColumn, new ExtendedBean()).asString()); - - assertEquals("renderer(ExtendedBean(42, 3.14))", - render(extendedBeanColumn, new ExtendedBean()).asString()); - } - - @Test - public void testNullEncoding() { - - textColumn.setRenderer(new TextRenderer()); - htmlColumn.setRenderer(new HtmlRenderer()); - numberColumn.setRenderer(new NumberRenderer()); - dateColumn.setRenderer(new DateRenderer()); - buttonColumn.setRenderer(new ButtonRenderer()); - - assertEquals("", textColumn.getRenderer().encode(null).asString()); - assertEquals("", htmlColumn.getRenderer().encode(null).asString()); - assertEquals("", numberColumn.getRenderer().encode(null).asString()); - assertEquals("", dateColumn.getRenderer().encode(null).asString()); - assertEquals("", buttonColumn.getRenderer().encode(null).asString()); - } - - @Test - public void testNullEncodingWithDefault() { - - textColumn.setRenderer(new TextRenderer("default value")); - htmlColumn.setRenderer(new HtmlRenderer("default value")); - numberColumn.setRenderer( - new NumberRenderer("%s", Locale.getDefault(), "default value")); - dateColumn.setRenderer(new DateRenderer("%s", "default value")); - buttonColumn.setRenderer(new ButtonRenderer("default value")); - - assertEquals("default value", - textColumn.getRenderer().encode(null).asString()); - assertEquals("default value", - htmlColumn.getRenderer().encode(null).asString()); - assertEquals("default value", - numberColumn.getRenderer().encode(null).asString()); - assertEquals("default value", - dateColumn.getRenderer().encode(null).asString()); - assertEquals("default value", - buttonColumn.getRenderer().encode(null).asString()); - } - - private TestConverter converter() { - return new TestConverter(); - } - - private TestRenderer renderer() { - return new TestRenderer(); - } - - private JsonValue render(Column column, Object value) { - return AbstractRenderer.encodeValue(value, column.getRenderer(), - column.getConverter(), grid.getLocale()); - } -} diff --git a/server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java b/server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java deleted file mode 100644 index a8e6238d4b..0000000000 --- a/server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.vaadin.tests.server.validation; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.fieldgroup.BeanFieldGroup; -import com.vaadin.tests.data.bean.BeanToValidate; -import com.vaadin.v7.data.Validator.InvalidValueException; -import com.vaadin.v7.data.validator.LegacyBeanValidator; -import com.vaadin.v7.ui.LegacyField; - -public class BeanValidationTest { - @Test(expected = InvalidValueException.class) - public void testBeanValidationNull() { - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "firstname"); - validator.validate(null); - } - - @Test(expected = InvalidValueException.class) - public void testBeanValidationStringTooShort() { - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "firstname"); - validator.validate("aa"); - } - - @Test - public void testBeanValidationStringOk() { - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "firstname"); - validator.validate("aaa"); - } - - @Test(expected = InvalidValueException.class) - public void testBeanValidationIntegerTooSmall() { - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "age"); - validator.validate(17); - } - - @Test - public void testBeanValidationIntegerOk() { - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "age"); - validator.validate(18); - } - - @Test(expected = InvalidValueException.class) - public void testBeanValidationTooManyDigits() { - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "decimals"); - validator.validate("1234.567"); - } - - @Test - public void testBeanValidationDigitsOk() { - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "decimals"); - validator.validate("123.45"); - } - - @Test - public void testBeanValidationException_OneValidationError() { - InvalidValueException[] causes = null; - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "lastname"); - try { - validator.validate(null); - } catch (InvalidValueException e) { - causes = e.getCauses(); - } - - Assert.assertEquals(1, causes.length); - } - - @Test - public void testBeanValidationsException_TwoValidationErrors() { - InvalidValueException[] causes = null; - LegacyBeanValidator validator = new LegacyBeanValidator( - BeanToValidate.class, "nickname"); - try { - validator.validate("A"); - } catch (InvalidValueException e) { - causes = e.getCauses(); - } - - Assert.assertEquals(2, causes.length); - } - - @Test - public void testBeanValidationNotAddedTwice() { - // See ticket #11045 - BeanFieldGroup fieldGroup = new BeanFieldGroup( - BeanToValidate.class); - - BeanToValidate beanToValidate = new BeanToValidate(); - beanToValidate.setFirstname("a"); - fieldGroup.setItemDataSource(beanToValidate); - - LegacyField nameField = fieldGroup.buildAndBind("firstname"); - Assert.assertEquals(1, nameField.getValidators().size()); - - try { - nameField.validate(); - } catch (InvalidValueException e) { - // The 1 cause is from BeanValidator, where it tells what failed - // 1 validation exception never gets wrapped. - Assert.assertEquals(1, e.getCauses().length); - } - - // Create new, identical bean to cause duplicate validator unless #11045 - // is fixed - beanToValidate = new BeanToValidate(); - beanToValidate.setFirstname("a"); - fieldGroup.setItemDataSource(beanToValidate); - - Assert.assertEquals(1, nameField.getValidators().size()); - - try { - nameField.validate(); - } catch (InvalidValueException e) { - // The 1 cause is from BeanValidator, where it tells what failed - // 1 validation exception never gets wrapped. - Assert.assertEquals(1, e.getCauses().length); - } - - } - -} diff --git a/server/src/test/java/com/vaadin/ui/TableTest.java b/server/src/test/java/com/vaadin/ui/TableTest.java deleted file mode 100644 index f09313f685..0000000000 --- a/server/src/test/java/com/vaadin/ui/TableTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.ui; - -import java.util.Collection; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.BeanItemContainerGenerator; - -public class TableTest { - - Table table; - - @Before - public void init() { - table = new Table(); - } - - @Test - public void initiallyEmpty() { - Assert.assertTrue(table.isEmpty()); - } - - @Test - public void emptyAfterClearSingleSelect() { - table.setContainerDataSource( - BeanItemContainerGenerator.createContainer(100)); - Assert.assertTrue(table.isEmpty()); - Object first = table.getContainerDataSource().getItemIds().iterator() - .next(); - table.setValue(first); - Assert.assertEquals(first, table.getValue()); - Assert.assertFalse(table.isEmpty()); - table.clear(); - Assert.assertEquals(null, table.getValue()); - Assert.assertTrue(table.isEmpty()); - } - - @Test - public void emptyAfterClearMultiSelect() { - table.setMultiSelect(true); - table.setContainerDataSource( - BeanItemContainerGenerator.createContainer(100)); - - Assert.assertTrue(table.isEmpty()); - Assert.assertArrayEquals(new Object[] {}, - ((Collection) table.getValue()).toArray()); - - Object first = table.getContainerDataSource().getItemIds().iterator() - .next(); - table.select(first); - Assert.assertArrayEquals(new Object[] { first }, - ((Collection) table.getValue()).toArray()); - Assert.assertFalse(table.isEmpty()); - - table.clear(); - Assert.assertArrayEquals(new Object[] {}, - ((Collection) table.getValue()).toArray()); - Assert.assertTrue(table.isEmpty()); - } - -} diff --git a/uitest/ivy.xml b/uitest/ivy.xml index c926e4105a..2a7bbff47f 100644 --- a/uitest/ivy.xml +++ b/uitest/ivy.xml @@ -39,15 +39,29 @@ + + + + + + + + + + + diff --git a/uitest/src/main/java/com/vaadin/tests/components/UnknownComponentConnector.java b/uitest/src/main/java/com/vaadin/tests/components/UnknownComponentConnector.java deleted file mode 100644 index 11f4255719..0000000000 --- a/uitest/src/main/java/com/vaadin/tests/components/UnknownComponentConnector.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.tests.components; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.ui.AbstractComponent; - -public class UnknownComponentConnector extends AbstractTestUI { - - public static class ComponentWithoutConnector extends AbstractComponent { - - } - - @Override - protected void setup(VaadinRequest request) { - ComponentWithoutConnector component = new ComponentWithoutConnector(); - component.setId("no-connector-component"); - addComponent(component); - } - - @Override - protected String getTestDescription() { - // TODO Auto-generated method stub - return null; - } - - @Override - protected Integer getTicketNumber() { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/uitest/src/main/java/com/vaadin/tests/components/ui/ComponentMissingFromDefaultWidgetset.java b/uitest/src/main/java/com/vaadin/tests/components/ui/ComponentMissingFromDefaultWidgetset.java index 554a461c37..bf243a53e2 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/ui/ComponentMissingFromDefaultWidgetset.java +++ b/uitest/src/main/java/com/vaadin/tests/components/ui/ComponentMissingFromDefaultWidgetset.java @@ -1,9 +1,11 @@ package com.vaadin.tests.components.ui; +import com.vaadin.annotations.Widgetset; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.tests.widgetset.server.MissingFromDefaultWidgetsetComponent; +@Widgetset("com.vaadin.DefaultWidgetSet") public class ComponentMissingFromDefaultWidgetset extends AbstractTestUI { @Override diff --git a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java index 86e6108a4a..42c8040600 100644 --- a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java +++ b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java @@ -15,14 +15,14 @@ */ package com.vaadin.tests.widgetset.client.grid; -import com.vaadin.client.connectors.AbstractRendererConnector; +import com.vaadin.client.connectors.AbstractGridRendererConnector; import com.vaadin.client.renderers.Renderer; import com.vaadin.client.widget.grid.RendererCellReference; import com.vaadin.shared.ui.Connect; @Connect(com.vaadin.tests.components.grid.IntArrayRenderer.class) public class IntArrayRendererConnector - extends AbstractRendererConnector { + extends AbstractGridRendererConnector { public static class IntArrayRenderer implements Renderer { private static final String JOINER = " :: "; diff --git a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/PojoRendererConnector.java b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/PojoRendererConnector.java index 82ba236fce..eef728e130 100644 --- a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/PojoRendererConnector.java +++ b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/PojoRendererConnector.java @@ -15,7 +15,7 @@ */ package com.vaadin.tests.widgetset.client.grid; -import com.vaadin.client.connectors.AbstractRendererConnector; +import com.vaadin.client.connectors.AbstractGridRendererConnector; import com.vaadin.client.renderers.Renderer; import com.vaadin.client.widget.grid.RendererCellReference; import com.vaadin.shared.ui.Connect; @@ -23,7 +23,7 @@ import com.vaadin.tests.widgetset.client.SimpleTestBean; @Connect(com.vaadin.tests.components.grid.BeanRenderer.class) public class PojoRendererConnector - extends AbstractRendererConnector { + extends AbstractGridRendererConnector { public static class BeanRenderer implements Renderer { @Override diff --git a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/RowAwareRendererConnector.java b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/RowAwareRendererConnector.java index 8b1c00d71c..444d4f7b26 100644 --- a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/RowAwareRendererConnector.java +++ b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/RowAwareRendererConnector.java @@ -22,7 +22,7 @@ import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.user.client.DOM; -import com.vaadin.client.connectors.AbstractRendererConnector; +import com.vaadin.client.connectors.AbstractGridRendererConnector; import com.vaadin.client.renderers.ComplexRenderer; import com.vaadin.client.renderers.Renderer; import com.vaadin.client.widget.grid.CellReference; @@ -33,7 +33,7 @@ import com.vaadin.shared.ui.Connect; import elemental.json.JsonObject; @Connect(com.vaadin.tests.components.grid.RowAwareRenderer.class) -public class RowAwareRendererConnector extends AbstractRendererConnector { +public class RowAwareRendererConnector extends AbstractGridRendererConnector { public interface RowAwareRendererRpc extends ServerRpc { void clicky(String key); } diff --git a/uitest/src/main/resources/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml b/uitest/src/main/resources/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml index 3878e85193..53fa88e704 100644 --- a/uitest/src/main/resources/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml +++ b/uitest/src/main/resources/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml @@ -1,8 +1,7 @@ - - + diff --git a/uitest/src/main/webapp/WEB-INF/web.xml b/uitest/src/main/webapp/WEB-INF/web.xml index 0def4001b8..c8b8c9caec 100644 --- a/uitest/src/main/webapp/WEB-INF/web.xml +++ b/uitest/src/main/webapp/WEB-INF/web.xml @@ -12,6 +12,10 @@ productionMode false + + widgetset + com.vaadin.Vaadin7WidgetSet + resourceCacheTime @@ -63,10 +67,6 @@ VaadinApplicationRunner com.vaadin.launcher.ApplicationRunnerServlet - - legacyPropertyToString - false - heartbeatInterval 301 diff --git a/uitest/src/test/java/com/vaadin/tests/application/DeploymentConfigurationTest.java b/uitest/src/test/java/com/vaadin/tests/application/DeploymentConfigurationTest.java index c1c2707943..499b828fbc 100644 --- a/uitest/src/test/java/com/vaadin/tests/application/DeploymentConfigurationTest.java +++ b/uitest/src/test/java/com/vaadin/tests/application/DeploymentConfigurationTest.java @@ -24,15 +24,15 @@ import java.util.List; import org.junit.Test; import com.vaadin.testbench.elements.LabelElement; -import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.tb3.SingleBrowserTest; -public class DeploymentConfigurationTest extends MultiBrowserTest { +public class DeploymentConfigurationTest extends SingleBrowserTest { @Test public void testParameters() { openTestURL(); List texts = new ArrayList(Arrays.asList( - "Init parameters:", "legacyPropertyToString: false", + "Init parameters:", "widgetset: com.vaadin.Vaadin7WidgetSet", "closeIdleSessions: true", "productionMode: false", "testParam: 42", "heartbeatInterval: 301", "resourceCacheTime: 3601")); diff --git a/uitest/src/test/java/com/vaadin/tests/components/UnknownComponentConnectorTest.java b/uitest/src/test/java/com/vaadin/tests/components/UnknownComponentConnectorTest.java deleted file mode 100644 index c72e219910..0000000000 --- a/uitest/src/test/java/com/vaadin/tests/components/UnknownComponentConnectorTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.tests.components; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.openqa.selenium.WebElement; - -import com.vaadin.tests.tb3.MultiBrowserTest; - -/** - * Tests that a user is notified about a missing component from the widgetset - */ -public class UnknownComponentConnectorTest extends MultiBrowserTest { - - @Test - public void testConnectorNotFoundInWidgetset() throws Exception { - openTestURL(); - WebElement component = vaadinElementById("no-connector-component"); - assertTrue(component.getText().startsWith( - "Widgetset 'com.vaadin.DefaultWidgetSet' does not contain " - + "implementation for com.vaadin.tests.components.UnknownComponentConnector." - + "ComponentWithoutConnector.")); - } -} -- cgit v1.2.3