From c6b44ac8adc9b2ffd6290c98643a633f405dd6c6 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Thu, 18 Aug 2016 23:19:41 +0300 Subject: Move and rename server classes which go into the compatibility package * Use com.vaadin.v7 * Use the same class name as in Vaadin 7 * Use a "vaadin7-" declarative prefix for Vaadin 7 components Change-Id: I19a27f3835b18980b91a4f8f9464b2adde1a5fd5 --- .../legacy/ui/LegacyCustomFieldConnector.java | 4 +- .../v7/ui/checkbox/LegacyCheckBoxConnector.java | 4 +- .../LegacyPasswordFieldConnector.java | 4 +- .../v7/ui/textfield/LegacyTextFieldConnector.java | 4 +- .../client/connectors/ButtonRendererConnector.java | 2 +- .../client/connectors/DateRendererConnector.java | 2 +- .../DetailComponentManagerConnector.java | 2 +- .../vaadin/client/connectors/GridConnector.java | 3 +- .../client/connectors/ImageRendererConnector.java | 2 +- .../connectors/JavaScriptRendererConnector.java | 2 +- .../connectors/MultiSelectionModelConnector.java | 2 +- .../connectors/NoSelectionModelConnector.java | 2 +- .../client/connectors/NumberRendererConnector.java | 2 +- .../connectors/ProgressBarRendererConnector.java | 2 +- .../client/connectors/RpcDataSourceConnector.java | 2 +- .../connectors/SingleSelectionModelConnector.java | 2 +- .../client/connectors/TextRendererConnector.java | 2 +- .../connectors/UnsafeHtmlRendererConnector.java | 2 +- .../java/com/vaadin/client/ui/VFilterSelect.java | 2 +- .../client/ui/calendar/CalendarConnector.java | 2 +- .../ui/colorpicker/ColorPickerAreaConnector.java | 4 +- .../ui/colorpicker/ColorPickerConnector.java | 4 +- .../colorpicker/ColorPickerGradientConnector.java | 4 +- .../ui/colorpicker/ColorPickerGridConnector.java | 4 +- .../client/ui/combobox/ComboBoxConnector.java | 2 +- .../java/com/vaadin/client/ui/dd/VIsOverId.java | 2 +- .../java/com/vaadin/client/ui/dd/VItemIdIs.java | 2 +- .../client/ui/listselect/ListSelectConnector.java | 2 +- .../ui/nativeselect/NativeSelectConnector.java | 2 +- .../ui/optiongroup/OptionGroupConnector.java | 2 +- .../ui/richtextarea/RichTextAreaConnector.java | 2 +- .../com/vaadin/client/ui/table/TableConnector.java | 2 +- .../ui/table/VTableLazyInitItemIdentifiers.java | 2 +- .../client/ui/textarea/TextAreaConnector.java | 2 +- .../com/vaadin/client/ui/tree/TreeConnector.java | 2 +- .../vaadin/client/ui/tree/VTargetInSubtree.java | 2 +- .../ui/tree/VTreeLazyInitItemIdentifiers.java | 2 +- .../client/ui/treetable/TreeTableConnector.java | 2 +- .../ui/twincolselect/TwinColSelectConnector.java | 2 +- .../client/v7/ui/LegacyDateFieldConnector.java | 4 +- .../v7/ui/LegacyInlineDateFieldConnector.java | 4 +- .../v7/ui/LegacyPopupDateFieldConnector.java | 4 +- .../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 | 62 - .../vaadin/data/util/AbstractBeanContainer.java | 929 --- .../com/vaadin/data/util/AbstractContainer.java | 307 - .../data/util/AbstractInMemoryContainer.java | 1165 ---- .../java/com/vaadin/data/util/BeanContainer.java | 179 - .../main/java/com/vaadin/data/util/BeanItem.java | 264 - .../com/vaadin/data/util/BeanItemContainer.java | 253 - .../data/util/ContainerHierarchicalWrapper.java | 865 --- .../com/vaadin/data/util/FilesystemContainer.java | 924 --- .../data/util/GeneratedPropertyContainer.java | 776 --- .../vaadin/data/util/HierarchicalContainer.java | 860 --- .../com/vaadin/data/util/IndexedContainer.java | 1201 ---- .../data/util/sqlcontainer/CacheFlushNotifier.java | 103 - .../vaadin/data/util/sqlcontainer/CacheMap.java | 43 - .../data/util/sqlcontainer/ColumnProperty.java | 357 - .../util/sqlcontainer/OptimisticLockException.java | 50 - .../data/util/sqlcontainer/ReadOnlyRowId.java | 48 - .../vaadin/data/util/sqlcontainer/Reference.java | 68 - .../com/vaadin/data/util/sqlcontainer/RowId.java | 78 - .../com/vaadin/data/util/sqlcontainer/RowItem.java | 145 - .../data/util/sqlcontainer/SQLContainer.java | 1875 ----- .../com/vaadin/data/util/sqlcontainer/SQLUtil.java | 51 - .../data/util/sqlcontainer/TemporaryRowId.java | 44 - .../connection/J2EEConnectionPool.java | 84 - .../connection/JDBCConnectionPool.java | 53 - .../connection/SimpleJDBCConnectionPool.java | 181 - .../query/AbstractTransactionalQuery.java | 185 - .../util/sqlcontainer/query/FreeformQuery.java | 495 -- .../sqlcontainer/query/FreeformQueryDelegate.java | 130 - .../query/FreeformStatementDelegate.java | 69 - .../data/util/sqlcontainer/query/OrderBy.java | 58 - .../util/sqlcontainer/query/QueryDelegate.java | 239 - .../data/util/sqlcontainer/query/TableQuery.java | 869 --- .../query/generator/DefaultSQLGenerator.java | 395 -- .../query/generator/MSSQLGenerator.java | 115 - .../query/generator/OracleGenerator.java | 127 - .../sqlcontainer/query/generator/SQLGenerator.java | 100 - .../query/generator/StatementHelper.java | 179 - .../query/generator/filter/AndTranslator.java | 35 - .../query/generator/filter/BetweenTranslator.java | 37 - .../query/generator/filter/CompareTranslator.java | 50 - .../query/generator/filter/FilterTranslator.java | 28 - .../query/generator/filter/IsNullTranslator.java | 34 - .../query/generator/filter/LikeTranslator.java | 42 - .../query/generator/filter/NotTranslator.java | 41 - .../query/generator/filter/OrTranslator.java | 35 - .../query/generator/filter/QueryBuilder.java | 110 - .../generator/filter/SimpleStringTranslator.java | 42 - .../query/generator/filter/StringDecorator.java | 70 - .../server/communication/data/DataGenerator.java | 58 - .../data/RpcDataProviderExtension.java | 632 -- .../java/com/vaadin/ui/AbstractColorPicker.java | 588 -- .../main/java/com/vaadin/ui/AbstractSelect.java | 2353 ------- .../src/main/java/com/vaadin/ui/Calendar.java | 2029 ------ .../src/main/java/com/vaadin/ui/ColorPicker.java | 67 - .../main/java/com/vaadin/ui/ColorPickerArea.java | 77 - .../src/main/java/com/vaadin/ui/ComboBox.java | 925 --- .../java/com/vaadin/ui/DefaultFieldFactory.java | 114 - .../src/main/java/com/vaadin/ui/LegacyGrid.java | 7352 ------------------- .../src/main/java/com/vaadin/ui/ListSelect.java | 80 - .../src/main/java/com/vaadin/ui/NativeSelect.java | 108 - .../src/main/java/com/vaadin/ui/OptionGroup.java | 253 - .../src/main/java/com/vaadin/ui/RichTextArea.java | 318 - .../src/main/java/com/vaadin/ui/Select.java | 60 - .../src/main/java/com/vaadin/ui/Table.java | 6533 ----------------- .../main/java/com/vaadin/ui/TableFieldFactory.java | 56 - .../src/main/java/com/vaadin/ui/TextArea.java | 171 - .../src/main/java/com/vaadin/ui/Tree.java | 1984 ------ .../src/main/java/com/vaadin/ui/TreeTable.java | 985 --- .../src/main/java/com/vaadin/ui/TwinColSelect.java | 169 - .../calendar/CalendarComponentEvent.java | 51 - .../calendar/CalendarComponentEvents.java | 603 -- .../ui/components/calendar/CalendarDateRange.java | 97 - .../components/calendar/CalendarTargetDetails.java | 80 - .../calendar/ContainerEventProvider.java | 566 -- .../ui/components/calendar/event/BasicEvent.java | 265 - .../calendar/event/BasicEventProvider.java | 177 - .../event/CalendarEditableEventProvider.java | 42 - .../components/calendar/event/CalendarEvent.java | 147 - .../calendar/event/CalendarEventProvider.java | 112 - .../calendar/event/EditableCalendarEvent.java | 91 - .../calendar/handler/BasicBackwardHandler.java | 96 - .../calendar/handler/BasicDateClickHandler.java | 70 - .../calendar/handler/BasicEventMoveHandler.java | 74 - .../calendar/handler/BasicEventResizeHandler.java | 70 - .../calendar/handler/BasicForwardHandler.java | 94 - .../calendar/handler/BasicWeekClickHandler.java | 82 - .../components/colorpicker/ColorChangeEvent.java | 43 - .../colorpicker/ColorChangeListener.java | 42 - .../colorpicker/ColorPickerGradient.java | 144 - .../ui/components/colorpicker/ColorPickerGrid.java | 258 - .../components/colorpicker/ColorPickerHistory.java | 217 - .../components/colorpicker/ColorPickerPopup.java | 759 -- .../components/colorpicker/ColorPickerPreview.java | 198 - .../components/colorpicker/ColorPickerSelect.java | 235 - .../ui/components/colorpicker/ColorSelector.java | 43 - .../colorpicker/HasColorChangeListener.java | 36 - .../ui/renderers/AbstractJavaScriptRenderer.java | 175 - .../com/vaadin/ui/renderers/ButtonRenderer.java | 74 - .../com/vaadin/ui/renderers/ClickableRenderer.java | 143 - .../java/com/vaadin/ui/renderers/DateRenderer.java | 240 - .../java/com/vaadin/ui/renderers/HtmlRenderer.java | 48 - .../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 | 49 - .../vaadin/v7/data/fieldgroup/BeanFieldGroup.java | 272 + .../com/vaadin/v7/data/fieldgroup/Caption.java | 27 + .../fieldgroup/DefaultFieldGroupFieldFactory.java | 251 + .../com/vaadin/v7/data/fieldgroup/FieldGroup.java | 1258 ++++ .../v7/data/fieldgroup/FieldGroupFieldFactory.java | 43 + .../com/vaadin/v7/data/fieldgroup/PropertyId.java | 62 + .../vaadin/v7/data/util/AbstractBeanContainer.java | 929 +++ .../com/vaadin/v7/data/util/AbstractContainer.java | 307 + .../v7/data/util/AbstractInMemoryContainer.java | 1165 ++++ .../com/vaadin/v7/data/util/BeanContainer.java | 179 + .../java/com/vaadin/v7/data/util/BeanItem.java | 266 + .../com/vaadin/v7/data/util/BeanItemContainer.java | 253 + .../v7/data/util/ContainerHierarchicalWrapper.java | 865 +++ .../vaadin/v7/data/util/FilesystemContainer.java | 924 +++ .../v7/data/util/GeneratedPropertyContainer.java | 776 +++ .../vaadin/v7/data/util/HierarchicalContainer.java | 860 +++ .../com/vaadin/v7/data/util/IndexedContainer.java | 1201 ++++ .../data/util/sqlcontainer/CacheFlushNotifier.java | 103 + .../vaadin/v7/data/util/sqlcontainer/CacheMap.java | 43 + .../v7/data/util/sqlcontainer/ColumnProperty.java | 357 + .../util/sqlcontainer/OptimisticLockException.java | 50 + .../v7/data/util/sqlcontainer/ReadOnlyRowId.java | 48 + .../v7/data/util/sqlcontainer/Reference.java | 68 + .../vaadin/v7/data/util/sqlcontainer/RowId.java | 78 + .../vaadin/v7/data/util/sqlcontainer/RowItem.java | 145 + .../v7/data/util/sqlcontainer/SQLContainer.java | 1875 +++++ .../vaadin/v7/data/util/sqlcontainer/SQLUtil.java | 51 + .../v7/data/util/sqlcontainer/TemporaryRowId.java | 44 + .../connection/J2EEConnectionPool.java | 84 + .../connection/JDBCConnectionPool.java | 53 + .../connection/SimpleJDBCConnectionPool.java | 181 + .../query/AbstractTransactionalQuery.java | 185 + .../util/sqlcontainer/query/FreeformQuery.java | 495 ++ .../sqlcontainer/query/FreeformQueryDelegate.java | 130 + .../query/FreeformStatementDelegate.java | 69 + .../v7/data/util/sqlcontainer/query/OrderBy.java | 58 + .../util/sqlcontainer/query/QueryDelegate.java | 239 + .../data/util/sqlcontainer/query/TableQuery.java | 869 +++ .../query/generator/DefaultSQLGenerator.java | 395 ++ .../query/generator/MSSQLGenerator.java | 115 + .../query/generator/OracleGenerator.java | 127 + .../sqlcontainer/query/generator/SQLGenerator.java | 100 + .../query/generator/StatementHelper.java | 179 + .../query/generator/filter/AndTranslator.java | 35 + .../query/generator/filter/BetweenTranslator.java | 37 + .../query/generator/filter/CompareTranslator.java | 50 + .../query/generator/filter/FilterTranslator.java | 28 + .../query/generator/filter/IsNullTranslator.java | 34 + .../query/generator/filter/LikeTranslator.java | 42 + .../query/generator/filter/NotTranslator.java | 41 + .../query/generator/filter/OrTranslator.java | 35 + .../query/generator/filter/QueryBuilder.java | 110 + .../generator/filter/SimpleStringTranslator.java | 42 + .../query/generator/filter/StringDecorator.java | 70 + .../v7/data/validator/AbstractStringValidator.java | 54 + .../v7/data/validator/AbstractValidator.java | 149 + .../vaadin/v7/data/validator/BeanValidator.java | 184 + .../data/validator/BigDecimalRangeValidator.java | 51 + .../data/validator/BigIntegerRangeValidator.java | 51 + .../v7/data/validator/ByteRangeValidator.java | 47 + .../v7/data/validator/CompositeValidator.java | 270 + .../v7/data/validator/DateRangeValidator.java | 61 + .../v7/data/validator/DoubleRangeValidator.java | 47 + .../vaadin/v7/data/validator/DoubleValidator.java | 73 + .../vaadin/v7/data/validator/EmailValidator.java | 49 + .../v7/data/validator/FloatRangeValidator.java | 47 + .../v7/data/validator/IntegerRangeValidator.java | 47 + .../vaadin/v7/data/validator/IntegerValidator.java | 73 + .../validator/LegacyAbstractStringValidator.java | 54 - .../v7/data/validator/LegacyAbstractValidator.java | 149 - .../v7/data/validator/LegacyBeanValidator.java | 184 - .../validator/LegacyBigDecimalRangeValidator.java | 51 - .../validator/LegacyBigIntegerRangeValidator.java | 51 - .../data/validator/LegacyByteRangeValidator.java | 47 - .../data/validator/LegacyCompositeValidator.java | 270 - .../data/validator/LegacyDateRangeValidator.java | 61 - .../data/validator/LegacyDoubleRangeValidator.java | 47 - .../v7/data/validator/LegacyDoubleValidator.java | 73 - .../v7/data/validator/LegacyEmailValidator.java | 49 - .../data/validator/LegacyFloatRangeValidator.java | 47 - .../validator/LegacyIntegerRangeValidator.java | 47 - .../v7/data/validator/LegacyIntegerValidator.java | 73 - .../data/validator/LegacyLongRangeValidator.java | 47 - .../v7/data/validator/LegacyNullValidator.java | 102 - .../v7/data/validator/LegacyRangeValidator.java | 198 - .../v7/data/validator/LegacyRegexpValidator.java | 116 - .../data/validator/LegacyShortRangeValidator.java | 47 - .../validator/LegacyStringLengthValidator.java | 149 - .../v7/data/validator/LongRangeValidator.java | 47 + .../vaadin/v7/data/validator/NullValidator.java | 102 + .../vaadin/v7/data/validator/RangeValidator.java | 198 + .../vaadin/v7/data/validator/RegexpValidator.java | 116 + .../v7/data/validator/ShortRangeValidator.java | 47 + .../v7/data/validator/StringLengthValidator.java | 149 + .../server/communication/data/DataGenerator.java | 58 + .../data/RpcDataProviderExtension.java | 632 ++ .../java/com/vaadin/v7/ui/AbstractColorPicker.java | 590 ++ .../main/java/com/vaadin/v7/ui/AbstractSelect.java | 2355 +++++++ .../src/main/java/com/vaadin/v7/ui/Calendar.java | 2031 ++++++ .../main/java/com/vaadin/v7/ui/ColorPicker.java | 67 + .../java/com/vaadin/v7/ui/ColorPickerArea.java | 77 + .../src/main/java/com/vaadin/v7/ui/ComboBox.java | 926 +++ .../src/main/java/com/vaadin/v7/ui/DateField.java | 1010 +++ .../java/com/vaadin/v7/ui/DefaultFieldFactory.java | 111 + .../src/main/java/com/vaadin/v7/ui/Grid.java | 7355 ++++++++++++++++++++ .../java/com/vaadin/v7/ui/InlineDateField.java | 57 + .../java/com/vaadin/v7/ui/LegacyDateField.java | 1010 --- .../com/vaadin/v7/ui/LegacyInlineDateField.java | 57 - .../com/vaadin/v7/ui/LegacyPopupDateField.java | 147 - .../src/main/java/com/vaadin/v7/ui/ListSelect.java | 80 + .../main/java/com/vaadin/v7/ui/NativeSelect.java | 108 + .../main/java/com/vaadin/v7/ui/OptionGroup.java | 253 + .../main/java/com/vaadin/v7/ui/PopupDateField.java | 147 + .../main/java/com/vaadin/v7/ui/RichTextArea.java | 317 + .../src/main/java/com/vaadin/v7/ui/Select.java | 60 + .../src/main/java/com/vaadin/v7/ui/Table.java | 6536 +++++++++++++++++ .../java/com/vaadin/v7/ui/TableFieldFactory.java | 56 + .../src/main/java/com/vaadin/v7/ui/TextArea.java | 170 + .../src/main/java/com/vaadin/v7/ui/Tree.java | 1985 ++++++ .../src/main/java/com/vaadin/v7/ui/TreeTable.java | 985 +++ .../main/java/com/vaadin/v7/ui/TwinColSelect.java | 169 + .../calendar/CalendarComponentEvent.java | 51 + .../calendar/CalendarComponentEvents.java | 603 ++ .../ui/components/calendar/CalendarDateRange.java | 97 + .../components/calendar/CalendarTargetDetails.java | 80 + .../calendar/ContainerEventProvider.java | 566 ++ .../ui/components/calendar/event/BasicEvent.java | 265 + .../calendar/event/BasicEventProvider.java | 177 + .../event/CalendarEditableEventProvider.java | 42 + .../components/calendar/event/CalendarEvent.java | 147 + .../calendar/event/CalendarEventProvider.java | 112 + .../calendar/event/EditableCalendarEvent.java | 91 + .../calendar/handler/BasicBackwardHandler.java | 96 + .../calendar/handler/BasicDateClickHandler.java | 70 + .../calendar/handler/BasicEventMoveHandler.java | 74 + .../calendar/handler/BasicEventResizeHandler.java | 70 + .../calendar/handler/BasicForwardHandler.java | 94 + .../calendar/handler/BasicWeekClickHandler.java | 82 + .../components/colorpicker/ColorChangeEvent.java | 43 + .../colorpicker/ColorChangeListener.java | 42 + .../colorpicker/ColorPickerGradient.java | 144 + .../ui/components/colorpicker/ColorPickerGrid.java | 258 + .../components/colorpicker/ColorPickerHistory.java | 217 + .../components/colorpicker/ColorPickerPopup.java | 759 ++ .../components/colorpicker/ColorPickerPreview.java | 198 + .../components/colorpicker/ColorPickerSelect.java | 235 + .../ui/components/colorpicker/ColorSelector.java | 43 + .../colorpicker/HasColorChangeListener.java | 36 + .../ui/renderers/AbstractJavaScriptRenderer.java | 175 + .../com/vaadin/v7/ui/renderers/ButtonRenderer.java | 74 + .../vaadin/v7/ui/renderers/ClickableRenderer.java | 143 + .../com/vaadin/v7/ui/renderers/DateRenderer.java | 240 + .../com/vaadin/v7/ui/renderers/HtmlRenderer.java | 48 + .../com/vaadin/v7/ui/renderers/ImageRenderer.java | 68 + .../com/vaadin/v7/ui/renderers/NumberRenderer.java | 207 + .../v7/ui/renderers/ProgressBarRenderer.java | 46 + .../java/com/vaadin/v7/ui/renderers/Renderer.java | 69 + .../com/vaadin/v7/ui/renderers/TextRenderer.java | 49 + .../vaadin/data/fieldgroup/BeanFieldGroupTest.java | 70 - .../DefaultFieldGroupFieldFactoryTest.java | 124 - .../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 - .../data/util/filter/AbstractFilterTestBase.java | 97 - .../vaadin/data/util/filter/AndOrFilterTest.java | 246 - .../data/util/filter/CompareFilterDateTest.java | 142 - .../vaadin/data/util/filter/CompareFilterTest.java | 322 - .../vaadin/data/util/filter/IsNullFilterTest.java | 61 - .../vaadin/data/util/filter/LikeFilterTest.java | 48 - .../com/vaadin/data/util/filter/NotFilterTest.java | 54 - .../data/util/filter/SimpleStringFilterTest.java | 139 - .../vaadin/data/util/sqlcontainer/AllTests.java | 24 - .../data/util/sqlcontainer/ColumnPropertyTest.java | 318 - .../data/util/sqlcontainer/DataGenerator.java | 132 - .../data/util/sqlcontainer/FreeformQueryUtil.java | 66 - .../data/util/sqlcontainer/ReadOnlyRowIdTest.java | 55 - .../vaadin/data/util/sqlcontainer/RowIdTest.java | 60 - .../sqlcontainer/SQLContainerTableQueryTest.java | 1353 ---- .../data/util/sqlcontainer/SQLContainerTest.java | 2469 ------- .../data/util/sqlcontainer/SQLTestsConstants.java | 154 - .../vaadin/data/util/sqlcontainer/TicketTest.java | 195 - .../vaadin/data/util/sqlcontainer/UtilTest.java | 50 - .../connection/J2EEConnectionPoolTest.java | 107 - .../connection/MockInitialContextFactory.java | 25 - .../connection/SimpleJDBCConnectionPoolTest.java | 185 - .../util/sqlcontainer/filters/BetweenTest.java | 182 - .../util/sqlcontainer/filters/CompareTest.java | 44 - .../data/util/sqlcontainer/filters/LikeTest.java | 229 - .../sqlcontainer/generator/SQLGeneratorsTest.java | 239 - .../generator/StatementHelperTest.java | 64 - .../util/sqlcontainer/query/FreeformQueryTest.java | 1005 --- .../util/sqlcontainer/query/QueryBuilderTest.java | 315 - .../util/sqlcontainer/query/TableQueryTest.java | 746 -- .../query/ValidatingSimpleJDBCConnectionPool.java | 88 - .../validator/BigDecimalRangeValidatorTest.java | 60 - .../validator/BigIntegerRangeValidatorTest.java | 60 - .../data/validator/ByteRangeValidatorTest.java | 59 - .../data/validator/CompositeValidatorTest.java | 124 - .../data/validator/DateRangeValidatorTest.java | 106 - .../data/validator/DoubleRangeValidatorTest.java | 52 - .../tests/data/validator/EmailValidatorTest.java | 31 - .../data/validator/FloatRangeValidatorTest.java | 54 - .../data/validator/IntegerRangeValidatorTest.java | 52 - .../data/validator/LongRangeValidatorTest.java | 52 - .../tests/data/validator/NullValidatorTest.java | 47 - .../tests/data/validator/RegexpValidatorTest.java | 53 - .../data/validator/ShortRangeValidatorTest.java | 60 - .../data/validator/StringLengthValidatorTest.java | 77 - .../design/ParseAllSupportedComponentsTest.java | 45 - .../vaadin/tests/design/ParseLegacyPrefixTest.java | 44 - .../vaadin/tests/design/WriteLegacyDesignTest.java | 99 - .../server/AbstractBeanContainerListenersTest.java | 16 - .../server/AbstractContainerListenersTest.java | 26 - .../AbstractInMemoryContainerListenersTest.java | 14 - .../tests/server/ContextClickListenerTest.java | 163 - .../server/IndexedContainerListenersTest.java | 26 - .../com/vaadin/tests/server/SerializationTest.java | 135 - .../LegacyAbstractFieldListenersTest.java | 27 - .../AbstractSelectDeclarativeTest.java | 306 - .../AbstractSelectListenersTest.java | 26 - .../abstractselect/AbstractSelectStateTest.java | 60 - .../abstractselect/OptionGroupDeclarativeTest.java | 160 - .../component/abstractselect/OptionGroupTest.java | 32 - .../component/calendar/CalendarBasicsTest.java | 290 - .../calendar/CalendarDeclarativeTest.java | 62 - .../calendar/ContainerDataSourceTest.java | 396 -- .../calendar/ContainerEventProviderTest.java | 88 - .../AbstractColorPickerDeclarativeTest.java | 87 - .../colorpicker/ColorConversionsTest.java | 57 - .../combobox/ComboBoxDeclarativeTest.java | 88 - .../component/combobox/ComboBoxStateTest.java | 59 - .../datefield/DateFieldConverterTest.java | 79 - .../datefield/LegacyDateFieldDeclarativeTest.java | 113 - .../LegacyPopupDateFieldDeclarativeTest.java | 73 - .../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 | 295 - .../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 | 102 - .../declarative/GridDeclarativeAttributeTest.java | 85 - .../grid/declarative/GridDeclarativeTestBase.java | 159 - .../GridHeaderFooterDeclarativeTest.java | 356 - .../declarative/GridInlineDataDeclarativeTest.java | 124 - .../declarative/GridStructureDeclarativeTest.java | 50 - .../tests/server/component/grid/sort/SortTest.java | 205 - .../listselect/ListSelectDeclarativeTest.java | 71 - .../component/listselect/ListSelectStateTest.java | 45 - .../nativeselect/NativeSelectDeclarativeTest.java | 69 - .../optiongroup/OptionGroupListenersTest.java | 25 - .../optiongroup/OptionGroupStateTest.java | 60 - .../richtextarea/RichTextAreaDeclarativeTest.java | 70 - .../richtextarea/RichTextAreaStateTest.java | 59 - .../component/select/SelectListenersTest.java | 25 - .../table/CacheUpdateExceptionCausesTest.java | 55 - .../tests/server/component/table/FooterTest.java | 103 - .../component/table/MultipleSelectionTest.java | 62 - .../component/table/TableColumnAlignmentsTest.java | 143 - .../component/table/TableContextClickTest.java | 57 - .../component/table/TableDeclarativeTest.java | 179 - .../component/table/TableDeclarativeTestBase.java | 74 - .../server/component/table/TableGeneratorTest.java | 42 - .../server/component/table/TableListenersTest.java | 49 - .../table/TablePropertyValueConverterTest.java | 384 - .../component/table/TableSelectableTest.java | 75 - .../component/table/TableSerializationTest.java | 26 - .../server/component/table/TableStateTest.java | 60 - .../component/table/TableVisibleColumnsTest.java | 72 - .../textarea/TextAreaDeclarativeTest.java | 74 - .../TextFieldWithConverterAndValidatorTest.java | 49 - .../TextFieldWithPropertyFormatterTest.java | 111 - .../textfield/TextFieldWithValidatorTest.java | 179 - .../tests/server/component/tree/ListenersTest.java | 143 - .../server/component/tree/TreeDeclarativeTest.java | 81 - .../server/component/tree/TreeListenersTest.java | 33 - .../tests/server/component/tree/TreeTest.java | 178 - .../component/treetable/EmptyTreeTableTest.java | 17 - .../treetable/TreeTableDeclarativeTest.java | 156 - .../treetable/TreeTableSetContainerNullTest.java | 16 - .../server/component/treetable/TreeTableTest.java | 102 - .../TwinColSelectDeclarativeTest.java | 74 - .../twincolselect/TwinColSelectStateTest.java | 60 - .../server/components/ComboBoxValueChangeTest.java | 46 - .../tests/server/renderer/ImageRendererTest.java | 85 - .../vaadin/tests/server/renderer/RendererTest.java | 268 - .../server/validation/BeanValidationTest.java | 129 - .../server/validation/RangeValidatorTest.java | 63 - .../server/validation/ReadOnlyValidationTest.java | 17 - .../src/test/java/com/vaadin/ui/AbsSelectTest.java | 147 - .../test/java/com/vaadin/ui/NativeSelectTest.java | 70 - .../test/java/com/vaadin/ui/RichTextAreaTest.java | 47 - .../src/test/java/com/vaadin/ui/TableTest.java | 78 - .../src/test/java/com/vaadin/ui/TextAreaTest.java | 47 - .../v7/data/fieldgroup/BeanFieldGroupTest.java | 70 + .../DefaultFieldGroupFieldFactoryTest.java | 124 + .../v7/data/fieldgroup/FieldGroupDateTest.java | 97 + .../data/fieldgroup/FieldGroupExceptionTest.java | 33 + .../vaadin/v7/data/fieldgroup/FieldGroupTest.java | 96 + .../data/util/AbstractBeanContainerTestBase.java | 77 + .../v7/data/util/AbstractContainerTestBase.java | 866 +++ .../AbstractHierarchicalContainerTestBase.java | 289 + .../util/AbstractInMemoryContainerTestBase.java | 6 + .../com/vaadin/v7/data/util/BeanContainerTest.java | 516 ++ .../v7/data/util/BeanItemContainerGenerator.java | 150 + .../v7/data/util/BeanItemContainerSortTest.java | 166 + .../vaadin/v7/data/util/BeanItemContainerTest.java | 997 +++ .../java/com/vaadin/v7/data/util/BeanItemTest.java | 389 ++ .../util/ContainerHierarchicalWrapperTest.java | 26 + .../v7/data/util/ContainerOrderedWrapperTest.java | 107 + .../v7/data/util/ContainerSizeAssertTest.java | 59 + .../vaadin/v7/data/util/ContainerSortingTest.java | 222 + .../v7/data/util/FileSystemContainerTest.java | 16 + .../util/GeneratedPropertyContainerBasicTest.java | 596 ++ .../data/util/GeneratedPropertyContainerTest.java | 318 + .../HierarchicalContainerOrderedWrapperTest.java | 33 + .../v7/data/util/HierarchicalContainerTest.java | 286 + .../vaadin/v7/data/util/IndexedContainerTest.java | 533 ++ .../util/MethodPropertyMemoryConsumptionTest.java | 150 + .../v7/data/util/NestedMethodPropertyTest.java | 351 + .../vaadin/v7/data/util/ObjectPropertyTest.java | 102 + .../util/PerformanceTestIndexedContainerTest.java | 120 + .../v7/data/util/PropertyDescriptorTest.java | 84 + .../vaadin/v7/data/util/PropertySetItemTest.java | 432 ++ .../data/util/ReflectToolsGetSuperFieldTest.java | 35 + .../util/TransactionalPropertyWrapperTest.java | 117 + .../data/util/filter/AbstractFilterTestBase.java | 97 + .../v7/data/util/filter/AndOrFilterTest.java | 246 + .../v7/data/util/filter/CompareFilterDateTest.java | 142 + .../v7/data/util/filter/CompareFilterTest.java | 322 + .../v7/data/util/filter/IsNullFilterTest.java | 61 + .../vaadin/v7/data/util/filter/LikeFilterTest.java | 48 + .../vaadin/v7/data/util/filter/NotFilterTest.java | 54 + .../data/util/filter/SimpleStringFilterTest.java | 139 + .../vaadin/v7/data/util/sqlcontainer/AllTests.java | 24 + .../data/util/sqlcontainer/ColumnPropertyTest.java | 318 + .../v7/data/util/sqlcontainer/DataGenerator.java | 132 + .../data/util/sqlcontainer/FreeformQueryUtil.java | 66 + .../data/util/sqlcontainer/ReadOnlyRowIdTest.java | 55 + .../v7/data/util/sqlcontainer/RowIdTest.java | 60 + .../sqlcontainer/SQLContainerTableQueryTest.java | 1353 ++++ .../data/util/sqlcontainer/SQLContainerTest.java | 2469 +++++++ .../data/util/sqlcontainer/SQLTestsConstants.java | 154 + .../v7/data/util/sqlcontainer/TicketTest.java | 195 + .../vaadin/v7/data/util/sqlcontainer/UtilTest.java | 50 + .../connection/J2EEConnectionPoolTest.java | 107 + .../connection/MockInitialContextFactory.java | 25 + .../connection/SimpleJDBCConnectionPoolTest.java | 185 + .../util/sqlcontainer/filters/BetweenTest.java | 182 + .../util/sqlcontainer/filters/CompareTest.java | 44 + .../data/util/sqlcontainer/filters/LikeTest.java | 229 + .../sqlcontainer/generator/SQLGeneratorsTest.java | 239 + .../generator/StatementHelperTest.java | 64 + .../util/sqlcontainer/query/FreeformQueryTest.java | 1005 +++ .../util/sqlcontainer/query/QueryBuilderTest.java | 315 + .../util/sqlcontainer/query/TableQueryTest.java | 746 ++ .../query/ValidatingSimpleJDBCConnectionPool.java | 88 + .../validator/BigDecimalRangeValidatorTest.java | 60 + .../validator/BigIntegerRangeValidatorTest.java | 60 + .../data/validator/ByteRangeValidatorTest.java | 59 + .../data/validator/CompositeValidatorTest.java | 124 + .../data/validator/DateRangeValidatorTest.java | 106 + .../data/validator/DoubleRangeValidatorTest.java | 52 + .../tests/data/validator/EmailValidatorTest.java | 31 + .../data/validator/FloatRangeValidatorTest.java | 54 + .../data/validator/IntegerRangeValidatorTest.java | 52 + .../data/validator/LongRangeValidatorTest.java | 52 + .../v7/tests/data/validator/NullValidatorTest.java | 47 + .../tests/data/validator/RegexpValidatorTest.java | 53 + .../data/validator/ShortRangeValidatorTest.java | 60 + .../data/validator/StringLengthValidatorTest.java | 77 + .../design/ParseAllSupportedComponentsTest.java | 45 + .../v7/tests/design/ParseLegacyPrefixTest.java | 44 + .../v7/tests/design/WriteLegacyDesignTest.java | 99 + .../server/AbstractBeanContainerListenersTest.java | 16 + .../server/AbstractContainerListenersTest.java | 26 + .../AbstractInMemoryContainerListenersTest.java | 14 + .../v7/tests/server/ContextClickListenerTest.java | 163 + .../server/IndexedContainerListenersTest.java | 26 + .../vaadin/v7/tests/server/SerializationTest.java | 135 + .../abstractfield/AbstractFieldListenersTest.java | 27 + .../AbstractSelectDeclarativeTest.java | 306 + .../AbstractSelectListenersTest.java | 26 + .../abstractselect/AbstractSelectStateTest.java | 60 + .../abstractselect/OptionGroupDeclarativeTest.java | 160 + .../component/abstractselect/OptionGroupTest.java | 32 + .../component/calendar/CalendarBasicsTest.java | 290 + .../calendar/CalendarDeclarativeTest.java | 62 + .../calendar/ContainerDataSourceTest.java | 396 ++ .../calendar/ContainerEventProviderTest.java | 88 + .../AbstractColorPickerDeclarativeTest.java | 87 + .../colorpicker/ColorConversionsTest.java | 57 + .../combobox/ComboBoxDeclarativeTest.java | 88 + .../component/combobox/ComboBoxStateTest.java | 59 + .../datefield/DateFieldConverterTest.java | 79 + .../datefield/LegacyDateFieldDeclarativeTest.java | 113 + .../datefield/PopupDateFieldDeclarativeTest.java | 61 + .../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 | 295 + .../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 + .../v7/tests/server/component/grid/TestGrid.java | 62 + .../declarative/GridColumnDeclarativeTest.java | 102 + .../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 + .../listselect/ListSelectDeclarativeTest.java | 71 + .../component/listselect/ListSelectStateTest.java | 45 + .../nativeselect/NativeSelectDeclarativeTest.java | 69 + .../optiongroup/OptionGroupListenersTest.java | 25 + .../optiongroup/OptionGroupStateTest.java | 60 + .../richtextarea/RichTextAreaDeclarativeTest.java | 70 + .../richtextarea/RichTextAreaStateTest.java | 59 + .../component/select/SelectListenersTest.java | 25 + .../table/CacheUpdateExceptionCausesTest.java | 55 + .../tests/server/component/table/FooterTest.java | 103 + .../component/table/MultipleSelectionTest.java | 62 + .../component/table/TableColumnAlignmentsTest.java | 143 + .../component/table/TableContextClickTest.java | 57 + .../component/table/TableDeclarativeTest.java | 179 + .../component/table/TableDeclarativeTestBase.java | 74 + .../server/component/table/TableGeneratorTest.java | 42 + .../server/component/table/TableListenersTest.java | 49 + .../table/TablePropertyValueConverterTest.java | 384 + .../component/table/TableSelectableTest.java | 75 + .../component/table/TableSerializationTest.java | 26 + .../server/component/table/TableStateTest.java | 60 + .../component/table/TableVisibleColumnsTest.java | 72 + .../textarea/TextAreaDeclarativeTest.java | 74 + .../TextFieldWithConverterAndValidatorTest.java | 49 + .../TextFieldWithPropertyFormatterTest.java | 111 + .../textfield/TextFieldWithValidatorTest.java | 179 + .../tests/server/component/tree/ListenersTest.java | 143 + .../server/component/tree/TreeDeclarativeTest.java | 81 + .../server/component/tree/TreeListenersTest.java | 33 + .../v7/tests/server/component/tree/TreeTest.java | 178 + .../component/treetable/EmptyTreeTableTest.java | 17 + .../treetable/TreeTableDeclarativeTest.java | 156 + .../treetable/TreeTableSetContainerNullTest.java | 16 + .../server/component/treetable/TreeTableTest.java | 102 + .../TwinColSelectDeclarativeTest.java | 74 + .../twincolselect/TwinColSelectStateTest.java | 60 + .../server/components/ComboBoxValueChangeTest.java | 47 + .../tests/server/renderer/ImageRendererTest.java | 85 + .../v7/tests/server/renderer/RendererTest.java | 268 + .../server/validation/BeanValidationTest.java | 129 + .../server/validation/RangeValidatorTest.java | 63 + .../server/validation/ReadOnlyValidationTest.java | 17 + .../test/java/com/vaadin/v7/ui/AbsSelectTest.java | 147 + .../java/com/vaadin/v7/ui/NativeSelectTest.java | 70 + .../java/com/vaadin/v7/ui/RichTextAreaTest.java | 47 + .../src/test/java/com/vaadin/v7/ui/TableTest.java | 78 + .../test/java/com/vaadin/v7/ui/TextAreaTest.java | 47 + .../vaadin/tests/design/all-components-legacy.html | 122 - .../com/vaadin/tests/design/all-components.html | 122 - .../com/vaadin/tests/design/testFile-legacy.html | 19 - .../v7/tests/design/all-components-legacy.html | 122 + .../com/vaadin/v7/tests/design/all-components.html | 122 + .../vaadin/v7/tests/design/testFile-legacy.html | 19 + server/src/main/java/com/vaadin/data/Buffered.java | 177 - .../java/com/vaadin/data/BufferedValidatable.java | 47 - .../src/main/java/com/vaadin/data/Collapsible.java | 80 - .../src/main/java/com/vaadin/data/Container.java | 1255 ---- .../java/com/vaadin/data/ContainerHelpers.java | 102 - server/src/main/java/com/vaadin/data/Item.java | 206 - server/src/main/java/com/vaadin/data/Property.java | 425 -- .../com/vaadin/data/util/AbstractProperty.java | 270 - .../vaadin/data/util/ContainerOrderedWrapper.java | 728 -- .../com/vaadin/data/util/DefaultItemSorter.java | 221 - .../util/HierarchicalContainerOrderedWrapper.java | 82 - .../main/java/com/vaadin/data/util/ItemSorter.java | 70 - .../main/java/com/vaadin/data/util/ListSet.java | 276 - .../java/com/vaadin/data/util/MethodProperty.java | 774 -- .../vaadin/data/util/MethodPropertyDescriptor.java | 148 - .../com/vaadin/data/util/NestedMethodProperty.java | 269 - .../vaadin/data/util/NestedPropertyDescriptor.java | 72 - .../java/com/vaadin/data/util/ObjectProperty.java | 142 - .../com/vaadin/data/util/PropertyFormatter.java | 257 - .../vaadin/data/util/PropertyValueGenerator.java | 103 - .../java/com/vaadin/data/util/PropertysetItem.java | 374 - .../com/vaadin/data/util/TextFileProperty.java | 157 - .../data/util/TransactionalPropertyWrapper.java | 153 - .../vaadin/data/util/VaadinPropertyDescriptor.java | 55 - .../data/util/filter/AbstractJunctionFilter.java | 88 - .../main/java/com/vaadin/data/util/filter/And.java | 56 - .../java/com/vaadin/data/util/filter/Between.java | 106 - .../java/com/vaadin/data/util/filter/Compare.java | 370 - .../java/com/vaadin/data/util/filter/IsNull.java | 96 - .../java/com/vaadin/data/util/filter/Like.java | 105 - .../main/java/com/vaadin/data/util/filter/Not.java | 82 - .../main/java/com/vaadin/data/util/filter/Or.java | 75 - .../data/util/filter/SimpleStringFilter.java | 167 - .../util/filter/UnsupportedFilterException.java | 47 - .../com/vaadin/event/DataBoundTransferable.java | 6 +- .../main/java/com/vaadin/event/FieldEvents.java | 16 +- .../main/java/com/vaadin/event/ItemClickEvent.java | 6 +- .../com/vaadin/legacy/ui/LegacyCustomField.java | 158 - .../com/vaadin/server/AbstractErrorMessage.java | 2 +- .../main/java/com/vaadin/server/ClassResource.java | 2 +- .../java/com/vaadin/server/LegacyApplication.java | 2 +- .../vaadin/server/LegacyApplicationUIProvider.java | 2 +- server/src/main/java/com/vaadin/server/Page.java | 2 +- .../main/java/com/vaadin/server/VaadinSession.java | 24 +- .../src/main/java/com/vaadin/ui/AbstractField.java | 2 +- server/src/main/java/com/vaadin/ui/Label.java | 38 +- .../src/main/java/com/vaadin/ui/LegacyWindow.java | 481 -- .../src/main/java/com/vaadin/ui/ProgressBar.java | 6 +- .../main/java/com/vaadin/ui/ProgressIndicator.java | 2 +- server/src/main/java/com/vaadin/ui/TextField.java | 4 +- .../ui/declarative/DesignAttributeHandler.java | 4 +- .../com/vaadin/ui/declarative/DesignFormatter.java | 56 +- .../converters/DesignDateConverter.java | 8 +- .../converters/DesignEnumConverter.java | 8 +- .../converters/DesignObjectConverter.java | 8 +- .../converters/DesignResourceConverter.java | 20 +- .../converters/DesignShortcutActionConverter.java | 8 +- .../converters/DesignTimeZoneConverter.java | 8 +- .../converters/DesignToStringConverter.java | 20 +- .../src/main/java/com/vaadin/v7/data/Buffered.java | 177 + .../com/vaadin/v7/data/BufferedValidatable.java | 45 + .../main/java/com/vaadin/v7/data/Collapsible.java | 80 + .../main/java/com/vaadin/v7/data/Container.java | 1255 ++++ .../java/com/vaadin/v7/data/ContainerHelpers.java | 102 + server/src/main/java/com/vaadin/v7/data/Item.java | 206 + .../src/main/java/com/vaadin/v7/data/Property.java | 425 ++ .../com/vaadin/v7/data/util/AbstractProperty.java | 270 + .../v7/data/util/ContainerOrderedWrapper.java | 728 ++ .../com/vaadin/v7/data/util/DefaultItemSorter.java | 221 + .../util/HierarchicalContainerOrderedWrapper.java | 82 + .../java/com/vaadin/v7/data/util/ItemSorter.java | 70 + .../main/java/com/vaadin/v7/data/util/ListSet.java | 276 + .../com/vaadin/v7/data/util/MethodProperty.java | 774 ++ .../v7/data/util/MethodPropertyDescriptor.java | 148 + .../vaadin/v7/data/util/NestedMethodProperty.java | 269 + .../v7/data/util/NestedPropertyDescriptor.java | 72 + .../com/vaadin/v7/data/util/ObjectProperty.java | 142 + .../com/vaadin/v7/data/util/PropertyFormatter.java | 257 + .../v7/data/util/PropertyValueGenerator.java | 103 + .../com/vaadin/v7/data/util/PropertysetItem.java | 374 + .../com/vaadin/v7/data/util/TextFileProperty.java | 157 + .../v7/data/util/TransactionalPropertyWrapper.java | 153 + .../v7/data/util/VaadinPropertyDescriptor.java | 55 + .../converter/AbstractStringToNumberConverter.java | 123 + .../vaadin/v7/data/util/converter/Converter.java | 176 + .../v7/data/util/converter/ConverterFactory.java | 33 + .../v7/data/util/converter/ConverterUtil.java | 260 + .../data/util/converter/DateToLongConverter.java | 89 + .../util/converter/DateToSqlDateConverter.java | 82 + .../util/converter/DefaultConverterFactory.java | 133 + .../LegacyAbstractStringToNumberConverter.java | 123 - .../v7/data/util/converter/LegacyConverter.java | 176 - .../util/converter/LegacyConverterFactory.java | 33 - .../data/util/converter/LegacyConverterUtil.java | 260 - .../util/converter/LegacyDateToLongConverter.java | 89 - .../converter/LegacyDateToSqlDateConverter.java | 82 - .../converter/LegacyDefaultConverterFactory.java | 133 - .../util/converter/LegacyReverseConverter.java | 97 - .../LegacyStringToBigDecimalConverter.java | 60 - .../LegacyStringToBigIntegerConverter.java | 67 - .../converter/LegacyStringToBooleanConverter.java | 182 - .../converter/LegacyStringToByteConverter.java | 89 - .../LegacyStringToCollectionConverter.java | 244 - .../converter/LegacyStringToDateConverter.java | 132 - .../converter/LegacyStringToDoubleConverter.java | 64 - .../converter/LegacyStringToEnumConverter.java | 161 - .../converter/LegacyStringToFloatConverter.java | 63 - .../converter/LegacyStringToIntegerConverter.java | 94 - .../converter/LegacyStringToLongConverter.java | 78 - .../converter/LegacyStringToShortConverter.java | 89 - .../v7/data/util/converter/ReverseConverter.java | 97 + .../converter/StringToBigDecimalConverter.java | 60 + .../converter/StringToBigIntegerConverter.java | 67 + .../util/converter/StringToBooleanConverter.java | 182 + .../data/util/converter/StringToByteConverter.java | 89 + .../converter/StringToCollectionConverter.java | 244 + .../data/util/converter/StringToDateConverter.java | 132 + .../util/converter/StringToDoubleConverter.java | 64 + .../data/util/converter/StringToEnumConverter.java | 161 + .../util/converter/StringToFloatConverter.java | 63 + .../util/converter/StringToIntegerConverter.java | 94 + .../data/util/converter/StringToLongConverter.java | 78 + .../util/converter/StringToShortConverter.java | 89 + .../data/util/filter/AbstractJunctionFilter.java | 88 + .../java/com/vaadin/v7/data/util/filter/And.java | 56 + .../com/vaadin/v7/data/util/filter/Between.java | 106 + .../com/vaadin/v7/data/util/filter/Compare.java | 370 + .../com/vaadin/v7/data/util/filter/IsNull.java | 96 + .../java/com/vaadin/v7/data/util/filter/Like.java | 105 + .../java/com/vaadin/v7/data/util/filter/Not.java | 82 + .../java/com/vaadin/v7/data/util/filter/Or.java | 75 + .../v7/data/util/filter/SimpleStringFilter.java | 167 + .../util/filter/UnsupportedFilterException.java | 47 + .../main/java/com/vaadin/v7/ui/AbstractField.java | 1810 +++++ .../java/com/vaadin/v7/ui/AbstractTextField.java | 814 +++ .../src/main/java/com/vaadin/v7/ui/CheckBox.java | 269 + .../main/java/com/vaadin/v7/ui/CustomField.java | 155 + server/src/main/java/com/vaadin/v7/ui/Field.java | 150 + .../java/com/vaadin/v7/ui/LegacyAbstractField.java | 1810 ----- .../com/vaadin/v7/ui/LegacyAbstractTextField.java | 814 --- .../main/java/com/vaadin/v7/ui/LegacyCheckBox.java | 269 - .../main/java/com/vaadin/v7/ui/LegacyField.java | 150 - .../java/com/vaadin/v7/ui/LegacyPasswordField.java | 123 - .../java/com/vaadin/v7/ui/LegacyTextField.java | 151 - .../main/java/com/vaadin/v7/ui/LegacyWindow.java | 486 ++ .../main/java/com/vaadin/v7/ui/PasswordField.java | 123 + .../src/main/java/com/vaadin/v7/ui/TextField.java | 151 + .../test/java/com/vaadin/tests/VaadinClasses.java | 6 +- .../converter/AnyEnumToStringConverterTest.java | 18 +- .../tests/data/converter/ConverterFactoryTest.java | 24 +- .../data/converter/DateToLongConverterTest.java | 4 +- .../data/converter/DateToSqlDateConverterTest.java | 4 +- .../converter/DefaultConverterFactoryTest.java | 4 +- .../SpecificEnumToStringConverterTest.java | 18 +- .../converter/StringToBigDecimalConverterTest.java | 4 +- .../converter/StringToBigIntegerConverterTest.java | 4 +- .../converter/StringToBooleanConverterTest.java | 8 +- .../data/converter/StringToByteConverterTest.java | 12 +- .../converter/StringToCollectionConverterTest.java | 38 +- .../data/converter/StringToDateConverterTest.java | 4 +- .../converter/StringToDoubleConverterTest.java | 4 +- .../data/converter/StringToEnumConverterTest.java | 12 +- .../data/converter/StringToFloatConverterTest.java | 4 +- .../converter/StringToIntegerConverterTest.java | 6 +- .../data/converter/StringToLongConverterTest.java | 10 +- .../data/converter/StringToShortConverterTest.java | 12 +- .../vaadin/tests/design/DesignFormatterTest.java | 2 +- .../server/AbstractPropertyListenersTest.java | 12 +- .../com/vaadin/tests/server/EventRouterTest.java | 8 +- .../com/vaadin/tests/server/ExtensionTest.java | 4 +- .../vaadin/tests/server/PropertyFormatterTest.java | 4 +- .../tests/server/PropertysetItemListenersTest.java | 6 +- .../server/component/FieldDefaultValuesTest.java | 12 +- .../abstractfield/AbsFieldValidatorsTest.java | 4 +- .../AbsFieldValueConversionErrorTest.java | 24 +- .../AbsFieldValueConversionsTest.java | 40 +- .../abstractfield/AbstractFieldReadOnlyTest.java | 12 +- .../abstractfield/DefaultConverterFactoryTest.java | 10 +- .../abstractfield/RemoveListenersOnDetachTest.java | 10 +- .../AbstractTextFieldListenersTest.java | 8 +- .../server/component/button/ButtonClickTest.java | 2 +- .../component/gridlayout/DefaultAlignmentTest.java | 6 +- .../component/label/LabelConvertersTest.java | 4 +- .../server/component/label/LabelListenersTest.java | 8 +- .../orderedlayout/DefaultAlignmentTest.java | 6 +- .../ui/LegacyUIAddRemoveComponentsTest.java | 2 +- .../component/window/AddRemoveSubWindowTest.java | 2 +- .../AbstractFieldValueChangeTestBase.java | 20 +- .../components/TextFieldValueChangeTest.java | 14 +- .../vaadin/tests/server/components/WindowTest.java | 2 +- .../ui/AbsFieldDataSourceLocaleChangeTest.java | 8 +- .../java/com/vaadin/ui/LabelDataSourceTest.java | 2 +- .../src/test/java/com/vaadin/ui/TextFieldTest.java | 10 +- .../testbench/customelements/DateFieldElement.java | 13 + .../customelements/FixedNotificationElement.java | 28 + .../testbench/customelements/MenuBarElement.java | 15 + .../testbench/customelements/WindowElement.java | 76 + .../testbench/customelements/CalendarElement.java | 48 + .../testbench/customelements/CheckBoxElement.java | 9 + .../customelements/ColorPickerAreaElement.java | 9 + .../customelements/ColorPickerElement.java | 9 + .../testbench/customelements/ComboBoxElement.java | 61 + .../testbench/customelements/DateFieldElement.java | 24 + .../v7/testbench/customelements/GridElement.java | 50 + .../customelements/InlineDateFieldElement.java | 28 + .../customelements/ListSelectElement.java | 9 + .../customelements/NativeSelectElement.java | 9 + .../customelements/OptionGroupElement.java | 9 + .../customelements/PasswordFieldElement.java | 9 + .../customelements/PopupDateFieldElement.java | 28 + .../customelements/ProgressBarElement.java | 56 + .../customelements/RichTextAreaElement.java | 9 + .../v7/testbench/customelements/SelectElement.java | 9 + .../v7/testbench/customelements/TableElement.java | 40 + .../testbench/customelements/TextAreaElement.java | 9 + .../testbench/customelements/TextFieldElement.java | 9 + .../v7/testbench/customelements/TreeElement.java | 9 + .../testbench/customelements/TreeTableElement.java | 9 + .../customelements/TwinColSelectElement.java | 9 + .../data/util/sqlcontainer/SQLTestsConstants.java | 154 - .../screenshotbrowser/ScreenshotBrowser.java | 8 +- .../src/main/java/com/vaadin/tests/Components.java | 12 +- .../java/com/vaadin/tests/CustomLayoutDemo.java | 8 +- .../java/com/vaadin/tests/FocusingComponents.java | 14 +- .../src/main/java/com/vaadin/tests/LayoutDemo.java | 2 +- .../main/java/com/vaadin/tests/ListenerOrder.java | 18 +- .../main/java/com/vaadin/tests/ModalWindow.java | 8 +- .../java/com/vaadin/tests/NativeWindowing.java | 2 +- .../src/main/java/com/vaadin/tests/Parameters.java | 4 +- .../PerformanceTestBasicComponentRendering.java | 8 +- .../tests/PerformanceTestSubTreeCaching.java | 2 +- .../java/com/vaadin/tests/RandomLayoutStress.java | 8 +- .../java/com/vaadin/tests/ScrollbarStressTest.java | 6 +- .../com/vaadin/tests/StressComponentsInTable.java | 2 +- .../com/vaadin/tests/TableChangingDatasource.java | 2 +- .../java/com/vaadin/tests/TableSelectTest.java | 4 +- .../src/main/java/com/vaadin/tests/TestBench.java | 8 +- .../java/com/vaadin/tests/TestCaptionWrapper.java | 18 +- .../com/vaadin/tests/TestContainerChanges.java | 18 +- .../java/com/vaadin/tests/TestForAlignments.java | 6 +- ...ApplicationLayoutThatUsesWholeBrosersSpace.java | 4 +- .../tests/TestForBasicApplicationLayout.java | 2 +- .../tests/TestForChildComponentRendering.java | 2 +- .../vaadin/tests/TestForContainerFilterable.java | 10 +- .../TestForGridLayoutChildComponentRendering.java | 2 +- .../vaadin/tests/TestForMultipleStyleNames.java | 8 +- .../com/vaadin/tests/TestForNativeWindowing.java | 2 +- .../tests/TestForPreconfiguredComponents.java | 10 +- .../com/vaadin/tests/TestForRichTextEditor.java | 6 +- .../java/com/vaadin/tests/TestForStyledUpload.java | 2 +- ...tForTablesInitialColumnWidthLogicRendering.java | 2 +- .../main/java/com/vaadin/tests/TestForTrees.java | 2 +- .../main/java/com/vaadin/tests/TestForUpload.java | 16 +- .../java/com/vaadin/tests/TestForWindowing.java | 10 +- .../java/com/vaadin/tests/TestMethodProperty.java | 2 +- .../tests/TestSelectAndDatefieldInDeepLayouts.java | 2 +- .../com/vaadin/tests/TestSetVisibleAndCaching.java | 2 +- .../com/vaadin/tests/TestSizeableIncomponents.java | 14 +- .../main/java/com/vaadin/tests/TestSplitPanel.java | 2 +- .../main/java/com/vaadin/tests/TreeFilesystem.java | 8 +- .../com/vaadin/tests/TreeFilesystemContainer.java | 12 +- .../tests/UsingCustomNewItemHandlerInSelect.java | 6 +- .../com/vaadin/tests/UsingObjectsInSelect.java | 8 +- .../tests/actions/ActionsWithoutKeyCode.java | 4 +- .../com/vaadin/tests/appengine/GAESyncTest.java | 12 +- .../tests/application/ApplicationCloseTest.java | 2 +- .../tests/application/CommErrorEmulatorUI.java | 20 +- .../tests/application/ErrorInUnloadEvent.java | 2 +- .../tests/application/ThreadLocalInstances.java | 2 +- .../vaadin/tests/application/calculator/Calc.java | 10 +- .../tests/applicationcontext/ChangeSessionId.java | 2 +- .../tests/applicationservlet/SystemMessages.java | 6 +- .../tests/browserfeatures/WebkitScrollbarTest.java | 2 +- .../components/AbstractComponentContainerTest.java | 10 +- .../components/AbstractComponentTestCase.java | 4 +- .../AbstractOrderedLayoutWithCaptions.java | 4 +- .../components/AddRemoveSetStyleNamesTest.java | 6 +- .../vaadin/tests/components/ComponentTestCase.java | 12 +- .../components/DisableEnableCascadeStyles.java | 8 +- .../com/vaadin/tests/components/ErrorMessages.java | 6 +- .../tests/components/FocusAndBlurListeners.java | 8 +- .../tests/components/FocusFromShortcutAction.java | 6 +- .../components/HierarchicalContainerSorting.java | 8 +- .../tests/components/LayoutAttachListenerInfo.java | 6 +- .../vaadin/tests/components/MultipleDebugIds.java | 6 +- .../com/vaadin/tests/components/SaneErrors.java | 4 +- .../java/com/vaadin/tests/components/TestBase.java | 2 +- .../tests/components/TouchDevicesTooltip.java | 12 +- .../vaadin/tests/components/TouchScrollables.java | 8 +- .../AbsoluteLayoutHideComponent.java | 8 +- .../AbsoluteLayoutRelativeSizeContent.java | 2 +- .../absolutelayout/AbsoluteLayoutResizing.java | 2 +- .../abstractcomponent/AllComponentTooltipTest.java | 2 +- .../components/abstractcomponent/EnableState.java | 2 +- .../components/abstractcomponent/PrimaryStyle.java | 4 +- .../AbstractComponentDataBindingTest.java | 20 +- .../AbstractFieldCommitWithInvalidValues.java | 12 +- .../AbstractFieldDataSourceReadOnly.java | 6 +- .../abstractfield/DateFieldBackedByString.java | 6 +- .../abstractfield/DateFieldBasedOnLong.java | 6 +- .../abstractfield/DoubleInTextField.java | 8 +- .../abstractfield/FieldFocusOnClick.java | 6 +- .../IntegerDoubleFieldsWithDataSource.java | 22 +- .../IntegerFieldWithoutDataSource.java | 12 +- .../abstractfield/LegacyAbstractFieldTest.java | 16 +- .../abstractfield/LegacyAbstractTextFieldTest.java | 10 +- .../RequiredIndicatorForFieldsWithoutCaption.java | 10 +- .../abstractfield/TextFieldConversions.java | 14 +- .../Vaadin6ImplicitDoubleConverter.java | 8 +- .../accordion/AccordionInactiveTabSize.java | 6 +- .../BeanItemContainerGenerator.java | 2 +- .../BeanItemContainerNullValues.java | 2 +- .../TestBeanItemContainerUsage.java | 4 +- .../tests/components/button/ButtonTabIndex.java | 6 +- .../components/button/ButtonUndefinedWidth.java | 4 +- .../calendar/BeanItemContainerTestUI.java | 28 +- .../calendar/CalendarActionEventSource.java | 14 +- .../calendar/CalendarActionsMenuTest.java | 22 +- .../components/calendar/CalendarActionsUI.java | 6 +- .../calendar/CalendarBackwardForward.java | 8 +- .../components/calendar/CalendarDragAndDrop.java | 12 +- .../components/calendar/CalendarHtmlInEvents.java | 14 +- .../calendar/CalendarMonthViewDndEvent.java | 12 +- .../components/calendar/CalendarNotifications.java | 12 +- .../components/calendar/CalendarReadOnly.java | 6 +- .../calendar/CalendarRescheduleEvent.java | 12 +- .../calendar/CalendarResizeOverlappingEvents.java | 8 +- ...darShownNotCorrectlyWhenPartiallyOutOfView.java | 8 +- .../tests/components/calendar/CalendarTest.java | 64 +- .../components/calendar/CalendarTestEvent.java | 4 +- .../components/calendar/CalendarVisibleHours.java | 8 +- .../components/calendar/CalendarWeekSelection.java | 2 +- .../calendar/DndCalendarTargetDetails.java | 2 +- .../components/calendar/HiddenFwdBackButtons.java | 6 +- .../components/calendar/NotificationTestUI.java | 12 +- .../components/calendar/NullEventMoveHandler.java | 10 +- .../calendar/SetFirstVisibleHourOfDay.java | 6 +- .../calendar/TestHideTimeAndSeparator.java | 4 +- .../tests/components/caption/EmptyCaptions.java | 22 +- .../tests/components/caption/IconsInCaption.java | 6 +- .../components/colorpicker/ColorPickerHsvTest.java | 2 +- .../components/colorpicker/ColorPickerTestUI.java | 10 +- .../colorpicker/DefaultCaptionWidth.java | 2 +- .../tests/components/combobox/ComboBoxBorder.java | 6 +- .../components/combobox/ComboBoxClickIcon.java | 2 +- .../ComboBoxCombinedWithEnterShortcut.java | 2 +- .../combobox/ComboBoxCursorPositionReset.java | 2 +- .../combobox/ComboBoxDataSourceChange.java | 10 +- .../combobox/ComboBoxDuplicateCaption.java | 10 +- .../ComboBoxEmptyItemsKeyboardNavigation.java | 2 +- .../combobox/ComboBoxEnablesComboBox.java | 6 +- .../combobox/ComboBoxIdenticalItems.java | 8 +- .../tests/components/combobox/ComboBoxInPopup.java | 2 +- .../components/combobox/ComboBoxInPopupView.java | 2 +- .../components/combobox/ComboBoxInputPrompt.java | 2 +- .../combobox/ComboBoxInvalidNullSelection.java | 8 +- .../ComboBoxItemAddingWithFocusListener.java | 2 +- .../components/combobox/ComboBoxItemIcon.java | 4 +- .../components/combobox/ComboBoxLargeIcons.java | 4 +- .../combobox/ComboBoxMouseSelectEnter.java | 4 +- .../components/combobox/ComboBoxMousewheel.java | 2 +- .../components/combobox/ComboBoxNavigation.java | 2 +- .../components/combobox/ComboBoxOnSmallScreen.java | 4 +- .../components/combobox/ComboBoxPageLength.java | 6 +- .../components/combobox/ComboBoxParentDisable.java | 4 +- .../combobox/ComboBoxPopupWhenBodyScrolls.java | 2 +- .../combobox/ComboBoxReapperingOldValue.java | 12 +- .../components/combobox/ComboBoxResetValue.java | 2 +- .../ComboBoxSQLContainerFilteredValueChange.java | 16 +- .../combobox/ComboBoxScrollingToPageDisabled.java | 6 +- .../combobox/ComboBoxScrollingWithArrows.java | 2 +- .../components/combobox/ComboBoxSelecting.java | 8 +- .../ComboBoxSelectingWithNewItemsAllowed.java | 2 +- .../ComboBoxSetNullWhenNewItemsAllowed.java | 6 +- .../tests/components/combobox/ComboBoxSlow.java | 4 +- .../components/combobox/ComboBoxSlowInFF.java | 6 +- .../combobox/ComboBoxSuggestionOnDetach.java | 2 +- .../combobox/ComboBoxSuggestionPageLength.java | 2 +- .../combobox/ComboBoxSuggestionPopupClose.java | 2 +- .../combobox/ComboBoxSuggestionPopupWidth.java | 2 +- .../ComboBoxSuggestionPopupWidthLegacy.java | 2 +- .../ComboBoxSuggestionPopupWidthPercentage.java | 2 +- .../ComboBoxSuggestionPopupWidthPixels.java | 2 +- .../components/combobox/ComboBoxTabWhenFilter.java | 8 +- .../combobox/ComboBoxTextFieldEventOrder.java | 10 +- .../combobox/ComboBoxUndefinedWidthAndIcon.java | 4 +- .../components/combobox/ComboBoxValueInput.java | 2 +- .../components/combobox/ComboBoxValueUpdate.java | 4 +- .../tests/components/combobox/ComboBoxes2.java | 4 +- .../components/combobox/ComboFocusBlurEvents.java | 8 +- .../tests/components/combobox/ComboPushTiming.java | 8 +- ...mboSelectedValueBeyondTheFirstDropdownPage.java | 6 +- .../combobox/ComboboxInPopupViewWithItems.java | 4 +- .../combobox/ComboboxMenuBarAutoopen.java | 2 +- .../combobox/ComboboxPopupScrolling.java | 2 +- .../combobox/ComboboxPrimaryStyleNames.java | 2 +- .../combobox/ComboboxStyleChangeWidth.java | 2 +- .../tests/components/combobox/Comboboxes.java | 2 +- .../components/combobox/ComboxBoxErrorMessage.java | 2 +- .../combobox/EscapeClosesComboboxNotWindow.java | 2 +- .../combobox/FilteringTurkishLocale.java | 8 +- .../combobox/GridLayoutComboBoxZoomOut.java | 4 +- .../components/combobox/NewItemsESCPress.java | 6 +- .../tests/components/combobox/PopUpWidth.java | 4 +- .../components/combobox/RemovalOfSelectedIcon.java | 2 +- .../tests/components/combobox/SlowComboBox.java | 2 +- .../components/combobox/WidthToggleReadOnly.java | 2 +- .../components/customcomponent/ClipContent.java | 12 +- .../customlayout/CustomLayoutPrimaryStyleName.java | 4 +- .../customlayout/CustomLayoutUsingTemplate.java | 4 +- .../customlayout/CustomLayoutUsingTheme.java | 8 +- .../customlayout/CustomLayoutWithMissingSlot.java | 6 +- .../components/datefield/CustomDateFormats.java | 8 +- .../components/datefield/DateFieldEmptyValid.java | 10 +- .../datefield/DateFieldPopupOffScreen.java | 2 +- .../datefield/DateFieldRangeValidation.java | 18 +- .../components/datefield/DateFieldRanges.java | 6 +- .../components/datefield/DateFieldTimezone.java | 6 +- .../datefield/DateFieldUnparsableDate.java | 10 +- .../DefaultHandleUnparsableDateField.java | 14 +- .../datefield/LegacyDateFieldIsValid.java | 8 +- .../datefield/LegacyDateFieldRanges.java | 36 +- .../components/datefield/LegacyDateFieldTest.java | 6 +- .../datefield/LegacyInlineDateFieldTest.java | 8 +- .../datefield/LegacyPopupDateFieldTest.java | 16 +- .../datefield/NarrowPopupDateFieldInTable.java | 2 +- .../datefield/PopupDateFieldValueChangeEvents.java | 6 +- .../datefield/RequiredInvalidDateField.java | 18 +- .../components/datefield/ValueThroughProperty.java | 12 +- .../draganddropwrapper/DragAndDropDisable.java | 4 +- .../draganddropwrapper/DragAndDropFocusObtain.java | 16 +- .../draganddropwrapper/DragAndDropTextArea.java | 2 +- .../DragAndDropWrapperInPanel.java | 2 +- .../formlayout/CaptionEnableDisable.java | 8 +- .../formlayout/FormLayoutCaptionStyles.java | 10 +- .../formlayout/FormLayoutInVerticalLayout.java | 4 +- .../components/formlayout/FormLayoutResizing.java | 6 +- .../formlayout/HtmlCaptionInFormLayout.java | 6 +- .../TableInFormLayoutCausesScrolling.java | 6 +- .../vaadin/tests/components/grid/BeanRenderer.java | 2 +- .../tests/components/grid/CustomRenderer.java | 12 +- .../grid/GridAddAndRemoveDataOnInit.java | 10 +- .../vaadin/tests/components/grid/GridAddRow.java | 6 +- .../tests/components/grid/GridCheckBoxDisplay.java | 8 +- .../vaadin/tests/components/grid/GridColspans.java | 20 +- .../components/grid/GridColumnAutoExpand.java | 6 +- .../tests/components/grid/GridColumnAutoWidth.java | 14 +- .../tests/components/grid/GridColumnExpand.java | 8 +- .../grid/GridColumnWidthRecalculation.java | 12 +- .../grid/GridColumnWidthsWithoutData.java | 20 +- .../components/grid/GridCustomSelectionModel.java | 2 +- .../tests/components/grid/GridDataSourceReset.java | 8 +- .../components/grid/GridDefaultSelectionMode.java | 6 +- .../tests/components/grid/GridDetailsDetach.java | 16 +- .../components/grid/GridDetailsLayoutExpand.java | 8 +- .../tests/components/grid/GridDetailsLocation.java | 12 +- .../tests/components/grid/GridDetailsWidth.java | 12 +- .../components/grid/GridDisabledMultiselect.java | 8 +- .../tests/components/grid/GridDragAndDrop.java | 6 +- .../grid/GridDragSelectionWhileScrolled.java | 2 +- .../grid/GridEditingWithNoScrollBars.java | 10 +- .../grid/GridEditorConverterNotFound.java | 4 +- .../components/grid/GridEditorCustomField.java | 10 +- .../components/grid/GridEditorFrozenColumnsUI.java | 6 +- .../components/grid/GridEditorMultiselect.java | 6 +- .../vaadin/tests/components/grid/GridEditorUI.java | 14 +- .../grid/GridExtensionCommunication.java | 14 +- .../tests/components/grid/GridFastAsyncUpdate.java | 12 +- .../components/grid/GridGeneratedProperties.java | 22 +- .../grid/GridHeaderFooterComponents.java | 20 +- .../components/grid/GridHeaderFormatChange.java | 16 +- .../components/grid/GridHeaderStyleNames.java | 14 +- .../vaadin/tests/components/grid/GridHeight.java | 16 +- .../tests/components/grid/GridInGridLayout.java | 6 +- .../tests/components/grid/GridInTabSheet.java | 10 +- .../tests/components/grid/GridInWindowResize.java | 8 +- .../grid/GridInitiallyHiddenColumns.java | 4 +- .../tests/components/grid/GridItemSetChange.java | 6 +- .../components/grid/GridLayoutDetailsRow.java | 8 +- .../components/grid/GridMultiSelectionOnInit.java | 8 +- .../grid/GridMultiSelectionScrollBar.java | 4 +- .../tests/components/grid/GridRendererChange.java | 10 +- .../components/grid/GridReplaceContainer.java | 10 +- .../tests/components/grid/GridResizeAndScroll.java | 10 +- .../components/grid/GridResizeHiddenColumn.java | 8 +- .../tests/components/grid/GridResizeTerror.java | 4 +- .../tests/components/grid/GridRowHeightChange.java | 8 +- .../grid/GridScrollToLineWhileResizing.java | 10 +- .../tests/components/grid/GridScrolling.java | 10 +- .../tests/components/grid/GridSelectAllCell.java | 6 +- .../tests/components/grid/GridSidebarPosition.java | 8 +- .../tests/components/grid/GridSingleColumn.java | 12 +- .../tests/components/grid/GridSortIndicator.java | 8 +- .../grid/GridSubPixelProblemWrapping.java | 8 +- .../tests/components/grid/GridSwitchRenderers.java | 16 +- .../tests/components/grid/GridThemeChange.java | 6 +- .../vaadin/tests/components/grid/GridThemeUI.java | 26 +- .../tests/components/grid/GridWidthIncrease.java | 4 +- .../components/grid/GridWithBrokenRenderer.java | 6 +- .../tests/components/grid/GridWithLabelEditor.java | 8 +- .../tests/components/grid/GridWithoutRenderer.java | 4 +- .../components/grid/InitialFrozenColumns.java | 6 +- .../tests/components/grid/IntArrayRenderer.java | 2 +- .../tests/components/grid/JavaScriptRenderers.java | 8 +- .../components/grid/JavaScriptStringRenderer.java | 2 +- .../tests/components/grid/MyBeanJSRenderer.java | 2 +- .../vaadin/tests/components/grid/NullHeaders.java | 4 +- .../tests/components/grid/NullRenderers.java | 28 +- .../tests/components/grid/PersonTestGrid.java | 6 +- .../components/grid/ProgrammaticEditorControl.java | 8 +- .../tests/components/grid/RowAwareRenderer.java | 2 +- .../tests/components/grid/SelectDuringInit.java | 6 +- .../components/grid/SortableHeaderStyles.java | 12 +- .../tests/components/grid/WidgetRenderers.java | 20 +- .../grid/basicfeatures/GridBasicFeatures.java | 384 +- .../grid/basicfeatures/GridHeightByRowOnInit.java | 6 +- .../grid/basicfeatures/GridSortingIndicators.java | 10 +- .../basicfeatures/server/GridClearContainer.java | 6 +- .../gridlayout/GridLayoutCaptionAlignment.java | 4 +- .../GridLayoutRequiredIndicatorLocation.java | 6 +- .../components/gridlayout/UniformGridLayoutUI.java | 8 +- .../components/label/LabelPropertySourceValue.java | 2 +- .../vaadin/tests/components/label/LabelTest.java | 4 +- .../EmptySpaceOnPageAfterExpandedComponent.java | 6 +- .../listselect/ListSelectAddRemoveItems.java | 4 +- .../listselect/ListSelectAllowNewItem.java | 2 +- .../components/listselect/ListSelectJump.java | 4 +- .../listselect/ListSelectPrimaryStylename.java | 2 +- .../listselect/ListSelectPushSelectionChanges.java | 8 +- .../tests/components/listselect/ListSelects.java | 2 +- .../loginform/LoginFormWithMultipleWindows.java | 2 +- .../components/menubar/MenuBarInSplitPanel.java | 4 +- .../menubar/MenuBarRunsOutOfBrowser.java | 2 +- .../components/nativeselect/NativeSelectNull.java | 6 +- .../components/nativeselect/NativeSelects.java | 2 +- .../components/notification/Notifications.java | 4 +- .../notification/NotificationsHtmlAllowed.java | 8 +- .../notification/NotificationsWaiAria.java | 8 +- .../optiongroup/DisabledOptionGroupItems.java | 2 +- .../optiongroup/HtmlOptionGroupItems.java | 2 +- .../optiongroup/OptionGroupDisabled.java | 2 +- .../OptionGroupMultipleValueChange.java | 6 +- .../OptionGroupRetainFocusKeyboardValueChange.java | 6 +- .../tests/components/optiongroup/OptionGroups.java | 2 +- .../optiongroup/ReadOnlyOptionGroup.java | 2 +- .../components/orderedlayout/BoxLayoutTest.java | 24 +- .../components/orderedlayout/CaptionLeak.java | 4 +- .../components/orderedlayout/ErrorIndicator.java | 6 +- .../orderedlayout/ExpandChangeReattach.java | 2 +- ...orizontalLayoutFullsizeContentWithErrorMsg.java | 6 +- .../HorizontalLayoutVerticalAlign.java | 2 +- .../orderedlayout/HorizontalRelativeChildren.java | 2 +- .../HorizontalRelativeSizeWithoutExpand.java | 4 +- .../InsertComponentInHorizontalLayout.java | 2 +- .../orderedlayout/LayoutClickListenerTest.java | 10 +- .../orderedlayout/LayoutRenderTimeTest.java | 2 +- .../components/orderedlayout/LayoutResizeTest.java | 2 +- .../orderedlayout/OrderedLayoutCases.java | 6 +- .../orderedlayout/TooltipOnRequiredIndicator.java | 6 +- .../orderedlayout/VaadinTunesLayout.java | 6 +- .../VerticalLayoutFocusWithDOMChanges.java | 10 +- .../VerticalLayoutWidthCalculation.java | 8 +- .../VerticalLayoutWithEmptyLabel.java | 12 +- .../orderedlayout/VerticalRelativeChildren.java | 2 +- .../VerticalRelativeSizeWithoutExpand.java | 4 +- .../tests/components/panel/BasicPanelTest.java | 2 +- .../panel/PanelShouldRemoveActionHandler.java | 4 +- .../components/panel/UndefinedSizeScrollbars.java | 8 +- .../components/panel/WebkitScrollbarTest.java | 2 +- .../passwordfield/PasswordFieldTest.java | 8 +- .../popupview/PopupViewClickShortcut.java | 2 +- .../components/popupview/PopupViewNullValues.java | 6 +- .../popupview/PopupViewShortcutActionHandler.java | 4 +- .../popupview/PopupViewShouldCloseOnTabOut.java | 10 +- .../components/popupview/PopupViewWithRTE.java | 2 +- .../richtextarea/RichTextAreaEmptyString.java | 2 +- .../RichTextAreaRelativeHeightResize.java | 2 +- .../richtextarea/RichTextAreaScrolling.java | 2 +- .../components/richtextarea/RichTextAreaSize.java | 2 +- .../components/richtextarea/RichTextAreaTest.java | 2 +- .../RichTextAreaUpdateWhileTyping.java | 2 +- .../RichTextAreaWithKeyboardShortcuts.java | 6 +- .../components/richtextarea/RichTextAreas.java | 2 +- .../components/select/AbstractSelectTestCase.java | 8 +- .../select/ComboBoxAddWhileFiltering.java | 2 +- .../vaadin/tests/components/select/EnumSelect.java | 6 +- .../select/FocusListenerBreaksDropdownMenu.java | 2 +- .../tests/components/select/NativeSelects.java | 2 +- .../components/select/NullSelectionItemId.java | 2 +- .../components/select/OptionGroupBaseSelects.java | 10 +- .../components/select/SelectDisplaysOldValue.java | 8 +- .../select/SelectItemCaptionRefresh.java | 2 +- .../vaadin/tests/components/select/SelectTest.java | 2 +- .../components/select/SelectWithIntegers.java | 6 +- .../components/select/StylingPopupOpener.java | 2 +- .../select/TwinColSelectCaptionStyles.java | 2 +- .../tests/components/select/TwinColSelectTest.java | 2 +- .../tests/components/select/TwinColSelects.java | 2 +- .../components/slider/HiddenSliderHandle.java | 4 +- .../slider/SliderValueFromDataSource.java | 2 +- .../splitpanel/SplitPanelExtraScrollbars.java | 2 +- .../splitpanel/SplitPanelReversePosition.java | 2 +- .../splitpanel/SplitPanelWidthOnResize.java | 6 +- .../splitpanel/SplitPanelWithRichTextArea.java | 2 +- .../components/table/AddItemToEmptyTable.java | 4 +- .../tests/components/table/AddNonRenderedRow.java | 2 +- .../table/AddSelectionToRemovedRange.java | 2 +- .../tests/components/table/AsyncPushUpdates.java | 4 +- .../components/table/CellStyleGeneratorTest.java | 4 +- .../components/table/ClippedComponentsInTable.java | 10 +- .../table/CollapseIndicatorOverlapsColumn.java | 4 +- .../table/ColumnCollapsingAndColumnExpansion.java | 6 +- .../tests/components/table/ColumnExpandRatio.java | 6 +- .../table/ColumnExpandWithFixedColumns.java | 2 +- .../table/ColumnGeneratorAddingOrder.java | 4 +- .../components/table/ColumnHeaderAlignments.java | 10 +- .../tests/components/table/ColumnReorderEvent.java | 8 +- .../table/ColumnReorderingWithManyColumns.java | 2 +- .../tests/components/table/ColumnResizeEvent.java | 10 +- .../tests/components/table/ColumnWidths.java | 6 +- ...nWidthsAfterChangeTableColumnsCountOrOrder.java | 4 +- .../ContainerChangeWithPartlySamePropertyIds.java | 6 +- .../components/table/ContainerSizeChange.java | 6 +- .../table/ContainerSizeChangeDuringTablePaint.java | 8 +- .../tests/components/table/ContextMenuSize.java | 4 +- .../components/table/CtrlShiftMultiselect.java | 10 +- .../components/table/DelayedColumnLayouting.java | 2 +- .../components/table/DisabledSortingTable.java | 2 +- .../table/DisabledTableKeyboardNavigation.java | 8 +- ...isabledTableShouldNotSendPageLengthUpdates.java | 2 +- .../tests/components/table/DndEmptyTable.java | 2 +- .../components/table/DndTableTargetDetails.java | 6 +- .../tests/components/table/DoublesInTable.java | 32 +- .../tests/components/table/EditableModeChange.java | 12 +- .../tests/components/table/EditableTableFocus.java | 2 +- .../tests/components/table/EditableTableLeak.java | 16 +- .../components/table/EmptyRowsWhenScrolling.java | 6 +- .../vaadin/tests/components/table/EmptyTable.java | 2 +- .../tests/components/table/ExpandingContainer.java | 10 +- .../ExpandingContainerVisibleRowRaceCondition.java | 2 +- .../tests/components/table/FixedHeightTable.java | 2 +- .../components/table/FocusOnSelectedItem.java | 2 +- .../com/vaadin/tests/components/table/Footer.java | 8 +- .../vaadin/tests/components/table/FooterClick.java | 10 +- .../vaadin/tests/components/table/HeaderClick.java | 10 +- .../table/HeaderFooterClickLeftRightMiddle.java | 16 +- .../table/HeaderPositionWhenSorting.java | 4 +- .../table/HeaderRightClickAfterDrag.java | 4 +- .../tests/components/table/HeaderSyncOnScroll.java | 2 +- .../components/table/HeaderUpdateWhenNoRows.java | 4 +- .../table/HiddenColumnsExpandRatios.java | 4 +- .../components/table/HiddenComponentCells.java | 4 +- .../tests/components/table/HugeRowCount.java | 14 +- .../tests/components/table/ItemClickEvents.java | 8 +- .../vaadin/tests/components/table/KeyControl.java | 10 +- .../KeyboardNavigationWithChangingContent.java | 4 +- .../table/LabelEmbeddedClickThroughForTable.java | 4 +- .../components/table/LargeSelectionCausesNPE.java | 14 +- .../tests/components/table/LastColumnNegative.java | 4 +- .../components/table/LeftColumnAlignment.java | 6 +- .../tests/components/table/LongMultiselect.java | 4 +- .../tests/components/table/MemoryLeakTable.java | 4 +- .../tests/components/table/MissingScrollbar.java | 6 +- .../components/table/ModifyContainerProperty.java | 4 +- .../table/MultiClickingItemThatDetachesTable.java | 4 +- .../table/MultiSelectWithNotIdentityEqualIds.java | 10 +- .../table/MultiSelectWithRemovedRow.java | 4 +- .../table/NotselectablePaintSelections.java | 4 +- .../tests/components/table/OddEvenRowStyling.java | 2 +- .../tests/components/table/PopupViewInTable.java | 2 +- .../table/ProgrammaticUnselectInRange.java | 6 +- .../components/table/PropertyValueChange.java | 26 +- .../table/RefreshRenderedCellsOnlyIfAttached.java | 2 +- .../tests/components/table/ReloadWidgets.java | 4 +- .../tests/components/table/RemoveItemOnClick.java | 12 +- .../tests/components/table/RowAdditionTest.java | 6 +- .../tests/components/table/RowGenerators.java | 12 +- .../table/RowUpdateShouldRetainContextMenu.java | 6 +- .../table/SafariRenderingBugWhiteSpace.java | 6 +- .../components/table/ScrollCausesRequestLoop.java | 6 +- .../table/ScrollDetachSynchronization.java | 2 +- .../table/SelectAllConstantViewport.java | 2 +- .../tests/components/table/SelectAllRows.java | 2 +- .../tests/components/table/SelectableEditable.java | 2 +- .../table/SelectingItemScrollsRight.java | 2 +- .../table/SetCurrentPageFirstItemId.java | 2 +- .../table/SetCurrentPageFirstItemIndex.java | 6 +- .../table/SetDataSourceWithPropertyIds.java | 6 +- .../table/SetPageFirstItemLoadsNeededRowsOnly.java | 4 +- .../tests/components/table/ShowLastItem.java | 2 +- .../tests/components/table/SortLabelsInTable.java | 4 +- .../tests/components/table/SortLongTable.java | 4 +- .../components/table/SortableHeaderStyles.java | 8 +- .../table/TableAfterRemovingExpandRatios.java | 2 +- .../table/TableAndBrowserContextMenu.java | 4 +- .../tests/components/table/TableBlurFocus.java | 2 +- .../table/TableCacheBuildEfficiency.java | 4 +- .../table/TableCacheMinimizingOnFetchRows.java | 4 +- .../table/TableChildMeasurementHint.java | 8 +- .../TableClickAndDragOnIconAndComponents.java | 20 +- .../table/TableClickValueChangeInteraction.java | 6 +- .../components/table/TableColumnAddAndResize.java | 2 +- .../table/TableColumnResizeContentsWidth.java | 8 +- .../table/TableColumnWidthsAndExpandRatios.java | 2 +- .../table/TableColumnWidthsAndSorting.java | 2 +- .../tests/components/table/TableContextMenu.java | 2 +- .../components/table/TableContextMenuOnField.java | 8 +- .../components/table/TableContextMenuTouch.java | 2 +- .../components/table/TableDropIndicatorValo.java | 2 +- .../components/table/TableExtraScrollbars.java | 10 +- .../components/table/TableFirstRowFlicker.java | 8 +- .../table/TableFocusOnRefreshRowCache.java | 2 +- .../components/table/TableHeaderShifting.java | 2 +- .../tests/components/table/TableHeaderZoom.java | 4 +- .../table/TableHeightWhenHidingHeaders.java | 4 +- .../table/TableInSubWindowMemoryLeak.java | 2 +- .../tests/components/table/TableInTabsheet.java | 4 +- .../table/TableItemDescriptionGeneratorUI.java | 14 +- .../tests/components/table/TableItemIcon.java | 4 +- .../vaadin/tests/components/table/TableJumpUI.java | 8 +- .../components/table/TableLastRowMissing.java | 10 +- .../table/TableMatchesMouseDownMouseUpElement.java | 4 +- .../table/TableModifcationsWhenScrolledRight.java | 2 +- .../table/TableMoveFocusWithSelection.java | 2 +- .../components/table/TableMultiSelectSimple.java | 6 +- .../components/table/TableNavigationPageDown.java | 2 +- .../table/TablePageLengthCalculation.java | 2 +- .../components/table/TablePageLengthUpdate.java | 8 +- .../table/TableParentEnabledStateChange.java | 4 +- .../components/table/TableReduceContainerSize.java | 8 +- .../TableRemovedQuicklySendsInvalidRpcCalls.java | 2 +- .../table/TableRepaintWhenMadeVisibile.java | 2 +- ...ableRepairsScrollPositionOnReAddingAllRows.java | 4 +- .../components/table/TableRequiredIndicator.java | 6 +- .../tests/components/table/TableRowHeight.java | 8 +- .../tests/components/table/TableRowHeight2.java | 4 +- .../tests/components/table/TableRowHeight3.java | 4 +- .../components/table/TableRowScrolledBottom.java | 2 +- .../components/table/TableScrollAfterAddRow.java | 10 +- .../tests/components/table/TableScrollOnFocus.java | 2 +- .../components/table/TableScrollUpOnSelect.java | 12 +- .../table/TableScrollingWithSQLContainer.java | 14 +- .../components/table/TableScrollsOnSelection.java | 6 +- .../components/table/TableSelectPagingOff.java | 4 +- .../components/table/TableSetUndefinedSize.java | 2 +- .../table/TableShouldNotEatValueChanges.java | 8 +- .../tests/components/table/TableSingleSelect.java | 6 +- .../components/table/TableSizeInTabsheet.java | 2 +- .../tests/components/table/TableSorting.java | 8 +- .../components/table/TableSortingIndicator.java | 6 +- .../table/TableSortingStopsWorkingOnChrome.java | 12 +- .../tests/components/table/TableSqlContainer.java | 14 +- .../table/TableToggleColumnVisibility.java | 2 +- .../table/TableToggleColumnVisibilityWidth.java | 2 +- .../components/table/TableToggleVisibility.java | 4 +- .../components/table/TableTooManyColumns.java | 4 +- .../tests/components/table/TableUndefinedSize.java | 12 +- .../components/table/TableUnregisterComponent.java | 26 +- .../table/TableVisibleColumnsUpdate.java | 2 +- .../components/table/TableWidthItemRemove.java | 2 +- .../TableWithBrokenGeneratorAndContainer.java | 12 +- .../components/table/TableWithChildComponents.java | 6 +- ...TableWithContainerRequiringEqualsForItemId.java | 6 +- .../table/TableWithCustomConverterFactory.java | 16 +- .../components/table/TableWithManyColumns.java | 6 +- .../table/TableWithNoncollapsibleColumns.java | 2 +- .../tests/components/table/TableWithPolling.java | 2 +- .../com/vaadin/tests/components/table/Tables.java | 28 +- .../tests/components/table/TabletContextMenu.java | 2 +- .../components/table/TestCurrentPageFirstItem.java | 8 +- .../components/table/TextFieldRelativeWidth.java | 14 +- .../table/TextFieldValueGoesMissing.java | 2 +- .../table/TooManySetColumnCollapsedCalls.java | 2 +- .../components/table/UncollapsedCollumnWidth.java | 2 +- .../table/UnnecessaryScrollbarWhenZooming.java | 2 +- .../components/table/UpdateTableWhenUnfocused.java | 8 +- .../table/ValueAfterClearingContainer.java | 6 +- .../components/table/ViewPortCalculation.java | 6 +- .../components/table/WideSelectableTable.java | 2 +- .../tabsheet/ScrollbarsInNestedTabsheets.java | 4 +- .../components/tabsheet/TabKeyboardNavigation.java | 8 +- .../tabsheet/TabKeyboardNavigationWaiAria.java | 4 +- .../tabsheet/TabSheetDiscardsMovedComponents.java | 4 +- .../tests/components/tabsheet/TabSheetIcons.java | 4 +- .../tabsheet/TabSheetWithHasComponent.java | 6 +- .../tests/components/tabsheet/TabsheetNPE.java | 2 +- .../tabsheet/TabsheetShouldUpdateHeight.java | 4 +- .../tabsheet/VerticalScrollbarPosition.java | 2 +- .../tests/components/textarea/ScrollCursor.java | 2 +- .../textarea/TextAreaCursorPosition.java | 18 +- .../components/textarea/TextAreaSizeResetted.java | 2 +- .../tests/components/textarea/TextAreaTest.java | 2 +- .../components/textarea/TextDisappearsOnBlur.java | 2 +- .../vaadin/tests/components/textarea/Wordwrap.java | 2 +- .../components/textfield/AutomaticImmediate.java | 8 +- .../components/textfield/BigDecimalTextField.java | 10 +- .../EnterShortcutMaySendInputPromptAsValue.java | 8 +- .../tests/components/textfield/EnumTextField.java | 10 +- .../tests/components/textfield/IE6Cursor.java | 6 +- .../components/textfield/InputPromptGetText.java | 4 +- .../textfield/LocaleChangeOnReadOnlyField.java | 10 +- .../components/textfield/RequiredTextField.java | 4 +- .../textfield/SelectionAndCursorPosition.java | 2 +- .../components/textfield/SizedTextFields.java | 10 +- .../components/textfield/TextChangeEvents.java | 2 +- .../components/textfield/TextChangeEvents2.java | 28 +- ...extChangeEventsWithNonImmediateValueChange.java | 8 +- ...extChangeListenerChangingNonTextProperties.java | 6 +- .../textfield/TextChangeListenerLosesFocus.java | 12 +- .../textfield/TextChangeTimeoutAfterDetach.java | 6 +- .../textfield/TextFieldEagerRepaint.java | 8 +- .../textfield/TextFieldInLayoutInTable.java | 8 +- .../TextFieldInputPromptAndClickShortcut.java | 4 +- .../TextFieldMaxLengthRemovedFromDOM.java | 4 +- .../textfield/TextFieldPrimaryStyleName.java | 4 +- .../tests/components/textfield/TextFieldTest.java | 8 +- .../TextFieldWithDataSourceAndInputPrompt.java | 8 +- .../textfield/TextFieldWithProperty.java | 6 +- .../textfield/TextFieldWithPropertyFormatter.java | 8 +- .../components/tree/CtrlShiftMultiselect.java | 10 +- .../components/tree/DndTreeTargetDetails.java | 2 +- .../tests/components/tree/ExpandCollapseTree.java | 10 +- .../tests/components/tree/ItemStyleGenerator.java | 2 +- .../components/tree/PreselectedTreeVisible.java | 2 +- .../components/tree/SelectItemAfterRemove.java | 2 +- .../vaadin/tests/components/tree/SimpleTree.java | 10 +- .../tests/components/tree/TreeConnectors.java | 8 +- .../tests/components/tree/TreeContainerChange.java | 10 +- .../components/tree/TreeDragAndDropFromTable.java | 12 +- .../tests/components/tree/TreeFiltering.java | 6 +- .../tests/components/tree/TreeFocusGaining.java | 10 +- .../components/tree/TreeHorizontalResize.java | 6 +- .../components/tree/TreeHtmlContentAllowed.java | 2 +- .../tests/components/tree/TreeIconUpdate.java | 6 +- .../components/tree/TreeItemClickListening.java | 2 +- .../tests/components/tree/TreeItemDoubleClick.java | 2 +- .../tree/TreeItemSelectionWithoutImmediate.java | 2 +- .../tree/TreeKeyboardNavigationScrolls.java | 6 +- .../tree/TreeKeyboardNavigationToNone.java | 2 +- .../tree/TreeKeyboardNavigationValidators.java | 6 +- .../components/tree/TreeNodeCaptionWrapping.java | 2 +- .../tests/components/tree/TreePerformanceTest.java | 4 +- .../tests/components/tree/TreeScrolling.java | 10 +- .../components/tree/TreeScrollingOnRightClick.java | 2 +- .../components/tree/TreeScrollingOnSelection.java | 4 +- .../vaadin/tests/components/tree/TreeToolTips.java | 8 +- .../tests/components/tree/TreeWithIcons.java | 2 +- .../com/vaadin/tests/components/tree/Trees.java | 28 +- .../components/treetable/AddNodesOnExpand.java | 8 +- .../treetable/ChangeDataSourcePageLengthZero.java | 6 +- .../treetable/ComponentsInTreeTable.java | 2 +- .../treetable/DisappearingComponents.java | 2 +- .../components/treetable/DynamicallyModified.java | 8 +- .../treetable/ExpandAnimationsInChameleon.java | 6 +- .../components/treetable/KeepAllItemsVisible.java | 16 +- .../components/treetable/MinimalWidthColumns.java | 2 +- .../components/treetable/ProgrammaticCollapse.java | 2 +- .../components/treetable/ProgrammaticSelect.java | 8 +- .../treetable/RemoveAllItemsRefresh.java | 8 +- .../components/treetable/RowHeightWithoutRows.java | 4 +- .../treetable/TreeTableCacheOnPartialUpdates.java | 32 +- .../TreeTableContainerHierarchicalWrapper.java | 10 +- .../treetable/TreeTableExtraScrollbar.java | 4 +- .../TreeTableExtraScrollbarWithChildren.java | 4 +- .../treetable/TreeTableInternalError.java | 6 +- .../TreeTableItemDescriptionGeneratorUI.java | 4 +- .../treetable/TreeTableModifyAndSetCollapsed.java | 6 +- .../components/treetable/TreeTableOutOfSync.java | 4 +- .../treetable/TreeTablePartialUpdates.java | 6 +- .../TreeTablePartialUpdatesPageLength0.java | 6 +- .../treetable/TreeTableRowGenerator.java | 8 +- .../treetable/TreeTableRowHeaderMode.java | 4 +- .../components/treetable/TreeTableRowIcons.java | 2 +- .../treetable/TreeTableScrollOnExpand.java | 2 +- .../treetable/TreeTableSetCollapsed.java | 2 +- .../tests/components/treetable/TreeTableTest.java | 22 +- .../components/twincolselect/TwinColSelects.java | 2 +- .../components/ui/ComboboxSelectedItemText.java | 2 +- .../ui/LoadingIndicatorConfigurationTest.java | 18 +- .../components/ui/TextAreaEventPropagation.java | 2 +- .../com/vaadin/tests/components/ui/UIPolling.java | 10 +- .../tests/components/uitest/BackButtonTest.java | 8 +- .../tests/components/uitest/ThemeTestUI.java | 6 +- .../components/uitest/components/FormsCssTest.java | 6 +- .../uitest/components/SelectsCssTest.java | 14 +- .../uitest/components/TablesCssTest.java | 2 +- .../uitest/components/TextFieldsCssTest.java | 4 +- .../components/uitest/components/TreeCssTest.java | 6 +- .../uitest/components/TreeTableCssTest.java | 6 +- .../upload/DragAndDropUploadAndInteractions.java | 2 +- .../tests/components/upload/ForceSubmit.java | 4 +- .../window/AttachShouldBeCalledForSubWindows.java | 2 +- .../components/window/CloseModalSubWindow.java | 4 +- .../window/ComboboxScrollableWindow.java | 2 +- .../tests/components/window/DownloadAndUpdate.java | 2 +- .../tests/components/window/ExecuteJavaScript.java | 2 +- .../tests/components/window/GridInWindow.java | 4 +- .../tests/components/window/LazyWindowResize.java | 2 +- .../components/window/LegacyWindowOpenTest.java | 2 +- .../window/ModalWindowInitialLocation.java | 2 +- .../components/window/ModalWindowNativeSelect.java | 2 +- .../window/OpenModalWindowAndFocusField.java | 2 +- .../tests/components/window/SubWindowFocus.java | 8 +- .../window/SubWindowFocusAndBlurListeners.java | 6 +- .../tests/components/window/SubWindowOrder.java | 4 +- .../window/SubWindowWithUndefinedHeight.java | 2 +- .../vaadin/tests/components/window/SubWindows.java | 12 +- .../components/window/WindowCloseShortcuts.java | 2 +- .../window/WindowMaximizeRestoreTest.java | 8 +- .../window/WindowScrollingComponentIntoView.java | 4 +- .../tests/components/window/WindowScrollingUp.java | 4 +- .../window/WindowShouldRemoveActionHandler.java | 4 +- .../containers/BeanItemContainerFilteringTest.java | 16 +- .../tests/containers/BeanItemContainerTest.java | 2 +- .../containers/HierarchicalWrapperOrdering.java | 14 +- .../containers/IndexedContainerFilteringTest.java | 16 +- .../containers/TableWithFileSystemContainer.java | 4 +- .../vaadin/tests/containers/TestItemSorter.java | 8 +- .../FileSystemContainerInTreeTable.java | 18 +- .../sqlcontainer/ComboBoxUpdateProblem.java | 4 +- .../containers/sqlcontainer/DatabaseHelper.java | 10 +- .../sqlcontainer/MassInsertMemoryLeakTestApp.java | 10 +- .../sqlcontainer/SqlcontainertableApplication.java | 8 +- .../TableQueryWithNonUniqueFirstPrimaryKey.java | 18 +- .../BrowserContextMenuInSubComponent.java | 2 +- .../tests/contextclick/GridContextClick.java | 12 +- .../tests/contextclick/TableContextClick.java | 6 +- .../tests/contextclick/TreeContextClick.java | 4 +- .../tests/contextclick/TreeTableContextClick.java | 6 +- .../converter/ConverterThatEnforcesAFormat.java | 8 +- ...ngToDoubleConverterWithThreeFractionDigits.java | 4 +- .../com/vaadin/tests/dd/AcceptFromComponent.java | 2 +- .../src/main/java/com/vaadin/tests/dd/DDTest1.java | 16 +- .../src/main/java/com/vaadin/tests/dd/DDTest2.java | 22 +- .../src/main/java/com/vaadin/tests/dd/DDTest4.java | 8 +- .../src/main/java/com/vaadin/tests/dd/DDTest6.java | 16 +- .../src/main/java/com/vaadin/tests/dd/DDTest7.java | 8 +- .../src/main/java/com/vaadin/tests/dd/DDTest8.java | 12 +- .../vaadin/tests/dd/NotPaintedAcceptSource.java | 10 +- .../tests/dd/NotPaintedAcceptSourceInTabSheet.java | 10 +- .../com/vaadin/tests/dd/ScrolledDropTarget.java | 8 +- .../java/com/vaadin/tests/dd/TreeDragStart.java | 22 +- .../tests/declarative/DeclarativeEditor.java | 10 +- .../com/vaadin/tests/declarative/PotusCrud.java | 12 +- .../com/vaadin/tests/declarative/PotusForm.java | 10 +- .../vaadin/tests/fieldgroup/AbstractBasicCrud.java | 60 +- .../fieldgroup/AbstractBeanFieldGroupTest.java | 4 +- .../com/vaadin/tests/fieldgroup/BasicCrudGrid.java | 10 +- .../tests/fieldgroup/BasicCrudGridEditorRow.java | 16 +- .../vaadin/tests/fieldgroup/BasicCrudTable.java | 8 +- .../vaadin/tests/fieldgroup/BasicPersonForm.java | 42 +- .../com/vaadin/tests/fieldgroup/ComplexPerson.java | 2 +- .../java/com/vaadin/tests/fieldgroup/DateForm.java | 26 +- .../fieldgroup/FieldBinderWithBeanValidation.java | 22 +- .../FormBuilderWithNestedProperties.java | 16 +- .../tests/fieldgroup/FormWithNestedProperties.java | 18 +- .../tests/fieldgroup/MultipleValidationErrors.java | 12 +- .../java/com/vaadin/tests/fields/TabIndexes.java | 30 +- .../java/com/vaadin/tests/fonticon/FontIcons.java | 22 +- .../vaadin/tests/integration/EmbedSizeTest.java | 2 +- .../integration/IntegrationTestApplication.java | 10 +- .../vaadin/tests/integration/JSR286Portlet.java | 4 +- ...tletSizeInLiferayFreeformLayoutApplication.java | 2 +- .../tests/integration/ServletIntegrationUI.java | 8 +- .../vaadin/tests/layouts/CaptionsInLayouts.java | 28 +- .../tests/layouts/CaptionsInLayoutsWaiAria.java | 22 +- .../ComplexGLColumnExpansionWithColSpan.java | 6 +- .../tests/layouts/CssLayoutRemoveComponent.java | 8 +- .../CssLayoutRemoveComponentWithCaption.java | 6 +- .../vaadin/tests/layouts/DeepComponentTrees.java | 4 +- .../layouts/FormLayoutWithInvisibleComponent.java | 2 +- .../layouts/GridLayoutExpandRatioModification.java | 6 +- .../tests/layouts/GridLayoutInsidePanel2.java | 2 +- .../com/vaadin/tests/layouts/HtmlInCaption.java | 8 +- .../tests/layouts/LayoutPerformanceTests.java | 6 +- .../MovingComponentsWhileOldParentInvisible.java | 6 +- .../vaadin/tests/layouts/MovingInvisibleField.java | 6 +- .../vaadin/tests/layouts/OrderedLayoutBasics.java | 60 +- .../layouts/OrderedLayoutCSSCompatibility.java | 6 +- .../layouts/RelativeSizeInUndefinedCssLayout.java | 4 +- .../tests/layouts/TestLayoutClickListeners.java | 2 +- .../tests/layouts/TestLayoutPerformance.java | 6 +- .../tests/layouts/TreeWithBordersInLayout.java | 4 +- .../VerticalLayoutExpandRatioModification.java | 6 +- .../gridlayout/GridLayoutMoveComponent.java | 4 +- .../layouts/gridlayout/GridLayoutWidthChange.java | 4 +- .../layouts/layouttester/BaseAddReplaceMove.java | 6 +- .../tests/layouts/layouttester/BaseCaption.java | 2 +- .../tests/layouts/layouttester/BaseIcon.java | 6 +- .../layouts/layouttester/BaseLayoutExpand.java | 2 +- .../layouttester/BaseLayoutForSpacingMargin.java | 2 +- .../layouts/layouttester/BaseLayoutTestUI.java | 6 +- .../tests/layouts/layouttester/BaseRegError.java | 8 +- .../GridLayout/GridAddReplaceMove.java | 6 +- .../layouttester/GridLayout/GridCaption.java | 2 +- .../layouts/layouttester/GridLayout/GridIcon.java | 6 +- .../layouttester/GridLayout/GridLayoutExpand.java | 2 +- .../GridLayout/GridLayoutMarginSpacing.java | 2 +- .../GridLayout/GridLayoutRegError.java | 8 +- .../broadcastingmessages/BroadcasterUI.java | 2 +- .../tests/minitutorials/v70/CookieMonsterUI.java | 4 +- .../tests/minitutorials/v70/SimpleLoginView.java | 20 +- .../v71beta/CSSInjectWithColorpicker.java | 14 +- .../tests/minitutorials/v7_3/ThemeChangeUI.java | 6 +- .../v7_4/ConfiguringColumnWidths.java | 22 +- .../v7_4/ConfiguringGridHeadersAndFooters.java | 10 +- .../minitutorials/v7_4/FormattingDataInGrid.java | 20 +- .../minitutorials/v7_4/GridExampleHelper.java | 2 +- .../v7_4/ShowingInlineDataInGrid.java | 4 +- .../v7_4/UsingGridWithAContainer.java | 4 +- .../v7_5/ShowingExtraDataForRows.java | 8 +- .../minitutorials/v7a1/AutoGeneratingForm.java | 6 +- .../minitutorials/v7a1/CreatingPreserveState.java | 4 +- .../v7a1/CustomConverterFactoryUI.java | 4 +- .../v7a1/FormUsingExistingLayout.java | 14 +- .../tests/minitutorials/v7a1/FormatTableValue.java | 8 +- .../v7a1/IntegerTextFieldDataSource.java | 8 +- .../v7a1/IntegerTextFieldStandalone.java | 10 +- .../minitutorials/v7a1/MyConverterFactory.java | 10 +- .../v7a1/MyStringToDoubleConverter.java | 4 +- .../minitutorials/v7a1/StringMyTypeConverter.java | 10 +- .../minitutorials/v7a1/UsingBeanValidation.java | 10 +- .../v7b5/SettingReadingSessionAttributesUI.java | 4 +- .../vaadin/tests/minitutorials/v7b9/LoginView.java | 8 +- .../tests/minitutorials/v7b9/SettingsView.java | 14 +- .../com/vaadin/tests/navigator/NavigatorTest.java | 4 +- .../tests/performance/BasicPerformanceTest.java | 2 +- .../main/java/com/vaadin/tests/push/BasicPush.java | 2 +- .../com/vaadin/tests/push/PushConfiguration.java | 2 +- .../com/vaadin/tests/push/PushConfigurator.java | 12 +- .../com/vaadin/tests/push/PushErrorHandling.java | 6 +- .../java/com/vaadin/tests/push/PushLargeData.java | 14 +- .../java/com/vaadin/tests/push/RoundTripTest.java | 6 +- .../vaadin/tests/push/SendMultibyteCharacters.java | 2 +- .../com/vaadin/tests/push/TablePushStreaming.java | 8 +- .../com/vaadin/tests/rpclogger/RPCLoggerUI.java | 2 +- .../java/com/vaadin/tests/themes/ButtonsTest.java | 2 +- .../com/vaadin/tests/themes/CSSInjectTest.java | 2 +- .../tests/themes/LegacyComponentThemeChange.java | 4 +- .../vaadin/tests/themes/ThemeChangeOnTheFly.java | 2 +- .../com/vaadin/tests/themes/valo/CalendarTest.java | 64 +- .../com/vaadin/tests/themes/valo/CheckBoxes.java | 4 +- .../tests/themes/valo/CollapsibleTableColumn.java | 4 +- .../com/vaadin/tests/themes/valo/ColorPickers.java | 4 +- .../com/vaadin/tests/themes/valo/ComboBoxes.java | 2 +- .../com/vaadin/tests/themes/valo/CommonParts.java | 12 +- .../com/vaadin/tests/themes/valo/DateFields.java | 6 +- .../tests/themes/valo/FormLayoutInsideTable.java | 2 +- .../java/com/vaadin/tests/themes/valo/Forms.java | 22 +- .../com/vaadin/tests/themes/valo/GridDisabled.java | 4 +- .../vaadin/tests/themes/valo/NativeSelects.java | 6 +- .../tests/themes/valo/TableSortIndicator.java | 2 +- .../tests/themes/valo/TableWithEmptyCaption.java | 2 +- .../java/com/vaadin/tests/themes/valo/Tables.java | 38 +- .../com/vaadin/tests/themes/valo/TextFields.java | 48 +- .../java/com/vaadin/tests/themes/valo/Trees.java | 6 +- .../vaadin/tests/themes/valo/ValoMiscTests.java | 2 +- .../com/vaadin/tests/themes/valo/ValoThemeUI.java | 16 +- .../java/com/vaadin/tests/tooltip/LongTooltip.java | 14 +- .../vaadin/tests/tooltip/TooltipConfiguration.java | 20 +- .../com/vaadin/tests/tooltip/TooltipInWindow.java | 6 +- .../vaadin/tests/tooltip/TooltipWidthUpdating.java | 18 +- .../tests/tooltip/ValidatorCaptionTooltip.java | 10 +- .../com/vaadin/tests/util/AlwaysFailValidator.java | 4 +- .../tests/util/CheckBoxWithPropertyDataSource.java | 4 +- .../java/com/vaadin/tests/util/LargeContainer.java | 12 +- .../com/vaadin/tests/util/PersonContainer.java | 2 +- .../com/vaadin/tests/util/SampleDirectory.java | 2 +- .../main/java/com/vaadin/tests/util/TestUtils.java | 6 +- .../tests/validation/FieldErrorIndication.java | 42 +- .../ValidationOfRequiredEmptyFields.java | 22 +- .../server/DelegateWithoutStateClassComponent.java | 2 +- .../tests/widgetset/server/ExtraSuperTextArea.java | 2 +- .../widgetset/server/OverriddenDecendants.java | 2 +- .../tests/widgetset/server/SuperTextArea.java | 2 +- .../server/grid/GridClientColumnRenderers.java | 4 +- .../data/util/sqlcontainer/SQLTestsConstants.java | 154 + .../application/TerminalErrorNotificationTest.java | 2 +- .../application/VaadinSessionAttributeTest.java | 2 +- .../tests/applicationcontext/CloseSessionTest.java | 2 +- .../applicationservlet/SystemMessagesTest.java | 2 +- .../NoLayoutUpdateWhichNeedsLayoutTest.java | 6 +- .../components/calendar/CalendarDisabledTest.java | 2 +- .../calendar/CalendarHtmlInEventsTest.java | 4 +- .../components/calendar/CalendarReadOnlyTest.java | 2 +- .../colorpicker/DefaultCaptionWidthTest.java | 2 +- .../components/combobox/ComboBoxBorderTest.java | 2 +- .../components/combobox/ComboBoxClickIconTest.java | 2 +- .../combobox/ComboBoxClosePopupRetainTextTest.java | 2 +- .../ComboBoxCombinedWithEnterShortcutTest.java | 2 +- .../ComboBoxEmptyItemsKeyboardNavigationTest.java | 2 +- .../combobox/ComboBoxIdenticalItemsTest.java | 2 +- .../combobox/ComboBoxInputPromptTest.java | 2 +- .../ComboBoxItemAddingWithFocusListenerTest.java | 2 +- .../components/combobox/ComboBoxItemIconTest.java | 2 +- .../combobox/ComboBoxItemStyleGeneratorTest.java | 2 +- .../combobox/ComboBoxLargeIconsTest.java | 4 +- .../combobox/ComboBoxMouseSelectEnterTest.java | 2 +- .../combobox/ComboBoxNoTextInputTest.java | 5 +- .../combobox/ComboBoxOnSmallScreenTest.java | 2 +- .../combobox/ComboBoxPopupWhenBodyScrollsTest.java | 2 +- .../combobox/ComboBoxResetValueTest.java | 2 +- .../ComboBoxScrollingToPageDisabledTest.java | 2 +- .../combobox/ComboBoxScrollingWithArrowsTest.java | 2 +- .../components/combobox/ComboBoxSelectingTest.java | 2 +- .../ComboBoxSelectingWithNewItemsAllowedTest.java | 2 +- .../ComboBoxSetNullWhenNewItemsAllowedTest.java | 2 +- .../combobox/ComboBoxSuggestionPageLengthTest.java | 2 +- .../combobox/ComboBoxSuggestionPopupCloseTest.java | 2 +- .../ComboBoxSuggestionPopupWidthLegacyTest.java | 4 +- ...ComboBoxSuggestionPopupWidthPercentageTest.java | 4 +- .../ComboBoxSuggestionPopupWidthPixelsTest.java | 4 +- .../combobox/ComboBoxSuggestionPopupWidthTest.java | 4 +- .../combobox/ComboBoxValueInputTest.java | 2 +- ...electedValueBeyondTheFirstDropdownPageTest.java | 2 +- .../combobox/ComboboxMenuBarAutoopenTest.java | 2 +- .../combobox/ComboboxPopupScrollingTest.java | 3 +- .../combobox/ComboboxStyleChangeWidthTest.java | 2 +- .../components/combobox/CustomComboBoxElement.java | 40 - .../combobox/FilteringTurkishLocaleTest.java | 4 +- .../datefield/LegacyDateFieldIsValidTest.java | 4 +- .../PopupDateFieldValueChangeEventsTest.java | 2 +- .../formlayout/FormLayoutResizingTest.java | 2 +- .../TableInFormLayoutCausesScrollingTest.java | 2 +- .../tests/components/grid/CustomRendererTest.java | 5 +- .../grid/GridAddAndRemoveDataOnInitTest.java | 7 +- .../tests/components/grid/GridAddRowTest.java | 5 +- .../grid/GridCellFocusOnResetSizeTest.java | 2 +- .../components/grid/GridCheckBoxDisplayTest.java | 13 +- .../tests/components/grid/GridClientRenderers.java | 4 +- .../tests/components/grid/GridColspansTest.java | 11 +- .../components/grid/GridColumnAutoExpandTest.java | 3 +- .../grid/GridColumnWidthRecalculationTest.java | 5 +- .../grid/GridColumnWidthsWithoutDataTest.java | 13 +- .../grid/GridCustomSelectionModelTest.java | 5 +- .../components/grid/GridDataSourceResetTest.java | 5 +- .../grid/GridDefaultSelectionModeTest.java | 11 +- .../components/grid/GridDetailsDetachTest.java | 13 +- .../grid/GridDetailsLayoutExpandTest.java | 5 +- .../components/grid/GridDetailsLocationTest.java | 6 +- .../components/grid/GridDetailsWidthTest.java | 7 +- .../grid/GridDragSelectionWhileScrolledTest.java | 5 +- .../grid/GridEditingWithNoScrollBarsTest.java | 5 +- .../grid/GridEditorConverterNotFoundTest.java | 5 +- .../components/grid/GridEditorCustomFieldTest.java | 6 +- .../grid/GridEditorFrozenColumnsUITest.java | 5 +- .../components/grid/GridEditorMultiselectTest.java | 5 +- .../tests/components/grid/GridEditorUITest.java | 13 +- .../grid/GridExtensionCommunicationTest.java | 3 +- .../grid/GridGeneratedPropertiesTest.java | 11 +- .../grid/GridHeaderFooterComponentsTest.java | 20 +- .../grid/GridHeaderFormatChangeTest.java | 9 +- .../components/grid/GridHeaderStyleNamesTest.java | 4 +- .../tests/components/grid/GridHeightTest.java | 7 +- .../tests/components/grid/GridInTabSheetTest.java | 5 +- .../components/grid/GridInWindowResizeTest.java | 5 +- .../grid/GridInitiallyHiddenColumnsTest.java | 5 +- .../components/grid/GridItemSetChangeTest.java | 4 +- .../components/grid/GridLayoutDetailsRowTest.java | 5 +- .../grid/GridMultiSelectionOnInitTest.java | 7 +- .../grid/GridMultiSelectionScrollBarTest.java | 3 +- .../components/grid/GridRendererChangeTest.java | 5 +- .../components/grid/GridReplaceContainerTest.java | 5 +- .../components/grid/GridResizeAndScrollTest.java | 5 +- .../grid/GridResizeHiddenColumnTest.java | 5 +- .../components/grid/GridRowHeightChangeTest.java | 7 +- .../grid/GridScrollToLineWhileResizingTest.java | 3 +- .../components/grid/GridSelectAllCellTest.java | 5 +- .../components/grid/GridSidebarPositionTest.java | 9 +- .../components/grid/GridSingleColumnTest.java | 3 +- .../components/grid/GridSortIndicatorTest.java | 5 +- .../components/grid/GridSpacerDecoClipTest.java | 7 +- .../grid/GridSubPixelProblemWrappingTest.java | 4 +- .../components/grid/GridSwitchRenderersTest.java | 5 +- .../tests/components/grid/GridThemeChangeTest.java | 7 +- .../tests/components/grid/GridThemeUITest.java | 11 +- .../components/grid/GridWidthIncreaseTest.java | 5 +- .../grid/GridWithBrokenRendererTest.java | 5 +- .../components/grid/GridWithLabelEditorTest.java | 3 +- .../components/grid/InitialFrozenColumnsTest.java | 3 +- .../components/grid/JavaScriptRenderersTest.java | 5 +- .../tests/components/grid/LegacyGridElement.java | 31 - .../tests/components/grid/NullHeadersTest.java | 5 +- .../tests/components/grid/NullRenderersTest.java | 7 +- .../grid/ProgrammaticEditorControlTest.java | 5 +- .../components/grid/SelectDuringInitTest.java | 5 +- .../components/grid/SortableHeaderStylesTest.java | 5 +- .../tests/components/grid/WidgetRenderersTest.java | 3 +- .../basicfeatures/GridBasicClientFeaturesTest.java | 6 +- .../grid/basicfeatures/GridBasicFeaturesTest.java | 6 +- .../basicfeatures/GridDefaultTextRendererTest.java | 9 +- .../basicfeatures/GridHeightByRowOnInitTest.java | 4 +- .../client/GridClientColumnPropertiesTest.java | 2 +- .../client/GridDetailsClientTest.java | 2 +- .../client/GridSidebarContentTest.java | 4 +- .../basicfeatures/element/CustomGridElement.java | 46 - .../server/GridCellFocusAdjustmentTest.java | 2 +- .../server/GridClearContainerTest.java | 6 +- .../server/GridKeyboardNavigationTest.java | 2 +- .../grid/basicfeatures/server/GridScrollTest.java | 8 +- .../basicfeatures/server/GridSelectionTest.java | 2 +- .../grid/basicfeatures/server/GridSortingTest.java | 2 +- .../basicfeatures/server/GridStructureTest.java | 6 +- .../basicfeatures/server/LoadingIndicatorTest.java | 6 +- .../gridlayout/GridLayoutAlignmentsTest.java | 2 +- .../listselect/ListSelectAddRemoveItemsTest.java | 2 +- .../listselect/ListSelectNoDomRebuildTest.java | 2 +- .../ListSelectPushSelectionChangesTest.java | 4 +- .../menubar/MenuBarNavigationMouseTest.java | 14 +- .../nativeselect/NativeSelectNullTest.java | 2 +- .../NativeSelectsFocusAndBlurListenerTests.java | 2 +- .../notification/NotificationsWaiAriaTest.java | 8 +- .../orderedlayout/ErrorIndicatorTest.java | 6 +- .../tests/components/select/EnumSelectTest.java | 4 +- .../components/select/SelectWithIntegersTest.java | 2 +- .../components/table/AsyncPushUpdatesTest.java | 2 +- .../ColumnCollapsingAndColumnExpansionTest.java | 7 +- .../table/ColumnReorderingWithManyColumnsTest.java | 2 +- .../components/table/ContainerSizeChangeTest.java | 2 +- .../tests/components/table/CustomTableElement.java | 57 - .../components/table/DisabledSortingTableTest.java | 2 +- .../components/table/EditableModeChangeTest.java | 2 +- .../components/table/FocusOnSelectedItemTest.java | 2 +- .../tests/components/table/FooterClickTest.java | 2 +- .../vaadin/tests/components/table/FooterTest.java | 2 +- .../tests/components/table/HeaderClickTest.java | 2 +- .../HeaderFooterClickLeftRightMiddleTest.java | 2 +- .../table/HeaderPositionWhenSortingTest.java | 2 +- .../table/HeaderRightClickAfterDragTest.java | 2 +- .../components/table/HeaderSyncOnScrollTest.java | 2 +- .../table/HeaderUpdateWhenNoRowsTest.java | 2 +- .../components/table/ItemClickEventsTest.java | 2 +- .../table/ItemClickEventsTestWithShiftOrCtrl.java | 2 +- .../LabelEmbeddedClickThroughForTableTest.java | 2 +- .../components/table/LongMultiselectTest.java | 2 +- .../components/table/MemoryLeakTableTest.java | 2 +- .../components/table/OnlyCollapsibleInMenu.java | 3 +- .../tests/components/table/ReloadWidgetsTest.java | 2 +- .../table/SelectAllConstantViewportTest.java | 2 +- .../tests/components/table/SelectAllRowsTest.java | 2 +- .../components/table/SelectableEditableTest.java | 2 +- .../table/SetCurrentPageFirstItemIndexTest.java | 2 +- .../components/table/SortLabelsInTableTest.java | 2 +- .../components/table/SortableHeaderStylesTest.java | 4 +- .../tests/components/table/TableBlurFocusTest.java | 2 +- .../TableClickAndDragOnIconAndComponentsTest.java | 2 +- .../table/TableColumnAddAndResizeTest.java | 2 +- .../table/TableColumnResizeContentsWidthTest.java | 2 +- .../table/TableColumnWidthsAndSortingTest.java | 2 +- .../table/TableContextMenuAndIconsTest.java | 2 +- .../components/table/TableDragColumnTest.java | 2 +- .../table/TableDropIndicatorValoTest.java | 2 +- .../table/TableInIframeRowClickScrollJumpTest.java | 2 +- .../table/TableItemDescriptionGeneratorUITest.java | 2 +- .../TableMatchesMouseDownMouseUpElementTest.java | 2 +- .../table/TableNavigationPageDownTest.java | 2 +- .../table/TableParentEnabledStateChangeTest.java | 2 +- ...RepairsScrollPositionOnReAddingAllRowsTest.java | 2 +- .../components/table/TableRowHeight2Test.java | 2 +- .../table/TableRowScrolledBottomTest.java | 2 +- .../table/TableScrollUpOnSelectTest.java | 2 +- .../table/TableScrollsOnSelectionTest.java | 2 +- .../table/TableSetUndefinedSizeTest.java | 2 +- .../table/TableSortingIndicatorTest.java | 2 +- .../table/TableToggleColumnVisibilityTest.java | 2 +- ...eWithContainerRequiringEqualsForItemIdTest.java | 2 +- .../components/table/TableWithPollingTest.java | 2 +- .../table/UpdateTableWhenUnfocusedTest.java | 2 +- .../textarea/TextAreaSizeResettedTest.java | 2 +- .../components/textfield/EnumTextFieldTest.java | 26 +- .../textfield/LocaleChangeOnReadOnlyFieldTest.java | 5 +- .../textfield/RequiredTextFieldTest.java | 4 +- .../tree/TreeHtmlContentAllowedTest.java | 2 +- .../treetable/DisappearingComponentsTest.java | 2 +- .../TreeTableContainerHierarchicalWrapperTest.java | 2 +- .../TreeTableItemDescriptionGeneratorUITest.java | 2 +- .../treetable/TreeTableOutOfSyncTest.java | 2 +- .../TreeTablePartialUpdatesPageLength0Test.java | 2 +- .../treetable/TreeTablePartialUpdatesTest.java | 2 +- .../treetable/TreeTableScrollOnExpandTest.java | 2 +- .../TextAreaEventPropagationModifierKeysTest.java | 2 +- .../ui/TextAreaEventPropagationTest.java | 2 +- .../vaadin/tests/components/ui/UIPollingTest.java | 6 +- .../vaadin/tests/components/uitest/ThemeTest.java | 83 +- .../window/ComboboxScrollableWindowTest.java | 4 +- .../tests/components/window/GridInWindowTest.java | 2 +- ...MaximizeRestoreWindowWithManagedLayoutTest.java | 2 +- .../window/MaximizedWindowOrderTest.java | 2 +- .../window/OpenModalWindowAndFocusFieldTest.java | 2 +- .../tests/components/window/WindowCaptionTest.java | 2 +- ...TableQueryWithNonUniqueFirstPrimaryKeyTest.java | 2 +- .../tests/contextclick/GridContextClickTest.java | 16 +- .../tests/contextclick/TableContextClickTest.java | 2 +- .../contextclick/TableContextClickTestBase.java | 2 +- .../tests/contextclick/TreeContextClickTest.java | 2 +- .../ConverterThatEnforcesAFormatTest.java | 5 +- .../customelements/CustomProgressBarElement.java | 56 - .../java/com/vaadin/tests/dd/DnDOnSubtreeTest.java | 2 +- .../fieldgroup/BasicCrudGridEditorRowTest.java | 10 +- .../vaadin/tests/fieldgroup/BasicCrudGridTest.java | 6 +- .../tests/fieldgroup/BasicCrudTableTest.java | 2 +- .../tests/fieldgroup/BasicPersonFormTest.java | 17 +- .../com/vaadin/tests/fieldgroup/DateFormTest.java | 16 +- .../fieldgroup/MultipleValidationErrorsTest.java | 7 +- .../com/vaadin/tests/fonticon/FontIconsTest.java | 2 +- .../AbstractServletIntegrationTest.java | 2 +- .../legacyelements/LegacyPasswordFieldElement.java | 9 - .../com/vaadin/tests/navigator/NavigationTest.java | 2 +- .../vaadin/tests/push/PushConfigurationTest.java | 2 +- .../tests/push/SendMultibyteCharactersTest.java | 2 +- .../tests/tb3/newelements/CalendarElement.java | 48 - .../tests/tb3/newelements/ComboBoxElement.java | 61 - .../tb3/newelements/FixedNotificationElement.java | 28 - .../tests/tb3/newelements/WindowElement.java | 76 - .../themes/LegacyComponentThemeChangeTest.java | 4 +- .../themes/valo/CollapsibleTableColumnTest.java | 2 +- .../themes/valo/ValoDefaultCaptionWidthTest.java | 2 +- .../vaadin/tests/themes/valo/ValoThemeUITest.java | 2 +- .../tests/tooltip/ValidatorCaptionTooltipTest.java | 7 +- .../ValidationOfRequiredEmptyFieldsTest.java | 7 +- .../widgetset/server/OverriddenDecendantsTest.java | 2 +- .../v7/tests/elements/LegacyCheckBoxElement.java | 9 - .../v7/tests/elements/LegacyDateFieldElement.java | 28 - .../elements/LegacyInlineDateFieldElement.java | 28 - .../elements/LegacyPopupDateFieldElement.java | 28 - .../v7/tests/elements/LegacyTextFieldElement.java | 9 - 1996 files changed, 97348 insertions(+), 97301 deletions(-) delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/Caption.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/AbstractBeanContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/AbstractContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/AbstractInMemoryContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/BeanContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/BeanItem.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/BeanItemContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/ContainerHierarchicalWrapper.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/FilesystemContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/GeneratedPropertyContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/HierarchicalContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/IndexedContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheFlushNotifier.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheMap.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ColumnProperty.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowId.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/Reference.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowId.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowItem.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLContainer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLUtil.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/TemporaryRowId.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/JDBCConnectionPool.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/AbstractTransactionalQuery.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryDelegate.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformStatementDelegate.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/OrderBy.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/QueryDelegate.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/TableQuery.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/MSSQLGenerator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/OracleGenerator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/SQLGenerator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/StatementHelper.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/AndTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/NotTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/OrTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/StringDecorator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/server/communication/data/DataGenerator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/server/communication/data/RpcDataProviderExtension.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/AbstractColorPicker.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/AbstractSelect.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/Calendar.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/ColorPicker.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/ColorPickerArea.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/ComboBox.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/DefaultFieldFactory.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/ListSelect.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/NativeSelect.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/OptionGroup.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/RichTextArea.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/Select.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/Table.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/TableFieldFactory.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/TextArea.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/Tree.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/TreeTable.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/TwinColSelect.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvent.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvents.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarDateRange.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarTargetDetails.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/ContainerEventProvider.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEvent.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEventProvider.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEvent.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeEvent.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeListener.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGradient.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorSelector.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/HasColorChangeListener.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/AbstractJavaScriptRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ClickableRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ImageRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/Renderer.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/BeanFieldGroup.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/Caption.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/DefaultFieldGroupFieldFactory.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroupFieldFactory.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/PropertyId.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractBeanContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractInMemoryContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItem.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItemContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/ContainerHierarchicalWrapper.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/FilesystemContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/GeneratedPropertyContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/HierarchicalContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/IndexedContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheFlushNotifier.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheMap.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ColumnProperty.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/OptimisticLockException.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ReadOnlyRowId.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/Reference.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowId.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowItem.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLContainer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLUtil.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/TemporaryRowId.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/J2EEConnectionPool.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/JDBCConnectionPool.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/AbstractTransactionalQuery.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQuery.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQueryDelegate.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformStatementDelegate.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/OrderBy.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/QueryDelegate.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/TableQuery.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/MSSQLGenerator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/OracleGenerator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/SQLGenerator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/StatementHelper.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/AndTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/NotTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/OrTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/StringDecorator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractStringValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/BeanValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigDecimalRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigIntegerRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/ByteRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/CompositeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/DateRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/EmailValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/FloatRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractStringValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBeanValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigDecimalRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigIntegerRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyByteRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyCompositeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDateRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyEmailValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyFloatRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyLongRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyNullValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRegexpValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyShortRangeValidator.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyStringLengthValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/LongRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/NullValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/RangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/RegexpValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/ShortRangeValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/data/validator/StringLengthValidator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/DataGenerator.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/RpcDataProviderExtension.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractColorPicker.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractSelect.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/Calendar.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPicker.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPickerArea.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/ComboBox.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/DateField.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/DefaultFieldFactory.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/InlineDateField.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyDateField.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyInlineDateField.java delete mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyPopupDateField.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/ListSelect.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/NativeSelect.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/OptionGroup.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/PopupDateField.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/RichTextArea.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/Select.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/Table.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/TableFieldFactory.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/TextArea.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/Tree.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/TreeTable.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/TwinColSelect.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvent.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvents.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarDateRange.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarTargetDetails.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/ContainerEventProvider.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEvent.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEventProvider.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEditableEventProvider.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEvent.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEventProvider.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/EditableCalendarEvent.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicBackwardHandler.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicDateClickHandler.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventMoveHandler.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventResizeHandler.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicForwardHandler.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicWeekClickHandler.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeEvent.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeListener.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGradient.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGrid.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerHistory.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPopup.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPreview.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerSelect.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorSelector.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/HasColorChangeListener.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/AbstractJavaScriptRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ButtonRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ClickableRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/DateRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/HtmlRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ImageRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/NumberRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ProgressBarRenderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/Renderer.java create mode 100644 compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/TextRenderer.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/BeanFieldGroupTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupDateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/BeanItemTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/AbstractFilterTestBase.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/AndOrFilterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterDateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/IsNullFilterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/LikeFilterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/NotFilterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/filter/SimpleStringFilterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/AllTests.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ColumnPropertyTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/DataGenerator.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/FreeformQueryUtil.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowIdTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/RowIdTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTest.java delete mode 100755 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/TicketTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/UtilTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPoolTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/MockInitialContextFactory.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/BetweenTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/CompareTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/LikeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/SQLGeneratorsTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/StatementHelperTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/QueryBuilderTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/ValidatingSimpleJDBCConnectionPool.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigDecimalRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigIntegerRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/ByteRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/CompositeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/DateRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/DoubleRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/EmailValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/FloatRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/IntegerRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/LongRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/NullValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/RegexpValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/ShortRangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/data/validator/StringLengthValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/design/ParseLegacyPrefixTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/design/WriteLegacyDesignTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/AbstractBeanContainerListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/AbstractContainerListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/AbstractInMemoryContainerListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/ContextClickListenerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/IndexedContainerListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/SerializationTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractfield/LegacyAbstractFieldListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarBasicsTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerDataSourceTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerEventProviderTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/ColorConversionsTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldConverterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyDateFieldDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyPopupDateFieldDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridAddRowBuiltinContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridChildrenTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnAddingAndRemovingTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridColumnsTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerNotSortableTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridContainerTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridExtensionTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridSelectionTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridStaticSectionTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/MultiSelectionModelTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/SingleSelectionModelTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/TestGrid.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeTestBase.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/sort/SortTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/select/SelectListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/CacheUpdateExceptionCausesTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/FooterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/MultipleSelectionTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableColumnAlignmentsTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableContextClickTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTestBase.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableGeneratorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TablePropertyValueConverterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSelectableTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSerializationTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableVisibleColumnsTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/textarea/TextAreaDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/textfield/TextFieldWithConverterAndValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/textfield/TextFieldWithPropertyFormatterTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/textfield/TextFieldWithValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/tree/ListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/tree/TreeDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/tree/TreeListenersTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/tree/TreeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/treetable/EmptyTreeTableTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/treetable/TreeTableDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/treetable/TreeTableSetContainerNullTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/treetable/TreeTableTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/component/twincolselect/TwinColSelectStateTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/components/ComboBoxValueChangeTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/renderer/ImageRendererTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/renderer/RendererTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/validation/BeanValidationTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/validation/RangeValidatorTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/tests/server/validation/ReadOnlyValidationTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/ui/AbsSelectTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/ui/NativeSelectTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/ui/RichTextAreaTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/ui/TableTest.java delete mode 100644 compatibility-server/src/test/java/com/vaadin/ui/TextAreaTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/fieldgroup/BeanFieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/fieldgroup/FieldGroupDateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/fieldgroup/FieldGroupExceptionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/fieldgroup/FieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/AbstractBeanContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/AbstractContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/AbstractHierarchicalContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/AbstractInMemoryContainerTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemContainerGenerator.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemContainerSortTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/ContainerHierarchicalWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/ContainerOrderedWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/ContainerSizeAssertTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/ContainerSortingTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/FileSystemContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/GeneratedPropertyContainerBasicTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/GeneratedPropertyContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/HierarchicalContainerOrderedWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/HierarchicalContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/IndexedContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/MethodPropertyMemoryConsumptionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/NestedMethodPropertyTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/ObjectPropertyTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/PerformanceTestIndexedContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/PropertyDescriptorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/PropertySetItemTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/ReflectToolsGetSuperFieldTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/TransactionalPropertyWrapperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/AbstractFilterTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/AndOrFilterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/CompareFilterDateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/CompareFilterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/IsNullFilterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/LikeFilterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/NotFilterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/filter/SimpleStringFilterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/AllTests.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/ColumnPropertyTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/DataGenerator.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/FreeformQueryUtil.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/ReadOnlyRowIdTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/RowIdTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/SQLContainerTableQueryTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/SQLContainerTest.java create mode 100755 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/SQLTestsConstants.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/TicketTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/UtilTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/connection/J2EEConnectionPoolTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/connection/MockInitialContextFactory.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/filters/BetweenTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/filters/CompareTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/filters/LikeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/generator/SQLGeneratorsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/generator/StatementHelperTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQueryTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/query/QueryBuilderTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/query/TableQueryTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/data/util/sqlcontainer/query/ValidatingSimpleJDBCConnectionPool.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/BigDecimalRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/BigIntegerRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/ByteRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/CompositeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/DateRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/DoubleRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/EmailValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/FloatRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/IntegerRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/LongRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/NullValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/RegexpValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/ShortRangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/StringLengthValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/design/ParseAllSupportedComponentsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/design/ParseLegacyPrefixTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/design/WriteLegacyDesignTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/AbstractBeanContainerListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/AbstractContainerListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/AbstractInMemoryContainerListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/ContextClickListenerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/IndexedContainerListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/SerializationTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractfield/AbstractFieldListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractselect/AbstractSelectListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractselect/AbstractSelectStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractselect/OptionGroupDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/abstractselect/OptionGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/calendar/CalendarBasicsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/calendar/CalendarDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/calendar/ContainerDataSourceTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/calendar/ContainerEventProviderTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/colorpicker/ColorConversionsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/combobox/ComboBoxDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/combobox/ComboBoxStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/datefield/DateFieldConverterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/datefield/LegacyDateFieldDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/datefield/PopupDateFieldDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/fieldgroup/BeanFieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/fieldgroup/CaseInsensitiveBindingTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/fieldgroup/FieldGroupTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/fieldgroup/FieldGroupWithReadOnlyPropertiesTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/fieldgroup/FieldNamedDescriptionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridAddRowBuiltinContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridChildrenTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridColumnAddingAndRemovingTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridColumnsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridContainerNotSortableTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridContainerTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridExtensionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridSelectionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridStaticSectionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/MultiSelectionModelTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/SingleSelectionModelTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/TestGrid.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/declarative/GridDeclarativeTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/declarative/GridHeaderFooterDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/declarative/GridInlineDataDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/declarative/GridStructureDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/sort/SortTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/listselect/ListSelectDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/listselect/ListSelectStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/optiongroup/OptionGroupListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/optiongroup/OptionGroupStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/richtextarea/RichTextAreaDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/richtextarea/RichTextAreaStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/select/SelectListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/CacheUpdateExceptionCausesTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/FooterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/MultipleSelectionTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableColumnAlignmentsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableContextClickTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableDeclarativeTestBase.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableGeneratorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TablePropertyValueConverterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableSelectableTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableSerializationTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/table/TableVisibleColumnsTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textarea/TextAreaDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldWithConverterAndValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldWithPropertyFormatterTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/textfield/TextFieldWithValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/tree/ListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/tree/TreeDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/tree/TreeListenersTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/tree/TreeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/treetable/EmptyTreeTableTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/treetable/TreeTableDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/treetable/TreeTableSetContainerNullTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/treetable/TreeTableTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/twincolselect/TwinColSelectDeclarativeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/twincolselect/TwinColSelectStateTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/components/ComboBoxValueChangeTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/renderer/ImageRendererTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/renderer/RendererTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/validation/BeanValidationTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/validation/RangeValidatorTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/tests/server/validation/ReadOnlyValidationTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/ui/AbsSelectTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/ui/NativeSelectTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/ui/RichTextAreaTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/ui/TableTest.java create mode 100644 compatibility-server/src/test/java/com/vaadin/v7/ui/TextAreaTest.java delete mode 100644 compatibility-server/src/test/resources/com/vaadin/tests/design/all-components-legacy.html delete mode 100644 compatibility-server/src/test/resources/com/vaadin/tests/design/all-components.html delete mode 100644 compatibility-server/src/test/resources/com/vaadin/tests/design/testFile-legacy.html create mode 100644 compatibility-server/src/test/resources/com/vaadin/v7/tests/design/all-components-legacy.html create mode 100644 compatibility-server/src/test/resources/com/vaadin/v7/tests/design/all-components.html create mode 100644 compatibility-server/src/test/resources/com/vaadin/v7/tests/design/testFile-legacy.html delete mode 100644 server/src/main/java/com/vaadin/data/Buffered.java delete mode 100644 server/src/main/java/com/vaadin/data/BufferedValidatable.java delete mode 100644 server/src/main/java/com/vaadin/data/Collapsible.java delete mode 100644 server/src/main/java/com/vaadin/data/Container.java delete mode 100644 server/src/main/java/com/vaadin/data/ContainerHelpers.java delete mode 100644 server/src/main/java/com/vaadin/data/Item.java delete mode 100644 server/src/main/java/com/vaadin/data/Property.java delete mode 100644 server/src/main/java/com/vaadin/data/util/AbstractProperty.java delete mode 100644 server/src/main/java/com/vaadin/data/util/ContainerOrderedWrapper.java delete mode 100644 server/src/main/java/com/vaadin/data/util/DefaultItemSorter.java delete mode 100644 server/src/main/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapper.java delete mode 100644 server/src/main/java/com/vaadin/data/util/ItemSorter.java delete mode 100644 server/src/main/java/com/vaadin/data/util/ListSet.java delete mode 100644 server/src/main/java/com/vaadin/data/util/MethodProperty.java delete mode 100644 server/src/main/java/com/vaadin/data/util/MethodPropertyDescriptor.java delete mode 100644 server/src/main/java/com/vaadin/data/util/NestedMethodProperty.java delete mode 100644 server/src/main/java/com/vaadin/data/util/NestedPropertyDescriptor.java delete mode 100644 server/src/main/java/com/vaadin/data/util/ObjectProperty.java delete mode 100644 server/src/main/java/com/vaadin/data/util/PropertyFormatter.java delete mode 100644 server/src/main/java/com/vaadin/data/util/PropertyValueGenerator.java delete mode 100644 server/src/main/java/com/vaadin/data/util/PropertysetItem.java delete mode 100644 server/src/main/java/com/vaadin/data/util/TextFileProperty.java delete mode 100644 server/src/main/java/com/vaadin/data/util/TransactionalPropertyWrapper.java delete mode 100644 server/src/main/java/com/vaadin/data/util/VaadinPropertyDescriptor.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/AbstractJunctionFilter.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/And.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/Between.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/Compare.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/IsNull.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/Like.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/Not.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/Or.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/SimpleStringFilter.java delete mode 100644 server/src/main/java/com/vaadin/data/util/filter/UnsupportedFilterException.java delete mode 100644 server/src/main/java/com/vaadin/legacy/ui/LegacyCustomField.java delete mode 100644 server/src/main/java/com/vaadin/ui/LegacyWindow.java create mode 100644 server/src/main/java/com/vaadin/v7/data/Buffered.java create mode 100644 server/src/main/java/com/vaadin/v7/data/BufferedValidatable.java create mode 100644 server/src/main/java/com/vaadin/v7/data/Collapsible.java create mode 100644 server/src/main/java/com/vaadin/v7/data/Container.java create mode 100644 server/src/main/java/com/vaadin/v7/data/ContainerHelpers.java create mode 100644 server/src/main/java/com/vaadin/v7/data/Item.java create mode 100644 server/src/main/java/com/vaadin/v7/data/Property.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/AbstractProperty.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/ContainerOrderedWrapper.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/DefaultItemSorter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/HierarchicalContainerOrderedWrapper.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/ItemSorter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/ListSet.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/MethodProperty.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/MethodPropertyDescriptor.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/NestedMethodProperty.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/NestedPropertyDescriptor.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/ObjectProperty.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/PropertyFormatter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/PropertyValueGenerator.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/PropertysetItem.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/TextFileProperty.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/TransactionalPropertyWrapper.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/VaadinPropertyDescriptor.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/AbstractStringToNumberConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/Converter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/ConverterFactory.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/ConverterUtil.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/DateToLongConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/DateToSqlDateConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/DefaultConverterFactory.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyAbstractStringToNumberConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyConverterFactory.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyConverterUtil.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyDateToLongConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyDateToSqlDateConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyDefaultConverterFactory.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyReverseConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToBigDecimalConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToBigIntegerConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToBooleanConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToByteConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToCollectionConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToDateConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToDoubleConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToEnumConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToFloatConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToIntegerConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToLongConverter.java delete mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/LegacyStringToShortConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/ReverseConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToBigDecimalConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToBigIntegerConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToBooleanConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToByteConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToCollectionConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToDateConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToDoubleConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToEnumConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToFloatConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToIntegerConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToLongConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/converter/StringToShortConverter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/AbstractJunctionFilter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/And.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/Between.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/Compare.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/IsNull.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/Like.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/Not.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/Or.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/SimpleStringFilter.java create mode 100644 server/src/main/java/com/vaadin/v7/data/util/filter/UnsupportedFilterException.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/AbstractField.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/AbstractTextField.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/CheckBox.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/CustomField.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/Field.java delete mode 100644 server/src/main/java/com/vaadin/v7/ui/LegacyAbstractField.java delete mode 100644 server/src/main/java/com/vaadin/v7/ui/LegacyAbstractTextField.java delete mode 100644 server/src/main/java/com/vaadin/v7/ui/LegacyCheckBox.java delete mode 100644 server/src/main/java/com/vaadin/v7/ui/LegacyField.java delete mode 100644 server/src/main/java/com/vaadin/v7/ui/LegacyPasswordField.java delete mode 100644 server/src/main/java/com/vaadin/v7/ui/LegacyTextField.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/LegacyWindow.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/PasswordField.java create mode 100644 server/src/main/java/com/vaadin/v7/ui/TextField.java create mode 100644 uitest-common/src/main/java/com/vaadin/testbench/customelements/DateFieldElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/testbench/customelements/FixedNotificationElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/testbench/customelements/MenuBarElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/testbench/customelements/WindowElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/CalendarElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/CheckBoxElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/ColorPickerAreaElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/ColorPickerElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/ComboBoxElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/DateFieldElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/GridElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/InlineDateFieldElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/ListSelectElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/NativeSelectElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/OptionGroupElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/PasswordFieldElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/PopupDateFieldElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/ProgressBarElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/RichTextAreaElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/SelectElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/TableElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/TextAreaElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/TextFieldElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/TreeElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/TreeTableElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/v7/testbench/customelements/TwinColSelectElement.java delete mode 100755 uitest/src/main/java/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java create mode 100755 uitest/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLTestsConstants.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/components/combobox/CustomComboBoxElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/components/grid/LegacyGridElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/element/CustomGridElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/components/table/CustomTableElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/customelements/CustomProgressBarElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/legacyelements/LegacyPasswordFieldElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/tb3/newelements/CalendarElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/tb3/newelements/ComboBoxElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/tb3/newelements/FixedNotificationElement.java delete mode 100644 uitest/src/test/java/com/vaadin/tests/tb3/newelements/WindowElement.java delete mode 100644 uitest/src/test/java/com/vaadin/v7/tests/elements/LegacyCheckBoxElement.java delete mode 100644 uitest/src/test/java/com/vaadin/v7/tests/elements/LegacyDateFieldElement.java delete mode 100644 uitest/src/test/java/com/vaadin/v7/tests/elements/LegacyInlineDateFieldElement.java delete mode 100644 uitest/src/test/java/com/vaadin/v7/tests/elements/LegacyPopupDateFieldElement.java delete mode 100644 uitest/src/test/java/com/vaadin/v7/tests/elements/LegacyTextFieldElement.java diff --git a/client/src/main/java/com/vaadin/client/legacy/ui/LegacyCustomFieldConnector.java b/client/src/main/java/com/vaadin/client/legacy/ui/LegacyCustomFieldConnector.java index eca56612cb..19b13f53d6 100644 --- a/client/src/main/java/com/vaadin/client/legacy/ui/LegacyCustomFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/legacy/ui/LegacyCustomFieldConnector.java @@ -16,10 +16,10 @@ package com.vaadin.client.legacy.ui; import com.vaadin.client.ui.customfield.CustomFieldConnector; -import com.vaadin.legacy.ui.LegacyCustomField; import com.vaadin.shared.ui.Connect; +import com.vaadin.v7.ui.CustomField; -@Connect(value = LegacyCustomField.class) +@Connect(value = CustomField.class) public class LegacyCustomFieldConnector extends CustomFieldConnector { } diff --git a/client/src/main/java/com/vaadin/client/v7/ui/checkbox/LegacyCheckBoxConnector.java b/client/src/main/java/com/vaadin/client/v7/ui/checkbox/LegacyCheckBoxConnector.java index c8a4762928..f3e46e9b01 100644 --- a/client/src/main/java/com/vaadin/client/v7/ui/checkbox/LegacyCheckBoxConnector.java +++ b/client/src/main/java/com/vaadin/client/v7/ui/checkbox/LegacyCheckBoxConnector.java @@ -17,9 +17,9 @@ package com.vaadin.client.v7.ui.checkbox; import com.vaadin.client.ui.checkbox.CheckBoxConnector; import com.vaadin.shared.ui.Connect; -import com.vaadin.v7.ui.LegacyCheckBox; +import com.vaadin.v7.ui.CheckBox; -@Connect(LegacyCheckBox.class) +@Connect(CheckBox.class) public class LegacyCheckBoxConnector extends CheckBoxConnector { } diff --git a/client/src/main/java/com/vaadin/client/v7/ui/passwordfield/LegacyPasswordFieldConnector.java b/client/src/main/java/com/vaadin/client/v7/ui/passwordfield/LegacyPasswordFieldConnector.java index 1c3f89d693..d2fda689f1 100644 --- a/client/src/main/java/com/vaadin/client/v7/ui/passwordfield/LegacyPasswordFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/v7/ui/passwordfield/LegacyPasswordFieldConnector.java @@ -19,10 +19,10 @@ package com.vaadin.client.v7.ui.passwordfield; import com.vaadin.client.v7.ui.VLegacyPasswordField; import com.vaadin.client.v7.ui.textfield.LegacyTextFieldConnector; import com.vaadin.shared.ui.Connect; -import com.vaadin.v7.ui.LegacyPasswordField; +import com.vaadin.v7.ui.PasswordField; @Deprecated -@Connect(LegacyPasswordField.class) +@Connect(PasswordField.class) public class LegacyPasswordFieldConnector extends LegacyTextFieldConnector { @Override diff --git a/client/src/main/java/com/vaadin/client/v7/ui/textfield/LegacyTextFieldConnector.java b/client/src/main/java/com/vaadin/client/v7/ui/textfield/LegacyTextFieldConnector.java index 7a2c583779..92ae3a47da 100644 --- a/client/src/main/java/com/vaadin/client/v7/ui/textfield/LegacyTextFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/v7/ui/textfield/LegacyTextFieldConnector.java @@ -28,10 +28,10 @@ import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.v7.ui.textfield.LegacyAbstractTextFieldState; import com.vaadin.shared.v7.ui.textfield.LegacyTextFieldConstants; -import com.vaadin.v7.ui.LegacyTextField; +import com.vaadin.v7.ui.TextField; @Deprecated -@Connect(value = LegacyTextField.class, loadStyle = LoadStyle.EAGER) +@Connect(value = TextField.class, loadStyle = LoadStyle.EAGER) public class LegacyTextFieldConnector extends AbstractFieldConnector implements Paintable { 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 index fad8918c92..d0d2b6196b 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/ButtonRendererConnector.java @@ -28,7 +28,7 @@ import elemental.json.JsonObject; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.ui.renderers.ButtonRenderer.class) +@Connect(com.vaadin.v7.ui.renderers.ButtonRenderer.class) public class ButtonRendererConnector extends ClickableRendererConnector { 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 index 4b8c3872da..4eb56376f4 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/DateRendererConnector.java @@ -28,7 +28,7 @@ import com.vaadin.shared.ui.Connect; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.ui.renderers.DateRenderer.class) +@Connect(com.vaadin.v7.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 index 000a24af00..49874c84b4 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/DetailComponentManagerConnector.java @@ -18,7 +18,7 @@ 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; +import com.vaadin.v7.ui.Grid.DetailComponentManager; /** * Client-side connector for the DetailComponentManager of Grid. 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 index 045f51f508..b30557885a 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/GridConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/GridConnector.java @@ -95,7 +95,6 @@ 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; @@ -111,7 +110,7 @@ import elemental.json.JsonValue; * @since 7.4 * @author Vaadin Ltd */ -@Connect(LegacyGrid.class) +@Connect(com.vaadin.v7.ui.Grid.class) public class GridConnector extends AbstractHasComponentsConnector implements SimpleManagedLayout, DeferredWorker { 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 index 7949eeab3c..d71cb28a7d 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/ImageRendererConnector.java @@ -32,7 +32,7 @@ import elemental.json.JsonValue; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.ui.renderers.ImageRenderer.class) +@Connect(com.vaadin.v7.ui.renderers.ImageRenderer.class) public class ImageRendererConnector extends ClickableRendererConnector { @Override 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 index 1515f1aead..4bd618dacd 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/JavaScriptRendererConnector.java @@ -30,7 +30,7 @@ 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 com.vaadin.v7.ui.renderers.AbstractJavaScriptRenderer; import elemental.json.JsonObject; import elemental.json.JsonValue; 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 index e7494737cb..7a13863a9e 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/MultiSelectionModelConnector.java @@ -46,7 +46,7 @@ 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 com.vaadin.v7.ui.Grid.MultiSelectionModel; import elemental.json.JsonObject; 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 index 1a080f5082..b3fbb16e6b 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/NoSelectionModelConnector.java @@ -19,7 +19,7 @@ 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 com.vaadin.v7.ui.Grid.NoSelectionModel; import elemental.json.JsonObject; 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 index ff16047b0d..6383594ca2 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/NumberRendererConnector.java @@ -28,7 +28,7 @@ import com.vaadin.shared.ui.Connect; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.ui.renderers.NumberRenderer.class) +@Connect(com.vaadin.v7.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 index 5687d031aa..57bafc69ba 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/ProgressBarRendererConnector.java @@ -24,7 +24,7 @@ import com.vaadin.shared.ui.Connect; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.ui.renderers.ProgressBarRenderer.class) +@Connect(com.vaadin.v7.ui.renderers.ProgressBarRenderer.class) public class ProgressBarRendererConnector extends AbstractGridRendererConnector { 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 index 52fa8a2e48..d33cf72a33 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/RpcDataSourceConnector.java @@ -42,7 +42,7 @@ import elemental.json.JsonObject; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.server.communication.data.RpcDataProviderExtension.class) +@Connect(com.vaadin.v7.server.communication.data.RpcDataProviderExtension.class) public class RpcDataSourceConnector extends AbstractExtensionConnector { /** 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 index 980c4458d4..84f0633e9b 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/SingleSelectionModelConnector.java @@ -27,7 +27,7 @@ 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 com.vaadin.v7.ui.Grid.SingleSelectionModel; import elemental.json.JsonObject; 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 index d3a289ec3e..faa09fda13 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/TextRendererConnector.java @@ -24,7 +24,7 @@ import com.vaadin.shared.ui.Connect; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.ui.renderers.TextRenderer.class) +@Connect(com.vaadin.v7.ui.renderers.TextRenderer.class) public class TextRendererConnector extends AbstractGridRendererConnector { @Override 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 index 95c47dd242..8888c1041b 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/connectors/UnsafeHtmlRendererConnector.java @@ -25,7 +25,7 @@ import com.vaadin.shared.ui.Connect; * @since 7.4 * @author Vaadin Ltd */ -@Connect(com.vaadin.ui.renderers.HtmlRenderer.class) +@Connect(com.vaadin.v7.ui.renderers.HtmlRenderer.class) public class UnsafeHtmlRendererConnector extends AbstractGridRendererConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/VFilterSelect.java b/compatibility-client/src/main/java/com/vaadin/client/ui/VFilterSelect.java index 44cbcf28f6..814767e19f 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/VFilterSelect.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/VFilterSelect.java @@ -182,7 +182,7 @@ public class VFilterSelect extends Composite /** * Gets the style set for this suggestion item. Styles are typically set - * by a server-side {@link com.vaadin.ui.ComboBox.ItemStyleGenerator}. + * by a server-side {@link com.vaadin.v7.ui.ComboBox.ItemStyleGenerator}. * The returned style is prefixed by v-filterselect-item-. * * @since 7.5.6 diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/calendar/CalendarConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/calendar/CalendarConnector.java index f97ba9d14a..8f6118eed6 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/calendar/CalendarConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/calendar/CalendarConnector.java @@ -72,7 +72,7 @@ import com.vaadin.shared.ui.calendar.CalendarEventId; import com.vaadin.shared.ui.calendar.CalendarServerRpc; import com.vaadin.shared.ui.calendar.CalendarState; import com.vaadin.shared.ui.calendar.DateConstants; -import com.vaadin.ui.Calendar; +import com.vaadin.v7.ui.Calendar; /** * Handles communication between Calendar on the server side and diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerAreaConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerAreaConnector.java index 828cc689c7..6c1bee17a0 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerAreaConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerAreaConnector.java @@ -24,11 +24,11 @@ import com.vaadin.client.ui.VColorPickerArea; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc; -import com.vaadin.ui.ColorPickerArea; +import com.vaadin.v7.ui.ColorPickerArea; /** * A class that defines an implementation for a color picker connector. Connects - * the server side {@link com.vaadin.ui.ColorPickerArea} with the client side + * the server side {@link com.vaadin.v7.ui.ColorPickerArea} with the client side * counterpart {@link VColorPickerArea} * * @since 7.0.0 diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerConnector.java index 6254e7adbe..cc917a044e 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerConnector.java @@ -23,11 +23,11 @@ import com.vaadin.client.ui.VColorPicker; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc; -import com.vaadin.ui.ColorPicker; +import com.vaadin.v7.ui.ColorPicker; /** * A class that defines default implementation for a color picker connector. - * Connects the server side {@link com.vaadin.ui.ColorPicker} with the client + * Connects the server side {@link com.vaadin.v7.ui.ColorPicker} with the client * side counterpart {@link VColorPicker} * * @since 7.0.0 diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGradientConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGradientConnector.java index c05449d7bd..98ebfa086c 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGradientConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGradientConnector.java @@ -26,12 +26,12 @@ import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.ui.colorpicker.ColorPickerGradientServerRpc; import com.vaadin.shared.ui.colorpicker.ColorPickerGradientState; -import com.vaadin.ui.components.colorpicker.ColorPickerGradient; +import com.vaadin.v7.ui.components.colorpicker.ColorPickerGradient; /** * A class that defines the default implementation for a color picker gradient * connector. Connects the server side - * {@link com.vaadin.ui.components.colorpicker.ColorPickerGradient} with the + * {@link com.vaadin.v7.ui.components.colorpicker.ColorPickerGradient} with the * client side counterpart {@link VColorPickerGradient} * * @since 7.0.0 diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGridConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGridConnector.java index cd0a3d1466..b842154e40 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGridConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/colorpicker/ColorPickerGridConnector.java @@ -26,12 +26,12 @@ import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.ui.colorpicker.ColorPickerGridServerRpc; import com.vaadin.shared.ui.colorpicker.ColorPickerGridState; -import com.vaadin.ui.components.colorpicker.ColorPickerGrid; +import com.vaadin.v7.ui.components.colorpicker.ColorPickerGrid; /** * A class that defines the default implementation for a color picker grid * connector. Connects the server side - * {@link com.vaadin.ui.components.colorpicker.ColorPickerGrid} with the client + * {@link com.vaadin.v7.ui.components.colorpicker.ColorPickerGrid} with the client * side counterpart {@link VColorPickerGrid} * * @since 7.0.0 diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/combobox/ComboBoxConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/combobox/ComboBoxConnector.java index f6dff893f5..672e428802 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/combobox/ComboBoxConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/combobox/ComboBoxConnector.java @@ -35,7 +35,7 @@ import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.combobox.ComboBoxServerRpc; import com.vaadin.shared.ui.combobox.ComboBoxState; -import com.vaadin.ui.ComboBox; +import com.vaadin.v7.ui.ComboBox; @Connect(ComboBox.class) public class ComboBoxConnector extends AbstractFieldConnector diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VIsOverId.java b/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VIsOverId.java index b02f8df147..cd4bf13ba0 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VIsOverId.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VIsOverId.java @@ -21,7 +21,7 @@ package com.vaadin.client.ui.dd; import com.vaadin.client.ComponentConnector; import com.vaadin.client.UIDL; import com.vaadin.shared.ui.dd.AcceptCriterion; -import com.vaadin.ui.AbstractSelect; +import com.vaadin.v7.ui.AbstractSelect; @AcceptCriterion(AbstractSelect.TargetItemIs.class) final public class VIsOverId extends VAcceptCriterion { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VItemIdIs.java b/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VItemIdIs.java index 6992ebcaad..f8bfe075a3 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VItemIdIs.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/dd/VItemIdIs.java @@ -21,7 +21,7 @@ package com.vaadin.client.ui.dd; import com.vaadin.client.ComponentConnector; import com.vaadin.client.UIDL; import com.vaadin.shared.ui.dd.AcceptCriterion; -import com.vaadin.ui.AbstractSelect; +import com.vaadin.v7.ui.AbstractSelect; @AcceptCriterion(AbstractSelect.AcceptItem.class) final public class VItemIdIs extends VAcceptCriterion { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/listselect/ListSelectConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/listselect/ListSelectConnector.java index a69043548b..e288c088a9 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/listselect/ListSelectConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/listselect/ListSelectConnector.java @@ -19,7 +19,7 @@ package com.vaadin.client.ui.listselect; import com.vaadin.client.ui.VListSelect; import com.vaadin.client.ui.optiongroup.OptionGroupBaseConnector; import com.vaadin.shared.ui.Connect; -import com.vaadin.ui.ListSelect; +import com.vaadin.v7.ui.ListSelect; @Connect(ListSelect.class) public class ListSelectConnector extends OptionGroupBaseConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java index 1780d20c69..0063116cdb 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java @@ -20,7 +20,7 @@ import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; import com.vaadin.client.ui.VNativeSelect; import com.vaadin.client.ui.optiongroup.OptionGroupBaseConnector; import com.vaadin.shared.ui.Connect; -import com.vaadin.ui.NativeSelect; +import com.vaadin.v7.ui.NativeSelect; @Connect(NativeSelect.class) public class NativeSelectConnector extends OptionGroupBaseConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/optiongroup/OptionGroupConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/optiongroup/OptionGroupConnector.java index 3dd3b897a5..e8164632b4 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/optiongroup/OptionGroupConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/optiongroup/OptionGroupConnector.java @@ -27,7 +27,7 @@ import com.vaadin.client.ui.VOptionGroup; import com.vaadin.shared.EventId; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.optiongroup.OptionGroupState; -import com.vaadin.ui.OptionGroup; +import com.vaadin.v7.ui.OptionGroup; @Connect(OptionGroup.class) public class OptionGroupConnector extends OptionGroupBaseConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java index a8aeb79d07..2ea59046c7 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java @@ -27,7 +27,7 @@ import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.ui.textarea.RichTextAreaState; import com.vaadin.shared.util.SharedUtil; -import com.vaadin.ui.RichTextArea; +import com.vaadin.v7.ui.RichTextArea; @Connect(value = RichTextArea.class, loadStyle = LoadStyle.LAZY) public class RichTextAreaConnector extends AbstractFieldConnector diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/table/TableConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/table/TableConnector.java index ccfb715291..8bd2418aea 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/table/TableConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/table/TableConnector.java @@ -51,7 +51,7 @@ import com.vaadin.shared.ui.table.TableConstants.Section; import com.vaadin.shared.ui.table.TableServerRpc; import com.vaadin.shared.ui.table.TableState; -@Connect(com.vaadin.ui.Table.class) +@Connect(com.vaadin.v7.ui.Table.class) public class TableConnector extends AbstractFieldConnector implements HasComponentsConnector, ConnectorHierarchyChangeHandler, Paintable, DirectionalManagedLayout, PostLayoutListener, diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/table/VTableLazyInitItemIdentifiers.java b/compatibility-client/src/main/java/com/vaadin/client/ui/table/VTableLazyInitItemIdentifiers.java index 20433dd960..c3b2185de8 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/table/VTableLazyInitItemIdentifiers.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/table/VTableLazyInitItemIdentifiers.java @@ -17,7 +17,7 @@ package com.vaadin.client.ui.table; import com.vaadin.client.ui.dd.VLazyInitItemIdentifiers; import com.vaadin.shared.ui.dd.AcceptCriterion; -import com.vaadin.ui.Table; +import com.vaadin.v7.ui.Table; @AcceptCriterion(Table.TableDropCriterion.class) public final class VTableLazyInitItemIdentifiers diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/textarea/TextAreaConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/textarea/TextAreaConnector.java index e081a0e0a4..75b6ba21ee 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/textarea/TextAreaConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/textarea/TextAreaConnector.java @@ -24,7 +24,7 @@ import com.vaadin.client.ui.VTextArea; import com.vaadin.client.v7.ui.textfield.LegacyTextFieldConnector; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.textarea.TextAreaState; -import com.vaadin.ui.TextArea; +import com.vaadin.v7.ui.TextArea; @Connect(TextArea.class) public class TextAreaConnector extends LegacyTextFieldConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/tree/TreeConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/tree/TreeConnector.java index c28a58aaad..173048ec1e 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/tree/TreeConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/tree/TreeConnector.java @@ -40,7 +40,7 @@ import com.vaadin.shared.ui.MultiSelectMode; import com.vaadin.shared.ui.tree.TreeConstants; import com.vaadin.shared.ui.tree.TreeServerRpc; import com.vaadin.shared.ui.tree.TreeState; -import com.vaadin.ui.Tree; +import com.vaadin.v7.ui.Tree; @Connect(Tree.class) public class TreeConnector extends AbstractComponentConnector diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTargetInSubtree.java b/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTargetInSubtree.java index 34b8428582..8f38a8cfb4 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTargetInSubtree.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTargetInSubtree.java @@ -26,7 +26,7 @@ import com.vaadin.client.ui.dd.VAcceptCriterion; import com.vaadin.client.ui.dd.VDragAndDropManager; import com.vaadin.client.ui.dd.VDragEvent; import com.vaadin.shared.ui.dd.AcceptCriterion; -import com.vaadin.ui.Tree; +import com.vaadin.v7.ui.Tree; @AcceptCriterion(Tree.TargetInSubtree.class) final public class VTargetInSubtree extends VAcceptCriterion { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTreeLazyInitItemIdentifiers.java b/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTreeLazyInitItemIdentifiers.java index 46089af16e..e4ba64e12a 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTreeLazyInitItemIdentifiers.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/tree/VTreeLazyInitItemIdentifiers.java @@ -17,7 +17,7 @@ package com.vaadin.client.ui.tree; import com.vaadin.client.ui.dd.VLazyInitItemIdentifiers; import com.vaadin.shared.ui.dd.AcceptCriterion; -import com.vaadin.ui.Tree; +import com.vaadin.v7.ui.Tree; @AcceptCriterion(Tree.TreeDropCriterion.class) public final class VTreeLazyInitItemIdentifiers diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/treetable/TreeTableConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/treetable/TreeTableConnector.java index 77074d4cb5..7c1cac61c6 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/treetable/TreeTableConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/treetable/TreeTableConnector.java @@ -29,7 +29,7 @@ import com.vaadin.client.ui.table.TableConnector; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.treetable.TreeTableConstants; import com.vaadin.shared.ui.treetable.TreeTableState; -import com.vaadin.ui.TreeTable; +import com.vaadin.v7.ui.TreeTable; @Connect(TreeTable.class) public class TreeTableConnector extends TableConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/ui/twincolselect/TwinColSelectConnector.java b/compatibility-client/src/main/java/com/vaadin/client/ui/twincolselect/TwinColSelectConnector.java index 81e32e5711..5e05dae166 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/ui/twincolselect/TwinColSelectConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/ui/twincolselect/TwinColSelectConnector.java @@ -23,7 +23,7 @@ import com.vaadin.client.ui.VTwinColSelect; import com.vaadin.client.ui.optiongroup.OptionGroupBaseConnector; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.twincolselect.TwinColSelectState; -import com.vaadin.ui.TwinColSelect; +import com.vaadin.v7.ui.TwinColSelect; @Connect(TwinColSelect.class) public class TwinColSelectConnector extends OptionGroupBaseConnector diff --git a/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyDateFieldConnector.java b/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyDateFieldConnector.java index 6687baa6d4..ee62b03588 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyDateFieldConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyDateFieldConnector.java @@ -17,13 +17,13 @@ package com.vaadin.client.v7.ui; import com.vaadin.client.ui.datefield.DateFieldConnector; import com.vaadin.shared.ui.Connect; -import com.vaadin.v7.ui.LegacyDateField; +import com.vaadin.v7.ui.DateField; /** * @author Vaadin Ltd * */ -@Connect(LegacyDateField.class) +@Connect(DateField.class) @Deprecated public class LegacyDateFieldConnector extends DateFieldConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyInlineDateFieldConnector.java b/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyInlineDateFieldConnector.java index 5fb5b7a5dd..60459ff9c0 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyInlineDateFieldConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyInlineDateFieldConnector.java @@ -17,13 +17,13 @@ package com.vaadin.client.v7.ui; import com.vaadin.client.ui.datefield.InlineDateFieldConnector; import com.vaadin.shared.ui.Connect; -import com.vaadin.v7.ui.LegacyInlineDateField; +import com.vaadin.v7.ui.InlineDateField; /** * @author Vaadin Ltd * */ -@Connect(LegacyInlineDateField.class) +@Connect(InlineDateField.class) @Deprecated public class LegacyInlineDateFieldConnector extends InlineDateFieldConnector { diff --git a/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyPopupDateFieldConnector.java b/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyPopupDateFieldConnector.java index 9da986d879..15311ade5f 100644 --- a/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyPopupDateFieldConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/client/v7/ui/LegacyPopupDateFieldConnector.java @@ -17,14 +17,14 @@ package com.vaadin.client.v7.ui; import com.vaadin.client.ui.datefield.PopupDateFieldConnector; import com.vaadin.shared.ui.Connect; -import com.vaadin.v7.ui.LegacyPopupDateField; +import com.vaadin.v7.ui.PopupDateField; /** * @author Vaadin Ltd * */ @Deprecated -@Connect(LegacyPopupDateField.class) +@Connect(PopupDateField.class) public class LegacyPopupDateFieldConnector extends PopupDateFieldConnector { } 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 deleted file mode 100644 index 96e4621761..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/Caption.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/Caption.java deleted file mode 100644 index d752aa78d2..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java deleted file mode 100644 index 24c97eedc5..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java deleted file mode 100644 index 1254009cfc..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/FieldGroupFieldFactory.java deleted file mode 100644 index a80d51c6df..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.java deleted file mode 100644 index 6476635829..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/PropertyId.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.data.fieldgroup; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import com.vaadin.v7.ui.LegacyField; - -/** - * 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/data/util/AbstractBeanContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/AbstractBeanContainer.java deleted file mode 100644 index 995e2f8675..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/AbstractBeanContainer.java +++ /dev/null @@ -1,929 +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.Serializable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Filterable; -import com.vaadin.data.Container.PropertySetChangeNotifier; -import com.vaadin.data.Container.SimpleFilterable; -import com.vaadin.data.Container.Sortable; -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.data.util.MethodProperty.MethodException; -import com.vaadin.data.util.filter.SimpleStringFilter; -import com.vaadin.data.util.filter.UnsupportedFilterException; - -/** - * An abstract base class for in-memory containers for JavaBeans. - * - *

- * The properties of the container are determined automatically by introspecting - * the used JavaBean class and explicitly adding or removing properties is not - * supported. Only beans of the same type can be added to the container. - *

- * - *

- * Subclasses should implement any public methods adding items to the container, - * typically calling the protected methods {@link #addItem(Object, Object)}, - * {@link #addItemAfter(Object, Object, Object)} and - * {@link #addItemAt(int, Object, Object)}. - *

- * - * @param - * The type of the item identifier - * @param - * The type of the Bean - * - * @since 6.5 - */ -public abstract class AbstractBeanContainer - extends AbstractInMemoryContainer> - implements Filterable, SimpleFilterable, Sortable, ValueChangeListener, - PropertySetChangeNotifier { - - /** - * Resolver that maps beans to their (item) identifiers, removing the need - * to explicitly specify item identifiers when there is no need to customize - * this. - * - * Note that beans can also be added with an explicit id even if a resolver - * has been set. - * - * @param - * @param - * - * @since 6.5 - */ - public static interface BeanIdResolver - extends Serializable { - /** - * Return the item identifier for a bean. - * - * @param bean - * @return - */ - public IDTYPE getIdForBean(BEANTYPE bean); - } - - /** - * A item identifier resolver that returns the value of a bean property. - * - * The bean must have a getter for the property, and the getter must return - * an object of type IDTYPE. - */ - protected class PropertyBasedBeanIdResolver - implements BeanIdResolver { - - private final Object propertyId; - - public PropertyBasedBeanIdResolver(Object propertyId) { - if (propertyId == null) { - throw new IllegalArgumentException( - "Property identifier must not be null"); - } - this.propertyId = propertyId; - } - - @Override - @SuppressWarnings("unchecked") - public IDTYPE getIdForBean(BEANTYPE bean) - throws IllegalArgumentException { - VaadinPropertyDescriptor pd = model.get(propertyId); - if (null == pd) { - throw new IllegalStateException( - "Property " + propertyId + " not found"); - } - try { - Property property = (Property) pd - .createProperty(bean); - return property.getValue(); - } catch (MethodException e) { - throw new IllegalArgumentException(e); - } - } - - } - - /** - * The resolver that finds the item ID for a bean, or null not to use - * automatic resolving. - * - * Methods that add a bean without specifying an ID must not be called if no - * resolver has been set. - */ - private BeanIdResolver beanIdResolver = null; - - /** - * Maps all item ids in the container (including filtered) to their - * corresponding BeanItem. - */ - private final Map> itemIdToItem = new HashMap>(); - - /** - * The type of the beans in the container. - */ - private final Class type; - - /** - * A description of the properties found in beans of type {@link #type}. - * Determines the property ids that are present in the container. - */ - private final LinkedHashMap> model; - - /** - * Constructs a {@code AbstractBeanContainer} for beans of the given type. - * - * @param type - * the type of the beans that will be added to the container. - * @throws IllegalArgumentException - * If {@code type} is null - */ - protected AbstractBeanContainer(Class type) { - if (type == null) { - throw new IllegalArgumentException( - "The bean type passed to AbstractBeanContainer must not be null"); - } - this.type = type; - model = BeanItem.getPropertyDescriptors((Class) type); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getType(java.lang.Object) - */ - @Override - public Class getType(Object propertyId) { - VaadinPropertyDescriptor descriptor = model.get(propertyId); - if (descriptor == null) { - return null; - } - return descriptor.getPropertyType(); - } - - /** - * Create a BeanItem for a bean using pre-parsed bean metadata (based on - * {@link #getBeanType()}). - * - * @param bean - * @return created {@link BeanItem} or null if bean is null - */ - protected BeanItem createBeanItem(BEANTYPE bean) { - return bean == null ? null : new BeanItem(bean, model); - } - - /** - * Returns the type of beans this Container can contain. - * - * This comes from the bean type constructor parameter, and bean metadata - * (including container properties) is based on this. - * - * @return - */ - public Class getBeanType() { - return type; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getContainerPropertyIds() - */ - @Override - public Collection getContainerPropertyIds() { - return model.keySet(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeAllItems() - */ - @Override - public boolean removeAllItems() { - int origSize = size(); - IDTYPE firstItem = getFirstVisibleItem(); - - internalRemoveAllItems(); - - // detach listeners from all Items - for (Item item : itemIdToItem.values()) { - removeAllValueChangeListeners(item); - } - itemIdToItem.clear(); - - // fire event only if the visible view changed, regardless of whether - // filtered out items were removed or not - if (origSize != 0) { - fireItemsRemoved(0, firstItem, origSize); - } - - return true; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getItem(java.lang.Object) - */ - @Override - public BeanItem getItem(Object itemId) { - // TODO return only if visible? - return getUnfilteredItem(itemId); - } - - @Override - protected BeanItem getUnfilteredItem(Object itemId) { - return itemIdToItem.get(itemId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getItemIds() - */ - @Override - @SuppressWarnings("unchecked") - public List getItemIds() { - return (List) super.getItemIds(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object, - * java.lang.Object) - */ - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - Item item = getItem(itemId); - if (item == null) { - return null; - } - return item.getItemProperty(propertyId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeItem(java.lang.Object) - */ - @Override - public boolean removeItem(Object itemId) { - // TODO should also remove items that are filtered out - int origSize = size(); - Item item = getItem(itemId); - int position = indexOfId(itemId); - - if (internalRemoveItem(itemId)) { - // detach listeners from Item - removeAllValueChangeListeners(item); - - // remove item - itemIdToItem.remove(itemId); - - // fire event only if the visible view changed, regardless of - // whether filtered out items were removed or not - if (size() != origSize) { - fireItemRemoved(position, itemId); - } - - return true; - } else { - return false; - } - } - - /** - * Re-filter the container when one of the monitored properties changes. - */ - @Override - public void valueChange(ValueChangeEvent event) { - // if a property that is used in a filter is changed, refresh filtering - filterAll(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.Container.Filterable#addContainerFilter(java.lang.Object, - * java.lang.String, boolean, boolean) - */ - @Override - public void addContainerFilter(Object propertyId, String filterString, - boolean ignoreCase, boolean onlyMatchPrefix) { - try { - addFilter(new SimpleStringFilter(propertyId, filterString, - ignoreCase, onlyMatchPrefix)); - } catch (UnsupportedFilterException e) { - // the filter instance created here is always valid for in-memory - // containers - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Filterable#removeAllContainerFilters() - */ - @Override - public void removeAllContainerFilters() { - if (!getFilters().isEmpty()) { - for (Item item : itemIdToItem.values()) { - removeAllValueChangeListeners(item); - } - removeAllFilters(); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.Container.Filterable#removeContainerFilters(java.lang - * .Object) - */ - @Override - public void removeContainerFilters(Object propertyId) { - Collection removedFilters = super.removeFilters(propertyId); - if (!removedFilters.isEmpty()) { - // stop listening to change events for the property - for (Item item : itemIdToItem.values()) { - removeValueChangeListener(item, propertyId); - } - } - } - - @Override - public void addContainerFilter(Filter filter) - throws UnsupportedFilterException { - addFilter(filter); - } - - @Override - public void removeContainerFilter(Filter filter) { - removeFilter(filter); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.AbstractInMemoryContainer#hasContainerFilters() - */ - @Override - public boolean hasContainerFilters() { - return super.hasContainerFilters(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() - */ - @Override - public Collection getContainerFilters() { - return super.getContainerFilters(); - } - - /** - * Make this container listen to the given property provided it notifies - * when its value changes. - * - * @param item - * The {@link Item} that contains the property - * @param propertyId - * The id of the property - */ - private void addValueChangeListener(Item item, Object propertyId) { - Property property = item.getItemProperty(propertyId); - if (property instanceof ValueChangeNotifier) { - // avoid multiple notifications for the same property if - // multiple filters are in use - ValueChangeNotifier notifier = (ValueChangeNotifier) property; - notifier.removeListener(this); - notifier.addListener(this); - } - } - - /** - * Remove this container as a listener for the given property. - * - * @param item - * The {@link Item} that contains the property - * @param propertyId - * The id of the property - */ - private void removeValueChangeListener(Item item, Object propertyId) { - Property property = item.getItemProperty(propertyId); - if (property instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) property).removeListener(this); - } - } - - /** - * Remove this contains as a listener for all the properties in the given - * {@link Item}. - * - * @param item - * The {@link Item} that contains the properties - */ - private void removeAllValueChangeListeners(Item item) { - for (Object propertyId : item.getItemPropertyIds()) { - removeValueChangeListener(item, propertyId); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds() - */ - @Override - public Collection getSortableContainerPropertyIds() { - return getSortablePropertyIds(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], - * boolean[]) - */ - @Override - public void sort(Object[] propertyId, boolean[] ascending) { - sortContainer(propertyId, ascending); - } - - @Override - public ItemSorter getItemSorter() { - return super.getItemSorter(); - } - - @Override - public void setItemSorter(ItemSorter itemSorter) { - super.setItemSorter(itemSorter); - } - - @Override - protected void registerNewItem(int position, IDTYPE itemId, - BeanItem item) { - itemIdToItem.put(itemId, item); - - // add listeners to be able to update filtering on property - // changes - for (Filter filter : getFilters()) { - for (String propertyId : getContainerPropertyIds()) { - if (filter.appliesToProperty(propertyId)) { - // addValueChangeListener avoids adding duplicates - addValueChangeListener(item, propertyId); - } - } - } - } - - /** - * Check that a bean can be added to the container (is of the correct type - * for the container). - * - * @param bean - * @return - */ - private boolean validateBean(BEANTYPE bean) { - return bean != null && getBeanType().isAssignableFrom(bean.getClass()); - } - - /** - * Adds the bean to the Container. - * - * Note: the behavior of this method changed in Vaadin 6.6 - now items are - * added at the very end of the unfiltered container and not after the last - * visible item if filtering is used. - * - * @see com.vaadin.data.Container#addItem(Object) - */ - protected BeanItem addItem(IDTYPE itemId, BEANTYPE bean) { - if (!validateBean(bean)) { - return null; - } - return internalAddItemAtEnd(itemId, createBeanItem(bean), true); - } - - /** - * Adds the bean after the given bean. - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(Object, Object) - */ - protected BeanItem addItemAfter(IDTYPE previousItemId, - IDTYPE newItemId, BEANTYPE bean) { - if (!validateBean(bean)) { - return null; - } - return internalAddItemAfter(previousItemId, newItemId, - createBeanItem(bean), true); - } - - /** - * Adds a new bean at the given index. - * - * The bean is used both as the item contents and as the item identifier. - * - * @param index - * Index at which the bean should be added. - * @param newItemId - * The item id for the bean to add to the container. - * @param bean - * The bean to add to the container. - * - * @return Returns the new BeanItem or null if the operation fails. - */ - protected BeanItem addItemAt(int index, IDTYPE newItemId, - BEANTYPE bean) { - if (!validateBean(bean)) { - return null; - } - return internalAddItemAt(index, newItemId, createBeanItem(bean), true); - } - - /** - * Adds a bean to the container using the bean item id resolver to find its - * identifier. - * - * A bean id resolver must be set before calling this method. - * - * @see #addItem(Object, Object) - * - * @param bean - * the bean to add - * @return BeanItem item added or null - * @throws IllegalStateException - * if no bean identifier resolver has been set - * @throws IllegalArgumentException - * if an identifier cannot be resolved for the bean - */ - protected BeanItem addBean(BEANTYPE bean) - throws IllegalStateException, IllegalArgumentException { - if (bean == null) { - return null; - } - IDTYPE itemId = resolveBeanId(bean); - if (itemId == null) { - throw new IllegalArgumentException( - "Resolved identifier for a bean must not be null"); - } - return addItem(itemId, bean); - } - - /** - * Adds a bean to the container after a specified item identifier, using the - * bean item id resolver to find its identifier. - * - * A bean id resolver must be set before calling this method. - * - * @see #addItemAfter(Object, Object, Object) - * - * @param previousItemId - * the identifier of the bean after which this bean should be - * added, null to add to the beginning - * @param bean - * the bean to add - * @return BeanItem item added or null - * @throws IllegalStateException - * if no bean identifier resolver has been set - * @throws IllegalArgumentException - * if an identifier cannot be resolved for the bean - */ - protected BeanItem addBeanAfter(IDTYPE previousItemId, - BEANTYPE bean) - throws IllegalStateException, IllegalArgumentException { - if (bean == null) { - return null; - } - IDTYPE itemId = resolveBeanId(bean); - if (itemId == null) { - throw new IllegalArgumentException( - "Resolved identifier for a bean must not be null"); - } - return addItemAfter(previousItemId, itemId, bean); - } - - /** - * Adds a bean at a specified (filtered view) position in the container - * using the bean item id resolver to find its identifier. - * - * A bean id resolver must be set before calling this method. - * - * @see #addItemAfter(Object, Object, Object) - * - * @param index - * the index (in the filtered view) at which to add the item - * @param bean - * the bean to add - * @return BeanItem item added or null - * @throws IllegalStateException - * if no bean identifier resolver has been set - * @throws IllegalArgumentException - * if an identifier cannot be resolved for the bean - */ - protected BeanItem addBeanAt(int index, BEANTYPE bean) - throws IllegalStateException, IllegalArgumentException { - if (bean == null) { - return null; - } - IDTYPE itemId = resolveBeanId(bean); - if (itemId == null) { - throw new IllegalArgumentException( - "Resolved identifier for a bean must not be null"); - } - return addItemAt(index, itemId, bean); - } - - /** - * Adds all the beans from a {@link Collection} in one operation using the - * bean item identifier resolver. More efficient than adding them one by - * one. - * - * A bean id resolver must be set before calling this method. - * - * Note: the behavior of this method changed in Vaadin 6.6 - now items are - * added at the very end of the unfiltered container and not after the last - * visible item if filtering is used. - * - * @param collection - * The collection of beans to add. Must not be null. - * @throws IllegalStateException - * if no bean identifier resolver has been set - * @throws IllegalArgumentException - * if the resolver returns a null itemId for one of the beans in - * the collection - */ - protected void addAll(Collection collection) - throws IllegalStateException, IllegalArgumentException { - boolean modified = false; - int origSize = size(); - - for (BEANTYPE bean : collection) { - // TODO skipping invalid beans - should not allow them in javadoc? - if (bean == null - || !getBeanType().isAssignableFrom(bean.getClass())) { - continue; - } - IDTYPE itemId = resolveBeanId(bean); - if (itemId == null) { - throw new IllegalArgumentException( - "Resolved identifier for a bean must not be null"); - } - - if (internalAddItemAtEnd(itemId, createBeanItem(bean), - false) != null) { - modified = true; - } - } - - if (modified) { - // Filter the contents when all items have been added - if (isFiltered()) { - doFilterContainer(!getFilters().isEmpty()); - } - if (visibleNewItemsWasAdded(origSize)) { - // fire event about added items - int firstPosition = origSize; - IDTYPE firstItemId = getVisibleItemIds().get(firstPosition); - int affectedItems = size() - origSize; - fireItemsAdded(firstPosition, firstItemId, affectedItems); - } - } - } - - private boolean visibleNewItemsWasAdded(int origSize) { - return size() > origSize; - } - - /** - * Use the bean resolver to get the identifier for a bean. - * - * @param bean - * @return resolved bean identifier, null if could not be resolved - * @throws IllegalStateException - * if no bean resolver is set - */ - protected IDTYPE resolveBeanId(BEANTYPE bean) { - if (beanIdResolver == null) { - throw new IllegalStateException( - "Bean item identifier resolver is required."); - } - return beanIdResolver.getIdForBean(bean); - } - - /** - * Sets the resolver that finds the item id for a bean, or null not to use - * automatic resolving. - * - * Methods that add a bean without specifying an id must not be called if no - * resolver has been set. - * - * Note that methods taking an explicit id can be used whether a resolver - * has been defined or not. - * - * @param beanIdResolver - * to use or null to disable automatic id resolution - */ - protected void setBeanIdResolver( - BeanIdResolver beanIdResolver) { - this.beanIdResolver = beanIdResolver; - } - - /** - * Returns the resolver that finds the item ID for a bean. - * - * @return resolver used or null if automatic item id resolving is disabled - */ - public BeanIdResolver getBeanIdResolver() { - return beanIdResolver; - } - - /** - * Create an item identifier resolver using a named bean property. - * - * @param propertyId - * property identifier, which must map to a getter in BEANTYPE - * @return created resolver - */ - protected BeanIdResolver createBeanPropertyResolver( - Object propertyId) { - return new PropertyBasedBeanIdResolver(propertyId); - } - - /** - * @deprecated As of 7.0, replaced by {@link #addPropertySetChangeListener} - **/ - @Deprecated - @Override - public void addListener(Container.PropertySetChangeListener listener) { - addPropertySetChangeListener(listener); - } - - @Override - public void addPropertySetChangeListener( - Container.PropertySetChangeListener listener) { - super.addPropertySetChangeListener(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removePropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Deprecated - @Override - public void removeListener(Container.PropertySetChangeListener listener) { - removePropertySetChangeListener(listener); - } - - @Override - public void removePropertySetChangeListener( - Container.PropertySetChangeListener listener) { - super.removePropertySetChangeListener(listener); - } - - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Use addNestedContainerProperty(String) to add container properties to a " - + getClass().getSimpleName()); - } - - /** - * Adds a property for the container and all its items. - * - * Primarily for internal use, may change in future versions. - * - * @param propertyId - * @param propertyDescriptor - * @return true if the property was added - */ - protected final boolean addContainerProperty(String propertyId, - VaadinPropertyDescriptor propertyDescriptor) { - if (null == propertyId || null == propertyDescriptor) { - return false; - } - - // Fails if the Property is already present - if (model.containsKey(propertyId)) { - return false; - } - - model.put(propertyId, propertyDescriptor); - for (BeanItem item : itemIdToItem.values()) { - item.addItemProperty(propertyId, - propertyDescriptor.createProperty(item.getBean())); - } - - // Sends a change event - fireContainerPropertySetChange(); - - return true; - } - - /** - * Adds a nested container property for the container, e.g. - * "manager.address.street". - * - * All intermediate getters must exist and should return non-null values - * when the property value is accessed. If an intermediate getter returns - * null, a null value will be returned. - * - * @see NestedMethodProperty - * - * @param propertyId - * @return true if the property was added - */ - public boolean addNestedContainerProperty(String propertyId) { - return addContainerProperty(propertyId, - new NestedPropertyDescriptor(propertyId, type)); - } - - /** - * Adds a nested container properties for all sub-properties of a named - * property to the container. The named property itself is removed from the - * model as its subproperties are added. - * - * All intermediate getters must exist and should return non-null values - * when the property value is accessed. If an intermediate getter returns - * null, a null value will be returned. - * - * @see NestedMethodProperty - * @see #addNestedContainerProperty(String) - * - * @param propertyId - */ - @SuppressWarnings("unchecked") - public void addNestedContainerBean(String propertyId) { - Class propertyType = getType(propertyId); - LinkedHashMap> pds = BeanItem - .getPropertyDescriptors((Class) propertyType); - for (String subPropertyId : pds.keySet()) { - String qualifiedPropertyId = propertyId + "." + subPropertyId; - NestedPropertyDescriptor pd = new NestedPropertyDescriptor( - qualifiedPropertyId, (Class) type); - model.put(qualifiedPropertyId, pd); - model.remove(propertyId); - for (BeanItem item : itemIdToItem.values()) { - item.addItemProperty(qualifiedPropertyId, - pd.createProperty(item.getBean())); - item.removeItemProperty(propertyId); - } - } - - // Sends a change event - fireContainerPropertySetChange(); - } - - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - // Fails if the Property is not present - if (!model.containsKey(propertyId)) { - return false; - } - - // Removes the Property to Property list and types - model.remove(propertyId); - - // If remove the Property from all Items - for (final Iterator i = getAllItemIds().iterator(); i - .hasNext();) { - getUnfilteredItem(i.next()).removeItemProperty(propertyId); - } - - // Sends a change event - fireContainerPropertySetChange(); - - return true; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/AbstractContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/AbstractContainer.java deleted file mode 100644 index 1995102345..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/AbstractContainer.java +++ /dev/null @@ -1,307 +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.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.EventObject; -import java.util.LinkedList; - -import com.vaadin.data.Container; - -/** - * Abstract container class that manages event listeners and sending events to - * them ({@link PropertySetChangeNotifier}, {@link ItemSetChangeNotifier}). - * - * Note that this class provides the internal implementations for both types of - * events and notifiers as protected methods, but does not implement the - * {@link PropertySetChangeNotifier} and {@link ItemSetChangeNotifier} - * interfaces directly. This way, subclasses can choose not to implement them. - * Subclasses implementing those interfaces should also override the - * corresponding {@link #addListener()} and {@link #removeListener()} methods to - * make them public. - * - * @since 6.6 - */ -public abstract class AbstractContainer implements Container { - - /** - * List of all Property set change event listeners. - */ - private Collection propertySetChangeListeners = null; - - /** - * List of all container Item set change event listeners. - */ - private Collection itemSetChangeListeners = null; - - /** - * An event object specifying the container whose Property set - * has changed. - * - * This class does not provide information about which properties were - * concerned by the change, but subclasses can provide additional - * information about the changes. - */ - protected static class BasePropertySetChangeEvent extends EventObject - implements Container.PropertySetChangeEvent, Serializable { - - protected BasePropertySetChangeEvent(Container source) { - super(source); - } - - @Override - public Container getContainer() { - return (Container) getSource(); - } - } - - /** - * An event object specifying the container whose Item set has - * changed. - * - * This class does not provide information about the exact changes - * performed, but subclasses can add provide additional information about - * the changes. - */ - protected static class BaseItemSetChangeEvent extends EventObject - implements Container.ItemSetChangeEvent, Serializable { - - protected BaseItemSetChangeEvent(Container source) { - super(source); - } - - @Override - public Container getContainer() { - return (Container) getSource(); - } - } - - // PropertySetChangeNotifier - - /** - * Implementation of the corresponding method in - * {@link PropertySetChangeNotifier}, override with the corresponding public - * method and implement the interface to use this. - * - * @see PropertySetChangeNotifier#addListener(com.vaadin.data.Container.PropertySetChangeListener) - */ - protected void addPropertySetChangeListener( - Container.PropertySetChangeListener listener) { - if (getPropertySetChangeListeners() == null) { - setPropertySetChangeListeners( - new LinkedList()); - } - getPropertySetChangeListeners().add(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addPropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Deprecated - protected void addListener(Container.PropertySetChangeListener listener) { - addPropertySetChangeListener(listener); - } - - /** - * Implementation of the corresponding method in - * {@link PropertySetChangeNotifier}, override with the corresponding public - * method and implement the interface to use this. - * - * @see PropertySetChangeNotifier#removeListener(com.vaadin.data.Container. - * PropertySetChangeListener) - */ - protected void removePropertySetChangeListener( - Container.PropertySetChangeListener listener) { - if (getPropertySetChangeListeners() != null) { - getPropertySetChangeListeners().remove(listener); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removePropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Deprecated - protected void removeListener( - Container.PropertySetChangeListener listener) { - removePropertySetChangeListener(listener); - } - - // ItemSetChangeNotifier - - /** - * Implementation of the corresponding method in - * {@link ItemSetChangeNotifier}, override with the corresponding public - * method and implement the interface to use this. - * - * @see ItemSetChangeNotifier#addListener(com.vaadin.data.Container.ItemSetChangeListener) - */ - protected void addItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (getItemSetChangeListeners() == null) { - setItemSetChangeListeners( - new LinkedList()); - } - getItemSetChangeListeners().add(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Deprecated - protected void addListener(Container.ItemSetChangeListener listener) { - addItemSetChangeListener(listener); - } - - /** - * Implementation of the corresponding method in - * {@link ItemSetChangeNotifier}, override with the corresponding public - * method and implement the interface to use this. - * - * @see ItemSetChangeNotifier#removeListener(com.vaadin.data.Container.ItemSetChangeListener) - */ - protected void removeItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (getItemSetChangeListeners() != null) { - getItemSetChangeListeners().remove(listener); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Deprecated - protected void removeListener(Container.ItemSetChangeListener listener) { - removeItemSetChangeListener(listener); - } - - /** - * Sends a simple Property set change event to all interested listeners. - */ - protected void fireContainerPropertySetChange() { - fireContainerPropertySetChange(new BasePropertySetChangeEvent(this)); - } - - /** - * Sends a Property set change event to all interested listeners. - * - * Use {@link #fireContainerPropertySetChange()} instead of this method - * unless additional information about the exact changes is available and - * should be included in the event. - * - * @param event - * the property change event to send, optionally with additional - * information - */ - protected void fireContainerPropertySetChange( - Container.PropertySetChangeEvent event) { - if (getPropertySetChangeListeners() != null) { - final Object[] l = getPropertySetChangeListeners().toArray(); - for (int i = 0; i < l.length; i++) { - ((Container.PropertySetChangeListener) l[i]) - .containerPropertySetChange(event); - } - } - } - - /** - * Sends a simple Item set change event to all interested listeners, - * indicating that anything in the contents may have changed (items added, - * removed etc.). - */ - protected void fireItemSetChange() { - fireItemSetChange(new BaseItemSetChangeEvent(this)); - } - - /** - * Sends an Item set change event to all registered interested listeners. - * - * @param event - * the item set change event to send, optionally with additional - * information - */ - protected void fireItemSetChange(ItemSetChangeEvent event) { - if (getItemSetChangeListeners() != null) { - final Object[] l = getItemSetChangeListeners().toArray(); - for (int i = 0; i < l.length; i++) { - ((Container.ItemSetChangeListener) l[i]) - .containerItemSetChange(event); - } - } - } - - /** - * Sets the property set change listener collection. For internal use only. - * - * @param propertySetChangeListeners - */ - protected void setPropertySetChangeListeners( - Collection propertySetChangeListeners) { - this.propertySetChangeListeners = propertySetChangeListeners; - } - - /** - * Returns the property set change listener collection. For internal use - * only. - */ - protected Collection getPropertySetChangeListeners() { - return propertySetChangeListeners; - } - - /** - * Sets the item set change listener collection. For internal use only. - * - * @param itemSetChangeListeners - */ - protected void setItemSetChangeListeners( - Collection itemSetChangeListeners) { - this.itemSetChangeListeners = itemSetChangeListeners; - } - - /** - * Returns the item set change listener collection. For internal use only. - */ - protected Collection getItemSetChangeListeners() { - return itemSetChangeListeners; - } - - public Collection getListeners(Class eventType) { - if (Container.PropertySetChangeEvent.class - .isAssignableFrom(eventType)) { - if (propertySetChangeListeners == null) { - return Collections.EMPTY_LIST; - } else { - return Collections - .unmodifiableCollection(propertySetChangeListeners); - } - } else if (Container.ItemSetChangeEvent.class - .isAssignableFrom(eventType)) { - if (itemSetChangeListeners == null) { - return Collections.EMPTY_LIST; - } else { - return Collections - .unmodifiableCollection(itemSetChangeListeners); - } - } - - return Collections.EMPTY_LIST; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/AbstractInMemoryContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/AbstractInMemoryContainer.java deleted file mode 100644 index 43b65ab75e..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/AbstractInMemoryContainer.java +++ /dev/null @@ -1,1165 +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.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.EventObject; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.ItemSetChangeNotifier; -import com.vaadin.data.Item; -import com.vaadin.data.util.filter.SimpleStringFilter; -import com.vaadin.data.util.filter.UnsupportedFilterException; - -/** - * Abstract {@link Container} class that handles common functionality for - * in-memory containers. Concrete in-memory container classes can either inherit - * this class, inherit {@link AbstractContainer}, or implement the - * {@link Container} interface directly. - * - * Adding and removing items (if desired) must be implemented in subclasses by - * overriding the appropriate add*Item() and remove*Item() and removeAllItems() - * methods, calling the corresponding - * {@link #internalAddItemAfter(Object, Object, Item)}, - * {@link #internalAddItemAt(int, Object, Item)}, - * {@link #internalAddItemAtEnd(Object, Item, boolean)}, - * {@link #internalRemoveItem(Object)} and {@link #internalRemoveAllItems()} - * methods. - * - * By default, adding and removing container properties is not supported, and - * subclasses need to implement {@link #getContainerPropertyIds()}. Optionally, - * subclasses can override {@link #addContainerProperty(Object, Class, Object)} - * and {@link #removeContainerProperty(Object)} to implement them. - * - * Features: - *
    - *
  • {@link Container.Ordered} - *
  • {@link Container.Indexed} - *
  • {@link Filterable} and {@link SimpleFilterable} (internal implementation, - * does not implement the interface directly) - *
  • {@link Sortable} (internal implementation, does not implement the - * interface directly) - *
- * - * To implement {@link Sortable}, subclasses need to implement - * {@link #getSortablePropertyIds()} and call the superclass method - * {@link #sortContainer(Object[], boolean[])} in the method - * sort(Object[], boolean[]). - * - * To implement {@link Filterable}, subclasses need to implement the methods - * {@link Filterable#addContainerFilter(com.vaadin.data.Container.Filter)} - * (calling {@link #addFilter(Filter)}), - * {@link Filterable#removeAllContainerFilters()} (calling - * {@link #removeAllFilters()}) and - * {@link Filterable#removeContainerFilter(com.vaadin.data.Container.Filter)} - * (calling {@link #removeFilter(com.vaadin.data.Container.Filter)}). - * - * To implement {@link SimpleFilterable}, subclasses also need to implement the - * methods - * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)} - * and {@link SimpleFilterable#removeContainerFilters(Object)} calling - * {@link #addFilter(com.vaadin.data.Container.Filter)} and - * {@link #removeFilters(Object)} respectively. - * - * @param - * the class of item identifiers in the container, use Object if can - * be any class - * @param - * the class of property identifiers for the items in the container, - * use Object if can be any class - * @param - * the (base) class of the Item instances in the container, use - * {@link Item} if unknown - * - * @since 6.6 - */ -public abstract class AbstractInMemoryContainer - extends AbstractContainer - implements ItemSetChangeNotifier, Container.Indexed { - - /** - * An ordered {@link List} of all item identifiers in the container, - * including those that have been filtered out. - * - * Must not be null. - */ - private List allItemIds; - - /** - * An ordered {@link List} of item identifiers in the container after - * filtering, excluding those that have been filtered out. - * - * This is what the external API of the {@link Container} interface and its - * subinterfaces shows (e.g. {@link #size()}, {@link #nextItemId(Object)}). - * - * If null, the full item id list is used instead. - */ - private List filteredItemIds; - - /** - * Filters that are applied to the container to limit the items visible in - * it - */ - private Set filters = new HashSet(); - - /** - * The item sorter which is used for sorting the container. - */ - private ItemSorter itemSorter = new DefaultItemSorter(); - - // Constructors - - /** - * Constructor for an abstract in-memory container. - */ - protected AbstractInMemoryContainer() { - setAllItemIds(new ListSet()); - } - - // Container interface methods with more specific return class - - // default implementation, can be overridden - @Override - public ITEMCLASS getItem(Object itemId) { - if (containsId(itemId)) { - return getUnfilteredItem(itemId); - } else { - return null; - } - } - - private static abstract class BaseItemAddOrRemoveEvent extends EventObject - implements Serializable { - protected Object itemId; - protected int index; - protected int count; - - public BaseItemAddOrRemoveEvent(Container source, Object itemId, - int index, int count) { - super(source); - this.itemId = itemId; - this.index = index; - this.count = count; - } - - public Container getContainer() { - return (Container) getSource(); - } - - public Object getFirstItemId() { - return itemId; - } - - public int getFirstIndex() { - return index; - } - - public int getAffectedItemsCount() { - return count; - } - } - - /** - * An Event object specifying information about the added - * items. - * - *

- * This class provides information about the first added item and the number - * of added items. - *

- * - * @since 7.4 - */ - protected static class BaseItemAddEvent extends BaseItemAddOrRemoveEvent - implements Container.Indexed.ItemAddEvent { - - public BaseItemAddEvent(Container source, Object itemId, int index, - int count) { - super(source, itemId, index, count); - } - - @Override - public int getAddedItemsCount() { - return getAffectedItemsCount(); - } - } - - /** - * An Event object specifying information about the removed - * items. - * - *

- * This class provides information about the first removed item and the - * number of removed items. - *

- * - * @since 7.4 - */ - protected static class BaseItemRemoveEvent extends BaseItemAddOrRemoveEvent - implements Container.Indexed.ItemRemoveEvent { - - public BaseItemRemoveEvent(Container source, Object itemId, int index, - int count) { - super(source, itemId, index, count); - } - - @Override - public int getRemovedItemsCount() { - return getAffectedItemsCount(); - } - } - - /** - * Get an item even if filtered out. - * - * For internal use only. - * - * @param itemId - * @return - */ - protected abstract ITEMCLASS getUnfilteredItem(Object itemId); - - // cannot override getContainerPropertyIds() and getItemIds(): if subclass - // uses Object as ITEMIDCLASS or PROPERTYIDCLASS, Collection cannot - // be cast to Collection - - // public abstract Collection getContainerPropertyIds(); - // public abstract Collection getItemIds(); - - // Container interface method implementations - - @Override - public int size() { - return getVisibleItemIds().size(); - } - - @Override - public boolean containsId(Object itemId) { - // only look at visible items after filtering - if (itemId == null) { - return false; - } else { - return getVisibleItemIds().contains(itemId); - } - } - - @Override - public List getItemIds() { - return Collections.unmodifiableList(getVisibleItemIds()); - } - - // Container.Ordered - - @Override - public ITEMIDTYPE nextItemId(Object itemId) { - int index = indexOfId(itemId); - if (index >= 0 && index < size() - 1) { - return getIdByIndex(index + 1); - } else { - // out of bounds - return null; - } - } - - @Override - public ITEMIDTYPE prevItemId(Object itemId) { - int index = indexOfId(itemId); - if (index > 0) { - return getIdByIndex(index - 1); - } else { - // out of bounds - return null; - } - } - - @Override - public ITEMIDTYPE firstItemId() { - if (size() > 0) { - return getIdByIndex(0); - } else { - return null; - } - } - - @Override - public ITEMIDTYPE lastItemId() { - if (size() > 0) { - return getIdByIndex(size() - 1); - } else { - return null; - } - } - - @Override - public boolean isFirstId(Object itemId) { - if (itemId == null) { - return false; - } - return itemId.equals(firstItemId()); - } - - @Override - public boolean isLastId(Object itemId) { - if (itemId == null) { - return false; - } - return itemId.equals(lastItemId()); - } - - // Container.Indexed - - @Override - public ITEMIDTYPE getIdByIndex(int index) { - return getVisibleItemIds().get(index); - } - - @Override - public List getItemIds(int startIndex, int numberOfIds) { - if (startIndex < 0) { - throw new IndexOutOfBoundsException( - "Start index cannot be negative! startIndex=" + startIndex); - } - - if (startIndex > getVisibleItemIds().size()) { - throw new IndexOutOfBoundsException( - "Start index exceeds container size! startIndex=" - + startIndex + " containerLastItemIndex=" - + (getVisibleItemIds().size() - 1)); - } - - if (numberOfIds < 1) { - if (numberOfIds == 0) { - return Collections.emptyList(); - } - - throw new IllegalArgumentException( - "Cannot get negative amount of items! numberOfItems=" - + numberOfIds); - } - - int endIndex = startIndex + numberOfIds; - - if (endIndex > getVisibleItemIds().size()) { - endIndex = getVisibleItemIds().size(); - } - - return Collections.unmodifiableList( - getVisibleItemIds().subList(startIndex, endIndex)); - - } - - @Override - public int indexOfId(Object itemId) { - return getVisibleItemIds().indexOf(itemId); - } - - // methods that are unsupported by default, override to support - - @Override - public Object addItemAt(int index) throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public Item addItemAt(int index, Object newItemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public Object addItemAfter(Object previousItemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public Item addItemAfter(Object previousItemId, Object newItemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public Item addItem(Object itemId) throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public Object addItem() throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Removing items not supported. Override the removeItem() method if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public boolean removeAllItems() throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Removing items not supported. Override the removeAllItems() method if required as specified in AbstractInMemoryContainer javadoc."); - } - - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Adding container properties not supported. Override the addContainerProperty() method if required."); - } - - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Removing container properties not supported. Override the addContainerProperty() method if required."); - } - - // ItemSetChangeNotifier - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Deprecated - @Override - public void addListener(Container.ItemSetChangeListener listener) { - addItemSetChangeListener(listener); - } - - @Override - public void addItemSetChangeListener( - Container.ItemSetChangeListener listener) { - super.addItemSetChangeListener(listener); - } - - @Override - public void removeItemSetChangeListener( - Container.ItemSetChangeListener listener) { - super.removeItemSetChangeListener(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Deprecated - @Override - public void removeListener(Container.ItemSetChangeListener listener) { - removeItemSetChangeListener(listener); - } - - // internal methods - - // Filtering support - - /** - * Filter the view to recreate the visible item list from the unfiltered - * items, and send a notification if the set of visible items changed in any - * way. - */ - protected void filterAll() { - if (doFilterContainer(!getFilters().isEmpty())) { - fireItemSetChange(); - } - } - - /** - * Filters the data in the container and updates internal data structures. - * This method should reset any internal data structures and then repopulate - * them so {@link #getItemIds()} and other methods only return the filtered - * items. - * - * @param hasFilters - * true if filters has been set for the container, false - * otherwise - * @return true if the item set has changed as a result of the filtering - */ - protected boolean doFilterContainer(boolean hasFilters) { - if (!hasFilters) { - boolean changed = getAllItemIds().size() != getVisibleItemIds() - .size(); - setFilteredItemIds(null); - return changed; - } - - // Reset filtered list - List originalFilteredItemIds = getFilteredItemIds(); - boolean wasUnfiltered = false; - if (originalFilteredItemIds == null) { - originalFilteredItemIds = Collections.emptyList(); - wasUnfiltered = true; - } - setFilteredItemIds(new ListSet()); - - // Filter - boolean equal = true; - Iterator origIt = originalFilteredItemIds.iterator(); - for (final Iterator i = getAllItemIds().iterator(); i - .hasNext();) { - final ITEMIDTYPE id = i.next(); - if (passesFilters(id)) { - // filtered list comes from the full list, can use == - equal = equal && origIt.hasNext() && origIt.next() == id; - getFilteredItemIds().add(id); - } - } - - return (wasUnfiltered && !getAllItemIds().isEmpty()) || !equal - || origIt.hasNext(); - } - - /** - * Checks if the given itemId passes the filters set for the container. The - * caller should make sure the itemId exists in the container. For - * non-existing itemIds the behavior is undefined. - * - * @param itemId - * An itemId that exists in the container. - * @return true if the itemId passes all filters or no filters are set, - * false otherwise. - */ - protected boolean passesFilters(Object itemId) { - ITEMCLASS item = getUnfilteredItem(itemId); - if (getFilters().isEmpty()) { - return true; - } - final Iterator i = getFilters().iterator(); - while (i.hasNext()) { - final Filter f = i.next(); - if (!f.passesFilter(itemId, item)) { - return false; - } - } - return true; - } - - /** - * Adds a container filter and re-filter the view. - * - * The filter must implement Filter and its sub-filters (if any) must also - * be in-memory filterable. - * - * This can be used to implement - * {@link Filterable#addContainerFilter(com.vaadin.data.Container.Filter)} - * and optionally also - * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)} - * (with {@link SimpleStringFilter}). - * - * Note that in some cases, incompatible filters cannot be detected when - * added and an {@link UnsupportedFilterException} may occur when performing - * filtering. - * - * @throws UnsupportedFilterException - * if the filter is detected as not supported by the container - */ - protected void addFilter(Filter filter) throws UnsupportedFilterException { - getFilters().add(filter); - filterAll(); - } - - /** - * Returns true if any filters have been applied to the container. - * - * @return true if the container has filters applied, false otherwise - * @since 7.1 - */ - protected boolean hasContainerFilters() { - return !getContainerFilters().isEmpty(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Filterable#getContainerFilters() - */ - protected Collection getContainerFilters() { - return Collections.unmodifiableCollection(filters); - } - - /** - * Remove a specific container filter and re-filter the view (if necessary). - * - * This can be used to implement - * {@link Filterable#removeContainerFilter(com.vaadin.data.Container.Filter)} - * . - */ - protected void removeFilter(Filter filter) { - for (Iterator iterator = getFilters().iterator(); iterator - .hasNext();) { - Filter f = iterator.next(); - if (f.equals(filter)) { - iterator.remove(); - filterAll(); - return; - } - } - } - - /** - * Remove all container filters for all properties and re-filter the view. - * - * This can be used to implement - * {@link Filterable#removeAllContainerFilters()}. - */ - protected void removeAllFilters() { - if (getFilters().isEmpty()) { - return; - } - getFilters().clear(); - filterAll(); - } - - /** - * Checks if there is a filter that applies to a given property. - * - * @param propertyId - * @return true if there is an active filter for the property - */ - protected boolean isPropertyFiltered(Object propertyId) { - if (getFilters().isEmpty() || propertyId == null) { - return false; - } - final Iterator i = getFilters().iterator(); - while (i.hasNext()) { - final Filter f = i.next(); - if (f.appliesToProperty(propertyId)) { - return true; - } - } - return false; - } - - /** - * Remove all container filters for a given property identifier and - * re-filter the view. This also removes filters applying to multiple - * properties including the one identified by propertyId. - * - * This can be used to implement - * {@link Filterable#removeContainerFilters(Object)}. - * - * @param propertyId - * @return Collection removed filters - */ - protected Collection removeFilters(Object propertyId) { - if (getFilters().isEmpty() || propertyId == null) { - return Collections.emptyList(); - } - List removedFilters = new LinkedList(); - for (Iterator iterator = getFilters().iterator(); iterator - .hasNext();) { - Filter f = iterator.next(); - if (f.appliesToProperty(propertyId)) { - removedFilters.add(f); - iterator.remove(); - } - } - if (!removedFilters.isEmpty()) { - filterAll(); - return removedFilters; - } - return Collections.emptyList(); - } - - // sorting - - /** - * Returns the ItemSorter used for comparing items in a sort. See - * {@link #setItemSorter(ItemSorter)} for more information. - * - * @return The ItemSorter used for comparing two items in a sort. - */ - protected ItemSorter getItemSorter() { - return itemSorter; - } - - /** - * Sets the ItemSorter used for comparing items in a sort. The - * {@link ItemSorter#compare(Object, Object)} method is called with item ids - * to perform the sorting. A default ItemSorter is used if this is not - * explicitly set. - * - * @param itemSorter - * The ItemSorter used for comparing two items in a sort (not - * null). - */ - protected void setItemSorter(ItemSorter itemSorter) { - this.itemSorter = itemSorter; - } - - /** - * Sort base implementation to be used to implement {@link Sortable}. - * - * Subclasses should call this from a public - * {@link #sort(Object[], boolean[])} method when implementing Sortable. - * - * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], - * boolean[]) - */ - protected void sortContainer(Object[] propertyId, boolean[] ascending) { - if (!(this instanceof Sortable)) { - throw new UnsupportedOperationException( - "Cannot sort a Container that does not implement Sortable"); - } - - // Set up the item sorter for the sort operation - getItemSorter().setSortProperties((Sortable) this, propertyId, - ascending); - - // Perform the actual sort - doSort(); - - // Post sort updates - if (isFiltered()) { - filterAll(); - } else { - fireItemSetChange(); - } - - } - - /** - * Perform the sorting of the data structures in the container. This is - * invoked when the itemSorter has been prepared for the sort - * operation. Typically this method calls - * Collections.sort(aCollection, getItemSorter()) on all arrays - * (containing item ids) that need to be sorted. - * - */ - protected void doSort() { - Collections.sort(getAllItemIds(), getItemSorter()); - } - - /** - * Returns the sortable property identifiers for the container. Can be used - * to implement {@link Sortable#getSortableContainerPropertyIds()}. - */ - protected Collection getSortablePropertyIds() { - LinkedList sortables = new LinkedList(); - for (Object propertyId : getContainerPropertyIds()) { - Class propertyType = getType(propertyId); - if (Comparable.class.isAssignableFrom(propertyType) - || propertyType.isPrimitive()) { - sortables.add(propertyId); - } - } - return sortables; - } - - // removing items - - /** - * Removes all items from the internal data structures of this class. This - * can be used to implement {@link #removeAllItems()} in subclasses. - * - * No notification is sent, the caller has to fire a suitable item set - * change notification. - */ - protected void internalRemoveAllItems() { - // Removes all Items - getAllItemIds().clear(); - if (isFiltered()) { - getFilteredItemIds().clear(); - } - } - - /** - * Removes a single item from the internal data structures of this class. - * This can be used to implement {@link #removeItem(Object)} in subclasses. - * - * No notification is sent, the caller has to fire a suitable item set - * change notification. - * - * @param itemId - * the identifier of the item to remove - * @return true if an item was successfully removed, false if failed to - * remove or no such item - */ - protected boolean internalRemoveItem(Object itemId) { - if (itemId == null) { - return false; - } - - boolean result = getAllItemIds().remove(itemId); - if (result && isFiltered()) { - getFilteredItemIds().remove(itemId); - } - - return result; - } - - // adding items - - /** - * Adds the bean to all internal data structures at the given position. - * Fails if an item with itemId is already in the container. Returns a the - * item if it was added successfully, null otherwise. - * - *

- * Caller should initiate filtering after calling this method. - *

- * - * For internal use only - subclasses should use - * {@link #internalAddItemAtEnd(Object, Item, boolean)}, - * {@link #internalAddItemAt(int, Object, Item, boolean)} and - * {@link #internalAddItemAfter(Object, Object, Item, boolean)} instead. - * - * @param position - * The position at which the item should be inserted in the - * unfiltered collection of items - * @param itemId - * The item identifier for the item to insert - * @param item - * The item to insert - * - * @return ITEMCLASS if the item was added successfully, null otherwise - */ - private ITEMCLASS internalAddAt(int position, ITEMIDTYPE itemId, - ITEMCLASS item) { - if (position < 0 || position > getAllItemIds().size() || itemId == null - || item == null) { - return null; - } - // Make sure that the item has not been added previously - if (getAllItemIds().contains(itemId)) { - return null; - } - - // "filteredList" will be updated in filterAll() which should be invoked - // by the caller after calling this method. - getAllItemIds().add(position, itemId); - registerNewItem(position, itemId, item); - - return item; - } - - /** - * Add an item at the end of the container, and perform filtering if - * necessary. An event is fired if the filtered view changes. - * - * @param newItemId - * @param item - * new item to add - * @param filter - * true to perform filtering and send event after adding the - * item, false to skip these operations for batch inserts - if - * false, caller needs to make sure these operations are - * performed at the end of the batch - * @return item added or null if no item was added - */ - protected ITEMCLASS internalAddItemAtEnd(ITEMIDTYPE newItemId, - ITEMCLASS item, boolean filter) { - ITEMCLASS newItem = internalAddAt(getAllItemIds().size(), newItemId, - item); - if (newItem != null && filter) { - // TODO filter only this item, use fireItemAdded() - filterAll(); - if (!isFiltered()) { - // TODO hack: does not detect change in filterAll() in this case - fireItemAdded(indexOfId(newItemId), newItemId, item); - } - } - return newItem; - } - - /** - * Add an item after a given (visible) item, and perform filtering. An event - * is fired if the filtered view changes. - * - * The new item is added at the beginning if previousItemId is null. - * - * @param previousItemId - * item id of a visible item after which to add the new item, or - * null to add at the beginning - * @param newItemId - * @param item - * new item to add - * @param filter - * true to perform filtering and send event after adding the - * item, false to skip these operations for batch inserts - if - * false, caller needs to make sure these operations are - * performed at the end of the batch - * @return item added or null if no item was added - */ - protected ITEMCLASS internalAddItemAfter(ITEMIDTYPE previousItemId, - ITEMIDTYPE newItemId, ITEMCLASS item, boolean filter) { - // only add if the previous item is visible - ITEMCLASS newItem = null; - if (previousItemId == null) { - newItem = internalAddAt(0, newItemId, item); - } else if (containsId(previousItemId)) { - newItem = internalAddAt(getAllItemIds().indexOf(previousItemId) + 1, - newItemId, item); - } - if (newItem != null && filter) { - // TODO filter only this item, use fireItemAdded() - filterAll(); - if (!isFiltered()) { - // TODO hack: does not detect change in filterAll() in this case - fireItemAdded(indexOfId(newItemId), newItemId, item); - } - } - return newItem; - } - - /** - * Add an item at a given (visible after filtering) item index, and perform - * filtering. An event is fired if the filtered view changes. - * - * @param index - * position where to add the item (visible/view index) - * @param newItemId - * @param item - * new item to add - * @param filter - * true to perform filtering and send event after adding the - * item, false to skip these operations for batch inserts - if - * false, caller needs to make sure these operations are - * performed at the end of the batch - * @return item added or null if no item was added - */ - protected ITEMCLASS internalAddItemAt(int index, ITEMIDTYPE newItemId, - ITEMCLASS item, boolean filter) { - if (index < 0 || index > size()) { - return null; - } else if (index == 0) { - // add before any item, visible or not - return internalAddItemAfter(null, newItemId, item, filter); - } else { - // if index==size(), adds immediately after last visible item - return internalAddItemAfter(getIdByIndex(index - 1), newItemId, - item, filter); - } - } - - /** - * Registers a new item as having been added to the container. This can - * involve storing the item or any relevant information about it in internal - * container-specific collections if necessary, as well as registering - * listeners etc. - * - * The full identifier list in {@link AbstractInMemoryContainer} has already - * been updated to reflect the new item when this method is called. - * - * @param position - * @param itemId - * @param item - */ - protected void registerNewItem(int position, ITEMIDTYPE itemId, - ITEMCLASS item) { - } - - // item set change notifications - - /** - * Notify item set change listeners that an item has been added to the - * container. - * - * @since 7.4 - * - * @param position - * position of the added item in the view - * @param itemId - * id of the added item - * @param item - * the added item - */ - protected void fireItemAdded(int position, ITEMIDTYPE itemId, - ITEMCLASS item) { - fireItemsAdded(position, itemId, 1); - } - - /** - * Notify item set change listeners that items has been added to the - * container. - * - * @param firstPosition - * position of the first visible added item in the view - * @param firstItemId - * id of the first visible added item - * @param numberOfItems - * the number of visible added items - */ - protected void fireItemsAdded(int firstPosition, ITEMIDTYPE firstItemId, - int numberOfItems) { - BaseItemAddEvent addEvent = new BaseItemAddEvent(this, firstItemId, - firstPosition, numberOfItems); - fireItemSetChange(addEvent); - } - - /** - * Notify item set change listeners that an item has been removed from the - * container. - * - * @since 7.4 - * - * @param position - * position of the removed item in the view prior to removal (if - * was visible) - * @param itemId - * id of the removed item, of type {@link Object} to satisfy - * {@link Container#removeItem(Object)} API - */ - protected void fireItemRemoved(int position, Object itemId) { - fireItemsRemoved(position, itemId, 1); - } - - /** - * Notify item set change listeners that items has been removed from the - * container. - * - * @param firstPosition - * position of the first visible removed item in the view prior - * to removal - * @param firstItemId - * id of the first visible removed item, of type {@link Object} - * to satisfy {@link Container#removeItem(Object)} API - * @param numberOfItems - * the number of removed visible items - * - */ - protected void fireItemsRemoved(int firstPosition, Object firstItemId, - int numberOfItems) { - BaseItemRemoveEvent removeEvent = new BaseItemRemoveEvent(this, - firstItemId, firstPosition, numberOfItems); - fireItemSetChange(removeEvent); - } - - // visible and filtered item identifier lists - - /** - * Returns the internal list of visible item identifiers after filtering. - * - * For internal use only. - */ - protected List getVisibleItemIds() { - if (isFiltered()) { - return getFilteredItemIds(); - } else { - return getAllItemIds(); - } - } - - /** - * Returns the item id of the first visible item after filtering. 'Null' is - * returned if there is no visible items. - *

- * For internal use only. - * - * @since 7.4 - * - * @return item id of the first visible item - */ - protected ITEMIDTYPE getFirstVisibleItem() { - if (!getVisibleItemIds().isEmpty()) { - return getVisibleItemIds().get(0); - } - return null; - } - - /** - * Returns true is the container has active filters. - * - * @return true if the container is currently filtered - */ - protected boolean isFiltered() { - return filteredItemIds != null; - } - - /** - * Internal helper method to set the internal list of filtered item - * identifiers. Should not be used outside this class except for - * implementing clone(), may disappear from future versions. - * - * @param filteredItemIds - */ - @Deprecated - protected void setFilteredItemIds(List filteredItemIds) { - this.filteredItemIds = filteredItemIds; - } - - /** - * Internal helper method to get the internal list of filtered item - * identifiers. Should not be used outside this class except for - * implementing clone(), may disappear from future versions - use - * {@link #getVisibleItemIds()} in other contexts. - * - * @return List - */ - protected List getFilteredItemIds() { - return filteredItemIds; - } - - /** - * Internal helper method to set the internal list of all item identifiers. - * Should not be used outside this class except for implementing clone(), - * may disappear from future versions. - * - * @param allItemIds - */ - @Deprecated - protected void setAllItemIds(List allItemIds) { - this.allItemIds = allItemIds; - } - - /** - * Internal helper method to get the internal list of all item identifiers. - * Avoid using this method outside this class, may disappear in future - * versions. - * - * @return List - */ - protected List getAllItemIds() { - return allItemIds; - } - - /** - * Set the internal collection of filters without performing filtering. - * - * This method is mostly for internal use, use - * {@link #addFilter(com.vaadin.data.Container.Filter)} and - * remove*Filter* (which also re-filter the container) instead - * when possible. - * - * @param filters - */ - protected void setFilters(Set filters) { - this.filters = filters; - } - - /** - * Returns the internal collection of filters. The returned collection - * should not be modified by callers outside this class. - * - * @return Set - */ - protected Set getFilters() { - return filters; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/BeanContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/BeanContainer.java deleted file mode 100644 index 01a5256199..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/BeanContainer.java +++ /dev/null @@ -1,179 +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.util.Collection; - -/** - * An in-memory container for JavaBeans. - * - *

- * The properties of the container are determined automatically by introspecting - * the used JavaBean class. Only beans of the same type can be added to the - * container. - *

- * - *

- * In BeanContainer (unlike {@link BeanItemContainer}), the item IDs do not have - * to be the beans themselves. The container can be used either with explicit - * item IDs or the item IDs can be generated when adding beans. - *

- * - *

- * To use explicit item IDs, use the methods {@link #addItem(Object, Object)}, - * {@link #addItemAfter(Object, Object, Object)} and - * {@link #addItemAt(int, Object, Object)}. - *

- * - *

- * If a bean id resolver is set using - * {@link #setBeanIdResolver(com.vaadin.data.util.AbstractBeanContainer.BeanIdResolver)} - * or {@link #setBeanIdProperty(Object)}, the methods {@link #addBean(Object)}, - * {@link #addBeanAfter(Object, Object)}, {@link #addBeanAt(int, Object)} and - * {@link #addAll(java.util.Collection)} can be used to add items to the - * container. If one of these methods is called, the resolver is used to - * generate an identifier for the item (must not return null). - *

- * - *

- * Note that explicit item identifiers can also be used when a resolver has been - * set by calling the addItem*() methods - the resolver is only used when adding - * beans using the addBean*() or {@link #addAll(Collection)} methods. - *

- * - *

- * It is not possible to add additional properties to the container. - *

- * - * @param - * The type of the item identifier - * @param - * The type of the Bean - * - * @see AbstractBeanContainer - * @see BeanItemContainer - * - * @since 6.5 - */ -public class BeanContainer - extends AbstractBeanContainer { - - public BeanContainer(Class type) { - super(type); - } - - /** - * Adds the bean to the Container. - * - * @see com.vaadin.data.Container#addItem(Object) - */ - @Override - public BeanItem addItem(IDTYPE itemId, BEANTYPE bean) { - if (itemId != null && bean != null) { - return super.addItem(itemId, bean); - } else { - return null; - } - } - - /** - * Adds the bean after the given item id. - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(Object, Object) - */ - @Override - public BeanItem addItemAfter(IDTYPE previousItemId, - IDTYPE newItemId, BEANTYPE bean) { - if (newItemId != null && bean != null) { - return super.addItemAfter(previousItemId, newItemId, bean); - } else { - return null; - } - } - - /** - * Adds a new bean at the given index. - * - * The bean is used both as the item contents and as the item identifier. - * - * @param index - * Index at which the bean should be added. - * @param newItemId - * The item id for the bean to add to the container. - * @param bean - * The bean to add to the container. - * - * @return Returns the new BeanItem or null if the operation fails. - */ - @Override - public BeanItem addItemAt(int index, IDTYPE newItemId, - BEANTYPE bean) { - if (newItemId != null && bean != null) { - return super.addItemAt(index, newItemId, bean); - } else { - return null; - } - } - - // automatic item id resolution - - /** - * Sets the bean id resolver to use a property of the beans as the - * identifier. - * - * @param propertyId - * the identifier of the property to use to find item identifiers - */ - public void setBeanIdProperty(Object propertyId) { - setBeanIdResolver(createBeanPropertyResolver(propertyId)); - } - - @Override - // overridden to make public - public void setBeanIdResolver( - BeanIdResolver beanIdResolver) { - super.setBeanIdResolver(beanIdResolver); - } - - @Override - // overridden to make public - public BeanItem addBean(BEANTYPE bean) - throws IllegalStateException, IllegalArgumentException { - return super.addBean(bean); - } - - @Override - // overridden to make public - public BeanItem addBeanAfter(IDTYPE previousItemId, BEANTYPE bean) - throws IllegalStateException, IllegalArgumentException { - return super.addBeanAfter(previousItemId, bean); - } - - @Override - // overridden to make public - public BeanItem addBeanAt(int index, BEANTYPE bean) - throws IllegalStateException, IllegalArgumentException { - return super.addBeanAt(index, bean); - } - - @Override - // overridden to make public - public void addAll(Collection collection) - throws IllegalStateException { - super.addAll(collection); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/BeanItem.java b/compatibility-server/src/main/java/com/vaadin/data/util/BeanItem.java deleted file mode 100644 index 2725606a4d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/BeanItem.java +++ /dev/null @@ -1,264 +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.beans.PropertyDescriptor; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * A wrapper class for adding the Item interface to any Java Bean. - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class BeanItem extends PropertysetItem { - - /** - * The bean which this Item is based on. - */ - private final BT bean; - - /** - *

- * Creates a new instance of BeanItem and adds all properties - * of a Java Bean to it. The properties are identified by their respective - * bean names. - *

- * - *

- * Note : This version only supports introspectable bean properties and - * their getter and setter methods. Stand-alone is and - * are methods are not supported. - *

- * - * @param bean - * the Java Bean to copy properties from. - * - */ - public BeanItem(BT bean) { - this(bean, (Class) bean.getClass()); - } - - /** - *

- * Creates a new instance of BeanItem and adds all properties - * of a Java Bean to it. The properties are identified by their respective - * bean names. - *

- * - *

- * Note : This version only supports introspectable bean properties and - * their getter and setter methods. Stand-alone is and - * are methods are not supported. - *

- * - * @since 7.4 - * - * @param bean - * the Java Bean to copy properties from. - * @param beanClass - * class of the {@code bean} - * - */ - public BeanItem(BT bean, Class beanClass) { - this(bean, getPropertyDescriptors(beanClass)); - } - - /** - *

- * Creates a new instance of BeanItem using a pre-computed set - * of properties. The properties are identified by their respective bean - * names. - *

- * - * @param bean - * the Java Bean to copy properties from. - * @param propertyDescriptors - * pre-computed property descriptors - */ - BeanItem(BT bean, - Map> propertyDescriptors) { - - this.bean = bean; - - for (VaadinPropertyDescriptor pd : propertyDescriptors.values()) { - addItemProperty(pd.getName(), pd.createProperty(bean)); - } - } - - /** - *

- * Creates a new instance of BeanItem and adds all listed - * properties of a Java Bean to it - in specified order. The properties are - * identified by their respective bean names. - *

- * - *

- * Note : This version only supports introspectable bean properties and - * their getter and setter methods. Stand-alone is and - * are methods are not supported. - *

- * - * @param bean - * the Java Bean to copy properties from. - * @param propertyIds - * id of the property. - */ - public BeanItem(BT bean, Collection propertyIds) { - - this.bean = bean; - - // Create bean information - LinkedHashMap> pds = getPropertyDescriptors( - (Class) bean.getClass()); - - // Add all the bean properties as MethodProperties to this Item - for (Object id : propertyIds) { - VaadinPropertyDescriptor pd = pds.get(id); - if (pd != null) { - addItemProperty(pd.getName(), pd.createProperty(bean)); - } - } - - } - - /** - *

- * Creates a new instance of BeanItem and adds all listed - * properties of a Java Bean to it - in specified order. The properties are - * identified by their respective bean names. - *

- * - *

- * Note : This version only supports introspectable bean properties and - * their getter and setter methods. Stand-alone is and - * are methods are not supported. - *

- * - * @param bean - * the Java Bean to copy properties from. - * @param propertyIds - * ids of the properties. - */ - public BeanItem(BT bean, String... propertyIds) { - this(bean, Arrays.asList(propertyIds)); - } - - /** - *

- * Perform introspection on a Java Bean class to find its properties. - *

- * - *

- * Note : This version only supports introspectable bean properties and - * their getter and setter methods. Stand-alone is and - * are methods are not supported. - *

- * - * @param beanClass - * the Java Bean class to get properties for. - * @return an ordered map from property names to property descriptors - */ - static LinkedHashMap> getPropertyDescriptors( - final Class beanClass) { - final LinkedHashMap> pdMap = new LinkedHashMap>(); - - // Try to introspect, if it fails, we just have an empty Item - try { - List propertyDescriptors = BeanUtil - .getBeanPropertyDescriptors(beanClass); - - // Add all the bean properties as MethodProperties to this Item - // later entries on the list overwrite earlier ones - for (PropertyDescriptor pd : propertyDescriptors) { - final Method getMethod = pd.getReadMethod(); - if ((getMethod != null) - && getMethod.getDeclaringClass() != Object.class) { - VaadinPropertyDescriptor vaadinPropertyDescriptor = new MethodPropertyDescriptor( - pd.getName(), pd.getPropertyType(), - pd.getReadMethod(), pd.getWriteMethod()); - pdMap.put(pd.getName(), vaadinPropertyDescriptor); - } - } - } catch (final java.beans.IntrospectionException ignored) { - } - - return pdMap; - } - - /** - * Expands nested bean properties by replacing a top-level property with - * some or all of its sub-properties. The expansion is not recursive. - * - * @param propertyId - * property id for the property whose sub-properties are to be - * expanded, - * @param subPropertyIds - * sub-properties to expand, all sub-properties are expanded if - * not specified - */ - public void expandProperty(String propertyId, String... subPropertyIds) { - Set subPropertySet = new HashSet( - Arrays.asList(subPropertyIds)); - - if (0 == subPropertyIds.length) { - // Enumerate all sub-properties - Class propertyType = getItemProperty(propertyId).getType(); - Map pds = getPropertyDescriptors(propertyType); - subPropertySet.addAll(pds.keySet()); - } - - for (String subproperty : subPropertySet) { - String qualifiedPropertyId = propertyId + "." + subproperty; - addNestedProperty(qualifiedPropertyId); - } - - removeItemProperty(propertyId); - } - - /** - * Adds a nested property to the item. The property must not exist in the - * item already and must of form "field1.field2" where field2 is a field in - * the object referenced to by field1. If an intermediate property returns - * null, the property will return a null value - * - * @param nestedPropertyId - * property id to add. - */ - public void addNestedProperty(String nestedPropertyId) { - addItemProperty(nestedPropertyId, - new NestedMethodProperty(getBean(), nestedPropertyId)); - } - - /** - * Gets the underlying JavaBean object. - * - * @return the bean object. - */ - public BT getBean() { - return bean; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/BeanItemContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/BeanItemContainer.java deleted file mode 100644 index 66e553164d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/BeanItemContainer.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.data.util; - -import java.util.Collection; - -/** - * An in-memory container for JavaBeans. - * - *

- * The properties of the container are determined automatically by introspecting - * the used JavaBean class. Only beans of the same type can be added to the - * container. - *

- * - *

- * BeanItemContainer uses the beans themselves as identifiers. The - * {@link Object#hashCode()} of a bean is used when storing and looking up beans - * so it must not change during the lifetime of the bean (it should not depend - * on any part of the bean that can be modified). Typically this restricts the - * implementation of {@link Object#equals(Object)} as well in order for it to - * fulfill the contract between {@code equals()} and {@code hashCode()}. - *

- * - *

- * To add items to the container, use the methods {@link #addBean(Object)}, - * {@link #addBeanAfter(Object, Object)} and {@link #addBeanAt(int, Object)}. - * Also {@link #addItem(Object)}, {@link #addItemAfter(Object, Object)} and - * {@link #addItemAt(int, Object)} can be used as synonyms for them. - *

- * - *

- * It is not possible to add additional properties to the container. - *

- * - * @param - * The type of the Bean - * - * @since 5.4 - */ -@SuppressWarnings("serial") -public class BeanItemContainer - extends AbstractBeanContainer { - - /** - * Bean identity resolver that returns the bean itself as its item - * identifier. - * - * This corresponds to the old behavior of {@link BeanItemContainer}, and - * requires suitable (identity-based) equals() and hashCode() methods on the - * beans. - * - * @param - * - * @since 6.5 - */ - private static class IdentityBeanIdResolver - implements BeanIdResolver { - - @Override - public BT getIdForBean(BT bean) { - return bean; - } - - } - - /** - * Constructs a {@code BeanItemContainer} for beans of the given type. - * - * @param type - * the type of the beans that will be added to the container. - * @throws IllegalArgumentException - * If {@code type} is null - */ - public BeanItemContainer(Class type) - throws IllegalArgumentException { - super(type); - super.setBeanIdResolver(new IdentityBeanIdResolver()); - } - - /** - * Constructs a {@code BeanItemContainer} and adds the given beans to it. - * The collection must not be empty. - * {@link BeanItemContainer#BeanItemContainer(Class)} can be used for - * creating an initially empty {@code BeanItemContainer}. - * - * Note that when using this constructor, the actual class of the first item - * in the collection is used to determine the bean properties supported by - * the container instance, and only beans of that class or its subclasses - * can be added to the collection. If this is problematic or empty - * collections need to be supported, use {@link #BeanItemContainer(Class)} - * and {@link #addAll(Collection)} instead. - * - * @param collection - * a non empty {@link Collection} of beans. - * @throws IllegalArgumentException - * If the collection is null or empty. - * - * @deprecated As of 6.5, use {@link #BeanItemContainer(Class, Collection)} - * instead - */ - @SuppressWarnings("unchecked") - @Deprecated - public BeanItemContainer(Collection collection) - throws IllegalArgumentException { - // must assume the class is BT - // the class information is erased by the compiler - this((Class) getBeanClassForCollection(collection), - collection); - } - - /** - * Internal helper method to support the deprecated {@link Collection} - * container. - * - * @param - * @param collection - * @return - * @throws IllegalArgumentException - */ - @SuppressWarnings("unchecked") - @Deprecated - private static Class getBeanClassForCollection( - Collection collection) - throws IllegalArgumentException { - if (collection == null || collection.isEmpty()) { - throw new IllegalArgumentException( - "The collection passed to BeanItemContainer constructor must not be null or empty. Use the other BeanItemContainer constructor."); - } - return (Class) collection.iterator().next().getClass(); - } - - /** - * Constructs a {@code BeanItemContainer} and adds the given beans to it. - * - * @param type - * the type of the beans that will be added to the container. - * @param collection - * a {@link Collection} of beans (can be empty or null). - * @throws IllegalArgumentException - * If {@code type} is null - */ - public BeanItemContainer(Class type, - Collection collection) - throws IllegalArgumentException { - super(type); - super.setBeanIdResolver(new IdentityBeanIdResolver()); - - if (collection != null) { - addAll(collection); - } - } - - /** - * Adds all the beans from a {@link Collection} in one go. More efficient - * than adding them one by one. - * - * @param collection - * The collection of beans to add. Must not be null. - */ - @Override - public void addAll(Collection collection) { - super.addAll(collection); - } - - /** - * Adds the bean after the given bean. - * - * The bean is used both as the item contents and as the item identifier. - * - * @param previousItemId - * the bean (of type BT) after which to add newItemId - * @param newItemId - * the bean (of type BT) to add (not null) - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(Object, Object) - */ - @Override - @SuppressWarnings("unchecked") - public BeanItem addItemAfter(Object previousItemId, - Object newItemId) throws IllegalArgumentException { - return super.addBeanAfter((BEANTYPE) previousItemId, - (BEANTYPE) newItemId); - } - - /** - * Adds a new bean at the given index. - * - * The bean is used both as the item contents and as the item identifier. - * - * @param index - * Index at which the bean should be added. - * @param newItemId - * The bean to add to the container. - * @return Returns the new BeanItem or null if the operation fails. - */ - @Override - @SuppressWarnings("unchecked") - public BeanItem addItemAt(int index, Object newItemId) - throws IllegalArgumentException { - return super.addBeanAt(index, (BEANTYPE) newItemId); - } - - /** - * Adds the bean to the Container. - * - * The bean is used both as the item contents and as the item identifier. - * - * @see com.vaadin.data.Container#addItem(Object) - */ - @Override - @SuppressWarnings("unchecked") - public BeanItem addItem(Object itemId) { - return super.addBean((BEANTYPE) itemId); - } - - /** - * Adds the bean to the Container. - * - * The bean is used both as the item contents and as the item identifier. - * - * @see com.vaadin.data.Container#addItem(Object) - */ - @Override - public BeanItem addBean(BEANTYPE bean) { - return addItem(bean); - } - - /** - * Unsupported in BeanItemContainer. - */ - @Override - protected void setBeanIdResolver( - AbstractBeanContainer.BeanIdResolver beanIdResolver) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "BeanItemContainer always uses an IdentityBeanIdResolver"); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/ContainerHierarchicalWrapper.java b/compatibility-server/src/main/java/com/vaadin/data/util/ContainerHierarchicalWrapper.java deleted file mode 100644 index 9e108cd615..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/ContainerHierarchicalWrapper.java +++ /dev/null @@ -1,865 +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.Serializable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; - -/** - *

- * A wrapper class for adding external hierarchy to containers not implementing - * the {@link com.vaadin.data.Container.Hierarchical} interface. - *

- * - *

- * If the wrapped container is changed directly (that is, not through the - * wrapper), and does not implement Container.ItemSetChangeNotifier and/or - * Container.PropertySetChangeNotifier the hierarchy information must be updated - * with the {@link #updateHierarchicalWrapper()} method. - *

- * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class ContainerHierarchicalWrapper implements Container.Hierarchical, - Container.ItemSetChangeNotifier, Container.PropertySetChangeNotifier { - - /** The wrapped container */ - private final Container container; - - /** Set of IDs of those contained Items that can't have children. */ - private HashSet noChildrenAllowed = null; - - /** Mapping from Item ID to parent Item ID */ - private Hashtable parent = null; - - /** Mapping from Item ID to a list of child IDs */ - private Hashtable> children = null; - - /** List that contains all root elements of the container. */ - private LinkedHashSet roots = null; - - /** Is the wrapped container hierarchical by itself ? */ - private boolean hierarchical; - - /** - * A comparator that sorts the listed items before other items. Otherwise, - * the order is undefined. - */ - private static class ListedItemsFirstComparator - implements Comparator, Serializable { - private final Collection itemIds; - - private ListedItemsFirstComparator(Collection itemIds) { - this.itemIds = itemIds; - } - - @Override - public int compare(Object o1, Object o2) { - if (o1.equals(o2)) { - return 0; - } - for (Object id : itemIds) { - if (id == o1) { - return -1; - } else if (id == o2) { - return 1; - } - } - return 0; - } - } - - /** - * Constructs a new hierarchical wrapper for an existing Container. Works - * even if the to-be-wrapped container already implements the - * Container.Hierarchical interface. - * - * @param toBeWrapped - * the container that needs to be accessed hierarchically - * @see #updateHierarchicalWrapper() - */ - public ContainerHierarchicalWrapper(Container toBeWrapped) { - - container = toBeWrapped; - hierarchical = container instanceof Container.Hierarchical; - - // Check arguments - if (container == null) { - throw new NullPointerException("Null can not be wrapped"); - } - - // Create initial order if needed - if (!hierarchical) { - noChildrenAllowed = new HashSet(); - parent = new Hashtable(); - children = new Hashtable>(); - roots = new LinkedHashSet(container.getItemIds()); - } - - updateHierarchicalWrapper(); - - } - - /** - * Updates the wrapper's internal hierarchy data to include all Items in the - * underlying container. If the contents of the wrapped container change - * without the wrapper's knowledge, this method needs to be called to update - * the hierarchy information of the Items. - */ - public void updateHierarchicalWrapper() { - - if (!hierarchical) { - - // Recreate hierarchy and data structures if missing - if (noChildrenAllowed == null || parent == null || children == null - || roots == null) { - noChildrenAllowed = new HashSet(); - parent = new Hashtable(); - children = new Hashtable>(); - roots = new LinkedHashSet(container.getItemIds()); - } - - // Check that the hierarchy is up-to-date - else { - - // ensure order of root and child lists is same as in wrapped - // container - Collection itemIds = container.getItemIds(); - Comparator basedOnOrderFromWrappedContainer = new ListedItemsFirstComparator( - itemIds); - - // Calculate the set of all items in the hierarchy - final HashSet s = new HashSet(); - s.addAll(parent.keySet()); - s.addAll(children.keySet()); - s.addAll(roots); - - // Remove unnecessary items - for (final Iterator i = s.iterator(); i.hasNext();) { - final Object id = i.next(); - if (!container.containsId(id)) { - removeFromHierarchyWrapper(id); - } - } - - // Add all the missing items - final Collection ids = container.getItemIds(); - for (final Iterator i = ids.iterator(); i.hasNext();) { - final Object id = i.next(); - if (!s.contains(id)) { - addToHierarchyWrapper(id); - s.add(id); - } - } - - Object[] array = roots.toArray(); - Arrays.sort(array, basedOnOrderFromWrappedContainer); - roots = new LinkedHashSet(); - for (int i = 0; i < array.length; i++) { - roots.add(array[i]); - } - for (Object object : children.keySet()) { - LinkedList object2 = children.get(object); - Collections.sort(object2, basedOnOrderFromWrappedContainer); - } - - } - } - } - - /** - * Removes the specified Item from the wrapper's internal hierarchy - * structure. - *

- * Note : The Item is not removed from the underlying Container. - *

- * - * @param itemId - * the ID of the item to remove from the hierarchy. - */ - private void removeFromHierarchyWrapper(Object itemId) { - - LinkedList oprhanedChildren = children.remove(itemId); - if (oprhanedChildren != null) { - for (Object object : oprhanedChildren) { - // make orphaned children root nodes - setParent(object, null); - } - } - - roots.remove(itemId); - final Object p = parent.get(itemId); - if (p != null) { - final LinkedList c = children.get(p); - if (c != null) { - c.remove(itemId); - } - } - parent.remove(itemId); - noChildrenAllowed.remove(itemId); - } - - /** - * Adds the specified Item specified to the internal hierarchy structure. - * The new item is added as a root Item. The underlying container is not - * modified. - * - * @param itemId - * the ID of the item to add to the hierarchy. - */ - private void addToHierarchyWrapper(Object itemId) { - roots.add(itemId); - - } - - /* - * Can the specified Item have any children? Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public boolean areChildrenAllowed(Object itemId) { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container) - .areChildrenAllowed(itemId); - } - - if (noChildrenAllowed.contains(itemId)) { - return false; - } - - return containsId(itemId); - } - - /* - * Gets the IDs of the children of the specified Item. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Collection getChildren(Object itemId) { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container).getChildren(itemId); - } - - final Collection c = children.get(itemId); - if (c == null) { - return null; - } - return Collections.unmodifiableCollection(c); - } - - /* - * Gets the ID of the parent of the specified Item. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Object getParent(Object itemId) { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container).getParent(itemId); - } - - return parent.get(itemId); - } - - /* - * Is the Item corresponding to the given ID a leaf node? Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public boolean hasChildren(Object itemId) { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container).hasChildren(itemId); - } - - LinkedList list = children.get(itemId); - return (list != null && !list.isEmpty()); - } - - /* - * Is the Item corresponding to the given ID a root node? Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public boolean isRoot(Object itemId) { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container).isRoot(itemId); - } - - if (parent.containsKey(itemId)) { - return false; - } - - return containsId(itemId); - } - - /* - * Gets the IDs of the root elements in the container. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Collection rootItemIds() { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container).rootItemIds(); - } - - return Collections.unmodifiableCollection(roots); - } - - /** - *

- * Sets the given Item's capability to have children. If the Item identified - * with the itemId already has children and the areChildrenAllowed is false - * this method fails and false is returned; the children must - * be first explicitly removed with - * {@link #setParent(Object itemId, Object newParentId)} or - * {@link com.vaadin.data.Container#removeItem(Object itemId)}. - *

- * - * @param itemId - * the ID of the Item in the container whose child capability is - * to be set. - * @param childrenAllowed - * the boolean value specifying if the Item can have children or - * not. - * @return true if the operation succeeded, false - * if not - */ - @Override - public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container) - .setChildrenAllowed(itemId, childrenAllowed); - } - - // Check that the item is in the container - if (!containsId(itemId)) { - return false; - } - - // Update status - if (childrenAllowed) { - noChildrenAllowed.remove(itemId); - } else { - noChildrenAllowed.add(itemId); - } - - return true; - } - - /** - *

- * Sets the parent of an Item. The new parent item must exist and be able to - * have children. (canHaveChildren(newParentId) == true). It is - * also possible to detach a node from the hierarchy (and thus make it root) - * by setting the parent null. - *

- * - * @param itemId - * the ID of the item to be set as the child of the Item - * identified with newParentId. - * @param newParentId - * the ID of the Item that's to be the new parent of the Item - * identified with itemId. - * @return true if the operation succeeded, false - * if not - */ - @Override - public boolean setParent(Object itemId, Object newParentId) { - - // If the wrapped container implements the method directly, use it - if (hierarchical) { - return ((Container.Hierarchical) container).setParent(itemId, - newParentId); - } - - // Check that the item is in the container - if (!containsId(itemId)) { - return false; - } - - // Get the old parent - final Object oldParentId = parent.get(itemId); - - // Check if no change is necessary - if ((newParentId == null && oldParentId == null) - || (newParentId != null && newParentId.equals(oldParentId))) { - return true; - } - - // Making root - if (newParentId == null) { - - // Remove from old parents children list - final LinkedList l = children.get(oldParentId); - if (l != null) { - l.remove(itemId); - if (l.isEmpty()) { - children.remove(itemId); - } - } - - // Add to be a root - roots.add(itemId); - - // Update parent - parent.remove(itemId); - - fireItemSetChangeIfAbstractContainer(); - - return true; - } - - // Check that the new parent exists in container and can have - // children - if (!containsId(newParentId) - || noChildrenAllowed.contains(newParentId)) { - return false; - } - - // Check that setting parent doesn't result to a loop - Object o = newParentId; - while (o != null && !o.equals(itemId)) { - o = parent.get(o); - } - if (o != null) { - return false; - } - - // Update parent - parent.put(itemId, newParentId); - LinkedList pcl = children.get(newParentId); - if (pcl == null) { - pcl = new LinkedList(); - children.put(newParentId, pcl); - } - pcl.add(itemId); - - // Remove from old parent or root - if (oldParentId == null) { - roots.remove(itemId); - } else { - final LinkedList l = children.get(oldParentId); - if (l != null) { - l.remove(itemId); - if (l.isEmpty()) { - children.remove(oldParentId); - } - } - } - - fireItemSetChangeIfAbstractContainer(); - - return true; - } - - /** - * inform container (if it is instance of AbstractContainer) about the - * change in hierarchy (#15421) - */ - private void fireItemSetChangeIfAbstractContainer() { - if (container instanceof AbstractContainer) { - ((AbstractContainer) container).fireItemSetChange(); - } - } - - /** - * Creates a new Item into the Container, assigns it an automatic ID, and - * adds it to the hierarchy. - * - * @return the autogenerated ID of the new Item or null if the - * operation failed - * @throws UnsupportedOperationException - * if the addItem is not supported. - */ - @Override - public Object addItem() throws UnsupportedOperationException { - - final Object id = container.addItem(); - if (!hierarchical && id != null) { - addToHierarchyWrapper(id); - } - return id; - } - - /** - * Adds a new Item by its ID to the underlying container and to the - * hierarchy. - * - * @param itemId - * the ID of the Item to be created. - * @return the added Item or null if the operation failed. - * @throws UnsupportedOperationException - * if the addItem is not supported. - */ - @Override - public Item addItem(Object itemId) throws UnsupportedOperationException { - // Null ids are not accepted - if (itemId == null) { - return null; - } - - final Item item = container.addItem(itemId); - if (!hierarchical && item != null) { - addToHierarchyWrapper(itemId); - } - return item; - } - - /** - * Removes all items from the underlying container and from the hierarcy. - * - * @return true if the operation succeeded, false - * if not - * @throws UnsupportedOperationException - * if the removeAllItems is not supported. - */ - @Override - public boolean removeAllItems() throws UnsupportedOperationException { - - final boolean success = container.removeAllItems(); - - if (!hierarchical && success) { - roots.clear(); - parent.clear(); - children.clear(); - noChildrenAllowed.clear(); - } - return success; - } - - /** - * Removes an Item specified by the itemId from the underlying container and - * from the hierarchy. - * - * @param itemId - * the ID of the Item to be removed. - * @return true if the operation succeeded, false - * if not - * @throws UnsupportedOperationException - * if the removeItem is not supported. - */ - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - - final boolean success = container.removeItem(itemId); - - if (!hierarchical && success) { - removeFromHierarchyWrapper(itemId); - } - - return success; - } - - /** - * Removes the Item identified by given itemId and all its children. - * - * @see #removeItem(Object) - * @param itemId - * the identifier of the Item to be removed - * @return true if the operation succeeded - */ - public boolean removeItemRecursively(Object itemId) { - return HierarchicalContainer.removeItemRecursively(this, itemId); - } - - /** - * Adds a new Property to all Items in the Container. - * - * @param propertyId - * the ID of the new Property. - * @param type - * the Data type of the new Property. - * @param defaultValue - * the value all created Properties are initialized to. - * @return true if the operation succeeded, false - * if not - * @throws UnsupportedOperationException - * if the addContainerProperty is not supported. - */ - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - - return container.addContainerProperty(propertyId, type, defaultValue); - } - - /** - * Removes the specified Property from the underlying container and from the - * hierarchy. - *

- * Note : The Property will be removed from all Items in the Container. - *

- * - * @param propertyId - * the ID of the Property to remove. - * @return true if the operation succeeded, false - * if not - * @throws UnsupportedOperationException - * if the removeContainerProperty is not supported. - */ - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - return container.removeContainerProperty(propertyId); - } - - /* - * Does the container contain the specified Item? Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public boolean containsId(Object itemId) { - return container.containsId(itemId); - } - - /* - * Gets the specified Item from the container. Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public Item getItem(Object itemId) { - return container.getItem(itemId); - } - - /* - * Gets the ID's of all Items stored in the Container Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Collection getItemIds() { - return container.getItemIds(); - } - - /* - * Gets the Property identified by the given itemId and propertyId from the - * Container Don't add a JavaDoc comment here, we use the default - * documentation from implemented interface. - */ - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - return container.getContainerProperty(itemId, propertyId); - } - - /* - * Gets the ID's of all Properties stored in the Container Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Collection getContainerPropertyIds() { - return container.getContainerPropertyIds(); - } - - /* - * Gets the data type of all Properties identified by the given Property ID. - * Don't add a JavaDoc comment here, we use the default documentation from - * implemented interface. - */ - @Override - public Class getType(Object propertyId) { - return container.getType(propertyId); - } - - /* - * Gets the number of Items in the Container. Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public int size() { - int size = container.size(); - assert size >= 0; - return size; - } - - /* - * Registers a new Item set change listener for this Container. Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public void addItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (container instanceof Container.ItemSetChangeNotifier) { - ((Container.ItemSetChangeNotifier) container) - .addItemSetChangeListener(new PiggybackListener(listener)); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Override - @Deprecated - public void addListener(Container.ItemSetChangeListener listener) { - addItemSetChangeListener(listener); - } - - /* - * Removes a Item set change listener from the object. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public void removeItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (container instanceof Container.ItemSetChangeNotifier) { - ((Container.ItemSetChangeNotifier) container) - .removeItemSetChangeListener( - new PiggybackListener(listener)); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(Container.ItemSetChangeListener listener) { - removeItemSetChangeListener(listener); - } - - /* - * Registers a new Property set change listener for this Container. Don't - * add a JavaDoc comment here, we use the default documentation from - * implemented interface. - */ - @Override - public void addPropertySetChangeListener( - Container.PropertySetChangeListener listener) { - if (container instanceof Container.PropertySetChangeNotifier) { - ((Container.PropertySetChangeNotifier) container) - .addPropertySetChangeListener( - new PiggybackListener(listener)); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addPropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Override - @Deprecated - public void addListener(Container.PropertySetChangeListener listener) { - addPropertySetChangeListener(listener); - } - - /* - * Removes a Property set change listener from the object. Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public void removePropertySetChangeListener( - Container.PropertySetChangeListener listener) { - if (container instanceof Container.PropertySetChangeNotifier) { - ((Container.PropertySetChangeNotifier) container) - .removePropertySetChangeListener( - new PiggybackListener(listener)); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removePropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(Container.PropertySetChangeListener listener) { - removePropertySetChangeListener(listener); - } - - /** - * This listener 'piggybacks' on the real listener in order to update the - * wrapper when needed. It proxies equals() and hashCode() to the real - * listener so that the correct listener gets removed. - * - */ - private class PiggybackListener - implements Container.PropertySetChangeListener, - Container.ItemSetChangeListener { - - Object listener; - - public PiggybackListener(Object realListener) { - listener = realListener; - } - - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - updateHierarchicalWrapper(); - ((Container.ItemSetChangeListener) listener) - .containerItemSetChange(event); - - } - - @Override - public void containerPropertySetChange(PropertySetChangeEvent event) { - updateHierarchicalWrapper(); - ((Container.PropertySetChangeListener) listener) - .containerPropertySetChange(event); - - } - - @Override - public boolean equals(Object obj) { - return obj == listener || (obj != null && obj.equals(listener)); - } - - @Override - public int hashCode() { - return listener.hashCode(); - } - - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/FilesystemContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/FilesystemContainer.java deleted file mode 100644 index 46b4f4bcd4..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/FilesystemContainer.java +++ /dev/null @@ -1,924 +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.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.server.Resource; -import com.vaadin.util.FileTypeResolver; - -/** - * A hierarchical container wrapper for a filesystem. - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class FilesystemContainer implements Container.Hierarchical { - - /** - * String identifier of a file's "name" property. - */ - public static String PROPERTY_NAME = "Name"; - - /** - * String identifier of a file's "size" property. - */ - public static String PROPERTY_SIZE = "Size"; - - /** - * String identifier of a file's "icon" property. - */ - public static String PROPERTY_ICON = "Icon"; - - /** - * String identifier of a file's "last modified" property. - */ - public static String PROPERTY_LASTMODIFIED = "Last Modified"; - - /** - * List of the string identifiers for the available properties. - */ - public static Collection FILE_PROPERTIES; - - private final static Method FILEITEM_LASTMODIFIED; - - private final static Method FILEITEM_NAME; - - private final static Method FILEITEM_ICON; - - private final static Method FILEITEM_SIZE; - - static { - - FILE_PROPERTIES = new ArrayList(); - FILE_PROPERTIES.add(PROPERTY_NAME); - FILE_PROPERTIES.add(PROPERTY_ICON); - FILE_PROPERTIES.add(PROPERTY_SIZE); - FILE_PROPERTIES.add(PROPERTY_LASTMODIFIED); - FILE_PROPERTIES = Collections.unmodifiableCollection(FILE_PROPERTIES); - try { - FILEITEM_LASTMODIFIED = FileItem.class.getMethod("lastModified", - new Class[] {}); - FILEITEM_NAME = FileItem.class.getMethod("getName", new Class[] {}); - FILEITEM_ICON = FileItem.class.getMethod("getIcon", new Class[] {}); - FILEITEM_SIZE = FileItem.class.getMethod("getSize", new Class[] {}); - } catch (final NoSuchMethodException e) { - throw new RuntimeException( - "Internal error finding methods in FilesystemContainer"); - } - } - - private File[] roots = new File[] {}; - - private FilenameFilter filter = null; - - private boolean recursive = true; - - /** - * Constructs a new FileSystemContainer with the specified file - * as the root of the filesystem. The files are included recursively. - * - * @param root - * the root file for the new file-system container. Null values - * are ignored. - */ - public FilesystemContainer(File root) { - if (root != null) { - roots = new File[] { root }; - } - } - - /** - * Constructs a new FileSystemContainer with the specified file - * as the root of the filesystem. The files are included recursively. - * - * @param root - * the root file for the new file-system container. - * @param recursive - * should the container recursively contain subdirectories. - */ - public FilesystemContainer(File root, boolean recursive) { - this(root); - setRecursive(recursive); - } - - /** - * Constructs a new FileSystemContainer with the specified file - * as the root of the filesystem. - * - * @param root - * the root file for the new file-system container. - * @param extension - * the Filename extension (w/o separator) to limit the files in - * container. - * @param recursive - * should the container recursively contain subdirectories. - */ - public FilesystemContainer(File root, String extension, boolean recursive) { - this(root); - this.setFilter(extension); - setRecursive(recursive); - } - - /** - * Constructs a new FileSystemContainer with the specified root - * and recursivity status. - * - * @param root - * the root file for the new file-system container. - * @param filter - * the Filename filter to limit the files in container. - * @param recursive - * should the container recursively contain subdirectories. - */ - public FilesystemContainer(File root, FilenameFilter filter, - boolean recursive) { - this(root); - this.setFilter(filter); - setRecursive(recursive); - } - - /** - * Adds new root file directory. Adds a file to be included as root file - * directory in the FilesystemContainer. - * - * @param root - * the File to be added as root directory. Null values are - * ignored. - */ - public void addRoot(File root) { - if (root != null) { - final File[] newRoots = new File[roots.length + 1]; - for (int i = 0; i < roots.length; i++) { - newRoots[i] = roots[i]; - } - newRoots[roots.length] = root; - roots = newRoots; - } - } - - /** - * Tests if the specified Item in the container may have children. Since a - * FileSystemContainer contains files and directories, this - * method returns true for directory Items only. - * - * @param itemId - * the id of the item. - * @return true if the specified Item is a directory, - * false otherwise. - */ - @Override - public boolean areChildrenAllowed(Object itemId) { - return itemId instanceof File && ((File) itemId).canRead() - && ((File) itemId).isDirectory(); - } - - /* - * Gets the ID's of all Items who are children of the specified Item. Don't - * add a JavaDoc comment here, we use the default documentation from - * implemented interface. - */ - @Override - public Collection getChildren(Object itemId) { - - if (!(itemId instanceof File)) { - return Collections.unmodifiableCollection(new LinkedList()); - } - File[] f; - if (filter != null) { - f = ((File) itemId).listFiles(filter); - } else { - f = ((File) itemId).listFiles(); - } - - if (f == null) { - return Collections.unmodifiableCollection(new LinkedList()); - } - - final List l = Arrays.asList(f); - Collections.sort(l); - - return Collections.unmodifiableCollection(l); - } - - /* - * Gets the parent item of the specified Item. Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public Object getParent(Object itemId) { - - if (!(itemId instanceof File)) { - return null; - } - return ((File) itemId).getParentFile(); - } - - /* - * Tests if the specified Item has any children. Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public boolean hasChildren(Object itemId) { - - if (!(itemId instanceof File)) { - return false; - } - String[] l; - if (filter != null) { - l = ((File) itemId).list(filter); - } else { - l = ((File) itemId).list(); - } - return (l != null) && (l.length > 0); - } - - /* - * Tests if the specified Item is the root of the filesystem. Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public boolean isRoot(Object itemId) { - - if (!(itemId instanceof File)) { - return false; - } - for (int i = 0; i < roots.length; i++) { - if (roots[i].equals(itemId)) { - return true; - } - } - return false; - } - - /* - * Gets the ID's of all root Items in the container. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Collection rootItemIds() { - - File[] f; - - // in single root case we use children - if (roots.length == 1) { - if (filter != null) { - f = roots[0].listFiles(filter); - } else { - f = roots[0].listFiles(); - } - } else { - f = roots; - } - - if (f == null) { - return Collections.unmodifiableCollection(new LinkedList()); - } - - final List l = Arrays.asList(f); - Collections.sort(l); - - return Collections.unmodifiableCollection(l); - } - - /** - * Returns false when conversion from files to directories is - * not supported. - * - * @param itemId - * the ID of the item. - * @param areChildrenAllowed - * the boolean value specifying if the Item can have children or - * not. - * @return true if the operaton is successful otherwise - * false. - * @throws UnsupportedOperationException - * if the setChildrenAllowed is not supported. - */ - @Override - public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) - throws UnsupportedOperationException { - - throw new UnsupportedOperationException( - "Conversion file to/from directory is not supported"); - } - - /** - * Returns false when moving files around in the filesystem is - * not supported. - * - * @param itemId - * the ID of the item. - * @param newParentId - * the ID of the Item that's to be the new parent of the Item - * identified with itemId. - * @return true if the operation is successful otherwise - * false. - * @throws UnsupportedOperationException - * if the setParent is not supported. - */ - @Override - public boolean setParent(Object itemId, Object newParentId) - throws UnsupportedOperationException { - - throw new UnsupportedOperationException("File moving is not supported"); - } - - /* - * Tests if the filesystem contains the specified Item. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public boolean containsId(Object itemId) { - - if (!(itemId instanceof File)) { - return false; - } - boolean val = false; - - // Try to match all roots - for (int i = 0; i < roots.length; i++) { - try { - val |= ((File) itemId).getCanonicalPath() - .startsWith(roots[i].getCanonicalPath()); - } catch (final IOException e) { - // Exception ignored - } - - } - if (val && filter != null) { - val &= filter.accept(((File) itemId).getParentFile(), - ((File) itemId).getName()); - } - return val; - } - - /* - * Gets the specified Item from the filesystem. Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public Item getItem(Object itemId) { - - if (!(itemId instanceof File)) { - return null; - } - return new FileItem((File) itemId); - } - - /** - * Internal recursive method to add the files under the specified directory - * to the collection. - * - * @param col - * the collection where the found items are added - * @param f - * the root file where to start adding files - */ - private void addItemIds(Collection col, File f) { - File[] l; - if (filter != null) { - l = f.listFiles(filter); - } else { - l = f.listFiles(); - } - if (l == null) { - // File.listFiles returns null if File does not exist or if there - // was an IO error (permission denied) - return; - } - final List ll = Arrays.asList(l); - Collections.sort(ll); - - for (final Iterator i = ll.iterator(); i.hasNext();) { - final File lf = i.next(); - col.add(lf); - if (lf.isDirectory()) { - addItemIds(col, lf); - } - } - } - - /* - * Gets the IDs of Items in the filesystem. Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public Collection getItemIds() { - - if (recursive) { - final Collection col = new ArrayList(); - for (int i = 0; i < roots.length; i++) { - addItemIds(col, roots[i]); - } - return Collections.unmodifiableCollection(col); - } else { - File[] f; - if (roots.length == 1) { - if (filter != null) { - f = roots[0].listFiles(filter); - } else { - f = roots[0].listFiles(); - } - } else { - f = roots; - } - - if (f == null) { - return Collections - .unmodifiableCollection(new LinkedList()); - } - - final List l = Arrays.asList(f); - Collections.sort(l); - return Collections.unmodifiableCollection(l); - } - - } - - /** - * Gets the specified property of the specified file Item. The available - * file properties are "Name", "Size" and "Last Modified". If propertyId is - * not one of those, null is returned. - * - * @param itemId - * the ID of the file whose property is requested. - * @param propertyId - * the property's ID. - * @return the requested property's value, or null - */ - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - - if (!(itemId instanceof File)) { - return null; - } - - if (propertyId.equals(PROPERTY_NAME)) { - return new MethodProperty(getType(propertyId), - new FileItem((File) itemId), FILEITEM_NAME, null); - } - - if (propertyId.equals(PROPERTY_ICON)) { - return new MethodProperty(getType(propertyId), - new FileItem((File) itemId), FILEITEM_ICON, null); - } - - if (propertyId.equals(PROPERTY_SIZE)) { - return new MethodProperty(getType(propertyId), - new FileItem((File) itemId), FILEITEM_SIZE, null); - } - - if (propertyId.equals(PROPERTY_LASTMODIFIED)) { - return new MethodProperty(getType(propertyId), - new FileItem((File) itemId), FILEITEM_LASTMODIFIED, null); - } - - return null; - } - - /** - * Gets the collection of available file properties. - * - * @return Unmodifiable collection containing all available file properties. - */ - @Override - public Collection getContainerPropertyIds() { - return FILE_PROPERTIES; - } - - /** - * Gets the specified property's data type. "Name" is a String, - * "Size" is a Long, "Last Modified" is a Date. If - * propertyId is not one of those, null is returned. - * - * @param propertyId - * the ID of the property whose type is requested. - * @return data type of the requested property, or null - */ - @Override - public Class getType(Object propertyId) { - - if (propertyId.equals(PROPERTY_NAME)) { - return String.class; - } - if (propertyId.equals(PROPERTY_ICON)) { - return Resource.class; - } - if (propertyId.equals(PROPERTY_SIZE)) { - return Long.class; - } - if (propertyId.equals(PROPERTY_LASTMODIFIED)) { - return Date.class; - } - return null; - } - - /** - * Internal method to recursively calculate the number of files under a root - * directory. - * - * @param f - * the root to start counting from. - */ - private int getFileCounts(File f) { - File[] l; - if (filter != null) { - l = f.listFiles(filter); - } else { - l = f.listFiles(); - } - - if (l == null) { - return 0; - } - int ret = l.length; - for (int i = 0; i < l.length; i++) { - if (l[i].isDirectory()) { - ret += getFileCounts(l[i]); - } - } - return ret; - } - - /** - * Gets the number of Items in the container. In effect, this is the - * combined amount of files and directories. - * - * @return Number of Items in the container. - */ - @Override - public int size() { - - if (recursive) { - int counts = 0; - for (int i = 0; i < roots.length; i++) { - counts += getFileCounts(roots[i]); - } - return counts; - } else { - File[] f; - if (roots.length == 1) { - if (filter != null) { - f = roots[0].listFiles(filter); - } else { - f = roots[0].listFiles(); - } - } else { - f = roots; - } - - if (f == null) { - return 0; - } - return f.length; - } - } - - /** - * A Item wrapper for files in a filesystem. - * - * @author Vaadin Ltd. - * @since 3.0 - */ - public class FileItem implements Item { - - /** - * The wrapped file. - */ - private final File file; - - /** - * Constructs a FileItem from a existing file. - */ - private FileItem(File file) { - this.file = file; - } - - /* - * Gets the specified property of this file. Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public Property getItemProperty(Object id) { - return getContainerProperty(file, id); - } - - /* - * Gets the IDs of all properties available for this item Don't add a - * JavaDoc comment here, we use the default documentation from - * implemented interface. - */ - @Override - public Collection getItemPropertyIds() { - return getContainerPropertyIds(); - } - - /** - * Calculates a integer hash-code for the Property that's unique inside - * the Item containing the Property. Two different Properties inside the - * same Item contained in the same list always have different - * hash-codes, though Properties in different Items may have identical - * hash-codes. - * - * @return A locally unique hash-code as integer - */ - @Override - public int hashCode() { - return file.hashCode() ^ FilesystemContainer.this.hashCode(); - } - - /** - * Tests if the given object is the same as the this object. Two - * Properties got from an Item with the same ID are equal. - * - * @param obj - * an object to compare with this object. - * @return true if the given object is the same as this - * object, false if not - */ - @Override - public boolean equals(Object obj) { - if (obj == null || !(obj instanceof FileItem)) { - return false; - } - final FileItem fi = (FileItem) obj; - return fi.getHost() == getHost() && fi.file.equals(file); - } - - /** - * Gets the host of this file. - */ - private FilesystemContainer getHost() { - return FilesystemContainer.this; - } - - /** - * Gets the last modified date of this file. - * - * @return Date - */ - public Date lastModified() { - return new Date(file.lastModified()); - } - - /** - * Gets the name of this file. - * - * @return file name of this file. - */ - public String getName() { - return file.getName(); - } - - /** - * Gets the icon of this file. - * - * @return the icon of this file. - */ - public Resource getIcon() { - return FileTypeResolver.getIcon(file); - } - - /** - * Gets the size of this file. - * - * @return size - */ - public long getSize() { - if (file.isDirectory()) { - return 0; - } - return file.length(); - } - - /** - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - if ("".equals(file.getName())) { - return file.getAbsolutePath(); - } - return file.getName(); - } - - /** - * Filesystem container does not support adding new properties. - * - * @see com.vaadin.data.Item#addItemProperty(Object, Property) - */ - @Override - public boolean addItemProperty(Object id, Property property) - throws UnsupportedOperationException { - throw new UnsupportedOperationException("Filesystem container " - + "does not support adding new properties"); - } - - /** - * Filesystem container does not support removing properties. - * - * @see com.vaadin.data.Item#removeItemProperty(Object) - */ - @Override - public boolean removeItemProperty(Object id) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Filesystem container does not support property removal"); - } - - } - - /** - * Generic file extension filter for displaying only files having certain - * extension. - * - * @author Vaadin Ltd. - * @since 3.0 - */ - public class FileExtensionFilter implements FilenameFilter, Serializable { - - private final String filter; - - /** - * Constructs a new FileExtensionFilter using given extension. - * - * @param fileExtension - * the File extension without the separator (dot). - */ - public FileExtensionFilter(String fileExtension) { - filter = "." + fileExtension; - } - - /** - * Allows only files with the extension and directories. - * - * @see java.io.FilenameFilter#accept(File, String) - */ - @Override - public boolean accept(File dir, String name) { - if (name.endsWith(filter)) { - return true; - } - return new File(dir, name).isDirectory(); - } - - } - - /** - * Returns the file filter used to limit the files in this container. - * - * @return Used filter instance or null if no filter is assigned. - */ - public FilenameFilter getFilter() { - return filter; - } - - /** - * Sets the file filter used to limit the files in this container. - * - * @param filter - * The filter to set. null disables filtering. - */ - public void setFilter(FilenameFilter filter) { - this.filter = filter; - } - - /** - * Sets the file filter used to limit the files in this container. - * - * @param extension - * the Filename extension (w/o separator) to limit the files in - * container. - */ - public void setFilter(String extension) { - filter = new FileExtensionFilter(extension); - } - - /** - * Is this container recursive filesystem. - * - * @return true if container is recursive, false - * otherwise. - */ - public boolean isRecursive() { - return recursive; - } - - /** - * Sets the container recursive property. Set this to false to limit the - * files directly under the root file. - *

- * Note : This is meaningful only if the root really is a directory. - *

- * - * @param recursive - * the New value for recursive property. - */ - public void setRecursive(boolean recursive) { - this.recursive = recursive; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object, - * java.lang.Class, java.lang.Object) - */ - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "File system container does not support this operation"); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#addItem() - */ - @Override - public Object addItem() throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "File system container does not support this operation"); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#addItem(java.lang.Object) - */ - @Override - public Item addItem(Object itemId) throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "File system container does not support this operation"); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeAllItems() - */ - @Override - public boolean removeAllItems() throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "File system container does not support this operation"); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeItem(java.lang.Object) - */ - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "File system container does not support this operation"); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object ) - */ - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "File system container does not support this operation"); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/GeneratedPropertyContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/GeneratedPropertyContainer.java deleted file mode 100644 index 8d7a1512f9..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/GeneratedPropertyContainer.java +++ /dev/null @@ -1,776 +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.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.sort.SortOrder; -import com.vaadin.data.util.filter.UnsupportedFilterException; -import com.vaadin.shared.data.sort.SortDirection; - -/** - * Container wrapper that adds support for generated properties. This container - * only supports adding new generated properties. Adding new normal properties - * should be done for the wrapped container. - * - *

- * Removing properties from this container does not remove anything from the - * wrapped container but instead only hides them from the results. These - * properties can be returned to this container by calling - * {@link #addContainerProperty(Object, Class, Object)} with same property id - * which was removed. - * - *

- * If wrapped container is Filterable and/or Sortable it should only be handled - * through this container as generated properties need to be handled in a - * specific way when sorting/filtering. - * - *

- * Items returned by this container do not support adding or removing - * properties. Generated properties are always read-only. Trying to make them - * editable throws an exception. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class GeneratedPropertyContainer extends AbstractContainer - implements Container.Indexed, Container.Sortable, Container.Filterable, - Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier { - - private final Container.Indexed wrappedContainer; - private final Map> propertyGenerators; - private final Map> activeFilters; - private Sortable sortableContainer = null; - private Filterable filterableContainer = null; - - /* Removed properties which are hidden but not actually removed */ - private final Set removedProperties = new HashSet(); - - /** - * Property implementation for generated properties - */ - protected static class GeneratedProperty implements Property { - - private Item item; - private Object itemId; - private Object propertyId; - private PropertyValueGenerator generator; - - public GeneratedProperty(Item item, Object propertyId, Object itemId, - PropertyValueGenerator generator) { - this.item = item; - this.itemId = itemId; - this.propertyId = propertyId; - this.generator = generator; - } - - @Override - public T getValue() { - return generator.getValue(item, itemId, propertyId); - } - - @Override - public void setValue(T newValue) throws ReadOnlyException { - throw new ReadOnlyException("Generated properties are read only"); - } - - @Override - public Class getType() { - return generator.getType(); - } - - @Override - public boolean isReadOnly() { - return true; - } - - @Override - public void setReadOnly(boolean newStatus) { - if (newStatus) { - // No-op - return; - } - throw new UnsupportedOperationException( - "Generated properties are read only"); - } - } - - /** - * Item implementation for generated properties, used to wrap the Item that - * belongs to the wrapped container. To reach that Item use - * {@link #getWrappedItem()} - */ - public class GeneratedPropertyItem implements Item { - - private Item wrappedItem; - private Object itemId; - - protected GeneratedPropertyItem(Object itemId, Item item) { - this.itemId = itemId; - wrappedItem = item; - } - - @Override - public Property getItemProperty(Object id) { - if (propertyGenerators.containsKey(id)) { - return createProperty(wrappedItem, id, itemId, - propertyGenerators.get(id)); - } - return wrappedItem.getItemProperty(id); - } - - @Override - public Collection getItemPropertyIds() { - Set wrappedProperties = new LinkedHashSet( - wrappedItem.getItemPropertyIds()); - wrappedProperties.removeAll(removedProperties); - wrappedProperties.addAll(propertyGenerators.keySet()); - return wrappedProperties; - } - - @Override - public boolean addItemProperty(Object id, Property property) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "GeneratedPropertyItem does not support adding properties"); - } - - @Override - public boolean removeItemProperty(Object id) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "GeneratedPropertyItem does not support removing properties"); - } - - /** - * Tests if the given object is the same as the this object. Two Items - * from the same container with the same ID are equal. - * - * @param obj - * an object to compare with this object - * @return true if the given object is the same as this - * object, false if not - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null - || !obj.getClass().equals(GeneratedPropertyItem.class)) { - return false; - } - final GeneratedPropertyItem li = (GeneratedPropertyItem) obj; - return getContainer() == li.getContainer() - && itemId.equals(li.itemId); - } - - @Override - public int hashCode() { - return itemId.hashCode(); - } - - private GeneratedPropertyContainer getContainer() { - return GeneratedPropertyContainer.this; - } - - /** - * Returns the wrapped Item that belongs to the wrapped container - * - * @return wrapped item. - * @since 7.6.8 - */ - public Item getWrappedItem() { - return wrappedItem; - } - }; - - /** - * Base implementation for item add or remove events. This is used when an - * event is fired from wrapped container and needs to be reconstructed to - * act like it actually came from this container. - */ - protected abstract class GeneratedItemAddOrRemoveEvent - implements Serializable { - - private Object firstItemId; - private int firstIndex; - private int count; - - protected GeneratedItemAddOrRemoveEvent(Object itemId, int first, - int count) { - firstItemId = itemId; - firstIndex = first; - this.count = count; - } - - public Container getContainer() { - return GeneratedPropertyContainer.this; - } - - public Object getFirstItemId() { - return firstItemId; - } - - public int getFirstIndex() { - return firstIndex; - } - - public int getAffectedItemsCount() { - return count; - } - }; - - protected class GeneratedItemRemoveEvent - extends GeneratedItemAddOrRemoveEvent implements ItemRemoveEvent { - - protected GeneratedItemRemoveEvent(ItemRemoveEvent event) { - super(event.getFirstItemId(), event.getFirstIndex(), - event.getRemovedItemsCount()); - } - - @Override - public int getRemovedItemsCount() { - return super.getAffectedItemsCount(); - } - } - - protected class GeneratedItemAddEvent extends GeneratedItemAddOrRemoveEvent - implements ItemAddEvent { - - protected GeneratedItemAddEvent(ItemAddEvent event) { - super(event.getFirstItemId(), event.getFirstIndex(), - event.getAddedItemsCount()); - } - - @Override - public int getAddedItemsCount() { - return super.getAffectedItemsCount(); - } - - } - - /** - * Constructor for GeneratedPropertyContainer. - * - * @param container - * underlying indexed container - */ - public GeneratedPropertyContainer(Container.Indexed container) { - wrappedContainer = container; - propertyGenerators = new HashMap>(); - - if (wrappedContainer instanceof Sortable) { - sortableContainer = (Sortable) wrappedContainer; - } - - if (wrappedContainer instanceof Filterable) { - activeFilters = new HashMap>(); - filterableContainer = (Filterable) wrappedContainer; - } else { - activeFilters = null; - } - - // ItemSetChangeEvents - if (wrappedContainer instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) wrappedContainer) - .addItemSetChangeListener(new ItemSetChangeListener() { - - @Override - public void containerItemSetChange( - ItemSetChangeEvent event) { - if (event instanceof ItemAddEvent) { - final ItemAddEvent addEvent = (ItemAddEvent) event; - fireItemSetChange( - new GeneratedItemAddEvent(addEvent)); - } else if (event instanceof ItemRemoveEvent) { - final ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; - fireItemSetChange(new GeneratedItemRemoveEvent( - removeEvent)); - } else { - fireItemSetChange(); - } - } - }); - } - - // PropertySetChangeEvents - if (wrappedContainer instanceof PropertySetChangeNotifier) { - ((PropertySetChangeNotifier) wrappedContainer) - .addPropertySetChangeListener( - new PropertySetChangeListener() { - - @Override - public void containerPropertySetChange( - PropertySetChangeEvent event) { - fireContainerPropertySetChange(); - } - }); - } - } - - /* Functions related to generated properties */ - - /** - * Add a new PropertyValueGenerator with given property id. This will - * override any existing properties with the same property id. Fires a - * PropertySetChangeEvent. - * - * @param propertyId - * property id - * @param generator - * a property value generator - */ - public void addGeneratedProperty(Object propertyId, - PropertyValueGenerator generator) { - propertyGenerators.put(propertyId, generator); - fireContainerPropertySetChange(); - } - - /** - * Removes any possible PropertyValueGenerator with given property id. Fires - * a PropertySetChangeEvent. - * - * @param propertyId - * property id - */ - public void removeGeneratedProperty(Object propertyId) { - if (propertyGenerators.containsKey(propertyId)) { - propertyGenerators.remove(propertyId); - fireContainerPropertySetChange(); - } - } - - private Item createGeneratedPropertyItem(final Object itemId, - final Item item) { - return new GeneratedPropertyItem(itemId, item); - } - - private Property createProperty(final Item item, - final Object propertyId, final Object itemId, - final PropertyValueGenerator generator) { - return new GeneratedProperty(item, propertyId, itemId, generator); - } - - /* Listener functionality */ - - @Override - public void addItemSetChangeListener(ItemSetChangeListener listener) { - super.addItemSetChangeListener(listener); - } - - @Override - public void addListener(ItemSetChangeListener listener) { - super.addListener(listener); - } - - @Override - public void removeItemSetChangeListener(ItemSetChangeListener listener) { - super.removeItemSetChangeListener(listener); - } - - @Override - public void removeListener(ItemSetChangeListener listener) { - super.removeListener(listener); - } - - @Override - public void addPropertySetChangeListener( - PropertySetChangeListener listener) { - super.addPropertySetChangeListener(listener); - } - - @Override - public void addListener(PropertySetChangeListener listener) { - super.addListener(listener); - } - - @Override - public void removePropertySetChangeListener( - PropertySetChangeListener listener) { - super.removePropertySetChangeListener(listener); - } - - @Override - public void removeListener(PropertySetChangeListener listener) { - super.removeListener(listener); - } - - /* Filtering functionality */ - - @Override - public void addContainerFilter(Filter filter) - throws UnsupportedFilterException { - if (filterableContainer == null) { - throw new UnsupportedOperationException( - "Wrapped container is not filterable"); - } - - List addedFilters = new ArrayList(); - for (Entry> entry : propertyGenerators - .entrySet()) { - Object property = entry.getKey(); - if (filter.appliesToProperty(property)) { - // Have generated property modify filter to fit the original - // data in the container. - Filter modifiedFilter = entry.getValue().modifyFilter(filter); - filterableContainer.addContainerFilter(modifiedFilter); - // Keep track of added filters - addedFilters.add(modifiedFilter); - } - } - - if (addedFilters.isEmpty()) { - // No generated property modified this filter, use it as is - addedFilters.add(filter); - filterableContainer.addContainerFilter(filter); - } - // Map filter to actually added filters - activeFilters.put(filter, addedFilters); - } - - @Override - public void removeContainerFilter(Filter filter) { - if (filterableContainer == null) { - throw new UnsupportedOperationException( - "Wrapped container is not filterable"); - } - - if (activeFilters.containsKey(filter)) { - for (Filter f : activeFilters.get(filter)) { - filterableContainer.removeContainerFilter(f); - } - activeFilters.remove(filter); - } - } - - @Override - public void removeAllContainerFilters() { - if (filterableContainer == null) { - throw new UnsupportedOperationException( - "Wrapped container is not filterable"); - } - filterableContainer.removeAllContainerFilters(); - activeFilters.clear(); - } - - @Override - public Collection getContainerFilters() { - if (filterableContainer == null) { - throw new UnsupportedOperationException( - "Wrapped container is not filterable"); - } - return Collections.unmodifiableSet(activeFilters.keySet()); - } - - /* Sorting functionality */ - - @Override - public void sort(Object[] propertyId, boolean[] ascending) { - if (sortableContainer == null) { - throw new UnsupportedOperationException( - "Wrapped container is not Sortable"); - } - - if (propertyId.length == 0) { - sortableContainer.sort(propertyId, ascending); - return; - } - - List actualSortProperties = new ArrayList(); - List actualSortDirections = new ArrayList(); - - for (int i = 0; i < propertyId.length; ++i) { - Object property = propertyId[i]; - SortDirection direction; - boolean isAscending = i < ascending.length ? ascending[i] : true; - if (isAscending) { - direction = SortDirection.ASCENDING; - } else { - direction = SortDirection.DESCENDING; - } - - if (propertyGenerators.containsKey(property)) { - // Sorting by a generated property. Generated property should - // modify sort orders to work with original properties in the - // container. - for (SortOrder s : propertyGenerators.get(property) - .getSortProperties( - new SortOrder(property, direction))) { - actualSortProperties.add(s.getPropertyId()); - actualSortDirections - .add(s.getDirection() == SortDirection.ASCENDING); - } - } else { - actualSortProperties.add(property); - actualSortDirections.add(isAscending); - } - } - - boolean[] actualAscending = new boolean[actualSortDirections.size()]; - for (int i = 0; i < actualAscending.length; ++i) { - actualAscending[i] = actualSortDirections.get(i); - } - - sortableContainer.sort(actualSortProperties.toArray(), actualAscending); - } - - @Override - public Collection getSortableContainerPropertyIds() { - if (sortableContainer == null) { - return Collections.emptySet(); - } - - Set sortablePropertySet = new HashSet( - sortableContainer.getSortableContainerPropertyIds()); - for (Entry> entry : propertyGenerators - .entrySet()) { - Object property = entry.getKey(); - SortOrder order = new SortOrder(property, SortDirection.ASCENDING); - if (entry.getValue().getSortProperties(order).length > 0) { - sortablePropertySet.add(property); - } else { - sortablePropertySet.remove(property); - } - } - - return sortablePropertySet; - } - - /* Item related overrides */ - - @Override - public Item addItemAfter(Object previousItemId, Object newItemId) - throws UnsupportedOperationException { - Item item = wrappedContainer.addItemAfter(previousItemId, newItemId); - if (item == null) { - return null; - } - return createGeneratedPropertyItem(newItemId, item); - } - - @Override - public Item addItem(Object itemId) throws UnsupportedOperationException { - Item item = wrappedContainer.addItem(itemId); - if (item == null) { - return null; - } - return createGeneratedPropertyItem(itemId, item); - } - - @Override - public Item addItemAt(int index, Object newItemId) - throws UnsupportedOperationException { - Item item = wrappedContainer.addItemAt(index, newItemId); - if (item == null) { - return null; - } - return createGeneratedPropertyItem(newItemId, item); - } - - @Override - public Item getItem(Object itemId) { - Item item = wrappedContainer.getItem(itemId); - if (item == null) { - return null; - } - - return createGeneratedPropertyItem(itemId, item); - } - - /* Property related overrides */ - - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - if (propertyGenerators.keySet().contains(propertyId)) { - return getItem(itemId).getItemProperty(propertyId); - } else if (!removedProperties.contains(propertyId)) { - return wrappedContainer.getContainerProperty(itemId, propertyId); - } - return null; - } - - /** - * Returns a list of propety ids available in this container. This - * collection will contain properties for generated properties. Removed - * properties will not show unless there is a generated property overriding - * those. - */ - @Override - public Collection getContainerPropertyIds() { - Set wrappedProperties = new LinkedHashSet( - wrappedContainer.getContainerPropertyIds()); - wrappedProperties.removeAll(removedProperties); - wrappedProperties.addAll(propertyGenerators.keySet()); - return wrappedProperties; - } - - /** - * Adds a previously removed property back to GeneratedPropertyContainer. - * Adding a property that is not previously removed causes an - * UnsupportedOperationException. - */ - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - if (!removedProperties.contains(propertyId)) { - throw new UnsupportedOperationException( - "GeneratedPropertyContainer does not support adding properties."); - } - removedProperties.remove(propertyId); - fireContainerPropertySetChange(); - return true; - } - - /** - * Marks the given property as hidden. This property from wrapped container - * will be removed from {@link #getContainerPropertyIds()} and is no longer - * be available in Items retrieved from this container. - */ - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - if (wrappedContainer.getContainerPropertyIds().contains(propertyId) - && removedProperties.add(propertyId)) { - fireContainerPropertySetChange(); - return true; - } - return false; - } - - /* Type related overrides */ - - @Override - public Class getType(Object propertyId) { - if (propertyGenerators.containsKey(propertyId)) { - return propertyGenerators.get(propertyId).getType(); - } else { - return wrappedContainer.getType(propertyId); - } - } - - /* Unmodified functions */ - - @Override - public Object nextItemId(Object itemId) { - return wrappedContainer.nextItemId(itemId); - } - - @Override - public Object prevItemId(Object itemId) { - return wrappedContainer.prevItemId(itemId); - } - - @Override - public Object firstItemId() { - return wrappedContainer.firstItemId(); - } - - @Override - public Object lastItemId() { - return wrappedContainer.lastItemId(); - } - - @Override - public boolean isFirstId(Object itemId) { - return wrappedContainer.isFirstId(itemId); - } - - @Override - public boolean isLastId(Object itemId) { - return wrappedContainer.isLastId(itemId); - } - - @Override - public Object addItemAfter(Object previousItemId) - throws UnsupportedOperationException { - return wrappedContainer.addItemAfter(previousItemId); - } - - @Override - public Collection getItemIds() { - return wrappedContainer.getItemIds(); - } - - @Override - public int size() { - return wrappedContainer.size(); - } - - @Override - public boolean containsId(Object itemId) { - return wrappedContainer.containsId(itemId); - } - - @Override - public Object addItem() throws UnsupportedOperationException { - return wrappedContainer.addItem(); - } - - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - return wrappedContainer.removeItem(itemId); - } - - @Override - public boolean removeAllItems() throws UnsupportedOperationException { - return wrappedContainer.removeAllItems(); - } - - @Override - public int indexOfId(Object itemId) { - return wrappedContainer.indexOfId(itemId); - } - - @Override - public Object getIdByIndex(int index) { - return wrappedContainer.getIdByIndex(index); - } - - @Override - public List getItemIds(int startIndex, int numberOfItems) { - return wrappedContainer.getItemIds(startIndex, numberOfItems); - } - - @Override - public Object addItemAt(int index) throws UnsupportedOperationException { - return wrappedContainer.addItemAt(index); - } - - /** - * Returns the original underlying container. - * - * @return the original underlying container - */ - public Container.Indexed getWrappedContainer() { - return wrappedContainer; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/HierarchicalContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/HierarchicalContainer.java deleted file mode 100644 index 115fd91791..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/HierarchicalContainer.java +++ /dev/null @@ -1,860 +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.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; - -/** - * A specialized Container whose contents can be accessed like it was a - * tree-like structure. - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class HierarchicalContainer extends IndexedContainer - implements Container.Hierarchical { - - /** - * Set of IDs of those contained Items that can't have children. - */ - private final HashSet noChildrenAllowed = new HashSet(); - - /** - * Mapping from Item ID to parent Item ID. - */ - private final HashMap parent = new HashMap(); - - /** - * Mapping from Item ID to parent Item ID for items included in the filtered - * container. - */ - private HashMap filteredParent = null; - - /** - * Mapping from Item ID to a list of child IDs. - */ - private final HashMap> children = new HashMap>(); - - /** - * Mapping from Item ID to a list of child IDs when filtered - */ - private HashMap> filteredChildren = null; - - /** - * List that contains all root elements of the container. - */ - private final LinkedList roots = new LinkedList(); - - /** - * List that contains all filtered root elements of the container. - */ - private LinkedList filteredRoots = null; - - /** - * Determines how filtering of the container is done. - */ - private boolean includeParentsWhenFiltering = true; - - /** - * Counts how many nested contents change disable calls are in progress. - * - * Pending events are only fired when the counter reaches zero again. - */ - private int contentChangedEventsDisabledCount = 0; - - private boolean contentsChangedEventPending; - - /* - * Can the specified Item have any children? Don't add a JavaDoc comment - * here, we use the default documentation from implemented interface. - */ - @Override - public boolean areChildrenAllowed(Object itemId) { - if (noChildrenAllowed.contains(itemId)) { - return false; - } - return containsId(itemId); - } - - /* - * Gets the IDs of the children of the specified Item. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Collection getChildren(Object itemId) { - LinkedList c; - - if (filteredChildren != null) { - c = filteredChildren.get(itemId); - } else { - c = children.get(itemId); - } - - if (c == null) { - return null; - } - return Collections.unmodifiableCollection(c); - } - - /* - * Gets the ID of the parent of the specified Item. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Object getParent(Object itemId) { - if (filteredParent != null) { - return filteredParent.get(itemId); - } - return parent.get(itemId); - } - - /* - * Is the Item corresponding to the given ID a leaf node? Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public boolean hasChildren(Object itemId) { - if (filteredChildren != null) { - return filteredChildren.containsKey(itemId); - } else { - return children.containsKey(itemId); - } - } - - /* - * Is the Item corresponding to the given ID a root node? Don't add a - * JavaDoc comment here, we use the default documentation from implemented - * interface. - */ - @Override - public boolean isRoot(Object itemId) { - // If the container is filtered the itemId must be among filteredRoots - // to be a root. - if (filteredRoots != null) { - if (!filteredRoots.contains(itemId)) { - return false; - } - } else { - // Container is not filtered - if (parent.containsKey(itemId)) { - return false; - } - } - - return containsId(itemId); - } - - /* - * Gets the IDs of the root elements in the container. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public Collection rootItemIds() { - if (filteredRoots != null) { - return Collections.unmodifiableCollection(filteredRoots); - } else { - return Collections.unmodifiableCollection(roots); - } - } - - /** - *

- * Sets the given Item's capability to have children. If the Item identified - * with the itemId already has children and the areChildrenAllowed is false - * this method fails and false is returned; the children must - * be first explicitly removed with - * {@link #setParent(Object itemId, Object newParentId)} or - * {@link com.vaadin.data.Container#removeItem(Object itemId)}. - *

- * - * @param itemId - * the ID of the Item in the container whose child capability is - * to be set. - * @param childrenAllowed - * the boolean value specifying if the Item can have children or - * not. - * @return true if the operation succeeded, false - * if not - */ - @Override - public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) { - - // Checks that the item is in the container - if (!containsId(itemId)) { - return false; - } - - // Updates status - if (childrenAllowed) { - noChildrenAllowed.remove(itemId); - } else { - noChildrenAllowed.add(itemId); - } - - return true; - } - - /** - *

- * Sets the parent of an Item. The new parent item must exist and be able to - * have children. (canHaveChildren(newParentId) == true). It is - * also possible to detach a node from the hierarchy (and thus make it root) - * by setting the parent null. - *

- * - * @param itemId - * the ID of the item to be set as the child of the Item - * identified with newParentId. - * @param newParentId - * the ID of the Item that's to be the new parent of the Item - * identified with itemId. - * @return true if the operation succeeded, false - * if not - */ - @Override - public boolean setParent(Object itemId, Object newParentId) { - - // Checks that the item is in the container - if (!containsId(itemId)) { - return false; - } - - // Gets the old parent - final Object oldParentId = parent.get(itemId); - - // Checks if no change is necessary - if ((newParentId == null && oldParentId == null) - || ((newParentId != null) && newParentId.equals(oldParentId))) { - return true; - } - - // Making root? - if (newParentId == null) { - // The itemId should become a root so we need to - // - Remove it from the old parent's children list - // - Add it as a root - // - Remove it from the item -> parent list (parent is null for - // roots) - - // Removes from old parents children list - final LinkedList l = children.get(oldParentId); - if (l != null) { - l.remove(itemId); - if (l.isEmpty()) { - children.remove(oldParentId); - } - - } - - // Add to be a root - roots.add(itemId); - - // Updates parent - parent.remove(itemId); - - if (hasFilters()) { - // Refilter the container if setParent is called when filters - // are applied. Changing parent can change what is included in - // the filtered version (if includeParentsWhenFiltering==true). - doFilterContainer(hasFilters()); - } - - fireItemSetChange(); - - return true; - } - - // We get here when the item should not become a root and we need to - // - Verify the new parent exists and can have children - // - Check that the new parent is not a child of the selected itemId - // - Updated the item -> parent mapping to point to the new parent - // - Remove the item from the roots list if it was a root - // - Remove the item from the old parent's children list if it was not a - // root - - // Checks that the new parent exists in container and can have - // children - if (!containsId(newParentId) - || noChildrenAllowed.contains(newParentId)) { - return false; - } - - // Checks that setting parent doesn't result to a loop - Object o = newParentId; - while (o != null && !o.equals(itemId)) { - o = parent.get(o); - } - if (o != null) { - return false; - } - - // Updates parent - parent.put(itemId, newParentId); - LinkedList pcl = children.get(newParentId); - if (pcl == null) { - // Create an empty list for holding children if one were not - // previously created - pcl = new LinkedList(); - children.put(newParentId, pcl); - } - pcl.add(itemId); - - // Removes from old parent or root - if (oldParentId == null) { - roots.remove(itemId); - } else { - final LinkedList l = children.get(oldParentId); - if (l != null) { - l.remove(itemId); - if (l.isEmpty()) { - children.remove(oldParentId); - } - } - } - - if (hasFilters()) { - // Refilter the container if setParent is called when filters - // are applied. Changing parent can change what is included in - // the filtered version (if includeParentsWhenFiltering==true). - doFilterContainer(hasFilters()); - } - - fireItemSetChange(); - - return true; - } - - private boolean hasFilters() { - return (filteredRoots != null); - } - - /** - * Moves a node (an Item) in the container immediately after a sibling node. - * The two nodes must have the same parent in the container. - * - * @param itemId - * the identifier of the moved node (Item) - * @param siblingId - * the identifier of the reference node (Item), after which the - * other node will be located - */ - public void moveAfterSibling(Object itemId, Object siblingId) { - Object parent2 = getParent(itemId); - LinkedList childrenList; - if (parent2 == null) { - childrenList = roots; - } else { - childrenList = children.get(parent2); - } - if (siblingId == null) { - childrenList.remove(itemId); - childrenList.addFirst(itemId); - - } else { - int oldIndex = childrenList.indexOf(itemId); - int indexOfSibling = childrenList.indexOf(siblingId); - if (indexOfSibling != -1 && oldIndex != -1) { - int newIndex; - if (oldIndex > indexOfSibling) { - newIndex = indexOfSibling + 1; - } else { - newIndex = indexOfSibling; - } - childrenList.remove(oldIndex); - childrenList.add(newIndex, itemId); - } else { - throw new IllegalArgumentException( - "Given identifiers no not have the same parent."); - } - } - fireItemSetChange(); - - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.IndexedContainer#addItem() - */ - @Override - public Object addItem() { - disableContentsChangeEvents(); - try { - final Object itemId = super.addItem(); - if (itemId == null) { - return null; - } - - if (!roots.contains(itemId)) { - roots.add(itemId); - if (filteredRoots != null) { - if (passesFilters(itemId)) { - filteredRoots.add(itemId); - } - } - } - return itemId; - } finally { - enableAndFireContentsChangeEvents(); - } - } - - @Override - protected void fireItemSetChange( - com.vaadin.data.Container.ItemSetChangeEvent event) { - if (contentsChangeEventsOn()) { - super.fireItemSetChange(event); - } else { - contentsChangedEventPending = true; - } - } - - private boolean contentsChangeEventsOn() { - return contentChangedEventsDisabledCount == 0; - } - - private void disableContentsChangeEvents() { - contentChangedEventsDisabledCount++; - } - - private void enableAndFireContentsChangeEvents() { - if (contentChangedEventsDisabledCount <= 0) { - getLogger().log(Level.WARNING, - "Mismatched calls to disable and enable contents change events in HierarchicalContainer"); - contentChangedEventsDisabledCount = 0; - } else { - contentChangedEventsDisabledCount--; - } - if (contentChangedEventsDisabledCount == 0) { - if (contentsChangedEventPending) { - fireItemSetChange(); - } - contentsChangedEventPending = false; - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.IndexedContainer#addItem(java.lang.Object) - */ - @Override - public Item addItem(Object itemId) { - disableContentsChangeEvents(); - try { - final Item item = super.addItem(itemId); - if (item == null) { - return null; - } - - roots.add(itemId); - - if (filteredRoots != null) { - if (passesFilters(itemId)) { - filteredRoots.add(itemId); - } - } - return item; - } finally { - enableAndFireContentsChangeEvents(); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.IndexedContainer#removeAllItems() - */ - @Override - public boolean removeAllItems() { - disableContentsChangeEvents(); - try { - final boolean success = super.removeAllItems(); - - if (success) { - roots.clear(); - parent.clear(); - children.clear(); - noChildrenAllowed.clear(); - if (filteredRoots != null) { - filteredRoots = null; - } - if (filteredChildren != null) { - filteredChildren = null; - } - if (filteredParent != null) { - filteredParent = null; - } - } - return success; - } finally { - enableAndFireContentsChangeEvents(); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.IndexedContainer#removeItem(java.lang.Object ) - */ - @Override - public boolean removeItem(Object itemId) { - disableContentsChangeEvents(); - try { - final boolean success = super.removeItem(itemId); - - if (success) { - // Remove from roots if this was a root - if (roots.remove(itemId)) { - - // If filtering is enabled we might need to remove it from - // the filtered list also - if (filteredRoots != null) { - filteredRoots.remove(itemId); - } - } - - // Clear the children list. Old children will now become root - // nodes - LinkedList childNodeIds = children.remove(itemId); - if (childNodeIds != null) { - if (filteredChildren != null) { - filteredChildren.remove(itemId); - } - for (Object childId : childNodeIds) { - setParent(childId, null); - } - } - - // Parent of the item that we are removing will contain the item - // id in its children list - final Object parentItemId = parent.get(itemId); - if (parentItemId != null) { - final LinkedList c = children.get(parentItemId); - if (c != null) { - c.remove(itemId); - - if (c.isEmpty()) { - children.remove(parentItemId); - } - - // Found in the children list so might also be in the - // filteredChildren list - if (filteredChildren != null) { - LinkedList f = filteredChildren - .get(parentItemId); - if (f != null) { - f.remove(itemId); - if (f.isEmpty()) { - filteredChildren.remove(parentItemId); - } - } - } - } - } - parent.remove(itemId); - if (filteredParent != null) { - // Item id no longer has a parent as the item id is not in - // the container. - filteredParent.remove(itemId); - } - noChildrenAllowed.remove(itemId); - } - - return success; - } finally { - enableAndFireContentsChangeEvents(); - } - } - - /** - * Removes the Item identified by given itemId and all its children. - * - * @see #removeItem(Object) - * @param itemId - * the identifier of the Item to be removed - * @return true if the operation succeeded - */ - public boolean removeItemRecursively(Object itemId) { - disableContentsChangeEvents(); - try { - boolean removeItemRecursively = removeItemRecursively(this, itemId); - return removeItemRecursively; - } finally { - enableAndFireContentsChangeEvents(); - } - } - - /** - * Removes the Item identified by given itemId and all its children from the - * given Container. - * - * @param container - * the container where the item is to be removed - * @param itemId - * the identifier of the Item to be removed - * @return true if the operation succeeded - */ - public static boolean removeItemRecursively( - Container.Hierarchical container, Object itemId) { - boolean success = true; - Collection children2 = container.getChildren(itemId); - if (children2 != null) { - Object[] array = children2.toArray(); - for (int i = 0; i < array.length; i++) { - boolean removeItemRecursively = removeItemRecursively(container, - array[i]); - if (!removeItemRecursively) { - success = false; - } - } - } - // remove the root of subtree if children where succesfully removed - if (success) { - success = container.removeItem(itemId); - } - return success; - - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.IndexedContainer#doSort() - */ - @Override - protected void doSort() { - super.doSort(); - - Collections.sort(roots, getItemSorter()); - for (LinkedList childList : children.values()) { - Collections.sort(childList, getItemSorter()); - } - } - - /** - * Used to control how filtering works. @see - * {@link #setIncludeParentsWhenFiltering(boolean)} for more information. - * - * @return true if all parents for items that match the filter are included - * when filtering, false if only the matching items are included - */ - public boolean isIncludeParentsWhenFiltering() { - return includeParentsWhenFiltering; - } - - /** - * Controls how the filtering of the container works. Set this to true to - * make filtering include parents for all matched items in addition to the - * items themselves. Setting this to false causes the filtering to only - * include the matching items and make items with excluded parents into root - * items. - * - * @param includeParentsWhenFiltering - * true to include all parents for items that match the filter, - * false to only include the matching items - */ - public void setIncludeParentsWhenFiltering( - boolean includeParentsWhenFiltering) { - this.includeParentsWhenFiltering = includeParentsWhenFiltering; - if (filteredRoots != null) { - // Currently filtered so needs to be re-filtered - doFilterContainer(true); - } - } - - /* - * Overridden to provide filtering for root & children items. - * - * (non-Javadoc) - * - * @see com.vaadin.data.util.IndexedContainer#updateContainerFiltering() - */ - @Override - protected boolean doFilterContainer(boolean hasFilters) { - if (!hasFilters) { - // All filters removed - filteredRoots = null; - filteredChildren = null; - filteredParent = null; - - return super.doFilterContainer(hasFilters); - } - - // Reset data structures - filteredRoots = new LinkedList(); - filteredChildren = new HashMap>(); - filteredParent = new HashMap(); - - if (includeParentsWhenFiltering) { - // Filter so that parents for items that match the filter are also - // included - HashSet includedItems = new HashSet(); - for (Object rootId : roots) { - if (filterIncludingParents(rootId, includedItems)) { - filteredRoots.add(rootId); - addFilteredChildrenRecursively(rootId, includedItems); - } - } - // includedItemIds now contains all the item ids that should be - // included. Filter IndexedContainer based on this - filterOverride = includedItems; - super.doFilterContainer(hasFilters); - filterOverride = null; - - return true; - } else { - // Filter by including all items that pass the filter and make items - // with no parent new root items - - // Filter IndexedContainer first so getItemIds return the items that - // match - super.doFilterContainer(hasFilters); - - LinkedHashSet filteredItemIds = new LinkedHashSet( - getItemIds()); - - for (Object itemId : filteredItemIds) { - Object itemParent = parent.get(itemId); - if (itemParent == null - || !filteredItemIds.contains(itemParent)) { - // Parent is not included or this was a root, in both cases - // this should be a filtered root - filteredRoots.add(itemId); - } else { - // Parent is included. Add this to the children list (create - // it first if necessary) - addFilteredChild(itemParent, itemId); - } - } - - return true; - } - } - - /** - * Adds the given childItemId as a filteredChildren for the parentItemId and - * sets it filteredParent. - * - * @param parentItemId - * @param childItemId - */ - private void addFilteredChild(Object parentItemId, Object childItemId) { - LinkedList parentToChildrenList = filteredChildren - .get(parentItemId); - if (parentToChildrenList == null) { - parentToChildrenList = new LinkedList(); - filteredChildren.put(parentItemId, parentToChildrenList); - } - filteredParent.put(childItemId, parentItemId); - parentToChildrenList.add(childItemId); - - } - - /** - * Recursively adds all items in the includedItems list to the - * filteredChildren map in the same order as they are in the children map. - * Starts from parentItemId and recurses down as long as child items that - * should be included are found. - * - * @param parentItemId - * The item id to start recurse from. Not added to a - * filteredChildren list - * @param includedItems - * Set containing the item ids for the items that should be - * included in the filteredChildren map - */ - private void addFilteredChildrenRecursively(Object parentItemId, - HashSet includedItems) { - LinkedList childList = children.get(parentItemId); - if (childList == null) { - return; - } - - for (Object childItemId : childList) { - if (includedItems.contains(childItemId)) { - addFilteredChild(parentItemId, childItemId); - addFilteredChildrenRecursively(childItemId, includedItems); - } - } - } - - /** - * Scans the itemId and all its children for which items should be included - * when filtering. All items which passes the filters are included. - * Additionally all items that have a child node that should be included are - * also themselves included. - * - * @param itemId - * @param includedItems - * @return true if the itemId should be included in the filtered container. - */ - private boolean filterIncludingParents(Object itemId, - HashSet includedItems) { - boolean toBeIncluded = passesFilters(itemId); - - LinkedList childList = children.get(itemId); - if (childList != null) { - for (Object childItemId : children.get(itemId)) { - toBeIncluded |= filterIncludingParents(childItemId, - includedItems); - } - } - - if (toBeIncluded) { - includedItems.add(itemId); - } - return toBeIncluded; - } - - private Set filterOverride = null; - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.util.IndexedContainer#passesFilters(java.lang.Object) - */ - @Override - protected boolean passesFilters(Object itemId) { - if (filterOverride != null) { - return filterOverride.contains(itemId); - } else { - return super.passesFilters(itemId); - } - } - - private static final Logger getLogger() { - return Logger.getLogger(HierarchicalContainer.class.getName()); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/IndexedContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/IndexedContainer.java deleted file mode 100644 index 4ff253394a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/IndexedContainer.java +++ /dev/null @@ -1,1201 +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.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EventObject; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.filter.SimpleStringFilter; -import com.vaadin.data.util.filter.UnsupportedFilterException; - -/** - * An implementation of the {@link Container.Indexed} interface - * with all important features. - *

- * - * Features: - *
    - *
  • {@link Container.Indexed} - *
  • {@link Container.Ordered} - *
  • {@link Container.Sortable} - *
  • {@link Container.Filterable} - *
  • {@link Cloneable} (deprecated, might be removed in the future) - *
  • Sends all needed events on content changes. - *
- * - * @see com.vaadin.data.Container - * - * @author Vaadin Ltd. - * @since 3.0 - */ - -@SuppressWarnings("serial") -// item type is really IndexedContainerItem, but using Item not to show it in -// public API -public class IndexedContainer - extends AbstractInMemoryContainer - implements Container.PropertySetChangeNotifier, - Property.ValueChangeNotifier, Container.Sortable, Cloneable, - Container.Filterable, Container.SimpleFilterable { - - /* Internal structure */ - - /** - * Linked list of ordered Property IDs. - */ - private ArrayList propertyIds = new ArrayList(); - - /** - * Property ID to type mapping. - */ - private Hashtable> types = new Hashtable>(); - - /** - * Hash of Items, where each Item is implemented as a mapping from Property - * ID to Property value. - */ - private Hashtable> items = new Hashtable>(); - - /** - * Set of properties that are read-only. - */ - private HashSet> readOnlyProperties = new HashSet>(); - - /** - * List of all Property value change event listeners listening all the - * properties. - */ - private LinkedList propertyValueChangeListeners = null; - - /** - * Data structure containing all listeners interested in changes to single - * Properties. The data structure is a hashtable mapping Property IDs to a - * hashtable that maps Item IDs to a linked list of listeners listening - * Property identified by given Property ID and Item ID. - */ - private Hashtable>> singlePropertyValueChangeListeners = null; - - private HashMap defaultPropertyValues; - - private int nextGeneratedItemId = 1; - - /* Container constructors */ - - public IndexedContainer() { - super(); - } - - public IndexedContainer(Collection itemIds) { - this(); - if (items != null) { - for (final Iterator i = itemIds.iterator(); i.hasNext();) { - Object itemId = i.next(); - internalAddItemAtEnd(itemId, new IndexedContainerItem(itemId), - false); - } - filterAll(); - } - } - - /* Container methods */ - - @Override - protected Item getUnfilteredItem(Object itemId) { - if (itemId != null && items.containsKey(itemId)) { - return new IndexedContainerItem(itemId); - } - return null; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getContainerPropertyIds() - */ - @Override - public Collection getContainerPropertyIds() { - return Collections.unmodifiableCollection(propertyIds); - } - - /** - * Gets the type of a Property stored in the list. - * - * @param id - * the ID of the Property. - * @return Type of the requested Property - */ - @Override - public Class getType(Object propertyId) { - return types.get(propertyId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object, - * java.lang.Object) - */ - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - // map lookup more efficient than propertyIds if there are many - // properties - if (!containsId(itemId) || propertyId == null - || !types.containsKey(propertyId)) { - return null; - } - - return new IndexedContainerProperty(itemId, propertyId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object, - * java.lang.Class, java.lang.Object) - */ - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) { - - // Fails, if nulls are given - if (propertyId == null || type == null) { - return false; - } - - // Fails if the Property is already present - if (propertyIds.contains(propertyId)) { - return false; - } - - // Adds the Property to Property list and types - propertyIds.add(propertyId); - types.put(propertyId, type); - - // If default value is given, set it - if (defaultValue != null) { - // for existing rows - for (final Iterator i = getAllItemIds().iterator(); i - .hasNext();) { - getItem(i.next()).getItemProperty(propertyId) - .setValue(defaultValue); - } - // store for next rows - if (defaultPropertyValues == null) { - defaultPropertyValues = new HashMap(); - } - defaultPropertyValues.put(propertyId, defaultValue); - } - - // Sends a change event - fireContainerPropertySetChange(); - - return true; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeAllItems() - */ - @Override - public boolean removeAllItems() { - int origSize = size(); - Object firstItem = getFirstVisibleItem(); - - internalRemoveAllItems(); - - items.clear(); - - // fire event only if the visible view changed, regardless of whether - // filtered out items were removed or not - if (origSize != 0) { - // Sends a change event - fireItemsRemoved(0, firstItem, origSize); - } - - return true; - } - - /** - * {@inheritDoc} - *

- * The item ID is generated from a sequence of Integers. The id of the first - * added item is 1. - */ - @Override - public Object addItem() { - - // Creates a new id - final Object id = generateId(); - - // Adds the Item into container - addItem(id); - - return id; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#addItem(java.lang.Object) - */ - @Override - public Item addItem(Object itemId) { - Item item = internalAddItemAtEnd(itemId, - new IndexedContainerItem(itemId), false); - if (item == null) { - return null; - } else if (!isFiltered()) { - // always the last item - fireItemAdded(size() - 1, itemId, item); - } else if (passesFilters(itemId) && !containsId(itemId)) { - getFilteredItemIds().add(itemId); - // always the last item - fireItemAdded(size() - 1, itemId, item); - } - return item; - } - - /** - * Helper method to add default values for items if available - * - * @param t - * data table of added item - */ - private void addDefaultValues(Hashtable t) { - if (defaultPropertyValues != null) { - for (Object key : defaultPropertyValues.keySet()) { - t.put(key, defaultPropertyValues.get(key)); - } - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeItem(java.lang.Object) - */ - @Override - public boolean removeItem(Object itemId) { - if (itemId == null || items.remove(itemId) == null) { - return false; - } - int origSize = size(); - int position = indexOfId(itemId); - if (internalRemoveItem(itemId)) { - // fire event only if the visible view changed, regardless of - // whether filtered out items were removed or not - if (size() != origSize) { - fireItemRemoved(position, itemId); - } - - return true; - } else { - return false; - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object ) - */ - @Override - public boolean removeContainerProperty(Object propertyId) { - - // Fails if the Property is not present - if (!propertyIds.contains(propertyId)) { - return false; - } - - // Removes the Property to Property list and types - propertyIds.remove(propertyId); - types.remove(propertyId); - if (defaultPropertyValues != null) { - defaultPropertyValues.remove(propertyId); - } - - // If remove the Property from all Items - for (final Iterator i = getAllItemIds().iterator(); i - .hasNext();) { - items.get(i.next()).remove(propertyId); - } - - // Sends a change event - fireContainerPropertySetChange(); - - return true; - } - - /* Container.Ordered methods */ - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object, - * java.lang.Object) - */ - @Override - public Item addItemAfter(Object previousItemId, Object newItemId) { - return internalAddItemAfter(previousItemId, newItemId, - new IndexedContainerItem(newItemId), true); - } - - /** - * {@inheritDoc} - *

- * The item ID is generated from a sequence of Integers. The id of the first - * added item is 1. - */ - @Override - public Object addItemAfter(Object previousItemId) { - - // Creates a new id - final Object id = generateId(); - - if (addItemAfter(previousItemId, id) != null) { - return id; - } else { - return null; - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object) - */ - @Override - public Item addItemAt(int index, Object newItemId) { - return internalAddItemAt(index, newItemId, - new IndexedContainerItem(newItemId), true); - } - - /** - * {@inheritDoc} - *

- * The item ID is generated from a sequence of Integers. The id of the first - * added item is 1. - */ - @Override - public Object addItemAt(int index) { - - // Creates a new id - final Object id = generateId(); - - // Adds the Item into container - addItemAt(index, id); - - return id; - } - - /** - * Generates an unique identifier for use as an item id. Guarantees that the - * generated id is not currently used as an id. - * - * @return - */ - private Serializable generateId() { - Serializable id; - do { - id = Integer.valueOf(nextGeneratedItemId++); - } while (items.containsKey(id)); - - return id; - } - - @Override - protected void registerNewItem(int index, Object newItemId, Item item) { - Hashtable t = new Hashtable(); - items.put(newItemId, t); - addDefaultValues(t); - } - - /* Event notifiers */ - - /** - * An event object specifying the list whose Item set has - * changed. - * - * @author Vaadin Ltd. - * @since 3.0 - */ - public static class ItemSetChangeEvent extends BaseItemSetChangeEvent { - - private final int addedItemIndex; - - private ItemSetChangeEvent(IndexedContainer source, - int addedItemIndex) { - super(source); - this.addedItemIndex = addedItemIndex; - } - - /** - * Iff one item is added, gives its index. - * - * @return -1 if either multiple items are changed or some other change - * than add is done. - */ - public int getAddedItemIndex() { - return addedItemIndex; - } - - } - - /** - * An event object specifying the Property in a list whose - * value has changed. - * - * @author Vaadin Ltd. - * @since 3.0 - */ - private static class PropertyValueChangeEvent extends EventObject - implements Property.ValueChangeEvent, Serializable { - - private PropertyValueChangeEvent(Property source) { - super(source); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property.ValueChangeEvent#getProperty() - */ - @Override - public Property getProperty() { - return (Property) getSource(); - } - - } - - @Override - public void addPropertySetChangeListener( - Container.PropertySetChangeListener listener) { - super.addPropertySetChangeListener(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addPropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Deprecated - @Override - public void addListener(Container.PropertySetChangeListener listener) { - addPropertySetChangeListener(listener); - } - - @Override - public void removePropertySetChangeListener( - Container.PropertySetChangeListener listener) { - super.removePropertySetChangeListener(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removePropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Deprecated - @Override - public void removeListener(Container.PropertySetChangeListener listener) { - removePropertySetChangeListener(listener); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property.ValueChangeNotifier#addListener(com. - * vaadin.data.Property.ValueChangeListener) - */ - @Override - public void addValueChangeListener(Property.ValueChangeListener listener) { - if (propertyValueChangeListeners == null) { - propertyValueChangeListeners = new LinkedList(); - } - propertyValueChangeListeners.add(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addValueChangeListener(com.vaadin.data.Property.ValueChangeListener)} - **/ - @Override - @Deprecated - public void addListener(Property.ValueChangeListener listener) { - addValueChangeListener(listener); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property.ValueChangeNotifier#removeListener(com - * .vaadin.data.Property.ValueChangeListener) - */ - @Override - public void removeValueChangeListener( - Property.ValueChangeListener listener) { - if (propertyValueChangeListeners != null) { - propertyValueChangeListeners.remove(listener); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeValueChangeListener(com.vaadin.data.Property.ValueChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(Property.ValueChangeListener listener) { - removeValueChangeListener(listener); - } - - /** - * Sends a Property value change event to all interested listeners. - * - * @param source - * the IndexedContainerProperty object. - */ - private void firePropertyValueChange(IndexedContainerProperty source) { - - // Sends event to listeners listening all value changes - if (propertyValueChangeListeners != null) { - final Object[] l = propertyValueChangeListeners.toArray(); - final Property.ValueChangeEvent event = new IndexedContainer.PropertyValueChangeEvent( - source); - for (int i = 0; i < l.length; i++) { - ((Property.ValueChangeListener) l[i]).valueChange(event); - } - } - - // Sends event to single property value change listeners - if (singlePropertyValueChangeListeners != null) { - final Map> propertySetToListenerListMap = singlePropertyValueChangeListeners - .get(source.propertyId); - if (propertySetToListenerListMap != null) { - final List listenerList = propertySetToListenerListMap - .get(source.itemId); - if (listenerList != null) { - final Property.ValueChangeEvent event = new IndexedContainer.PropertyValueChangeEvent( - source); - Object[] listeners = listenerList.toArray(); - for (int i = 0; i < listeners.length; i++) { - ((Property.ValueChangeListener) listeners[i]) - .valueChange(event); - } - } - } - } - - } - - @Override - public Collection getListeners(Class eventType) { - if (Property.ValueChangeEvent.class.isAssignableFrom(eventType)) { - if (propertyValueChangeListeners == null) { - return Collections.EMPTY_LIST; - } else { - return Collections - .unmodifiableCollection(propertyValueChangeListeners); - } - } - return super.getListeners(eventType); - } - - @Override - protected void fireItemAdded(int position, Object itemId, Item item) { - if (position >= 0) { - super.fireItemAdded(position, itemId, item); - } - } - - @Override - protected void fireItemSetChange() { - fireItemSetChange(new IndexedContainer.ItemSetChangeEvent(this, -1)); - } - - /** - * Adds new single Property change listener. - * - * @param propertyId - * the ID of the Property to add. - * @param itemId - * the ID of the Item . - * @param listener - * the listener to be added. - */ - private void addSinglePropertyChangeListener(Object propertyId, - Object itemId, Property.ValueChangeListener listener) { - if (listener != null) { - if (singlePropertyValueChangeListeners == null) { - singlePropertyValueChangeListeners = new Hashtable>>(); - } - Map> propertySetToListenerListMap = singlePropertyValueChangeListeners - .get(propertyId); - if (propertySetToListenerListMap == null) { - propertySetToListenerListMap = new Hashtable>(); - singlePropertyValueChangeListeners.put(propertyId, - propertySetToListenerListMap); - } - List listenerList = propertySetToListenerListMap - .get(itemId); - if (listenerList == null) { - listenerList = new LinkedList(); - propertySetToListenerListMap.put(itemId, listenerList); - } - listenerList.add(listener); - } - } - - /** - * Removes a previously registered single Property change listener. - * - * @param propertyId - * the ID of the Property to remove. - * @param itemId - * the ID of the Item. - * @param listener - * the listener to be removed. - */ - private void removeSinglePropertyChangeListener(Object propertyId, - Object itemId, Property.ValueChangeListener listener) { - if (listener != null && singlePropertyValueChangeListeners != null) { - final Map> propertySetToListenerListMap = singlePropertyValueChangeListeners - .get(propertyId); - if (propertySetToListenerListMap != null) { - final List listenerList = propertySetToListenerListMap - .get(itemId); - if (listenerList != null) { - listenerList.remove(listener); - if (listenerList.isEmpty()) { - propertySetToListenerListMap.remove(itemId); - } - } - if (propertySetToListenerListMap.isEmpty()) { - singlePropertyValueChangeListeners.remove(propertyId); - } - } - if (singlePropertyValueChangeListeners.isEmpty()) { - singlePropertyValueChangeListeners = null; - } - } - } - - /* Internal Item and Property implementations */ - - /* - * A class implementing the com.vaadin.data.Item interface to be contained - * in the list. - * - * @author Vaadin Ltd. - * - * - * @since 3.0 - */ - class IndexedContainerItem implements Item { - - /** - * Item ID in the host container for this Item. - */ - private final Object itemId; - - /** - * Constructs a new ListItem instance and connects it to a host - * container. - * - * @param itemId - * the Item ID of the new Item. - */ - private IndexedContainerItem(Object itemId) { - this.itemId = itemId; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Item#getItemProperty(java.lang.Object) - */ - @Override - public Property getItemProperty(Object id) { - if (!propertyIds.contains(id)) { - return null; - } - - return new IndexedContainerProperty(itemId, id); - } - - @Override - public Collection getItemPropertyIds() { - return Collections.unmodifiableCollection(propertyIds); - } - - /** - * Gets the String representation of the contents of the - * Item. The format of the string is a space separated catenation of the - * String representations of the values of the Properties - * contained by the Item. - * - * @return String representation of the Item contents - */ - @Override - public String toString() { - String retValue = ""; - - for (final Iterator i = propertyIds.iterator(); i.hasNext();) { - final Object propertyId = i.next(); - retValue += getItemProperty(propertyId).getValue(); - if (i.hasNext()) { - retValue += " "; - } - } - - return retValue; - } - - /** - * Calculates a integer hash-code for the Item that's unique inside the - * list. Two Items inside the same list have always different - * hash-codes, though Items in different lists may have identical - * hash-codes. - * - * @return A locally unique hash-code as integer - */ - @Override - public int hashCode() { - return itemId.hashCode(); - } - - /** - * Tests if the given object is the same as the this object. Two Items - * got from a list container with the same ID are equal. - * - * @param obj - * an object to compare with this object - * @return true if the given object is the same as this - * object, false if not - */ - @Override - public boolean equals(Object obj) { - if (obj == null - || !obj.getClass().equals(IndexedContainerItem.class)) { - return false; - } - final IndexedContainerItem li = (IndexedContainerItem) obj; - return getHost() == li.getHost() && itemId.equals(li.itemId); - } - - private IndexedContainer getHost() { - return IndexedContainer.this; - } - - /** - * IndexedContainerItem does not support adding new properties. Add - * properties at container level. See - * {@link IndexedContainer#addContainerProperty(Object, Class, Object)} - * - * @see com.vaadin.data.Item#addProperty(Object, Property) - */ - @Override - public boolean addItemProperty(Object id, Property property) - throws UnsupportedOperationException { - throw new UnsupportedOperationException("Indexed container item " - + "does not support adding new properties"); - } - - /** - * Indexed container does not support removing properties. Remove - * properties at container level. See - * {@link IndexedContainer#removeContainerProperty(Object)} - * - * @see com.vaadin.data.Item#removeProperty(Object) - */ - @Override - public boolean removeItemProperty(Object id) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Indexed container item does not support property removal"); - } - - } - - /** - * A class implementing the {@link Property} interface to be contained in - * the {@link IndexedContainerItem} contained in the - * {@link IndexedContainer}. - * - * @author Vaadin Ltd. - * - * @since 3.0 - */ - private class IndexedContainerProperty - implements Property, Property.ValueChangeNotifier { - - /** - * ID of the Item, where this property resides. - */ - private final Object itemId; - - /** - * Id of the Property. - */ - private final Object propertyId; - - /** - * Constructs a new {@link IndexedContainerProperty} object. - * - * @param itemId - * the ID of the Item to connect the new Property to. - * @param propertyId - * the Property ID of the new Property. - * @param host - * the list that contains the Item to contain the new - * Property. - */ - private IndexedContainerProperty(Object itemId, Object propertyId) { - if (itemId == null || propertyId == null) { - // Null ids are not accepted - throw new NullPointerException( - "Container item or property ids can not be null"); - } - this.propertyId = propertyId; - this.itemId = itemId; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property#getType() - */ - @Override - public Class getType() { - return (Class) types.get(propertyId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property#getValue() - */ - @Override - public T getValue() { - return (T) items.get(itemId).get(propertyId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property#isReadOnly() - */ - @Override - public boolean isReadOnly() { - return readOnlyProperties.contains(this); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property#setReadOnly(boolean) - */ - @Override - public void setReadOnly(boolean newStatus) { - if (newStatus) { - readOnlyProperties.add(this); - } else { - readOnlyProperties.remove(this); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property#setValue(java.lang.Object) - */ - @Override - public void setValue(Object newValue) - throws Property.ReadOnlyException { - // Gets the Property set - final Map propertySet = items.get(itemId); - - // Support null values on all types - if (newValue == null) { - propertySet.remove(propertyId); - } else if (getType().isAssignableFrom(newValue.getClass())) { - propertySet.put(propertyId, newValue); - } else { - throw new IllegalArgumentException( - "Value is of invalid type, got " - + newValue.getClass().getName() + " but " - + getType().getName() + " was expected"); - } - - // update the container filtering if this property is being filtered - if (isPropertyFiltered(propertyId)) { - filterAll(); - } - - firePropertyValueChange(this); - } - - /** - * Calculates a integer hash-code for the Property that's unique inside - * the Item containing the Property. Two different Properties inside the - * same Item contained in the same list always have different - * hash-codes, though Properties in different Items may have identical - * hash-codes. - * - * @return A locally unique hash-code as integer - */ - @Override - public int hashCode() { - return itemId.hashCode() ^ propertyId.hashCode(); - } - - /** - * Tests if the given object is the same as the this object. Two - * Properties got from an Item with the same ID are equal. - * - * @param obj - * an object to compare with this object - * @return true if the given object is the same as this - * object, false if not - */ - @Override - public boolean equals(Object obj) { - if (obj == null - || !obj.getClass().equals(IndexedContainerProperty.class)) { - return false; - } - final IndexedContainerProperty lp = (IndexedContainerProperty) obj; - return lp.getHost() == getHost() && lp.propertyId.equals(propertyId) - && lp.itemId.equals(itemId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property.ValueChangeNotifier#addListener( - * com.vaadin.data.Property.ValueChangeListener) - */ - @Override - public void addValueChangeListener( - Property.ValueChangeListener listener) { - addSinglePropertyChangeListener(propertyId, itemId, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addValueChangeListener(com.vaadin.data.Property.ValueChangeListener)} - **/ - @Override - @Deprecated - public void addListener(Property.ValueChangeListener listener) { - addValueChangeListener(listener); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Property.ValueChangeNotifier#removeListener - * (com.vaadin.data.Property.ValueChangeListener) - */ - @Override - public void removeValueChangeListener( - Property.ValueChangeListener listener) { - removeSinglePropertyChangeListener(propertyId, itemId, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeValueChangeListener(com.vaadin.data.Property.ValueChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(Property.ValueChangeListener listener) { - removeValueChangeListener(listener); - } - - private IndexedContainer getHost() { - return IndexedContainer.this; - } - - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], - * boolean[]) - */ - @Override - public void sort(Object[] propertyId, boolean[] ascending) { - sortContainer(propertyId, ascending); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds - * () - */ - @Override - public Collection getSortableContainerPropertyIds() { - return getSortablePropertyIds(); - } - - @Override - public ItemSorter getItemSorter() { - return super.getItemSorter(); - } - - @Override - public void setItemSorter(ItemSorter itemSorter) { - super.setItemSorter(itemSorter); - } - - /** - * Supports cloning of the IndexedContainer cleanly. - * - * @throws CloneNotSupportedException - * if an object cannot be cloned. . - * - * @deprecated As of 6.6. Cloning support might be removed from - * IndexedContainer in the future - */ - @Deprecated - @Override - public Object clone() throws CloneNotSupportedException { - - // Creates the clone - final IndexedContainer nc = new IndexedContainer(); - - // Clone the shallow properties - nc.setAllItemIds(getAllItemIds() != null - ? (ListSet) ((ListSet) getAllItemIds()).clone() - : null); - nc.setItemSetChangeListeners(getItemSetChangeListeners() != null - ? new LinkedList( - getItemSetChangeListeners()) - : null); - nc.propertyIds = propertyIds != null - ? (ArrayList) propertyIds.clone() : null; - nc.setPropertySetChangeListeners(getPropertySetChangeListeners() != null - ? new LinkedList( - getPropertySetChangeListeners()) - : null); - nc.propertyValueChangeListeners = propertyValueChangeListeners != null - ? (LinkedList) propertyValueChangeListeners - .clone() - : null; - nc.readOnlyProperties = readOnlyProperties != null - ? (HashSet>) readOnlyProperties.clone() : null; - nc.singlePropertyValueChangeListeners = singlePropertyValueChangeListeners != null - ? (Hashtable>>) singlePropertyValueChangeListeners - .clone() - : null; - - nc.types = types != null ? (Hashtable>) types.clone() - : null; - - nc.setFilters( - (HashSet) ((HashSet) getFilters()).clone()); - - nc.setFilteredItemIds(getFilteredItemIds() == null ? null - : (ListSet) ((ListSet) getFilteredItemIds()) - .clone()); - - // Clone property-values - if (items == null) { - nc.items = null; - } else { - nc.items = new Hashtable>(); - for (final Iterator i = items.keySet().iterator(); i - .hasNext();) { - final Object id = i.next(); - final Hashtable it = (Hashtable) items - .get(id); - nc.items.put(id, (Map) it.clone()); - } - } - - return nc; - } - - @Override - public void addContainerFilter(Object propertyId, String filterString, - boolean ignoreCase, boolean onlyMatchPrefix) { - try { - addFilter(new SimpleStringFilter(propertyId, filterString, - ignoreCase, onlyMatchPrefix)); - } catch (UnsupportedFilterException e) { - // the filter instance created here is always valid for in-memory - // containers - } - } - - @Override - public void removeAllContainerFilters() { - removeAllFilters(); - } - - @Override - public void removeContainerFilters(Object propertyId) { - removeFilters(propertyId); - } - - @Override - public void addContainerFilter(Filter filter) - throws UnsupportedFilterException { - addFilter(filter); - } - - @Override - public void removeContainerFilter(Filter filter) { - removeFilter(filter); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() - */ - @Override - public boolean hasContainerFilters() { - return super.hasContainerFilters(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() - */ - @Override - public Collection getContainerFilters() { - return super.getContainerFilters(); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheFlushNotifier.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheFlushNotifier.java deleted file mode 100644 index d3559b5652..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheFlushNotifier.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.data.util.sqlcontainer; - -import java.io.Serializable; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -import com.vaadin.data.util.sqlcontainer.query.FreeformQuery; -import com.vaadin.data.util.sqlcontainer.query.QueryDelegate; -import com.vaadin.data.util.sqlcontainer.query.TableQuery; - -/** - * CacheFlushNotifier is a simple static notification mechanism to inform other - * SQLContainers that the contents of their caches may have become stale. - */ -class CacheFlushNotifier implements Serializable { - /* - * SQLContainer instance reference list and dead reference queue. Used for - * the cache flush notification feature. - */ - private static List> allInstances = new ArrayList>(); - private static ReferenceQueue deadInstances = new ReferenceQueue(); - - /** - * Adds the given SQLContainer to the cache flush notification receiver list - * - * @param c - * Container to add - */ - public static void addInstance(SQLContainer c) { - removeDeadReferences(); - if (c != null) { - allInstances.add(new WeakReference(c, deadInstances)); - } - } - - /** - * Removes dead references from instance list - */ - private static void removeDeadReferences() { - java.lang.ref.Reference dead = deadInstances - .poll(); - while (dead != null) { - allInstances.remove(dead); - dead = deadInstances.poll(); - } - } - - /** - * Iterates through the instances and notifies containers which are - * connected to the same table or are using the same query string. - * - * @param c - * SQLContainer that issued the cache flush notification - */ - public static void notifyOfCacheFlush(SQLContainer c) { - removeDeadReferences(); - for (WeakReference wr : allInstances) { - if (wr.get() != null) { - SQLContainer wrc = wr.get(); - if (wrc == null) { - continue; - } - /* - * If the reference points to the container sending the - * notification, do nothing. - */ - if (wrc.equals(c)) { - continue; - } - /* Compare QueryDelegate types and tableName/queryString */ - QueryDelegate wrQd = wrc.getQueryDelegate(); - QueryDelegate qd = c.getQueryDelegate(); - if (wrQd instanceof TableQuery && qd instanceof TableQuery - && ((TableQuery) wrQd).getTableName() - .equals(((TableQuery) qd).getTableName())) { - wrc.refresh(); - } else if (wrQd instanceof FreeformQuery - && qd instanceof FreeformQuery - && ((FreeformQuery) wrQd).getQueryString().equals( - ((FreeformQuery) qd).getQueryString())) { - wrc.refresh(); - } - } - } - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheMap.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheMap.java deleted file mode 100644 index 77919f72b2..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/CacheMap.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.util.sqlcontainer; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * CacheMap extends LinkedHashMap, adding the possibility to adjust maximum - * number of items. In SQLContainer this is used for RowItem -cache. Cache size - * will be two times the page length parameter of the container. - */ -class CacheMap extends LinkedHashMap { - private static final long serialVersionUID = 679999766473555231L; - private int cacheLimit = SQLContainer.CACHE_RATIO - * SQLContainer.DEFAULT_PAGE_LENGTH; - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > cacheLimit; - } - - void setCacheLimit(int limit) { - cacheLimit = limit > 0 ? limit : SQLContainer.DEFAULT_PAGE_LENGTH; - } - - int getCacheLimit() { - return cacheLimit; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ColumnProperty.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ColumnProperty.java deleted file mode 100644 index e7d83e118d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ColumnProperty.java +++ /dev/null @@ -1,357 +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.sqlcontainer; - -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.logging.Logger; - -import com.vaadin.data.Property; -import com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException; - -/** - * ColumnProperty represents the value of one column in a RowItem. In addition - * to the value, ColumnProperty also contains some basic column attributes such - * as nullability status, read-only status and data type. - * - * Note that depending on the QueryDelegate in use this does not necessarily map - * into an actual column in a database table. - */ -final public class ColumnProperty implements Property { - private static final long serialVersionUID = -3694463129581802457L; - - private RowItem owner; - - private String propertyId; - - private boolean readOnly; - private boolean allowReadOnlyChange = true; - private boolean nullable = true; - - private Object value; - private Object changedValue; - private Class type; - - private boolean modified; - - private boolean versionColumn; - private boolean primaryKey = false; - - /** - * Prevent instantiation without required parameters. - */ - @SuppressWarnings("unused") - private ColumnProperty() { - } - - /** - * Deprecated constructor for ColumnProperty. If this is used the primary - * keys are not identified correctly in some cases for some databases (i.e. - * Oracle). See http://dev.vaadin.com/ticket/9145. - * - * @param propertyId - * @param readOnly - * @param allowReadOnlyChange - * @param nullable - * @param value - * @param type - * - * @deprecated As of 7.0. Use - * {@link #ColumnProperty(String, boolean, boolean, boolean, boolean, Object, Class) - * instead - */ - @Deprecated - public ColumnProperty(String propertyId, boolean readOnly, - boolean allowReadOnlyChange, boolean nullable, Object value, - Class type) { - this(propertyId, readOnly, allowReadOnlyChange, nullable, false, value, - type); - } - - /** - * Creates a new ColumnProperty instance. - * - * @param propertyId - * The ID of this property. - * @param readOnly - * Whether this property is read-only. - * @param allowReadOnlyChange - * Whether the read-only status of this property can be changed. - * @param nullable - * Whether this property accepts null values. - * @param primaryKey - * Whether this property corresponds to a database primary key. - * @param value - * The value of this property. - * @param type - * The type of this property. - */ - public ColumnProperty(String propertyId, boolean readOnly, - boolean allowReadOnlyChange, boolean nullable, boolean primaryKey, - Object value, Class type) { - - if (propertyId == null) { - throw new IllegalArgumentException("Properties must be named."); - } - if (type == null) { - throw new IllegalArgumentException("Property type must be set."); - } - this.propertyId = propertyId; - this.type = type; - this.value = value; - - this.allowReadOnlyChange = allowReadOnlyChange; - this.nullable = nullable; - this.readOnly = readOnly; - this.primaryKey = primaryKey; - } - - /** - * Returns the current value for this property. To get the previous value - * (if one exists) for a modified property use {@link #getOldValue()}. - * - * @return - */ - @Override - public Object getValue() { - if (isModified()) { - return changedValue; - } - return value; - } - - /** - * Returns the original non-modified value of this property if it has been - * modified. - * - * @return The original value if isModified() is true, - * getValue() otherwise. - */ - public Object getOldValue() { - return value; - } - - @Override - public void setValue(Object newValue) - throws ReadOnlyException, ConversionException { - if (newValue == null && !nullable) { - throw new NotNullableException( - "Null values are not allowed for this property."); - } - if (readOnly) { - throw new ReadOnlyException( - "Cannot set value for read-only property."); - } - - /* Check if this property is a date property. */ - boolean isDateProperty = Time.class.equals(getType()) - || Date.class.equals(getType()) - || Timestamp.class.equals(getType()); - - if (newValue != null) { - /* Handle SQL dates, times and Timestamps given as java.util.Date */ - if (isDateProperty) { - /* - * Try to get the millisecond value from the new value of this - * property. Possible type to convert from is java.util.Date. - */ - long millis = 0; - if (newValue instanceof java.util.Date) { - millis = ((java.util.Date) newValue).getTime(); - /* - * Create the new object based on the millisecond value, - * according to the type of this property. - */ - if (Time.class.equals(getType())) { - newValue = new Time(millis); - } else if (Date.class.equals(getType())) { - newValue = new Date(millis); - } else if (Timestamp.class.equals(getType())) { - newValue = new Timestamp(millis); - } - } - } - - if (!getType().isAssignableFrom(newValue.getClass())) { - throw new IllegalArgumentException( - "Illegal value type for ColumnProperty"); - } - - /* - * If the value to be set is the same that has already been set, do - * not set it again. - */ - if (isValueAlreadySet(newValue)) { - return; - } - } - - /* Set the new value and notify container of the change. */ - changedValue = newValue; - modified = true; - owner.getContainer().itemChangeNotification(owner); - } - - private boolean isValueAlreadySet(Object newValue) { - Object referenceValue = isModified() ? changedValue : value; - - return (isNullable() && newValue == null && referenceValue == null) - || newValue.equals(referenceValue); - } - - @Override - public Class getType() { - return type; - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - /** - * Returns whether the read-only status of this property can be changed - * using {@link #setReadOnly(boolean)}. - *

- * Used to prevent setting to read/write mode a property that is not allowed - * to be written by the underlying database. Also used for values like - * VERSION and AUTO_INCREMENT fields that might be set to read-only by the - * container but the database still allows writes. - * - * @return true if the read-only status can be changed, false otherwise. - */ - public boolean isReadOnlyChangeAllowed() { - return allowReadOnlyChange; - } - - @Override - public void setReadOnly(boolean newStatus) { - if (allowReadOnlyChange) { - readOnly = newStatus; - } - } - - public boolean isPrimaryKey() { - return primaryKey; - } - - public String getPropertyId() { - return propertyId; - } - - private static Logger getLogger() { - return Logger.getLogger(ColumnProperty.class.getName()); - } - - public void setOwner(RowItem owner) { - if (owner == null) { - throw new IllegalArgumentException("Owner can not be set to null."); - } - if (this.owner != null) { - throw new IllegalStateException( - "ColumnProperties can only be bound once."); - } - this.owner = owner; - } - - public boolean isModified() { - return modified; - } - - public boolean isVersionColumn() { - return versionColumn; - } - - public void setVersionColumn(boolean versionColumn) { - this.versionColumn = versionColumn; - } - - public boolean isNullable() { - return nullable; - } - - /** - * Return whether the value of this property should be persisted to the - * database. - * - * @return true if the value should be written to the database, false - * otherwise. - */ - public boolean isPersistent() { - if (isVersionColumn()) { - return false; - } else if (isReadOnlyChangeAllowed() && !isReadOnly()) { - return true; - } else { - return false; - } - } - - /** - * Returns whether or not this property is used as a row identifier. - * - * @return true if the property is a row identifier, false otherwise. - */ - public boolean isRowIdentifier() { - return isPrimaryKey() || isVersionColumn(); - } - - /** - * An exception that signals that a null value was passed to - * the setValue method, but the value of this property can not - * be set to null. - */ - @SuppressWarnings("serial") - public class NotNullableException extends RuntimeException { - - /** - * Constructs a new NotNullableException without a detail - * message. - */ - public NotNullableException() { - } - - /** - * Constructs a new NotNullableException with the specified - * detail message. - * - * @param msg - * the detail message - */ - public NotNullableException(String msg) { - super(msg); - } - - /** - * Constructs a new NotNullableException from another - * exception. - * - * @param cause - * The cause of the failure - */ - public NotNullableException(Throwable cause) { - super(cause); - } - } - - public void commit() { - if (isModified()) { - modified = false; - value = changedValue; - } - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/OptimisticLockException.java deleted file mode 100644 index 1a29d531bc..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/OptimisticLockException.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.data.util.sqlcontainer; - -import com.vaadin.data.util.sqlcontainer.query.TableQuery; - -/** - * An OptimisticLockException is thrown when trying to update or delete a row - * that has been changed since last read from the database. - * - * OptimisticLockException is a runtime exception because optimistic locking is - * turned off by default, and as such will never be thrown in a default - * configuration. In order to turn on optimistic locking, you need to specify - * the version column in your TableQuery instance. - * - * @see TableQuery#setVersionColumn(String) - * - * @author Jonatan Kronqvist / Vaadin Ltd - */ -public class OptimisticLockException extends RuntimeException { - - private final RowId rowId; - - public OptimisticLockException(RowId rowId) { - super(); - this.rowId = rowId; - } - - public OptimisticLockException(String msg, RowId rowId) { - super(msg); - this.rowId = rowId; - } - - public RowId getRowId() { - return rowId; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowId.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowId.java deleted file mode 100644 index 367135782f..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowId.java +++ /dev/null @@ -1,48 +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.sqlcontainer; - -public class ReadOnlyRowId extends RowId { - private static final long serialVersionUID = -2626764781642012467L; - private final Integer rowNum; - - public ReadOnlyRowId(int rowNum) { - super(); - this.rowNum = rowNum; - } - - @Override - public int hashCode() { - return getRowNum(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null || !(ReadOnlyRowId.class.equals(obj.getClass()))) { - return false; - } - return getRowNum() == (((ReadOnlyRowId) obj).getRowNum()); - } - - public int getRowNum() { - return rowNum; - } - - @Override - public String toString() { - return String.valueOf(getRowNum()); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/Reference.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/Reference.java deleted file mode 100644 index ce6680089d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/Reference.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.data.util.sqlcontainer; - -import java.io.Serializable; - -/** - * The reference class represents a simple [usually foreign key] reference to - * another SQLContainer. Actual foreign key reference in the database is not - * required, but it is recommended to make sure that certain constraints are - * followed. - */ -@SuppressWarnings("serial") -class Reference implements Serializable { - - /** - * The SQLContainer that this reference points to. - */ - private SQLContainer referencedContainer; - - /** - * The column ID/name in the referencing SQLContainer that contains the key - * used for the reference. - */ - private String referencingColumn; - - /** - * The column ID/name in the referenced SQLContainer that contains the key - * used for the reference. - */ - private String referencedColumn; - - /** - * Constructs a new reference to be used within the SQLContainer to - * reference another SQLContainer. - */ - Reference(SQLContainer referencedContainer, String referencingColumn, - String referencedColumn) { - this.referencedContainer = referencedContainer; - this.referencingColumn = referencingColumn; - this.referencedColumn = referencedColumn; - } - - SQLContainer getReferencedContainer() { - return referencedContainer; - } - - String getReferencingColumn() { - return referencingColumn; - } - - String getReferencedColumn() { - return referencedColumn; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowId.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowId.java deleted file mode 100644 index 5f78df79e1..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowId.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.data.util.sqlcontainer; - -import java.io.Serializable; -import java.util.Arrays; - -/** - * RowId represents identifiers of a single database result set row. - * - * The data structure of a RowId is an Object array which contains the values of - * the primary key columns of the identified row. This allows easy equals() - * -comparison of RowItems. - */ -public class RowId implements Serializable { - private static final long serialVersionUID = -3161778404698901258L; - protected Object[] id; - - /** - * Prevent instantiation without required parameters. - */ - protected RowId() { - } - - public RowId(Object... id) { - if (id == null) { - throw new IllegalArgumentException( - "id parameter must not be null!"); - } - this.id = id; - } - - public Object[] getId() { - return id; - } - - @Override - public int hashCode() { - return Arrays.hashCode(getId()); - } - - @Override - public boolean equals(Object obj) { - if (obj == null || !(RowId.class.equals(obj.getClass()))) { - return false; - } - return Arrays.equals(getId(), ((RowId) obj).getId()); - } - - @Override - public String toString() { - if (getId() == null) { - return ""; - } - StringBuilder builder = new StringBuilder(); - for (Object id : getId()) { - builder.append(id); - builder.append('/'); - } - if (builder.length() > 0) { - return builder.substring(0, builder.length() - 1); - } - return builder.toString(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowItem.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowItem.java deleted file mode 100644 index ffc8bfc6f7..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/RowItem.java +++ /dev/null @@ -1,145 +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.sqlcontainer; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; - -/** - * RowItem represents one row of a result set obtained from a QueryDelegate. - * - * Note that depending on the QueryDelegate in use this does not necessarily map - * into an actual row in a database table. - */ -public final class RowItem implements Item { - private static final long serialVersionUID = -6228966439127951408L; - private SQLContainer container; - private RowId id; - private Collection properties; - - /** - * Prevent instantiation without required parameters. - */ - @SuppressWarnings("unused") - private RowItem() { - } - - public RowItem(SQLContainer container, RowId id, - Collection properties) { - if (container == null) { - throw new IllegalArgumentException("Container cannot be null."); - } - if (id == null) { - throw new IllegalArgumentException("Row ID cannot be null."); - } - this.container = container; - this.properties = properties; - /* Set this RowItem as owner to the properties */ - if (properties != null) { - for (ColumnProperty p : properties) { - p.setOwner(this); - } - } - this.id = id; - } - - @Override - public Property getItemProperty(Object id) { - if (id instanceof String && id != null) { - for (ColumnProperty cp : properties) { - if (id.equals(cp.getPropertyId())) { - return cp; - } - } - } - return null; - } - - @Override - public Collection getItemPropertyIds() { - Collection ids = new ArrayList(properties.size()); - for (ColumnProperty cp : properties) { - ids.add(cp.getPropertyId()); - } - return Collections.unmodifiableCollection(ids); - } - - /** - * Adding properties is not supported. Properties are generated by - * SQLContainer. - */ - @Override - public boolean addItemProperty(Object id, Property property) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /** - * Removing properties is not supported. Properties are generated by - * SQLContainer. - */ - @Override - public boolean removeItemProperty(Object id) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - public RowId getId() { - return id; - } - - public SQLContainer getContainer() { - return container; - } - - public boolean isModified() { - if (properties != null) { - for (ColumnProperty p : properties) { - if (p.isModified()) { - return true; - } - } - } - return false; - } - - @Override - public String toString() { - StringBuffer s = new StringBuffer(); - s.append("ID:"); - s.append(getId().toString()); - for (Object propId : getItemPropertyIds()) { - s.append("|"); - s.append(propId.toString()); - s.append(":"); - Object value = getItemProperty(propId).getValue(); - s.append((null != value) ? value.toString() : null); - } - return s.toString(); - } - - public void commit() { - if (properties != null) { - for (ColumnProperty p : properties) { - p.commit(); - } - } - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLContainer.java deleted file mode 100644 index dde57d3610..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLContainer.java +++ /dev/null @@ -1,1875 +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.sqlcontainer; - -import java.io.IOException; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.ConcurrentModificationException; -import java.util.Date; -import java.util.EventObject; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.data.Container; -import com.vaadin.data.ContainerHelpers; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.filter.UnsupportedFilterException; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; -import com.vaadin.data.util.sqlcontainer.query.QueryDelegate; -import com.vaadin.data.util.sqlcontainer.query.QueryDelegate.RowIdChangeListener; -import com.vaadin.data.util.sqlcontainer.query.TableQuery; -import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator; - -public class SQLContainer implements Container, Container.Filterable, - Container.Indexed, Container.Sortable, Container.ItemSetChangeNotifier { - - /** Query delegate */ - private QueryDelegate queryDelegate; - /** Auto commit mode, default = false */ - private boolean autoCommit = false; - - /** Page length = number of items contained in one page */ - private int pageLength = DEFAULT_PAGE_LENGTH; - public static final int DEFAULT_PAGE_LENGTH = 100; - - /** Number of items to cache = CACHE_RATIO x pageLength */ - public static final int CACHE_RATIO = 2; - - /** Amount of cache to overlap with previous page */ - private int cacheOverlap = pageLength; - - /** Item and index caches */ - private final Map itemIndexes = new HashMap(); - private final CacheMap cachedItems = new CacheMap(); - - /** Container properties = column names, data types and statuses */ - private final List propertyIds = new ArrayList(); - private final Map> propertyTypes = new HashMap>(); - private final Map propertyReadOnly = new HashMap(); - private final Map propertyPersistable = new HashMap(); - private final Map propertyNullable = new HashMap(); - private final Map propertyPrimaryKey = new HashMap(); - - /** Filters (WHERE) and sorters (ORDER BY) */ - private final List filters = new ArrayList(); - private final List sorters = new ArrayList(); - - /** - * Total number of items available in the data source using the current - * query, filters and sorters. - */ - private int size; - - /** - * Size updating logic. Do not update size from data source if it has been - * updated in the last sizeValidMilliSeconds milliseconds. - */ - private final int sizeValidMilliSeconds = 10000; - private boolean sizeDirty = true; - private Date sizeUpdated = new Date(); - - /** Starting row number of the currently fetched page */ - private int currentOffset; - - /** ItemSetChangeListeners */ - private LinkedList itemSetChangeListeners; - - /** - * Temporary storage for modified items and items to be removed and added - */ - private final Map removedItems = new HashMap(); - private final List addedItems = new ArrayList(); - private final List modifiedItems = new ArrayList(); - - /** List of references to other SQLContainers */ - private final Map references = new HashMap(); - - /** Cache flush notification system enabled. Disabled by default. */ - private boolean notificationsEnabled; - - /** - * Prevent instantiation without a QueryDelegate. - */ - @SuppressWarnings("unused") - private SQLContainer() { - } - - /** - * Creates and initializes SQLContainer using the given QueryDelegate - * - * @param delegate - * QueryDelegate implementation - * @throws SQLException - */ - public SQLContainer(QueryDelegate delegate) throws SQLException { - if (delegate == null) { - throw new IllegalArgumentException( - "QueryDelegate must not be null."); - } - queryDelegate = delegate; - getPropertyIds(); - cachedItems.setCacheLimit(CACHE_RATIO * getPageLength() + cacheOverlap); - } - - /**************************************/ - /** Methods from interface Container **/ - /**************************************/ - - /** - * Note! If auto commit mode is enabled, this method will still return the - * temporary row ID assigned for the item. Implement - * QueryDelegate.RowIdChangeListener to receive the actual Row ID value - * after the addition has been committed. - * - * {@inheritDoc} - */ - - @Override - public Object addItem() throws UnsupportedOperationException { - Object emptyKey[] = new Object[queryDelegate.getPrimaryKeyColumns() - .size()]; - RowId itemId = new TemporaryRowId(emptyKey); - // Create new empty column properties for the row item. - List itemProperties = new ArrayList(); - for (String propertyId : propertyIds) { - /* Default settings for new item properties. */ - ColumnProperty cp = new ColumnProperty(propertyId, - propertyReadOnly.get(propertyId), - propertyPersistable.get(propertyId), - propertyNullable.get(propertyId), - propertyPrimaryKey.get(propertyId), null, - getType(propertyId)); - - itemProperties.add(cp); - } - RowItem newRowItem = new RowItem(this, itemId, itemProperties); - - if (autoCommit) { - /* Add and commit instantly */ - try { - if (queryDelegate instanceof TableQuery) { - itemId = ((TableQuery) queryDelegate) - .storeRowImmediately(newRowItem); - } else { - queryDelegate.beginTransaction(); - queryDelegate.storeRow(newRowItem); - queryDelegate.commit(); - } - refresh(); - if (notificationsEnabled) { - CacheFlushNotifier.notifyOfCacheFlush(this); - } - getLogger().log(Level.FINER, "Row added to DB..."); - return itemId; - } catch (SQLException e) { - getLogger().log(Level.WARNING, - "Failed to add row to DB. Rolling back.", e); - try { - queryDelegate.rollback(); - } catch (SQLException ee) { - getLogger().log(Level.SEVERE, - "Failed to roll back row addition", e); - } - return null; - } - } else { - addedItems.add(newRowItem); - fireContentsChange(); - return itemId; - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#containsId(java.lang.Object) - */ - - @Override - public boolean containsId(Object itemId) { - if (itemId == null) { - return false; - } - - if (cachedItems.containsKey(itemId)) { - return true; - } else { - for (RowItem item : addedItems) { - if (item.getId().equals(itemId)) { - return itemPassesFilters(item); - } - } - } - if (removedItems.containsKey(itemId)) { - return false; - } - - if (itemId instanceof ReadOnlyRowId) { - int rowNum = ((ReadOnlyRowId) itemId).getRowNum(); - return rowNum >= 0 && rowNum < size; - } - - if (itemId instanceof RowId && !(itemId instanceof TemporaryRowId)) { - try { - return queryDelegate - .containsRowWithKey(((RowId) itemId).getId()); - } catch (Exception e) { - /* Query failed, just return false. */ - getLogger().log(Level.WARNING, "containsId query failed", e); - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object, - * java.lang.Object) - */ - - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - Item item = getItem(itemId); - if (item == null) { - return null; - } - return item.getItemProperty(propertyId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getContainerPropertyIds() - */ - - @Override - public Collection getContainerPropertyIds() { - return Collections.unmodifiableCollection(propertyIds); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getItem(java.lang.Object) - */ - - @Override - public Item getItem(Object itemId) { - if (!cachedItems.containsKey(itemId)) { - int index = indexOfId(itemId); - if (index >= size) { - // The index is in the added items - int offset = index - size; - RowItem item = addedItems.get(offset); - if (itemPassesFilters(item)) { - return item; - } else { - return null; - } - } else { - // load the item into cache - updateOffsetAndCache(index); - } - } - return cachedItems.get(itemId); - } - - /** - * Bypasses in-memory filtering to return items that are cached in memory. - * NOTE: This does not bypass database-level filtering. - * - * @param itemId - * the id of the item to retrieve. - * @return the item represented by itemId. - */ - public Item getItemUnfiltered(Object itemId) { - if (!cachedItems.containsKey(itemId)) { - for (RowItem item : addedItems) { - if (item.getId().equals(itemId)) { - return item; - } - } - } - return cachedItems.get(itemId); - } - - /** - * NOTE! Do not use this method if in any way avoidable. This method doesn't - * (and cannot) use lazy loading, which means that all rows in the database - * will be loaded into memory. - * - * {@inheritDoc} - */ - - @Override - public Collection getItemIds() { - updateCount(); - ArrayList ids = new ArrayList(); - ResultSet rs = null; - try { - // Load ALL rows :( - queryDelegate.beginTransaction(); - rs = queryDelegate.getResults(0, 0); - List pKeys = queryDelegate.getPrimaryKeyColumns(); - while (rs.next()) { - RowId id = null; - if (pKeys.isEmpty()) { - /* Create a read only itemId */ - id = new ReadOnlyRowId(rs.getRow()); - } else { - /* Generate itemId for the row based on primary key(s) */ - Object[] itemId = new Object[pKeys.size()]; - for (int i = 0; i < pKeys.size(); i++) { - itemId[i] = rs.getObject(pKeys.get(i)); - } - id = new RowId(itemId); - } - if (id != null && !removedItems.containsKey(id)) { - ids.add(id); - } - } - rs.getStatement().close(); - rs.close(); - queryDelegate.commit(); - } catch (SQLException e) { - getLogger().log(Level.WARNING, "getItemIds() failed, rolling back.", - e); - try { - queryDelegate.rollback(); - } catch (SQLException e1) { - getLogger().log(Level.SEVERE, "Failed to roll back state", e1); - } - try { - rs.getStatement().close(); - rs.close(); - } catch (SQLException e1) { - getLogger().log(Level.WARNING, "Closing session failed", e1); - } - throw new RuntimeException("Failed to fetch item indexes.", e); - } - for (RowItem item : getFilteredAddedItems()) { - ids.add(item.getId()); - } - return Collections.unmodifiableCollection(ids); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#getType(java.lang.Object) - */ - - @Override - public Class getType(Object propertyId) { - if (!propertyIds.contains(propertyId)) { - return null; - } - return propertyTypes.get(propertyId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#size() - */ - - @Override - public int size() { - updateCount(); - return size + sizeOfAddedItems() - removedItems.size(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeItem(java.lang.Object) - */ - - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - if (!containsId(itemId)) { - return false; - } - for (RowItem item : addedItems) { - if (item.getId().equals(itemId)) { - addedItems.remove(item); - fireContentsChange(); - return true; - } - } - - if (autoCommit) { - /* Remove and commit instantly. */ - Item i = getItem(itemId); - if (i == null) { - return false; - } - try { - queryDelegate.beginTransaction(); - boolean success = queryDelegate.removeRow((RowItem) i); - queryDelegate.commit(); - refresh(); - if (notificationsEnabled) { - CacheFlushNotifier.notifyOfCacheFlush(this); - } - if (success) { - getLogger().log(Level.FINER, "Row removed from DB..."); - } - return success; - } catch (SQLException e) { - getLogger().log(Level.WARNING, - "Failed to remove row, rolling back", e); - try { - queryDelegate.rollback(); - } catch (SQLException ee) { - /* Nothing can be done here */ - getLogger().log(Level.SEVERE, - "Failed to rollback row removal", ee); - } - return false; - } catch (OptimisticLockException e) { - getLogger().log(Level.WARNING, - "Failed to remove row, rolling back", e); - try { - queryDelegate.rollback(); - } catch (SQLException ee) { - /* Nothing can be done here */ - getLogger().log(Level.SEVERE, - "Failed to rollback row removal", ee); - } - throw e; - } - } else { - removedItems.put((RowId) itemId, (RowItem) getItem(itemId)); - cachedItems.remove(itemId); - refresh(); - return true; - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeAllItems() - */ - - @Override - public boolean removeAllItems() throws UnsupportedOperationException { - if (autoCommit) { - /* Remove and commit instantly. */ - try { - queryDelegate.beginTransaction(); - boolean success = true; - for (Object id : getItemIds()) { - if (!queryDelegate.removeRow((RowItem) getItem(id))) { - success = false; - } - } - if (success) { - queryDelegate.commit(); - getLogger().log(Level.FINER, "All rows removed from DB..."); - refresh(); - if (notificationsEnabled) { - CacheFlushNotifier.notifyOfCacheFlush(this); - } - } else { - queryDelegate.rollback(); - } - return success; - } catch (SQLException e) { - getLogger().log(Level.WARNING, - "removeAllItems() failed, rolling back", e); - try { - queryDelegate.rollback(); - } catch (SQLException ee) { - /* Nothing can be done here */ - getLogger().log(Level.SEVERE, "Failed to roll back", ee); - } - return false; - } catch (OptimisticLockException e) { - getLogger().log(Level.WARNING, - "removeAllItems() failed, rolling back", e); - try { - queryDelegate.rollback(); - } catch (SQLException ee) { - /* Nothing can be done here */ - getLogger().log(Level.SEVERE, "Failed to roll back", ee); - } - throw e; - } - } else { - for (Object id : getItemIds()) { - removedItems.put((RowId) id, (RowItem) getItem(id)); - cachedItems.remove(id); - } - refresh(); - return true; - } - } - - /*************************************************/ - /** Methods from interface Container.Filterable **/ - /*************************************************/ - - /** - * {@inheritDoc} - */ - - @Override - public void addContainerFilter(Filter filter) - throws UnsupportedFilterException { - // filter.setCaseSensitive(!ignoreCase); - - filters.add(filter); - refresh(); - } - - /** - * {@inheritDoc} - */ - - @Override - public void removeContainerFilter(Filter filter) { - filters.remove(filter); - refresh(); - } - - /** - * {@inheritDoc} - */ - public void addContainerFilter(Object propertyId, String filterString, - boolean ignoreCase, boolean onlyMatchPrefix) { - if (propertyId == null || !propertyIds.contains(propertyId)) { - return; - } - - /* Generate Filter -object */ - String likeStr = onlyMatchPrefix ? filterString + "%" - : "%" + filterString + "%"; - Like like = new Like(propertyId.toString(), likeStr); - like.setCaseSensitive(!ignoreCase); - filters.add(like); - refresh(); - } - - /** - * {@inheritDoc} - */ - public void removeContainerFilters(Object propertyId) { - ArrayList toRemove = new ArrayList(); - for (Filter f : filters) { - if (f.appliesToProperty(propertyId)) { - toRemove.add(f); - } - } - filters.removeAll(toRemove); - refresh(); - } - - /** - * {@inheritDoc} - */ - @Override - public void removeAllContainerFilters() { - filters.clear(); - refresh(); - } - - /** - * Returns true if any filters have been applied to the container. - * - * @return true if the container has filters applied, false otherwise - * @since 7.1 - */ - public boolean hasContainerFilters() { - return !getContainerFilters().isEmpty(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Filterable#getContainerFilters() - */ - @Override - public Collection getContainerFilters() { - return Collections.unmodifiableCollection(filters); - } - - /**********************************************/ - /** Methods from interface Container.Indexed **/ - /**********************************************/ - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Indexed#indexOfId(java.lang.Object) - */ - - @Override - public int indexOfId(Object itemId) { - // First check if the id is in the added items - for (int ix = 0; ix < addedItems.size(); ix++) { - RowItem item = addedItems.get(ix); - if (item.getId().equals(itemId)) { - if (itemPassesFilters(item)) { - updateCount(); - return size + ix; - } else { - return -1; - } - } - } - - if (!containsId(itemId)) { - return -1; - } - if (cachedItems.isEmpty()) { - getPage(); - } - // this protects against infinite looping - int counter = 0; - int oldIndex; - while (counter < size) { - if (itemIndexes.containsValue(itemId)) { - for (Integer idx : itemIndexes.keySet()) { - if (itemIndexes.get(idx).equals(itemId)) { - return idx; - } - } - } - oldIndex = currentOffset; - // load in the next page. - int nextIndex = currentOffset + pageLength * CACHE_RATIO - + cacheOverlap; - if (nextIndex >= size) { - // Container wrapped around, start from index 0. - nextIndex = 0; - } - updateOffsetAndCache(nextIndex); - - // Update counter - if (currentOffset > oldIndex) { - counter += currentOffset - oldIndex; - } else { - counter += size - oldIndex; - } - } - // safeguard in case item not found - return -1; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Indexed#getIdByIndex(int) - */ - - @Override - public Object getIdByIndex(int index) { - if (index < 0) { - throw new IndexOutOfBoundsException( - "Index is negative! index=" + index); - } - // make sure the size field is valid - updateCount(); - if (index < size) { - if (itemIndexes.keySet().contains(index)) { - return itemIndexes.get(index); - } - updateOffsetAndCache(index); - return itemIndexes.get(index); - } else { - // The index is in the added items - int offset = index - size; - // TODO this is very inefficient if looping - should improve - // getItemIds(int, int) - return getFilteredAddedItems().get(offset).getId(); - } - } - - @Override - public List getItemIds(int startIndex, int numberOfIds) { - // TODO create a better implementation - return (List) ContainerHelpers - .getItemIdsUsingGetIdByIndex(startIndex, numberOfIds, this); - } - - /**********************************************/ - /** Methods from interface Container.Ordered **/ - /**********************************************/ - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object) - */ - - @Override - public Object nextItemId(Object itemId) { - int index = indexOfId(itemId) + 1; - try { - return getIdByIndex(index); - } catch (IndexOutOfBoundsException e) { - return null; - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object) - */ - - @Override - public Object prevItemId(Object itemId) { - int prevIndex = indexOfId(itemId) - 1; - try { - return getIdByIndex(prevIndex); - } catch (IndexOutOfBoundsException e) { - return null; - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#firstItemId() - */ - - @Override - public Object firstItemId() { - updateCount(); - if (size == 0) { - if (addedItems.isEmpty()) { - return null; - } else { - int ix = -1; - do { - ix++; - } while (!itemPassesFilters(addedItems.get(ix)) - && ix < addedItems.size()); - if (ix < addedItems.size()) { - return addedItems.get(ix).getId(); - } - } - } - if (!itemIndexes.containsKey(0)) { - updateOffsetAndCache(0); - } - return itemIndexes.get(0); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#lastItemId() - */ - - @Override - public Object lastItemId() { - if (addedItems.isEmpty()) { - int lastIx = size() - 1; - if (!itemIndexes.containsKey(lastIx)) { - updateOffsetAndCache(size - 1); - } - return itemIndexes.get(lastIx); - } else { - int ix = addedItems.size(); - do { - ix--; - } while (!itemPassesFilters(addedItems.get(ix)) && ix >= 0); - if (ix >= 0) { - return addedItems.get(ix).getId(); - } else { - return null; - } - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object) - */ - - @Override - public boolean isFirstId(Object itemId) { - return firstItemId().equals(itemId); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object) - */ - - @Override - public boolean isLastId(Object itemId) { - return lastItemId().equals(itemId); - } - - /***********************************************/ - /** Methods from interface Container.Sortable **/ - /***********************************************/ - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], - * boolean[]) - */ - - @Override - public void sort(Object[] propertyId, boolean[] ascending) { - sorters.clear(); - if (propertyId == null || propertyId.length == 0) { - refresh(); - return; - } - /* Generate OrderBy -objects */ - boolean asc = true; - for (int i = 0; i < propertyId.length; i++) { - /* Check that the property id is valid */ - if (propertyId[i] instanceof String - && propertyIds.contains(propertyId[i])) { - try { - asc = ascending[i]; - } catch (Exception e) { - getLogger().log(Level.WARNING, "", e); - } - sorters.add(new OrderBy((String) propertyId[i], asc)); - } - } - refresh(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds() - */ - - @Override - public Collection getSortableContainerPropertyIds() { - return getContainerPropertyIds(); - } - - /**************************************/ - /** Methods specific to SQLContainer **/ - /**************************************/ - - /** - * Refreshes the container - clears all caches and resets size and offset. - * Does NOT remove sorting or filtering rules! - */ - public void refresh() { - refresh(true); - } - - /** - * Refreshes the container. If setSizeDirty is - * false, assumes that the current size is up to date. This is - * used in {@link #updateCount()} to refresh the contents when we know the - * size was just updated. - * - * @param setSizeDirty - */ - private void refresh(boolean setSizeDirty) { - if (setSizeDirty) { - sizeDirty = true; - } - currentOffset = 0; - cachedItems.clear(); - itemIndexes.clear(); - fireContentsChange(); - } - - /** - * Returns modify state of the container. - * - * @return true if contents of this container have been modified - */ - public boolean isModified() { - return !removedItems.isEmpty() || !addedItems.isEmpty() - || !modifiedItems.isEmpty(); - } - - /** - * Set auto commit mode enabled or disabled. Auto commit mode means that all - * changes made to items of this container will be immediately written to - * the underlying data source. - * - * @param autoCommitEnabled - * true to enable auto commit mode - */ - public void setAutoCommit(boolean autoCommitEnabled) { - autoCommit = autoCommitEnabled; - } - - /** - * Returns status of the auto commit mode. - * - * @return true if auto commit mode is enabled - */ - public boolean isAutoCommit() { - return autoCommit; - } - - /** - * Returns the currently set page length. - * - * @return current page length - */ - public int getPageLength() { - return pageLength; - } - - /** - * Sets the page length used in lazy fetching of items from the data source. - * Also resets the cache size to match the new page length. - * - * As a side effect the container will be refreshed. - * - * @param pageLength - * new page length - */ - public void setPageLength(int pageLength) { - setPageLengthInternal(pageLength); - refresh(); - } - - /** - * Sets the page length internally, without refreshing the container. - * - * @param pageLength - * the new page length - */ - private void setPageLengthInternal(int pageLength) { - this.pageLength = pageLength > 0 ? pageLength : DEFAULT_PAGE_LENGTH; - cacheOverlap = getPageLength(); - cachedItems.setCacheLimit(CACHE_RATIO * getPageLength() + cacheOverlap); - } - - /** - * Adds the given OrderBy to this container and refreshes the container - * contents with the new sorting rules. - * - * Note that orderBy.getColumn() must return a column name that exists in - * this container. - * - * @param orderBy - * OrderBy to be added to the container sorting rules - */ - public void addOrderBy(OrderBy orderBy) { - if (orderBy == null) { - return; - } - if (!propertyIds.contains(orderBy.getColumn())) { - throw new IllegalArgumentException( - "The column given for sorting does not exist in this container."); - } - sorters.add(orderBy); - refresh(); - } - - /** - * Commits all the changes, additions and removals made to the items of this - * container. - * - * @throws UnsupportedOperationException - * @throws SQLException - */ - public void commit() throws UnsupportedOperationException, SQLException { - try { - getLogger().log(Level.FINER, - "Commiting changes through delegate..."); - queryDelegate.beginTransaction(); - /* Perform buffered deletions */ - for (RowItem item : removedItems.values()) { - try { - if (!queryDelegate.removeRow(item)) { - throw new SQLException( - "Removal failed for row with ID: " - + item.getId()); - } - } catch (IllegalArgumentException e) { - throw new SQLException( - "Removal failed for row with ID: " + item.getId(), - e); - } - } - /* Perform buffered modifications */ - for (RowItem item : modifiedItems) { - if (!removedItems.containsKey(item.getId())) { - if (queryDelegate.storeRow(item) > 0) { - /* - * Also reset the modified state in the item in case it - * is reused e.g. in a form. - */ - item.commit(); - } else { - queryDelegate.rollback(); - refresh(); - throw new ConcurrentModificationException( - "Item with the ID '" + item.getId() - + "' has been externally modified."); - } - } - } - /* Perform buffered additions */ - for (RowItem item : addedItems) { - queryDelegate.storeRow(item); - } - queryDelegate.commit(); - removedItems.clear(); - addedItems.clear(); - modifiedItems.clear(); - refresh(); - if (notificationsEnabled) { - CacheFlushNotifier.notifyOfCacheFlush(this); - } - } catch (SQLException e) { - queryDelegate.rollback(); - throw e; - } catch (OptimisticLockException e) { - queryDelegate.rollback(); - throw e; - } - } - - /** - * Rolls back all the changes, additions and removals made to the items of - * this container. - * - * @throws UnsupportedOperationException - * @throws SQLException - */ - public void rollback() throws UnsupportedOperationException, SQLException { - getLogger().log(Level.FINE, "Rolling back changes..."); - removedItems.clear(); - addedItems.clear(); - modifiedItems.clear(); - refresh(); - } - - /** - * Notifies this container that a property in the given item has been - * modified. The change will be buffered or made instantaneously depending - * on auto commit mode. - * - * @param changedItem - * item that has a modified property - */ - void itemChangeNotification(RowItem changedItem) { - if (autoCommit) { - try { - queryDelegate.beginTransaction(); - if (queryDelegate.storeRow(changedItem) == 0) { - queryDelegate.rollback(); - refresh(); - throw new ConcurrentModificationException( - "Item with the ID '" + changedItem.getId() - + "' has been externally modified."); - } - queryDelegate.commit(); - if (notificationsEnabled) { - CacheFlushNotifier.notifyOfCacheFlush(this); - } - getLogger().log(Level.FINER, "Row updated to DB..."); - } catch (SQLException e) { - getLogger().log(Level.WARNING, - "itemChangeNotification failed, rolling back...", e); - try { - queryDelegate.rollback(); - } catch (SQLException ee) { - /* Nothing can be done here */ - getLogger().log(Level.SEVERE, "Rollback failed", e); - } - throw new RuntimeException(e); - } - } else { - if (!(changedItem.getId() instanceof TemporaryRowId) - && !modifiedItems.contains(changedItem)) { - modifiedItems.add(changedItem); - } - } - } - - /** - * Determines a new offset for updating the row cache. The offset is - * calculated from the given index, and will be fixed to match the start of - * a page, based on the value of pageLength. - * - * @param index - * Index of the item that was requested, but not found in cache - */ - private void updateOffsetAndCache(int index) { - - int oldOffset = currentOffset; - - currentOffset = (index / pageLength) * pageLength - cacheOverlap; - - if (currentOffset < 0) { - currentOffset = 0; - } - - if (oldOffset == currentOffset && !cachedItems.isEmpty()) { - return; - } - - getPage(); - } - - /** - * Fetches new count of rows from the data source, if needed. - */ - private void updateCount() { - if (!sizeDirty && new Date().getTime() < sizeUpdated.getTime() - + sizeValidMilliSeconds) { - return; - } - try { - try { - queryDelegate.setFilters(filters); - } catch (UnsupportedOperationException e) { - getLogger().log(Level.FINE, - "The query delegate doesn't support filtering", e); - } - try { - queryDelegate.setOrderBy(sorters); - } catch (UnsupportedOperationException e) { - getLogger().log(Level.FINE, - "The query delegate doesn't support sorting", e); - } - int newSize = queryDelegate.getCount(); - sizeUpdated = new Date(); - sizeDirty = false; - if (newSize != size) { - size = newSize; - // Size is up to date so don't set it back to dirty in refresh() - refresh(false); - } - getLogger().log(Level.FINER, "Updated row count. New count is: {0}", - size); - } catch (SQLException e) { - throw new RuntimeException("Failed to update item set size.", e); - } - } - - /** - * Fetches property id's (column names and their types) from the data - * source. - * - * @throws SQLException - */ - private void getPropertyIds() throws SQLException { - propertyIds.clear(); - propertyTypes.clear(); - queryDelegate.setFilters(null); - queryDelegate.setOrderBy(null); - ResultSet rs = null; - ResultSetMetaData rsmd = null; - try { - queryDelegate.beginTransaction(); - rs = queryDelegate.getResults(0, 1); - rsmd = rs.getMetaData(); - boolean resultExists = rs.next(); - Class type = null; - for (int i = 1; i <= rsmd.getColumnCount(); i++) { - if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) { - continue; - } - String colName = rsmd.getColumnLabel(i); - /* - * Make sure not to add the same colName twice. This can easily - * happen if the SQL query joins many tables with an ID column. - */ - if (!propertyIds.contains(colName)) { - propertyIds.add(colName); - } - /* Try to determine the column's JDBC class by all means. */ - if (resultExists && rs.getObject(i) != null) { - type = rs.getObject(i).getClass(); - } else { - try { - type = Class.forName(rsmd.getColumnClassName(i)); - } catch (Exception e) { - getLogger().log(Level.WARNING, "Class not found", e); - /* On failure revert to Object and hope for the best. */ - type = Object.class; - } - } - /* - * Determine read only and nullability status of the column. A - * column is read only if it is reported as either read only or - * auto increment by the database, and also it is set as the - * version column in a TableQuery delegate. - */ - boolean readOnly = rsmd.isAutoIncrement(i) - || rsmd.isReadOnly(i); - - boolean persistable = !rsmd.isReadOnly(i); - - if (queryDelegate instanceof TableQuery) { - if (rsmd.getColumnLabel(i).equals( - ((TableQuery) queryDelegate).getVersionColumn())) { - readOnly = true; - } - } - - propertyReadOnly.put(colName, readOnly); - propertyPersistable.put(colName, persistable); - propertyNullable.put(colName, - rsmd.isNullable(i) == ResultSetMetaData.columnNullable); - propertyPrimaryKey.put(colName, - queryDelegate.getPrimaryKeyColumns() - .contains(rsmd.getColumnLabel(i))); - propertyTypes.put(colName, type); - } - rs.getStatement().close(); - rs.close(); - queryDelegate.commit(); - getLogger().log(Level.FINER, "Property IDs fetched."); - } catch (SQLException e) { - getLogger().log(Level.WARNING, - "Failed to fetch property ids, rolling back", e); - try { - queryDelegate.rollback(); - } catch (SQLException e1) { - getLogger().log(Level.SEVERE, "Failed to roll back", e1); - } - try { - if (rs != null) { - if (rs.getStatement() != null) { - rs.getStatement().close(); - } - rs.close(); - } - } catch (SQLException e1) { - getLogger().log(Level.WARNING, "Failed to close session", e1); - } - throw e; - } - } - - /** - * Fetches a page from the data source based on the values of pageLength and - * currentOffset. Also updates the set of primary keys, used in - * identification of RowItems. - */ - private void getPage() { - updateCount(); - ResultSet rs = null; - ResultSetMetaData rsmd = null; - cachedItems.clear(); - itemIndexes.clear(); - try { - try { - queryDelegate.setOrderBy(sorters); - } catch (UnsupportedOperationException e) { - /* The query delegate doesn't support sorting. */ - /* No need to do anything. */ - getLogger().log(Level.FINE, - "The query delegate doesn't support sorting", e); - } - queryDelegate.beginTransaction(); - int fetchedRows = pageLength * CACHE_RATIO + cacheOverlap; - rs = queryDelegate.getResults(currentOffset, fetchedRows); - rsmd = rs.getMetaData(); - List pKeys = queryDelegate.getPrimaryKeyColumns(); - // } - /* Create new items and column properties */ - ColumnProperty cp = null; - int rowCount = currentOffset; - if (!queryDelegate.implementationRespectsPagingLimits()) { - rowCount = currentOffset = 0; - setPageLengthInternal(size); - } - while (rs.next()) { - List itemProperties = new ArrayList(); - /* Generate row itemId based on primary key(s) */ - Object[] itemId = new Object[pKeys.size()]; - for (int i = 0; i < pKeys.size(); i++) { - itemId[i] = rs.getObject(pKeys.get(i)); - } - RowId id = null; - if (pKeys.isEmpty()) { - id = new ReadOnlyRowId(rs.getRow()); - } else { - id = new RowId(itemId); - } - List propertiesToAdd = new ArrayList( - propertyIds); - if (!removedItems.containsKey(id)) { - for (int i = 1; i <= rsmd.getColumnCount(); i++) { - if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) { - continue; - } - String colName = rsmd.getColumnLabel(i); - Object value = rs.getObject(i); - Class type = value != null ? value.getClass() - : Object.class; - if (value == null) { - for (String propName : propertyTypes.keySet()) { - if (propName.equals(rsmd.getColumnLabel(i))) { - type = propertyTypes.get(propName); - break; - } - } - } - /* - * In case there are more than one column with the same - * name, add only the first one. This can easily happen - * if you join many tables where each table has an ID - * column. - */ - if (propertiesToAdd.contains(colName)) { - - cp = new ColumnProperty(colName, - propertyReadOnly.get(colName), - propertyPersistable.get(colName), - propertyNullable.get(colName), - propertyPrimaryKey.get(colName), value, - type); - itemProperties.add(cp); - propertiesToAdd.remove(colName); - } - } - /* Cache item */ - itemIndexes.put(rowCount, id); - - // if an item with the id is contained in the modified - // cache, then use this record and add it to the cached - // items. Otherwise create a new item - int modifiedIndex = indexInModifiedCache(id); - if (modifiedIndex != -1) { - cachedItems.put(id, modifiedItems.get(modifiedIndex)); - } else { - cachedItems.put(id, - new RowItem(this, id, itemProperties)); - } - - rowCount++; - } - } - rs.getStatement().close(); - rs.close(); - queryDelegate.commit(); - getLogger().log(Level.FINER, "Fetched {0} rows starting from {1}", - new Object[] { fetchedRows, currentOffset }); - } catch (SQLException e) { - getLogger().log(Level.WARNING, "Failed to fetch rows, rolling back", - e); - try { - queryDelegate.rollback(); - } catch (SQLException e1) { - getLogger().log(Level.SEVERE, "Failed to roll back", e1); - } - try { - if (rs != null) { - if (rs.getStatement() != null) { - rs.getStatement().close(); - rs.close(); - } - } - } catch (SQLException e1) { - getLogger().log(Level.WARNING, "Failed to close session", e1); - } - throw new RuntimeException("Failed to fetch page.", e); - } - } - - /** - * Returns the index of the item with the given itemId for the modified - * cache. - * - * @param itemId - * @return the index of the item with the itemId in the modified cache. Or - * -1 if not found. - */ - private int indexInModifiedCache(Object itemId) { - for (int ix = 0; ix < modifiedItems.size(); ix++) { - RowItem item = modifiedItems.get(ix); - if (item.getId().equals(itemId)) { - return ix; - } - } - return -1; - } - - private int sizeOfAddedItems() { - return getFilteredAddedItems().size(); - } - - private List getFilteredAddedItems() { - ArrayList filtered = new ArrayList(addedItems); - if (filters != null && !filters.isEmpty()) { - for (RowItem item : addedItems) { - if (!itemPassesFilters(item)) { - filtered.remove(item); - } - } - } - return filtered; - } - - private boolean itemPassesFilters(RowItem item) { - for (Filter filter : filters) { - if (!filter.passesFilter(item.getId(), item)) { - return false; - } - } - return true; - } - - /** - * Checks is the given column identifier valid to be used with SQLContainer. - * Currently the only non-valid identifier is "rownum" when MSSQL or Oracle - * is used. This is due to the way the SELECT queries are constructed in - * order to implement paging in these databases. - * - * @param identifier - * Column identifier - * @return true if the identifier is valid - */ - private boolean isColumnIdentifierValid(String identifier) { - if (identifier.equalsIgnoreCase("rownum") - && queryDelegate instanceof TableQuery) { - TableQuery tq = (TableQuery) queryDelegate; - if (tq.getSqlGenerator() instanceof MSSQLGenerator - || tq.getSqlGenerator() instanceof OracleGenerator) { - return false; - } - } - return true; - } - - /** - * Returns the QueryDelegate set for this SQLContainer. - * - * @return current querydelegate - */ - protected QueryDelegate getQueryDelegate() { - return queryDelegate; - } - - /************************************/ - /** UNSUPPORTED CONTAINER FEATURES **/ - /************************************/ - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object, - * java.lang.Class, java.lang.Object) - */ - - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object) - */ - - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#addItem(java.lang.Object) - */ - - @Override - public Item addItem(Object itemId) throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object, - * java.lang.Object) - */ - - @Override - public Item addItemAfter(Object previousItemId, Object newItemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object) - */ - - @Override - public Item addItemAt(int index, Object newItemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Indexed#addItemAt(int) - */ - - @Override - public Object addItemAt(int index) throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object) - */ - - @Override - public Object addItemAfter(Object previousItemId) - throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } - - /******************************************/ - /** ITEMSETCHANGENOTIFIER IMPLEMENTATION **/ - /******************************************/ - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin - * .data.Container.ItemSetChangeListener) - */ - - @Override - public void addItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (itemSetChangeListeners == null) { - itemSetChangeListeners = new LinkedList(); - } - itemSetChangeListeners.add(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Override - @Deprecated - public void addListener(Container.ItemSetChangeListener listener) { - addItemSetChangeListener(listener); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin - * .data.Container.ItemSetChangeListener) - */ - - @Override - public void removeItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (itemSetChangeListeners != null) { - itemSetChangeListeners.remove(listener); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(Container.ItemSetChangeListener listener) { - removeItemSetChangeListener(listener); - } - - protected void fireContentsChange() { - if (itemSetChangeListeners != null) { - final Object[] l = itemSetChangeListeners.toArray(); - final Container.ItemSetChangeEvent event = new SQLContainer.ItemSetChangeEvent( - this); - for (int i = 0; i < l.length; i++) { - ((Container.ItemSetChangeListener) l[i]) - .containerItemSetChange(event); - } - } - } - - /** - * Simple ItemSetChangeEvent implementation. - */ - @SuppressWarnings("serial") - public static class ItemSetChangeEvent extends EventObject - implements Container.ItemSetChangeEvent { - - private ItemSetChangeEvent(SQLContainer source) { - super(source); - } - - @Override - public Container getContainer() { - return (Container) getSource(); - } - } - - /**************************************************/ - /** ROWIDCHANGELISTENER PASSING TO QUERYDELEGATE **/ - /**************************************************/ - - /** - * Adds a RowIdChangeListener to the QueryDelegate - * - * @param listener - */ - public void addRowIdChangeListener(RowIdChangeListener listener) { - if (queryDelegate instanceof QueryDelegate.RowIdChangeNotifier) { - ((QueryDelegate.RowIdChangeNotifier) queryDelegate) - .addListener(listener); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addRowIdChangeListener(RowIdChangeListener)} - **/ - @Deprecated - public void addListener(RowIdChangeListener listener) { - addRowIdChangeListener(listener); - } - - /** - * Removes a RowIdChangeListener from the QueryDelegate - * - * @param listener - */ - public void removeRowIdChangeListener(RowIdChangeListener listener) { - if (queryDelegate instanceof QueryDelegate.RowIdChangeNotifier) { - ((QueryDelegate.RowIdChangeNotifier) queryDelegate) - .removeListener(listener); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeRowIdChangeListener(RowIdChangeListener)} - **/ - @Deprecated - public void removeListener(RowIdChangeListener listener) { - removeRowIdChangeListener(listener); - } - - /** - * Calling this will enable this SQLContainer to send and receive cache - * flush notifications for its lifetime. - */ - public void enableCacheFlushNotifications() { - if (!notificationsEnabled) { - notificationsEnabled = true; - CacheFlushNotifier.addInstance(this); - } - } - - /******************************************/ - /** Referencing mechanism implementation **/ - /******************************************/ - - /** - * Adds a new reference to the given SQLContainer. In addition to the - * container you must provide the column (property) names used for the - * reference in both this and the referenced SQLContainer. - * - * Note that multiple references pointing to the same SQLContainer are not - * supported. - * - * @param refdCont - * Target SQLContainer of the new reference - * @param refingCol - * Column (property) name in this container storing the (foreign - * key) reference - * @param refdCol - * Column (property) name in the referenced container storing the - * referenced key - */ - public void addReference(SQLContainer refdCont, String refingCol, - String refdCol) { - if (refdCont == null) { - throw new IllegalArgumentException( - "Referenced SQLContainer can not be null."); - } - if (!getContainerPropertyIds().contains(refingCol)) { - throw new IllegalArgumentException( - "Given referencing column name is invalid." - + " Please ensure that this container" - + " contains a property ID named: " + refingCol); - } - if (!refdCont.getContainerPropertyIds().contains(refdCol)) { - throw new IllegalArgumentException( - "Given referenced column name is invalid." - + " Please ensure that the referenced container" - + " contains a property ID named: " + refdCol); - } - if (references.keySet().contains(refdCont)) { - throw new IllegalArgumentException( - "An SQLContainer instance can only be referenced once."); - } - references.put(refdCont, new Reference(refdCont, refingCol, refdCol)); - } - - /** - * Removes the reference pointing to the given SQLContainer. - * - * @param refdCont - * Target SQLContainer of the reference - * @return true if successful, false if the reference did not exist - */ - public boolean removeReference(SQLContainer refdCont) { - if (refdCont == null) { - throw new IllegalArgumentException( - "Referenced SQLContainer can not be null."); - } - return references.remove(refdCont) == null ? false : true; - } - - /** - * Sets the referenced item. The referencing column of the item in this - * container is updated accordingly. - * - * @param itemId - * Item Id of the reference source (from this container) - * @param refdItemId - * Item Id of the reference target (from referenced container) - * @param refdCont - * Target SQLContainer of the reference - * @return true if the referenced item was successfully set, false on - * failure - */ - public boolean setReferencedItem(Object itemId, Object refdItemId, - SQLContainer refdCont) { - if (refdCont == null) { - throw new IllegalArgumentException( - "Referenced SQLContainer can not be null."); - } - Reference r = references.get(refdCont); - if (r == null) { - throw new IllegalArgumentException( - "Reference to the given SQLContainer not defined."); - } - try { - getContainerProperty(itemId, r.getReferencingColumn()) - .setValue(refdCont.getContainerProperty(refdItemId, - r.getReferencedColumn())); - return true; - } catch (Exception e) { - getLogger().log(Level.WARNING, "Setting referenced item failed.", - e); - return false; - } - } - - /** - * Fetches the Item Id of the referenced item from the target SQLContainer. - * - * @param itemId - * Item Id of the reference source (from this container) - * @param refdCont - * Target SQLContainer of the reference - * @return Item Id of the referenced item, or null if not found - */ - public Object getReferencedItemId(Object itemId, SQLContainer refdCont) { - if (refdCont == null) { - throw new IllegalArgumentException( - "Referenced SQLContainer can not be null."); - } - Reference r = references.get(refdCont); - if (r == null) { - throw new IllegalArgumentException( - "Reference to the given SQLContainer not defined."); - } - Object refKey = getContainerProperty(itemId, r.getReferencingColumn()) - .getValue(); - - refdCont.removeAllContainerFilters(); - refdCont.addContainerFilter(new Equal(r.getReferencedColumn(), refKey)); - Object toReturn = refdCont.firstItemId(); - refdCont.removeAllContainerFilters(); - return toReturn; - } - - /** - * Fetches the referenced item from the target SQLContainer. - * - * @param itemId - * Item Id of the reference source (from this container) - * @param refdCont - * Target SQLContainer of the reference - * @return The referenced item, or null if not found - */ - public Item getReferencedItem(Object itemId, SQLContainer refdCont) { - return refdCont.getItem(getReferencedItemId(itemId, refdCont)); - } - - private void writeObject(java.io.ObjectOutputStream out) - throws IOException { - out.defaultWriteObject(); - } - - private void readObject(java.io.ObjectInputStream in) - throws IOException, ClassNotFoundException { - in.defaultReadObject(); - if (notificationsEnabled) { - /* - * Register instance with CacheFlushNotifier after de-serialization - * if notifications are enabled - */ - CacheFlushNotifier.addInstance(this); - } - } - - private static final Logger getLogger() { - return Logger.getLogger(SQLContainer.class.getName()); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLUtil.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLUtil.java deleted file mode 100644 index 2abe45d5fc..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/SQLUtil.java +++ /dev/null @@ -1,51 +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.sqlcontainer; - -import java.io.Serializable; - -public class SQLUtil implements Serializable { - /** - * Escapes different special characters in strings that are passed to SQL. - * Replaces the following: - * - * - *
  • ' is replaced with ''
  • - *
  • \x00 is removed
  • - *
  • \ is replaced with \\
  • - *
  • " is replaced with \"
  • - *
  • \x1a is removed
  • - * - * Also note! The escaping done here may or may not be enough to prevent any - * and all SQL injections so it is recommended to check user input before - * giving it to the SQLContainer/TableQuery. - * - * @param constant - * @return \\\'\' - */ - public static String escapeSQL(String constant) { - if (constant == null) { - return null; - } - String fixedConstant = constant; - fixedConstant = fixedConstant.replaceAll("\\\\x00", ""); - fixedConstant = fixedConstant.replaceAll("\\\\x1a", ""); - fixedConstant = fixedConstant.replaceAll("'", "''"); - fixedConstant = fixedConstant.replaceAll("\\\\", "\\\\\\\\"); - fixedConstant = fixedConstant.replaceAll("\\\"", "\\\\\""); - return fixedConstant; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/TemporaryRowId.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/TemporaryRowId.java deleted file mode 100644 index 5cd3e6fac8..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/TemporaryRowId.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.data.util.sqlcontainer; - -public class TemporaryRowId extends RowId { - private static final long serialVersionUID = -641983830469018329L; - - public TemporaryRowId(Object... id) { - super(id); - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null || !(TemporaryRowId.class.equals(obj.getClass()))) { - return false; - } - Object[] compId = ((TemporaryRowId) obj).getId(); - return id.equals(compId); - } - - @Override - public String toString() { - return "Temporary row id"; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.java deleted file mode 100644 index cf50a641de..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPool.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.data.util.sqlcontainer.connection; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.sql.DataSource; - -public class J2EEConnectionPool implements JDBCConnectionPool { - - private String dataSourceJndiName; - - private DataSource dataSource = null; - - public J2EEConnectionPool(DataSource dataSource) { - this.dataSource = dataSource; - } - - public J2EEConnectionPool(String dataSourceJndiName) { - this.dataSourceJndiName = dataSourceJndiName; - } - - @Override - public Connection reserveConnection() throws SQLException { - Connection conn = getDataSource().getConnection(); - conn.setAutoCommit(false); - - return conn; - } - - private DataSource getDataSource() throws SQLException { - if (dataSource == null) { - dataSource = lookupDataSource(); - } - return dataSource; - } - - private DataSource lookupDataSource() throws SQLException { - try { - InitialContext ic = new InitialContext(); - return (DataSource) ic.lookup(dataSourceJndiName); - } catch (NamingException e) { - throw new SQLException( - "NamingException - Cannot connect to the database. Cause: " - + e.getMessage()); - } - } - - @Override - public void releaseConnection(Connection conn) { - if (conn != null) { - try { - conn.close(); - } catch (SQLException e) { - Logger.getLogger(J2EEConnectionPool.class.getName()) - .log(Level.FINE, "Could not release SQL connection", e); - } - } - } - - @Override - public void destroy() { - dataSource = null; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/JDBCConnectionPool.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/JDBCConnectionPool.java deleted file mode 100644 index 842a264caa..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/JDBCConnectionPool.java +++ /dev/null @@ -1,53 +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.sqlcontainer.connection; - -import java.io.Serializable; -import java.sql.Connection; -import java.sql.SQLException; - -/** - * Interface for implementing connection pools to be used with SQLContainer. - */ -public interface JDBCConnectionPool extends Serializable { - /** - * Retrieves a connection. - * - * @return a usable connection to the database - * @throws SQLException - */ - public Connection reserveConnection() throws SQLException; - - /** - * Releases a connection that was retrieved earlier. - * - * Note that depending on implementation, the transaction possibly open in - * the connection may or may not be rolled back. - * - * @param conn - * Connection to be released - */ - public void releaseConnection(Connection conn); - - /** - * Destroys the connection pool: close() is called an all the connections in - * the pool, whether available or reserved. - * - * This method was added to fix PostgreSQL -related issues with connections - * that were left hanging 'idle'. - */ - public void destroy(); -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java deleted file mode 100644 index 9cf02a9e68..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java +++ /dev/null @@ -1,181 +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.sqlcontainer.connection; - -import java.io.IOException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashSet; -import java.util.Set; - -/** - * Simple implementation of the JDBCConnectionPool interface. Handles loading - * the JDBC driver, setting up the connections and ensuring they are still - * usable upon release. - */ -@SuppressWarnings("serial") -public class SimpleJDBCConnectionPool implements JDBCConnectionPool { - - private int initialConnections = 5; - private int maxConnections = 20; - - private String driverName; - private String connectionUri; - private String userName; - private String password; - - private transient Set availableConnections; - private transient Set reservedConnections; - - private boolean initialized; - - public SimpleJDBCConnectionPool(String driverName, String connectionUri, - String userName, String password) throws SQLException { - if (driverName == null) { - throw new IllegalArgumentException( - "JDBC driver class name must be given."); - } - if (connectionUri == null) { - throw new IllegalArgumentException( - "Database connection URI must be given."); - } - if (userName == null) { - throw new IllegalArgumentException( - "Database username must be given."); - } - if (password == null) { - throw new IllegalArgumentException( - "Database password must be given."); - } - this.driverName = driverName; - this.connectionUri = connectionUri; - this.userName = userName; - this.password = password; - - /* Initialize JDBC driver */ - try { - Class.forName(driverName).newInstance(); - } catch (Exception ex) { - throw new RuntimeException("Specified JDBC Driver: " + driverName - + " - initialization failed.", ex); - } - } - - public SimpleJDBCConnectionPool(String driverName, String connectionUri, - String userName, String password, int initialConnections, - int maxConnections) throws SQLException { - this(driverName, connectionUri, userName, password); - this.initialConnections = initialConnections; - this.maxConnections = maxConnections; - } - - private void initializeConnections() throws SQLException { - availableConnections = new HashSet(initialConnections); - reservedConnections = new HashSet(initialConnections); - for (int i = 0; i < initialConnections; i++) { - availableConnections.add(createConnection()); - } - initialized = true; - } - - @Override - public synchronized Connection reserveConnection() throws SQLException { - if (!initialized) { - initializeConnections(); - } - if (availableConnections.isEmpty()) { - if (reservedConnections.size() < maxConnections) { - availableConnections.add(createConnection()); - } else { - throw new SQLException("Connection limit has been reached."); - } - } - - Connection c = availableConnections.iterator().next(); - availableConnections.remove(c); - reservedConnections.add(c); - - return c; - } - - @Override - public synchronized void releaseConnection(Connection conn) { - if (conn == null || !initialized) { - return; - } - /* Try to roll back if necessary */ - try { - if (!conn.getAutoCommit()) { - conn.rollback(); - } - } catch (SQLException e) { - /* Roll back failed, close and discard connection */ - try { - conn.close(); - } catch (SQLException e1) { - /* Nothing needs to be done */ - } - reservedConnections.remove(conn); - return; - } - reservedConnections.remove(conn); - availableConnections.add(conn); - } - - private Connection createConnection() throws SQLException { - Connection c = DriverManager.getConnection(connectionUri, userName, - password); - c.setAutoCommit(false); - if (driverName.toLowerCase().contains("mysql")) { - try { - Statement s = c.createStatement(); - s.execute("SET SESSION sql_mode = 'ANSI'"); - s.close(); - } catch (Exception e) { - // Failed to set ansi mode; continue - } - } - return c; - } - - @Override - public void destroy() { - for (Connection c : availableConnections) { - try { - c.close(); - } catch (SQLException e) { - // No need to do anything - } - } - for (Connection c : reservedConnections) { - try { - c.close(); - } catch (SQLException e) { - // No need to do anything - } - } - - } - - private void writeObject(java.io.ObjectOutputStream out) - throws IOException { - initialized = false; - out.defaultWriteObject(); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/AbstractTransactionalQuery.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/AbstractTransactionalQuery.java deleted file mode 100644 index 586fe28171..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/AbstractTransactionalQuery.java +++ /dev/null @@ -1,185 +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.sqlcontainer.query; - -import java.io.Serializable; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; - -/** - * Common base class for database query classes that handle connections and - * transactions. - * - * @author Vaadin Ltd - * @since 6.8.9 - */ -public abstract class AbstractTransactionalQuery implements Serializable { - - private JDBCConnectionPool connectionPool; - private transient Connection activeConnection; - - AbstractTransactionalQuery() { - } - - AbstractTransactionalQuery(JDBCConnectionPool connectionPool) { - this.connectionPool = connectionPool; - } - - /** - * Reserves a connection with auto-commit off if no transaction is in - * progress. - * - * @throws IllegalStateException - * if a transaction is already open - * @throws SQLException - * if a connection could not be obtained or configured - */ - public void beginTransaction() - throws UnsupportedOperationException, SQLException { - if (isInTransaction()) { - throw new IllegalStateException("A transaction is already active!"); - } - activeConnection = connectionPool.reserveConnection(); - activeConnection.setAutoCommit(false); - } - - /** - * Commits (if not in auto-commit mode) and releases the active connection. - * - * @throws SQLException - * if not in a transaction managed by this query - */ - public void commit() throws UnsupportedOperationException, SQLException { - if (!isInTransaction()) { - throw new SQLException("No active transaction"); - } - if (!activeConnection.getAutoCommit()) { - activeConnection.commit(); - } - connectionPool.releaseConnection(activeConnection); - activeConnection = null; - } - - /** - * Rolls back and releases the active connection. - * - * @throws SQLException - * if not in a transaction managed by this query - */ - public void rollback() throws UnsupportedOperationException, SQLException { - if (!isInTransaction()) { - throw new SQLException("No active transaction"); - } - activeConnection.rollback(); - connectionPool.releaseConnection(activeConnection); - activeConnection = null; - } - - /** - * Check that a transaction is active. - * - * @throws SQLException - * if no active transaction - */ - protected void ensureTransaction() throws SQLException { - if (!isInTransaction()) { - throw new SQLException("No active transaction!"); - } - } - - /** - * Closes a statement and a resultset, then releases the connection if it is - * not part of an active transaction. A failure in closing one of the - * parameters does not prevent closing the rest. - * - * If the statement is a {@link PreparedStatement}, its parameters are - * cleared prior to closing the statement. - * - * Although JDBC specification does state that closing a statement closes - * its result set and closing a connection closes statements and result - * sets, this method does try to close the result set and statement - * explicitly whenever not null. This can guard against bugs in certain JDBC - * drivers and reduce leaks in case e.g. closing the result set succeeds but - * closing the statement or connection fails. - * - * @param conn - * the connection to release - * @param statement - * the statement to close, may be null to skip closing - * @param rs - * the result set to close, may be null to skip closing - * @throws SQLException - * if closing the result set or the statement fails - */ - protected void releaseConnection(Connection conn, Statement statement, - ResultSet rs) throws SQLException { - try { - try { - if (null != rs) { - rs.close(); - } - } finally { - if (null != statement) { - if (statement instanceof PreparedStatement) { - try { - ((PreparedStatement) statement).clearParameters(); - } catch (Exception e) { - // will be closed below anyway - } - } - statement.close(); - } - } - } finally { - releaseConnection(conn); - } - } - - /** - * Returns the currently active connection, reserves and returns a new - * connection if no active connection. - * - * @return previously active or newly reserved connection - * @throws SQLException - */ - protected Connection getConnection() throws SQLException { - if (activeConnection != null) { - return activeConnection; - } - return connectionPool.reserveConnection(); - } - - protected boolean isInTransaction() { - return activeConnection != null; - } - - /** - * Releases the connection if it is not part of an active transaction. - * - * @param conn - * the connection to release - */ - private void releaseConnection(Connection conn) { - if (conn != activeConnection && conn != null) { - connectionPool.releaseConnection(conn); - } - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java deleted file mode 100644 index 8066d563a4..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQuery.java +++ /dev/null @@ -1,495 +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.sqlcontainer.query; - -import java.io.IOException; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.RowItem; -import com.vaadin.data.util.sqlcontainer.SQLContainer; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; - -@SuppressWarnings("serial") -public class FreeformQuery extends AbstractTransactionalQuery - implements QueryDelegate { - - FreeformQueryDelegate delegate = null; - private String queryString; - private List primaryKeyColumns; - - /** - * Prevent no-parameters instantiation of FreeformQuery - */ - @SuppressWarnings("unused") - private FreeformQuery() { - } - - /** - * Creates a new freeform query delegate to be used with the - * {@link SQLContainer}. - * - * @param queryString - * The actual query to perform. - * @param primaryKeyColumns - * The primary key columns. Read-only mode is forced if this - * parameter is null or empty. - * @param connectionPool - * the JDBCConnectionPool to use to open connections to the SQL - * database. - * @deprecated As of 6.7, @see - * {@link FreeformQuery#FreeformQuery(String, JDBCConnectionPool, String...)} - */ - @Deprecated - public FreeformQuery(String queryString, List primaryKeyColumns, - JDBCConnectionPool connectionPool) { - super(connectionPool); - if (primaryKeyColumns == null) { - primaryKeyColumns = new ArrayList(); - } - if (primaryKeyColumns.contains("")) { - throw new IllegalArgumentException( - "The primary key columns contain an empty string!"); - } else if (queryString == null || "".equals(queryString)) { - throw new IllegalArgumentException( - "The query string may not be empty or null!"); - } else if (connectionPool == null) { - throw new IllegalArgumentException( - "The connectionPool may not be null!"); - } - this.queryString = queryString; - this.primaryKeyColumns = Collections - .unmodifiableList(primaryKeyColumns); - } - - /** - * Creates a new freeform query delegate to be used with the - * {@link SQLContainer}. - * - * @param queryString - * The actual query to perform. - * @param connectionPool - * the JDBCConnectionPool to use to open connections to the SQL - * database. - * @param primaryKeyColumns - * The primary key columns. Read-only mode is forced if none are - * provided. (optional) - */ - public FreeformQuery(String queryString, JDBCConnectionPool connectionPool, - String... primaryKeyColumns) { - this(queryString, Arrays.asList(primaryKeyColumns), connectionPool); - } - - /** - * This implementation of getCount() actually fetches all records from the - * database, which might be a performance issue. Override this method with a - * SELECT COUNT(*) ... query if this is too slow for your needs. - * - * {@inheritDoc} - */ - @Override - public int getCount() throws SQLException { - // First try the delegate - int count = countByDelegate(); - if (count < 0) { - // Couldn't use the delegate, use the bad way. - Statement statement = null; - ResultSet rs = null; - Connection conn = getConnection(); - try { - statement = conn.createStatement( - ResultSet.TYPE_SCROLL_INSENSITIVE, - ResultSet.CONCUR_READ_ONLY); - - rs = statement.executeQuery(queryString); - if (rs.last()) { - count = rs.getRow(); - } else { - count = 0; - } - } finally { - releaseConnection(conn, statement, rs); - } - } - return count; - } - - @SuppressWarnings("deprecation") - private int countByDelegate() throws SQLException { - int count = -1; - if (delegate == null) { - return count; - } - /* First try using prepared statement */ - if (delegate instanceof FreeformStatementDelegate) { - try { - StatementHelper sh = ((FreeformStatementDelegate) delegate) - .getCountStatement(); - PreparedStatement pstmt = null; - ResultSet rs = null; - Connection c = getConnection(); - try { - pstmt = c.prepareStatement(sh.getQueryString()); - sh.setParameterValuesToStatement(pstmt); - rs = pstmt.executeQuery(); - if (rs.next()) { - count = rs.getInt(1); - } else { - // The result can be empty when using group by and there - // are no matches (#18043) - count = 0; - } - } finally { - releaseConnection(c, pstmt, rs); - } - return count; - } catch (UnsupportedOperationException e) { - // Count statement generation not supported - } - } - /* Try using regular statement */ - try { - String countQuery = delegate.getCountQuery(); - if (countQuery != null) { - Statement statement = null; - ResultSet rs = null; - Connection conn = getConnection(); - try { - statement = conn.createStatement(); - rs = statement.executeQuery(countQuery); - if (rs.next()) { - count = rs.getInt(1); - } else { - // The result can be empty when using group by and there - // are no matches (#18043) - count = 0; - } - return count; - } finally { - releaseConnection(conn, statement, rs); - } - } - } catch (UnsupportedOperationException e) { - // Count query generation not supported - } - return count; - } - - /** - * Fetches the results for the query. This implementation always fetches the - * entire record set, ignoring the offset and page length parameters. In - * order to support lazy loading of records, you must supply a - * FreeformQueryDelegate that implements the - * FreeformQueryDelegate.getQueryString(int,int) method. - * - * @throws SQLException - * - * @see FreeformQueryDelegate#getQueryString(int, int) - */ - @Override - @SuppressWarnings({ "deprecation", "finally" }) - public ResultSet getResults(int offset, int pagelength) - throws SQLException { - ensureTransaction(); - String query = queryString; - if (delegate != null) { - /* First try using prepared statement */ - if (delegate instanceof FreeformStatementDelegate) { - try { - StatementHelper sh = ((FreeformStatementDelegate) delegate) - .getQueryStatement(offset, pagelength); - PreparedStatement pstmt = getConnection() - .prepareStatement(sh.getQueryString()); - sh.setParameterValuesToStatement(pstmt); - return pstmt.executeQuery(); - } catch (UnsupportedOperationException e) { - // Statement generation not supported, continue... - } - } - try { - query = delegate.getQueryString(offset, pagelength); - } catch (UnsupportedOperationException e) { - // This is fine, we'll just use the default queryString. - } - } - Statement statement = getConnection().createStatement(); - ResultSet rs; - try { - rs = statement.executeQuery(query); - } catch (SQLException e) { - try { - statement.close(); - } finally { - // throw the original exception even if closing the statement - // fails - throw e; - } - } - return rs; - } - - @Override - @SuppressWarnings("deprecation") - public boolean implementationRespectsPagingLimits() { - if (delegate == null) { - return false; - } - /* First try using prepared statement */ - if (delegate instanceof FreeformStatementDelegate) { - try { - StatementHelper sh = ((FreeformStatementDelegate) delegate) - .getCountStatement(); - if (sh != null && sh.getQueryString() != null - && sh.getQueryString().length() > 0) { - return true; - } - } catch (UnsupportedOperationException e) { - // Statement generation not supported, continue... - } - } - try { - String queryString = delegate.getQueryString(0, 50); - return queryString != null && queryString.length() > 0; - } catch (UnsupportedOperationException e) { - return false; - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setFilters(java - * .util.List) - */ - @Override - public void setFilters(List filters) - throws UnsupportedOperationException { - if (delegate != null) { - delegate.setFilters(filters); - } else if (filters != null) { - throw new UnsupportedOperationException( - "FreeFormQueryDelegate not set!"); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setOrderBy(java - * .util.List) - */ - @Override - public void setOrderBy(List orderBys) - throws UnsupportedOperationException { - if (delegate != null) { - delegate.setOrderBy(orderBys); - } else if (orderBys != null) { - throw new UnsupportedOperationException( - "FreeFormQueryDelegate not set!"); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin - * .data.util.sqlcontainer.RowItem) - */ - @Override - public int storeRow(RowItem row) throws SQLException { - if (!isInTransaction()) { - throw new IllegalStateException("No transaction is active!"); - } else if (primaryKeyColumns.isEmpty()) { - throw new UnsupportedOperationException( - "Cannot store items fetched with a read-only freeform query!"); - } - if (delegate != null) { - return delegate.storeRow(getConnection(), row); - } else { - throw new UnsupportedOperationException( - "FreeFormQueryDelegate not set!"); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate#removeRow(com. - * vaadin .data.util.sqlcontainer.RowItem) - */ - @Override - public boolean removeRow(RowItem row) throws SQLException { - if (!isInTransaction()) { - throw new IllegalStateException("No transaction is active!"); - } else if (primaryKeyColumns.isEmpty()) { - throw new UnsupportedOperationException( - "Cannot remove items fetched with a read-only freeform query!"); - } - if (delegate != null) { - return delegate.removeRow(getConnection(), row); - } else { - throw new UnsupportedOperationException( - "FreeFormQueryDelegate not set!"); - } - } - - @Override - public synchronized void beginTransaction() - throws UnsupportedOperationException, SQLException { - super.beginTransaction(); - } - - @Override - public synchronized void commit() - throws UnsupportedOperationException, SQLException { - super.commit(); - } - - @Override - public synchronized void rollback() - throws UnsupportedOperationException, SQLException { - super.rollback(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate# - * getPrimaryKeyColumns () - */ - @Override - public List getPrimaryKeyColumns() { - return primaryKeyColumns; - } - - public String getQueryString() { - return queryString; - } - - public FreeformQueryDelegate getDelegate() { - return delegate; - } - - public void setDelegate(FreeformQueryDelegate delegate) { - this.delegate = delegate; - } - - /** - * This implementation of the containsRowWithKey method rewrites existing - * WHERE clauses in the query string. The logic is, however, not very - * complex and some times can do the Wrong ThingTM. For the - * situations where this logic is not enough, you can implement the - * getContainsRowQueryString method in FreeformQueryDelegate and this will - * be used instead of the logic. - * - * @see FreeformQueryDelegate#getContainsRowQueryString(Object...) - * - */ - @Override - @SuppressWarnings("deprecation") - public boolean containsRowWithKey(Object... keys) throws SQLException { - String query = null; - boolean contains = false; - if (delegate != null) { - if (delegate instanceof FreeformStatementDelegate) { - try { - StatementHelper sh = ((FreeformStatementDelegate) delegate) - .getContainsRowQueryStatement(keys); - - PreparedStatement pstmt = null; - ResultSet rs = null; - Connection c = getConnection(); - try { - pstmt = c.prepareStatement(sh.getQueryString()); - sh.setParameterValuesToStatement(pstmt); - rs = pstmt.executeQuery(); - contains = rs.next(); - return contains; - } finally { - releaseConnection(c, pstmt, rs); - } - } catch (UnsupportedOperationException e) { - // Statement generation not supported, continue... - } - } - try { - query = delegate.getContainsRowQueryString(keys); - } catch (UnsupportedOperationException e) { - query = modifyWhereClause(keys); - } - } else { - query = modifyWhereClause(keys); - } - Statement statement = null; - ResultSet rs = null; - Connection conn = getConnection(); - try { - statement = conn.createStatement(); - rs = statement.executeQuery(query); - contains = rs.next(); - } finally { - releaseConnection(conn, statement, rs); - } - return contains; - } - - private String modifyWhereClause(Object... keys) { - // Build the where rules for the provided keys - StringBuffer where = new StringBuffer(); - for (int ix = 0; ix < primaryKeyColumns.size(); ix++) { - where.append(QueryBuilder.quote(primaryKeyColumns.get(ix))); - if (keys[ix] == null) { - where.append(" IS NULL"); - } else { - where.append(" = '").append(keys[ix]).append("'"); - } - if (ix < primaryKeyColumns.size() - 1) { - where.append(" AND "); - } - } - // Is there already a WHERE clause in the query string? - int index = queryString.toLowerCase().indexOf("where "); - if (index > -1) { - // Rewrite the where clause - return queryString.substring(0, index) + "WHERE " + where + " AND " - + queryString.substring(index + 6); - } - // Append a where clause - return queryString + " WHERE " + where; - } - - private void writeObject(java.io.ObjectOutputStream out) - throws IOException { - try { - rollback(); - } catch (SQLException ignored) { - } - out.defaultWriteObject(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryDelegate.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryDelegate.java deleted file mode 100644 index fac0b1ab45..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryDelegate.java +++ /dev/null @@ -1,130 +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.sqlcontainer.query; - -import java.io.Serializable; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.RowItem; - -public interface FreeformQueryDelegate extends Serializable { - /** - * Should return the SQL query string to be performed. This method is - * responsible for gluing together the select query from the filters and the - * order by conditions if these are supported. - * - * @param offset - * the first record (row) to fetch. - * @param pagelength - * the number of records (rows) to fetch. 0 means all records - * starting from offset. - * @deprecated As of 6.7. Implement {@link FreeformStatementDelegate} - * instead of {@link FreeformQueryDelegate} - */ - @Deprecated - public String getQueryString(int offset, int limit) - throws UnsupportedOperationException; - - /** - * Generates and executes a query to determine the current row count from - * the DB. Row count will be fetched using filters that are currently set to - * the QueryDelegate. - * - * @return row count - * @throws SQLException - * @deprecated As of 6.7. Implement {@link FreeformStatementDelegate} - * instead of {@link FreeformQueryDelegate} - */ - @Deprecated - public String getCountQuery() throws UnsupportedOperationException; - - /** - * Sets the filters to apply when performing the SQL query. These are - * translated into a WHERE clause. Default filtering mode will be used. - * - * @param filters - * The filters to apply. - * @throws UnsupportedOperationException - * if the implementation doesn't support filtering. - */ - public void setFilters(List filters) - throws UnsupportedOperationException; - - /** - * Sets the order in which to retrieve rows from the database. The result - * can be ordered by zero or more columns and each column can be in - * ascending or descending order. These are translated into an ORDER BY - * clause in the SQL query. - * - * @param orderBys - * A list of the OrderBy conditions. - * @throws UnsupportedOperationException - * if the implementation doesn't support ordering. - */ - public void setOrderBy(List orderBys) - throws UnsupportedOperationException; - - /** - * Stores a row in the database. The implementation of this interface - * decides how to identify whether to store a new row or update an existing - * one. - * - * @param conn - * the JDBC connection to use - * @param row - * RowItem to be stored or updated. - * @throws UnsupportedOperationException - * if the implementation is read only. - * @throws SQLException - */ - public int storeRow(Connection conn, RowItem row) - throws UnsupportedOperationException, SQLException; - - /** - * Removes the given RowItem from the database. - * - * @param conn - * the JDBC connection to use - * @param row - * RowItem to be removed - * @return true on success - * @throws UnsupportedOperationException - * @throws SQLException - */ - public boolean removeRow(Connection conn, RowItem row) - throws UnsupportedOperationException, SQLException; - - /** - * Generates an SQL Query string that allows the user of the FreeformQuery - * class to customize the query string used by the - * FreeformQuery.containsRowWithKeys() method. This is useful for cases when - * the logic in the containsRowWithKeys method is not enough to support more - * complex free form queries. - * - * @param keys - * the values of the primary keys - * @throws UnsupportedOperationException - * to use the default logic in FreeformQuery - * @deprecated As of 6.7. Implement {@link FreeformStatementDelegate} - * instead of {@link FreeformQueryDelegate} - */ - @Deprecated - public String getContainsRowQueryString(Object... keys) - throws UnsupportedOperationException; -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformStatementDelegate.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformStatementDelegate.java deleted file mode 100644 index 884a303684..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/FreeformStatementDelegate.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.data.util.sqlcontainer.query; - -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -/** - * FreeformStatementDelegate is an extension to FreeformQueryDelegate that - * provides definitions for methods that produce StatementHelper objects instead - * of basic query strings. This allows the FreeformQuery query delegate to use - * PreparedStatements instead of regular Statement when accessing the database. - * - * Due to the injection protection and other benefits of prepared statements, it - * is advisable to implement this interface instead of the FreeformQueryDelegate - * whenever possible. - */ -public interface FreeformStatementDelegate extends FreeformQueryDelegate { - /** - * Should return a new instance of StatementHelper that contains the query - * string and parameter values required to create a PreparedStatement. This - * method is responsible for gluing together the select query from the - * filters and the order by conditions if these are supported. - * - * @param offset - * the first record (row) to fetch. - * @param pagelength - * the number of records (rows) to fetch. 0 means all records - * starting from offset. - */ - public StatementHelper getQueryStatement(int offset, int limit) - throws UnsupportedOperationException; - - /** - * Should return a new instance of StatementHelper that contains the query - * string and parameter values required to create a PreparedStatement that - * will fetch the row count from the DB. Row count should be fetched using - * filters that are currently set to the QueryDelegate. - */ - public StatementHelper getCountStatement() - throws UnsupportedOperationException; - - /** - * Should return a new instance of StatementHelper that contains the query - * string and parameter values required to create a PreparedStatement used - * by the FreeformQuery.containsRowWithKeys() method. This is useful for - * cases when the default logic in said method is not enough to support more - * complex free form queries. - * - * @param keys - * the values of the primary keys - * @throws UnsupportedOperationException - * to use the default logic in FreeformQuery - */ - public StatementHelper getContainsRowQueryStatement(Object... keys) - throws UnsupportedOperationException; -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/OrderBy.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/OrderBy.java deleted file mode 100644 index ed57967772..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/OrderBy.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.data.util.sqlcontainer.query; - -import java.io.Serializable; - -/** - * OrderBy represents a sorting rule to be applied to a query made by the - * SQLContainer's QueryDelegate. - * - * The sorting rule is simple and contains only the affected column's name and - * the direction of the sort. - */ -public class OrderBy implements Serializable { - private String column; - private boolean isAscending; - - /** - * Prevent instantiation without required parameters. - */ - @SuppressWarnings("unused") - private OrderBy() { - } - - public OrderBy(String column, boolean isAscending) { - setColumn(column); - setAscending(isAscending); - } - - public void setColumn(String column) { - this.column = column; - } - - public String getColumn() { - return column; - } - - public void setAscending(boolean isAscending) { - this.isAscending = isAscending; - } - - public boolean isAscending() { - return isAscending; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/QueryDelegate.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/QueryDelegate.java deleted file mode 100644 index 16e04a7da0..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/QueryDelegate.java +++ /dev/null @@ -1,239 +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.sqlcontainer.query; - -import java.io.Serializable; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.RowId; -import com.vaadin.data.util.sqlcontainer.RowItem; - -public interface QueryDelegate extends Serializable { - /** - * Generates and executes a query to determine the current row count from - * the DB. Row count will be fetched using filters that are currently set to - * the QueryDelegate. - * - * @return row count - * @throws SQLException - */ - public int getCount() throws SQLException; - - /** - * Executes a paged SQL query and returns the ResultSet. The query is - * defined through implementations of this QueryDelegate interface. - * - * @param offset - * the first item of the page to load - * @param pagelength - * the length of the page to load - * @return a ResultSet containing the rows of the page - * @throws SQLException - * if the database access fails. - */ - public ResultSet getResults(int offset, int pagelength) throws SQLException; - - /** - * Allows the SQLContainer implementation to check whether the QueryDelegate - * implementation implements paging in the getResults method. - * - * @see QueryDelegate#getResults(int, int) - * - * @return true if the delegate implements paging - */ - public boolean implementationRespectsPagingLimits(); - - /** - * Sets the filters to apply when performing the SQL query. These are - * translated into a WHERE clause. Default filtering mode will be used. - * - * @param filters - * The filters to apply. - * @throws UnsupportedOperationException - * if the implementation doesn't support filtering. - */ - public void setFilters(List filters) - throws UnsupportedOperationException; - - /** - * Sets the order in which to retrieve rows from the database. The result - * can be ordered by zero or more columns and each column can be in - * ascending or descending order. These are translated into an ORDER BY - * clause in the SQL query. - * - * @param orderBys - * A list of the OrderBy conditions. - * @throws UnsupportedOperationException - * if the implementation doesn't support ordering. - */ - public void setOrderBy(List orderBys) - throws UnsupportedOperationException; - - /** - * Stores a row in the database. The implementation of this interface - * decides how to identify whether to store a new row or update an existing - * one. - * - * @param columnToValueMap - * A map containing the values for all columns to be stored or - * updated. - * @return the number of affected rows in the database table - * @throws UnsupportedOperationException - * if the implementation is read only. - */ - public int storeRow(RowItem row) - throws UnsupportedOperationException, SQLException; - - /** - * Removes the given RowItem from the database. - * - * @param row - * RowItem to be removed - * @return true on success - * @throws UnsupportedOperationException - * @throws SQLException - */ - public boolean removeRow(RowItem row) - throws UnsupportedOperationException, SQLException; - - /** - * Starts a new database transaction. Used when storing multiple changes. - * - * Note that if a transaction is already open, it will be rolled back when a - * new transaction is started. - * - * @throws SQLException - * if the database access fails. - */ - public void beginTransaction() throws SQLException; - - /** - * Commits a transaction. If a transaction is not open nothing should - * happen. - * - * @throws SQLException - * if the database access fails. - */ - public void commit() throws SQLException; - - /** - * Rolls a transaction back. If a transaction is not open nothing should - * happen. - * - * @throws SQLException - * if the database access fails. - */ - public void rollback() throws SQLException; - - /** - * Returns a list of primary key column names. The list is either fetched - * from the database (TableQuery) or given as an argument depending on - * implementation. - * - * @return - */ - public List getPrimaryKeyColumns(); - - /** - * Performs a query to find out whether the SQL table contains a row with - * the given set of primary keys. - * - * @param keys - * the primary keys - * @return true if the SQL table contains a row with the provided keys - * @throws SQLException - */ - public boolean containsRowWithKey(Object... keys) throws SQLException; - - /************************/ - /** ROWID CHANGE EVENT **/ - /************************/ - - /** - * An Event object specifying the old and new RowId of an added - * item after the addition has been successfully committed. - */ - public interface RowIdChangeEvent extends Serializable { - /** - * Gets the old (temporary) RowId of the added row that raised this - * event. - * - * @return old RowId - */ - public RowId getOldRowId(); - - /** - * Gets the new, possibly database assigned RowId of the added row that - * raised this event. - * - * @return new RowId - */ - public RowId getNewRowId(); - } - - /** RowId change listener interface. */ - public interface RowIdChangeListener extends Serializable { - /** - * Lets the listener know that a RowId has been changed. - * - * @param event - */ - public void rowIdChange(QueryDelegate.RowIdChangeEvent event); - } - - /** - * The interface for adding and removing RowIdChangeEvent - * listeners. By implementing this interface a class explicitly announces - * that it will generate a RowIdChangeEvent when it performs a - * database commit that may change the RowId. - */ - public interface RowIdChangeNotifier extends Serializable { - /** - * Adds a RowIdChangeListener for the object. - * - * @param listener - * listener to be added - */ - public void addRowIdChangeListener( - QueryDelegate.RowIdChangeListener listener); - - /** - * @deprecated As of 7.0, replaced by - * {@link #addRowIdChangeListener(RowIdChangeListener)} - **/ - @Deprecated - public void addListener(QueryDelegate.RowIdChangeListener listener); - - /** - * Removes the specified RowIdChangeListener from the object. - * - * @param listener - * listener to be removed - */ - public void removeRowIdChangeListener( - QueryDelegate.RowIdChangeListener listener); - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeRowIdChangeListener(RowIdChangeListener)} - **/ - @Deprecated - public void removeListener(QueryDelegate.RowIdChangeListener listener); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/TableQuery.java deleted file mode 100644 index 959c494634..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/TableQuery.java +++ /dev/null @@ -1,869 +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.sqlcontainer.query; - -import java.io.IOException; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EventObject; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.sqlcontainer.ColumnProperty; -import com.vaadin.data.util.sqlcontainer.OptimisticLockException; -import com.vaadin.data.util.sqlcontainer.RowId; -import com.vaadin.data.util.sqlcontainer.RowItem; -import com.vaadin.data.util.sqlcontainer.SQLUtil; -import com.vaadin.data.util.sqlcontainer.TemporaryRowId; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -@SuppressWarnings("serial") -public class TableQuery extends AbstractTransactionalQuery - implements QueryDelegate, QueryDelegate.RowIdChangeNotifier { - - /** - * Table name (without catalog or schema information). - */ - private String tableName; - private String catalogName; - private String schemaName; - /** - * Cached concatenated version of the table name. - */ - private String fullTableName; - /** - * Primary key column name(s) in the table. - */ - private List primaryKeyColumns; - /** - * Version column name in the table. - */ - private String versionColumn; - - /** Currently set Filters and OrderBys */ - private List filters; - private List orderBys; - - /** SQLGenerator instance to use for generating queries */ - private SQLGenerator sqlGenerator; - - /** Row ID change listeners */ - private LinkedList rowIdChangeListeners; - /** Row ID change events, stored until commit() is called */ - private final List bufferedEvents = new ArrayList(); - - /** Set to true to output generated SQL Queries to System.out */ - private final boolean debug = false; - - /** - * Creates a new TableQuery using the given connection pool, SQL generator - * and table name to fetch the data from. All parameters must be non-null. - * - * The table name must be a simple name with no catalog or schema - * information. If those are needed, use - * {@link #TableQuery(String, String, String, JDBCConnectionPool, SQLGenerator)} - * . - * - * @param tableName - * Name of the database table to connect to - * @param connectionPool - * Connection pool for accessing the database - * @param sqlGenerator - * SQL query generator implementation - */ - public TableQuery(String tableName, JDBCConnectionPool connectionPool, - SQLGenerator sqlGenerator) { - this(null, null, tableName, connectionPool, sqlGenerator); - } - - /** - * Creates a new TableQuery using the given connection pool, SQL generator - * and table name to fetch the data from. Catalog and schema names can be - * null, all other parameters must be non-null. - * - * @param catalogName - * Name of the database catalog (can be null) - * @param schemaName - * Name of the database schema (can be null) - * @param tableName - * Name of the database table to connect to - * @param connectionPool - * Connection pool for accessing the database - * @param sqlGenerator - * SQL query generator implementation - * @since 7.1 - */ - public TableQuery(String catalogName, String schemaName, String tableName, - JDBCConnectionPool connectionPool, SQLGenerator sqlGenerator) { - this(catalogName, schemaName, tableName, connectionPool, sqlGenerator, - true); - } - - /** - * Creates a new TableQuery using the given connection pool and table name - * to fetch the data from. All parameters must be non-null. The default SQL - * generator will be used for queries. - * - * The table name must be a simple name with no catalog or schema - * information. If those are needed, use - * {@link #TableQuery(String, String, String, JDBCConnectionPool, SQLGenerator)} - * . - * - * @param tableName - * Name of the database table to connect to - * @param connectionPool - * Connection pool for accessing the database - */ - public TableQuery(String tableName, JDBCConnectionPool connectionPool) { - this(tableName, connectionPool, new DefaultSQLGenerator()); - } - - /** - * Creates a new TableQuery using the given connection pool, SQL generator - * and table name to fetch the data from. Catalog and schema names can be - * null, all other parameters must be non-null. - * - * @param catalogName - * Name of the database catalog (can be null) - * @param schemaName - * Name of the database schema (can be null) - * @param tableName - * Name of the database table to connect to - * @param connectionPool - * Connection pool for accessing the database - * @param sqlGenerator - * SQL query generator implementation - * @param escapeNames - * true to escape special characters in catalog, schema and table - * names, false to use the names as-is - * @since 7.1 - */ - protected TableQuery(String catalogName, String schemaName, - String tableName, JDBCConnectionPool connectionPool, - SQLGenerator sqlGenerator, boolean escapeNames) { - super(connectionPool); - if (tableName == null || tableName.trim().length() < 1 - || connectionPool == null || sqlGenerator == null) { - throw new IllegalArgumentException( - "Table name, connection pool and SQL generator parameters must be non-null and non-empty."); - } - if (escapeNames) { - this.catalogName = SQLUtil.escapeSQL(catalogName); - this.schemaName = SQLUtil.escapeSQL(schemaName); - this.tableName = SQLUtil.escapeSQL(tableName); - } else { - this.catalogName = catalogName; - this.schemaName = schemaName; - this.tableName = tableName; - } - this.sqlGenerator = sqlGenerator; - fetchMetaData(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getCount() - */ - @Override - public int getCount() throws SQLException { - getLogger().log(Level.FINE, "Fetching count..."); - StatementHelper sh = sqlGenerator.generateSelectQuery( - getFullTableName(), filters, null, 0, 0, "COUNT(*)"); - boolean shouldCloseTransaction = false; - if (!isInTransaction()) { - shouldCloseTransaction = true; - beginTransaction(); - } - ResultSet r = null; - int count = -1; - try { - r = executeQuery(sh); - r.next(); - count = r.getInt(1); - } finally { - try { - if (r != null) { - // Do not release connection, it is done in commit() - releaseConnection(null, r.getStatement(), r); - } - } finally { - if (shouldCloseTransaction) { - commit(); - } - } - } - return count; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getResults(int, - * int) - */ - @Override - public ResultSet getResults(int offset, int pagelength) - throws SQLException { - StatementHelper sh; - /* - * If no ordering is explicitly set, results will be ordered by the - * first primary key column. - */ - if (orderBys == null || orderBys.isEmpty()) { - List ob = new ArrayList(); - for (int i = 0; i < primaryKeyColumns.size(); i++) { - ob.add(new OrderBy(primaryKeyColumns.get(i), true)); - } - sh = sqlGenerator.generateSelectQuery(getFullTableName(), filters, - ob, offset, pagelength, null); - } else { - sh = sqlGenerator.generateSelectQuery(getFullTableName(), filters, - orderBys, offset, pagelength, null); - } - return executeQuery(sh); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate# - * implementationRespectsPagingLimits() - */ - @Override - public boolean implementationRespectsPagingLimits() { - return true; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin - * .addon.sqlcontainer.RowItem) - */ - @Override - public int storeRow(RowItem row) - throws UnsupportedOperationException, SQLException { - if (row == null) { - throw new IllegalArgumentException( - "Row argument must be non-null."); - } - StatementHelper sh; - int result = 0; - if (row.getId() instanceof TemporaryRowId) { - setVersionColumnFlagInProperty(row); - sh = sqlGenerator.generateInsertQuery(getFullTableName(), row); - result = executeUpdateReturnKeys(sh, row); - } else { - setVersionColumnFlagInProperty(row); - sh = sqlGenerator.generateUpdateQuery(getFullTableName(), row); - result = executeUpdate(sh); - } - if (versionColumn != null && result == 0) { - throw new OptimisticLockException( - "Someone else changed the row that was being updated.", - row.getId()); - } - return result; - } - - private void setVersionColumnFlagInProperty(RowItem row) { - ColumnProperty versionProperty = (ColumnProperty) row - .getItemProperty(versionColumn); - if (versionProperty != null) { - versionProperty.setVersionColumn(true); - } - } - - /** - * Inserts the given row in the database table immediately. Begins and - * commits the transaction needed. This method was added specifically to - * solve the problem of returning the final RowId immediately on the - * SQLContainer.addItem() call when auto commit mode is enabled in the - * SQLContainer. - * - * @param row - * RowItem to add to the database - * @return Final RowId of the added row - * @throws SQLException - */ - public RowId storeRowImmediately(RowItem row) throws SQLException { - beginTransaction(); - /* Set version column, if one is provided */ - setVersionColumnFlagInProperty(row); - /* Generate query */ - StatementHelper sh = sqlGenerator - .generateInsertQuery(getFullTableName(), row); - Connection connection = null; - PreparedStatement pstmt = null; - ResultSet generatedKeys = null; - connection = getConnection(); - try { - pstmt = connection.prepareStatement(sh.getQueryString(), - primaryKeyColumns.toArray(new String[0])); - sh.setParameterValuesToStatement(pstmt); - getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); - int result = pstmt.executeUpdate(); - RowId newId = null; - if (result > 0) { - /* - * If affected rows exist, we'll get the new RowId, commit the - * transaction and return the new RowId. - */ - generatedKeys = pstmt.getGeneratedKeys(); - newId = getNewRowId(row, generatedKeys); - } - // transaction has to be closed in any case - commit(); - return newId; - } finally { - releaseConnection(connection, pstmt, generatedKeys); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util - * .List) - */ - @Override - public void setFilters(List filters) - throws UnsupportedOperationException { - if (filters == null) { - this.filters = null; - return; - } - this.filters = Collections.unmodifiableList(filters); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setOrderBy(java.util - * .List) - */ - @Override - public void setOrderBy(List orderBys) - throws UnsupportedOperationException { - if (orderBys == null) { - this.orderBys = null; - return; - } - this.orderBys = Collections.unmodifiableList(orderBys); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#beginTransaction() - */ - @Override - public void beginTransaction() - throws UnsupportedOperationException, SQLException { - getLogger().log(Level.FINE, "DB -> begin transaction"); - super.beginTransaction(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#commit() - */ - @Override - public void commit() throws UnsupportedOperationException, SQLException { - getLogger().log(Level.FINE, "DB -> commit"); - super.commit(); - - /* Handle firing row ID change events */ - RowIdChangeEvent[] unFiredEvents = bufferedEvents - .toArray(new RowIdChangeEvent[] {}); - bufferedEvents.clear(); - if (rowIdChangeListeners != null && !rowIdChangeListeners.isEmpty()) { - for (RowIdChangeListener r : rowIdChangeListeners) { - for (RowIdChangeEvent e : unFiredEvents) { - r.rowIdChange(e); - } - } - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#rollback() - */ - @Override - public void rollback() throws UnsupportedOperationException, SQLException { - getLogger().log(Level.FINE, "DB -> rollback"); - super.rollback(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.sqlcontainer.query.QueryDelegate#getPrimaryKeyColumns() - */ - @Override - public List getPrimaryKeyColumns() { - return Collections.unmodifiableList(primaryKeyColumns); - } - - public String getVersionColumn() { - return versionColumn; - } - - public void setVersionColumn(String column) { - versionColumn = column; - } - - /** - * Returns the table name for the query without catalog and schema - * information. - * - * @return table name, not null - */ - public String getTableName() { - return tableName; - } - - /** - * Returns the catalog name for the query. - * - * @return catalog name, can be null - * @since 7.1 - */ - public String getCatalogName() { - return catalogName; - } - - /** - * Returns the catalog name for the query. - * - * @return catalog name, can be null - * @since 7.1 - */ - public String getSchemaName() { - return schemaName; - } - - /** - * Returns the complete table name obtained by concatenation of the catalog - * and schema names (if any) and the table name. - * - * This method can be overridden if customization is needed. - * - * @return table name in the form it should be used in query and update - * statements - * @since 7.1 - */ - protected String getFullTableName() { - if (fullTableName == null) { - StringBuilder sb = new StringBuilder(); - if (catalogName != null) { - sb.append(catalogName).append("."); - } - if (schemaName != null) { - sb.append(schemaName).append("."); - } - sb.append(tableName); - fullTableName = sb.toString(); - } - return fullTableName; - } - - public SQLGenerator getSqlGenerator() { - return sqlGenerator; - } - - /** - * Executes the given query string using either the active connection if a - * transaction is already open, or a new connection from this query's - * connection pool. - * - * @param sh - * an instance of StatementHelper, containing the query string - * and parameter values. - * @return ResultSet of the query - * @throws SQLException - */ - private ResultSet executeQuery(StatementHelper sh) throws SQLException { - ensureTransaction(); - Connection connection = getConnection(); - PreparedStatement pstmt = null; - try { - pstmt = connection.prepareStatement(sh.getQueryString()); - sh.setParameterValuesToStatement(pstmt); - getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); - return pstmt.executeQuery(); - } catch (SQLException e) { - releaseConnection(null, pstmt, null); - throw e; - } - } - - /** - * Executes the given update query string using either the active connection - * if a transaction is already open, or a new connection from this query's - * connection pool. - * - * @param sh - * an instance of StatementHelper, containing the query string - * and parameter values. - * @return Number of affected rows - * @throws SQLException - */ - private int executeUpdate(StatementHelper sh) throws SQLException { - PreparedStatement pstmt = null; - Connection connection = null; - try { - connection = getConnection(); - pstmt = connection.prepareStatement(sh.getQueryString()); - sh.setParameterValuesToStatement(pstmt); - getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); - int retval = pstmt.executeUpdate(); - return retval; - } finally { - releaseConnection(connection, pstmt, null); - } - } - - /** - * Executes the given update query string using either the active connection - * if a transaction is already open, or a new connection from this query's - * connection pool. - * - * Additionally adds a new RowIdChangeEvent to the event buffer. - * - * @param sh - * an instance of StatementHelper, containing the query string - * and parameter values. - * @param row - * the row item to update - * @return Number of affected rows - * @throws SQLException - */ - private int executeUpdateReturnKeys(StatementHelper sh, RowItem row) - throws SQLException { - PreparedStatement pstmt = null; - ResultSet genKeys = null; - Connection connection = null; - try { - connection = getConnection(); - pstmt = connection.prepareStatement(sh.getQueryString(), - primaryKeyColumns.toArray(new String[0])); - sh.setParameterValuesToStatement(pstmt); - getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); - int result = pstmt.executeUpdate(); - genKeys = pstmt.getGeneratedKeys(); - RowId newId = getNewRowId(row, genKeys); - bufferedEvents.add(new RowIdChangeEvent(row.getId(), newId)); - return result; - } finally { - releaseConnection(connection, pstmt, genKeys); - } - } - - /** - * Fetches name(s) of primary key column(s) from DB metadata. - * - * Also tries to get the escape string to be used in search strings. - */ - private void fetchMetaData() { - Connection connection = null; - ResultSet rs = null; - ResultSet tables = null; - try { - connection = getConnection(); - DatabaseMetaData dbmd = connection.getMetaData(); - if (dbmd != null) { - tables = dbmd.getTables(catalogName, schemaName, tableName, - null); - if (!tables.next()) { - String catalog = (catalogName != null) - ? catalogName.toUpperCase() : null; - String schema = (schemaName != null) - ? schemaName.toUpperCase() : null; - tables = dbmd.getTables(catalog, schema, - tableName.toUpperCase(), null); - if (!tables.next()) { - throw new IllegalArgumentException( - "Table with the name \"" + getFullTableName() - + "\" was not found. Check your database contents."); - } else { - catalogName = catalog; - schemaName = schema; - tableName = tableName.toUpperCase(); - } - } - tables.close(); - rs = dbmd.getPrimaryKeys(catalogName, schemaName, tableName); - List names = new ArrayList(); - while (rs.next()) { - names.add(rs.getString("COLUMN_NAME")); - } - rs.close(); - if (!names.isEmpty()) { - primaryKeyColumns = names; - } - if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) { - throw new IllegalArgumentException( - "Primary key constraints have not been defined for the table \"" - + getFullTableName() - + "\". Use FreeFormQuery to access this table."); - } - for (String colName : primaryKeyColumns) { - if (colName.equalsIgnoreCase("rownum")) { - if (getSqlGenerator() instanceof MSSQLGenerator - || getSqlGenerator() instanceof MSSQLGenerator) { - throw new IllegalArgumentException( - "When using Oracle or MSSQL, a primary key column" - + " named \'rownum\' is not allowed!"); - } - } - } - } - } catch (SQLException e) { - throw new RuntimeException(e); - } finally { - try { - releaseConnection(connection, null, rs); - } catch (SQLException ignore) { - } finally { - try { - if (tables != null) { - tables.close(); - } - } catch (SQLException ignore) { - } - } - } - } - - private RowId getNewRowId(RowItem row, ResultSet genKeys) { - try { - /* Fetch primary key values and generate a map out of them. */ - Map values = new HashMap(); - ResultSetMetaData rsmd = genKeys.getMetaData(); - int colCount = rsmd.getColumnCount(); - if (genKeys.next()) { - for (int i = 1; i <= colCount; i++) { - values.put(rsmd.getColumnName(i), genKeys.getObject(i)); - } - } - /* Generate new RowId */ - List newRowId = new ArrayList(); - if (values.size() == 1) { - if (primaryKeyColumns.size() == 1) { - newRowId.add(values.get(values.keySet().iterator().next())); - } else { - for (String s : primaryKeyColumns) { - if (!((ColumnProperty) row.getItemProperty(s)) - .isReadOnlyChangeAllowed()) { - newRowId.add(values - .get(values.keySet().iterator().next())); - } else { - newRowId.add(values.get(s)); - } - } - } - } else { - for (String s : primaryKeyColumns) { - newRowId.add(values.get(s)); - } - } - return new RowId(newRowId.toArray()); - } catch (Exception e) { - getLogger().log(Level.FINE, - "Failed to fetch key values on insert: {0}", - e.getMessage()); - return null; - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.sqlcontainer.query.QueryDelegate#removeRow(com.vaadin - * .addon.sqlcontainer.RowItem) - */ - @Override - public boolean removeRow(RowItem row) - throws UnsupportedOperationException, SQLException { - if (getLogger().isLoggable(Level.FINE)) { - getLogger().log(Level.FINE, "Removing row with id: {0}", - row.getId().getId()[0]); - } - if (executeUpdate(sqlGenerator.generateDeleteQuery(getFullTableName(), - primaryKeyColumns, versionColumn, row)) == 1) { - return true; - } - if (versionColumn != null) { - throw new OptimisticLockException( - "Someone else changed the row that was being deleted.", - row.getId()); - } - return false; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.sqlcontainer.query.QueryDelegate#containsRowWithKey( - * java.lang.Object[]) - */ - @Override - public boolean containsRowWithKey(Object... keys) throws SQLException { - ArrayList filtersAndKeys = new ArrayList(); - if (filters != null) { - filtersAndKeys.addAll(filters); - } - int ix = 0; - for (String colName : primaryKeyColumns) { - filtersAndKeys.add(new Equal(colName, keys[ix])); - ix++; - } - StatementHelper sh = sqlGenerator.generateSelectQuery( - getFullTableName(), filtersAndKeys, orderBys, 0, 0, "*"); - - boolean shouldCloseTransaction = false; - if (!isInTransaction()) { - shouldCloseTransaction = true; - beginTransaction(); - } - ResultSet rs = null; - try { - rs = executeQuery(sh); - boolean contains = rs.next(); - return contains; - } finally { - try { - if (rs != null) { - // Do not release connection, it is done in commit() - releaseConnection(null, rs.getStatement(), rs); - } - } finally { - if (shouldCloseTransaction) { - commit(); - } - } - } - } - - /** - * Custom writeObject to call rollback() if object is serialized. - */ - private void writeObject(java.io.ObjectOutputStream out) - throws IOException { - try { - rollback(); - } catch (SQLException ignored) { - } - out.defaultWriteObject(); - } - - /** - * Simple RowIdChangeEvent implementation. - */ - public static class RowIdChangeEvent extends EventObject - implements QueryDelegate.RowIdChangeEvent { - private final RowId oldId; - private final RowId newId; - - private RowIdChangeEvent(RowId oldId, RowId newId) { - super(oldId); - this.oldId = oldId; - this.newId = newId; - } - - @Override - public RowId getNewRowId() { - return newId; - } - - @Override - public RowId getOldRowId() { - return oldId; - } - } - - /** - * Adds RowIdChangeListener to this query - */ - @Override - public void addRowIdChangeListener(RowIdChangeListener listener) { - if (rowIdChangeListeners == null) { - rowIdChangeListeners = new LinkedList(); - } - rowIdChangeListeners.add(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addRowIdChangeListener(com.vaadin.data.util.sqlcontainer.query.QueryDelegate.RowIdChangeListener)} - **/ - @Override - @Deprecated - public void addListener(RowIdChangeListener listener) { - addRowIdChangeListener(listener); - } - - /** - * Removes the given RowIdChangeListener from this query - */ - @Override - public void removeRowIdChangeListener(RowIdChangeListener listener) { - if (rowIdChangeListeners != null) { - rowIdChangeListeners.remove(listener); - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeRowIdChangeListener(com.vaadin.data.util.sqlcontainer.query.QueryDelegate.RowIdChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(RowIdChangeListener listener) { - removeRowIdChangeListener(listener); - } - - private static final Logger getLogger() { - return Logger.getLogger(TableQuery.class.getName()); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java deleted file mode 100644 index 9c05545e41..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java +++ /dev/null @@ -1,395 +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.sqlcontainer.query.generator; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.ColumnProperty; -import com.vaadin.data.util.sqlcontainer.RowItem; -import com.vaadin.data.util.sqlcontainer.SQLUtil; -import com.vaadin.data.util.sqlcontainer.TemporaryRowId; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.StringDecorator; - -/** - * Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL. - * - * @author Jonatan Kronqvist / Vaadin Ltd - */ -@SuppressWarnings("serial") -public class DefaultSQLGenerator implements SQLGenerator { - - private Class statementHelperClass = null; - - public DefaultSQLGenerator() { - - } - - /** - * Create a new DefaultSqlGenerator instance that uses the given - * implementation of {@link StatementHelper} - * - * @param statementHelper - */ - public DefaultSQLGenerator( - Class statementHelperClazz) { - this(); - statementHelperClass = statementHelperClazz; - } - - /** - * Construct a DefaultSQLGenerator with the specified identifiers for start - * and end of quoted strings. The identifiers may be different depending on - * the database engine and it's settings. - * - * @param quoteStart - * the identifier (character) denoting the start of a quoted - * string - * @param quoteEnd - * the identifier (character) denoting the end of a quoted string - */ - public DefaultSQLGenerator(String quoteStart, String quoteEnd) { - QueryBuilder - .setStringDecorator(new StringDecorator(quoteStart, quoteEnd)); - } - - /** - * Same as {@link #DefaultSQLGenerator(String, String)} but with support for - * custom {@link StatementHelper} implementation. - * - * @param quoteStart - * @param quoteEnd - * @param statementHelperClazz - */ - public DefaultSQLGenerator(String quoteStart, String quoteEnd, - Class statementHelperClazz) { - this(quoteStart, quoteEnd); - statementHelperClass = statementHelperClazz; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# - * generateSelectQuery(java.lang.String, java.util.List, java.util.List, - * int, int, java.lang.String) - */ - @Override - public StatementHelper generateSelectQuery(String tableName, - List filters, List orderBys, int offset, - int pagelength, String toSelect) { - if (tableName == null || tableName.trim().equals("")) { - throw new IllegalArgumentException("Table name must be given."); - } - toSelect = toSelect == null ? "*" : toSelect; - StatementHelper sh = getStatementHelper(); - StringBuffer query = new StringBuffer(); - query.append("SELECT " + toSelect + " FROM ") - .append(SQLUtil.escapeSQL(tableName)); - if (filters != null) { - query.append(QueryBuilder.getWhereStringForFilters(filters, sh)); - } - if (orderBys != null) { - for (OrderBy o : orderBys) { - generateOrderBy(query, o, orderBys.indexOf(o) == 0); - } - } - if (pagelength != 0) { - generateLimits(query, offset, pagelength); - } - sh.setQueryString(query.toString()); - return sh; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# - * generateUpdateQuery(java.lang.String, - * com.vaadin.addon.sqlcontainer.RowItem) - */ - @Override - public StatementHelper generateUpdateQuery(String tableName, RowItem item) { - if (tableName == null || tableName.trim().equals("")) { - throw new IllegalArgumentException("Table name must be given."); - } - if (item == null) { - throw new IllegalArgumentException("Updated item must be given."); - } - StatementHelper sh = getStatementHelper(); - StringBuffer query = new StringBuffer(); - query.append("UPDATE ").append(tableName).append(" SET"); - - /* Generate column<->value and rowidentifiers map */ - Map columnToValueMap = generateColumnToValueMap(item); - Map rowIdentifiers = generateRowIdentifiers(item); - /* Generate columns and values to update */ - boolean first = true; - for (String column : columnToValueMap.keySet()) { - if (first) { - query.append(" " + QueryBuilder.quote(column) + " = ?"); - } else { - query.append(", " + QueryBuilder.quote(column) + " = ?"); - } - sh.addParameterValue(columnToValueMap.get(column), - item.getItemProperty(column).getType()); - first = false; - } - /* Generate identifiers for the row to be updated */ - first = true; - for (String column : rowIdentifiers.keySet()) { - if (first) { - query.append(" WHERE " + QueryBuilder.quote(column) + " = ?"); - } else { - query.append(" AND " + QueryBuilder.quote(column) + " = ?"); - } - sh.addParameterValue(rowIdentifiers.get(column), - item.getItemProperty(column).getType()); - first = false; - } - sh.setQueryString(query.toString()); - return sh; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# - * generateInsertQuery(java.lang.String, - * com.vaadin.addon.sqlcontainer.RowItem) - */ - @Override - public StatementHelper generateInsertQuery(String tableName, RowItem item) { - if (tableName == null || tableName.trim().equals("")) { - throw new IllegalArgumentException("Table name must be given."); - } - if (item == null) { - throw new IllegalArgumentException("New item must be given."); - } - if (!(item.getId() instanceof TemporaryRowId)) { - throw new IllegalArgumentException( - "Cannot generate an insert query for item already in database."); - } - StatementHelper sh = getStatementHelper(); - StringBuffer query = new StringBuffer(); - query.append("INSERT INTO ").append(tableName).append(" ("); - - /* Generate column<->value map */ - Map columnToValueMap = generateColumnToValueMap(item); - /* Generate column names for insert query */ - boolean first = true; - for (String column : columnToValueMap.keySet()) { - if (!first) { - query.append(", "); - } - query.append(QueryBuilder.quote(column)); - first = false; - } - - /* Generate values for insert query */ - query.append(") VALUES ("); - first = true; - for (String column : columnToValueMap.keySet()) { - if (!first) { - query.append(", "); - } - query.append("?"); - sh.addParameterValue(columnToValueMap.get(column), - item.getItemProperty(column).getType()); - first = false; - } - query.append(")"); - sh.setQueryString(query.toString()); - return sh; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# - * generateDeleteQuery(java.lang.String, - * com.vaadin.addon.sqlcontainer.RowItem) - */ - @Override - public StatementHelper generateDeleteQuery(String tableName, - List primaryKeyColumns, String versionColumn, - RowItem item) { - if (tableName == null || tableName.trim().equals("")) { - throw new IllegalArgumentException("Table name must be given."); - } - if (item == null) { - throw new IllegalArgumentException( - "Item to be deleted must be given."); - } - if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) { - throw new IllegalArgumentException( - "Valid keyColumnNames must be provided."); - } - StatementHelper sh = getStatementHelper(); - StringBuffer query = new StringBuffer(); - query.append("DELETE FROM ").append(tableName).append(" WHERE "); - int count = 1; - for (String keyColName : primaryKeyColumns) { - if ((this instanceof MSSQLGenerator - || this instanceof OracleGenerator) - && keyColName.equalsIgnoreCase("rownum")) { - count++; - continue; - } - if (count > 1) { - query.append(" AND "); - } - if (item.getItemProperty(keyColName).getValue() != null) { - query.append(QueryBuilder.quote(keyColName) + " = ?"); - sh.addParameterValue( - item.getItemProperty(keyColName).getValue(), - item.getItemProperty(keyColName).getType()); - } - count++; - } - if (versionColumn != null) { - if (!item.getItemPropertyIds().contains(versionColumn)) { - throw new IllegalArgumentException(String.format( - "Table '%s' does not contain version column '%s'.", - tableName, versionColumn)); - } - - query.append(String.format(" AND %s = ?", - QueryBuilder.quote(versionColumn))); - sh.addParameterValue(item.getItemProperty(versionColumn).getValue(), - item.getItemProperty(versionColumn).getType()); - } - - sh.setQueryString(query.toString()); - return sh; - } - - /** - * Generates sorting rules as an ORDER BY -clause - * - * @param sb - * StringBuffer to which the clause is appended. - * @param o - * OrderBy object to be added into the sb. - * @param firstOrderBy - * If true, this is the first OrderBy. - * @return - */ - protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o, - boolean firstOrderBy) { - if (firstOrderBy) { - sb.append(" ORDER BY "); - } else { - sb.append(", "); - } - sb.append(QueryBuilder.quote(o.getColumn())); - if (o.isAscending()) { - sb.append(" ASC"); - } else { - sb.append(" DESC"); - } - return sb; - } - - /** - * Generates the LIMIT and OFFSET clause. - * - * @param sb - * StringBuffer to which the clause is appended. - * @param offset - * Value for offset. - * @param pagelength - * Value for pagelength. - * @return StringBuffer with LIMIT and OFFSET clause added. - */ - protected StringBuffer generateLimits(StringBuffer sb, int offset, - int pagelength) { - sb.append(" LIMIT ").append(pagelength).append(" OFFSET ") - .append(offset); - return sb; - } - - protected Map generateColumnToValueMap(RowItem item) { - Map columnToValueMap = new HashMap(); - for (Object id : item.getItemPropertyIds()) { - ColumnProperty cp = (ColumnProperty) item.getItemProperty(id); - /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */ - if ((this instanceof MSSQLGenerator - || this instanceof OracleGenerator) - && cp.getPropertyId().equalsIgnoreCase("rownum")) { - continue; - } - if (cp.isPersistent()) { - columnToValueMap.put(cp.getPropertyId(), cp.getValue()); - } - } - return columnToValueMap; - } - - protected Map generateRowIdentifiers(RowItem item) { - Map rowIdentifiers = new HashMap(); - for (Object id : item.getItemPropertyIds()) { - ColumnProperty cp = (ColumnProperty) item.getItemProperty(id); - /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */ - if ((this instanceof MSSQLGenerator - || this instanceof OracleGenerator) - && cp.getPropertyId().equalsIgnoreCase("rownum")) { - continue; - } - - if (cp.isRowIdentifier()) { - Object value; - if (cp.isPrimaryKey()) { - // If the value of a primary key has changed, its old value - // should be used to identify the row (#9145) - value = cp.getOldValue(); - } else { - value = cp.getValue(); - } - rowIdentifiers.put(cp.getPropertyId(), value); - } - } - return rowIdentifiers; - } - - /** - * Returns the statement helper for the generator. Override this to handle - * platform specific data types. - * - * @see http://dev.vaadin.com/ticket/9148 - * @return a new instance of the statement helper - */ - protected StatementHelper getStatementHelper() { - if (statementHelperClass == null) { - return new StatementHelper(); - } - - try { - return statementHelperClass.newInstance(); - } catch (InstantiationException e) { - throw new RuntimeException( - "Unable to instantiate custom StatementHelper", e); - } catch (IllegalAccessException e) { - throw new RuntimeException( - "Unable to instantiate custom StatementHelper", e); - } - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/MSSQLGenerator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/MSSQLGenerator.java deleted file mode 100644 index baddc60e3f..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/MSSQLGenerator.java +++ /dev/null @@ -1,115 +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.sqlcontainer.query.generator; - -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; - -@SuppressWarnings("serial") -public class MSSQLGenerator extends DefaultSQLGenerator { - - public MSSQLGenerator() { - - } - - /** - * Construct a MSSQLGenerator with the specified identifiers for start and - * end of quoted strings. The identifiers may be different depending on the - * database engine and it's settings. - * - * @param quoteStart - * the identifier (character) denoting the start of a quoted - * string - * @param quoteEnd - * the identifier (character) denoting the end of a quoted string - */ - public MSSQLGenerator(String quoteStart, String quoteEnd) { - super(quoteStart, quoteEnd); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator# - * generateSelectQuery(java.lang.String, java.util.List, - * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int, - * int, java.lang.String) - */ - @Override - public StatementHelper generateSelectQuery(String tableName, - List filters, List orderBys, int offset, - int pagelength, String toSelect) { - if (tableName == null || tableName.trim().equals("")) { - throw new IllegalArgumentException("Table name must be given."); - } - /* Adjust offset and page length parameters to match "row numbers" */ - offset = pagelength > 1 ? ++offset : offset; - pagelength = pagelength > 1 ? --pagelength : pagelength; - toSelect = toSelect == null ? "*" : toSelect; - StatementHelper sh = getStatementHelper(); - StringBuffer query = new StringBuffer(); - - /* Row count request is handled here */ - if ("COUNT(*)".equalsIgnoreCase(toSelect)) { - query.append(String.format( - "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s", - QueryBuilder.quote("rowcount"), tableName)); - if (filters != null && !filters.isEmpty()) { - query.append( - QueryBuilder.getWhereStringForFilters(filters, sh)); - } - query.append(") AS t"); - sh.setQueryString(query.toString()); - return sh; - } - - /* SELECT without row number constraints */ - if (offset == 0 && pagelength == 0) { - query.append("SELECT ").append(toSelect).append(" FROM ") - .append(tableName); - if (filters != null) { - query.append( - QueryBuilder.getWhereStringForFilters(filters, sh)); - } - if (orderBys != null) { - for (OrderBy o : orderBys) { - generateOrderBy(query, o, orderBys.indexOf(o) == 0); - } - } - sh.setQueryString(query.toString()); - return sh; - } - - /* Remaining SELECT cases are handled here */ - query.append("SELECT * FROM (SELECT row_number() OVER ("); - if (orderBys != null) { - for (OrderBy o : orderBys) { - generateOrderBy(query, o, orderBys.indexOf(o) == 0); - } - } - query.append(") AS rownum, " + toSelect + " FROM ").append(tableName); - if (filters != null) { - query.append(QueryBuilder.getWhereStringForFilters(filters, sh)); - } - query.append(") AS a WHERE a.rownum BETWEEN ").append(offset) - .append(" AND ").append(Integer.toString(offset + pagelength)); - sh.setQueryString(query.toString()); - return sh; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/OracleGenerator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/OracleGenerator.java deleted file mode 100644 index 0097b5017c..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/OracleGenerator.java +++ /dev/null @@ -1,127 +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.sqlcontainer.query.generator; - -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; - -@SuppressWarnings("serial") -public class OracleGenerator extends DefaultSQLGenerator { - - public OracleGenerator() { - - } - - public OracleGenerator( - Class statementHelperClazz) { - super(statementHelperClazz); - } - - /** - * Construct an OracleSQLGenerator with the specified identifiers for start - * and end of quoted strings. The identifiers may be different depending on - * the database engine and it's settings. - * - * @param quoteStart - * the identifier (character) denoting the start of a quoted - * string - * @param quoteEnd - * the identifier (character) denoting the end of a quoted string - */ - public OracleGenerator(String quoteStart, String quoteEnd) { - super(quoteStart, quoteEnd); - } - - public OracleGenerator(String quoteStart, String quoteEnd, - Class statementHelperClazz) { - super(quoteStart, quoteEnd, statementHelperClazz); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator# - * generateSelectQuery(java.lang.String, java.util.List, - * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int, - * int, java.lang.String) - */ - @Override - public StatementHelper generateSelectQuery(String tableName, - List filters, List orderBys, int offset, - int pagelength, String toSelect) { - if (tableName == null || tableName.trim().equals("")) { - throw new IllegalArgumentException("Table name must be given."); - } - /* Adjust offset and page length parameters to match "row numbers" */ - offset = pagelength > 1 ? ++offset : offset; - pagelength = pagelength > 1 ? --pagelength : pagelength; - toSelect = toSelect == null ? "*" : toSelect; - StatementHelper sh = getStatementHelper(); - StringBuffer query = new StringBuffer(); - - /* Row count request is handled here */ - if ("COUNT(*)".equalsIgnoreCase(toSelect)) { - query.append(String.format( - "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s", - QueryBuilder.quote("rowcount"), tableName)); - if (filters != null && !filters.isEmpty()) { - query.append( - QueryBuilder.getWhereStringForFilters(filters, sh)); - } - query.append(")"); - sh.setQueryString(query.toString()); - return sh; - } - - /* SELECT without row number constraints */ - if (offset == 0 && pagelength == 0) { - query.append("SELECT ").append(toSelect).append(" FROM ") - .append(tableName); - if (filters != null) { - query.append( - QueryBuilder.getWhereStringForFilters(filters, sh)); - } - if (orderBys != null) { - for (OrderBy o : orderBys) { - generateOrderBy(query, o, orderBys.indexOf(o) == 0); - } - } - sh.setQueryString(query.toString()); - return sh; - } - - /* Remaining SELECT cases are handled here */ - query.append(String.format( - "SELECT * FROM (SELECT x.*, ROWNUM AS %s FROM (SELECT %s FROM %s", - QueryBuilder.quote("rownum"), toSelect, tableName)); - if (filters != null) { - query.append(QueryBuilder.getWhereStringForFilters(filters, sh)); - } - if (orderBys != null) { - for (OrderBy o : orderBys) { - generateOrderBy(query, o, orderBys.indexOf(o) == 0); - } - } - query.append(String.format(") x) WHERE %s BETWEEN %d AND %d", - QueryBuilder.quote("rownum"), offset, offset + pagelength)); - sh.setQueryString(query.toString()); - return sh; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/SQLGenerator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/SQLGenerator.java deleted file mode 100644 index 6011346b78..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/SQLGenerator.java +++ /dev/null @@ -1,100 +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.sqlcontainer.query.generator; - -import java.io.Serializable; -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.RowItem; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; - -/** - * The SQLGenerator interface is meant to be implemented for each different SQL - * syntax that is to be supported. By default there are implementations for - * HSQLDB, MySQL, PostgreSQL, MSSQL and Oracle syntaxes. - * - * @author Jonatan Kronqvist / Vaadin Ltd - */ -public interface SQLGenerator extends Serializable { - /** - * Generates a SELECT query with the provided parameters. Uses default - * filtering mode (INCLUSIVE). - * - * @param tableName - * Name of the table queried - * @param filters - * The filters, converted into a WHERE clause - * @param orderBys - * The the ordering conditions, converted into an ORDER BY clause - * @param offset - * The offset of the first row to be included - * @param pagelength - * The number of rows to be returned when the query executes - * @param toSelect - * String containing what to select, e.g. "*", "COUNT(*)" - * @return StatementHelper instance containing the query string for a - * PreparedStatement and the values required for the parameters - */ - public StatementHelper generateSelectQuery(String tableName, - List filters, List orderBys, int offset, - int pagelength, String toSelect); - - /** - * Generates an UPDATE query with the provided parameters. - * - * @param tableName - * Name of the table queried - * @param item - * RowItem containing the updated values update. - * @return StatementHelper instance containing the query string for a - * PreparedStatement and the values required for the parameters - */ - public StatementHelper generateUpdateQuery(String tableName, RowItem item); - - /** - * Generates an INSERT query for inserting a new row with the provided - * values. - * - * @param tableName - * Name of the table queried - * @param item - * New RowItem to be inserted into the database. - * @return StatementHelper instance containing the query string for a - * PreparedStatement and the values required for the parameters - */ - public StatementHelper generateInsertQuery(String tableName, RowItem item); - - /** - * Generates a DELETE query for deleting data related to the given RowItem - * from the database. - * - * @param tableName - * Name of the table queried - * @param primaryKeyColumns - * the names of the columns holding the primary key. Usually just - * one column, but might be several. - * @param versionColumn - * the column containing the version number of the row, null if - * versioning (optimistic locking) not enabled. - * @param item - * Item to be deleted from the database - * @return StatementHelper instance containing the query string for a - * PreparedStatement and the values required for the parameters - */ - public StatementHelper generateDeleteQuery(String tableName, - List primaryKeyColumns, String versionColumn, RowItem item); -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/StatementHelper.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/StatementHelper.java deleted file mode 100644 index 43cfb597bb..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/StatementHelper.java +++ /dev/null @@ -1,179 +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.sqlcontainer.query.generator; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.sql.Date; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; -import java.sql.Types; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * StatementHelper is a simple helper class that assists TableQuery and the - * query generators in filling a PreparedStatement. The actual statement is - * generated by the query generator methods, but the resulting statement and all - * the parameter values are stored in an instance of StatementHelper. - * - * This class will also fill the values with correct setters into the - * PreparedStatement on request. - */ -public class StatementHelper implements Serializable { - - private String queryString; - - private List parameters = new ArrayList(); - private Map> dataTypes = new HashMap>(); - - public StatementHelper() { - } - - public void setQueryString(String queryString) { - this.queryString = queryString; - } - - public String getQueryString() { - return queryString; - } - - public void addParameterValue(Object parameter) { - if (parameter != null) { - parameters.add(parameter); - dataTypes.put(parameters.size() - 1, parameter.getClass()); - } else { - throw new IllegalArgumentException( - "You cannot add null parameters using addParamaters(Object). " - + "Use addParameters(Object,Class) instead"); - } - } - - public void addParameterValue(Object parameter, Class type) { - parameters.add(parameter); - dataTypes.put(parameters.size() - 1, type); - } - - public void setParameterValuesToStatement(PreparedStatement pstmt) - throws SQLException { - for (int i = 0; i < parameters.size(); i++) { - if (parameters.get(i) == null) { - handleNullValue(i, pstmt); - } else { - pstmt.setObject(i + 1, parameters.get(i)); - } - } - - /* - * The following list contains the data types supported by - * PreparedStatement but not supported by SQLContainer: - * - * [The list is provided as PreparedStatement method signatures] - * - * setNCharacterStream(int parameterIndex, Reader value) - * - * setNClob(int parameterIndex, NClob value) - * - * setNString(int parameterIndex, String value) - * - * setRef(int parameterIndex, Ref x) - * - * setRowId(int parameterIndex, RowId x) - * - * setSQLXML(int parameterIndex, SQLXML xmlObject) - * - * setBytes(int parameterIndex, byte[] x) - * - * setCharacterStream(int parameterIndex, Reader reader) - * - * setClob(int parameterIndex, Clob x) - * - * setURL(int parameterIndex, URL x) - * - * setArray(int parameterIndex, Array x) - * - * setAsciiStream(int parameterIndex, InputStream x) - * - * setBinaryStream(int parameterIndex, InputStream x) - * - * setBlob(int parameterIndex, Blob x) - */ - } - - private void handleNullValue(int i, PreparedStatement pstmt) - throws SQLException { - Class dataType = dataTypes.get(i); - int index = i + 1; - if (BigDecimal.class.equals(dataType)) { - pstmt.setBigDecimal(index, null); - } else if (Boolean.class.equals(dataType)) { - pstmt.setNull(index, Types.BOOLEAN); - } else if (Byte.class.equals(dataType)) { - pstmt.setNull(index, Types.SMALLINT); - } else if (Date.class.equals(dataType)) { - pstmt.setDate(index, null); - } else if (Double.class.equals(dataType)) { - pstmt.setNull(index, Types.DOUBLE); - } else if (Float.class.equals(dataType)) { - pstmt.setNull(index, Types.FLOAT); - } else if (Integer.class.equals(dataType)) { - pstmt.setNull(index, Types.INTEGER); - } else if (Long.class.equals(dataType)) { - pstmt.setNull(index, Types.BIGINT); - } else if (Short.class.equals(dataType)) { - pstmt.setNull(index, Types.SMALLINT); - } else if (String.class.equals(dataType)) { - pstmt.setString(index, null); - } else if (Time.class.equals(dataType)) { - pstmt.setTime(index, null); - } else if (Timestamp.class.equals(dataType)) { - pstmt.setTimestamp(index, null); - } else if (byte[].class.equals(dataType)) { - pstmt.setBytes(index, null); - } else { - - if (handleUnrecognizedTypeNullValue(i, pstmt, dataTypes)) { - return; - } - - throw new SQLException("Data type for parameter " + i - + " not supported by SQLContainer: " + dataType.getName()); - } - } - - /** - * Handle unrecognized null values. Override this to handle null values for - * platform specific data types that are not handled by the default - * implementation of the {@link StatementHelper}. - * - * @param i - * @param pstmt - * @param dataTypes2 - * - * @return true if handled, false otherwise - * - * @see {@link http://dev.vaadin.com/ticket/9148} - */ - protected boolean handleUnrecognizedTypeNullValue(int i, - PreparedStatement pstmt, Map> dataTypes) - throws SQLException { - return false; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/AndTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/AndTranslator.java deleted file mode 100644 index 6eab9f5596..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/AndTranslator.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.data.util.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.And; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class AndTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof And; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - return QueryBuilder.group(QueryBuilder - .getJoinedFilterString(((And) filter).getFilters(), "AND", sh)); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java deleted file mode 100644 index 2cdecd1e6d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.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.data.util.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Between; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class BetweenTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof Between; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - Between between = (Between) filter; - sh.addParameterValue(between.getStartValue()); - sh.addParameterValue(between.getEndValue()); - return QueryBuilder.quote(between.getPropertyId()) + " BETWEEN ? AND ?"; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java deleted file mode 100644 index bcb348dc8a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/CompareTranslator.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.data.util.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Compare; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class CompareTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof Compare; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - Compare compare = (Compare) filter; - sh.addParameterValue(compare.getValue()); - String prop = QueryBuilder.quote(compare.getPropertyId()); - switch (compare.getOperation()) { - case EQUAL: - return prop + " = ?"; - case GREATER: - return prop + " > ?"; - case GREATER_OR_EQUAL: - return prop + " >= ?"; - case LESS: - return prop + " < ?"; - case LESS_OR_EQUAL: - return prop + " <= ?"; - default: - return ""; - } - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java deleted file mode 100644 index e593146550..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java +++ /dev/null @@ -1,28 +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.sqlcontainer.query.generator.filter; - -import java.io.Serializable; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public interface FilterTranslator extends Serializable { - public boolean translatesFilter(Filter filter); - - public String getWhereStringForFilter(Filter filter, StatementHelper sh); - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java deleted file mode 100644 index dd7a90828a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.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.data.util.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.IsNull; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class IsNullTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof IsNull; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - IsNull in = (IsNull) filter; - return QueryBuilder.quote(in.getPropertyId()) + " IS NULL"; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java deleted file mode 100644 index 3c27240e9e..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java +++ /dev/null @@ -1,42 +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.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class LikeTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof Like; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - Like like = (Like) filter; - if (like.isCaseSensitive()) { - sh.addParameterValue(like.getValue()); - return QueryBuilder.quote(like.getPropertyId()) + " LIKE ?"; - } else { - sh.addParameterValue(like.getValue().toUpperCase()); - return "UPPER(" + QueryBuilder.quote(like.getPropertyId()) - + ") LIKE ?"; - } - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/NotTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/NotTranslator.java deleted file mode 100644 index fe98ca24b6..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/NotTranslator.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.data.util.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.IsNull; -import com.vaadin.data.util.filter.Not; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class NotTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof Not; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - Not not = (Not) filter; - if (not.getFilter() instanceof IsNull) { - IsNull in = (IsNull) not.getFilter(); - return QueryBuilder.quote(in.getPropertyId()) + " IS NOT NULL"; - } - return "NOT " - + QueryBuilder.getWhereStringForFilter(not.getFilter(), sh); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/OrTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/OrTranslator.java deleted file mode 100644 index 2f30acc89f..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/OrTranslator.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.data.util.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Or; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class OrTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof Or; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - return QueryBuilder.group(QueryBuilder - .getJoinedFilterString(((Or) filter).getFilters(), "OR", sh)); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java deleted file mode 100644 index b8fb306076..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java +++ /dev/null @@ -1,110 +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.sqlcontainer.query.generator.filter; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class QueryBuilder implements Serializable { - - private static ArrayList filterTranslators = new ArrayList(); - private static StringDecorator stringDecorator = new StringDecorator("\"", - "\""); - - static { - /* Register all default filter translators */ - addFilterTranslator(new AndTranslator()); - addFilterTranslator(new OrTranslator()); - addFilterTranslator(new LikeTranslator()); - addFilterTranslator(new BetweenTranslator()); - addFilterTranslator(new CompareTranslator()); - addFilterTranslator(new NotTranslator()); - addFilterTranslator(new IsNullTranslator()); - addFilterTranslator(new SimpleStringTranslator()); - } - - public synchronized static void addFilterTranslator( - FilterTranslator translator) { - filterTranslators.add(translator); - } - - /** - * Allows specification of a custom ColumnQuoter instance that handles - * quoting of column names for the current DB dialect. - * - * @param decorator - * the ColumnQuoter instance to use. - */ - public static void setStringDecorator(StringDecorator decorator) { - stringDecorator = decorator; - } - - public static String quote(Object str) { - return stringDecorator.quote(str); - } - - public static String group(String str) { - return stringDecorator.group(str); - } - - /** - * Constructs and returns a string representing the filter that can be used - * in a WHERE clause. - * - * @param filter - * the filter to translate - * @param sh - * the statement helper to update with the value(s) of the filter - * @return a string representing the filter. - */ - public synchronized static String getWhereStringForFilter(Filter filter, - StatementHelper sh) { - for (FilterTranslator ft : filterTranslators) { - if (ft.translatesFilter(filter)) { - return ft.getWhereStringForFilter(filter, sh); - } - } - return ""; - } - - public static String getJoinedFilterString(Collection filters, - String joinString, StatementHelper sh) { - StringBuilder result = new StringBuilder(); - for (Filter f : filters) { - result.append(getWhereStringForFilter(f, sh)); - result.append(" ").append(joinString).append(" "); - } - // Remove the last instance of joinString - result.delete(result.length() - joinString.length() - 2, - result.length()); - return result.toString(); - } - - public static String getWhereStringForFilters(List filters, - StatementHelper sh) { - if (filters == null || filters.isEmpty()) { - return ""; - } - StringBuilder where = new StringBuilder(" WHERE "); - where.append(getJoinedFilterString(filters, "AND", sh)); - return where.toString(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java deleted file mode 100644 index 312adc5ed7..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java +++ /dev/null @@ -1,42 +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.sqlcontainer.query.generator.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.filter.SimpleStringFilter; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class SimpleStringTranslator implements FilterTranslator { - - @Override - public boolean translatesFilter(Filter filter) { - return filter instanceof SimpleStringFilter; - } - - @Override - public String getWhereStringForFilter(Filter filter, StatementHelper sh) { - SimpleStringFilter ssf = (SimpleStringFilter) filter; - // Create a Like filter based on the SimpleStringFilter and execute the - // LikeTranslator - String likeStr = ssf.isOnlyMatchPrefix() ? ssf.getFilterString() + "%" - : "%" + ssf.getFilterString() + "%"; - Like like = new Like(ssf.getPropertyId().toString(), likeStr); - like.setCaseSensitive(!ssf.isIgnoreCase()); - return new LikeTranslator().getWhereStringForFilter(like, sh); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/StringDecorator.java b/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/StringDecorator.java deleted file mode 100644 index f8005f4290..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/data/util/sqlcontainer/query/generator/filter/StringDecorator.java +++ /dev/null @@ -1,70 +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.sqlcontainer.query.generator.filter; - -import java.io.Serializable; - -/** - * The StringDecorator knows how to produce a quoted string using the specified - * quote start and quote end characters. It also handles grouping of a string - * (surrounding it in parenthesis). - * - * Extend this class if you need to support special characters for grouping - * (parenthesis). - * - * @author Vaadin Ltd - */ -public class StringDecorator implements Serializable { - - private final String quoteStart; - private final String quoteEnd; - - /** - * Constructs a StringDecorator that uses the quoteStart and quoteEnd - * characters to create quoted strings. - * - * @param quoteStart - * the character denoting the start of a quote. - * @param quoteEnd - * the character denoting the end of a quote. - */ - public StringDecorator(String quoteStart, String quoteEnd) { - this.quoteStart = quoteStart; - this.quoteEnd = quoteEnd; - } - - /** - * Surround a string with quote characters. - * - * @param str - * the string to quote - * @return the quoted string - */ - public String quote(Object str) { - return quoteStart + str + quoteEnd; - } - - /** - * Groups a string by surrounding it in parenthesis - * - * @param str - * the string to group - * @return the grouped string - */ - public String group(String str) { - return "(" + str + ")"; - } -} diff --git a/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 deleted file mode 100644 index f7459a7dd3..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 341c3f1a45..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/AbstractColorPicker.java b/compatibility-server/src/main/java/com/vaadin/ui/AbstractColorPicker.java deleted file mode 100644 index d7d12c6d03..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/AbstractColorPicker.java +++ /dev/null @@ -1,588 +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.util.Collection; - -import org.jsoup.nodes.Attributes; -import org.jsoup.nodes.Element; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc; -import com.vaadin.shared.ui.colorpicker.ColorPickerState; -import com.vaadin.ui.Window.CloseEvent; -import com.vaadin.ui.Window.CloseListener; -import com.vaadin.ui.components.colorpicker.ColorChangeEvent; -import com.vaadin.ui.components.colorpicker.ColorChangeListener; -import com.vaadin.ui.components.colorpicker.ColorPickerPopup; -import com.vaadin.ui.components.colorpicker.ColorSelector; -import com.vaadin.ui.declarative.DesignAttributeHandler; -import com.vaadin.ui.declarative.DesignContext; - -/** - * An abstract class that defines default implementation for a color picker - * component. - * - * @since 7.0.0 - */ -public abstract class AbstractColorPicker extends AbstractComponent - implements CloseListener, ColorSelector { - private static final Method COLOR_CHANGE_METHOD; - static { - try { - COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( - "colorChanged", new Class[] { ColorChangeEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in ColorPicker"); - } - } - - /** - * Interface for converting 2d-coordinates to a Color - */ - public interface Coordinates2Color extends Serializable { - - /** - * Calculate color from coordinates - * - * @param x - * the x-coordinate - * @param y - * the y-coordinate - * - * @return the color - */ - public Color calculate(int x, int y); - - /** - * Calculate coordinates from color - * - * @param c - * the c - * - * @return the integer array with the coordinates - */ - public int[] calculate(Color c); - } - - public enum PopupStyle { - POPUP_NORMAL("normal"), POPUP_SIMPLE("simple"); - - private String style; - - PopupStyle(String styleName) { - style = styleName; - } - - @Override - public String toString() { - return style; - } - } - - private ColorPickerServerRpc rpc = new ColorPickerServerRpc() { - - @Override - public void openPopup(boolean open) { - showPopup(open); - } - }; - - protected static final String STYLENAME_DEFAULT = "v-colorpicker"; - protected static final String STYLENAME_BUTTON = "v-button"; - protected static final String STYLENAME_AREA = "v-colorpicker-area"; - - protected PopupStyle popupStyle = PopupStyle.POPUP_NORMAL; - - /** The popup window. */ - private ColorPickerPopup window; - - /** The color. */ - protected Color color; - - /** The UI. */ - private UI parent; - - protected String popupCaption = null; - private int positionX = 0; - private int positionY = 0; - - protected boolean rgbVisible = true; - protected boolean hsvVisible = true; - protected boolean swatchesVisible = true; - protected boolean historyVisible = true; - protected boolean textfieldVisible = true; - - /** - * Instantiates a new color picker. - */ - public AbstractColorPicker() { - this("Colors", Color.WHITE); - } - - /** - * Instantiates a new color picker. - * - * @param popupCaption - * the caption of the popup window - */ - public AbstractColorPicker(String popupCaption) { - this(popupCaption, Color.WHITE); - } - - /** - * Instantiates a new color picker. - * - * @param popupCaption - * the caption of the popup window - * @param initialColor - * the initial color - */ - public AbstractColorPicker(String popupCaption, Color initialColor) { - super(); - registerRpc(rpc); - setColor(initialColor); - this.popupCaption = popupCaption; - setDefaultStyles(); - setCaption(""); - } - - @Override - public void setColor(Color color) { - this.color = color; - - if (window != null) { - window.setColor(color); - } - getState().color = color.getCSS(); - } - - @Override - public Color getColor() { - return color; - } - - /** - * Set true if the component should show a default caption (css-code for the - * currently selected color, e.g. #ffffff) when no other caption is - * available. - * - * @param enabled - */ - public void setDefaultCaptionEnabled(boolean enabled) { - getState().showDefaultCaption = enabled; - } - - /** - * Returns true if the component shows the default caption (css-code for the - * currently selected color, e.g. #ffffff) if no other caption is available. - */ - public boolean isDefaultCaptionEnabled() { - return getState(false).showDefaultCaption; - } - - /** - * Sets the position of the popup window - * - * @param x - * the x-coordinate - * @param y - * the y-coordinate - */ - public void setPosition(int x, int y) { - positionX = x; - positionY = y; - - if (window != null) { - window.setPositionX(x); - window.setPositionY(y); - } - } - - @Override - public void addColorChangeListener(ColorChangeListener listener) { - addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); - } - - @Override - public void removeColorChangeListener(ColorChangeListener listener) { - removeListener(ColorChangeEvent.class, listener); - } - - @Override - public void windowClose(CloseEvent e) { - if (e.getWindow() == window) { - getState().popupVisible = false; - } - } - - /** - * Fired when a color change event occurs - * - * @param event - * The color change event - */ - protected void colorChanged(ColorChangeEvent event) { - setColor(event.getColor()); - fireColorChanged(); - } - - /** - * Notifies the listeners that the selected color has changed - */ - public void fireColorChanged() { - fireEvent(new ColorChangeEvent(this, color)); - } - - /** - * The style for the popup window - * - * @param style - * The style - */ - public void setPopupStyle(PopupStyle style) { - popupStyle = style; - - switch (style) { - case POPUP_NORMAL: { - setRGBVisibility(true); - setHSVVisibility(true); - setSwatchesVisibility(true); - setHistoryVisibility(true); - setTextfieldVisibility(true); - break; - } - - case POPUP_SIMPLE: { - setRGBVisibility(false); - setHSVVisibility(false); - setSwatchesVisibility(true); - setHistoryVisibility(false); - setTextfieldVisibility(false); - break; - } - } - } - - /** - * Gets the style for the popup window - * - * @since 7.5.0 - * @return popup window style - */ - public PopupStyle getPopupStyle() { - return popupStyle; - } - - /** - * Set the visibility of the RGB Tab - * - * @param visible - * The visibility - */ - public void setRGBVisibility(boolean visible) { - - if (!visible && !hsvVisible && !swatchesVisible) { - throw new IllegalArgumentException("Cannot hide all tabs."); - } - - rgbVisible = visible; - if (window != null) { - window.setRGBTabVisible(visible); - } - } - - /** - * Gets the visibility of the RGB Tab - * - * @since 7.5.0 - * @return visibility of the RGB tab - */ - public boolean getRGBVisibility() { - return rgbVisible; - } - - /** - * Set the visibility of the HSV Tab - * - * @param visible - * The visibility - */ - public void setHSVVisibility(boolean visible) { - if (!visible && !rgbVisible && !swatchesVisible) { - throw new IllegalArgumentException("Cannot hide all tabs."); - } - - hsvVisible = visible; - if (window != null) { - window.setHSVTabVisible(visible); - } - } - - /** - * Gets the visibility of the HSV Tab - * - * @since 7.5.0 - * @return visibility of the HSV tab - */ - public boolean getHSVVisibility() { - return hsvVisible; - } - - /** - * Set the visibility of the Swatches Tab - * - * @param visible - * The visibility - */ - public void setSwatchesVisibility(boolean visible) { - if (!visible && !hsvVisible && !rgbVisible) { - throw new IllegalArgumentException("Cannot hide all tabs."); - } - - swatchesVisible = visible; - if (window != null) { - window.setSwatchesTabVisible(visible); - } - } - - /** - * Gets the visibility of the Swatches Tab - * - * @since 7.5.0 - * @return visibility of the swatches tab - */ - public boolean getSwatchesVisibility() { - return swatchesVisible; - } - - /** - * Sets the visibility of the Color History - * - * @param visible - * The visibility - */ - public void setHistoryVisibility(boolean visible) { - historyVisible = visible; - if (window != null) { - window.setHistoryVisible(visible); - } - } - - /** - * Gets the visibility of the Color History - * - * @since 7.5.0 - * @return visibility of color history - */ - public boolean getHistoryVisibility() { - return historyVisible; - } - - /** - * Sets the visibility of the CSS color code text field - * - * @param visible - * The visibility - */ - public void setTextfieldVisibility(boolean visible) { - textfieldVisible = visible; - if (window != null) { - window.setPreviewVisible(visible); - } - } - - /** - * Gets the visibility of CSS color code text field - * - * @since 7.5.0 - * @return visibility of css color code text field - */ - public boolean getTextfieldVisibility() { - return textfieldVisible; - } - - @Override - protected ColorPickerState getState() { - return (ColorPickerState) super.getState(); - } - - @Override - protected ColorPickerState getState(boolean markAsDirty) { - return (ColorPickerState) super.getState(markAsDirty); - } - - /** - * Sets the default styles of the component - * - */ - abstract protected void setDefaultStyles(); - - /** - * Shows a popup-window for color selection. - */ - public void showPopup() { - showPopup(true); - } - - /** - * Hides a popup-window for color selection. - */ - public void hidePopup() { - showPopup(false); - } - - /** - * Shows or hides popup-window depending on the given parameter. If there is - * no such window yet, one is created. - * - * @param open - */ - protected void showPopup(boolean open) { - if (open && !isReadOnly()) { - if (parent == null) { - parent = getUI(); - } - - if (window == null) { - - // Create the popup - window = new ColorPickerPopup(color); - window.setCaption(popupCaption); - - window.setRGBTabVisible(rgbVisible); - window.setHSVTabVisible(hsvVisible); - window.setSwatchesTabVisible(swatchesVisible); - window.setHistoryVisible(historyVisible); - window.setPreviewVisible(textfieldVisible); - - window.setImmediate(true); - window.addCloseListener(this); - window.addColorChangeListener(new ColorChangeListener() { - @Override - public void colorChanged(ColorChangeEvent event) { - AbstractColorPicker.this.colorChanged(event); - } - }); - - window.getHistory().setColor(color); - parent.addWindow(window); - window.setVisible(true); - window.setPositionX(positionX); - window.setPositionY(positionY); - - } else if (!parent.equals(window.getParent())) { - - window.setRGBTabVisible(rgbVisible); - window.setHSVTabVisible(hsvVisible); - window.setSwatchesTabVisible(swatchesVisible); - window.setHistoryVisible(historyVisible); - window.setPreviewVisible(textfieldVisible); - - window.setColor(color); - window.getHistory().setColor(color); - window.setVisible(true); - parent.addWindow(window); - } - - } else if (window != null) { - window.setVisible(false); - parent.removeWindow(window); - } - getState().popupVisible = open; - } - - /** - * Set whether the caption text is rendered as HTML or not. You might need - * to re-theme component to allow higher content than the original text - * style. - * - * If set to true, the captions are passed to the browser as html and the - * developer is responsible for ensuring no harmful html is used. If set to - * false, the content is passed to the browser as plain text. - * - * @param htmlContentAllowed - * true if caption is rendered as HTML, - * false otherwise - * @deprecated as of , use {@link #setCaptionAsHtml(boolean)} instead - */ - @Deprecated - public void setHtmlContentAllowed(boolean htmlContentAllowed) { - setCaptionAsHtml(htmlContentAllowed); - } - - /** - * Return HTML rendering setting - * - * @return true if the caption text is to be rendered as HTML, - * false otherwise - * @deprecated as of , use {@link #isCaptionAsHtml()} instead - */ - @Deprecated - public boolean isHtmlContentAllowed() { - return isCaptionAsHtml(); - } - - @Override - public void readDesign(Element design, DesignContext designContext) { - super.readDesign(design, designContext); - - Attributes attributes = design.attributes(); - if (design.hasAttr("color")) { - // Ignore the # character - String hexColor = DesignAttributeHandler - .readAttribute("color", attributes, String.class) - .substring(1); - setColor(new Color(Integer.parseInt(hexColor, 16))); - } - if (design.hasAttr("popup-style")) { - setPopupStyle(PopupStyle.valueOf( - "POPUP_" + attributes.get("popup-style").toUpperCase())); - } - if (design.hasAttr("position")) { - String[] position = attributes.get("position").split(","); - setPosition(Integer.parseInt(position[0]), - Integer.parseInt(position[1])); - } - } - - @Override - public void writeDesign(Element design, DesignContext designContext) { - super.writeDesign(design, designContext); - - Attributes attribute = design.attributes(); - DesignAttributeHandler.writeAttribute("color", attribute, - color.getCSS(), Color.WHITE.getCSS(), String.class); - DesignAttributeHandler.writeAttribute("popup-style", attribute, - (popupStyle == PopupStyle.POPUP_NORMAL ? "normal" : "simple"), - "normal", String.class); - DesignAttributeHandler.writeAttribute("position", attribute, - positionX + "," + positionY, "0,0", String.class); - } - - @Override - protected Collection getCustomAttributes() { - Collection result = super.getCustomAttributes(); - result.add("color"); - result.add("position"); - result.add("popup-style"); - return result; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/AbstractSelect.java b/compatibility-server/src/main/java/com/vaadin/ui/AbstractSelect.java deleted file mode 100644 index 5411a63c15..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/AbstractSelect.java +++ /dev/null @@ -1,2353 +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.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EventObject; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.jsoup.nodes.Element; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.DataBoundTransferable; -import com.vaadin.event.Transferable; -import com.vaadin.event.dd.DragAndDropEvent; -import com.vaadin.event.dd.DropTarget; -import com.vaadin.event.dd.TargetDetailsImpl; -import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion; -import com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor; -import com.vaadin.event.dd.acceptcriteria.TargetDetailIs; -import com.vaadin.server.KeyMapper; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.server.Resource; -import com.vaadin.server.VaadinSession; -import com.vaadin.shared.ui.combobox.FilteringMode; -import com.vaadin.shared.ui.dd.VerticalDropLocation; -import com.vaadin.shared.ui.select.AbstractSelectState; -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.v7.data.Validator.InvalidValueException; -import com.vaadin.v7.data.util.converter.LegacyConverter; -import com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException; -import com.vaadin.v7.data.util.converter.LegacyConverterUtil; -import com.vaadin.v7.ui.LegacyAbstractField; - -/** - *

    - * A class representing a selection of items the user has selected in a UI. The - * set of choices is presented as a set of {@link com.vaadin.data.Item}s in a - * {@link com.vaadin.data.Container}. - *

    - * - *

    - * A Select component may be in single- or multiselect mode. - * Multiselect mode means that more than one item can be selected - * simultaneously. - *

    - * - * @author Vaadin Ltd. - * @since 5.0 - */ -@SuppressWarnings("serial") -// TODO currently cannot specify type more precisely in case of multi-select -public abstract class AbstractSelect extends LegacyAbstractField - implements Container, Container.Viewer, - Container.PropertySetChangeListener, - Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier, - Container.ItemSetChangeListener, LegacyComponent { - - public enum ItemCaptionMode { - /** - * Item caption mode: Item's ID converted to a String using - * {@link VaadinSession#getConverterFactory()} is used as caption. - */ - ID, - /** - * Item caption mode: Item's ID's String representation is - * used as caption. - * - * @since 7.5.6 - */ - ID_TOSTRING, - /** - * Item caption mode: Item's String representation is used - * as caption. - */ - ITEM, - /** - * Item caption mode: Index of the item is used as caption. The index - * mode can only be used with the containers implementing the - * {@link com.vaadin.data.Container.Indexed} interface. - */ - INDEX, - /** - * Item caption mode: If an Item has a caption it's used, if not, Item's - * ID converted to a String using - * {@link VaadinSession#getConverterFactory()} is used as caption. - * This is the default. - */ - EXPLICIT_DEFAULTS_ID, - /** - * Item caption mode: Captions must be explicitly specified. - */ - EXPLICIT, - /** - * Item caption mode: Only icons are shown, captions are hidden. - */ - ICON_ONLY, - /** - * Item caption mode: Item captions are read from property specified - * with setItemCaptionPropertyId. - */ - PROPERTY; - } - - /** - * @deprecated As of 7.0, use {@link ItemCaptionMode#ID} instead - */ - @Deprecated - public static final ItemCaptionMode ITEM_CAPTION_MODE_ID = ItemCaptionMode.ID; - - /** - * @deprecated As of 7.0, use {@link ItemCaptionMode#ITEM} instead - */ - @Deprecated - public static final ItemCaptionMode ITEM_CAPTION_MODE_ITEM = ItemCaptionMode.ITEM; - - /** - * @deprecated As of 7.0, use {@link ItemCaptionMode#INDEX} instead - */ - @Deprecated - public static final ItemCaptionMode ITEM_CAPTION_MODE_INDEX = ItemCaptionMode.INDEX; - - /** - * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID} - * instead - */ - @Deprecated - public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = ItemCaptionMode.EXPLICIT_DEFAULTS_ID; - - /** - * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT} instead - */ - @Deprecated - public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT = ItemCaptionMode.EXPLICIT; - - /** - * @deprecated As of 7.0, use {@link ItemCaptionMode#ICON_ONLY} instead - */ - @Deprecated - public static final ItemCaptionMode ITEM_CAPTION_MODE_ICON_ONLY = ItemCaptionMode.ICON_ONLY; - - /** - * @deprecated As of 7.0, use {@link ItemCaptionMode#PROPERTY} instead - */ - @Deprecated - public static final ItemCaptionMode ITEM_CAPTION_MODE_PROPERTY = ItemCaptionMode.PROPERTY; - - /** - * Interface for option filtering, used to filter options based on user - * entered value. The value is matched to the item caption. - * FilteringMode.OFF (0) turns the filtering off. - * FilteringMode.STARTSWITH (1) matches from the start of the - * caption. FilteringMode.CONTAINS (1) matches anywhere in the - * caption. - */ - public interface Filtering extends Serializable { - - /** - * @deprecated As of 7.0, use {@link FilteringMode#OFF} instead - */ - @Deprecated - public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF; - /** - * @deprecated As of 7.0, use {@link FilteringMode#STARTSWITH} instead - */ - @Deprecated - public static final FilteringMode FILTERINGMODE_STARTSWITH = FilteringMode.STARTSWITH; - /** - * @deprecated As of 7.0, use {@link FilteringMode#CONTAINS} instead - */ - @Deprecated - public static final FilteringMode FILTERINGMODE_CONTAINS = FilteringMode.CONTAINS; - - /** - * Sets the option filtering mode. - * - * @param filteringMode - * the filtering mode to use - */ - public void setFilteringMode(FilteringMode filteringMode); - - /** - * Gets the current filtering mode. - * - * @return the filtering mode in use - */ - public FilteringMode getFilteringMode(); - - } - - /** - * Select options. - */ - protected Container items; - - /** - * Is the user allowed to add new options? - */ - private boolean allowNewOptions; - - /** - * Keymapper used to map key values. - */ - protected KeyMapper itemIdMapper = new KeyMapper(); - - /** - * Item icons. - */ - private final HashMap itemIcons = new HashMap(); - - /** - * Item captions. - */ - private final HashMap itemCaptions = new HashMap(); - - /** - * Item caption mode. - */ - private ItemCaptionMode itemCaptionMode = ItemCaptionMode.EXPLICIT_DEFAULTS_ID; - - /** - * Item caption source property id. - */ - private Object itemCaptionPropertyId = null; - - /** - * Item icon source property id. - */ - private Object itemIconPropertyId = null; - - /** - * List of property set change event listeners. - */ - private Set propertySetEventListeners = null; - - /** - * List of item set change event listeners. - */ - private Set itemSetEventListeners = null; - - /** - * Item id that represents null selection of this select. - * - *

    - * Data interface does not support nulls as item ids. Selecting the item - * identified by this id is the same as selecting no items at all. This - * setting only affects the single select mode. - *

    - */ - private Object nullSelectionItemId = null; - - // Null (empty) selection is enabled by default - private boolean nullSelectionAllowed = true; - private NewItemHandler newItemHandler; - - // Caption (Item / Property) change listeners - CaptionChangeListener captionChangeListener; - - /* Constructors */ - - /** - * Creates an empty Select. The caption is not used. - */ - public AbstractSelect() { - setContainerDataSource(new IndexedContainer()); - } - - /** - * Creates an empty Select with caption. - */ - public AbstractSelect(String caption) { - setContainerDataSource(new IndexedContainer()); - setCaption(caption); - } - - /** - * Creates a new select that is connected to a data-source. - * - * @param caption - * the Caption of the component. - * @param dataSource - * the Container datasource to be selected from by this select. - */ - public AbstractSelect(String caption, Container dataSource) { - setCaption(caption); - setContainerDataSource(dataSource); - } - - /** - * Creates a new select that is filled from a collection of option values. - * - * @param caption - * the Caption of this field. - * @param options - * the Collection containing the options. - */ - public AbstractSelect(String caption, Collection options) { - - // Creates the options container and add given options to it - final Container c = new IndexedContainer(); - if (options != null) { - for (final Iterator i = options.iterator(); i.hasNext();) { - c.addItem(i.next()); - } - } - - setCaption(caption); - setContainerDataSource(c); - } - - /* Component methods */ - - /** - * Paints the content of this component. - * - * @param target - * the Paint Event. - * @throws PaintException - * if the paint operation failed. - */ - @Override - public void paintContent(PaintTarget target) throws PaintException { - - // Paints select attributes - if (isNewItemsAllowed()) { - target.addAttribute("allownewitem", true); - } - if (isNullSelectionAllowed()) { - target.addAttribute("nullselect", true); - if (getNullSelectionItemId() != null) { - target.addAttribute("nullselectitem", true); - } - } - - // Constructs selected keys array - String[] selectedKeys; - if (isMultiSelect()) { - selectedKeys = new String[((Set) getValue()).size()]; - } else { - selectedKeys = new String[(getValue() == null - && getNullSelectionItemId() == null ? 0 : 1)]; - } - - // == - // first remove all previous item/property listeners - getCaptionChangeListener().clear(); - // Paints the options and create array of selected id keys - - target.startTag("options"); - int keyIndex = 0; - // Support for external null selection item id - final Collection ids = getItemIds(); - if (isNullSelectionAllowed() && getNullSelectionItemId() != null - && !ids.contains(getNullSelectionItemId())) { - final Object id = getNullSelectionItemId(); - // Paints option - target.startTag("so"); - paintItem(target, id); - if (isSelected(id)) { - selectedKeys[keyIndex++] = itemIdMapper.key(id); - } - target.endTag("so"); - } - - final Iterator i = getItemIds().iterator(); - // Paints the available selection options from data source - while (i.hasNext()) { - // Gets the option attribute values - final Object id = i.next(); - if (!isNullSelectionAllowed() && id != null - && id.equals(getNullSelectionItemId())) { - // Remove item if it's the null selection item but null - // selection is not allowed - continue; - } - final String key = itemIdMapper.key(id); - // add listener for each item, to cause repaint if an item changes - getCaptionChangeListener().addNotifierForItem(id); - target.startTag("so"); - paintItem(target, id); - if (isSelected(id) && keyIndex < selectedKeys.length) { - selectedKeys[keyIndex++] = key; - } - target.endTag("so"); - } - target.endTag("options"); - // == - - // Paint variables - target.addVariable(this, "selected", selectedKeys); - if (isNewItemsAllowed()) { - target.addVariable(this, "newitem", ""); - } - - } - - protected void paintItem(PaintTarget target, Object itemId) - throws PaintException { - final String key = itemIdMapper.key(itemId); - final String caption = getItemCaption(itemId); - final Resource icon = getItemIcon(itemId); - if (icon != null) { - target.addAttribute("icon", icon); - } - target.addAttribute("caption", caption); - if (itemId != null && itemId.equals(getNullSelectionItemId())) { - target.addAttribute("nullselection", true); - } - target.addAttribute("key", key); - if (isSelected(itemId)) { - target.addAttribute("selected", true); - } - } - - /** - * Invoked when the value of a variable has changed. - * - * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object, - * java.util.Map) - */ - @Override - public void changeVariables(Object source, Map variables) { - - // New option entered (and it is allowed) - if (isNewItemsAllowed()) { - final String newitem = (String) variables.get("newitem"); - if (newitem != null && newitem.length() > 0) { - getNewItemHandler().addNewItem(newitem); - } - } - - // Selection change - if (variables.containsKey("selected")) { - final String[] clientSideSelectedKeys = (String[]) variables - .get("selected"); - - // Multiselect mode - if (isMultiSelect()) { - - // TODO Optimize by adding repaintNotNeeded when applicable - - // Converts the key-array to id-set - final LinkedList acceptedSelections = new LinkedList(); - for (int i = 0; i < clientSideSelectedKeys.length; i++) { - final Object id = itemIdMapper - .get(clientSideSelectedKeys[i]); - if (!isNullSelectionAllowed() - && (id == null || id == getNullSelectionItemId())) { - // skip empty selection if nullselection is not allowed - markAsDirty(); - } else if (id != null && containsId(id)) { - acceptedSelections.add(id); - } - } - - if (!isNullSelectionAllowed() - && acceptedSelections.size() < 1) { - // empty selection not allowed, keep old value - markAsDirty(); - return; - } - - // Limits the deselection to the set of visible items - // (non-visible items can not be deselected) - Collection visibleNotSelected = getVisibleItemIds(); - if (visibleNotSelected != null) { - visibleNotSelected = new HashSet( - visibleNotSelected); - // Don't remove those that will be added to preserve order - visibleNotSelected.removeAll(acceptedSelections); - - @SuppressWarnings("unchecked") - Set newsel = (Set) getValue(); - if (newsel == null) { - newsel = new LinkedHashSet(); - } else { - newsel = new LinkedHashSet(newsel); - } - newsel.removeAll(visibleNotSelected); - newsel.addAll(acceptedSelections); - setValue(newsel, true); - } - } else { - // Single select mode - if (!isNullSelectionAllowed() - && (clientSideSelectedKeys.length == 0 - || clientSideSelectedKeys[0] == null - || clientSideSelectedKeys[0] == getNullSelectionItemId())) { - markAsDirty(); - return; - } - if (clientSideSelectedKeys.length == 0) { - // Allows deselection only if the deselected item is - // visible - final Object current = getValue(); - final Collection visible = getVisibleItemIds(); - if (visible != null && visible.contains(current)) { - setValue(null, true); - } - } else { - String clientSelectedKey = clientSideSelectedKeys[0]; - if ("null".equals(clientSelectedKey) - || itemIdMapper.containsKey(clientSelectedKey)) { - // Happens to work for nullselection - // (get ("null") -> null)) - final Object id = itemIdMapper.get(clientSelectedKey); - - if (!isNullSelectionAllowed() && id == null) { - markAsDirty(); - } else if (id != null - && id.equals(getNullSelectionItemId())) { - setValue(null, true); - } else { - setValue(id, true); - } - } - } - } - } - } - - /** - * TODO refine doc Setter for new item handler that is called when user adds - * new item in newItemAllowed mode. - * - * @param newItemHandler - */ - public void setNewItemHandler(NewItemHandler newItemHandler) { - this.newItemHandler = newItemHandler; - } - - /** - * TODO refine doc - * - * @return - */ - public NewItemHandler getNewItemHandler() { - if (newItemHandler == null) { - newItemHandler = new DefaultNewItemHandler(); - } - return newItemHandler; - } - - public interface NewItemHandler extends Serializable { - void addNewItem(String newItemCaption); - } - - /** - * TODO refine doc - * - * This is a default class that handles adding new items that are typed by - * user to selects container. - * - * By extending this class one may implement some logic on new item addition - * like database inserts. - * - */ - public class DefaultNewItemHandler implements NewItemHandler { - @Override - public void addNewItem(String newItemCaption) { - // Checks for readonly - if (isReadOnly()) { - throw new Property.ReadOnlyException(); - } - - // Adds new option - if (addItem(newItemCaption) != null) { - - // Sets the caption property, if used - if (getItemCaptionPropertyId() != null) { - getContainerProperty(newItemCaption, - getItemCaptionPropertyId()) - .setValue(newItemCaption); - } - if (isMultiSelect()) { - Set values = new HashSet((Collection) getValue()); - values.add(newItemCaption); - setValue(values); - } else { - setValue(newItemCaption); - } - } - } - } - - /** - * Gets the visible item ids. In Select, this returns list of all item ids, - * but can be overriden in subclasses if they paint only part of the items - * to the terminal or null if no items is visible. - */ - public Collection getVisibleItemIds() { - return getItemIds(); - } - - /* Property methods */ - - /** - * Returns the type of the property. getValue and - * setValue methods must be compatible with this type: one can - * safely cast getValue to given type and pass any variable - * assignable to this type as a parameter to setValue. - * - * @return the Type of the property. - */ - @Override - public Class getType() { - if (isMultiSelect()) { - return Set.class; - } else { - return Object.class; - } - } - - /** - * Gets the selected item id or in multiselect mode a set of selected ids. - * - * @see com.vaadin.v7.ui.LegacyAbstractField#getValue() - */ - @Override - public Object getValue() { - final Object retValue = super.getValue(); - - if (isMultiSelect()) { - - // If the return value is not a set - if (retValue == null) { - return new HashSet(); - } - if (retValue instanceof Set) { - return Collections.unmodifiableSet((Set) retValue); - } else if (retValue instanceof Collection) { - return new HashSet((Collection) retValue); - } else { - final Set s = new HashSet(); - if (items.containsId(retValue)) { - s.add(retValue); - } - return s; - } - - } else { - return retValue; - } - } - - /** - * Sets the visible value of the property. - * - *

    - * The value of the select is the selected item id. If the select is in - * multiselect-mode, the value is a set of selected item keys. In - * multiselect mode all collections of id:s can be assigned. - *

    - * - * @param newValue - * the New selected item or collection of selected items. - * @see com.vaadin.v7.ui.LegacyAbstractField#setValue(java.lang.Object) - */ - @Override - public void setValue(Object newValue) throws Property.ReadOnlyException { - if (newValue == getNullSelectionItemId()) { - newValue = null; - } - - setValue(newValue, false); - } - - /** - * Sets the visible value of the property. - * - *

    - * The value of the select is the selected item id. If the select is in - * multiselect-mode, the value is a set of selected item keys. In - * multiselect mode all collections of id:s can be assigned. - *

    - * - * @since 7.5.7 - * @param newValue - * the New selected item or collection of selected items. - * @param repaintIsNotNeeded - * True if caller is sure that repaint is not needed. - * @param ignoreReadOnly - * True if read-only check should be omitted. - * @see com.vaadin.v7.ui.LegacyAbstractField#setValue(java.lang.Object, - * java.lang.Boolean) - */ - @Override - protected void setValue(Object newFieldValue, boolean repaintIsNotNeeded, - boolean ignoreReadOnly) - throws com.vaadin.data.Property.ReadOnlyException, - ConversionException, InvalidValueException { - if (isMultiSelect()) { - if (newFieldValue == null) { - super.setValue(new LinkedHashSet(), repaintIsNotNeeded, - ignoreReadOnly); - } else if (Collection.class - .isAssignableFrom(newFieldValue.getClass())) { - super.setValue( - new LinkedHashSet( - (Collection) newFieldValue), - repaintIsNotNeeded, ignoreReadOnly); - } - } else if (newFieldValue == null || items.containsId(newFieldValue)) { - super.setValue(newFieldValue, repaintIsNotNeeded, ignoreReadOnly); - } - } - - /* Container methods */ - - /** - * Gets the item from the container with given id. If the container does not - * contain the requested item, null is returned. - * - * @param itemId - * the item id. - * @return the item from the container. - */ - @Override - public Item getItem(Object itemId) { - return items.getItem(itemId); - } - - /** - * Gets the item Id collection from the container. - * - * @return the Collection of item ids. - */ - @Override - public Collection getItemIds() { - return items.getItemIds(); - } - - /** - * Gets the property Id collection from the container. - * - * @return the Collection of property ids. - */ - @Override - public Collection getContainerPropertyIds() { - return items.getContainerPropertyIds(); - } - - /** - * Gets the property type. - * - * @param propertyId - * the Id identifying the property. - * @see com.vaadin.data.Container#getType(java.lang.Object) - */ - @Override - public Class getType(Object propertyId) { - return items.getType(propertyId); - } - - /* - * Gets the number of items in the container. - * - * @return the Number of items in the container. - * - * @see com.vaadin.data.Container#size() - */ - @Override - public int size() { - int size = items.size(); - assert size >= 0; - return size; - } - - /** - * Tests, if the collection contains an item with given id. - * - * @param itemId - * the Id the of item to be tested. - */ - @Override - public boolean containsId(Object itemId) { - if (itemId != null) { - return items.containsId(itemId); - } else { - return false; - } - } - - /** - * Gets the Property identified by the given itemId and propertyId from the - * Container - * - * @see com.vaadin.data.Container#getContainerProperty(Object, Object) - */ - @Override - public Property getContainerProperty(Object itemId, Object propertyId) { - return items.getContainerProperty(itemId, propertyId); - } - - /** - * Adds the new property to all items. Adds a property with given id, type - * and default value to all items in the container. - * - * This functionality is optional. If the function is unsupported, it always - * returns false. - * - * @return True if the operation succeeded. - * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object, - * java.lang.Class, java.lang.Object) - */ - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - - final boolean retval = items.addContainerProperty(propertyId, type, - defaultValue); - if (retval && !(items instanceof Container.PropertySetChangeNotifier)) { - firePropertySetChange(); - } - return retval; - } - - /** - * Removes all items from the container. - * - * This functionality is optional. If the function is unsupported, it always - * returns false. - * - * @return True if the operation succeeded. - * @see com.vaadin.data.Container#removeAllItems() - */ - @Override - public boolean removeAllItems() throws UnsupportedOperationException { - - final boolean retval = items.removeAllItems(); - itemIdMapper.removeAll(); - if (retval) { - setValue(null); - if (!(items instanceof Container.ItemSetChangeNotifier)) { - fireItemSetChange(); - } - } - return retval; - } - - /** - * Creates a new item into container with container managed id. The id of - * the created new item is returned. The item can be fetched with getItem() - * method. if the creation fails, null is returned. - * - * @return the Id of the created item or null in case of failure. - * @see com.vaadin.data.Container#addItem() - */ - @Override - public Object addItem() throws UnsupportedOperationException { - - final Object retval = items.addItem(); - if (retval != null - && !(items instanceof Container.ItemSetChangeNotifier)) { - fireItemSetChange(); - } - return retval; - } - - /** - * Create a new item into container. The created new item is returned and - * ready for setting property values. if the creation fails, null is - * returned. In case the container already contains the item, null is - * returned. - * - * This functionality is optional. If the function is unsupported, it always - * returns null. - * - * @param itemId - * the Identification of the item to be created. - * @return the Created item with the given id, or null in case of failure. - * @see com.vaadin.data.Container#addItem(java.lang.Object) - */ - @Override - public Item addItem(Object itemId) throws UnsupportedOperationException { - - final Item retval = items.addItem(itemId); - if (retval != null - && !(items instanceof Container.ItemSetChangeNotifier)) { - fireItemSetChange(); - } - return retval; - } - - /** - * Adds given items with given item ids to container. - * - * @since 7.2 - * @param itemId - * item identifiers to be added to underlying container - * @throws UnsupportedOperationException - * if the underlying container don't support adding items with - * identifiers - */ - public void addItems(Object... itemId) - throws UnsupportedOperationException { - for (Object id : itemId) { - addItem(id); - } - } - - /** - * Adds given items with given item ids to container. - * - * @since 7.2 - * @param itemIds - * item identifiers to be added to underlying container - * @throws UnsupportedOperationException - * if the underlying container don't support adding items with - * identifiers - */ - public void addItems(Collection itemIds) - throws UnsupportedOperationException { - addItems(itemIds.toArray()); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container#removeItem(java.lang.Object) - */ - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - - unselect(itemId); - final boolean retval = items.removeItem(itemId); - itemIdMapper.remove(itemId); - if (retval && !(items instanceof Container.ItemSetChangeNotifier)) { - fireItemSetChange(); - } - return retval; - } - - /** - * Checks that the current selection is valid, i.e. the selected item ids - * exist in the container. Updates the selection if one or several selected - * item ids are no longer available in the container. - */ - @SuppressWarnings("unchecked") - public void sanitizeSelection() { - Object value = getValue(); - if (value == null) { - return; - } - - boolean changed = false; - - if (isMultiSelect()) { - Collection valueAsCollection = (Collection) value; - List newSelection = new ArrayList( - valueAsCollection.size()); - for (Object subValue : valueAsCollection) { - if (containsId(subValue)) { - newSelection.add(subValue); - } else { - changed = true; - } - } - if (changed) { - setValue(newSelection); - } - } else { - if (!containsId(value)) { - setValue(null); - } - } - - } - - /** - * Removes the property from all items. Removes a property with given id - * from all the items in the container. - * - * This functionality is optional. If the function is unsupported, it always - * returns false. - * - * @return True if the operation succeeded. - * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object) - */ - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - - final boolean retval = items.removeContainerProperty(propertyId); - if (retval && !(items instanceof Container.PropertySetChangeNotifier)) { - firePropertySetChange(); - } - return retval; - } - - /* Container.Viewer methods */ - - /** - * Sets the Container that serves as the data source of the viewer. - * - * As a side-effect the fields value (selection) is set to null due old - * selection not necessary exists in new Container. - * - * @see com.vaadin.data.Container.Viewer#setContainerDataSource(Container) - * - * @param newDataSource - * the new data source. - */ - @Override - public void setContainerDataSource(Container newDataSource) { - if (newDataSource == null) { - newDataSource = new IndexedContainer(); - } - - getCaptionChangeListener().clear(); - - if (items != newDataSource) { - - // Removes listeners from the old datasource - if (items != null) { - if (items instanceof Container.ItemSetChangeNotifier) { - ((Container.ItemSetChangeNotifier) items) - .removeItemSetChangeListener(this); - } - if (items instanceof Container.PropertySetChangeNotifier) { - ((Container.PropertySetChangeNotifier) items) - .removePropertySetChangeListener(this); - } - } - - // Assigns new data source - items = newDataSource; - - // Clears itemIdMapper also - itemIdMapper.removeAll(); - - // Adds listeners - if (items != null) { - if (items instanceof Container.ItemSetChangeNotifier) { - ((Container.ItemSetChangeNotifier) items) - .addItemSetChangeListener(this); - } - if (items instanceof Container.PropertySetChangeNotifier) { - ((Container.PropertySetChangeNotifier) items) - .addPropertySetChangeListener(this); - } - } - - /* - * We expect changing the data source should also clean value. See - * #810, #4607, #5281 - */ - setValue(null); - - markAsDirty(); - - } - } - - /** - * Gets the viewing data-source container. - * - * @see com.vaadin.data.Container.Viewer#getContainerDataSource() - */ - @Override - public Container getContainerDataSource() { - return items; - } - - /* Select attributes */ - - /** - * Is the select in multiselect mode? In multiselect mode - * - * @return the Value of property multiSelect. - */ - public boolean isMultiSelect() { - return getState(false).multiSelect; - } - - /** - * Sets the multiselect mode. Setting multiselect mode false may lose - * selection information: if selected items set contains one or more - * selected items, only one of the selected items is kept as selected. - * - * Subclasses of AbstractSelect can choose not to support changing the - * multiselect mode, and may throw {@link UnsupportedOperationException}. - * - * @param multiSelect - * the New value of property multiSelect. - */ - public void setMultiSelect(boolean multiSelect) { - if (multiSelect && getNullSelectionItemId() != null) { - throw new IllegalStateException( - "Multiselect and NullSelectionItemId can not be set at the same time."); - } - if (multiSelect != getState(false).multiSelect) { - - // Selection before mode change - final Object oldValue = getValue(); - - getState().multiSelect = multiSelect; - - // Convert the value type - if (multiSelect) { - final Set s = new HashSet(); - if (oldValue != null) { - s.add(oldValue); - } - setValue(s); - } else { - final Set s = (Set) oldValue; - if (s == null || s.isEmpty()) { - setValue(null); - } else { - // Set the single select to contain only the first - // selected value in the multiselect - setValue(s.iterator().next()); - } - } - - markAsDirty(); - } - } - - /** - * Does the select allow adding new options by the user. If true, the new - * options can be added to the Container. The text entered by the user is - * used as id. Note that data-source must allow adding new items. - * - * @return True if additions are allowed. - */ - public boolean isNewItemsAllowed() { - return allowNewOptions; - } - - /** - * Enables or disables possibility to add new options by the user. - * - * @param allowNewOptions - * the New value of property allowNewOptions. - */ - public void setNewItemsAllowed(boolean allowNewOptions) { - - // Only handle change requests - if (this.allowNewOptions != allowNewOptions) { - - this.allowNewOptions = allowNewOptions; - - markAsDirty(); - } - } - - /** - * Override the caption of an item. Setting caption explicitly overrides id, - * item and index captions. - * - * @param itemId - * the id of the item to be recaptioned. - * @param caption - * the New caption. - */ - public void setItemCaption(Object itemId, String caption) { - if (itemId != null) { - itemCaptions.put(itemId, caption); - markAsDirty(); - } - } - - /** - * Gets the caption of an item. The caption is generated as specified by the - * item caption mode. See setItemCaptionMode() for more - * details. - * - * @param itemId - * the id of the item to be queried. - * @return the caption for specified item. - */ - public String getItemCaption(Object itemId) { - - // Null items can not be found - if (itemId == null) { - return null; - } - - String caption = null; - - switch (getItemCaptionMode()) { - - case ID: - caption = idToCaption(itemId); - break; - case ID_TOSTRING: - caption = itemId.toString(); - break; - case INDEX: - if (items instanceof Container.Indexed) { - caption = String - .valueOf(((Container.Indexed) items).indexOfId(itemId)); - } else { - caption = "ERROR: Container is not indexed"; - } - break; - - case ITEM: - final Item i = getItem(itemId); - if (i != null) { - caption = i.toString(); - } - break; - - case EXPLICIT: - caption = itemCaptions.get(itemId); - break; - - case EXPLICIT_DEFAULTS_ID: - caption = itemCaptions.get(itemId); - if (caption == null) { - caption = idToCaption(itemId); - } - break; - - case PROPERTY: - final Property p = getContainerProperty(itemId, - getItemCaptionPropertyId()); - if (p != null) { - Object value = p.getValue(); - if (value != null) { - caption = value.toString(); - } - } - break; - } - - // All items must have some captions - return caption != null ? caption : ""; - } - - private String idToCaption(Object itemId) { - try { - LegacyConverter c = (LegacyConverter) LegacyConverterUtil - .getConverter(String.class, itemId.getClass(), - getSession()); - return LegacyConverterUtil.convertFromModel(itemId, String.class, c, - getLocale()); - } catch (Exception e) { - return itemId.toString(); - } - } - - /** - * Sets the icon for an item. - * - * @param itemId - * the id of the item to be assigned an icon. - * @param icon - * the icon to use or null. - */ - public void setItemIcon(Object itemId, Resource icon) { - if (itemId != null) { - if (icon == null) { - itemIcons.remove(itemId); - } else { - itemIcons.put(itemId, icon); - } - markAsDirty(); - } - } - - /** - * Gets the item icon. - * - * @param itemId - * the id of the item to be assigned an icon. - * @return the icon for the item or null, if not specified. - */ - public Resource getItemIcon(Object itemId) { - final Resource explicit = itemIcons.get(itemId); - if (explicit != null) { - return explicit; - } - - if (getItemIconPropertyId() == null) { - return null; - } - - final Property ip = getContainerProperty(itemId, - getItemIconPropertyId()); - if (ip == null) { - return null; - } - final Object icon = ip.getValue(); - if (icon instanceof Resource) { - return (Resource) icon; - } - - return null; - } - - /** - * Sets the item caption mode. - * - * See {@link ItemCaptionMode} for a description of the modes. - *

    - * {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID} is the default mode. - *

    - * - * @param mode - * the One of the modes listed above. - */ - public void setItemCaptionMode(ItemCaptionMode mode) { - if (mode != null) { - itemCaptionMode = mode; - markAsDirty(); - } - } - - /** - * Gets the item caption mode. - * - *

    - * The mode can be one of the following ones: - *

      - *
    • ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID : Items - * Id-objects toString is used as item caption. If caption is - * explicitly specified, it overrides the id-caption. - *
    • ITEM_CAPTION_MODE_ID : Items Id-objects - * toString is used as item caption.
    • - *
    • ITEM_CAPTION_MODE_ITEM : Item-objects - * toString is used as item caption.
    • - *
    • ITEM_CAPTION_MODE_INDEX : The index of the item is used - * as item caption. The index mode can only be used with the containers - * implementing Container.Indexed interface.
    • - *
    • ITEM_CAPTION_MODE_EXPLICIT : The item captions must be - * explicitly specified.
    • - *
    • ITEM_CAPTION_MODE_PROPERTY : The item captions are read - * from property, that must be specified with - * setItemCaptionPropertyId.
    • - *
    - * The ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID is the default - * mode. - *

    - * - * @return the One of the modes listed above. - */ - public ItemCaptionMode getItemCaptionMode() { - return itemCaptionMode; - } - - /** - * Sets the item caption property. - * - *

    - * Setting the id to a existing property implicitly sets the item caption - * mode to ITEM_CAPTION_MODE_PROPERTY. If the object is in - * ITEM_CAPTION_MODE_PROPERTY mode, setting caption property id - * null resets the item caption mode to - * ITEM_CAPTION_EXPLICIT_DEFAULTS_ID. - *

    - *

    - * Note that the type of the property used for caption must be String - *

    - *

    - * Setting the property id to null disables this feature. The id is null by - * default - *

    - * . - * - * @param propertyId - * the id of the property. - * - */ - public void setItemCaptionPropertyId(Object propertyId) { - if (propertyId != null) { - itemCaptionPropertyId = propertyId; - setItemCaptionMode(ITEM_CAPTION_MODE_PROPERTY); - markAsDirty(); - } else { - itemCaptionPropertyId = null; - if (getItemCaptionMode() == ITEM_CAPTION_MODE_PROPERTY) { - setItemCaptionMode(ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID); - } - markAsDirty(); - } - } - - /** - * Gets the item caption property. - * - * @return the Id of the property used as item caption source. - */ - public Object getItemCaptionPropertyId() { - return itemCaptionPropertyId; - } - - /** - * Sets the item icon property. - * - *

    - * If the property id is set to a valid value, each item is given an icon - * got from the given property of the items. The type of the property must - * be assignable to Resource. - *

    - * - *

    - * Note : The icons set with setItemIcon function override the - * icons from the property. - *

    - * - *

    - * Setting the property id to null disables this feature. The id is null by - * default - *

    - * . - * - * @param propertyId - * the id of the property that specifies icons for items or null - * @throws IllegalArgumentException - * If the propertyId is not in the container or is not of a - * valid type - */ - public void setItemIconPropertyId(Object propertyId) - throws IllegalArgumentException { - if (propertyId == null) { - itemIconPropertyId = null; - } else if (!getContainerPropertyIds().contains(propertyId)) { - throw new IllegalArgumentException( - "Property id not found in the container"); - } else if (Resource.class.isAssignableFrom(getType(propertyId))) { - itemIconPropertyId = propertyId; - } else { - throw new IllegalArgumentException( - "Property type must be assignable to Resource"); - } - markAsDirty(); - } - - /** - * Gets the item icon property. - * - *

    - * If the property id is set to a valid value, each item is given an icon - * got from the given property of the items. The type of the property must - * be assignable to Icon. - *

    - * - *

    - * Note : The icons set with setItemIcon function override the - * icons from the property. - *

    - * - *

    - * Setting the property id to null disables this feature. The id is null by - * default - *

    - * . - * - * @return the Id of the property containing the item icons. - */ - public Object getItemIconPropertyId() { - return itemIconPropertyId; - } - - /** - * Tests if an item is selected. - * - *

    - * In single select mode testing selection status of the item identified by - * {@link #getNullSelectionItemId()} returns true if the value of the - * property is null. - *

    - * - * @param itemId - * the Id the of the item to be tested. - * @see #getNullSelectionItemId() - * @see #setNullSelectionItemId(Object) - * - */ - public boolean isSelected(Object itemId) { - if (itemId == null) { - return false; - } - if (isMultiSelect()) { - return ((Set) getValue()).contains(itemId); - } else { - final Object value = getValue(); - return itemId - .equals(value == null ? getNullSelectionItemId() : value); - } - } - - /** - * Selects an item. - * - *

    - * In single select mode selecting item identified by - * {@link #getNullSelectionItemId()} sets the value of the property to null. - *

    - * - * @param itemId - * the identifier of Item to be selected. - * @see #getNullSelectionItemId() - * @see #setNullSelectionItemId(Object) - * - */ - public void select(Object itemId) { - if (!isMultiSelect()) { - setValue(itemId); - } else if (!isSelected(itemId) && itemId != null - && items.containsId(itemId)) { - final Set s = new HashSet((Set) getValue()); - s.add(itemId); - setValue(s); - } - } - - /** - * Unselects an item. - * - * @param itemId - * the identifier of the Item to be unselected. - * @see #getNullSelectionItemId() - * @see #setNullSelectionItemId(Object) - * - */ - public void unselect(Object itemId) { - if (isSelected(itemId)) { - if (isMultiSelect()) { - final Set s = new HashSet((Set) getValue()); - s.remove(itemId); - setValue(s); - } else { - setValue(null); - } - } - } - - /** - * Notifies this listener that the Containers contents has changed. - * - * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent) - */ - @Override - public void containerPropertySetChange( - Container.PropertySetChangeEvent event) { - firePropertySetChange(); - } - - /** - * Adds a new Property set change listener for this Container. - * - * @see com.vaadin.data.Container.PropertySetChangeNotifier#addListener(com.vaadin.data.Container.PropertySetChangeListener) - */ - @Override - public void addPropertySetChangeListener( - Container.PropertySetChangeListener listener) { - if (propertySetEventListeners == null) { - propertySetEventListeners = new LinkedHashSet(); - } - propertySetEventListeners.add(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addPropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Override - @Deprecated - public void addListener(Container.PropertySetChangeListener listener) { - addPropertySetChangeListener(listener); - } - - /** - * Removes a previously registered Property set change listener. - * - * @see com.vaadin.data.Container.PropertySetChangeNotifier#removeListener(com.vaadin.data.Container.PropertySetChangeListener) - */ - @Override - public void removePropertySetChangeListener( - Container.PropertySetChangeListener listener) { - if (propertySetEventListeners != null) { - propertySetEventListeners.remove(listener); - if (propertySetEventListeners.isEmpty()) { - propertySetEventListeners = null; - } - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removePropertySetChangeListener(com.vaadin.data.Container.PropertySetChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(Container.PropertySetChangeListener listener) { - removePropertySetChangeListener(listener); - } - - /** - * Adds an Item set change listener for the object. - * - * @see com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin.data.Container.ItemSetChangeListener) - */ - @Override - public void addItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (itemSetEventListeners == null) { - itemSetEventListeners = new LinkedHashSet(); - } - itemSetEventListeners.add(listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Override - @Deprecated - public void addListener(Container.ItemSetChangeListener listener) { - addItemSetChangeListener(listener); - } - - /** - * Removes the Item set change listener from the object. - * - * @see com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin.data.Container.ItemSetChangeListener) - */ - @Override - public void removeItemSetChangeListener( - Container.ItemSetChangeListener listener) { - if (itemSetEventListeners != null) { - itemSetEventListeners.remove(listener); - if (itemSetEventListeners.isEmpty()) { - itemSetEventListeners = null; - } - } - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeItemSetChangeListener(com.vaadin.data.Container.ItemSetChangeListener)} - **/ - @Override - @Deprecated - public void removeListener(Container.ItemSetChangeListener listener) { - removeItemSetChangeListener(listener); - } - - @Override - public Collection getListeners(Class eventType) { - if (Container.ItemSetChangeEvent.class.isAssignableFrom(eventType)) { - if (itemSetEventListeners == null) { - return Collections.EMPTY_LIST; - } else { - return Collections - .unmodifiableCollection(itemSetEventListeners); - } - } else if (Container.PropertySetChangeEvent.class - .isAssignableFrom(eventType)) { - if (propertySetEventListeners == null) { - return Collections.EMPTY_LIST; - } else { - return Collections - .unmodifiableCollection(propertySetEventListeners); - } - } - - return super.getListeners(eventType); - } - - /** - * Lets the listener know a Containers Item set has changed. - * - * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent) - */ - @Override - public void containerItemSetChange(Container.ItemSetChangeEvent event) { - // Clears the item id mapping table - itemIdMapper.removeAll(); - - // Notify all listeners - fireItemSetChange(); - } - - /** - * Fires the property set change event. - */ - protected void firePropertySetChange() { - if (propertySetEventListeners != null - && !propertySetEventListeners.isEmpty()) { - final Container.PropertySetChangeEvent event = new PropertySetChangeEvent( - this); - final Object[] listeners = propertySetEventListeners.toArray(); - for (int i = 0; i < listeners.length; i++) { - ((Container.PropertySetChangeListener) listeners[i]) - .containerPropertySetChange(event); - } - } - markAsDirty(); - } - - /** - * Fires the item set change event. - */ - protected void fireItemSetChange() { - if (itemSetEventListeners != null && !itemSetEventListeners.isEmpty()) { - final Container.ItemSetChangeEvent event = new ItemSetChangeEvent( - this); - final Object[] listeners = itemSetEventListeners.toArray(); - for (int i = 0; i < listeners.length; i++) { - ((Container.ItemSetChangeListener) listeners[i]) - .containerItemSetChange(event); - } - } - markAsDirty(); - } - - /** - * Implementation of item set change event. - */ - private static class ItemSetChangeEvent extends EventObject - implements Serializable, Container.ItemSetChangeEvent { - - private ItemSetChangeEvent(Container source) { - super(source); - } - - /** - * Gets the Property where the event occurred. - * - * @see com.vaadin.data.Container.ItemSetChangeEvent#getContainer() - */ - @Override - public Container getContainer() { - return (Container) getSource(); - } - - } - - /** - * Implementation of property set change event. - */ - private static class PropertySetChangeEvent extends EventObject - implements Container.PropertySetChangeEvent, Serializable { - - private PropertySetChangeEvent(Container source) { - super(source); - } - - /** - * Retrieves the Container whose contents have been modified. - * - * @see com.vaadin.data.Container.PropertySetChangeEvent#getContainer() - */ - @Override - public Container getContainer() { - return (Container) getSource(); - } - - } - - /** - * For multi-selectable fields, also an empty collection of values is - * considered to be an empty field. - * - * @see LegacyAbstractField#isEmpty(). - */ - @Override - public boolean isEmpty() { - if (!isMultiSelect()) { - return super.isEmpty(); - } else { - Object value = getValue(); - return super.isEmpty() || (value instanceof Collection - && ((Collection) value).isEmpty()); - } - } - - /** - * Allow or disallow empty selection by the user. If the select is in - * single-select mode, you can make an item represent the empty selection by - * calling setNullSelectionItemId(). This way you can for - * instance set an icon and caption for the null selection item. - * - * @param nullSelectionAllowed - * whether or not to allow empty selection - * @see #setNullSelectionItemId(Object) - * @see #isNullSelectionAllowed() - */ - public void setNullSelectionAllowed(boolean nullSelectionAllowed) { - if (nullSelectionAllowed != this.nullSelectionAllowed) { - this.nullSelectionAllowed = nullSelectionAllowed; - markAsDirty(); - } - } - - /** - * Checks if null empty selection is allowed by the user. - * - * @return whether or not empty selection is allowed - * @see #setNullSelectionAllowed(boolean) - */ - public boolean isNullSelectionAllowed() { - return nullSelectionAllowed; - } - - /** - * Returns the item id that represents null value of this select in single - * select mode. - * - *

    - * Data interface does not support nulls as item ids. Selecting the item - * identified by this id is the same as selecting no items at all. This - * setting only affects the single select mode. - *

    - * - * @return the Object Null value item id. - * @see #setNullSelectionItemId(Object) - * @see #isSelected(Object) - * @see #select(Object) - */ - public Object getNullSelectionItemId() { - return nullSelectionItemId; - } - - /** - * Sets the item id that represents null value of this select. - * - *

    - * Data interface does not support nulls as item ids. Selecting the item - * identified by this id is the same as selecting no items at all. This - * setting only affects the single select mode. - *

    - * - * @param nullSelectionItemId - * the nullSelectionItemId to set. - * @see #getNullSelectionItemId() - * @see #isSelected(Object) - * @see #select(Object) - */ - public void setNullSelectionItemId(Object nullSelectionItemId) { - if (nullSelectionItemId != null && isMultiSelect()) { - throw new IllegalStateException( - "Multiselect and NullSelectionItemId can not be set at the same time."); - } - this.nullSelectionItemId = nullSelectionItemId; - } - - /** - * Notifies the component that it is connected to an application. - * - * @see com.vaadin.v7.ui.LegacyAbstractField#attach() - */ - @Override - public void attach() { - super.attach(); - } - - /** - * Detaches the component from application. - * - * @see com.vaadin.ui.AbstractComponent#detach() - */ - @Override - public void detach() { - getCaptionChangeListener().clear(); - super.detach(); - } - - // Caption change listener - protected CaptionChangeListener getCaptionChangeListener() { - if (captionChangeListener == null) { - captionChangeListener = new CaptionChangeListener(); - } - return captionChangeListener; - } - - /** - * This is a listener helper for Item and Property changes that should cause - * a repaint. It should be attached to all items that are displayed, and the - * default implementation does this in paintContent(). Especially - * "lazyloading" components should take care to add and remove listeners as - * appropriate. Call addNotifierForItem() for each painted item (and - * remember to clear). - * - * NOTE: singleton, use getCaptionChangeListener(). - * - */ - protected class CaptionChangeListener implements - Item.PropertySetChangeListener, Property.ValueChangeListener { - - // TODO clean this up - type is either Item.PropertySetChangeNotifier or - // Property.ValueChangeNotifier - HashSet captionChangeNotifiers = new HashSet(); - - public void addNotifierForItem(Object itemId) { - switch (getItemCaptionMode()) { - case ITEM: - final Item i = getItem(itemId); - if (i == null) { - return; - } - if (i instanceof Item.PropertySetChangeNotifier) { - ((Item.PropertySetChangeNotifier) i) - .addPropertySetChangeListener( - getCaptionChangeListener()); - captionChangeNotifiers.add(i); - } - Collection pids = i.getItemPropertyIds(); - if (pids != null) { - for (Iterator it = pids.iterator(); it.hasNext();) { - Property p = i.getItemProperty(it.next()); - if (p != null - && p instanceof Property.ValueChangeNotifier) { - ((Property.ValueChangeNotifier) p) - .addValueChangeListener( - getCaptionChangeListener()); - captionChangeNotifiers.add(p); - } - } - - } - break; - case PROPERTY: - final Property p = getContainerProperty(itemId, - getItemCaptionPropertyId()); - if (p != null && p instanceof Property.ValueChangeNotifier) { - ((Property.ValueChangeNotifier) p) - .addValueChangeListener(getCaptionChangeListener()); - captionChangeNotifiers.add(p); - } - break; - - } - if (getItemIconPropertyId() != null) { - final Property p = getContainerProperty(itemId, - getItemIconPropertyId()); - if (p != null && p instanceof Property.ValueChangeNotifier) { - ((Property.ValueChangeNotifier) p) - .addValueChangeListener(getCaptionChangeListener()); - captionChangeNotifiers.add(p); - } - } - } - - public void clear() { - for (Iterator it = captionChangeNotifiers.iterator(); it - .hasNext();) { - Object notifier = it.next(); - if (notifier instanceof Item.PropertySetChangeNotifier) { - ((Item.PropertySetChangeNotifier) notifier) - .removePropertySetChangeListener( - getCaptionChangeListener()); - } else { - ((Property.ValueChangeNotifier) notifier) - .removeValueChangeListener( - getCaptionChangeListener()); - } - } - captionChangeNotifiers.clear(); - } - - @Override - public void valueChange( - com.vaadin.data.Property.ValueChangeEvent event) { - markAsDirty(); - } - - @Override - public void itemPropertySetChange( - com.vaadin.data.Item.PropertySetChangeEvent event) { - markAsDirty(); - } - - } - - /** - * Criterion which accepts a drop only if the drop target is (one of) the - * given Item identifier(s). Criterion can be used only on a drop targets - * that extends AbstractSelect like {@link Table} and {@link Tree}. The - * target and identifiers of valid Items are given in constructor. - * - * @since 6.3 - */ - public static class TargetItemIs extends AbstractItemSetCriterion { - - /** - * @param select - * the select implementation that is used as a drop target - * @param itemId - * the identifier(s) that are valid drop locations - */ - public TargetItemIs(AbstractSelect select, Object... itemId) { - super(select, itemId); - } - - @Override - public boolean accept(DragAndDropEvent dragEvent) { - AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent - .getTargetDetails(); - if (dropTargetData.getTarget() != select) { - return false; - } - return itemIds.contains(dropTargetData.getItemIdOver()); - } - - } - - /** - * Abstract helper class to implement item id based criterion. - * - * Note, inner class used not to open itemIdMapper for public access. - * - * @since 6.3 - * - */ - private static abstract class AbstractItemSetCriterion - extends ClientSideCriterion { - protected final Collection itemIds = new HashSet(); - protected AbstractSelect select; - - public AbstractItemSetCriterion(AbstractSelect select, - Object... itemId) { - if (itemIds == null || select == null) { - throw new IllegalArgumentException( - "Accepted item identifiers must be accepted."); - } - Collections.addAll(itemIds, itemId); - this.select = select; - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - super.paintContent(target); - String[] keys = new String[itemIds.size()]; - int i = 0; - for (Object itemId : itemIds) { - String key = select.itemIdMapper.key(itemId); - keys[i++] = key; - } - target.addAttribute("keys", keys); - target.addAttribute("s", select); - } - - } - - /** - * This criterion accepts a only a {@link Transferable} that contains given - * Item (practically its identifier) from a specific AbstractSelect. - * - * @since 6.3 - */ - public static class AcceptItem extends AbstractItemSetCriterion { - - /** - * @param select - * the select from which the item id's are checked - * @param itemId - * the item identifier(s) of the select that are accepted - */ - public AcceptItem(AbstractSelect select, Object... itemId) { - super(select, itemId); - } - - @Override - public boolean accept(DragAndDropEvent dragEvent) { - DataBoundTransferable transferable = (DataBoundTransferable) dragEvent - .getTransferable(); - if (transferable.getSourceComponent() != select) { - return false; - } - return itemIds.contains(transferable.getItemId()); - } - - /** - * A simple accept criterion which ensures that {@link Transferable} - * contains an {@link Item} (or actually its identifier). In other words - * the criterion check that drag is coming from a {@link Container} like - * {@link Tree} or {@link Table}. - */ - public static final ClientSideCriterion ALL = new ContainsDataFlavor( - "itemId"); - - } - - /** - * TargetDetails implementation for subclasses of {@link AbstractSelect} - * that implement {@link DropTarget}. - * - * @since 6.3 - */ - public class AbstractSelectTargetDetails extends TargetDetailsImpl { - - /** - * The item id over which the drag event happened. - */ - protected Object idOver; - - /** - * Constructor that automatically converts itemIdOver key to - * corresponding item Id - * - */ - protected AbstractSelectTargetDetails( - Map rawVariables) { - super(rawVariables, (DropTarget) AbstractSelect.this); - // eagar fetch itemid, mapper may be emptied - String keyover = (String) getData("itemIdOver"); - if (keyover != null) { - idOver = itemIdMapper.get(keyover); - } - } - - /** - * If the drag operation is currently over an {@link Item}, this method - * returns the identifier of that {@link Item}. - * - */ - public Object getItemIdOver() { - return idOver; - } - - /** - * Returns a detailed vertical location where the drop happened on Item. - */ - public VerticalDropLocation getDropLocation() { - String detail = (String) getData("detail"); - if (detail == null) { - return null; - } - return VerticalDropLocation.valueOf(detail); - } - - } - - /** - * An accept criterion to accept drops only on a specific vertical location - * of an item. - *

    - * This accept criterion is currently usable in Tree and Table - * implementations. - */ - public static class VerticalLocationIs extends TargetDetailIs { - public static VerticalLocationIs TOP = new VerticalLocationIs( - VerticalDropLocation.TOP); - public static VerticalLocationIs BOTTOM = new VerticalLocationIs( - VerticalDropLocation.BOTTOM); - public static VerticalLocationIs MIDDLE = new VerticalLocationIs( - VerticalDropLocation.MIDDLE); - - private VerticalLocationIs(VerticalDropLocation l) { - super("detail", l.name()); - } - } - - /** - * Implement this interface and pass it to Tree.setItemDescriptionGenerator - * or Table.setItemDescriptionGenerator to generate mouse over descriptions - * ("tooltips") for the rows and cells in Table or for the items in Tree. - */ - public interface ItemDescriptionGenerator extends Serializable { - - /** - * Called by Table when a cell (and row) is painted or a item is painted - * in Tree - * - * @param source - * The source of the generator, the Tree or Table the - * generator is attached to - * @param itemId - * The itemId of the painted cell - * @param propertyId - * The propertyId of the cell, null when getting row - * description - * @return The description or "tooltip" of the item. - */ - public String generateDescription(Component source, Object itemId, - Object propertyId); - } - - @Override - public void readDesign(Element design, DesignContext context) { - // handle default attributes - super.readDesign(design, context); - // handle children specifying selectable items (

    - * Vaadin Calendar is for visualizing events in a calendar. Calendar events can - * be visualized in the variable length view depending on the start and end - * dates. - *

    - * - *
  • You can set the viewable date range with the {@link #setStartDate(Date)} - * and {@link #setEndDate(Date)} methods. Calendar has a default date range of - * one week
  • - * - *
  • Calendar has two kind of views: monthly and weekly view
  • - * - *
  • If date range is seven days or shorter, the weekly view is used.
  • - * - *
  • Calendar queries its events by using a - * {@link com.vaadin.addon.calendar.event.CalendarEventProvider - * CalendarEventProvider}. By default, a - * {@link com.vaadin.addon.calendar.event.BasicEventProvider BasicEventProvider} - * is used.
  • - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class Calendar extends AbstractComponent - implements CalendarComponentEvents.NavigationNotifier, - CalendarComponentEvents.EventMoveNotifier, - CalendarComponentEvents.RangeSelectNotifier, - CalendarComponentEvents.EventResizeNotifier, - CalendarEventProvider.EventSetChangeListener, DropTarget, - CalendarEditableEventProvider, Action.Container, LegacyComponent { - - /** - * Calendar can use either 12 hours clock or 24 hours clock. - */ - public enum TimeFormat { - - Format12H(), Format24H(); - } - - /** Defines currently active format for time. 12H/24H. */ - protected TimeFormat currentTimeFormat; - - /** Internal calendar data source. */ - protected java.util.Calendar currentCalendar = java.util.Calendar - .getInstance(); - - /** Defines the component's active time zone. */ - protected TimeZone timezone; - - /** Defines the calendar's date range starting point. */ - protected Date startDate = null; - - /** Defines the calendar's date range ending point. */ - protected Date endDate = null; - - /** Event provider. */ - private CalendarEventProvider calendarEventProvider; - - /** - * Internal buffer for the events that are retrieved from the event - * provider. - */ - protected List events; - - /** Date format that will be used in the UIDL for dates. */ - protected DateFormat df_date = new SimpleDateFormat("yyyy-MM-dd"); - - /** Time format that will be used in the UIDL for time. */ - protected DateFormat df_time = new SimpleDateFormat("HH:mm:ss"); - - /** Date format that will be used in the UIDL for both date and time. */ - protected DateFormat df_date_time = new SimpleDateFormat( - DateConstants.CLIENT_DATE_FORMAT + "-" - + DateConstants.CLIENT_TIME_FORMAT); - - /** - * Week view's scroll position. Client sends updates to this value so that - * scroll position wont reset all the time. - */ - private int scrollTop = 0; - - /** Caption format for the weekly view */ - private String weeklyCaptionFormat = null; - - /** Map from event ids to event handlers */ - private final Map handlers; - - /** - * Drop Handler for Vaadin DD. By default null. - */ - private DropHandler dropHandler; - - /** - * First day to show for a week - */ - private int firstDay = 1; - - /** - * Last day to show for a week - */ - private int lastDay = 7; - - /** - * First hour to show for a day - */ - private int firstHour = 0; - - /** - * Last hour to show for a day - */ - private int lastHour = 23; - - /** - * List of action handlers. - */ - private LinkedList actionHandlers = null; - - /** - * Action mapper. - */ - private KeyMapper actionMapper = null; - - /** - * - */ - private CalendarServerRpcImpl rpc = new CalendarServerRpcImpl(); - - private Integer customFirstDayOfWeek; - - /** - * Returns the logger for the calendar - */ - protected Logger getLogger() { - return Logger.getLogger(Calendar.class.getName()); - } - - /** - * Construct a Vaadin Calendar with a BasicEventProvider and no caption. - * Default date range is one week. - */ - public Calendar() { - this(null, new BasicEventProvider()); - } - - /** - * Construct a Vaadin Calendar with a BasicEventProvider and the provided - * caption. Default date range is one week. - * - * @param caption - */ - public Calendar(String caption) { - this(caption, new BasicEventProvider()); - } - - /** - *

    - * Construct a Vaadin Calendar with event provider. Event provider is - * obligatory, because calendar component will query active events through - * it. - *

    - * - *

    - * By default, Vaadin Calendar will show dates from the start of the current - * week to the end of the current week. Use {@link #setStartDate(Date)} and - * {@link #setEndDate(Date)} to change this. - *

    - * - * @param eventProvider - * Event provider, cannot be null. - */ - public Calendar(CalendarEventProvider eventProvider) { - this(null, eventProvider); - } - - /** - *

    - * Construct a Vaadin Calendar with event provider and a caption. Event - * provider is obligatory, because calendar component will query active - * events through it. - *

    - * - *

    - * By default, Vaadin Calendar will show dates from the start of the current - * week to the end of the current week. Use {@link #setStartDate(Date)} and - * {@link #setEndDate(Date)} to change this. - *

    - * - * @param eventProvider - * Event provider, cannot be null. - */ - // this is the constructor every other constructor calls - public Calendar(String caption, CalendarEventProvider eventProvider) { - registerRpc(rpc); - setCaption(caption); - handlers = new HashMap(); - setDefaultHandlers(); - currentCalendar.setTime(new Date()); - setEventProvider(eventProvider); - getState().firstDayOfWeek = firstDay; - getState().lastVisibleDayOfWeek = lastDay; - getState().firstHourOfDay = firstHour; - getState().lastHourOfDay = lastHour; - setTimeFormat(null); - - } - - @Override - public CalendarState getState() { - return (CalendarState) super.getState(); - } - - @Override - protected CalendarState getState(boolean markAsDirty) { - return (CalendarState) super.getState(markAsDirty); - } - - @Override - public void beforeClientResponse(boolean initial) { - super.beforeClientResponse(initial); - - initCalendarWithLocale(); - - getState().format24H = TimeFormat.Format24H == getTimeFormat(); - setupDaysAndActions(); - setupCalendarEvents(); - rpc.scroll(scrollTop); - } - - /** - * Set all the wanted default handlers here. This is always called after - * constructing this object. All other events have default handlers except - * range and event click. - */ - protected void setDefaultHandlers() { - setHandler(new BasicBackwardHandler()); - setHandler(new BasicForwardHandler()); - setHandler(new BasicWeekClickHandler()); - setHandler(new BasicDateClickHandler()); - setHandler(new BasicEventMoveHandler()); - setHandler(new BasicEventResizeHandler()); - } - - /** - * Gets the calendar's start date. - * - * @return First visible date. - */ - public Date getStartDate() { - if (startDate == null) { - currentCalendar.set(java.util.Calendar.MILLISECOND, 0); - currentCalendar.set(java.util.Calendar.SECOND, 0); - currentCalendar.set(java.util.Calendar.MINUTE, 0); - currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0); - currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, - currentCalendar.getFirstDayOfWeek()); - return currentCalendar.getTime(); - } - return startDate; - } - - /** - * Sets start date for the calendar. This and {@link #setEndDate(Date)} - * control the range of dates visible on the component. The default range is - * one week. - * - * @param date - * First visible date to show. - */ - public void setStartDate(Date date) { - if (!date.equals(startDate)) { - startDate = date; - markAsDirty(); - } - } - - /** - * Gets the calendar's end date. - * - * @return Last visible date. - */ - public Date getEndDate() { - if (endDate == null) { - currentCalendar.set(java.util.Calendar.MILLISECOND, 0); - currentCalendar.set(java.util.Calendar.SECOND, 59); - currentCalendar.set(java.util.Calendar.MINUTE, 59); - currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 23); - currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, - currentCalendar.getFirstDayOfWeek() + 6); - return currentCalendar.getTime(); - } - return endDate; - } - - /** - * Sets end date for the calendar. Starting from startDate, only six weeks - * will be shown if duration to endDate is longer than six weeks. - * - * This and {@link #setStartDate(Date)} control the range of dates visible - * on the component. The default range is one week. - * - * @param date - * Last visible date to show. - */ - public void setEndDate(Date date) { - if (startDate != null && startDate.after(date)) { - startDate = (Date) date.clone(); - markAsDirty(); - } else if (!date.equals(endDate)) { - endDate = date; - markAsDirty(); - } - } - - /** - * Sets the locale to be used in the Calendar component. - * - * @see com.vaadin.ui.AbstractComponent#setLocale(java.util.Locale) - */ - @Override - public void setLocale(Locale newLocale) { - super.setLocale(newLocale); - initCalendarWithLocale(); - } - - /** - * Initialize the java calendar instance with the current locale and - * timezone. - */ - private void initCalendarWithLocale() { - if (timezone != null) { - currentCalendar = java.util.Calendar.getInstance(timezone, - getLocale()); - - } else { - currentCalendar = java.util.Calendar.getInstance(getLocale()); - } - - if (customFirstDayOfWeek != null) { - currentCalendar.setFirstDayOfWeek(customFirstDayOfWeek); - } - } - - private void setupCalendarEvents() { - int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) - / DateConstants.DAYINMILLIS); - durationInDays++; - if (durationInDays > 60) { - throw new RuntimeException( - "Daterange is too big (max 60) = " + durationInDays); - } - - Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); - Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); - - currentCalendar.setTime(firstDateToShow); - events = getEventProvider().getEvents(firstDateToShow, lastDateToShow); - - List calendarStateEvents = new ArrayList(); - if (events != null) { - for (int i = 0; i < events.size(); i++) { - CalendarEvent e = events.get(i); - CalendarState.Event event = new CalendarState.Event(); - event.index = i; - event.caption = e.getCaption() == null ? "" : e.getCaption(); - event.dateFrom = df_date.format(e.getStart()); - event.dateTo = df_date.format(e.getEnd()); - event.timeFrom = df_time.format(e.getStart()); - event.timeTo = df_time.format(e.getEnd()); - event.description = e.getDescription() == null ? "" - : e.getDescription(); - event.styleName = e.getStyleName() == null ? "" - : e.getStyleName(); - event.allDay = e.isAllDay(); - calendarStateEvents.add(event); - } - } - getState().events = calendarStateEvents; - } - - private void setupDaysAndActions() { - // Make sure we have a up-to-date locale - initCalendarWithLocale(); - - CalendarState state = getState(); - - state.firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); - - // If only one is null, throw exception - // If both are null, set defaults - if (startDate == null ^ endDate == null) { - String message = "Schedule cannot be painted without a proper date range.\n"; - if (startDate == null) { - throw new IllegalStateException(message - + "You must set a start date using setStartDate(Date)."); - - } else { - throw new IllegalStateException(message - + "You must set an end date using setEndDate(Date)."); - } - - } else if (startDate == null && endDate == null) { - // set defaults - startDate = getStartDate(); - endDate = getEndDate(); - } - - int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) - / DateConstants.DAYINMILLIS); - durationInDays++; - if (durationInDays > 60) { - throw new RuntimeException( - "Daterange is too big (max 60) = " + durationInDays); - } - - state.dayNames = getDayNamesShort(); - state.monthNames = getMonthNamesShort(); - - // Use same timezone in all dates this component handles. - // Show "now"-marker in browser within given timezone. - Date now = new Date(); - currentCalendar.setTime(now); - now = currentCalendar.getTime(); - - // Reset time zones for custom date formats - df_date.setTimeZone(currentCalendar.getTimeZone()); - df_time.setTimeZone(currentCalendar.getTimeZone()); - - state.now = (df_date.format(now) + " " + df_time.format(now)); - - Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); - Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); - - currentCalendar.setTime(firstDateToShow); - - DateFormat weeklyCaptionFormatter = getWeeklyCaptionFormatter(); - weeklyCaptionFormatter.setTimeZone(currentCalendar.getTimeZone()); - - Map> actionMap = new HashMap>(); - - List days = new ArrayList(); - - // Send all dates to client from server. This - // approach was taken because gwt doesn't - // support date localization properly. - while (currentCalendar.getTime().compareTo(lastDateToShow) < 1) { - final Date date = currentCalendar.getTime(); - final CalendarState.Day day = new CalendarState.Day(); - day.date = df_date.format(date); - day.localizedDateFormat = weeklyCaptionFormatter.format(date); - day.dayOfWeek = getDowByLocale(currentCalendar); - day.week = getWeek(currentCalendar); - day.yearOfWeek = getYearOfWeek(currentCalendar); - - days.add(day); - - // Get actions for a specific date - if (actionHandlers != null) { - for (Action.Handler actionHandler : actionHandlers) { - - // Create calendar which omits time - GregorianCalendar cal = new GregorianCalendar(getTimeZone(), - getLocale()); - cal.clear(); - cal.set(currentCalendar.get(java.util.Calendar.YEAR), - currentCalendar.get(java.util.Calendar.MONTH), - currentCalendar.get(java.util.Calendar.DATE)); - - // Get day start and end times - Date start = cal.getTime(); - cal.add(java.util.Calendar.DATE, 1); - cal.add(java.util.Calendar.SECOND, -1); - Date end = cal.getTime(); - - boolean monthView = (durationInDays > 7); - - /** - * If in day or week view add actions for each half-an-hour. - * If in month view add actions for each day - */ - if (monthView) { - setActionsForDay(actionMap, start, end, actionHandler); - } else { - setActionsForEachHalfHour(actionMap, start, end, - actionHandler); - } - - } - } - - currentCalendar.add(java.util.Calendar.DATE, 1); - } - state.days = days; - state.actions = createActionsList(actionMap); - } - - private int getWeek(java.util.Calendar calendar) { - return calendar.get(java.util.Calendar.WEEK_OF_YEAR); - } - - private int getYearOfWeek(java.util.Calendar calendar) { - // Would use calendar.getWeekYear() but it's only available since 1.7. - int week = getWeek(calendar); - int month = calendar.get(java.util.Calendar.MONTH); - int year = calendar.get(java.util.Calendar.YEAR); - - if (week == 1 && month == java.util.Calendar.DECEMBER) { - return year + 1; - } - - return year; - } - - private void setActionsForEachHalfHour( - Map> actionMap, Date start, Date end, - Action.Handler actionHandler) { - GregorianCalendar cal = new GregorianCalendar(getTimeZone(), - getLocale()); - cal.setTime(start); - while (cal.getTime().before(end)) { - Date s = cal.getTime(); - cal.add(java.util.Calendar.MINUTE, 30); - Date e = cal.getTime(); - CalendarDateRange range = new CalendarDateRange(s, e, - getTimeZone()); - Action[] actions = actionHandler.getActions(range, this); - if (actions != null) { - Set actionSet = new LinkedHashSet( - Arrays.asList(actions)); - actionMap.put(range, actionSet); - } - } - } - - private void setActionsForDay(Map> actionMap, - Date start, Date end, Action.Handler actionHandler) { - CalendarDateRange range = new CalendarDateRange(start, end, - getTimeZone()); - Action[] actions = actionHandler.getActions(range, this); - if (actions != null) { - Set actionSet = new LinkedHashSet( - Arrays.asList(actions)); - actionMap.put(range, actionSet); - } - } - - private List createActionsList( - Map> actionMap) { - if (actionMap.isEmpty()) { - return null; - } - - List calendarActions = new ArrayList(); - - SimpleDateFormat formatter = new SimpleDateFormat( - DateConstants.ACTION_DATE_FORMAT_PATTERN); - formatter.setTimeZone(getTimeZone()); - - for (Entry> entry : actionMap - .entrySet()) { - CalendarDateRange range = entry.getKey(); - Set actions = entry.getValue(); - for (Action action : actions) { - String key = actionMapper.key(action); - CalendarState.Action calendarAction = new CalendarState.Action(); - calendarAction.actionKey = key; - calendarAction.caption = action.getCaption(); - setResource(key, action.getIcon()); - calendarAction.iconKey = key; - calendarAction.startDate = formatter.format(range.getStart()); - calendarAction.endDate = formatter.format(range.getEnd()); - calendarActions.add(calendarAction); - } - } - - return calendarActions; - } - - /** - * Gets currently active time format. Value is either TimeFormat.Format12H - * or TimeFormat.Format24H. - * - * @return TimeFormat Format for the time. - */ - public TimeFormat getTimeFormat() { - if (currentTimeFormat == null) { - SimpleDateFormat f; - if (getLocale() == null) { - f = (SimpleDateFormat) SimpleDateFormat - .getTimeInstance(SimpleDateFormat.SHORT); - } else { - f = (SimpleDateFormat) SimpleDateFormat - .getTimeInstance(SimpleDateFormat.SHORT, getLocale()); - } - String p = f.toPattern(); - if (p.indexOf("HH") != -1 || p.indexOf("H") != -1) { - return TimeFormat.Format24H; - } - return TimeFormat.Format12H; - } - return currentTimeFormat; - } - - /** - * Example: setTimeFormat(TimeFormat.Format12H);
    - * Set to null, if you want the format being defined by the locale. - * - * @param format - * Set 12h or 24h format. Default is defined by the locale. - */ - public void setTimeFormat(TimeFormat format) { - currentTimeFormat = format; - markAsDirty(); - } - - /** - * Returns a time zone that is currently used by this component. - * - * @return Component's Time zone - */ - public TimeZone getTimeZone() { - if (timezone == null) { - return currentCalendar.getTimeZone(); - } - return timezone; - } - - /** - * Set time zone that this component will use. Null value sets the default - * time zone. - * - * @param zone - * Time zone to use - */ - public void setTimeZone(TimeZone zone) { - timezone = zone; - if (!currentCalendar.getTimeZone().equals(zone)) { - if (zone == null) { - zone = TimeZone.getDefault(); - } - currentCalendar.setTimeZone(zone); - df_date_time.setTimeZone(zone); - markAsDirty(); - } - } - - /** - * Get the internally used Calendar instance. This is the currently used - * instance of {@link java.util.Calendar} but is bound to change during the - * lifetime of the component. - * - * @return the currently used java calendar - */ - public java.util.Calendar getInternalCalendar() { - return currentCalendar; - } - - /** - *

    - * This method restricts the weekdays that are shown. This affects both the - * monthly and the weekly view. The general contract is that firstDay < - * lastDay. - *

    - * - *

    - * Note that this only affects the rendering process. Events are still - * requested by the dates set by {@link #setStartDate(Date)} and - * {@link #setEndDate(Date)}. - *

    - * - * @param firstDay - * the first day of the week to show, between 1 and 7 - */ - public void setFirstVisibleDayOfWeek(int firstDay) { - if (this.firstDay != firstDay && firstDay >= 1 && firstDay <= 7 - && getLastVisibleDayOfWeek() >= firstDay) { - this.firstDay = firstDay; - getState().firstVisibleDayOfWeek = firstDay; - } - } - - /** - * Get the first visible day of the week. Returns the weekdays as integers - * represented by {@link java.util.Calendar#DAY_OF_WEEK} - * - * @return An integer representing the week day according to - * {@link java.util.Calendar#DAY_OF_WEEK} - */ - public int getFirstVisibleDayOfWeek() { - return firstDay; - } - - /** - *

    - * This method restricts the weekdays that are shown. This affects both the - * monthly and the weekly view. The general contract is that firstDay < - * lastDay. - *

    - * - *

    - * Note that this only affects the rendering process. Events are still - * requested by the dates set by {@link #setStartDate(Date)} and - * {@link #setEndDate(Date)}. - *

    - * - * @param lastDay - * the first day of the week to show, between 1 and 7 - */ - public void setLastVisibleDayOfWeek(int lastDay) { - if (this.lastDay != lastDay && lastDay >= 1 && lastDay <= 7 - && getFirstVisibleDayOfWeek() <= lastDay) { - this.lastDay = lastDay; - getState().lastVisibleDayOfWeek = lastDay; - } - } - - /** - * Get the last visible day of the week. Returns the weekdays as integers - * represented by {@link java.util.Calendar#DAY_OF_WEEK} - * - * @return An integer representing the week day according to - * {@link java.util.Calendar#DAY_OF_WEEK} - */ - public int getLastVisibleDayOfWeek() { - return lastDay; - } - - /** - *

    - * This method restricts the hours that are shown per day. This affects the - * weekly view. The general contract is that firstHour < lastHour. - *

    - * - *

    - * Note that this only affects the rendering process. Events are still - * requested by the dates set by {@link #setStartDate(Date)} and - * {@link #setEndDate(Date)}. - *

    - * - * @param firstHour - * the first hour of the day to show, between 0 and 23 - */ - public void setFirstVisibleHourOfDay(int firstHour) { - if (this.firstHour != firstHour && firstHour >= 0 && firstHour <= 23 - && firstHour <= getLastVisibleHourOfDay()) { - this.firstHour = firstHour; - getState().firstHourOfDay = firstHour; - } - } - - /** - * Returns the first visible hour in the week view. Returns the hour using a - * 24h time format - * - */ - public int getFirstVisibleHourOfDay() { - return firstHour; - } - - /** - *

    - * This method restricts the hours that are shown per day. This affects the - * weekly view. The general contract is that firstHour < lastHour. - *

    - * - *

    - * Note that this only affects the rendering process. Events are still - * requested by the dates set by {@link #setStartDate(Date)} and - * {@link #setEndDate(Date)}. - *

    - * - * @param lastHour - * the first hour of the day to show, between 0 and 23 - */ - public void setLastVisibleHourOfDay(int lastHour) { - if (this.lastHour != lastHour && lastHour >= 0 && lastHour <= 23 - && lastHour >= getFirstVisibleHourOfDay()) { - this.lastHour = lastHour; - getState().lastHourOfDay = lastHour; - } - } - - /** - * Returns the last visible hour in the week view. Returns the hour using a - * 24h time format - * - */ - public int getLastVisibleHourOfDay() { - return lastHour; - } - - /** - * Gets the date caption format for the weekly view. - * - * @return The pattern used in caption of dates in weekly view. - */ - public String getWeeklyCaptionFormat() { - return weeklyCaptionFormat; - } - - /** - * Sets custom date format for the weekly view. This is the caption of the - * date. Format could be like "mmm MM/dd". - * - * @param dateFormatPattern - * The date caption pattern. - */ - public void setWeeklyCaptionFormat(String dateFormatPattern) { - if ((weeklyCaptionFormat == null && dateFormatPattern != null) - || (weeklyCaptionFormat != null - && !weeklyCaptionFormat.equals(dateFormatPattern))) { - weeklyCaptionFormat = dateFormatPattern; - markAsDirty(); - } - } - - private DateFormat getWeeklyCaptionFormatter() { - if (weeklyCaptionFormat != null) { - return new SimpleDateFormat(weeklyCaptionFormat, getLocale()); - } else { - return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT, - getLocale()); - } - } - - /** - * Get the day of week by the given calendar and its locale - * - * @param calendar - * The calendar to use - * @return - */ - private static int getDowByLocale(java.util.Calendar calendar) { - int fow = calendar.get(java.util.Calendar.DAY_OF_WEEK); - - // monday first - if (calendar.getFirstDayOfWeek() == java.util.Calendar.MONDAY) { - fow = (fow == java.util.Calendar.SUNDAY) ? 7 : fow - 1; - } - - return fow; - } - - /** - * Is the user allowed to trigger events which alters the events - * - * @return true if the client is allowed to send changes to server - * @see #isEventClickAllowed() - */ - protected boolean isClientChangeAllowed() { - return !isReadOnly(); - } - - /** - * Is the user allowed to trigger click events. Returns {@code true} by - * default. Subclass can override this method to disallow firing event - * clicks got from the client side. - * - * @return true if the client is allowed to click events - * @see #isClientChangeAllowed() - * @deprecated As of 7.4, override {@link #fireEventClick(Integer)} instead. - */ - @Deprecated - protected boolean isEventClickAllowed() { - return true; - } - - /** - * Fires an event when the user selecing moving forward/backward in the - * calendar. - * - * @param forward - * True if the calendar moved forward else backward is assumed. - */ - protected void fireNavigationEvent(boolean forward) { - if (forward) { - fireEvent(new ForwardEvent(this)); - } else { - fireEvent(new BackwardEvent(this)); - } - } - - /** - * Fires an event move event to all server side move listerners - * - * @param index - * The index of the event in the events list - * @param newFromDatetime - * The changed from date time - */ - protected void fireEventMove(int index, Date newFromDatetime) { - MoveEvent event = new MoveEvent(this, events.get(index), - newFromDatetime); - - if (calendarEventProvider instanceof EventMoveHandler) { - // Notify event provider if it is an event move handler - ((EventMoveHandler) calendarEventProvider).eventMove(event); - } - - // Notify event move handler attached by using the - // setHandler(EventMoveHandler) method - fireEvent(event); - } - - /** - * Fires event when a week was clicked in the calendar. - * - * @param week - * The week that was clicked - * @param year - * The year of the week - */ - protected void fireWeekClick(int week, int year) { - fireEvent(new WeekClick(this, week, year)); - } - - /** - * Fires event when a date was clicked in the calendar. Uses an existing - * event from the event cache. - * - * @param index - * The index of the event in the event cache. - */ - protected void fireEventClick(Integer index) { - fireEvent(new EventClick(this, events.get(index))); - } - - /** - * Fires event when a date was clicked in the calendar. Creates a new event - * for the date and passes it to the listener. - * - * @param date - * The date and time that was clicked - */ - protected void fireDateClick(Date date) { - fireEvent(new DateClickEvent(this, date)); - } - - /** - * Fires an event range selected event. The event is fired when a user - * highlights an area in the calendar. The highlighted areas start and end - * dates are returned as arguments. - * - * @param from - * The start date and time of the highlighted area - * @param to - * The end date and time of the highlighted area - * @param monthlyMode - * Is the calendar in monthly mode - */ - protected void fireRangeSelect(Date from, Date to, boolean monthlyMode) { - fireEvent(new RangeSelectEvent(this, from, to, monthlyMode)); - } - - /** - * Fires an event resize event. The event is fired when a user resizes the - * event in the calendar causing the time range of the event to increase or - * decrease. The new start and end times are returned as arguments to this - * method. - * - * @param index - * The index of the event in the event cache - * @param startTime - * The new start date and time of the event - * @param endTime - * The new end date and time of the event - */ - protected void fireEventResize(int index, Date startTime, Date endTime) { - EventResize event = new EventResize(this, events.get(index), startTime, - endTime); - - if (calendarEventProvider instanceof EventResizeHandler) { - // Notify event provider if it is an event resize handler - ((EventResizeHandler) calendarEventProvider).eventResize(event); - } - - // Notify event resize handler attached by using the - // setHandler(EventMoveHandler) method - fireEvent(event); - } - - /** - * Localized display names for week days starting from sunday. Returned - * array's length is always 7. - * - * @return Array of localized weekday names. - */ - protected String[] getDayNamesShort() { - DateFormatSymbols s = new DateFormatSymbols(getLocale()); - return Arrays.copyOfRange(s.getWeekdays(), 1, 8); - } - - /** - * Localized display names for months starting from January. Returned - * array's length is always 12. - * - * @return Array of localized month names. - */ - protected String[] getMonthNamesShort() { - DateFormatSymbols s = new DateFormatSymbols(getLocale()); - return Arrays.copyOf(s.getShortMonths(), 12); - } - - /** - * Gets a date that is first day in the week that target given date belongs - * to. - * - * @param date - * Target date - * @return Date that is first date in same week that given date is. - */ - protected Date getFirstDateForWeek(Date date) { - int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); - currentCalendar.setTime(date); - while (firstDayOfWeek != currentCalendar - .get(java.util.Calendar.DAY_OF_WEEK)) { - currentCalendar.add(java.util.Calendar.DATE, -1); - } - return currentCalendar.getTime(); - } - - /** - * Gets a date that is last day in the week that target given date belongs - * to. - * - * @param date - * Target date - * @return Date that is last date in same week that given date is. - */ - protected Date getLastDateForWeek(Date date) { - currentCalendar.setTime(date); - currentCalendar.add(java.util.Calendar.DATE, 1); - int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); - // Roll to weeks last day using firstdayofweek. Roll until FDofW is - // found and then roll back one day. - while (firstDayOfWeek != currentCalendar - .get(java.util.Calendar.DAY_OF_WEEK)) { - currentCalendar.add(java.util.Calendar.DATE, 1); - } - currentCalendar.add(java.util.Calendar.DATE, -1); - return currentCalendar.getTime(); - } - - /** - * Calculates the end time of the day using the given calendar and date - * - * @param date - * @param calendar - * the calendar instance to be used in the calculation. The given - * instance is unchanged in this operation. - * @return the given date, with time set to the end of the day - */ - private static Date getEndOfDay(java.util.Calendar calendar, Date date) { - java.util.Calendar calendarClone = (java.util.Calendar) calendar - .clone(); - - calendarClone.setTime(date); - calendarClone.set(java.util.Calendar.MILLISECOND, - calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND)); - calendarClone.set(java.util.Calendar.SECOND, - calendarClone.getActualMaximum(java.util.Calendar.SECOND)); - calendarClone.set(java.util.Calendar.MINUTE, - calendarClone.getActualMaximum(java.util.Calendar.MINUTE)); - calendarClone.set(java.util.Calendar.HOUR, - calendarClone.getActualMaximum(java.util.Calendar.HOUR)); - calendarClone.set(java.util.Calendar.HOUR_OF_DAY, - calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY)); - - return calendarClone.getTime(); - } - - /** - * Calculates the end time of the day using the given calendar and date - * - * @param date - * @param calendar - * the calendar instance to be used in the calculation. The given - * instance is unchanged in this operation. - * @return the given date, with time set to the end of the day - */ - private static Date getStartOfDay(java.util.Calendar calendar, Date date) { - java.util.Calendar calendarClone = (java.util.Calendar) calendar - .clone(); - - calendarClone.setTime(date); - calendarClone.set(java.util.Calendar.MILLISECOND, 0); - calendarClone.set(java.util.Calendar.SECOND, 0); - calendarClone.set(java.util.Calendar.MINUTE, 0); - calendarClone.set(java.util.Calendar.HOUR, 0); - calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0); - - return calendarClone.getTime(); - } - - /** - * Finds the first day of the week and returns a day representing the start - * of that day - * - * @param start - * The actual date - * @param expandToFullWeek - * Should the returned date be moved to the start of the week - * @return If expandToFullWeek is set then it returns the first day of the - * week, else it returns a clone of the actual date with the time - * set to the start of the day - */ - protected Date expandStartDate(Date start, boolean expandToFullWeek) { - // If the duration is more than week, use monthly view and get startweek - // and endweek. Example if views daterange is from tuesday to next weeks - // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = - // monday - if (expandToFullWeek) { - start = getFirstDateForWeek(start); - - } else { - start = (Date) start.clone(); - } - - // Always expand to the start of the first day to the end of the last - // day - start = getStartOfDay(currentCalendar, start); - - return start; - } - - /** - * Finds the last day of the week and returns a day representing the end of - * that day - * - * @param end - * The actual date - * @param expandToFullWeek - * Should the returned date be moved to the end of the week - * @return If expandToFullWeek is set then it returns the last day of the - * week, else it returns a clone of the actual date with the time - * set to the end of the day - */ - protected Date expandEndDate(Date end, boolean expandToFullWeek) { - // If the duration is more than week, use monthly view and get startweek - // and endweek. Example if views daterange is from tuesday to next weeks - // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = - // monday - if (expandToFullWeek) { - end = getLastDateForWeek(end); - - } else { - end = (Date) end.clone(); - } - - // Always expand to the start of the first day to the end of the last - // day - end = getEndOfDay(currentCalendar, end); - - return end; - } - - /** - * Set the {@link com.vaadin.addon.calendar.event.CalendarEventProvider - * CalendarEventProvider} to be used with this calendar. The EventProvider - * is used to query for events to show, and must be non-null. By default a - * {@link com.vaadin.addon.calendar.event.BasicEventProvider - * BasicEventProvider} is used. - * - * @param calendarEventProvider - * the calendarEventProvider to set. Cannot be null. - */ - public void setEventProvider(CalendarEventProvider calendarEventProvider) { - if (calendarEventProvider == null) { - throw new IllegalArgumentException( - "Calendar event provider cannot be null"); - } - - // remove old listener - if (getEventProvider() instanceof EventSetChangeNotifier) { - ((EventSetChangeNotifier) getEventProvider()) - .removeEventSetChangeListener(this); - } - - this.calendarEventProvider = calendarEventProvider; - - // add new listener - if (calendarEventProvider instanceof EventSetChangeNotifier) { - ((EventSetChangeNotifier) calendarEventProvider) - .addEventSetChangeListener(this); - } - } - - /** - * @return the {@link com.vaadin.addon.calendar.event.CalendarEventProvider - * CalendarEventProvider} currently used - */ - public CalendarEventProvider getEventProvider() { - return calendarEventProvider; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.ui.CalendarEvents.EventChangeListener# - * eventChange (com.vaadin.addon.calendar.ui.CalendarEvents.EventChange) - */ - @Override - public void eventSetChange(EventSetChangeEvent changeEvent) { - // sanity check - if (calendarEventProvider == changeEvent.getProvider()) { - markAsDirty(); - } - } - - /** - * Set the handler for the given type information. Mirrors - * {@link #addListener(String, Class, Object, Method) addListener} from - * AbstractComponent - * - * @param eventId - * A unique id for the event. Usually one of - * {@link CalendarEventId} - * @param eventType - * The class of the event, most likely a subclass of - * {@link CalendarComponentEvent} - * @param listener - * A listener that listens to the given event - * @param listenerMethod - * The method on the lister to call when the event is triggered - */ - protected void setHandler(String eventId, Class eventType, - EventListener listener, Method listenerMethod) { - if (handlers.get(eventId) != null) { - removeListener(eventId, eventType, handlers.get(eventId)); - handlers.remove(eventId); - } - - if (listener != null) { - addListener(eventId, eventType, listener, listenerMethod); - handlers.put(eventId, listener); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler) - */ - @Override - public void setHandler(ForwardHandler listener) { - setHandler(ForwardEvent.EVENT_ID, ForwardEvent.class, listener, - ForwardHandler.forwardMethod); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler) - */ - @Override - public void setHandler(BackwardHandler listener) { - setHandler(BackwardEvent.EVENT_ID, BackwardEvent.class, listener, - BackwardHandler.backwardMethod); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler) - */ - @Override - public void setHandler(DateClickHandler listener) { - setHandler(DateClickEvent.EVENT_ID, DateClickEvent.class, listener, - DateClickHandler.dateClickMethod); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventClickHandler) - */ - @Override - public void setHandler(EventClickHandler listener) { - setHandler(EventClick.EVENT_ID, EventClick.class, listener, - EventClickHandler.eventClickMethod); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler) - */ - @Override - public void setHandler(WeekClickHandler listener) { - setHandler(WeekClick.EVENT_ID, WeekClick.class, listener, - WeekClickHandler.weekClickMethod); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler - * ) - */ - @Override - public void setHandler(EventResizeHandler listener) { - setHandler(EventResize.EVENT_ID, EventResize.class, listener, - EventResizeHandler.eventResizeMethod); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectHandler - * ) - */ - @Override - public void setHandler(RangeSelectHandler listener) { - setHandler(RangeSelectEvent.EVENT_ID, RangeSelectEvent.class, listener, - RangeSelectHandler.rangeSelectMethod); - - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler) - */ - @Override - public void setHandler(EventMoveHandler listener) { - setHandler(MoveEvent.EVENT_ID, MoveEvent.class, listener, - EventMoveHandler.eventMoveMethod); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents. - * CalendarEventNotifier #getHandler(java.lang.String) - */ - @Override - public EventListener getHandler(String eventId) { - return handlers.get(eventId); - } - - /** - * Get the currently active drop handler - */ - @Override - public DropHandler getDropHandler() { - return dropHandler; - } - - /** - * Set the drop handler for the calendar See {@link DropHandler} for - * implementation details. - * - * @param dropHandler - * The drop handler to set - */ - public void setDropHandler(DropHandler dropHandler) { - this.dropHandler = dropHandler; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map) - */ - @Override - public TargetDetails translateDropTargetDetails( - Map clientVariables) { - Map serverVariables = new HashMap(); - - if (clientVariables.containsKey("dropSlotIndex")) { - int slotIndex = (Integer) clientVariables.get("dropSlotIndex"); - int dayIndex = (Integer) clientVariables.get("dropDayIndex"); - - currentCalendar.setTime(getStartOfDay(currentCalendar, startDate)); - currentCalendar.add(java.util.Calendar.DATE, dayIndex); - - // change this if slot length is modified - currentCalendar.add(java.util.Calendar.MINUTE, slotIndex * 30); - - serverVariables.put("dropTime", currentCalendar.getTime()); - - } else { - int dayIndex = (Integer) clientVariables.get("dropDayIndex"); - currentCalendar.setTime(expandStartDate(startDate, true)); - currentCalendar.add(java.util.Calendar.DATE, dayIndex); - serverVariables.put("dropDay", currentCalendar.getTime()); - } - serverVariables.put("mouseEvent", clientVariables.get("mouseEvent")); - - CalendarTargetDetails td = new CalendarTargetDetails(serverVariables, - this); - td.setHasDropTime(clientVariables.containsKey("dropSlotIndex")); - - return td; - } - - /** - * Sets a container as a data source for the events in the calendar. - * Equivalent for doing - * Calendar.setEventProvider(new ContainerEventProvider(container)) - * - * Use this method if you are adding a container which uses the default - * property ids like {@link BeanItemContainer} for instance. If you are - * using custom properties instead use - * {@link Calendar#setContainerDataSource(com.vaadin.data.Container.Indexed, Object, Object, Object, Object, Object)} - * - * Please note that the container must be sorted by date! - * - * @param container - * The container to use as a datasource - */ - public void setContainerDataSource(Container.Indexed container) { - ContainerEventProvider provider = new ContainerEventProvider(container); - provider.addEventSetChangeListener( - new CalendarEventProvider.EventSetChangeListener() { - @Override - public void eventSetChange( - EventSetChangeEvent changeEvent) { - // Repaint if events change - markAsDirty(); - } - }); - provider.addEventChangeListener(new EventChangeListener() { - @Override - public void eventChange(EventChangeEvent changeEvent) { - // Repaint if event changes - markAsDirty(); - } - }); - setEventProvider(provider); - } - - /** - * Sets a container as a data source for the events in the calendar. - * Equivalent for doing - * Calendar.setEventProvider(new ContainerEventProvider(container)) - * - * Please note that the container must be sorted by date! - * - * @param container - * The container to use as a data source - * @param captionProperty - * The property that has the caption, null if no caption property - * is present - * @param descriptionProperty - * The property that has the description, null if no description - * property is present - * @param startDateProperty - * The property that has the starting date - * @param endDateProperty - * The property that has the ending date - * @param styleNameProperty - * The property that has the stylename, null if no stylname - * property is present - */ - public void setContainerDataSource(Container.Indexed container, - Object captionProperty, Object descriptionProperty, - Object startDateProperty, Object endDateProperty, - Object styleNameProperty) { - ContainerEventProvider provider = new ContainerEventProvider(container); - provider.setCaptionProperty(captionProperty); - provider.setDescriptionProperty(descriptionProperty); - provider.setStartDateProperty(startDateProperty); - provider.setEndDateProperty(endDateProperty); - provider.setStyleNameProperty(styleNameProperty); - provider.addEventSetChangeListener( - new CalendarEventProvider.EventSetChangeListener() { - @Override - public void eventSetChange( - EventSetChangeEvent changeEvent) { - // Repaint if events change - markAsDirty(); - } - }); - provider.addEventChangeListener(new EventChangeListener() { - @Override - public void eventChange(EventChangeEvent changeEvent) { - // Repaint if event changes - markAsDirty(); - } - }); - setEventProvider(provider); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. - * util.Date, java.util.Date) - */ - @Override - public List getEvents(Date startDate, Date endDate) { - return getEventProvider().getEvents(startDate, endDate); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent - * (com.vaadin.addon.calendar.event.CalendarEvent) - */ - @Override - public void addEvent(CalendarEvent event) { - if (getEventProvider() instanceof CalendarEditableEventProvider) { - CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); - provider.addEvent(event); - markAsDirty(); - } else { - throw new UnsupportedOperationException( - "Event provider does not support adding events"); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent - * (com.vaadin.addon.calendar.event.CalendarEvent) - */ - @Override - public void removeEvent(CalendarEvent event) { - if (getEventProvider() instanceof CalendarEditableEventProvider) { - CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); - provider.removeEvent(event); - markAsDirty(); - } else { - throw new UnsupportedOperationException( - "Event provider does not support removing events"); - } - } - - /** - * Adds an action handler to the calender that handles event produced by the - * context menu. - * - *

    - * The {@link Handler#getActions(Object, Object)} parameters depend on what - * view the Calendar is in: - *

      - *
    • If the Calendar is in Day or Week View then the target - * parameter will be a {@link CalendarDateRange} with a range of - * half-an-hour. The {@link Handler#getActions(Object, Object)} method will - * be called once per half-hour slot.
    • - *
    • If the Calendar is in Month View then the target parameter - * will be a {@link CalendarDateRange} with a range of one day. The - * {@link Handler#getActions(Object, Object)} will be called once for each - * day. - *
    - * The Dates passed into the {@link CalendarDateRange} are in the same - * timezone as the calendar is. - *

    - * - *

    - * The {@link Handler#handleAction(Action, Object, Object)} parameters - * depend on what the context menu is called upon: - *

      - *
    • If the context menu is called upon an event then the target parameter - * is the event, i.e. instanceof {@link CalendarEvent}
    • - *
    • If the context menu is called upon an empty slot then the target is a - * {@link Date} representing that slot - *
    - *

    - */ - @Override - public void addActionHandler(Handler actionHandler) { - if (actionHandler != null) { - if (actionHandlers == null) { - actionHandlers = new LinkedList(); - actionMapper = new KeyMapper(); - } - if (!actionHandlers.contains(actionHandler)) { - actionHandlers.add(actionHandler); - markAsDirty(); - } - } - } - - /** - * Is the calendar in a mode where all days of the month is shown - * - * @return Returns true if calendar is in monthly mode and false if it is in - * weekly mode - */ - public boolean isMonthlyMode() { - CalendarState state = getState(false); - if (state.days != null) { - return state.days.size() > 7; - } else { - // Default mode - return true; - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.Action.Container#removeActionHandler(com.vaadin.event - * .Action.Handler) - */ - @Override - public void removeActionHandler(Handler actionHandler) { - if (actionHandlers != null && actionHandlers.contains(actionHandler)) { - actionHandlers.remove(actionHandler); - if (actionHandlers.isEmpty()) { - actionHandlers = null; - actionMapper = null; - } - markAsDirty(); - } - } - - private class CalendarServerRpcImpl implements CalendarServerRpc { - - @Override - public void eventMove(int eventIndex, String newDate) { - if (!isClientChangeAllowed()) { - return; - } - if (newDate != null) { - try { - Date d = df_date_time.parse(newDate); - if (eventIndex >= 0 && eventIndex < events.size() - && events.get(eventIndex) != null) { - fireEventMove(eventIndex, d); - } - } catch (ParseException e) { - getLogger().log(Level.WARNING, e.getMessage()); - } - } - } - - @Override - public void rangeSelect(String range) { - if (!isClientChangeAllowed()) { - return; - } - - if (range != null && range.length() > 14 && range.contains("TO")) { - String[] dates = range.split("TO"); - try { - Date d1 = df_date.parse(dates[0]); - Date d2 = df_date.parse(dates[1]); - - fireRangeSelect(d1, d2, true); - - } catch (ParseException e) { - // NOP - } - } else if (range != null && range.length() > 12 - && range.contains(":")) { - String[] dates = range.split(":"); - if (dates.length == 3) { - try { - Date d = df_date.parse(dates[0]); - currentCalendar.setTime(d); - int startMinutes = Integer.parseInt(dates[1]); - int endMinutes = Integer.parseInt(dates[2]); - currentCalendar.add(java.util.Calendar.MINUTE, - startMinutes); - Date start = currentCalendar.getTime(); - currentCalendar.add(java.util.Calendar.MINUTE, - endMinutes - startMinutes); - Date end = currentCalendar.getTime(); - fireRangeSelect(start, end, false); - } catch (ParseException e) { - // NOP - } catch (NumberFormatException e) { - // NOP - } - } - } - } - - @Override - public void forward() { - fireEvent(new ForwardEvent(Calendar.this)); - } - - @Override - public void backward() { - fireEvent(new BackwardEvent(Calendar.this)); - } - - @Override - public void dateClick(String date) { - if (date != null && date.length() > 6) { - try { - Date d = df_date.parse(date); - fireDateClick(d); - } catch (ParseException e) { - } - } - } - - @Override - public void weekClick(String event) { - if (event.length() > 0 && event.contains("w")) { - String[] splitted = event.split("w"); - if (splitted.length == 2) { - try { - int yr = Integer.parseInt(splitted[0]); - int week = Integer.parseInt(splitted[1]); - fireWeekClick(week, yr); - } catch (NumberFormatException e) { - // NOP - } - } - } - } - - @Override - public void eventClick(int eventIndex) { - if (!isEventClickAllowed()) { - return; - } - if (eventIndex >= 0 && eventIndex < events.size() - && events.get(eventIndex) != null) { - fireEventClick(eventIndex); - } - } - - @Override - public void eventResize(int eventIndex, String newStartDate, - String newEndDate) { - if (!isClientChangeAllowed()) { - return; - } - if (newStartDate != null && !"".equals(newStartDate) - && newEndDate != null && !"".equals(newEndDate)) { - try { - Date newStartTime = df_date_time.parse(newStartDate); - Date newEndTime = df_date_time.parse(newEndDate); - - fireEventResize(eventIndex, newStartTime, newEndTime); - } catch (ParseException e) { - // NOOP - } - } - } - - @Override - public void scroll(int scrollPosition) { - scrollTop = scrollPosition; - markAsDirty(); - } - - @Override - public void actionOnEmptyCell(String actionKey, String startDate, - String endDate) { - Action action = actionMapper.get(actionKey); - SimpleDateFormat formatter = new SimpleDateFormat( - DateConstants.ACTION_DATE_FORMAT_PATTERN); - formatter.setTimeZone(getTimeZone()); - try { - Date start = formatter.parse(startDate); - for (Action.Handler ah : actionHandlers) { - ah.handleAction(action, Calendar.this, start); - } - - } catch (ParseException e) { - getLogger().log(Level.WARNING, - "Could not parse action date string"); - } - - } - - @Override - public void actionOnEvent(String actionKey, String startDate, - String endDate, int eventIndex) { - Action action = actionMapper.get(actionKey); - SimpleDateFormat formatter = new SimpleDateFormat( - DateConstants.ACTION_DATE_FORMAT_PATTERN); - formatter.setTimeZone(getTimeZone()); - for (Action.Handler ah : actionHandlers) { - ah.handleAction(action, Calendar.this, events.get(eventIndex)); - } - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.server.VariableOwner#changeVariables(java.lang.Object, - * java.util.Map) - */ - @Override - public void changeVariables(Object source, Map variables) { - /* - * Only defined to fulfill the LegacyComponent interface used for - * calendar drag & drop. No implementation required. - */ - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.ui.LegacyComponent#paintContent(com.vaadin.server.PaintTarget) - */ - @Override - public void paintContent(PaintTarget target) throws PaintException { - if (dropHandler != null) { - dropHandler.getAcceptCriterion().paint(target); - } - } - - /** - * Sets whether the event captions are rendered as HTML. - *

    - * If set to true, the captions are rendered in the browser as HTML and the - * developer is responsible for ensuring no harmful HTML is used. If set to - * false, the caption is rendered in the browser as plain text. - *

    - * The default is false, i.e. to render that caption as plain text. - * - * @param captionAsHtml - * true if the captions are rendered as HTML, false if rendered - * as plain text - */ - public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) { - getState().eventCaptionAsHtml = eventCaptionAsHtml; - } - - /** - * Checks whether event captions are rendered as HTML - *

    - * The default is false, i.e. to render that caption as plain text. - * - * @return true if the captions are rendered as HTML, false if rendered as - * plain text - */ - public boolean isEventCaptionAsHtml() { - return getState(false).eventCaptionAsHtml; - } - - @Override - public void readDesign(Element design, DesignContext designContext) { - super.readDesign(design, designContext); - - Attributes attr = design.attributes(); - if (design.hasAttr("time-format")) { - setTimeFormat(TimeFormat.valueOf( - "Format" + design.attr("time-format").toUpperCase())); - } - - if (design.hasAttr("start-date")) { - setStartDate(DesignAttributeHandler.readAttribute("start-date", - attr, Date.class)); - } - if (design.hasAttr("end-date")) { - setEndDate(DesignAttributeHandler.readAttribute("end-date", attr, - Date.class)); - } - }; - - @Override - public void writeDesign(Element design, DesignContext designContext) { - super.writeDesign(design, designContext); - - if (currentTimeFormat != null) { - design.attr("time-format", - (currentTimeFormat == TimeFormat.Format12H ? "12h" - : "24h")); - } - if (startDate != null) { - design.attr("start-date", df_date.format(getStartDate())); - } - if (endDate != null) { - design.attr("end-date", df_date.format(getEndDate())); - } - if (!getTimeZone().equals(TimeZone.getDefault())) { - design.attr("time-zone", getTimeZone().getID()); - } - } - - @Override - protected Collection getCustomAttributes() { - Collection customAttributes = super.getCustomAttributes(); - customAttributes.add("time-format"); - customAttributes.add("start-date"); - customAttributes.add("end-date"); - return customAttributes; - } - - /** - * Allow setting first day of week independent of Locale. Set to null if you - * want first day of week being defined by the locale - * - * @since 7.6 - * @param dayOfWeek - * any of java.util.Calendar.SUNDAY..java.util.Calendar.SATURDAY - * or null to revert to default first day of week by locale - */ - public void setFirstDayOfWeek(Integer dayOfWeek) { - int minimalSupported = java.util.Calendar.SUNDAY; - int maximalSupported = java.util.Calendar.SATURDAY; - if (dayOfWeek != null && (dayOfWeek < minimalSupported - || dayOfWeek > maximalSupported)) { - throw new IllegalArgumentException(String.format( - "Day of week must be between %s and %s. Actually received: %s", - minimalSupported, maximalSupported, dayOfWeek)); - } - customFirstDayOfWeek = dayOfWeek; - markAsDirty(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/ColorPicker.java b/compatibility-server/src/main/java/com/vaadin/ui/ColorPicker.java deleted file mode 100644 index 67002373d0..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/ColorPicker.java +++ /dev/null @@ -1,67 +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 com.vaadin.shared.ui.colorpicker.Color; - -/** - * A class that defines default (button-like) implementation for a color picker - * component. - * - * @since 7.0.0 - * - * @see ColorPickerArea - * - */ -public class ColorPicker extends AbstractColorPicker { - - /** - * Instantiates a new color picker. - */ - public ColorPicker() { - super(); - } - - /** - * Instantiates a new color picker. - * - * @param popupCaption - * caption of the color select popup - */ - public ColorPicker(String popupCaption) { - super(popupCaption); - } - - /** - * Instantiates a new color picker. - * - * @param popupCaption - * caption of the color select popup - * @param initialColor - * the initial color - */ - public ColorPicker(String popupCaption, Color initialColor) { - super(popupCaption, initialColor); - setDefaultCaptionEnabled(true); - } - - @Override - protected void setDefaultStyles() { - setPrimaryStyleName(STYLENAME_BUTTON); - addStyleName(STYLENAME_DEFAULT); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/ColorPickerArea.java b/compatibility-server/src/main/java/com/vaadin/ui/ColorPickerArea.java deleted file mode 100644 index c4f3971259..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/ColorPickerArea.java +++ /dev/null @@ -1,77 +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 com.vaadin.shared.ui.colorpicker.Color; - -/** - * A class that defines area-like implementation for a color picker component. - * - * @since 7.0.0 - * - * @see ColorPicker - * - */ -public class ColorPickerArea extends AbstractColorPicker { - - /** - * Instantiates a new color picker. - */ - public ColorPickerArea() { - super(); - } - - /** - * Instantiates a new color picker. - * - * @param popupCaption - * caption of the color select popup - */ - public ColorPickerArea(String popupCaption) { - super(popupCaption); - } - - /** - * Instantiates a new color picker. - * - * @param popupCaption - * caption of the color select popup - * @param initialColor - * the initial color - */ - public ColorPickerArea(String popupCaption, Color initialColor) { - super(popupCaption, initialColor); - setDefaultCaptionEnabled(false); - } - - @Override - protected void setDefaultStyles() { - // state already has correct default - } - - @Override - public void beforeClientResponse(boolean initial) { - super.beforeClientResponse(initial); - - if ("".equals(getState().height)) { - getState().height = "30px"; - } - if ("".equals(getState().width)) { - getState().width = "30px"; - } - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/ComboBox.java b/compatibility-server/src/main/java/com/vaadin/ui/ComboBox.java deleted file mode 100644 index a78823d9a3..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/ComboBox.java +++ /dev/null @@ -1,925 +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.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import com.vaadin.data.Container; -import com.vaadin.data.util.filter.SimpleStringFilter; -import com.vaadin.event.FieldEvents; -import com.vaadin.event.FieldEvents.BlurEvent; -import com.vaadin.event.FieldEvents.BlurListener; -import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl; -import com.vaadin.event.FieldEvents.FocusEvent; -import com.vaadin.event.FieldEvents.FocusListener; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.server.Resource; -import com.vaadin.shared.ui.combobox.ComboBoxServerRpc; -import com.vaadin.shared.ui.combobox.ComboBoxState; -import com.vaadin.shared.ui.combobox.FilteringMode; - -/** - * A filtering dropdown single-select. Suitable for newItemsAllowed, but it's - * turned of by default to avoid mistakes. Items are filtered based on user - * input, and loaded dynamically ("lazy-loading") from the server. You can turn - * on newItemsAllowed and change filtering mode (and also turn it off), but you - * can not turn on multi-select mode. - * - */ -@SuppressWarnings("serial") -public class ComboBox extends AbstractSelect - implements AbstractSelect.Filtering, FieldEvents.BlurNotifier, - FieldEvents.FocusNotifier { - - /** - * ItemStyleGenerator can be used to add custom styles to combo box items - * shown in the popup. The CSS class name that will be added to the item - * style names is v-filterselect-item-[style name]. - * - * @since 7.5.6 - * @see ComboBox#setItemStyleGenerator(ItemStyleGenerator) - */ - public interface ItemStyleGenerator extends Serializable { - - /** - * Called by ComboBox when an item is painted. - * - * @param source - * the source combo box - * @param itemId - * The itemId of the item to be painted. Can be - * null if null selection is allowed. - * @return The style name to add to this item. (the CSS class name will - * be v-filterselect-item-[style name] - */ - public String getStyle(ComboBox source, Object itemId); - } - - private ComboBoxServerRpc rpc = new ComboBoxServerRpc() { - @Override - public void createNewItem(String itemValue) { - if (isNewItemsAllowed()) { - // New option entered (and it is allowed) - if (itemValue != null && itemValue.length() > 0) { - getNewItemHandler().addNewItem(itemValue); - // rebuild list - filterstring = null; - prevfilterstring = null; - } - } - } - - @Override - public void setSelectedItem(String item) { - if (item == null) { - setValue(null, true); - } else { - final Object id = itemIdMapper.get(item); - if (id != null && id.equals(getNullSelectionItemId())) { - setValue(null, true); - } else { - setValue(id, true); - } - } - } - - @Override - public void requestPage(String filter, int page) { - filterstring = filter; - if (filterstring != null) { - filterstring = filterstring.toLowerCase(getLocale()); - } - currentPage = page; - - // TODO this should trigger a data-only update instead of a full - // repaint - requestRepaint(); - } - }; - - FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl( - this) { - @Override - protected void fireEvent(Component.Event event) { - ComboBox.this.fireEvent(event); - } - }; - - // Current page when the user is 'paging' trough options - private int currentPage = -1; - - private String filterstring; - private String prevfilterstring; - - /** - * Number of options that pass the filter, excluding the null item if any. - */ - private int filteredSize; - - /** - * Cache of filtered options, used only by the in-memory filtering system. - */ - private List filteredOptions; - - /** - * Flag to indicate that request repaint is called by filter request only - */ - private boolean optionRequest; - - /** - * True while painting to suppress item set change notifications that could - * be caused by temporary filtering. - */ - private boolean isPainting; - - /** - * Flag to indicate whether to scroll the selected item visible (select the - * page on which it is) when opening the popup or not. Only applies to - * single select mode. - * - * This requires finding the index of the item, which can be expensive in - * many large lazy loading containers. - */ - private boolean scrollToSelectedItem = true; - - private ItemStyleGenerator itemStyleGenerator = null; - - public ComboBox() { - init(); - } - - public ComboBox(String caption, Collection options) { - super(caption, options); - init(); - } - - public ComboBox(String caption, Container dataSource) { - super(caption, dataSource); - init(); - } - - public ComboBox(String caption) { - super(caption); - init(); - } - - /** - * Initialize the ComboBox with default settings and register client to - * server RPC implementation. - */ - private void init() { - registerRpc(rpc); - registerRpc(focusBlurRpc); - - setNewItemsAllowed(false); - setImmediate(true); - } - - /** - * Gets the current input prompt. - * - * @see #setInputPrompt(String) - * @return the current input prompt, or null if not enabled - */ - public String getInputPrompt() { - return getState(false).inputPrompt; - } - - /** - * Sets the input prompt - a textual prompt that is displayed when the - * select would otherwise be empty, to prompt the user for input. - * - * @param inputPrompt - * the desired input prompt, or null to disable - */ - public void setInputPrompt(String inputPrompt) { - getState().inputPrompt = inputPrompt; - } - - private boolean isFilteringNeeded() { - return filterstring != null && filterstring.length() > 0 - && getFilteringMode() != FilteringMode.OFF; - } - - /** - * A class representing an item in a ComboBox for server to client - * communication. This class is for internal use only and subject to change. - * - * @since - */ - private static class ComboBoxItem implements Serializable { - String key = ""; - String caption = ""; - String style = null; - Resource icon = null; - - // constructor for a null item - public ComboBoxItem() { - } - - public ComboBoxItem(String key, String caption, String style, - Resource icon) { - this.key = key; - this.caption = caption; - this.style = style; - this.icon = icon; - } - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - isPainting = true; - try { - // clear caption change listeners - getCaptionChangeListener().clear(); - - if (isNewItemsAllowed()) { - target.addAttribute("allownewitem", true); - } - - boolean needNullSelectOption = false; - if (isNullSelectionAllowed()) { - target.addAttribute("nullselect", true); - needNullSelectOption = (getNullSelectionItemId() == null); - if (!needNullSelectOption) { - target.addAttribute("nullselectitem", true); - } - } - - // Constructs selected keys array - String[] selectedKeys = new String[(getValue() == null - && getNullSelectionItemId() == null ? 0 : 1)]; - - // Paints the options and create array of selected id keys - int keyIndex = 0; - - if (currentPage < 0) { - optionRequest = false; - currentPage = 0; - filterstring = ""; - } - - boolean nullFilteredOut = isFilteringNeeded(); - // null option is needed and not filtered out, even if not on - // current page - boolean nullOptionVisible = needNullSelectOption - && !nullFilteredOut; - - // first try if using container filters is possible - List options = getOptionsWithFilter(nullOptionVisible); - if (null == options) { - // not able to use container filters, perform explicit in-memory - // filtering - options = getFilteredOptions(); - filteredSize = options.size(); - options = sanitetizeList(options, nullOptionVisible); - } - - final boolean paintNullSelection = needNullSelectOption - && currentPage == 0 && !nullFilteredOut; - - List items = new ArrayList(); - - if (paintNullSelection) { - ComboBoxItem item = new ComboBoxItem(); - item.style = getItemStyle(null); - items.add(item); - } - - final Iterator i = options.iterator(); - // Paints the available selection options from data source - - while (i.hasNext()) { - - final Object id = i.next(); - - if (!isNullSelectionAllowed() && id != null - && id.equals(getNullSelectionItemId()) - && !isSelected(id)) { - continue; - } - - // Gets the option attribute values - final String key = itemIdMapper.key(id); - final String caption = getItemCaption(id); - final Resource icon = getItemIcon(id); - - getCaptionChangeListener().addNotifierForItem(id); - - // Prepare to paint the option - ComboBoxItem item = new ComboBoxItem(key, caption, - getItemStyle(id), icon); - items.add(item); - if (keyIndex < selectedKeys.length && isSelected(id)) { - // at most one item can be selected at a time - selectedKeys[keyIndex++] = key; - } - } - - // paint the items - target.startTag("options"); - for (ComboBoxItem item : items) { - target.startTag("so"); - if (item.icon != null) { - target.addAttribute("icon", item.icon); - } - target.addAttribute("caption", item.caption); - target.addAttribute("key", item.key); - if (item.style != null) { - target.addAttribute("style", item.style); - } - - target.endTag("so"); - } - target.endTag("options"); - - target.addAttribute("totalitems", - size() + (needNullSelectOption ? 1 : 0)); - if (filteredSize > 0 || nullOptionVisible) { - target.addAttribute("totalMatches", - filteredSize + (nullOptionVisible ? 1 : 0)); - } - - // Paint variables - target.addVariable(this, "selected", selectedKeys); - if (getValue() != null && selectedKeys[0] == null) { - // not always available, e.g. scrollToSelectedIndex=false - // Give the caption for selected item still, not to make it look - // like there is no selection at all - target.addAttribute("selectedCaption", - getItemCaption(getValue())); - } - if (isNewItemsAllowed()) { - target.addVariable(this, "newitem", ""); - } - - target.addVariable(this, "filter", filterstring); - target.addVariable(this, "page", currentPage); - - currentPage = -1; // current page is always set by client - - optionRequest = true; - } finally { - isPainting = false; - } - - } - - private String getItemStyle(Object itemId) throws PaintException { - if (itemStyleGenerator != null) { - return itemStyleGenerator.getStyle(this, itemId); - } - return null; - } - - /** - * Sets whether it is possible to input text into the field or whether the - * field area of the component is just used to show what is selected. By - * disabling text input, the comboBox will work in the same way as a - * {@link NativeSelect} - * - * @see #isTextInputAllowed() - * - * @param textInputAllowed - * true to allow entering text, false to just show the current - * selection - */ - public void setTextInputAllowed(boolean textInputAllowed) { - getState().textInputAllowed = textInputAllowed; - } - - /** - * Returns true if the user can enter text into the field to either filter - * the selections or enter a new value if {@link #isNewItemsAllowed()} - * returns true. If text input is disabled, the comboBox will work in the - * same way as a {@link NativeSelect} - * - * @return - */ - public boolean isTextInputAllowed() { - return getState(false).textInputAllowed; - } - - @Override - protected ComboBoxState getState() { - return (ComboBoxState) super.getState(); - } - - @Override - protected ComboBoxState getState(boolean markAsDirty) { - return (ComboBoxState) super.getState(markAsDirty); - } - - /** - * Returns the filtered options for the current page using a container - * filter. - * - * As a size effect, {@link #filteredSize} is set to the total number of - * items passing the filter. - * - * The current container must be {@link Filterable} and {@link Indexed}, and - * the filtering mode must be suitable for container filtering (tested with - * {@link #canUseContainerFilter()}). - * - * Use {@link #getFilteredOptions()} and - * {@link #sanitetizeList(List, boolean)} if this is not the case. - * - * @param needNullSelectOption - * @return filtered list of options (may be empty) or null if cannot use - * container filters - */ - protected List getOptionsWithFilter(boolean needNullSelectOption) { - Container container = getContainerDataSource(); - - if (getPageLength() == 0 && !isFilteringNeeded()) { - // no paging or filtering: return all items - filteredSize = container.size(); - assert filteredSize >= 0; - return new ArrayList(container.getItemIds()); - } - - if (!(container instanceof Filterable) - || !(container instanceof Indexed) - || getItemCaptionMode() != ITEM_CAPTION_MODE_PROPERTY) { - return null; - } - - Filterable filterable = (Filterable) container; - - Filter filter = buildFilter(filterstring, getFilteringMode()); - - // adding and removing filters leads to extraneous item set - // change events from the underlying container, but the ComboBox does - // not process or propagate them based on the flag filteringContainer - if (filter != null) { - filterable.addContainerFilter(filter); - } - - // try-finally to ensure that the filter is removed from container even - // if a exception is thrown... - try { - Indexed indexed = (Indexed) container; - - int indexToEnsureInView = -1; - - // if not an option request (item list when user changes page), go - // to page with the selected item after filtering if accepted by - // filter - Object selection = getValue(); - if (isScrollToSelectedItem() && !optionRequest - && selection != null) { - // ensure proper page - indexToEnsureInView = indexed.indexOfId(selection); - } - - filteredSize = container.size(); - assert filteredSize >= 0; - currentPage = adjustCurrentPage(currentPage, needNullSelectOption, - indexToEnsureInView, filteredSize); - int first = getFirstItemIndexOnCurrentPage(needNullSelectOption, - filteredSize); - int last = getLastItemIndexOnCurrentPage(needNullSelectOption, - filteredSize, first); - - // Compute the number of items to fetch from the indexes given or - // based on the filtered size of the container - int lastItemToFetch = Math.min(last, filteredSize - 1); - int nrOfItemsToFetch = (lastItemToFetch + 1) - first; - - List options = indexed.getItemIds(first, nrOfItemsToFetch); - - return options; - } finally { - // to the outside, filtering should not be visible - if (filter != null) { - filterable.removeContainerFilter(filter); - } - } - } - - /** - * Constructs a filter instance to use when using a Filterable container in - * the ITEM_CAPTION_MODE_PROPERTY mode. - * - * Note that the client side implementation expects the filter string to - * apply to the item caption string it sees, so changing the behavior of - * this method can cause problems. - * - * @param filterString - * @param filteringMode - * @return - */ - protected Filter buildFilter(String filterString, - FilteringMode filteringMode) { - Filter filter = null; - - if (null != filterString && !"".equals(filterString)) { - switch (filteringMode) { - case OFF: - break; - case STARTSWITH: - filter = new SimpleStringFilter(getItemCaptionPropertyId(), - filterString, true, true); - break; - case CONTAINS: - filter = new SimpleStringFilter(getItemCaptionPropertyId(), - filterString, true, false); - break; - } - } - return filter; - } - - @Override - public void containerItemSetChange(Container.ItemSetChangeEvent event) { - if (!isPainting) { - super.containerItemSetChange(event); - } - } - - /** - * Makes correct sublist of given list of options. - * - * If paint is not an option request (affected by page or filter change), - * page will be the one where possible selection exists. - * - * Detects proper first and last item in list to return right page of - * options. Also, if the current page is beyond the end of the list, it will - * be adjusted. - * - * @param options - * @param needNullSelectOption - * flag to indicate if nullselect option needs to be taken into - * consideration - */ - private List sanitetizeList(List options, - boolean needNullSelectOption) { - - if (getPageLength() != 0 && options.size() > getPageLength()) { - - int indexToEnsureInView = -1; - - // if not an option request (item list when user changes page), go - // to page with the selected item after filtering if accepted by - // filter - Object selection = getValue(); - if (isScrollToSelectedItem() && !optionRequest - && selection != null) { - // ensure proper page - indexToEnsureInView = options.indexOf(selection); - } - - int size = options.size(); - currentPage = adjustCurrentPage(currentPage, needNullSelectOption, - indexToEnsureInView, size); - int first = getFirstItemIndexOnCurrentPage(needNullSelectOption, - size); - int last = getLastItemIndexOnCurrentPage(needNullSelectOption, size, - first); - return options.subList(first, last + 1); - } else { - return options; - } - } - - /** - * Returns the index of the first item on the current page. The index is to - * the underlying (possibly filtered) contents. The null item, if any, does - * not have an index but takes up a slot on the first page. - * - * @param needNullSelectOption - * true if a null option should be shown before any other options - * (takes up the first slot on the first page, not counted in - * index) - * @param size - * number of items after filtering (not including the null item, - * if any) - * @return first item to show on the UI (index to the filtered list of - * options, not taking the null item into consideration if any) - */ - private int getFirstItemIndexOnCurrentPage(boolean needNullSelectOption, - int size) { - // Not all options are visible, find out which ones are on the - // current "page". - int first = currentPage * getPageLength(); - if (needNullSelectOption && currentPage > 0) { - first--; - } - return first; - } - - /** - * Returns the index of the last item on the current page. The index is to - * the underlying (possibly filtered) contents. If needNullSelectOption is - * true, the null item takes up the first slot on the first page, - * effectively reducing the first page size by one. - * - * @param needNullSelectOption - * true if a null option should be shown before any other options - * (takes up the first slot on the first page, not counted in - * index) - * @param size - * number of items after filtering (not including the null item, - * if any) - * @param first - * index in the filtered view of the first item of the page - * @return index in the filtered view of the last item on the page - */ - private int getLastItemIndexOnCurrentPage(boolean needNullSelectOption, - int size, int first) { - // page length usable for non-null items - int effectivePageLength = getPageLength() - - (needNullSelectOption && (currentPage == 0) ? 1 : 0); - return Math.min(size - 1, first + effectivePageLength - 1); - } - - /** - * Adjusts the index of the current page if necessary: make sure the current - * page is not after the end of the contents, and optionally go to the page - * containg a specific item. There are no side effects but the adjusted page - * index is returned. - * - * @param page - * page number to use as the starting point - * @param needNullSelectOption - * true if a null option should be shown before any other options - * (takes up the first slot on the first page, not counted in - * index) - * @param indexToEnsureInView - * index of an item that should be included on the page (in the - * data set, not counting the null item if any), -1 for none - * @param size - * number of items after filtering (not including the null item, - * if any) - */ - private int adjustCurrentPage(int page, boolean needNullSelectOption, - int indexToEnsureInView, int size) { - if (indexToEnsureInView != -1) { - int newPage = (indexToEnsureInView + (needNullSelectOption ? 1 : 0)) - / getPageLength(); - page = newPage; - } - // adjust the current page if beyond the end of the list - if (page * getPageLength() > size) { - page = (size + (needNullSelectOption ? 1 : 0)) / getPageLength(); - } - return page; - } - - /** - * Filters the options in memory and returns the full filtered list. - * - * This can be less efficient than using container filters, so use - * {@link #getOptionsWithFilter(boolean)} if possible (filterable container - * and suitable item caption mode etc.). - * - * @return - */ - protected List getFilteredOptions() { - if (!isFilteringNeeded()) { - prevfilterstring = null; - filteredOptions = new LinkedList(getItemIds()); - return filteredOptions; - } - - if (filterstring.equals(prevfilterstring)) { - return filteredOptions; - } - - Collection items; - if (prevfilterstring != null - && filterstring.startsWith(prevfilterstring)) { - items = filteredOptions; - } else { - items = getItemIds(); - } - prevfilterstring = filterstring; - - filteredOptions = new LinkedList(); - for (final Iterator it = items.iterator(); it.hasNext();) { - final Object itemId = it.next(); - String caption = getItemCaption(itemId); - if (caption == null || caption.equals("")) { - continue; - } else { - caption = caption.toLowerCase(getLocale()); - } - switch (getFilteringMode()) { - case CONTAINS: - if (caption.indexOf(filterstring) > -1) { - filteredOptions.add(itemId); - } - break; - case STARTSWITH: - default: - if (caption.startsWith(filterstring)) { - filteredOptions.add(itemId); - } - break; - } - } - - return filteredOptions; - } - - /** - * Invoked when the value of a variable has changed. - * - * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object, - * java.util.Map) - */ - @Override - public void changeVariables(Object source, Map variables) { - // Not calling super.changeVariables due the history of select - // component hierarchy - - // all the client to server requests are now handled by RPC - } - - @Override - public void setFilteringMode(FilteringMode filteringMode) { - getState().filteringMode = filteringMode; - } - - @Override - public FilteringMode getFilteringMode() { - return getState(false).filteringMode; - } - - @Override - public void addBlurListener(BlurListener listener) { - addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, - BlurListener.blurMethod); - } - - @Override - public void removeBlurListener(BlurListener listener) { - removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); - } - - @Override - public void addFocusListener(FocusListener listener) { - addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, - FocusListener.focusMethod); - } - - @Override - public void removeFocusListener(FocusListener listener) { - removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); - } - - /** - * ComboBox does not support multi select mode. - * - * @deprecated As of 7.0, use {@link ListSelect}, {@link OptionGroup} or - * {@link TwinColSelect} instead - * @see com.vaadin.ui.AbstractSelect#setMultiSelect(boolean) - * @throws UnsupportedOperationException - * if trying to activate multiselect mode - */ - @Deprecated - @Override - public void setMultiSelect(boolean multiSelect) { - if (multiSelect) { - throw new UnsupportedOperationException( - "Multiselect not supported"); - } - } - - /** - * ComboBox does not support multi select mode. - * - * @deprecated As of 7.0, use {@link ListSelect}, {@link OptionGroup} or - * {@link TwinColSelect} instead - * - * @see com.vaadin.ui.AbstractSelect#isMultiSelect() - * - * @return false - */ - @Deprecated - @Override - public boolean isMultiSelect() { - return false; - } - - /** - * Returns the page length of the suggestion popup. - * - * @return the pageLength - */ - public int getPageLength() { - return getState(false).pageLength; - } - - /** - * Returns the suggestion pop-up's width as a CSS string. - * - * @see #setPopupWidth - * @since 7.7 - */ - public String getPopupWidth() { - return getState(false).suggestionPopupWidth; - } - - /** - * Sets the page length for the suggestion popup. Setting the page length to - * 0 will disable suggestion popup paging (all items visible). - * - * @param pageLength - * the pageLength to set - */ - public void setPageLength(int pageLength) { - getState().pageLength = pageLength; - } - - /** - * Sets the suggestion pop-up's width as a CSS string. By using relative - * units (e.g. "50%") it's possible to set the popup's width relative to the - * ComboBox itself. - * - * @see #getPopupWidth() - * @since 7.7 - * @param width - * the width - */ - public void setPopupWidth(String width) { - getState().suggestionPopupWidth = width; - } - - /** - * Sets whether to scroll the selected item visible (directly open the page - * on which it is) when opening the combo box popup or not. Only applies to - * single select mode. - * - * This requires finding the index of the item, which can be expensive in - * many large lazy loading containers. - * - * @param scrollToSelectedItem - * true to find the page with the selected item when opening the - * selection popup - */ - public void setScrollToSelectedItem(boolean scrollToSelectedItem) { - this.scrollToSelectedItem = scrollToSelectedItem; - } - - /** - * Returns true if the select should find the page with the selected item - * when opening the popup (single select combo box only). - * - * @see #setScrollToSelectedItem(boolean) - * - * @return true if the page with the selected item will be shown when - * opening the popup - */ - public boolean isScrollToSelectedItem() { - return scrollToSelectedItem; - } - - /** - * Sets the item style generator that is used to produce custom styles for - * showing items in the popup. The CSS class name that will be added to the - * item style names is v-filterselect-item-[style name]. - * - * @param itemStyleGenerator - * the item style generator to set, or null to not - * use any custom item styles - * @since 7.5.6 - */ - public void setItemStyleGenerator(ItemStyleGenerator itemStyleGenerator) { - this.itemStyleGenerator = itemStyleGenerator; - markAsDirty(); - } - - /** - * Gets the currently used item style generator. - * - * @return the itemStyleGenerator the currently used item style generator, - * or null if no generator is used - * @since 7.5.6 - */ - public ItemStyleGenerator getItemStyleGenerator() { - return itemStyleGenerator; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/DefaultFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/ui/DefaultFieldFactory.java deleted file mode 100644 index 2ee5ecf00e..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/DefaultFieldFactory.java +++ /dev/null @@ -1,114 +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.text.Normalizer.Form; -import java.util.Date; - -import com.vaadin.data.Container; -import com.vaadin.data.Property; -import com.vaadin.shared.util.SharedUtil; -import com.vaadin.v7.ui.LegacyCheckBox; -import com.vaadin.v7.ui.LegacyDateField; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyTextField; - -/** - * This class contains a basic implementation for {@link TableFieldFactory}. The - * class is singleton, use {@link #get()} method to get reference to the - * instance. - * - *

    - * There are also some static helper methods available for custom built field - * factories. - * - */ -public class DefaultFieldFactory implements TableFieldFactory { - - private static final DefaultFieldFactory instance = new DefaultFieldFactory(); - - /** - * Singleton method to get an instance of DefaultFieldFactory. - * - * @return an instance of DefaultFieldFactory - */ - public static DefaultFieldFactory get() { - return instance; - } - - protected DefaultFieldFactory() { - } - - @Override - public LegacyField createField(Container container, Object itemId, - Object propertyId, Component uiContext) { - Property containerProperty = container.getContainerProperty(itemId, - propertyId); - Class type = containerProperty.getType(); - LegacyField field = createFieldByPropertyType(type); - field.setCaption(createCaptionByPropertyId(propertyId)); - return field; - } - - /** - * If name follows method naming conventions, convert the name to spaced - * upper case text. For example, convert "firstName" to "First Name" - * - * @param propertyId - * @return the formatted caption string - */ - public static String createCaptionByPropertyId(Object propertyId) { - return SharedUtil.propertyIdToHumanFriendly(propertyId); - } - - /** - * Creates fields based on the property type. - *

    - * The default field type is {@link LegacyTextField}. Other field types - * generated by this method: - *

    - * Boolean: {@link CheckBox}.
    - * Date: {@link LegacyDateField}(resolution: day).
    - * Item: {@link Form}.
    - * default field type: {@link LegacyTextField}. - *

    - * - * @param type - * the type of the property - * @return the most suitable generic {@link LegacyField} for given type - */ - public static LegacyField createFieldByPropertyType(Class type) { - // Null typed properties can not be edited - if (type == null) { - return null; - } - - // Date field - if (Date.class.isAssignableFrom(type)) { - final LegacyDateField df = new LegacyDateField(); - df.setResolution(LegacyDateField.RESOLUTION_DAY); - return df; - } - - // Boolean field - if (Boolean.class.isAssignableFrom(type)) { - return new LegacyCheckBox(); - } - - return new LegacyTextField(); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java b/compatibility-server/src/main/java/com/vaadin/ui/LegacyGrid.java deleted file mode 100644 index 9a98593b83..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/ListSelect.java b/compatibility-server/src/main/java/com/vaadin/ui/ListSelect.java deleted file mode 100644 index 9a720d1895..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/ListSelect.java +++ /dev/null @@ -1,80 +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 com.vaadin.data.Container; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; - -/** - * This is a simple list select without, for instance, support for new items, - * lazyloading, and other advanced features. - */ -@SuppressWarnings("serial") -public class ListSelect extends AbstractSelect { - - private int rows = 0; - - public ListSelect() { - super(); - } - - public ListSelect(String caption, Collection options) { - super(caption, options); - } - - public ListSelect(String caption, Container dataSource) { - super(caption, dataSource); - } - - public ListSelect(String caption) { - super(caption); - } - - public int getRows() { - return rows; - } - - /** - * Sets the number of rows in the editor. If the number of rows is set 0, - * the actual number of displayed rows is determined implicitly by the - * adapter. - * - * @param rows - * the number of rows to set. - */ - public void setRows(int rows) { - if (rows < 0) { - rows = 0; - } - if (this.rows != rows) { - this.rows = rows; - markAsDirty(); - } - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - // Adds the number of rows - if (rows != 0) { - target.addAttribute("rows", rows); - } - super.paintContent(target); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/NativeSelect.java b/compatibility-server/src/main/java/com/vaadin/ui/NativeSelect.java deleted file mode 100644 index 13556a0387..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/NativeSelect.java +++ /dev/null @@ -1,108 +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 com.vaadin.data.Container; -import com.vaadin.event.FieldEvents; -import com.vaadin.event.FieldEvents.BlurEvent; -import com.vaadin.event.FieldEvents.BlurListener; -import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl; -import com.vaadin.event.FieldEvents.FocusEvent; -import com.vaadin.event.FieldEvents.FocusListener; - -/** - * This is a simple drop-down select without, for instance, support for - * multiselect, new items, lazyloading, and other advanced features. Sometimes - * "native" select without all the bells-and-whistles of the ComboBox is a - * better choice. - */ -@SuppressWarnings("serial") -public class NativeSelect extends AbstractSelect - implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { - - FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl( - this) { - - @Override - protected void fireEvent(Event event) { - NativeSelect.this.fireEvent(event); - } - }; - - public NativeSelect() { - super(); - registerRpc(focusBlurRpc); - } - - public NativeSelect(String caption, Collection options) { - super(caption, options); - registerRpc(focusBlurRpc); - } - - public NativeSelect(String caption, Container dataSource) { - super(caption, dataSource); - registerRpc(focusBlurRpc); - } - - public NativeSelect(String caption) { - super(caption); - registerRpc(focusBlurRpc); - } - - @Override - public void setMultiSelect(boolean multiSelect) - throws UnsupportedOperationException { - if (multiSelect == true) { - throw new UnsupportedOperationException( - "Multiselect not supported"); - } - } - - @Override - public void setNewItemsAllowed(boolean allowNewOptions) - throws UnsupportedOperationException { - if (allowNewOptions == true) { - throw new UnsupportedOperationException( - "newItemsAllowed not supported"); - } - } - - @Override - public void addFocusListener(FocusListener listener) { - addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, - FocusListener.focusMethod); - } - - @Override - public void removeFocusListener(FocusListener listener) { - removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); - } - - @Override - public void addBlurListener(BlurListener listener) { - addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, - BlurListener.blurMethod); - } - - @Override - public void removeBlurListener(BlurListener listener) { - removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/OptionGroup.java b/compatibility-server/src/main/java/com/vaadin/ui/OptionGroup.java deleted file mode 100644 index 3e491906d5..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/OptionGroup.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.ui; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.jsoup.nodes.Element; - -import com.vaadin.data.Container; -import com.vaadin.event.FieldEvents; -import com.vaadin.event.FieldEvents.BlurEvent; -import com.vaadin.event.FieldEvents.BlurListener; -import com.vaadin.event.FieldEvents.FocusEvent; -import com.vaadin.event.FieldEvents.FocusListener; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.shared.ui.optiongroup.OptionGroupConstants; -import com.vaadin.shared.ui.optiongroup.OptionGroupState; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.ui.declarative.DesignFormatter; - -/** - * Configures select to be used as an option group. - */ -@SuppressWarnings("serial") -public class OptionGroup extends AbstractSelect - implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { - - private Set disabledItemIds = new HashSet(); - - public OptionGroup() { - super(); - } - - public OptionGroup(String caption, Collection options) { - super(caption, options); - } - - public OptionGroup(String caption, Container dataSource) { - super(caption, dataSource); - } - - public OptionGroup(String caption) { - super(caption); - } - - @Override - protected void paintItem(PaintTarget target, Object itemId) - throws PaintException { - super.paintItem(target, itemId); - if (!isItemEnabled(itemId)) { - target.addAttribute(OptionGroupConstants.ATTRIBUTE_OPTION_DISABLED, - true); - } - } - - @Override - public void changeVariables(Object source, Map variables) { - super.changeVariables(source, variables); - - if (variables.containsKey(FocusEvent.EVENT_ID)) { - fireEvent(new FocusEvent(this)); - } - if (variables.containsKey(BlurEvent.EVENT_ID)) { - fireEvent(new BlurEvent(this)); - } - } - - @Override - public void addBlurListener(BlurListener listener) { - addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, - BlurListener.blurMethod); - } - - @Override - public void removeBlurListener(BlurListener listener) { - removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); - } - - @Override - public void addFocusListener(FocusListener listener) { - addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, - FocusListener.focusMethod); - } - - @Override - public void removeFocusListener(FocusListener listener) { - removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); - - } - - @Override - protected void setValue(Object newValue, boolean repaintIsNotNeeded) { - if (repaintIsNotNeeded) { - /* - * Check that value from changeVariables() doesn't contain unallowed - * selections: In the multi select mode, the user has selected or - * deselected a disabled item. In the single select mode, the user - * has selected a disabled item. - */ - if (isMultiSelect()) { - Set currentValueSet = (Set) getValue(); - Set newValueSet = (Set) newValue; - for (Object itemId : currentValueSet) { - if (!isItemEnabled(itemId) - && !newValueSet.contains(itemId)) { - markAsDirty(); - return; - } - } - for (Object itemId : newValueSet) { - if (!isItemEnabled(itemId) - && !currentValueSet.contains(itemId)) { - markAsDirty(); - return; - } - } - } else { - if (newValue == null) { - newValue = getNullSelectionItemId(); - } - if (!isItemEnabled(newValue)) { - markAsDirty(); - return; - } - } - } - super.setValue(newValue, repaintIsNotNeeded); - } - - /** - * Sets an item disabled or enabled. In the multiselect mode, a disabled - * item cannot be selected or deselected by the user. In the single - * selection mode, a disable item cannot be selected. - * - * However, programmatical selection or deselection of an disable item is - * possible. By default, items are enabled. - * - * @param itemId - * the id of the item to be disabled or enabled - * @param enabled - * if true the item is enabled, otherwise the item is disabled - */ - public void setItemEnabled(Object itemId, boolean enabled) { - if (itemId != null) { - if (enabled) { - disabledItemIds.remove(itemId); - } else { - disabledItemIds.add(itemId); - } - markAsDirty(); - } - } - - /** - * Returns true if the item is enabled. - * - * @param itemId - * the id of the item to be checked - * @return true if the item is enabled, false otherwise - * @see #setItemEnabled(Object, boolean) - */ - public boolean isItemEnabled(Object itemId) { - if (itemId != null) { - return !disabledItemIds.contains(itemId); - } - return true; - } - - /** - * Sets whether html is allowed in the item captions. If set to true, the - * captions are passed to the browser as html and the developer is - * responsible for ensuring no harmful html is used. If set to false, the - * content is passed to the browser as plain text. - * - * @param htmlContentAllowed - * true if the captions are used as html, false if used as plain - * text - */ - public void setHtmlContentAllowed(boolean htmlContentAllowed) { - getState().htmlContentAllowed = htmlContentAllowed; - } - - /** - * Checks whether captions are interpreted as html or plain text. - * - * @return true if the captions are used as html, false if used as plain - * text - * @see #setHtmlContentAllowed(boolean) - */ - public boolean isHtmlContentAllowed() { - return getState(false).htmlContentAllowed; - } - - @Override - protected Object readItem(Element child, Set selected, - DesignContext context) { - Object itemId = super.readItem(child, selected, context); - - if (child.hasAttr("disabled")) { - setItemEnabled(itemId, false); - } - - return itemId; - } - - @Override - protected Element writeItem(Element design, Object itemId, - DesignContext context) { - Element elem = super.writeItem(design, itemId, context); - - if (!isItemEnabled(itemId)) { - elem.attr("disabled", ""); - } - if (isHtmlContentAllowed()) { - // need to unencode HTML entities. AbstractSelect.writeDesign can't - // check if HTML content is allowed, so it always encodes entities - // like '>', '<' and '&'; in case HTML content is allowed this is - // undesirable so we need to unencode entities. Entities other than - // '<' and '>' will be taken care by Jsoup. - elem.html(DesignFormatter.decodeFromTextNode(elem.html())); - } - - return elem; - } - - @Override - protected OptionGroupState getState() { - return (OptionGroupState) super.getState(); - } - - @Override - protected OptionGroupState getState(boolean markAsDirty) { - return (OptionGroupState) super.getState(markAsDirty); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/RichTextArea.java b/compatibility-server/src/main/java/com/vaadin/ui/RichTextArea.java deleted file mode 100644 index e52db6d043..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/RichTextArea.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.ui; - -import java.util.Map; - -import org.jsoup.nodes.Element; - -import com.vaadin.data.Property; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.shared.ui.textarea.RichTextAreaState; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.v7.ui.LegacyAbstractField; -import com.vaadin.v7.ui.LegacyTextField; - -/** - * A simple RichTextArea to edit HTML format text. - * - * Note, that using {@link LegacyTextField#setMaxLength(int)} method in - * {@link RichTextArea} may produce unexpected results as formatting is counted - * into length of field. - */ -public class RichTextArea extends LegacyAbstractField - implements LegacyComponent { - - /** - * Null representation. - */ - private String nullRepresentation = "null"; - - /** - * Is setting to null from non-null value allowed by setting with null - * representation . - */ - private boolean nullSettingAllowed = false; - - /** - * Temporary flag that indicates all content will be selected after the next - * paint. Reset to false after painted. - */ - private boolean selectAll = false; - - /** - * Constructs an empty RichTextArea with no caption. - */ - public RichTextArea() { - setValue(""); - } - - /** - * - * Constructs an empty RichTextArea with the given caption. - * - * @param caption - * the caption for the editor. - */ - public RichTextArea(String caption) { - this(); - setCaption(caption); - } - - /** - * Constructs a new RichTextArea that's bound to the specified - * Property and has no caption. - * - * @param dataSource - * the data source for the editor value - */ - public RichTextArea(Property dataSource) { - setPropertyDataSource(dataSource); - } - - /** - * Constructs a new RichTextArea that's bound to the specified - * Property and has the given caption. - * - * @param caption - * the caption for the editor. - * @param dataSource - * the data source for the editor value - */ - public RichTextArea(String caption, Property dataSource) { - this(dataSource); - setCaption(caption); - } - - /** - * Constructs a new RichTextArea with the given caption and - * initial text contents. - * - * @param caption - * the caption for the editor. - * @param value - * the initial text content of the editor. - */ - public RichTextArea(String caption, String value) { - setValue(value); - setCaption(caption); - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - if (selectAll) { - target.addAttribute("selectAll", true); - selectAll = false; - } - - // Adds the content as variable - String value = getValue(); - if (value == null) { - value = getNullRepresentation(); - } - if (value == null) { - throw new IllegalStateException( - "Null values are not allowed if the null-representation is null"); - } - target.addVariable(this, "text", value); - - } - - @Override - public void setReadOnly(boolean readOnly) { - super.setReadOnly(readOnly); - // IE6 cannot support multi-classname selectors properly - // TODO Can be optimized now that support for I6 is dropped - if (readOnly) { - addStyleName("v-richtextarea-readonly"); - } else { - removeStyleName("v-richtextarea-readonly"); - } - } - - /** - * Selects all text in the rich text area. As a side effect, focuses the - * rich text area. - * - * @since 6.5 - */ - public void selectAll() { - /* - * Set selection range functionality is currently being - * planned/developed for GWT RTA. Only selecting all is currently - * supported. Consider moving selectAll and other selection related - * functions to AbstractTextField at that point to share the - * implementation. Some third party components extending - * AbstractTextField might however not want to support them. - */ - selectAll = true; - focus(); - markAsDirty(); - } - - @Override - public void changeVariables(Object source, Map variables) { - // Sets the text - if (variables.containsKey("text") && !isReadOnly()) { - - // Only do the setting if the string representation of the value - // has been updated - String newValue = (String) variables.get("text"); - - final String oldValue = getValue(); - if (newValue != null && (oldValue == null || isNullSettingAllowed()) - && newValue.equals(getNullRepresentation())) { - newValue = null; - } - if (newValue != oldValue - && (newValue == null || !newValue.equals(oldValue))) { - boolean wasModified = isModified(); - setValue(newValue, true); - - // If the modified status changes, - // repaint is needed after all. - if (wasModified != isModified()) { - markAsDirty(); - } - } - } - - } - - @Override - public Class getType() { - return String.class; - } - - /** - * Gets the null-string representation. - * - *

    - * The null-valued strings are represented on the user interface by - * replacing the null value with this string. If the null representation is - * set null (not 'null' string), painting null value throws exception. - *

    - * - *

    - * The default value is string 'null'. - *

    - * - * @return the String Textual representation for null strings. - * @see LegacyTextField#isNullSettingAllowed() - */ - public String getNullRepresentation() { - return nullRepresentation; - } - - /** - * Is setting nulls with null-string representation allowed. - * - *

    - * If this property is true, writing null-representation string to text - * field always sets the field value to real null. If this property is - * false, null setting is not made, but the null values are maintained. - * Maintenance of null-values is made by only converting the textfield - * contents to real null, if the text field matches the null-string - * representation and the current value of the field is null. - *

    - * - *

    - * By default this setting is false - *

    - * - * @return boolean Should the null-string represenation be always converted - * to null-values. - * @see LegacyTextField#getNullRepresentation() - */ - public boolean isNullSettingAllowed() { - return nullSettingAllowed; - } - - /** - * Sets the null-string representation. - * - *

    - * The null-valued strings are represented on the user interface by - * replacing the null value with this string. If the null representation is - * set null (not 'null' string), painting null value throws exception. - *

    - * - *

    - * The default value is string 'null' - *

    - * - * @param nullRepresentation - * Textual representation for null strings. - * @see LegacyTextField#setNullSettingAllowed(boolean) - */ - public void setNullRepresentation(String nullRepresentation) { - this.nullRepresentation = nullRepresentation; - } - - /** - * Sets the null conversion mode. - * - *

    - * If this property is true, writing null-representation string to text - * field always sets the field value to real null. If this property is - * false, null setting is not made, but the null values are maintained. - * Maintenance of null-values is made by only converting the textfield - * contents to real null, if the text field matches the null-string - * representation and the current value of the field is null. - *

    - * - *

    - * By default this setting is false. - *

    - * - * @param nullSettingAllowed - * Should the null-string represenation be always converted to - * null-values. - * @see LegacyTextField#getNullRepresentation() - */ - public void setNullSettingAllowed(boolean nullSettingAllowed) { - this.nullSettingAllowed = nullSettingAllowed; - } - - @Override - public boolean isEmpty() { - return super.isEmpty() || getValue().length() == 0; - } - - @Override - public void clear() { - setValue(""); - } - - @Override - public void readDesign(Element design, DesignContext designContext) { - super.readDesign(design, designContext); - setValue(design.html(), false, true); - } - - @Override - public void writeDesign(Element design, DesignContext designContext) { - super.writeDesign(design, designContext); - design.html(getValue()); - } - - @Override - protected RichTextAreaState getState() { - return (RichTextAreaState) super.getState(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/Select.java b/compatibility-server/src/main/java/com/vaadin/ui/Select.java deleted file mode 100644 index 66b36f8ac2..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/Select.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.ui; - -import java.util.Collection; - -import com.vaadin.data.Container; - -/** - *

    - * A class representing a selection of items the user has selected in a UI. The - * set of choices is presented as a set of {@link com.vaadin.data.Item}s in a - * {@link com.vaadin.data.Container}. - *

    - * - *

    - * A Select component may be in single- or multiselect mode. - * Multiselect mode means that more than one item can be selected - * simultaneously. - *

    - * - * @author Vaadin Ltd. - * @since 3.0 - * @deprecated As of 7.0. Use {@link ComboBox} instead. - */ -@Deprecated -public class Select extends ComboBox { - /* Component methods */ - - public Select() { - super(); - } - - public Select(String caption, Collection options) { - super(caption, options); - } - - public Select(String caption, Container dataSource) { - super(caption, dataSource); - } - - public Select(String caption) { - super(caption); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/Table.java b/compatibility-server/src/main/java/com/vaadin/ui/Table.java deleted file mode 100644 index 13333146f2..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/Table.java +++ /dev/null @@ -1,6533 +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.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -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.Map; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.ContainerOrderedWrapper; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.event.Action; -import com.vaadin.event.Action.Handler; -import com.vaadin.event.ContextClickEvent; -import com.vaadin.event.DataBoundTransferable; -import com.vaadin.event.ItemClickEvent; -import com.vaadin.event.ItemClickEvent.ItemClickListener; -import com.vaadin.event.ItemClickEvent.ItemClickNotifier; -import com.vaadin.event.MouseEvents.ClickEvent; -import com.vaadin.event.dd.DragAndDropEvent; -import com.vaadin.event.dd.DragSource; -import com.vaadin.event.dd.DropHandler; -import com.vaadin.event.dd.DropTarget; -import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; -import com.vaadin.server.KeyMapper; -import com.vaadin.server.LegacyCommunicationManager; -import com.vaadin.server.LegacyPaint; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.server.Resource; -import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.ui.MultiSelectMode; -import com.vaadin.shared.ui.table.CollapseMenuContent; -import com.vaadin.shared.ui.table.TableConstants; -import com.vaadin.shared.ui.table.TableConstants.Section; -import com.vaadin.shared.ui.table.TableServerRpc; -import com.vaadin.shared.ui.table.TableState; -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.util.ReflectTools; -import com.vaadin.v7.data.util.converter.LegacyConverter; -import com.vaadin.v7.data.util.converter.LegacyConverterUtil; -import com.vaadin.v7.ui.LegacyField; - -/** - *

    - * Table is used for representing data or components in a pageable - * and selectable table. - *

    - * - *

    - * Scalability of the Table is largely dictated by the container. A table does - * not have a limit for the number of items and is just as fast with hundreds of - * thousands of items as with just a few. The current GWT implementation with - * scrolling however limits the number of rows to around 500000, depending on - * the browser and the pixel height of rows. - *

    - * - *

    - * Components in a Table will not have their caption nor icon rendered. - *

    - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings({ "deprecation" }) -public class Table extends AbstractSelect implements Action.Container, - Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource, - DropTarget, HasComponents, HasChildMeasurementHint { - - private transient Logger logger = null; - - /** - * Modes that Table support as drag sourse. - */ - public enum TableDragMode { - /** - * Table does not start drag and drop events. HTM5 style events started - * by browser may still happen. - */ - NONE, - /** - * Table starts drag with a one row only. - */ - ROW, - /** - * Table drags selected rows, if drag starts on a selected rows. Else it - * starts like in ROW mode. Note, that in Transferable there will still - * be only the row on which the drag started, other dragged rows need to - * be checked from the source Table. - */ - MULTIROW - } - - protected static final int CELL_KEY = 0; - - protected static final int CELL_HEADER = 1; - - protected static final int CELL_ICON = 2; - - protected static final int CELL_ITEMID = 3; - - protected static final int CELL_GENERATED_ROW = 4; - - protected static final int CELL_FIRSTCOL = 5; - - public enum Align { - /** - * Left column alignment. This is the default behaviour. - */ - LEFT("b"), - - /** - * Center column alignment. - */ - CENTER("c"), - - /** - * Right column alignment. - */ - RIGHT("e"); - - private String alignment; - - private Align(String alignment) { - this.alignment = alignment; - } - - @Override - public String toString() { - return alignment; - } - - public Align convertStringToAlign(String string) { - if (string == null) { - return null; - } - if (string.equals("b")) { - return Align.LEFT; - } else if (string.equals("c")) { - return Align.CENTER; - } else if (string.equals("e")) { - return Align.RIGHT; - } else { - return null; - } - } - } - - /** - * @deprecated As of 7.0, use {@link Align#LEFT} instead - */ - @Deprecated - public static final Align ALIGN_LEFT = Align.LEFT; - - /** - * @deprecated As of 7.0, use {@link Align#CENTER} instead - */ - @Deprecated - public static final Align ALIGN_CENTER = Align.CENTER; - - /** - * @deprecated As of 7.0, use {@link Align#RIGHT} instead - */ - @Deprecated - public static final Align ALIGN_RIGHT = Align.RIGHT; - - public enum ColumnHeaderMode { - /** - * Column headers are hidden. - */ - HIDDEN, - /** - * Property ID:s are used as column headers. - */ - ID, - /** - * Column headers are explicitly specified with - * {@link #setColumnHeaders(String[])}. - */ - EXPLICIT, - /** - * Column headers are explicitly specified with - * {@link #setColumnHeaders(String[])}. If a header is not specified for - * a given property, its property id is used instead. - *

    - * This is the default behavior. - */ - EXPLICIT_DEFAULTS_ID - } - - /** - * @deprecated As of 7.0, use {@link ColumnHeaderMode#HIDDEN} instead - */ - @Deprecated - public static final ColumnHeaderMode COLUMN_HEADER_MODE_HIDDEN = ColumnHeaderMode.HIDDEN; - - /** - * @deprecated As of 7.0, use {@link ColumnHeaderMode#ID} instead - */ - @Deprecated - public static final ColumnHeaderMode COLUMN_HEADER_MODE_ID = ColumnHeaderMode.ID; - - /** - * @deprecated As of 7.0, use {@link ColumnHeaderMode#EXPLICIT} instead - */ - @Deprecated - public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT = ColumnHeaderMode.EXPLICIT; - - /** - * @deprecated As of 7.0, use {@link ColumnHeaderMode#EXPLICIT_DEFAULTS_ID} - * instead - */ - @Deprecated - public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID; - - public enum RowHeaderMode { - /** - * Row caption mode: The row headers are hidden. This is the default - * mode. - */ - HIDDEN(null), - /** - * Row caption mode: Items Id-objects toString is used as row caption. - */ - ID(ItemCaptionMode.ID), - /** - * Row caption mode: Item-objects toString is used as row caption. - */ - ITEM(ItemCaptionMode.ITEM), - /** - * Row caption mode: Index of the item is used as item caption. The - * index mode can only be used with the containers implementing the - * {@link com.vaadin.data.Container.Indexed} interface. - */ - INDEX(ItemCaptionMode.INDEX), - /** - * Row caption mode: Item captions are explicitly specified, but if the - * caption is missing, the item id objects toString() is - * used instead. - */ - EXPLICIT_DEFAULTS_ID(ItemCaptionMode.EXPLICIT_DEFAULTS_ID), - /** - * Row caption mode: Item captions are explicitly specified. - */ - EXPLICIT(ItemCaptionMode.EXPLICIT), - /** - * Row caption mode: Only icons are shown, the captions are hidden. - */ - ICON_ONLY(ItemCaptionMode.ICON_ONLY), - /** - * Row caption mode: Item captions are read from property specified with - * {@link #setItemCaptionPropertyId(Object)} . - */ - PROPERTY(ItemCaptionMode.PROPERTY); - - ItemCaptionMode mode; - - private RowHeaderMode(ItemCaptionMode mode) { - this.mode = mode; - } - - public ItemCaptionMode getItemCaptionMode() { - return mode; - } - } - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#HIDDEN} instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_HIDDEN = RowHeaderMode.HIDDEN; - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#ID} instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_ID = RowHeaderMode.ID; - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#ITEM} instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_ITEM = RowHeaderMode.ITEM; - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#INDEX} instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_INDEX = RowHeaderMode.INDEX; - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#EXPLICIT_DEFAULTS_ID} - * instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = RowHeaderMode.EXPLICIT_DEFAULTS_ID; - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#EXPLICIT} instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT = RowHeaderMode.EXPLICIT; - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#ICON_ONLY} instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_ICON_ONLY = RowHeaderMode.ICON_ONLY; - - /** - * @deprecated As of 7.0, use {@link RowHeaderMode#PROPERTY} instead - */ - @Deprecated - public static final RowHeaderMode ROW_HEADER_MODE_PROPERTY = RowHeaderMode.PROPERTY; - - /** - * The default rate that table caches rows for smooth scrolling. - */ - private static final double CACHE_RATE_DEFAULT = 2; - - private static final String ROW_HEADER_COLUMN_KEY = "0"; - private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new UniqueSerializable() { - }; - - /** - * How layout manager should behave when measuring Table's child components - */ - private ChildMeasurementHint childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; - - /* Private table extensions to Select */ - - /** - * True if column collapsing is allowed. - */ - private boolean columnCollapsingAllowed = false; - - /** - * True if reordering of columns is allowed on the client side. - */ - private boolean columnReorderingAllowed = false; - - /** - * Keymapper for column ids. - */ - private final KeyMapper columnIdMap = new KeyMapper(); - - /** - * Holds visible column propertyIds - in order. - */ - private LinkedList visibleColumns = new LinkedList(); - - /** - * Holds noncollapsible columns. - */ - private HashSet noncollapsibleColumns = new HashSet(); - - /** - * Holds propertyIds of currently collapsed columns. - */ - private final HashSet collapsedColumns = new HashSet(); - - /** - * Holds headers for visible columns (by propertyId). - */ - private final HashMap columnHeaders = new HashMap(); - - /** - * Holds footers for visible columns (by propertyId). - */ - private final HashMap columnFooters = new HashMap(); - - /** - * Holds icons for visible columns (by propertyId). - */ - private final HashMap columnIcons = new HashMap(); - - /** - * Holds alignments for visible columns (by propertyId). - */ - private HashMap columnAlignments = new HashMap(); - - /** - * Holds column widths in pixels for visible columns (by propertyId). - */ - private final HashMap columnWidths = new HashMap(); - - /** - * Holds column expand rations for visible columns (by propertyId). - */ - private final HashMap columnExpandRatios = new HashMap(); - - /** - * Holds column generators - */ - private final HashMap columnGenerators = new LinkedHashMap(); - - /** - * Holds value of property pageLength. 0 disables paging. - */ - private int pageLength = 15; - - /** - * Id the first item on the current page. - */ - private Object currentPageFirstItemId = null; - - /* - * If all rows get removed then scroll position of the previous container - * can be restored after re-adding/replacing rows via addAll(). This - * resolves #14581. - */ - private int repairOnReAddAllRowsDataScrollPositionItemIndex = -1; - - /** - * Index of the first item on the current page. - */ - private int currentPageFirstItemIndex = 0; - - /** - * Index of the "first" item on the last page if a user has used - * setCurrentPageFirstItemIndex to scroll down. -1 if not set. - */ - private int currentPageFirstItemIndexOnLastPage = -1; - - /** - * Holds value of property selectable. - */ - private Boolean selectable; - - /** - * Holds value of property columnHeaderMode. - */ - private ColumnHeaderMode columnHeaderMode = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID; - - /** - * Holds value of property rowHeaderMode. - */ - private RowHeaderMode rowHeaderMode = RowHeaderMode.EXPLICIT_DEFAULTS_ID; - - /** - * Should the Table footer be visible? - */ - private boolean columnFootersVisible = false; - - /** - * Page contents buffer used in buffered mode. - */ - private Object[][] pageBuffer = null; - - /** - * Set of properties listened - the list is kept to release the listeners - * later. - */ - private HashSet> listenedProperties = null; - - /** - * Set of visible components - the is used for needsRepaint calculation. - */ - private HashSet visibleComponents = null; - - /** - * List of action handlers. - */ - private LinkedList actionHandlers = null; - - /** - * Action mapper. - */ - private KeyMapper actionMapper = null; - - /** - * Table cell editor factory. - */ - private TableFieldFactory fieldFactory = DefaultFieldFactory.get(); - - /** - * Is table editable. - */ - private boolean editable = false; - - /** - * Current sorting direction. - */ - private boolean sortAscending = true; - - /** - * Currently table is sorted on this propertyId. - */ - private Object sortContainerPropertyId = null; - - /** - * Is table sorting by the user enabled. - */ - private boolean sortEnabled = true; - - /** - * Number of rows explicitly requested by the client to be painted on next - * paint. This is -1 if no request by the client is made. Painting the - * component will automatically reset this to -1. - */ - private int reqRowsToPaint = -1; - - /** - * Index of the first rows explicitly requested by the client to be painted. - * This is -1 if no request by the client is made. Painting the component - * will automatically reset this to -1. - */ - private int reqFirstRowToPaint = -1; - - private int firstToBeRenderedInClient = -1; - - private int lastToBeRenderedInClient = -1; - - private boolean isContentRefreshesEnabled = true; - - private int pageBufferFirstIndex; - - private boolean containerChangeToBeRendered = false; - - /** - * Table cell specific style generator - */ - private CellStyleGenerator cellStyleGenerator = null; - - /** - * Table cell specific tooltip generator - */ - private ItemDescriptionGenerator itemDescriptionGenerator; - - /* - * EXPERIMENTAL feature: will tell the client to re-calculate column widths - * if set to true. Currently no setter: extend to enable. - */ - protected boolean alwaysRecalculateColumnWidths = false; - - private double cacheRate = CACHE_RATE_DEFAULT; - - private TableDragMode dragMode = TableDragMode.NONE; - - private DropHandler dropHandler; - - private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; - - private boolean rowCacheInvalidated; - - private RowGenerator rowGenerator = null; - - private final Map, Property> associatedProperties = new HashMap, Property>(); - - private boolean painted = false; - - private HashMap> propertyValueConverters = new HashMap>(); - - /** - * Set to true if the client-side should be informed that the key mapper has - * been reset so it can avoid sending back references to keys that are no - * longer present. - */ - private boolean keyMapperReset; - - private List exceptionsDuringCachePopulation = new ArrayList(); - - private boolean isBeingPainted; - - /* Table constructors */ - - /** - * Creates a new empty table. - */ - public Table() { - setRowHeaderMode(ROW_HEADER_MODE_HIDDEN); - - registerRpc(new TableServerRpc() { - - @Override - public void contextClick(String rowKey, String colKey, - Section section, MouseEventDetails details) { - Object itemId = itemIdMapper.get(rowKey); - Object propertyId = columnIdMap.get(colKey); - fireEvent(new TableContextClickEvent(Table.this, details, - itemId, propertyId, section)); - } - }); - } - - /** - * Creates a new empty table with caption. - * - * @param caption - */ - public Table(String caption) { - this(); - setCaption(caption); - } - - /** - * Creates a new table with caption and connect it to a Container. - * - * @param caption - * @param dataSource - */ - public Table(String caption, Container dataSource) { - this(); - setCaption(caption); - setContainerDataSource(dataSource); - } - - /* Table functionality */ - - /** - * Gets the array of visible column id:s, including generated columns. - * - *

    - * The columns are show in the order of their appearance in this array. - *

    - * - * @return an array of currently visible propertyIds and generated column - * ids. - */ - public Object[] getVisibleColumns() { - if (visibleColumns == null) { - return null; - } - return visibleColumns.toArray(); - } - - /** - * Sets the array of visible column property id:s. - * - *

    - * The columns are show in the order of their appearance in this array. - *

    - * - * @param visibleColumns - * the Array of shown property id:s. - */ - public void setVisibleColumns(Object... visibleColumns) { - - // Visible columns must exist - if (visibleColumns == null) { - throw new NullPointerException( - "Can not set visible columns to null value"); - } - - final LinkedList newVC = new LinkedList(); - - // Checks that the new visible columns contains no nulls, properties - // exist and that there are no duplicates before adding them to newVC. - final Collection properties = getContainerPropertyIds(); - for (int i = 0; i < visibleColumns.length; i++) { - if (visibleColumns[i] == null) { - throw new NullPointerException("Ids must be non-nulls"); - } else if (!properties.contains(visibleColumns[i]) - && !columnGenerators.containsKey(visibleColumns[i])) { - throw new IllegalArgumentException( - "Ids must exist in the Container or as a generated column, missing id: " - + visibleColumns[i]); - } else if (newVC.contains(visibleColumns[i])) { - throw new IllegalArgumentException( - "Ids must be unique, duplicate id: " - + visibleColumns[i]); - } else { - newVC.add(visibleColumns[i]); - } - } - - this.visibleColumns = newVC; - - // Assures visual refresh - refreshRowCache(); - } - - /** - * Gets the headers of the columns. - * - *

    - * The headers match the property id:s given by the set visible column - * headers. The table must be set in either - * {@link #COLUMN_HEADER_MODE_EXPLICIT} or - * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the - * headers. In the defaults mode any nulls in the headers array are replaced - * with id.toString(). - *

    - * - * @return the Array of column headers. - */ - public String[] getColumnHeaders() { - if (columnHeaders == null) { - return null; - } - final String[] headers = new String[visibleColumns.size()]; - int i = 0; - for (final Iterator it = visibleColumns.iterator(); it - .hasNext(); i++) { - headers[i] = getColumnHeader(it.next()); - } - return headers; - } - - /** - * Sets the headers of the columns. - * - *

    - * The headers match the property id:s given by the set visible column - * headers. The table must be set in either - * {@link #COLUMN_HEADER_MODE_EXPLICIT} or - * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the - * headers. In the defaults mode any nulls in the headers array are replaced - * with id.toString() outputs when rendering. - *

    - * - * @param columnHeaders - * the Array of column headers that match the - * {@link #getVisibleColumns()} method. - */ - public void setColumnHeaders(String... columnHeaders) { - - if (columnHeaders.length != visibleColumns.size()) { - throw new IllegalArgumentException( - "The length of the headers array must match the number of visible columns"); - } - - this.columnHeaders.clear(); - int i = 0; - for (final Iterator it = visibleColumns.iterator(); it.hasNext() - && i < columnHeaders.length; i++) { - this.columnHeaders.put(it.next(), columnHeaders[i]); - } - - markAsDirty(); - } - - /** - * Gets the icons of the columns. - * - *

    - * The icons in headers match the property id:s given by the set visible - * column headers. The table must be set in either - * {@link #COLUMN_HEADER_MODE_EXPLICIT} or - * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers - * with icons. - *

    - * - * @return the Array of icons that match the {@link #getVisibleColumns()}. - */ - public Resource[] getColumnIcons() { - if (columnIcons == null) { - return null; - } - final Resource[] icons = new Resource[visibleColumns.size()]; - int i = 0; - for (final Iterator it = visibleColumns.iterator(); it - .hasNext(); i++) { - icons[i] = columnIcons.get(it.next()); - } - - return icons; - } - - /** - * Sets the icons of the columns. - * - *

    - * The icons in headers match the property id:s given by the set visible - * column headers. The table must be set in either - * {@link #COLUMN_HEADER_MODE_EXPLICIT} or - * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers - * with icons. - *

    - * - * @param columnIcons - * the Array of icons that match the {@link #getVisibleColumns()} - * . - */ - public void setColumnIcons(Resource... columnIcons) { - - if (columnIcons.length != visibleColumns.size()) { - throw new IllegalArgumentException( - "The length of the icons array must match the number of visible columns"); - } - - this.columnIcons.clear(); - int i = 0; - for (final Iterator it = visibleColumns.iterator(); it.hasNext() - && i < columnIcons.length; i++) { - this.columnIcons.put(it.next(), columnIcons[i]); - } - - markAsDirty(); - } - - /** - * Gets the array of column alignments. - * - *

    - * The items in the array must match the properties identified by - * {@link #getVisibleColumns()}. The possible values for the alignments - * include: - *

      - *
    • {@link Align#LEFT}: Left alignment
    • - *
    • {@link Align#CENTER}: Centered
    • - *
    • {@link Align#RIGHT}: Right alignment
    • - *
    - * The alignments default to {@link Align#LEFT}: any null values are - * rendered as align lefts. - *

    - * - * @return the Column alignments array. - */ - public Align[] getColumnAlignments() { - if (columnAlignments == null) { - return null; - } - final Align[] alignments = new Align[visibleColumns.size()]; - int i = 0; - for (final Iterator it = visibleColumns.iterator(); it - .hasNext(); i++) { - alignments[i] = getColumnAlignment(it.next()); - } - - return alignments; - } - - /** - * Sets the column alignments. - * - *

    - * The amount of items in the array must match the amount of properties - * identified by {@link #getVisibleColumns()}. The possible values for the - * alignments include: - *

      - *
    • {@link Align#LEFT}: Left alignment
    • - *
    • {@link Align#CENTER}: Centered
    • - *
    • {@link Align#RIGHT}: Right alignment
    • - *
    - * The alignments default to {@link Align#LEFT} - *

    - * - * @param columnAlignments - * the Column alignments array. - */ - public void setColumnAlignments(Align... columnAlignments) { - - if (columnAlignments.length != visibleColumns.size()) { - throw new IllegalArgumentException( - "The length of the alignments array must match the number of visible columns"); - } - - // Resets the alignments - final HashMap newCA = new HashMap(); - int i = 0; - for (final Iterator it = visibleColumns.iterator(); it.hasNext() - && i < columnAlignments.length; i++) { - newCA.put(it.next(), columnAlignments[i]); - } - this.columnAlignments = newCA; - - // Assures the visual refresh. No need to reset the page buffer before - // as the content has not changed, only the alignments. - refreshRenderedCells(); - } - - /** - * Sets columns width (in pixels). Theme may not necessarily respect very - * small or very big values. Setting width to -1 (default) means that theme - * will make decision of width. - * - *

    - * Column can either have a fixed width or expand ratio. The latter one set - * is used. See @link {@link #setColumnExpandRatio(Object, float)}. - * - * @param propertyId - * columns property id - * @param width - * width to be reserved for columns content - * @since 4.0.3 - */ - public void setColumnWidth(Object propertyId, int width) { - if (propertyId == null) { - // Since propertyId is null, this is the row header. Use the magic - // id to store the width of the row header. - propertyId = ROW_HEADER_FAKE_PROPERTY_ID; - } - - // Setting column width should remove any expand ratios as well - columnExpandRatios.remove(propertyId); - - if (width < 0) { - columnWidths.remove(propertyId); - } else { - columnWidths.put(propertyId, width); - } - markAsDirty(); - } - - /** - * Sets the column expand ratio for given column. - *

    - * Expand ratios can be defined to customize the way how excess space is - * divided among columns. Table can have excess space if it has its width - * defined and there is horizontally more space than columns consume - * naturally. Excess space is the space that is not used by columns with - * explicit width (see {@link #setColumnWidth(Object, int)}) or with natural - * width (no width nor expand ratio). - * - *

    - * By default (without expand ratios) the excess space is divided - * proportionally to columns natural widths. - * - *

    - * Only expand ratios of visible columns are used in final calculations. - * - *

    - * Column can either have a fixed width or expand ratio. The latter one set - * is used. - * - *

    - * A column with expand ratio is considered to be minimum width by default - * (if no excess space exists). The minimum width is defined by terminal - * implementation. - * - *

    - * If terminal implementation supports re-sizable columns the column becomes - * fixed width column if users resizes the column. - * - * @param propertyId - * columns property id - * @param expandRatio - * the expandRatio used to divide excess space for this column - */ - public void setColumnExpandRatio(Object propertyId, float expandRatio) { - if (propertyId == null) { - // Since propertyId is null, this is the row header. Use the magic - // id to store the width of the row header. - propertyId = ROW_HEADER_FAKE_PROPERTY_ID; - } - - // Setting the column expand ratio should remove and defined column - // width - columnWidths.remove(propertyId); - - if (expandRatio < 0) { - columnExpandRatios.remove(propertyId); - } else { - columnExpandRatios.put(propertyId, expandRatio); - } - - requestRepaint(); - } - - /** - * Gets the column expand ratio for a column. See - * {@link #setColumnExpandRatio(Object, float)} - * - * @param propertyId - * columns property id - * @return the expandRatio used to divide excess space for this column - */ - public float getColumnExpandRatio(Object propertyId) { - final Float width = columnExpandRatios.get(propertyId); - if (width == null) { - return -1; - } - return width.floatValue(); - } - - /** - * Gets the pixel width of column - * - * @param propertyId - * @return width of column or -1 when value not set - */ - public int getColumnWidth(Object propertyId) { - if (propertyId == null) { - // Since propertyId is null, this is the row header. Use the magic - // id to retrieve the width of the row header. - propertyId = ROW_HEADER_FAKE_PROPERTY_ID; - } - final Integer width = columnWidths.get(propertyId); - if (width == null) { - return -1; - } - return width.intValue(); - } - - /** - * Gets the page length. - * - *

    - * Setting page length 0 disables paging. - *

    - * - * @return the Length of one page. - */ - public int getPageLength() { - return pageLength; - } - - /** - * Sets the page length. - * - *

    - * Setting page length 0 disables paging. The page length defaults to 15. - *

    - * - *

    - * If Table has height set ({@link #setHeight(float, Unit)} ) the client - * side may update the page length automatically the correct value. - *

    - * - * @param pageLength - * the length of one page. - */ - public void setPageLength(int pageLength) { - if (pageLength >= 0 && this.pageLength != pageLength) { - this.pageLength = pageLength; - // Assures the visual refresh - refreshRowCache(); - } - } - - /** - * This method adjusts a possible caching mechanism of table implementation. - * - *

    - * Table component may fetch and render some rows outside visible area. With - * complex tables (for example containing layouts and components), the - * client side may become unresponsive. Setting the value lower, UI will - * become more responsive. With higher values scrolling in client will hit - * server less frequently. - * - *

    - * The amount of cached rows will be cacheRate multiplied with pageLength ( - * {@link #setPageLength(int)} both below and above visible area.. - * - * @param cacheRate - * a value over 0 (fastest rendering time). Higher value will - * cache more rows on server (smoother scrolling). Default value - * is 2. - */ - public void setCacheRate(double cacheRate) { - if (cacheRate < 0) { - throw new IllegalArgumentException( - "cacheRate cannot be less than zero"); - } - if (this.cacheRate != cacheRate) { - this.cacheRate = cacheRate; - markAsDirty(); - } - } - - /** - * @see #setCacheRate(double) - * - * @return the current cache rate value - */ - public double getCacheRate() { - return cacheRate; - } - - /** - * Getter for property currentPageFirstItem. - * - * @return the Value of property currentPageFirstItem. - */ - public Object getCurrentPageFirstItemId() { - - // Prioritise index over id if indexes are supported - if (items instanceof Container.Indexed) { - final int index = getCurrentPageFirstItemIndex(); - Object id = null; - if (index >= 0 && index < size()) { - id = getIdByIndex(index); - } - if (id != null && !id.equals(currentPageFirstItemId)) { - currentPageFirstItemId = id; - } - } - - // If there is no item id at all, use the first one - if (currentPageFirstItemId == null) { - currentPageFirstItemId = firstItemId(); - } - - return currentPageFirstItemId; - } - - /** - * Returns the item ID for the item represented by the index given. Assumes - * that the current container implements {@link Container.Indexed}. - * - * See {@link Container.Indexed#getIdByIndex(int)} for more information - * about the exceptions that can be thrown. - * - * @param index - * the index for which the item ID should be fetched - * @return the item ID for the given index - * - * @throws ClassCastException - * if container does not implement {@link Container.Indexed} - * @throws IndexOutOfBoundsException - * thrown by {@link Container.Indexed#getIdByIndex(int)} if the - * index is invalid - */ - protected Object getIdByIndex(int index) { - return ((Container.Indexed) items).getIdByIndex(index); - } - - /** - * Setter for property currentPageFirstItemId. - * - * @param currentPageFirstItemId - * the New value of property currentPageFirstItemId. - */ - public void setCurrentPageFirstItemId(Object currentPageFirstItemId) { - - // Gets the corresponding index - int index = -1; - if (items instanceof Container.Indexed) { - index = indexOfId(currentPageFirstItemId); - } else { - // If the table item container does not have index, we have to - // calculates the index by hand - Object id = firstItemId(); - while (id != null && !id.equals(currentPageFirstItemId)) { - index++; - id = nextItemId(id); - } - if (id == null) { - index = -1; - } - } - - // If the search for item index was successful - if (index >= 0) { - /* - * The table is not capable of displaying an item in the container - * as the first if there are not enough items following the selected - * item so the whole table (pagelength) is filled. - */ - int maxIndex = size() - pageLength; - if (maxIndex < 0) { - maxIndex = 0; - } - - if (index > maxIndex) { - // Note that we pass index, not maxIndex, letting - // setCurrentPageFirstItemIndex handle the situation. - setCurrentPageFirstItemIndex(index); - return; - } - - this.currentPageFirstItemId = currentPageFirstItemId; - currentPageFirstItemIndex = index; - } - - // Assures the visual refresh - refreshRowCache(); - - } - - protected int indexOfId(Object itemId) { - return ((Container.Indexed) items).indexOfId(itemId); - } - - /** - * Gets the icon Resource for the specified column. - * - * @param propertyId - * the propertyId identifying the column. - * @return the icon for the specified column; null if the column has no icon - * set, or if the column is not visible. - */ - public Resource getColumnIcon(Object propertyId) { - return columnIcons.get(propertyId); - } - - /** - * Sets the icon Resource for the specified column. - *

    - * Throws IllegalArgumentException if the specified column is not visible. - *

    - * - * @param propertyId - * the propertyId identifying the column. - * @param icon - * the icon Resource to set. - */ - public void setColumnIcon(Object propertyId, Resource icon) { - - if (icon == null) { - columnIcons.remove(propertyId); - } else { - columnIcons.put(propertyId, icon); - } - - markAsDirty(); - } - - /** - * Gets the header for the specified column. - * - * @param propertyId - * the propertyId identifying the column. - * @return the header for the specified column if it has one. - */ - public String getColumnHeader(Object propertyId) { - if (getColumnHeaderMode() == ColumnHeaderMode.HIDDEN) { - return null; - } - - String header = columnHeaders.get(propertyId); - if ((header == null - && getColumnHeaderMode() == ColumnHeaderMode.EXPLICIT_DEFAULTS_ID) - || getColumnHeaderMode() == ColumnHeaderMode.ID) { - header = propertyId.toString(); - } - - return header; - } - - /** - * Sets the column header for the specified column; - * - * @param propertyId - * the propertyId identifying the column. - * @param header - * the header to set. - */ - public void setColumnHeader(Object propertyId, String header) { - - if (header == null) { - columnHeaders.remove(propertyId); - } else { - columnHeaders.put(propertyId, header); - } - - markAsDirty(); - } - - /** - * Gets the specified column's alignment. - * - * @param propertyId - * the propertyID identifying the column. - * @return the specified column's alignment if it as one; {@link Align#LEFT} - * otherwise. - */ - public Align getColumnAlignment(Object propertyId) { - final Align a = columnAlignments.get(propertyId); - return a == null ? Align.LEFT : a; - } - - /** - * Sets the specified column's alignment. - * - *

    - * Throws IllegalArgumentException if the alignment is not one of the - * following: {@link Align#LEFT}, {@link Align#CENTER} or - * {@link Align#RIGHT} - *

    - * - * @param propertyId - * the propertyID identifying the column. - * @param alignment - * the desired alignment. - */ - public void setColumnAlignment(Object propertyId, Align alignment) { - if (alignment == null || alignment == Align.LEFT) { - columnAlignments.remove(propertyId); - } else { - columnAlignments.put(propertyId, alignment); - } - - // Assures the visual refresh. No need to reset the page buffer before - // as the content has not changed, only the alignments. - refreshRenderedCells(); - } - - /** - * Checks if the specified column is collapsed. - * - * @param propertyId - * the propertyID identifying the column. - * @return true if the column is collapsed; false otherwise; - */ - public boolean isColumnCollapsed(Object propertyId) { - return collapsedColumns != null - && collapsedColumns.contains(propertyId); - } - - /** - * Sets whether the specified column is collapsed or not. - * - * - * @param propertyId - * the propertyID identifying the column. - * @param collapsed - * the desired collapsedness. - * @throws IllegalStateException - * if column collapsing is not allowed - * @throws IllegalArgumentException - * if the property id does not exist - */ - public void setColumnCollapsed(Object propertyId, boolean collapsed) - throws IllegalStateException { - if (!isColumnCollapsingAllowed()) { - throw new IllegalStateException("Column collapsing not allowed!"); - } - if (collapsed && noncollapsibleColumns.contains(propertyId)) { - throw new IllegalStateException("The column is noncollapsible!"); - } - if (!getContainerPropertyIds().contains(propertyId) - && !columnGenerators.containsKey(propertyId)) { - throw new IllegalArgumentException("Property '" + propertyId - + "' was not found in the container"); - } - - if (collapsed) { - if (collapsedColumns.add(propertyId)) { - fireColumnCollapseEvent(propertyId); - } - } else { - if (collapsedColumns.remove(propertyId)) { - fireColumnCollapseEvent(propertyId); - } - } - - // Assures the visual refresh - refreshRowCache(); - } - - /** - * Checks if column collapsing is allowed. - * - * @return true if columns can be collapsed; false otherwise. - */ - public boolean isColumnCollapsingAllowed() { - return columnCollapsingAllowed; - } - - /** - * Sets whether column collapsing is allowed or not. - * - * @param collapsingAllowed - * specifies whether column collapsing is allowed. - */ - public void setColumnCollapsingAllowed(boolean collapsingAllowed) { - columnCollapsingAllowed = collapsingAllowed; - - if (!collapsingAllowed) { - collapsedColumns.clear(); - } - - // Assures the visual refresh. No need to reset the page buffer before - // as the content has not changed, only the alignments. - refreshRenderedCells(); - } - - /** - * Sets whether the given column is collapsible. Note that collapsible - * columns can only be actually collapsed (via UI or with - * {@link #setColumnCollapsed(Object, boolean) setColumnCollapsed()}) if - * {@link #isColumnCollapsingAllowed()} is true. By default all columns are - * collapsible. - * - * @param propertyId - * the propertyID identifying the column. - * @param collapsible - * true if the column should be collapsible, false otherwise. - */ - public void setColumnCollapsible(Object propertyId, boolean collapsible) { - if (collapsible) { - noncollapsibleColumns.remove(propertyId); - } else { - noncollapsibleColumns.add(propertyId); - collapsedColumns.remove(propertyId); - } - refreshRowCache(); - } - - /** - * Checks if the given column is collapsible. Note that even if this method - * returns true, the column can only be actually collapsed (via - * UI or with {@link #setColumnCollapsed(Object, boolean) - * setColumnCollapsed()}) if {@link #isColumnCollapsingAllowed()} is also - * true. - * - * @return true if the column can be collapsed; false otherwise. - */ - public boolean isColumnCollapsible(Object propertyId) { - return !noncollapsibleColumns.contains(propertyId); - } - - /** - * Checks if column reordering is allowed. - * - * @return true if columns can be reordered; false otherwise. - */ - public boolean isColumnReorderingAllowed() { - return columnReorderingAllowed; - } - - /** - * Sets whether column reordering is allowed or not. - * - * @param columnReorderingAllowed - * specifies whether column reordering is allowed. - */ - public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { - if (columnReorderingAllowed != this.columnReorderingAllowed) { - this.columnReorderingAllowed = columnReorderingAllowed; - markAsDirty(); - } - } - - /* - * Arranges visible columns according to given columnOrder. Silently ignores - * colimnId:s that are not visible columns, and keeps the internal order of - * visible columns left out of the ordering (trailing). Silently does - * nothing if columnReordering is not allowed. - */ - private void setColumnOrder(Object[] columnOrder) { - if (columnOrder == null || !isColumnReorderingAllowed()) { - return; - } - final LinkedList newOrder = new LinkedList(); - for (int i = 0; i < columnOrder.length; i++) { - if (columnOrder[i] != null - && visibleColumns.contains(columnOrder[i])) { - visibleColumns.remove(columnOrder[i]); - newOrder.add(columnOrder[i]); - } - } - for (final Iterator it = visibleColumns.iterator(); it - .hasNext();) { - final Object columnId = it.next(); - if (!newOrder.contains(columnId)) { - newOrder.add(columnId); - } - } - visibleColumns = newOrder; - - // Assure visual refresh - refreshRowCache(); - } - - /** - * Getter for property currentPageFirstItem. - * - * @return the Value of property currentPageFirstItem. - */ - public int getCurrentPageFirstItemIndex() { - return currentPageFirstItemIndex; - } - - void setCurrentPageFirstItemIndex(int newIndex, - boolean needsPageBufferReset) { - - if (newIndex < 0) { - newIndex = 0; - } - - /* - * minimize Container.size() calls which may be expensive. For example - * it may cause sql query. - */ - final int size = size(); - - /* - * The table is not capable of displaying an item in the container as - * the first if there are not enough items following the selected item - * so the whole table (pagelength) is filled. - */ - int maxIndex = size - pageLength; - if (maxIndex < 0) { - maxIndex = 0; - } - - /* - * If the new index is on the last page we set the index to be the first - * item on that last page and make a note of the real index for the - * client side to be able to move the scroll position to the correct - * position. - */ - int indexOnLastPage = -1; - if (newIndex > maxIndex) { - indexOnLastPage = newIndex; - newIndex = maxIndex; - } - - // Refresh first item id - if (items instanceof Container.Indexed) { - try { - currentPageFirstItemId = getIdByIndex(newIndex); - } catch (final IndexOutOfBoundsException e) { - currentPageFirstItemId = null; - } - currentPageFirstItemIndex = newIndex; - - if (needsPageBufferReset) { - /* - * The flag currentPageFirstItemIndexOnLastPage denotes a user - * set scrolling position on the last page via - * setCurrentPageFirstItemIndex() and shouldn't be changed by - * the table component internally changing the firstvisible item - * on lazy row fetching. Doing so would make the scrolling - * position not be updated correctly when the lazy rows are - * finally rendered. - */ - - boolean isLastRowPossiblyPartiallyVisible = true; - if (indexOnLastPage != -1) { - /* - * If the requested row was greater than maxIndex, the last - * row should be fully visible (See - * TestCurrentPageFirstItem). - */ - isLastRowPossiblyPartiallyVisible = false; - } - - int extraRows = isLastRowPossiblyPartiallyVisible ? 0 : 1; - currentPageFirstItemIndexOnLastPage = currentPageFirstItemIndex - + extraRows; - } else { - currentPageFirstItemIndexOnLastPage = -1; - } - - } else { - - // For containers not supporting indexes, we must iterate the - // container forwards / backwards - // next available item forward or backward - - currentPageFirstItemId = firstItemId(); - - // Go forwards in the middle of the list (respect borders) - while (currentPageFirstItemIndex < newIndex - && !isLastId(currentPageFirstItemId)) { - currentPageFirstItemIndex++; - currentPageFirstItemId = nextItemId(currentPageFirstItemId); - } - - // If we did hit the border - if (isLastId(currentPageFirstItemId)) { - currentPageFirstItemIndex = size - 1; - } - - // Go backwards in the middle of the list (respect borders) - while (currentPageFirstItemIndex > newIndex - && !isFirstId(currentPageFirstItemId)) { - currentPageFirstItemIndex--; - currentPageFirstItemId = prevItemId(currentPageFirstItemId); - } - - // If we did hit the border - if (isFirstId(currentPageFirstItemId)) { - currentPageFirstItemIndex = 0; - } - - // Go forwards once more - while (currentPageFirstItemIndex < newIndex - && !isLastId(currentPageFirstItemId)) { - currentPageFirstItemIndex++; - currentPageFirstItemId = nextItemId(currentPageFirstItemId); - } - - // If for some reason we do hit border again, override - // the user index request - if (isLastId(currentPageFirstItemId)) { - newIndex = currentPageFirstItemIndex = size - 1; - } - } - - if (needsPageBufferReset) { - // Assures the visual refresh - refreshRowCache(); - } - } - - /** - * Setter for property currentPageFirstItem. - * - * @param newIndex - * the New value of property currentPageFirstItem. - */ - public void setCurrentPageFirstItemIndex(int newIndex) { - setCurrentPageFirstItemIndex(newIndex, true); - } - - /** - * Returns whether table is selectable. - * - *

    - * The table is not selectable until it's explicitly set as selectable or at - * least one {@link ValueChangeListener} is added. - *

    - * - * @return whether table is selectable. - */ - public boolean isSelectable() { - if (selectable == null) { - return hasListeners(ValueChangeEvent.class); - } - return selectable; - } - - /** - * Setter for property selectable. - * - *

    - * The table is not selectable until it's explicitly set as selectable via - * this method or alternatively at least one {@link ValueChangeListener} is - * added. - *

    - * - * @param selectable - * the New value of property selectable. - */ - public void setSelectable(boolean selectable) { - if (!SharedUtil.equals(this.selectable, selectable)) { - this.selectable = selectable; - markAsDirty(); - } - } - - /** - * Getter for property columnHeaderMode. - * - * @return the Value of property columnHeaderMode. - */ - public ColumnHeaderMode getColumnHeaderMode() { - return columnHeaderMode; - } - - /** - * Setter for property columnHeaderMode. - * - * @param columnHeaderMode - * the New value of property columnHeaderMode. - */ - public void setColumnHeaderMode(ColumnHeaderMode columnHeaderMode) { - if (columnHeaderMode == null) { - throw new IllegalArgumentException( - "Column header mode can not be null"); - } - if (columnHeaderMode != this.columnHeaderMode) { - this.columnHeaderMode = columnHeaderMode; - markAsDirty(); - } - - } - - /** - * Refreshes the rows in the internal cache. Only if - * {@link #resetPageBuffer()} is called before this then all values are - * guaranteed to be recreated. - */ - protected void refreshRenderedCells() { - if (!isAttached()) { - return; - } - - if (!isContentRefreshesEnabled) { - return; - } - - // Collects the basic facts about the table page - final int pagelen = getPageLength(); - int rows, totalRows; - rows = totalRows = size(); - int firstIndex = Math.min(getCurrentPageFirstItemIndex(), - totalRows - 1); - if (rows > 0 && firstIndex >= 0) { - rows -= firstIndex; - } - if (pagelen > 0 && pagelen < rows) { - rows = pagelen; - } - - // If "to be painted next" variables are set, use them - if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) { - rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1; - } - if (firstToBeRenderedInClient >= 0) { - if (firstToBeRenderedInClient < totalRows) { - firstIndex = firstToBeRenderedInClient; - } else { - firstIndex = totalRows - 1; - } - } else { - // initial load - - // #8805 send one extra row in the beginning in case a partial - // row is shown on the UI - if (firstIndex > 0) { - firstIndex = firstIndex - 1; - rows = rows + 1; - } - firstToBeRenderedInClient = firstIndex; - } - if (totalRows > 0) { - if (rows + firstIndex > totalRows) { - rows = totalRows - firstIndex; - } - } else { - rows = 0; - } - - // Saves the results to internal buffer - pageBuffer = getVisibleCellsNoCache(firstIndex, rows, true); - - if (rows > 0) { - pageBufferFirstIndex = firstIndex; - } - if (getPageLength() != 0) { - removeUnnecessaryRows(); - } - - setRowCacheInvalidated(true); - markAsDirty(); - maybeThrowCacheUpdateExceptions(); - - } - - private void maybeThrowCacheUpdateExceptions() { - if (!exceptionsDuringCachePopulation.isEmpty()) { - Throwable[] causes = new Throwable[exceptionsDuringCachePopulation - .size()]; - exceptionsDuringCachePopulation.toArray(causes); - - exceptionsDuringCachePopulation.clear(); - throw new CacheUpdateException(this, - "Error during Table cache update.", causes); - } - - } - - /** - * Exception thrown when one or more exceptions occurred during updating of - * the Table cache. - *

    - * Contains all exceptions which occurred during the cache update. The first - * occurred exception is set as the cause of this exception. All occurred - * exceptions can be accessed using {@link #getCauses()}. - *

    - * - */ - public static class CacheUpdateException extends RuntimeException { - private Throwable[] causes; - private Table table; - - public CacheUpdateException(Table table, String message, - Throwable[] causes) { - super(maybeSupplementMessage(message, causes.length), causes[0]); - this.table = table; - this.causes = causes; - } - - private static String maybeSupplementMessage(String message, - int causeCount) { - if (causeCount > 1) { - return message + " Additional causes not shown."; - } else { - return message; - } - } - - /** - * Returns the cause(s) for this exception - * - * @return the exception(s) which caused this exception - */ - public Throwable[] getCauses() { - return causes; - } - - public Table getTable() { - return table; - } - - } - - /** - * Removes rows that fall outside the required cache. - */ - private void removeUnnecessaryRows() { - int minPageBufferIndex = getMinPageBufferIndex(); - int maxPageBufferIndex = getMaxPageBufferIndex(); - - int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1; - - /* - * Number of rows that were previously cached. This is not necessarily - * the same as pageLength if we do not have enough rows in the - * container. - */ - int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length; - - if (currentlyCachedRowCount <= maxBufferSize) { - // removal unnecessary - return; - } - - /* Figure out which rows to get rid of. */ - int firstCacheRowToRemoveInPageBuffer = -1; - if (minPageBufferIndex > pageBufferFirstIndex) { - firstCacheRowToRemoveInPageBuffer = pageBufferFirstIndex; - } else if (maxPageBufferIndex < pageBufferFirstIndex - + currentlyCachedRowCount) { - firstCacheRowToRemoveInPageBuffer = maxPageBufferIndex + 1; - } - - if (firstCacheRowToRemoveInPageBuffer - - pageBufferFirstIndex < currentlyCachedRowCount) { - /* - * Unregister all components that fall beyond the cache limits after - * inserting the new rows. - */ - unregisterComponentsAndPropertiesInRows( - firstCacheRowToRemoveInPageBuffer, currentlyCachedRowCount - - firstCacheRowToRemoveInPageBuffer); - } - } - - /** - * Requests that the Table should be repainted as soon as possible. - * - * Note that a {@code Table} does not necessarily repaint its contents when - * this method has been called. See {@link #refreshRowCache()} for forcing - * an update of the contents. - * - * @deprecated As of 7.0, use {@link #markAsDirty()} instead - */ - - @Deprecated - @Override - public void requestRepaint() { - markAsDirty(); - } - - /** - * Requests that the Table should be repainted as soon as possible. - * - * Note that a {@code Table} does not necessarily repaint its contents when - * this method has been called. See {@link #refreshRowCache()} for forcing - * an update of the contents. - */ - - @Override - public void markAsDirty() { - // Overridden only for javadoc - super.markAsDirty(); - } - - @Override - public void markAsDirtyRecursive() { - super.markAsDirtyRecursive(); - - // Avoid sending a partial repaint (#8714) - refreshRowCache(); - } - - private void removeRowsFromCacheAndFillBottom(int firstIndex, int rows) { - int totalCachedRows = pageBuffer[CELL_ITEMID].length; - int totalRows = size(); - int firstIndexInPageBuffer = firstIndex - pageBufferFirstIndex; - - /* - * firstIndexInPageBuffer is the first row to be removed. "rows" rows - * after that should be removed. If the page buffer does not contain - * that many rows, we only remove the rows that actually are in the page - * buffer. - */ - if (firstIndexInPageBuffer + rows > totalCachedRows) { - rows = totalCachedRows - firstIndexInPageBuffer; - } - - /* - * Unregister components that will no longer be in the page buffer to - * make sure that no components leak. - */ - unregisterComponentsAndPropertiesInRows(firstIndex, rows); - - /* - * The number of rows that should be in the cache after this operation - * is done (pageBuffer currently contains the expanded items). - */ - int newCachedRowCount = totalCachedRows; - if (newCachedRowCount + pageBufferFirstIndex > totalRows) { - newCachedRowCount = totalRows - pageBufferFirstIndex; - } - - /* - * The index at which we should render the first row that does not come - * from the previous page buffer. - */ - int firstAppendedRowInPageBuffer = totalCachedRows - rows; - int firstAppendedRow = firstAppendedRowInPageBuffer - + pageBufferFirstIndex; - - /* - * Calculate the maximum number of new rows that we can add to the page - * buffer. Less than the rows we removed if the container does not - * contain that many items afterwards. - */ - int maxRowsToRender = (totalRows - firstAppendedRow); - int rowsToAdd = rows; - if (rowsToAdd > maxRowsToRender) { - rowsToAdd = maxRowsToRender; - } - - Object[][] cells = null; - if (rowsToAdd > 0) { - cells = getVisibleCellsNoCache(firstAppendedRow, rowsToAdd, false); - } - /* - * Create the new cache buffer by copying the first rows from the old - * buffer, moving the following rows upwards and appending more rows if - * applicable. - */ - Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount]; - - for (int i = 0; i < pageBuffer.length; i++) { - for (int row = 0; row < firstIndexInPageBuffer; row++) { - // Copy the first rows - newPageBuffer[i][row] = pageBuffer[i][row]; - } - for (int row = firstIndexInPageBuffer; row < firstAppendedRowInPageBuffer; row++) { - // Move the rows that were after the expanded rows - newPageBuffer[i][row] = pageBuffer[i][row + rows]; - } - for (int row = firstAppendedRowInPageBuffer; row < newCachedRowCount; row++) { - // Add the newly rendered rows. Only used if rowsToAdd > 0 - // (cells != null) - newPageBuffer[i][row] = cells[i][row - - firstAppendedRowInPageBuffer]; - } - } - pageBuffer = newPageBuffer; - } - - private Object[][] getVisibleCellsUpdateCacheRows(int firstIndex, - int rows) { - Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false); - int cacheIx = firstIndex - pageBufferFirstIndex; - // update the new rows in the cache. - int totalCachedRows = pageBuffer[CELL_ITEMID].length; - int end = Math.min(cacheIx + rows, totalCachedRows); - for (int ix = cacheIx; ix < end; ix++) { - for (int i = 0; i < pageBuffer.length; i++) { - pageBuffer[i][ix] = cells[i][ix - cacheIx]; - } - } - return cells; - } - - /** - * @param firstIndex - * The position where new rows should be inserted - * @param rows - * The maximum number of rows that should be inserted at position - * firstIndex. Less rows will be inserted if the page buffer is - * too small. - * @return - */ - private Object[][] getVisibleCellsInsertIntoCache(int firstIndex, - int rows) { - getLogger().log(Level.FINEST, - "Insert {0} rows at index {1} to existing page buffer requested", - new Object[] { rows, firstIndex }); - - int minPageBufferIndex = getMinPageBufferIndex(); - int maxPageBufferIndex = getMaxPageBufferIndex(); - - int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1; - - if (getPageLength() == 0) { - // If pageLength == 0 then all rows should be rendered - maxBufferSize = pageBuffer[CELL_ITEMID].length + rows; - } - /* - * Number of rows that were previously cached. This is not necessarily - * the same as maxBufferSize. - */ - int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length; - - /* If rows > size available in page buffer */ - if (firstIndex + rows - 1 > maxPageBufferIndex) { - rows = maxPageBufferIndex - firstIndex + 1; - } - - /* - * "rows" rows will be inserted at firstIndex. Find out how many old - * rows fall outside the new buffer so we can unregister components in - * the cache. - */ - - /* - * if there are rows before the new pageBuffer limits they must be - * removed - */ - int lastCacheRowToRemove = minPageBufferIndex - 1; - int rowsFromBeginning = lastCacheRowToRemove - pageBufferFirstIndex + 1; - if (lastCacheRowToRemove >= pageBufferFirstIndex) { - unregisterComponentsAndPropertiesInRows(pageBufferFirstIndex, - rowsFromBeginning); - } else { - rowsFromBeginning = 0; - } - - /* - * the rows that fall outside of the new pageBuffer limits after the new - * rows are inserted must also be removed - */ - int firstCacheRowToRemove = firstIndex; - /* - * IF there is space remaining in the buffer after the rows have been - * inserted, we can keep more rows. - */ - int numberOfOldRowsAfterInsertedRows = Math.min( - pageBufferFirstIndex + currentlyCachedRowCount + rows, - maxPageBufferIndex + 1) - (firstIndex + rows - 1); - if (numberOfOldRowsAfterInsertedRows > 0) { - firstCacheRowToRemove += numberOfOldRowsAfterInsertedRows; - } - int rowsFromAfter = currentlyCachedRowCount - - (firstCacheRowToRemove - pageBufferFirstIndex); - - if (rowsFromAfter > 0) { - /* - * Unregister all components that fall beyond the cache limits after - * inserting the new rows. - */ - unregisterComponentsAndPropertiesInRows(firstCacheRowToRemove, - rowsFromAfter); - } - - // Calculate the new cache size - int newCachedRowCount = maxBufferSize; - if (pageBufferFirstIndex + currentlyCachedRowCount + rows - - 1 < maxPageBufferIndex) { - // there aren't enough rows to fill the whole potential -> use what - // there is - newCachedRowCount -= maxPageBufferIndex - (pageBufferFirstIndex - + currentlyCachedRowCount + rows - 1); - } else if (minPageBufferIndex < pageBufferFirstIndex) { - newCachedRowCount -= pageBufferFirstIndex - minPageBufferIndex; - } - /* - * calculate the internal location of the new rows within the new cache - */ - int firstIndexInNewPageBuffer = firstIndex - pageBufferFirstIndex - - rowsFromBeginning; - - /* Paint the new rows into a separate buffer */ - Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false); - - /* - * Create the new cache buffer and fill it with the data from the old - * buffer as well as the inserted rows. - */ - Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount]; - - for (int i = 0; i < pageBuffer.length; i++) { - for (int row = 0; row < firstIndexInNewPageBuffer; row++) { - // Copy the first rows - newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row]; - } - for (int row = firstIndexInNewPageBuffer; row < firstIndexInNewPageBuffer - + rows; row++) { - // Copy the newly created rows - newPageBuffer[i][row] = cells[i][row - - firstIndexInNewPageBuffer]; - } - for (int row = firstIndexInNewPageBuffer - + rows; row < newCachedRowCount; row++) { - // Move the old rows down below the newly inserted rows - newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row - - rows]; - } - } - pageBuffer = newPageBuffer; - pageBufferFirstIndex = Math.max( - pageBufferFirstIndex + rowsFromBeginning, minPageBufferIndex); - if (getLogger().isLoggable(Level.FINEST)) { - getLogger().log(Level.FINEST, - "Page Buffer now contains {0} rows ({1}-{2})", - new Object[] { pageBuffer[CELL_ITEMID].length, - pageBufferFirstIndex, (pageBufferFirstIndex - + pageBuffer[CELL_ITEMID].length - 1) }); - } - return cells; - } - - private int getMaxPageBufferIndex() { - int total = size(); - if (getPageLength() == 0) { - // everything is shown at once, no caching - return total - 1; - } - // Page buffer must not become larger than pageLength*cacheRate after - // the current page - int maxPageBufferIndex = getCurrentPageFirstItemIndex() - + (int) (getPageLength() * (1 + getCacheRate())); - if (shouldHideNullSelectionItem()) { - --total; - } - if (maxPageBufferIndex >= total) { - maxPageBufferIndex = total - 1; - } - return maxPageBufferIndex; - } - - private int getMinPageBufferIndex() { - if (getPageLength() == 0) { - // everything is shown at once, no caching - return 0; - } - // Page buffer must not become larger than pageLength*cacheRate before - // the current page - int minPageBufferIndex = getCurrentPageFirstItemIndex() - - (int) (getPageLength() * getCacheRate()); - if (minPageBufferIndex < 0) { - minPageBufferIndex = 0; - } - return minPageBufferIndex; - } - - /** - * Render rows with index "firstIndex" to "firstIndex+rows-1" to a new - * buffer. - * - * Reuses values from the current page buffer if the rows are found there. - * - * @param firstIndex - * @param rows - * @param replaceListeners - * @return - */ - private Object[][] getVisibleCellsNoCache(int firstIndex, int rows, - boolean replaceListeners) { - if (getLogger().isLoggable(Level.FINEST)) { - getLogger().log(Level.FINEST, - "Render visible cells for rows {0}-{1}", - new Object[] { firstIndex, (firstIndex + rows - 1) }); - } - final Object[] colids = getVisibleColumns(); - final int cols = colids.length; - - HashSet> oldListenedProperties = listenedProperties; - HashSet oldVisibleComponents = visibleComponents; - - if (replaceListeners) { - // initialize the listener collections, this should only be done if - // the entire cache is refreshed (through refreshRenderedCells) - listenedProperties = new HashSet>(); - visibleComponents = new HashSet(); - } - - Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows]; - if (rows == 0) { - unregisterPropertiesAndComponents(oldListenedProperties, - oldVisibleComponents); - return cells; - } - - final RowHeaderMode headmode = getRowHeaderMode(); - final boolean[] iscomponent = new boolean[cols]; - for (int i = 0; i < cols; i++) { - iscomponent[i] = columnGenerators.containsKey(colids[i]) - || Component.class.isAssignableFrom(getType(colids[i])); - } - int firstIndexNotInCache; - if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) { - firstIndexNotInCache = pageBufferFirstIndex - + pageBuffer[CELL_ITEMID].length; - } else { - firstIndexNotInCache = -1; - } - - // Creates the page contents - int filledRows = 0; - if (items instanceof Container.Indexed) { - // more efficient implementation for containers supporting access by - // index - - List itemIds = getItemIds(firstIndex, rows); - for (int i = 0; i < rows && i < itemIds.size(); i++) { - Object id = itemIds.get(i); - if (id == null) { - throw new IllegalStateException( - "Null itemId returned from container"); - } - // Start by parsing the values, id should already be set - parseItemIdToCells(cells, id, i, firstIndex, headmode, cols, - colids, firstIndexNotInCache, iscomponent, - oldListenedProperties); - - filledRows++; - } - } else { - // slow back-up implementation for cases where the container does - // not support access by index - - // Gets the first item id - Object id = firstItemId(); - for (int i = 0; i < firstIndex; i++) { - id = nextItemId(id); - } - for (int i = 0; i < rows && id != null; i++) { - // Start by parsing the values, id should already be set - parseItemIdToCells(cells, id, i, firstIndex, headmode, cols, - colids, firstIndexNotInCache, iscomponent, - oldListenedProperties); - - // Gets the next item id for non indexed container - id = nextItemId(id); - - filledRows++; - } - } - - // Assures that all the rows of the cell-buffer are valid - if (filledRows != cells[0].length) { - final Object[][] temp = new Object[cells.length][filledRows]; - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < filledRows; j++) { - temp[i][j] = cells[i][j]; - } - } - cells = temp; - } - - unregisterPropertiesAndComponents(oldListenedProperties, - oldVisibleComponents); - - return cells; - } - - protected List getItemIds(int firstIndex, int rows) { - return (List) ((Container.Indexed) items).getItemIds(firstIndex, - rows); - } - - /** - * Update a cache array for a row, register any relevant listeners etc. - * - * This is an internal method extracted from - * {@link #getVisibleCellsNoCache(int, int, boolean)} and should be removed - * when the Table is rewritten. - */ - private void parseItemIdToCells(Object[][] cells, Object id, int i, - int firstIndex, RowHeaderMode headmode, int cols, Object[] colids, - int firstIndexNotInCache, boolean[] iscomponent, - HashSet> oldListenedProperties) { - - cells[CELL_ITEMID][i] = id; - cells[CELL_KEY][i] = itemIdMapper.key(id); - if (headmode != ROW_HEADER_MODE_HIDDEN) { - switch (headmode) { - case INDEX: - cells[CELL_HEADER][i] = String.valueOf(i + firstIndex + 1); - break; - default: - try { - cells[CELL_HEADER][i] = getItemCaption(id); - } catch (Exception e) { - exceptionsDuringCachePopulation.add(e); - cells[CELL_HEADER][i] = ""; - } - } - try { - cells[CELL_ICON][i] = getItemIcon(id); - } catch (Exception e) { - exceptionsDuringCachePopulation.add(e); - cells[CELL_ICON][i] = null; - } - } - - GeneratedRow generatedRow = rowGenerator != null - ? rowGenerator.generateRow(this, id) : null; - cells[CELL_GENERATED_ROW][i] = generatedRow; - - for (int j = 0; j < cols; j++) { - if (isColumnCollapsed(colids[j])) { - continue; - } - Property p = null; - Object value = ""; - boolean isGeneratedRow = generatedRow != null; - boolean isGeneratedColumn = columnGenerators.containsKey(colids[j]); - boolean isGenerated = isGeneratedRow || isGeneratedColumn; - - if (!isGenerated) { - try { - p = getContainerProperty(id, colids[j]); - } catch (Exception e) { - exceptionsDuringCachePopulation.add(e); - value = null; - } - } - - if (isGeneratedRow) { - if (generatedRow.isSpanColumns() && j > 0) { - value = null; - } else if (generatedRow.isSpanColumns() && j == 0 - && generatedRow.getValue() instanceof Component) { - value = generatedRow.getValue(); - } else if (generatedRow.getText().length > j) { - value = generatedRow.getText()[j]; - } - } else { - // check if current pageBuffer already has row - int index = firstIndex + i; - if (p != null || isGenerated) { - int indexInOldBuffer = index - pageBufferFirstIndex; - if (index < firstIndexNotInCache - && index >= pageBufferFirstIndex - && pageBuffer[CELL_GENERATED_ROW][indexInOldBuffer] == null - && id.equals( - pageBuffer[CELL_ITEMID][indexInOldBuffer])) { - // we already have data in our cache, - // recycle it instead of fetching it via - // getValue/getPropertyValue - value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer]; - if (!isGeneratedColumn && iscomponent[j] - || !(value instanceof Component)) { - listenProperty(p, oldListenedProperties); - } - } else { - if (isGeneratedColumn) { - ColumnGenerator cg = columnGenerators - .get(colids[j]); - try { - value = cg.generateCell(this, id, colids[j]); - } catch (Exception e) { - exceptionsDuringCachePopulation.add(e); - value = null; - } - if (value != null && !(value instanceof Component) - && !(value instanceof String)) { - // Avoid errors if a generator returns - // something - // other than a Component or a String - value = value.toString(); - } - } else if (iscomponent[j]) { - try { - value = p.getValue(); - } catch (Exception e) { - exceptionsDuringCachePopulation.add(e); - value = null; - } - listenProperty(p, oldListenedProperties); - } else if (p != null) { - try { - value = getPropertyValue(id, colids[j], p); - } catch (Exception e) { - exceptionsDuringCachePopulation.add(e); - value = null; - } - /* - * If returned value is Component (via fieldfactory - * or overridden getPropertyValue) we expect it to - * listen property value changes. Otherwise if - * property emits value change events, table will - * start to listen them and refresh content when - * needed. - */ - if (!(value instanceof Component)) { - listenProperty(p, oldListenedProperties); - } - } else { - try { - value = getPropertyValue(id, colids[j], null); - } catch (Exception e) { - exceptionsDuringCachePopulation.add(e); - value = null; - } - } - } - } - } - - if (value instanceof Component) { - registerComponent((Component) value); - } - cells[CELL_FIRSTCOL + j][i] = value; - } - } - - protected void registerComponent(Component component) { - getLogger().log(Level.FINEST, "Registered {0}: {1}", new Object[] { - component.getClass().getSimpleName(), component.getCaption() }); - if (!equals(component.getParent())) { - component.setParent(this); - } - visibleComponents.add(component); - } - - private void listenProperty(Property p, - HashSet> oldListenedProperties) { - if (p instanceof Property.ValueChangeNotifier) { - if (oldListenedProperties == null - || !oldListenedProperties.contains(p)) { - ((Property.ValueChangeNotifier) p).addListener(this); - } - /* - * register listened properties, so we can do proper cleanup to free - * memory. Essential if table has loads of data and it is used for a - * long time. - */ - listenedProperties.add(p); - - } - } - - /** - * @param firstIx - * Index of the first row to process. Global index, not relative - * to page buffer. - * @param count - */ - private void unregisterComponentsAndPropertiesInRows(int firstIx, - int count) { - if (getLogger().isLoggable(Level.FINEST)) { - getLogger().log(Level.FINEST, - "Unregistering components in rows {0}-{1}", - new Object[] { firstIx, (firstIx + count - 1) }); - } - Object[] colids = getVisibleColumns(); - if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) { - int bufSize = pageBuffer[CELL_ITEMID].length; - int ix = firstIx - pageBufferFirstIndex; - ix = ix < 0 ? 0 : ix; - if (ix < bufSize) { - count = count > bufSize - ix ? bufSize - ix : count; - for (int i = 0; i < count; i++) { - for (int c = 0; c < colids.length; c++) { - Object cellVal = pageBuffer[CELL_FIRSTCOL + c][i + ix]; - if (cellVal instanceof Component - && visibleComponents.contains(cellVal)) { - visibleComponents.remove(cellVal); - unregisterComponent((Component) cellVal); - } else { - Property p = getContainerProperty( - pageBuffer[CELL_ITEMID][i + ix], colids[c]); - if (p instanceof ValueChangeNotifier - && listenedProperties.contains(p)) { - listenedProperties.remove(p); - ((ValueChangeNotifier) p).removeListener(this); - } - } - } - } - } - } - } - - /** - * Helper method to remove listeners and maintain correct component - * hierarchy. Detaches properties and components if those are no more - * rendered in client. - * - * @param oldListenedProperties - * set of properties that where listened in last render - * @param oldVisibleComponents - * set of components that where attached in last render - */ - private void unregisterPropertiesAndComponents( - HashSet> oldListenedProperties, - HashSet oldVisibleComponents) { - if (oldVisibleComponents != null) { - for (final Iterator i = oldVisibleComponents - .iterator(); i.hasNext();) { - Component c = i.next(); - if (!visibleComponents.contains(c)) { - unregisterComponent(c); - } - } - } - - if (oldListenedProperties != null) { - for (final Iterator> i = oldListenedProperties - .iterator(); i.hasNext();) { - Property.ValueChangeNotifier o = (ValueChangeNotifier) i.next(); - if (!listenedProperties.contains(o)) { - o.removeListener(this); - } - } - } - } - - /** - * This method cleans up a Component that has been generated when Table is - * in editable mode. The component needs to be detached from its parent and - * if it is a field, it needs to be detached from its property data source - * in order to allow garbage collection to take care of removing the unused - * component from memory. - * - * Override this method and getPropertyValue(Object, Object, Property) with - * custom logic if you need to deal with buffered fields. - * - * @see #getPropertyValue(Object, Object, Property) - * - * @param component - * component that should be unregistered. - */ - protected void unregisterComponent(Component component) { - getLogger().log(Level.FINEST, "Unregistered {0}: {1}", new Object[] { - component.getClass().getSimpleName(), component.getCaption() }); - component.setParent(null); - /* - * Also remove property data sources to unregister listeners keeping the - * fields in memory. - */ - if (component instanceof LegacyField) { - LegacyField field = (LegacyField) component; - Property associatedProperty = associatedProperties - .remove(component); - if (associatedProperty != null - && field.getPropertyDataSource() == associatedProperty) { - // Remove the property data source only if it's the one we - // added in getPropertyValue - field.setPropertyDataSource(null); - } - } - } - - /** - * Sets the row header mode. - *

    - * The mode can be one of the following ones: - *

      - *
    • {@link #ROW_HEADER_MODE_HIDDEN}: The row captions are hidden.
    • - *
    • {@link #ROW_HEADER_MODE_ID}: Items Id-objects toString() - * is used as row caption. - *
    • {@link #ROW_HEADER_MODE_ITEM}: Item-objects toString() - * is used as row caption. - *
    • {@link #ROW_HEADER_MODE_PROPERTY}: Property set with - * {@link #setItemCaptionPropertyId(Object)} is used as row header. - *
    • {@link #ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID}: Items Id-objects - * toString() is used as row header. If caption is explicitly - * specified, it overrides the id-caption. - *
    • {@link #ROW_HEADER_MODE_EXPLICIT}: The row headers must be explicitly - * specified.
    • - *
    • {@link #ROW_HEADER_MODE_INDEX}: The index of the item is used as row - * caption. The index mode can only be used with the containers implementing - * Container.Indexed interface.
    • - *
    - * The default value is {@link #ROW_HEADER_MODE_HIDDEN} - *

    - * - * @param mode - * the One of the modes listed above. - */ - public void setRowHeaderMode(RowHeaderMode mode) { - if (mode != null) { - rowHeaderMode = mode; - if (mode != RowHeaderMode.HIDDEN) { - setItemCaptionMode(mode.getItemCaptionMode()); - } - // Assures the visual refresh. No need to reset the page buffer - // before - // as the content has not changed, only the alignments. - refreshRenderedCells(); - } - } - - /** - * Gets the row header mode. - * - * @return the Row header mode. - * @see #setRowHeaderMode - */ - public RowHeaderMode getRowHeaderMode() { - return rowHeaderMode; - } - - /** - * Adds the new row to table and fill the visible cells (except generated - * columns) with given values. - * - * @param cells - * the Object array that is used for filling the visible cells - * new row. The types must be settable to visible column property - * types. - * @param itemId - * the Id the new row. If null, a new id is automatically - * assigned. If given, the table cannot already have a item with - * given id. - * @return Returns item id for the new row. Returns null if operation fails. - */ - public Object addItem(Object[] cells, Object itemId) - throws UnsupportedOperationException { - - // remove generated columns from the list of columns being assigned - final LinkedList availableCols = new LinkedList(); - for (Iterator it = visibleColumns.iterator(); it.hasNext();) { - Object id = it.next(); - if (!columnGenerators.containsKey(id)) { - availableCols.add(id); - } - } - // Checks that a correct number of cells are given - if (cells.length != availableCols.size()) { - return null; - } - - // Creates new item - Item item; - if (itemId == null) { - itemId = items.addItem(); - if (itemId == null) { - return null; - } - item = items.getItem(itemId); - } else { - item = items.addItem(itemId); - } - if (item == null) { - return null; - } - - // Fills the item properties - for (int i = 0; i < availableCols.size(); i++) { - item.getItemProperty(availableCols.get(i)).setValue(cells[i]); - } - - if (!(items instanceof Container.ItemSetChangeNotifier)) { - refreshRowCache(); - } - - return itemId; - } - - /** - * Discards and recreates the internal row cache. Call this if you make - * changes that affect the rows but the information about the changes are - * not automatically propagated to the Table. - *

    - * Do not call this e.g. if you have updated the data model through a - * Property. These types of changes are automatically propagated to the - * Table. - *

    - * A typical case when this is needed is if you update a generator (e.g. - * CellStyleGenerator) and want to ensure that the rows are redrawn with new - * styles. - *

    - * Note that calling this method is not cheap so avoid calling it - * unnecessarily. - * - * @since 6.7.2 - */ - public void refreshRowCache() { - resetPageBuffer(); - refreshRenderedCells(); - } - - /** - * Sets the Container that serves as the data source of the viewer. As a - * side-effect the table's selection value is set to null as the old - * selection might not exist in new Container.
    - *
    - * All rows and columns are generated as visible using this method. If the - * new container contains properties that are not meant to be shown you - * should use {@link Table#setContainerDataSource(Container, Collection)} - * instead, especially if the table is editable. - *

    - * Keeps propertyValueConverters if the corresponding id exists in the new - * data source and is of a compatible type. - *

    - * - * @param newDataSource - * the new data source. - */ - @Override - public void setContainerDataSource(Container newDataSource) { - if (newDataSource == null) { - newDataSource = new IndexedContainer(); - } - - Collection generated; - if (columnGenerators != null) { - generated = columnGenerators.keySet(); - } else { - generated = Collections.emptyList(); - } - List visibleIds = new ArrayList(); - if (generated.isEmpty()) { - visibleIds.addAll(newDataSource.getContainerPropertyIds()); - } else { - for (Object id : newDataSource.getContainerPropertyIds()) { - // don't add duplicates - if (!generated.contains(id)) { - visibleIds.add(id); - } - } - // generated columns to the end - visibleIds.addAll(generated); - } - setContainerDataSource(newDataSource, visibleIds); - } - - /** - * Sets the container data source and the columns that will be visible. - * Columns are shown in the collection's iteration order. - *

    - * Keeps propertyValueConverters if the corresponding id exists in the new - * data source and is of a compatible type. - *

    - * - * @see Table#setContainerDataSource(Container) - * @see Table#setVisibleColumns(Object[]) - * @see Table#setConverter(Object, Converter) - * - * @param newDataSource - * the new data source. - * @param visibleIds - * IDs of the visible columns - */ - public void setContainerDataSource(Container newDataSource, - Collection visibleIds) { - - disableContentRefreshing(); - - if (newDataSource == null) { - newDataSource = new IndexedContainer(); - } - if (visibleIds == null) { - visibleIds = new ArrayList(); - } - - // Retain propertyValueConverters if their corresponding ids are - // properties of the new - // data source and are of a compatible type - if (propertyValueConverters != null) { - Collection newPropertyIds = newDataSource - .getContainerPropertyIds(); - LinkedList retainableValueConverters = new LinkedList(); - for (Object propertyId : newPropertyIds) { - LegacyConverter converter = getConverter(propertyId); - if (converter != null) { - if (typeIsCompatible(converter.getModelType(), - newDataSource.getType(propertyId))) { - retainableValueConverters.add(propertyId); - } - } - } - propertyValueConverters.keySet() - .retainAll(retainableValueConverters); - } - - // Assures that the data source is ordered by making unordered - // containers ordered by wrapping them - if (newDataSource instanceof Container.Ordered) { - super.setContainerDataSource(newDataSource); - } else { - super.setContainerDataSource( - new ContainerOrderedWrapper(newDataSource)); - } - - // Resets page position - currentPageFirstItemId = null; - currentPageFirstItemIndex = 0; - - // Resets column properties - if (collapsedColumns != null) { - collapsedColumns.clear(); - } - - // don't add the same id twice - Collection col = new LinkedList(); - for (Iterator it = visibleIds.iterator(); it.hasNext();) { - Object id = it.next(); - if (!col.contains(id)) { - col.add(id); - } - } - - setVisibleColumns(col.toArray()); - - // Assure visual refresh - resetPageBuffer(); - - enableContentRefreshing(true); - } - - /** - * Checks if class b can be safely assigned to class a. - * - * @param a - * @param b - * @return - */ - private boolean typeIsCompatible(Class a, Class b) { - // TODO Implement this check properly - // Basically we need to do a a.isAssignableFrom(b) - // with special considerations for primitive types. - return true; - } - - /** - * Gets items ids from a range of key values - * - * @param itemId - * The start key - * @param length - * amount of items to be retrieved - * @return - */ - private LinkedHashSet getItemIdsInRange(Object itemId, - final int length) { - LinkedHashSet ids = new LinkedHashSet(); - for (int i = 0; i < length; i++) { - assert itemId != null; // should not be null unless client-server - // are out of sync - ids.add(itemId); - itemId = nextItemId(itemId); - } - return ids; - } - - /** - * Handles selection if selection is a multiselection - * - * @param variables - * The variables - */ - private void handleSelectedItems(Map variables) { - final String[] ka = (String[]) variables.get("selected"); - final String[] ranges = (String[]) variables.get("selectedRanges"); - - Set renderedButNotSelectedItemIds = getCurrentlyRenderedItemIds(); - - @SuppressWarnings("unchecked") - HashSet newValue = new LinkedHashSet( - (Collection) getValue()); - - if (variables.containsKey("clearSelections")) { - // the client side has instructed to swipe all previous selections - newValue.clear(); - } - - /* - * Then add (possibly some of them back) rows that are currently - * selected on the client side (the ones that the client side is aware - * of). - */ - for (int i = 0; i < ka.length; i++) { - // key to id - final Object id = itemIdMapper.get(ka[i]); - if (!isNullSelectionAllowed() - && (id == null || id == getNullSelectionItemId())) { - // skip empty selection if nullselection is not allowed - markAsDirty(); - } else if (id != null && containsId(id)) { - newValue.add(id); - renderedButNotSelectedItemIds.remove(id); - } - } - - /* Add range items aka shift clicked multiselection areas */ - if (ranges != null) { - for (String range : ranges) { - String[] split = range.split("-"); - Object startItemId = itemIdMapper.get(split[0]); - int length = Integer.valueOf(split[1]); - LinkedHashSet itemIdsInRange = getItemIdsInRange( - startItemId, length); - newValue.addAll(itemIdsInRange); - renderedButNotSelectedItemIds.removeAll(itemIdsInRange); - } - } - /* - * finally clear all currently rendered rows (the ones that the client - * side counterpart is aware of) that the client didn't send as selected - */ - newValue.removeAll(renderedButNotSelectedItemIds); - - if (!isNullSelectionAllowed() && newValue.isEmpty()) { - // empty selection not allowed, keep old value - markAsDirty(); - return; - } - - setValue(newValue, true); - - } - - private Set getCurrentlyRenderedItemIds() { - HashSet ids = new HashSet(); - if (pageBuffer != null) { - for (int i = 0; i < pageBuffer[CELL_ITEMID].length; i++) { - ids.add(pageBuffer[CELL_ITEMID][i]); - } - } - return ids; - } - - /* Component basics */ - - /** - * Invoked when the value of a variable has changed. - * - * @see com.vaadin.ui.Select#changeVariables(java.lang.Object, - * java.util.Map) - */ - - @Override - public void changeVariables(Object source, Map variables) { - - boolean clientNeedsContentRefresh = false; - - handleClickEvent(variables); - - handleColumnResizeEvent(variables); - - handleColumnWidthUpdates(variables); - - disableContentRefreshing(); - - if (!isSelectable() && variables.containsKey("selected")) { - // Not-selectable is a special case, AbstractSelect does not support - // TODO could be optimized. - variables = new HashMap(variables); - variables.remove("selected"); - } - - /* - * The AbstractSelect cannot handle the multiselection properly, instead - * we handle it ourself - */ - else if (isSelectable() && isMultiSelect() - && variables.containsKey("selected") - && multiSelectMode == MultiSelectMode.DEFAULT) { - handleSelectedItems(variables); - variables = new HashMap(variables); - variables.remove("selected"); - } - - super.changeVariables(source, variables); - - // Client might update the pagelength if Table height is fixed - if (variables.containsKey("pagelength")) { - // Sets pageLength directly to avoid repaint that setter causes - pageLength = (Integer) variables.get("pagelength"); - } - - // Page start index - if (variables.containsKey("firstvisible")) { - final Integer value = (Integer) variables.get("firstvisible"); - if (value != null) { - setCurrentPageFirstItemIndex(value.intValue(), false); - } - } - - // Sets requested firstrow and rows for the next paint - if (variables.containsKey("reqfirstrow") - || variables.containsKey("reqrows")) { - - try { - firstToBeRenderedInClient = ((Integer) variables - .get("firstToBeRendered")).intValue(); - lastToBeRenderedInClient = ((Integer) variables - .get("lastToBeRendered")).intValue(); - } catch (Exception e) { - // FIXME: Handle exception - getLogger().log(Level.FINER, - "Could not parse the first and/or last rows.", e); - } - - // respect suggested rows only if table is not otherwise updated - // (row caches emptied by other event) - if (!containerChangeToBeRendered) { - Integer value = (Integer) variables.get("reqfirstrow"); - if (value != null) { - reqFirstRowToPaint = value.intValue(); - } - - value = (Integer) variables.get("reqrows"); - if (value != null) { - reqRowsToPaint = value.intValue(); - int size = size(); - // sanity check - - if (reqFirstRowToPaint >= size) { - reqFirstRowToPaint = size; - } - - if (reqFirstRowToPaint + reqRowsToPaint > size) { - reqRowsToPaint = size - reqFirstRowToPaint; - } - } - } - if (getLogger().isLoggable(Level.FINEST)) { - getLogger().log(Level.FINEST, "Client wants rows {0}-{1}", - new Object[] { reqFirstRowToPaint, - (reqFirstRowToPaint + reqRowsToPaint - 1) }); - } - clientNeedsContentRefresh = true; - } - - if (isSortEnabled()) { - // Sorting - boolean doSort = false; - if (variables.containsKey("sortcolumn")) { - final String colId = (String) variables.get("sortcolumn"); - if (colId != null && !"".equals(colId) - && !"null".equals(colId)) { - final Object id = columnIdMap.get(colId); - setSortContainerPropertyId(id, false); - doSort = true; - } - } - if (variables.containsKey("sortascending")) { - final boolean state = ((Boolean) variables.get("sortascending")) - .booleanValue(); - if (state != sortAscending) { - setSortAscending(state, false); - doSort = true; - } - } - if (doSort) { - this.sort(); - resetPageBuffer(); - } - } - - // Dynamic column hide/show and order - // Update visible columns - if (isColumnCollapsingAllowed()) { - if (variables.containsKey("collapsedcolumns")) { - try { - final Object[] ids = (Object[]) variables - .get("collapsedcolumns"); - Set idSet = new HashSet(); - for (Object id : ids) { - idSet.add(columnIdMap.get(id.toString())); - } - for (final Iterator it = visibleColumns - .iterator(); it.hasNext();) { - Object propertyId = it.next(); - if (isColumnCollapsed(propertyId)) { - if (!idSet.contains(propertyId)) { - setColumnCollapsed(propertyId, false); - } - } else if (idSet.contains(propertyId)) { - setColumnCollapsed(propertyId, true); - } - } - } catch (final Exception e) { - // FIXME: Handle exception - getLogger().log(Level.FINER, - "Could not determine column collapsing state", e); - } - clientNeedsContentRefresh = true; - } - } - if (isColumnReorderingAllowed()) { - if (variables.containsKey("columnorder")) { - try { - final Object[] ids = (Object[]) variables - .get("columnorder"); - // need a real Object[], ids can be a String[] - final Object[] idsTemp = new Object[ids.length]; - for (int i = 0; i < ids.length; i++) { - idsTemp[i] = columnIdMap.get(ids[i].toString()); - } - setColumnOrder(idsTemp); - if (hasListeners(ColumnReorderEvent.class)) { - fireEvent(new ColumnReorderEvent(this)); - } - } catch (final Exception e) { - // FIXME: Handle exception - getLogger().log(Level.FINER, - "Could not determine column reordering state", e); - } - clientNeedsContentRefresh = true; - } - } - - enableContentRefreshing(clientNeedsContentRefresh); - - // Actions - if (variables.containsKey("action")) { - final StringTokenizer st = new StringTokenizer( - (String) variables.get("action"), ","); - if (st.countTokens() == 2) { - final Object itemId = itemIdMapper.get(st.nextToken()); - final Action action = actionMapper.get(st.nextToken()); - - if (action != null && (itemId == null || containsId(itemId)) - && actionHandlers != null) { - for (Handler ah : actionHandlers) { - ah.handleAction(action, this, itemId); - } - } - } - } - - } - - /** - * Handles click event - * - * @param variables - */ - private void handleClickEvent(Map variables) { - - // Item click event - if (variables.containsKey("clickEvent")) { - String key = (String) variables.get("clickedKey"); - Object itemId = itemIdMapper.get(key); - Object propertyId = null; - String colkey = (String) variables.get("clickedColKey"); - // click is not necessary on a property - if (colkey != null) { - propertyId = columnIdMap.get(colkey); - } - MouseEventDetails evt = MouseEventDetails - .deSerialize((String) variables.get("clickEvent")); - Item item = getItem(itemId); - if (item != null) { - fireEvent(new ItemClickEvent(this, item, itemId, propertyId, - evt)); - } - } - - // Header click event - else if (variables.containsKey("headerClickEvent")) { - - MouseEventDetails details = MouseEventDetails - .deSerialize((String) variables.get("headerClickEvent")); - - Object cid = variables.get("headerClickCID"); - Object propertyId = null; - if (cid != null) { - propertyId = columnIdMap.get(cid.toString()); - } - fireEvent(new HeaderClickEvent(this, propertyId, details)); - } - - // Footer click event - else if (variables.containsKey("footerClickEvent")) { - MouseEventDetails details = MouseEventDetails - .deSerialize((String) variables.get("footerClickEvent")); - - Object cid = variables.get("footerClickCID"); - Object propertyId = null; - if (cid != null) { - propertyId = columnIdMap.get(cid.toString()); - } - fireEvent(new FooterClickEvent(this, propertyId, details)); - } - } - - /** - * Handles the column resize event sent by the client. - * - * @param variables - */ - private void handleColumnResizeEvent(Map variables) { - if (variables.containsKey("columnResizeEventColumn")) { - Object cid = variables.get("columnResizeEventColumn"); - Object propertyId = null; - if (cid != null) { - propertyId = columnIdMap.get(cid.toString()); - - Object prev = variables.get("columnResizeEventPrev"); - int previousWidth = -1; - if (prev != null) { - previousWidth = Integer.valueOf(prev.toString()); - } - - Object curr = variables.get("columnResizeEventCurr"); - int currentWidth = -1; - if (curr != null) { - currentWidth = Integer.valueOf(curr.toString()); - } - - fireColumnResizeEvent(propertyId, previousWidth, currentWidth); - } - } - } - - private void fireColumnCollapseEvent(Object propertyId) { - fireEvent(new ColumnCollapseEvent(this, propertyId)); - } - - private void fireColumnResizeEvent(Object propertyId, int previousWidth, - int currentWidth) { - /* - * Update the sizes on the server side. If a column previously had a - * expand ratio and the user resized the column then the expand ratio - * will be turned into a static pixel size. - */ - setColumnWidth(propertyId, currentWidth); - - fireEvent(new ColumnResizeEvent(this, propertyId, previousWidth, - currentWidth)); - } - - private void handleColumnWidthUpdates(Map variables) { - if (variables.containsKey("columnWidthUpdates")) { - String[] events = (String[]) variables.get("columnWidthUpdates"); - for (String str : events) { - String[] eventDetails = str.split(":"); - Object propertyId = columnIdMap.get(eventDetails[0]); - if (propertyId == null) { - propertyId = ROW_HEADER_FAKE_PROPERTY_ID; - } - int width = Integer.valueOf(eventDetails[1]); - setColumnWidth(propertyId, width); - } - } - } - - /** - * Go to mode where content updates are not done. This is due we want to - * bypass expensive content for some reason (like when we know we may have - * other content changes on their way). - * - * @return true if content refresh flag was enabled prior this call - */ - protected boolean disableContentRefreshing() { - boolean wasDisabled = isContentRefreshesEnabled; - isContentRefreshesEnabled = false; - return wasDisabled; - } - - /** - * Go to mode where content content refreshing has effect. - * - * @param refreshContent - * true if content refresh needs to be done - */ - protected void enableContentRefreshing(boolean refreshContent) { - isContentRefreshesEnabled = true; - if (refreshContent) { - refreshRenderedCells(); - // Ensure that client gets a response - markAsDirty(); - } - } - - @Override - public void beforeClientResponse(boolean initial) { - super.beforeClientResponse(initial); - - // Ensure pageBuffer is filled before sending the response to avoid - // calls to markAsDirty during paint - getVisibleCells(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin. - * terminal.PaintTarget) - */ - - @Override - public void paintContent(PaintTarget target) throws PaintException { - isBeingPainted = true; - try { - doPaintContent(target); - } finally { - isBeingPainted = false; - } - } - - private void doPaintContent(PaintTarget target) throws PaintException { - /* - * Body actions - Actions which has the target null and can be invoked - * by right clicking on the table body. - */ - final Set actionSet = findAndPaintBodyActions(target); - - final Object[][] cells = getVisibleCells(); - int rows = findNumRowsToPaint(target, cells); - - int total = size(); - if (shouldHideNullSelectionItem()) { - total--; - rows--; - } - - // Table attributes - paintTableAttributes(target, rows, total); - - paintVisibleColumnOrder(target); - - // Rows - if (isPartialRowUpdate() && painted && !target.isFullRepaint()) { - paintPartialRowUpdate(target, actionSet); - } else if (target.isFullRepaint() || isRowCacheInvalidated()) { - paintRows(target, cells, actionSet); - setRowCacheInvalidated(false); - } - - /* - * Send the page buffer indexes to ensure that the client side stays in - * sync. Otherwise we _might_ have the situation where the client side - * discards too few or too many rows, causing out of sync issues. - */ - int pageBufferLastIndex = pageBufferFirstIndex - + pageBuffer[CELL_ITEMID].length - 1; - target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST, - pageBufferFirstIndex); - target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_LAST, - pageBufferLastIndex); - - paintSorting(target); - - resetVariablesAndPageBuffer(target); - - // Actions - paintActions(target, actionSet); - - paintColumnOrder(target); - - // Available columns - paintAvailableColumns(target); - - paintVisibleColumns(target); - - if (keyMapperReset) { - keyMapperReset = false; - target.addAttribute(TableConstants.ATTRIBUTE_KEY_MAPPER_RESET, - true); - } - - if (dropHandler != null) { - dropHandler.getAcceptCriterion().paint(target); - } - - painted = true; - } - - private void setRowCacheInvalidated(boolean invalidated) { - rowCacheInvalidated = invalidated; - } - - protected boolean isRowCacheInvalidated() { - return rowCacheInvalidated; - } - - private void paintPartialRowUpdate(PaintTarget target, - Set actionSet) throws PaintException { - paintPartialRowUpdates(target, actionSet); - paintPartialRowAdditions(target, actionSet); - } - - private void paintPartialRowUpdates(PaintTarget target, - Set actionSet) throws PaintException { - final boolean[] iscomponent = findCellsWithComponents(); - - int firstIx = getFirstUpdatedItemIndex(); - int count = getUpdatedRowCount(); - - target.startTag("urows"); - target.addAttribute("firsturowix", firstIx); - target.addAttribute("numurows", count); - - // Partial row updates bypass the normal caching mechanism. - Object[][] cells = getVisibleCellsUpdateCacheRows(firstIx, count); - for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) { - final Object itemId = cells[CELL_ITEMID][indexInRowbuffer]; - - if (shouldHideNullSelectionItem()) { - // Remove null selection item if null selection is not allowed - continue; - } - - paintRow(target, cells, isEditable(), actionSet, iscomponent, - indexInRowbuffer, itemId); - } - target.endTag("urows"); - maybeThrowCacheUpdateExceptions(); - } - - private void paintPartialRowAdditions(PaintTarget target, - Set actionSet) throws PaintException { - final boolean[] iscomponent = findCellsWithComponents(); - - int firstIx = getFirstAddedItemIndex(); - int count = getAddedRowCount(); - - target.startTag("prows"); - - if (!shouldHideAddedRows()) { - getLogger().log(Level.FINEST, - "Paint rows for add. Index: {0}, count: {1}.", - new Object[] { firstIx, count }); - - // Partial row additions bypass the normal caching mechanism. - Object[][] cells = getVisibleCellsInsertIntoCache(firstIx, count); - if (cells[0].length < count) { - // delete the rows below, since they will fall beyond the cache - // page. - target.addAttribute("delbelow", true); - count = cells[0].length; - } - - for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) { - final Object itemId = cells[CELL_ITEMID][indexInRowbuffer]; - if (shouldHideNullSelectionItem()) { - // Remove null selection item if null selection is not - // allowed - continue; - } - - paintRow(target, cells, isEditable(), actionSet, iscomponent, - indexInRowbuffer, itemId); - } - } else { - getLogger().log(Level.FINEST, - "Paint rows for remove. Index: {0}, count: {1}.", - new Object[] { firstIx, count }); - removeRowsFromCacheAndFillBottom(firstIx, count); - target.addAttribute("hide", true); - } - - target.addAttribute("firstprowix", firstIx); - target.addAttribute("numprows", count); - target.endTag("prows"); - maybeThrowCacheUpdateExceptions(); - } - - /** - * Subclass and override this to enable partial row updates and additions, - * which bypass the normal caching mechanism. This is useful for e.g. - * TreeTable. - * - * @return true if this update is a partial row update, false if not. For - * plain Table it is always false. - */ - protected boolean isPartialRowUpdate() { - return false; - } - - /** - * Subclass and override this to enable partial row additions, bypassing the - * normal caching mechanism. This is useful for e.g. TreeTable, where - * expanding a node should only fetch and add the items inside of that node. - * - * @return The index of the first added item. For plain Table it is always - * 0. - */ - protected int getFirstAddedItemIndex() { - return 0; - } - - /** - * Subclass and override this to enable partial row additions, bypassing the - * normal caching mechanism. This is useful for e.g. TreeTable, where - * expanding a node should only fetch and add the items inside of that node. - * - * @return the number of rows to be added, starting at the index returned by - * {@link #getFirstAddedItemIndex()}. For plain Table it is always - * 0. - */ - protected int getAddedRowCount() { - return 0; - } - - /** - * Subclass and override this to enable removing of rows, bypassing the - * normal caching and lazy loading mechanism. This is useful for e.g. - * TreeTable, when you need to hide certain rows as a node is collapsed. - * - * This should return true if the rows pointed to by - * {@link #getFirstAddedItemIndex()} and {@link #getAddedRowCount()} should - * be hidden instead of added. - * - * @return whether the rows to add (see {@link #getFirstAddedItemIndex()} - * and {@link #getAddedRowCount()}) should be added or hidden. For - * plain Table it is always false. - */ - protected boolean shouldHideAddedRows() { - return false; - } - - /** - * Subclass and override this to enable partial row updates, bypassing the - * normal caching and lazy loading mechanism. This is useful for updating - * the state of certain rows, e.g. in the TreeTable the collapsed state of a - * single node is updated using this mechanism. - * - * @return the index of the first item to be updated. For plain Table it is - * always 0. - */ - protected int getFirstUpdatedItemIndex() { - return 0; - } - - /** - * Subclass and override this to enable partial row updates, bypassing the - * normal caching and lazy loading mechanism. This is useful for updating - * the state of certain rows, e.g. in the TreeTable the collapsed state of a - * single node is updated using this mechanism. - * - * @return the number of rows to update, starting at the index returned by - * {@link #getFirstUpdatedItemIndex()}. For plain table it is always - * 0. - */ - protected int getUpdatedRowCount() { - return 0; - } - - private void paintTableAttributes(PaintTarget target, int rows, int total) - throws PaintException { - paintTabIndex(target); - paintDragMode(target); - paintSelectMode(target); - paintTableChildLayoutMeasureMode(target); - - if (cacheRate != CACHE_RATE_DEFAULT) { - target.addAttribute("cr", cacheRate); - } - - target.addAttribute("cols", getVisibleColumns().length); - target.addAttribute("rows", rows); - - target.addAttribute("firstrow", (reqFirstRowToPaint >= 0 - ? reqFirstRowToPaint : firstToBeRenderedInClient)); - target.addAttribute("totalrows", total); - if (getPageLength() != 0) { - target.addAttribute("pagelength", getPageLength()); - } - if (areColumnHeadersEnabled()) { - target.addAttribute("colheaders", true); - } - if (rowHeadersAreEnabled()) { - target.addAttribute("rowheaders", true); - } - - target.addAttribute("colfooters", columnFootersVisible); - - // The cursors are only shown on pageable table - if (getCurrentPageFirstItemIndex() != 0 || getPageLength() > 0) { - target.addVariable(this, "firstvisible", - getCurrentPageFirstItemIndex()); - target.addVariable(this, "firstvisibleonlastpage", - currentPageFirstItemIndexOnLastPage); - } - } - - /** - * Resets and paints "to be painted next" variables. Also reset pageBuffer - */ - private void resetVariablesAndPageBuffer(PaintTarget target) - throws PaintException { - reqFirstRowToPaint = -1; - reqRowsToPaint = -1; - containerChangeToBeRendered = false; - target.addVariable(this, "reqrows", reqRowsToPaint); - target.addVariable(this, "reqfirstrow", reqFirstRowToPaint); - } - - private boolean areColumnHeadersEnabled() { - return getColumnHeaderMode() != ColumnHeaderMode.HIDDEN; - } - - private void paintVisibleColumns(PaintTarget target) throws PaintException { - target.startTag("visiblecolumns"); - if (rowHeadersAreEnabled()) { - target.startTag("column"); - target.addAttribute("cid", ROW_HEADER_COLUMN_KEY); - paintColumnWidth(target, ROW_HEADER_FAKE_PROPERTY_ID); - paintColumnExpandRatio(target, ROW_HEADER_FAKE_PROPERTY_ID); - target.endTag("column"); - } - final Collection sortables = getSortableContainerPropertyIds(); - for (Object colId : visibleColumns) { - if (colId != null) { - target.startTag("column"); - target.addAttribute("cid", columnIdMap.key(colId)); - final String head = getColumnHeader(colId); - target.addAttribute("caption", (head != null ? head : "")); - final String foot = getColumnFooter(colId); - target.addAttribute("fcaption", (foot != null ? foot : "")); - if (isColumnCollapsed(colId)) { - target.addAttribute("collapsed", true); - } - if (areColumnHeadersEnabled()) { - if (getColumnIcon(colId) != null) { - target.addAttribute("icon", getColumnIcon(colId)); - } - if (sortables.contains(colId)) { - target.addAttribute("sortable", true); - } - } - if (!Align.LEFT.equals(getColumnAlignment(colId))) { - target.addAttribute("align", - getColumnAlignment(colId).toString()); - } - paintColumnWidth(target, colId); - paintColumnExpandRatio(target, colId); - target.endTag("column"); - } - } - target.endTag("visiblecolumns"); - } - - private void paintAvailableColumns(PaintTarget target) - throws PaintException { - if (columnCollapsingAllowed) { - final HashSet collapsedCols = new HashSet(); - for (Object colId : visibleColumns) { - if (isColumnCollapsed(colId)) { - collapsedCols.add(colId); - } - } - final String[] collapsedKeys = new String[collapsedCols.size()]; - int nextColumn = 0; - for (Object colId : visibleColumns) { - if (isColumnCollapsed(colId)) { - collapsedKeys[nextColumn++] = columnIdMap.key(colId); - } - } - target.addVariable(this, "collapsedcolumns", collapsedKeys); - - final String[] noncollapsibleKeys = new String[noncollapsibleColumns - .size()]; - nextColumn = 0; - for (Object colId : noncollapsibleColumns) { - noncollapsibleKeys[nextColumn++] = columnIdMap.key(colId); - } - target.addVariable(this, "noncollapsiblecolumns", - noncollapsibleKeys); - } - - } - - private void paintActions(PaintTarget target, final Set actionSet) - throws PaintException { - if (!actionSet.isEmpty()) { - target.addVariable(this, "action", ""); - target.startTag("actions"); - for (Action a : actionSet) { - target.startTag("action"); - if (a.getCaption() != null) { - target.addAttribute("caption", a.getCaption()); - } - if (a.getIcon() != null) { - target.addAttribute("icon", a.getIcon()); - } - target.addAttribute("key", actionMapper.key(a)); - target.endTag("action"); - } - target.endTag("actions"); - } - } - - private void paintColumnOrder(PaintTarget target) throws PaintException { - if (columnReorderingAllowed) { - final String[] colorder = new String[visibleColumns.size()]; - int i = 0; - for (Object colId : visibleColumns) { - colorder[i++] = columnIdMap.key(colId); - } - target.addVariable(this, "columnorder", colorder); - } - } - - private void paintSorting(PaintTarget target) throws PaintException { - // Sorting - if (getContainerDataSource() instanceof Container.Sortable) { - target.addVariable(this, "sortcolumn", - columnIdMap.key(sortContainerPropertyId)); - target.addVariable(this, "sortascending", sortAscending); - } - } - - private void paintRows(PaintTarget target, final Object[][] cells, - final Set actionSet) throws PaintException { - final boolean[] iscomponent = findCellsWithComponents(); - - target.startTag("rows"); - // cells array contains all that are supposed to be visible on client, - // but we'll start from the one requested by client - int start = 0; - if (reqFirstRowToPaint != -1 && firstToBeRenderedInClient != -1) { - start = reqFirstRowToPaint - firstToBeRenderedInClient; - } - int end = cells[0].length; - if (reqRowsToPaint != -1) { - end = start + reqRowsToPaint; - } - // sanity check - if (lastToBeRenderedInClient != -1 && lastToBeRenderedInClient < end) { - end = lastToBeRenderedInClient + 1; - } - if (start > cells[CELL_ITEMID].length || start < 0) { - start = 0; - } - if (end > cells[CELL_ITEMID].length) { - end = cells[CELL_ITEMID].length; - } - - for (int indexInRowbuffer = start; indexInRowbuffer < end; indexInRowbuffer++) { - final Object itemId = cells[CELL_ITEMID][indexInRowbuffer]; - - if (shouldHideNullSelectionItem()) { - // Remove null selection item if null selection is not allowed - continue; - } - - paintRow(target, cells, isEditable(), actionSet, iscomponent, - indexInRowbuffer, itemId); - } - target.endTag("rows"); - } - - private boolean[] findCellsWithComponents() { - final boolean[] isComponent = new boolean[visibleColumns.size()]; - int ix = 0; - for (Object columnId : visibleColumns) { - if (columnGenerators.containsKey(columnId)) { - isComponent[ix++] = true; - } else { - final Class colType = getType(columnId); - isComponent[ix++] = colType != null - && Component.class.isAssignableFrom(colType); - } - } - return isComponent; - } - - private void paintVisibleColumnOrder(PaintTarget target) { - // Visible column order - final ArrayList visibleColOrder = new ArrayList(); - for (Object columnId : visibleColumns) { - if (!isColumnCollapsed(columnId)) { - visibleColOrder.add(columnIdMap.key(columnId)); - } - } - target.addAttribute("vcolorder", visibleColOrder.toArray()); - } - - private Set findAndPaintBodyActions(PaintTarget target) { - Set actionSet = new LinkedHashSet(); - if (actionHandlers != null) { - final ArrayList keys = new ArrayList(); - for (Handler ah : actionHandlers) { - // Getting actions for the null item, which in this case means - // the body item - final Action[] actions = ah.getActions(null, this); - if (actions != null) { - for (Action action : actions) { - actionSet.add(action); - keys.add(actionMapper.key(action)); - } - } - } - target.addAttribute("alb", keys.toArray()); - } - return actionSet; - } - - private boolean shouldHideNullSelectionItem() { - return !isNullSelectionAllowed() && getNullSelectionItemId() != null - && containsId(getNullSelectionItemId()); - } - - private int findNumRowsToPaint(PaintTarget target, final Object[][] cells) - throws PaintException { - int rows; - if (reqRowsToPaint >= 0) { - rows = reqRowsToPaint; - } else { - rows = cells[0].length; - if (alwaysRecalculateColumnWidths) { - // TODO experimental feature for now: tell the client to - // recalculate column widths. - // We'll only do this for paints that do not originate from - // table scroll/cache requests (i.e when reqRowsToPaint<0) - target.addAttribute("recalcWidths", true); - } - } - return rows; - } - - private void paintSelectMode(PaintTarget target) throws PaintException { - if (multiSelectMode != MultiSelectMode.DEFAULT) { - target.addAttribute("multiselectmode", multiSelectMode.ordinal()); - } - if (isSelectable()) { - target.addAttribute("selectmode", - (isMultiSelect() ? "multi" : "single")); - } else { - target.addAttribute("selectmode", "none"); - } - if (!isNullSelectionAllowed()) { - target.addAttribute("nsa", false); - } - - // selection support - // The select variable is only enabled if selectable - if (isSelectable()) { - target.addVariable(this, "selected", findSelectedKeys()); - } - } - - private String[] findSelectedKeys() { - LinkedList selectedKeys = new LinkedList(); - if (isMultiSelect()) { - HashSet sel = new HashSet((Set) getValue()); - Collection vids = getVisibleItemIds(); - for (Iterator it = vids.iterator(); it.hasNext();) { - Object id = it.next(); - if (sel.contains(id)) { - selectedKeys.add(itemIdMapper.key(id)); - } - } - } else { - Object value = getValue(); - if (value == null) { - value = getNullSelectionItemId(); - } - if (value != null) { - selectedKeys.add(itemIdMapper.key(value)); - } - } - return selectedKeys.toArray(new String[selectedKeys.size()]); - } - - private void paintDragMode(PaintTarget target) throws PaintException { - if (dragMode != TableDragMode.NONE) { - target.addAttribute("dragmode", dragMode.ordinal()); - } - } - - private void paintTabIndex(PaintTarget target) throws PaintException { - // The tab ordering number - if (getTabIndex() > 0) { - target.addAttribute("tabindex", getTabIndex()); - } - } - - private void paintColumnWidth(PaintTarget target, final Object columnId) - throws PaintException { - if (columnWidths.containsKey(columnId)) { - target.addAttribute("width", getColumnWidth(columnId)); - } - } - - private void paintColumnExpandRatio(PaintTarget target, - final Object columnId) throws PaintException { - if (columnExpandRatios.containsKey(columnId)) { - target.addAttribute("er", getColumnExpandRatio(columnId)); - } - } - - private void paintTableChildLayoutMeasureMode(PaintTarget target) - throws PaintException { - target.addAttribute("measurehint", getChildMeasurementHint().ordinal()); - } - - /** - * Checks whether row headers are visible. - * - * @return {@code false} if row headers are hidden, {@code true} otherwise - * @since 7.3.9 - */ - protected boolean rowHeadersAreEnabled() { - return getRowHeaderMode() != RowHeaderMode.HIDDEN; - } - - private void paintRow(PaintTarget target, final Object[][] cells, - final boolean iseditable, final Set actionSet, - final boolean[] iscomponent, int indexInRowbuffer, - final Object itemId) throws PaintException { - target.startTag("tr"); - - paintRowAttributes(target, cells, actionSet, indexInRowbuffer, itemId); - - // cells - int currentColumn = 0; - for (final Iterator it = visibleColumns.iterator(); it - .hasNext(); currentColumn++) { - final Object columnId = it.next(); - if (columnId == null || isColumnCollapsed(columnId)) { - continue; - } - /* - * For each cell, if a cellStyleGenerator is specified, get the - * specific style for the cell. If there is any, add it to the - * target. - */ - if (cellStyleGenerator != null) { - String cellStyle = cellStyleGenerator.getStyle(this, itemId, - columnId); - if (cellStyle != null && !cellStyle.equals("")) { - target.addAttribute("style-" + columnIdMap.key(columnId), - cellStyle); - } - } - - if ((iscomponent[currentColumn] || iseditable - || cells[CELL_GENERATED_ROW][indexInRowbuffer] != null) - && Component.class.isInstance(cells[CELL_FIRSTCOL - + currentColumn][indexInRowbuffer])) { - final Component c = (Component) cells[CELL_FIRSTCOL - + currentColumn][indexInRowbuffer]; - if (c == null || !LegacyCommunicationManager - .isComponentVisibleToClient(c)) { - target.addText(""); - } else { - LegacyPaint.paint(c, target); - } - } else { - target.addText((String) cells[CELL_FIRSTCOL - + currentColumn][indexInRowbuffer]); - } - paintCellTooltips(target, itemId, columnId); - } - - target.endTag("tr"); - } - - private void paintCellTooltips(PaintTarget target, Object itemId, - Object columnId) throws PaintException { - if (itemDescriptionGenerator != null) { - String itemDescription = itemDescriptionGenerator - .generateDescription(this, itemId, columnId); - if (itemDescription != null && !itemDescription.equals("")) { - target.addAttribute("descr-" + columnIdMap.key(columnId), - itemDescription); - } - } - } - - private void paintRowTooltips(PaintTarget target, Object itemId) - throws PaintException { - if (itemDescriptionGenerator != null) { - String rowDescription = itemDescriptionGenerator - .generateDescription(this, itemId, null); - if (rowDescription != null && !rowDescription.equals("")) { - target.addAttribute("rowdescr", rowDescription); - } - } - } - - private void paintRowAttributes(PaintTarget target, final Object[][] cells, - final Set actionSet, int indexInRowbuffer, - final Object itemId) throws PaintException { - // tr attributes - - paintRowIcon(target, cells, indexInRowbuffer); - paintRowHeader(target, cells, indexInRowbuffer); - paintGeneratedRowInfo(target, cells, indexInRowbuffer); - target.addAttribute("key", - Integer.parseInt(cells[CELL_KEY][indexInRowbuffer].toString())); - - if (isSelected(itemId)) { - target.addAttribute("selected", true); - } - - // Actions - if (actionHandlers != null) { - final ArrayList keys = new ArrayList(); - for (Handler ah : actionHandlers) { - final Action[] aa = ah.getActions(itemId, this); - if (aa != null) { - for (int ai = 0; ai < aa.length; ai++) { - final String key = actionMapper.key(aa[ai]); - actionSet.add(aa[ai]); - keys.add(key); - } - } - } - target.addAttribute("al", keys.toArray()); - } - - /* - * For each row, if a cellStyleGenerator is specified, get the specific - * style for the cell, using null as propertyId. If there is any, add it - * to the target. - */ - if (cellStyleGenerator != null) { - String rowStyle = cellStyleGenerator.getStyle(this, itemId, null); - if (rowStyle != null && !rowStyle.equals("")) { - target.addAttribute("rowstyle", rowStyle); - } - } - - paintRowTooltips(target, itemId); - - paintRowAttributes(target, itemId); - } - - private void paintGeneratedRowInfo(PaintTarget target, Object[][] cells, - int indexInRowBuffer) throws PaintException { - GeneratedRow generatedRow = (GeneratedRow) cells[CELL_GENERATED_ROW][indexInRowBuffer]; - if (generatedRow != null) { - target.addAttribute("gen_html", - generatedRow.isHtmlContentAllowed()); - target.addAttribute("gen_span", generatedRow.isSpanColumns()); - target.addAttribute("gen_widget", - generatedRow.getValue() instanceof Component); - } - } - - protected void paintRowHeader(PaintTarget target, Object[][] cells, - int indexInRowbuffer) throws PaintException { - if (rowHeadersAreEnabled()) { - if (cells[CELL_HEADER][indexInRowbuffer] != null) { - target.addAttribute("caption", - (String) cells[CELL_HEADER][indexInRowbuffer]); - } - } - - } - - protected void paintRowIcon(PaintTarget target, final Object[][] cells, - int indexInRowbuffer) throws PaintException { - if (rowHeadersAreEnabled() - && cells[CELL_ICON][indexInRowbuffer] != null) { - target.addAttribute("icon", - (Resource) cells[CELL_ICON][indexInRowbuffer]); - } - } - - /** - * A method where extended Table implementations may add their custom - * attributes for rows. - * - * @param target - * @param itemId - */ - protected void paintRowAttributes(PaintTarget target, Object itemId) - throws PaintException { - - } - - /** - * Gets the cached visible table contents. - * - * @return the cached visible table contents. - */ - private Object[][] getVisibleCells() { - if (pageBuffer == null) { - refreshRenderedCells(); - } - return pageBuffer; - } - - /** - * Gets the value of property. - * - * By default if the table is editable the fieldFactory is used to create - * editors for table cells. Otherwise formatPropertyValue is used to format - * the value representation. - * - * @param rowId - * the Id of the row (same as item Id). - * @param colId - * the Id of the column. - * @param property - * the Property to be presented. - * @return Object Either formatted value or Component for field. - * @see #setTableFieldFactory(TableFieldFactory) - */ - protected Object getPropertyValue(Object rowId, Object colId, - Property property) { - if (isEditable() && fieldFactory != null) { - final LegacyField f = fieldFactory - .createField(getContainerDataSource(), rowId, colId, this); - if (f != null) { - // Remember that we have made this association so we can remove - // it when the component is removed - associatedProperties.put(f, property); - bindPropertyToField(rowId, colId, property, f); - return f; - } - } - - return formatPropertyValue(rowId, colId, property); - } - - /** - * Binds an item property to a field generated by TableFieldFactory. The - * default behavior is to bind property straight to LegacyField. If - * Property.Viewer type property (e.g. PropertyFormatter) is already set for - * field, the property is bound to that Property.Viewer. - * - * @param rowId - * @param colId - * @param property - * @param field - * @since 6.7.3 - */ - protected void bindPropertyToField(Object rowId, Object colId, - Property property, LegacyField field) { - // check if field has a property that is Viewer set. In that case we - // expect developer has e.g. PropertyFormatter that he wishes to use and - // assign the property to the Viewer instead. - boolean hasFilterProperty = field.getPropertyDataSource() != null - && (field.getPropertyDataSource() instanceof Property.Viewer); - if (hasFilterProperty) { - ((Property.Viewer) field.getPropertyDataSource()) - .setPropertyDataSource(property); - } else { - field.setPropertyDataSource(property); - } - } - - /** - * Formats table cell property values. By default the property.toString() - * and return a empty string for null properties. - * - * @param rowId - * the Id of the row (same as item Id). - * @param colId - * the Id of the column. - * @param property - * the Property to be formatted. - * @return the String representation of property and its value. - * @since 3.1 - */ - protected String formatPropertyValue(Object rowId, Object colId, - Property property) { - if (property == null) { - return ""; - } - LegacyConverter converter = null; - - if (hasConverter(colId)) { - converter = getConverter(colId); - } else { - converter = (LegacyConverter) LegacyConverterUtil.getConverter( - String.class, property.getType(), getSession()); - } - Object value = property.getValue(); - if (converter != null) { - return converter.convertToPresentation(value, String.class, - getLocale()); - } - return (null != value) ? value.toString() : ""; - } - - /* Action container */ - - /** - * Registers a new action handler for this container - * - * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler) - */ - - @Override - public void addActionHandler(Action.Handler actionHandler) { - - if (actionHandler != null) { - - if (actionHandlers == null) { - actionHandlers = new LinkedList(); - actionMapper = new KeyMapper(); - } - - if (!actionHandlers.contains(actionHandler)) { - actionHandlers.add(actionHandler); - // Assures the visual refresh. No need to reset the page buffer - // before as the content has not changed, only the action - // handlers. - refreshRenderedCells(); - } - - } - } - - /** - * Removes a previously registered action handler for the contents of this - * container. - * - * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler) - */ - - @Override - public void removeActionHandler(Action.Handler actionHandler) { - - if (actionHandlers != null && actionHandlers.contains(actionHandler)) { - - actionHandlers.remove(actionHandler); - - if (actionHandlers.isEmpty()) { - actionHandlers = null; - actionMapper = null; - } - - // Assures the visual refresh. No need to reset the page buffer - // before as the content has not changed, only the action - // handlers. - refreshRenderedCells(); - } - } - - /** - * Removes all action handlers - */ - public void removeAllActionHandlers() { - actionHandlers = null; - actionMapper = null; - // Assures the visual refresh. No need to reset the page buffer - // before as the content has not changed, only the action - // handlers. - refreshRenderedCells(); - } - - /* Property value change listening support */ - - /** - * Notifies this listener that the Property's value has changed. - * - * Also listens changes in rendered items to refresh content area. - * - * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent) - */ - - @Override - public void valueChange(Property.ValueChangeEvent event) { - if (equals(event.getProperty()) - || event.getProperty() == getPropertyDataSource()) { - super.valueChange(event); - } else { - refreshRowCache(); - containerChangeToBeRendered = true; - } - markAsDirty(); - } - - /** - * Clears the current page buffer. Call this before - * {@link #refreshRenderedCells()} to ensure that all content is updated - * from the properties. - */ - protected void resetPageBuffer() { - firstToBeRenderedInClient = -1; - lastToBeRenderedInClient = -1; - reqFirstRowToPaint = -1; - reqRowsToPaint = -1; - pageBuffer = null; - } - - /** - * Notifies the component that it is connected to an application. - * - * @see com.vaadin.ui.Component#attach() - */ - - @Override - public void attach() { - super.attach(); - - refreshRenderedCells(); - } - - /** - * Notifies the component that it is detached from the application - * - * @see com.vaadin.ui.Component#detach() - */ - - @Override - public void detach() { - super.detach(); - } - - /** - * Removes all Items from the Container. - * - * @see com.vaadin.data.Container#removeAllItems() - */ - - @Override - public boolean removeAllItems() { - currentPageFirstItemId = null; - currentPageFirstItemIndex = 0; - return super.removeAllItems(); - } - - /** - * Removes the Item identified by ItemId from the Container. - * - * @see com.vaadin.data.Container#removeItem(Object) - */ - - @Override - public boolean removeItem(Object itemId) { - final Object nextItemId = nextItemId(itemId); - final boolean ret = super.removeItem(itemId); - if (ret && (itemId != null) - && (itemId.equals(currentPageFirstItemId))) { - currentPageFirstItemId = nextItemId; - } - if (!(items instanceof Container.ItemSetChangeNotifier)) { - refreshRowCache(); - } - return ret; - } - - /** - * Removes a Property specified by the given Property ID from the Container. - * - * @see com.vaadin.data.Container#removeContainerProperty(Object) - */ - - @Override - public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { - - // If a visible property is removed, remove the corresponding column - visibleColumns.remove(propertyId); - columnAlignments.remove(propertyId); - columnIcons.remove(propertyId); - columnHeaders.remove(propertyId); - columnFooters.remove(propertyId); - // If a propertyValueConverter was defined for the property, remove it. - propertyValueConverters.remove(propertyId); - - return super.removeContainerProperty(propertyId); - } - - /** - * Adds a new property to the table and show it as a visible column. - * - * @param propertyId - * the Id of the property. - * @param type - * the class of the property. - * @param defaultValue - * the default value given for all existing items. - * @see com.vaadin.data.Container#addContainerProperty(Object, Class, - * Object) - */ - - @Override - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue) throws UnsupportedOperationException { - - boolean visibleColAdded = false; - if (!visibleColumns.contains(propertyId)) { - visibleColumns.add(propertyId); - visibleColAdded = true; - } - - if (!super.addContainerProperty(propertyId, type, defaultValue)) { - if (visibleColAdded) { - visibleColumns.remove(propertyId); - } - return false; - } - if (!(items instanceof Container.PropertySetChangeNotifier)) { - refreshRowCache(); - } - return true; - } - - /** - * Adds a new property to the table and show it as a visible column. - * - * @param propertyId - * the Id of the property - * @param type - * the class of the property - * @param defaultValue - * the default value given for all existing items - * @param columnHeader - * the Explicit header of the column. If explicit header is not - * needed, this should be set null. - * @param columnIcon - * the Icon of the column. If icon is not needed, this should be - * set null. - * @param columnAlignment - * the Alignment of the column. Null implies align left. - * @throws UnsupportedOperationException - * if the operation is not supported. - * @see com.vaadin.data.Container#addContainerProperty(Object, Class, - * Object) - */ - public boolean addContainerProperty(Object propertyId, Class type, - Object defaultValue, String columnHeader, Resource columnIcon, - Align columnAlignment) throws UnsupportedOperationException { - if (!this.addContainerProperty(propertyId, type, defaultValue)) { - return false; - } - setColumnAlignment(propertyId, columnAlignment); - setColumnHeader(propertyId, columnHeader); - setColumnIcon(propertyId, columnIcon); - return true; - } - - /** - * Adds a generated column to the Table. - *

    - * A generated column is a column that exists only in the Table, not as a - * property in the underlying Container. It shows up just as a regular - * column. - *

    - *

    - * A generated column will override a property with the same id, so that the - * generated column is shown instead of the column representing the - * property. Note that getContainerProperty() will still get the real - * property. - *

    - *

    - * Table will not listen to value change events from properties overridden - * by generated columns. If the content of your generated column depends on - * properties that are not directly visible in the table, attach value - * change listener to update the content on all depended properties. - * Otherwise your UI might not get updated as expected. - *

    - *

    - * Also note that getVisibleColumns() will return the generated columns, - * while getContainerPropertyIds() will not. - *

    - * - * @param id - * the id of the column to be added - * @param generatedColumn - * the {@link ColumnGenerator} to use for this column - */ - public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) { - if (generatedColumn == null) { - throw new IllegalArgumentException( - "Can not add null as a GeneratedColumn"); - } - if (columnGenerators.containsKey(id)) { - throw new IllegalArgumentException( - "Can not add the same GeneratedColumn twice, id:" + id); - } else { - columnGenerators.put(id, generatedColumn); - /* - * add to visible column list unless already there (overriding - * column from DS) - */ - if (!visibleColumns.contains(id)) { - visibleColumns.add(id); - } - refreshRowCache(); - } - } - - /** - * Returns the ColumnGenerator used to generate the given column. - * - * @param columnId - * The id of the generated column - * @return The ColumnGenerator used for the given columnId or null. - */ - public ColumnGenerator getColumnGenerator(Object columnId) - throws IllegalArgumentException { - return columnGenerators.get(columnId); - } - - /** - * Removes a generated column previously added with addGeneratedColumn. - * - * @param columnId - * id of the generated column to remove - * @return true if the column could be removed (existed in the Table) - */ - public boolean removeGeneratedColumn(Object columnId) { - if (columnGenerators.containsKey(columnId)) { - columnGenerators.remove(columnId); - // remove column from visibleColumns list unless it exists in - // container (generator previously overrode this column) - if (!items.getContainerPropertyIds().contains(columnId)) { - visibleColumns.remove(columnId); - } - refreshRowCache(); - return true; - } else { - return false; - } - } - - /** - * Returns item identifiers of the items which are currently rendered on the - * client. - *

    - * Note, that some due to historical reasons the name of the method is bit - * misleading. Some items may be partly or totally out of the viewport of - * the table's scrollable area. Actually detecting rows which can be - * actually seen by the end user may be problematic due to the client server - * architecture. Using {@link #getCurrentPageFirstItemId()} combined with - * {@link #getPageLength()} may produce good enough estimates in some - * situations. - * - * @see com.vaadin.ui.Select#getVisibleItemIds() - */ - - @Override - public Collection getVisibleItemIds() { - - final LinkedList visible = new LinkedList(); - - final Object[][] cells = getVisibleCells(); - // may be null if the table has not been rendered yet (e.g. not attached - // to a layout) - if (null != cells) { - for (int i = 0; i < cells[CELL_ITEMID].length; i++) { - visible.add(cells[CELL_ITEMID][i]); - } - } - - return visible; - } - - /** - * Container datasource item set change. Table must flush its buffers on - * change. - * - * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent) - */ - - @Override - public void containerItemSetChange(Container.ItemSetChangeEvent event) { - if (isBeingPainted) { - return; - } - - super.containerItemSetChange(event); - - // super method clears the key map, must inform client about this to - // avoid getting invalid keys back (#8584) - keyMapperReset = true; - - int currentFirstItemIndex = getCurrentPageFirstItemIndex(); - - if (event.getContainer().size() == 0) { - repairOnReAddAllRowsDataScrollPositionItemIndex = getCurrentPageFirstItemIndex(); - } else { - if (repairOnReAddAllRowsDataScrollPositionItemIndex != -1) { - currentFirstItemIndex = repairOnReAddAllRowsDataScrollPositionItemIndex; - /* - * Reset repairOnReAddAllRowsDataScrollPositionItemIndex. - * - * Next string should be commented (removed) if we want to have - * possibility to restore scroll position during adding items to - * container one by one via add() but not only addAll(). The - * problem in this case: we cannot track what happened between - * add() and add()... So it is ambiguous where to stop restore - * scroll position. - */ - repairOnReAddAllRowsDataScrollPositionItemIndex = -1; - } - } - - // ensure that page still has first item in page, ignore buffer refresh - // (forced in this method) - setCurrentPageFirstItemIndex(currentFirstItemIndex, false); - refreshRowCache(); - } - - /** - * Container datasource property set change. Table must flush its buffers on - * change. - * - * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent) - */ - - @Override - public void containerPropertySetChange( - Container.PropertySetChangeEvent event) { - if (isBeingPainted) { - return; - } - - disableContentRefreshing(); - super.containerPropertySetChange(event); - - // sanitize visibleColumns. note that we are not adding previously - // non-existing properties as columns - Collection containerPropertyIds = getContainerDataSource() - .getContainerPropertyIds(); - - LinkedList newVisibleColumns = new LinkedList( - visibleColumns); - for (Iterator iterator = newVisibleColumns.iterator(); iterator - .hasNext();) { - Object id = iterator.next(); - if (!(containerPropertyIds.contains(id) - || columnGenerators.containsKey(id))) { - iterator.remove(); - } - } - setVisibleColumns(newVisibleColumns.toArray()); - // same for collapsed columns - for (Iterator iterator = collapsedColumns.iterator(); iterator - .hasNext();) { - Object id = iterator.next(); - if (!(containerPropertyIds.contains(id) - || columnGenerators.containsKey(id))) { - iterator.remove(); - } - } - - resetPageBuffer(); - enableContentRefreshing(true); - } - - /** - * Adding new items is not supported. - * - * @throws UnsupportedOperationException - * if set to true. - * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean) - */ - - @Override - public void setNewItemsAllowed(boolean allowNewOptions) - throws UnsupportedOperationException { - if (allowNewOptions) { - throw new UnsupportedOperationException(); - } - } - - /** - * Gets the ID of the Item following the Item that corresponds to itemId. - * - * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object) - */ - - @Override - public Object nextItemId(Object itemId) { - return ((Container.Ordered) items).nextItemId(itemId); - } - - /** - * Gets the ID of the Item preceding the Item that corresponds to the - * itemId. - * - * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object) - */ - - @Override - public Object prevItemId(Object itemId) { - return ((Container.Ordered) items).prevItemId(itemId); - } - - /** - * Gets the ID of the first Item in the Container. - * - * @see com.vaadin.data.Container.Ordered#firstItemId() - */ - - @Override - public Object firstItemId() { - return ((Container.Ordered) items).firstItemId(); - } - - /** - * Gets the ID of the last Item in the Container. - * - * @see com.vaadin.data.Container.Ordered#lastItemId() - */ - - @Override - public Object lastItemId() { - return ((Container.Ordered) items).lastItemId(); - } - - /** - * Tests if the Item corresponding to the given Item ID is the first Item in - * the Container. - * - * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object) - */ - - @Override - public boolean isFirstId(Object itemId) { - return ((Container.Ordered) items).isFirstId(itemId); - } - - /** - * Tests if the Item corresponding to the given Item ID is the last Item in - * the Container. - * - * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object) - */ - - @Override - public boolean isLastId(Object itemId) { - return ((Container.Ordered) items).isLastId(itemId); - } - - /** - * Adds new item after the given item. - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object) - */ - - @Override - public Object addItemAfter(Object previousItemId) - throws UnsupportedOperationException { - Object itemId = ((Container.Ordered) items) - .addItemAfter(previousItemId); - if (!(items instanceof Container.ItemSetChangeNotifier)) { - refreshRowCache(); - } - return itemId; - } - - /** - * Adds new item after the given item. - * - * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object, - * java.lang.Object) - */ - - @Override - public Item addItemAfter(Object previousItemId, Object newItemId) - throws UnsupportedOperationException { - Item item = ((Container.Ordered) items).addItemAfter(previousItemId, - newItemId); - if (!(items instanceof Container.ItemSetChangeNotifier)) { - refreshRowCache(); - } - return item; - } - - /** - * Sets the TableFieldFactory that is used to create editor for table cells. - * - * The TableFieldFactory is only used if the Table is editable. By default - * the DefaultFieldFactory is used. - * - * @param fieldFactory - * the field factory to set. - * @see #isEditable - * @see DefaultFieldFactory - */ - public void setTableFieldFactory(TableFieldFactory fieldFactory) { - this.fieldFactory = fieldFactory; - - // Assure visual refresh - refreshRowCache(); - } - - /** - * Gets the TableFieldFactory that is used to create editor for table cells. - * - * The FieldFactory is only used if the Table is editable. - * - * @return TableFieldFactory used to create the LegacyField instances. - * @see #isEditable - */ - public TableFieldFactory getTableFieldFactory() { - return fieldFactory; - } - - /** - * Is table editable. - * - * If table is editable a editor of type LegacyField is created for each - * table cell. The assigned FieldFactory is used to create the instances. - * - * To provide custom editors for table cells create a class implementing the - * FieldFactory interface, and assign it to table, and set the editable - * property to true. - * - * @return true if table is editable, false otherwise. - * @see LegacyField - * @see FieldFactory - * - */ - public boolean isEditable() { - return editable; - } - - /** - * Sets the editable property. - * - * If table is editable a editor of type LegacyField is created for each - * table cell. The assigned FieldFactory is used to create the instances. - * - * To provide custom editors for table cells create a class implementing the - * FieldFactory interface, and assign it to table, and set the editable - * property to true. - * - * @param editable - * true if table should be editable by user. - * @see LegacyField - * @see FieldFactory - * - */ - public void setEditable(boolean editable) { - this.editable = editable; - - // Assure visual refresh - refreshRowCache(); - } - - /** - * Sorts the table. - * - * @throws UnsupportedOperationException - * if the container data source does not implement - * Container.Sortable - * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], - * boolean[]) - * - */ - - @Override - public void sort(Object[] propertyId, boolean[] ascending) - throws UnsupportedOperationException { - final Container c = getContainerDataSource(); - if (c instanceof Container.Sortable) { - final int pageIndex = getCurrentPageFirstItemIndex(); - boolean refreshingPreviouslyEnabled = disableContentRefreshing(); - ((Container.Sortable) c).sort(propertyId, ascending); - setCurrentPageFirstItemIndex(pageIndex); - if (refreshingPreviouslyEnabled) { - enableContentRefreshing(true); - } - if (propertyId.length > 0 && ascending.length > 0) { - // The first propertyId is the primary sorting criterion, - // therefore the sort indicator should be there - sortAscending = ascending[0]; - sortContainerPropertyId = propertyId[0]; - } else { - sortAscending = true; - sortContainerPropertyId = null; - } - } else if (c != null) { - throw new UnsupportedOperationException( - "Underlying Data does not allow sorting"); - } - } - - /** - * Sorts the table by currently selected sorting column. - * - * @throws UnsupportedOperationException - * if the container data source does not implement - * Container.Sortable - */ - public void sort() { - if (getSortContainerPropertyId() == null) { - return; - } - sort(new Object[] { sortContainerPropertyId }, - new boolean[] { sortAscending }); - } - - /** - * Gets the container property IDs, which can be used to sort the item. - *

    - * Note that the {@link #isSortEnabled()} state affects what this method - * returns. Disabling sorting causes this method to always return an empty - * collection. - *

    - * - * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds() - */ - - @Override - public Collection getSortableContainerPropertyIds() { - final Container c = getContainerDataSource(); - if (c instanceof Container.Sortable && isSortEnabled()) { - return ((Container.Sortable) c).getSortableContainerPropertyIds(); - } else { - return Collections.EMPTY_LIST; - } - } - - /** - * Gets the currently sorted column property ID. - * - * @return the Container property id of the currently sorted column. - */ - public Object getSortContainerPropertyId() { - return sortContainerPropertyId; - } - - /** - * Sets the currently sorted column property id. - * - * @param propertyId - * the Container property id of the currently sorted column. - */ - public void setSortContainerPropertyId(Object propertyId) { - setSortContainerPropertyId(propertyId, true); - } - - /** - * Internal method to set currently sorted column property id. With doSort - * flag actual sorting may be bypassed. - * - * @param propertyId - * @param doSort - */ - private void setSortContainerPropertyId(Object propertyId, boolean doSort) { - if ((sortContainerPropertyId != null - && !sortContainerPropertyId.equals(propertyId)) - || (sortContainerPropertyId == null && propertyId != null)) { - sortContainerPropertyId = propertyId; - - if (doSort) { - sort(); - // Assures the visual refresh. This should not be necessary as - // sort() calls refreshRowCache - refreshRenderedCells(); - } - } - } - - /** - * Is the table currently sorted in ascending order. - * - * @return true if ascending, false if descending. - */ - public boolean isSortAscending() { - return sortAscending; - } - - /** - * Sets the table in ascending order. - * - * @param ascending - * true if ascending, false if - * descending. - */ - public void setSortAscending(boolean ascending) { - setSortAscending(ascending, true); - } - - /** - * Internal method to set sort ascending. With doSort flag actual sort can - * be bypassed. - * - * @param ascending - * @param doSort - */ - private void setSortAscending(boolean ascending, boolean doSort) { - if (sortAscending != ascending) { - sortAscending = ascending; - if (doSort) { - sort(); - // Assures the visual refresh. This should not be necessary as - // sort() calls refreshRowCache - refreshRenderedCells(); - } - } - } - - /** - * Is sorting disabled altogether. - * - * True iff no sortable columns are given even in the case where data source - * would support this. - * - * @return True iff sorting is disabled. - * @deprecated As of 7.0, use {@link #isSortEnabled()} instead - */ - @Deprecated - public boolean isSortDisabled() { - return !isSortEnabled(); - } - - /** - * Checks if sorting is enabled. - * - * @return true if sorting by the user is allowed, false otherwise - */ - public boolean isSortEnabled() { - return sortEnabled; - } - - /** - * Disables the sorting by the user altogether. - * - * @param sortDisabled - * True iff sorting is disabled. - * @deprecated As of 7.0, use {@link #setSortEnabled(boolean)} instead - */ - @Deprecated - public void setSortDisabled(boolean sortDisabled) { - setSortEnabled(!sortDisabled); - } - - /** - * Enables or disables sorting. - *

    - * Setting this to false disallows sorting by the user. It is still possible - * to call {@link #sort()}. - *

    - * - * @param sortEnabled - * true to allow the user to sort the table, false to disallow it - */ - public void setSortEnabled(boolean sortEnabled) { - if (this.sortEnabled != sortEnabled) { - this.sortEnabled = sortEnabled; - markAsDirty(); - } - } - - /** - * Used to create "generated columns"; columns that exist only in the Table, - * not in the underlying Container. Implement this interface and pass it to - * Table.addGeneratedColumn along with an id for the column to be generated. - * - */ - public interface ColumnGenerator extends Serializable { - - /** - * Called by Table when a cell in a generated column needs to be - * generated. - * - * @param source - * the source Table - * @param itemId - * the itemId (aka rowId) for the of the cell to be generated - * @param columnId - * the id for the generated column (as specified in - * addGeneratedColumn) - * @return A {@link Component} that should be rendered in the cell or a - * {@link String} that should be displayed in the cell. Other - * return values are not supported. - */ - public abstract Object generateCell(Table source, Object itemId, - Object columnId); - } - - /** - * Set cell style generator for Table. - * - * @param cellStyleGenerator - * New cell style generator or null to remove generator. - */ - public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) { - this.cellStyleGenerator = cellStyleGenerator; - // Assures the visual refresh. No need to reset the page buffer - // before as the content has not changed, only the style generators - refreshRenderedCells(); - - } - - /** - * Get the current cell style generator. - * - */ - public CellStyleGenerator getCellStyleGenerator() { - return cellStyleGenerator; - } - - /** - * Allow to define specific style on cells (and rows) contents. Implements - * this interface and pass it to Table.setCellStyleGenerator. Row styles are - * generated when porpertyId is null. The CSS class name that will be added - * to the cell content is v-table-cell-content-[style name], and - * the row style will be v-table-row-[style name]. - */ - public interface CellStyleGenerator extends Serializable { - - /** - * Called by Table when a cell (and row) is painted. - * - * @param source - * the source Table - * @param itemId - * The itemId of the painted cell - * @param propertyId - * The propertyId of the cell, null when getting row style - * @return The style name to add to this cell or row. (the CSS class - * name will be v-table-cell-content-[style name], or - * v-table-row-[style name] for rows) - */ - public abstract String getStyle(Table source, Object itemId, - Object propertyId); - } - - @Override - public void addItemClickListener(ItemClickListener listener) { - addListener(TableConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, - listener, ItemClickEvent.ITEM_CLICK_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemClickListener(ItemClickListener)} - **/ - @Override - @Deprecated - public void addListener(ItemClickListener listener) { - addItemClickListener(listener); - } - - @Override - public void removeItemClickListener(ItemClickListener listener) { - removeListener(TableConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, - listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeItemClickListener(ItemClickListener)} - **/ - @Override - @Deprecated - public void removeListener(ItemClickListener listener) { - removeItemClickListener(listener); - } - - // Identical to AbstractCompoenentContainer.setEnabled(); - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - if (getParent() != null && !getParent().isEnabled()) { - // some ancestor still disabled, don't update children - return; - } else { - markAsDirtyRecursive(); - } - } - - /** - * Sets the drag start mode of the Table. Drag start mode controls how Table - * behaves as a drag source. - * - * @param newDragMode - */ - public void setDragMode(TableDragMode newDragMode) { - dragMode = newDragMode; - markAsDirty(); - } - - /** - * @return the current start mode of the Table. Drag start mode controls how - * Table behaves as a drag source. - */ - public TableDragMode getDragMode() { - return dragMode; - } - - /** - * Concrete implementation of {@link DataBoundTransferable} for data - * transferred from a table. - * - * @see {@link DataBoundTransferable}. - * - * @since 6.3 - */ - public class TableTransferable extends DataBoundTransferable { - - protected TableTransferable(Map rawVariables) { - super(Table.this, rawVariables); - Object object = rawVariables.get("itemId"); - if (object != null) { - setData("itemId", itemIdMapper.get((String) object)); - } - object = rawVariables.get("propertyId"); - if (object != null) { - setData("propertyId", columnIdMap.get((String) object)); - } - } - - @Override - public Object getItemId() { - return getData("itemId"); - } - - @Override - public Object getPropertyId() { - return getData("propertyId"); - } - - @Override - public Table getSourceComponent() { - return (Table) super.getSourceComponent(); - } - - } - - @Override - public TableTransferable getTransferable(Map rawVariables) { - TableTransferable transferable = new TableTransferable(rawVariables); - return transferable; - } - - @Override - public DropHandler getDropHandler() { - return dropHandler; - } - - public void setDropHandler(DropHandler dropHandler) { - this.dropHandler = dropHandler; - } - - @Override - public AbstractSelectTargetDetails translateDropTargetDetails( - Map clientVariables) { - return new AbstractSelectTargetDetails(clientVariables); - } - - /** - * Sets the behavior of how the multi-select mode should behave when the - * table is both selectable and in multi-select mode. - *

    - * Note, that on some clients the mode may not be respected. E.g. on touch - * based devices CTRL/SHIFT base selection method is invalid, so touch based - * browsers always use the {@link MultiSelectMode#SIMPLE}. - * - * @param mode - * The select mode of the table - */ - public void setMultiSelectMode(MultiSelectMode mode) { - multiSelectMode = mode; - markAsDirty(); - } - - /** - * Returns the select mode in which multi-select is used. - * - * @return The multi select mode - */ - public MultiSelectMode getMultiSelectMode() { - return multiSelectMode; - } - - /** - * Lazy loading accept criterion for Table. Accepted target rows are loaded - * from server once per drag and drop operation. Developer must override one - * method that decides on which rows the currently dragged data can be - * dropped. - * - *

    - * Initially pretty much no data is sent to client. On first required - * criterion check (per drag request) the client side data structure is - * initialized from server and no subsequent requests requests are needed - * during that drag and drop operation. - */ - public static abstract class TableDropCriterion - extends ServerSideCriterion { - - private Table table; - - private Set allowedItemIds; - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.acceptcriteria.ServerSideCriterion#getIdentifier - * () - */ - - @Override - protected String getIdentifier() { - return TableDropCriterion.class.getCanonicalName(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#accepts(com.vaadin - * .event.dd.DragAndDropEvent) - */ - @Override - @SuppressWarnings("unchecked") - public boolean accept(DragAndDropEvent dragEvent) { - AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent - .getTargetDetails(); - table = (Table) dragEvent.getTargetDetails().getTarget(); - Collection visibleItemIds = table.getVisibleItemIds(); - allowedItemIds = getAllowedItemIds(dragEvent, table, - (Collection) visibleItemIds); - - return allowedItemIds.contains(dropTargetData.getItemIdOver()); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#paintResponse( - * com.vaadin.server.PaintTarget) - */ - - @Override - public void paintResponse(PaintTarget target) throws PaintException { - /* - * send allowed nodes to client so subsequent requests can be - * avoided - */ - Object[] array = allowedItemIds.toArray(); - for (int i = 0; i < array.length; i++) { - String key = table.itemIdMapper.key(array[i]); - array[i] = key; - } - target.addAttribute("allowedIds", array); - } - - /** - * @param dragEvent - * @param table - * the table for which the allowed item identifiers are - * defined - * @param visibleItemIds - * the list of currently rendered item identifiers, accepted - * item id's need to be detected only for these visible items - * @return the set of identifiers for items on which the dragEvent will - * be accepted - */ - protected abstract Set getAllowedItemIds( - DragAndDropEvent dragEvent, Table table, - Collection visibleItemIds); - - } - - /** - * Click event fired when clicking on the Table headers. The event includes - * a reference the the Table the event originated from, the property id of - * the column which header was pressed and details about the mouse event - * itself. - */ - public static class HeaderClickEvent extends ClickEvent { - public static final Method HEADER_CLICK_METHOD; - - static { - try { - // Set the header click method - HEADER_CLICK_METHOD = HeaderClickListener.class - .getDeclaredMethod("headerClick", - new Class[] { HeaderClickEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException(e); - } - } - - // The property id of the column which header was pressed - private final Object columnPropertyId; - - public HeaderClickEvent(Component source, Object propertyId, - MouseEventDetails details) { - super(source, details); - columnPropertyId = propertyId; - } - - /** - * Gets the property id of the column which header was pressed - * - * @return The column property id - */ - public Object getPropertyId() { - return columnPropertyId; - } - } - - /** - * Click event fired when clicking on the Table footers. The event includes - * a reference the the Table the event originated from, the property id of - * the column which header was pressed and details about the mouse event - * itself. - */ - public static class FooterClickEvent extends ClickEvent { - public static final Method FOOTER_CLICK_METHOD; - - static { - try { - // Set the header click method - FOOTER_CLICK_METHOD = FooterClickListener.class - .getDeclaredMethod("footerClick", - new Class[] { FooterClickEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException(e); - } - } - - // The property id of the column which header was pressed - private final Object columnPropertyId; - - /** - * Constructor - * - * @param source - * The source of the component - * @param propertyId - * The propertyId of the column - * @param details - * The mouse details of the click - */ - public FooterClickEvent(Component source, Object propertyId, - MouseEventDetails details) { - super(source, details); - columnPropertyId = propertyId; - } - - /** - * Gets the property id of the column which header was pressed - * - * @return The column property id - */ - public Object getPropertyId() { - return columnPropertyId; - } - } - - /** - * Interface for the listener for column header mouse click events. The - * headerClick method is called when the user presses a header column cell. - */ - public interface HeaderClickListener extends Serializable { - - /** - * Called when a user clicks a header column cell - * - * @param event - * The event which contains information about the column and - * the mouse click event - */ - public void headerClick(HeaderClickEvent event); - } - - /** - * Interface for the listener for column footer mouse click events. The - * footerClick method is called when the user presses a footer column cell. - */ - public interface FooterClickListener extends Serializable { - - /** - * Called when a user clicks a footer column cell - * - * @param event - * The event which contains information about the column and - * the mouse click event - */ - public void footerClick(FooterClickEvent event); - } - - /** - * Adds a header click listener which handles the click events when the user - * clicks on a column header cell in the Table. - *

    - * The listener will receive events which contain information about which - * column was clicked and some details about the mouse event. - *

    - * - * @param listener - * The handler which should handle the header click events. - */ - public void addHeaderClickListener(HeaderClickListener listener) { - addListener(TableConstants.HEADER_CLICK_EVENT_ID, - HeaderClickEvent.class, listener, - HeaderClickEvent.HEADER_CLICK_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addHeaderClickListener(HeaderClickListener)} - **/ - @Deprecated - public void addListener(HeaderClickListener listener) { - addHeaderClickListener(listener); - } - - /** - * Removes a header click listener - * - * @param listener - * The listener to remove. - */ - public void removeHeaderClickListener(HeaderClickListener listener) { - removeListener(TableConstants.HEADER_CLICK_EVENT_ID, - HeaderClickEvent.class, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeHeaderClickListener(HeaderClickListener)} - **/ - @Deprecated - public void removeListener(HeaderClickListener listener) { - removeHeaderClickListener(listener); - } - - /** - * Adds a footer click listener which handles the click events when the user - * clicks on a column footer cell in the Table. - *

    - * The listener will receive events which contain information about which - * column was clicked and some details about the mouse event. - *

    - * - * @param listener - * The handler which should handle the footer click events. - */ - public void addFooterClickListener(FooterClickListener listener) { - addListener(TableConstants.FOOTER_CLICK_EVENT_ID, - FooterClickEvent.class, listener, - FooterClickEvent.FOOTER_CLICK_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addFooterClickListener(FooterClickListener)} - **/ - @Deprecated - public void addListener(FooterClickListener listener) { - addFooterClickListener(listener); - } - - /** - * Removes a footer click listener - * - * @param listener - * The listener to remove. - */ - public void removeFooterClickListener(FooterClickListener listener) { - removeListener(TableConstants.FOOTER_CLICK_EVENT_ID, - FooterClickEvent.class, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeFooterClickListener(FooterClickListener)} - **/ - @Deprecated - public void removeListener(FooterClickListener listener) { - removeFooterClickListener(listener); - } - - /** - * Gets the footer caption beneath the rows - * - * @param propertyId - * The propertyId of the column * - * @return The caption of the footer or NULL if not set - */ - public String getColumnFooter(Object propertyId) { - return columnFooters.get(propertyId); - } - - /** - * Sets the column footer caption. The column footer caption is the text - * displayed beneath the column if footers have been set visible. - * - * @param propertyId - * The properyId of the column - * - * @param footer - * The caption of the footer - */ - public void setColumnFooter(Object propertyId, String footer) { - if (footer == null) { - columnFooters.remove(propertyId); - } else { - columnFooters.put(propertyId, footer); - } - - markAsDirty(); - } - - /** - * Sets the footer visible in the bottom of the table. - *

    - * The footer can be used to add column related data like sums to the bottom - * of the Table using setColumnFooter(Object propertyId, String footer). - *

    - * - * @param visible - * Should the footer be visible - */ - public void setFooterVisible(boolean visible) { - if (visible != columnFootersVisible) { - columnFootersVisible = visible; - markAsDirty(); - } - } - - /** - * Is the footer currently visible? - * - * @return Returns true if visible else false - */ - public boolean isFooterVisible() { - return columnFootersVisible; - } - - /** - * This event is fired when a column is resized. The event contains the - * columns property id which was fired, the previous width of the column and - * the width of the column after the resize. - */ - public static class ColumnResizeEvent extends Component.Event { - public static final Method COLUMN_RESIZE_METHOD; - - static { - try { - COLUMN_RESIZE_METHOD = ColumnResizeListener.class - .getDeclaredMethod("columnResize", - new Class[] { ColumnResizeEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException(e); - } - } - - private final int previousWidth; - private final int currentWidth; - private final Object columnPropertyId; - - /** - * Constructor - * - * @param source - * The source of the event - * @param propertyId - * The columns property id - * @param previous - * The width in pixels of the column before the resize event - * @param current - * The width in pixels of the column after the resize event - */ - public ColumnResizeEvent(Component source, Object propertyId, - int previous, int current) { - super(source); - previousWidth = previous; - currentWidth = current; - columnPropertyId = propertyId; - } - - /** - * Get the column property id of the column that was resized. - * - * @return The column property id - */ - public Object getPropertyId() { - return columnPropertyId; - } - - /** - * Get the width in pixels of the column before the resize event - * - * @return Width in pixels - */ - public int getPreviousWidth() { - return previousWidth; - } - - /** - * Get the width in pixels of the column after the resize event - * - * @return Width in pixels - */ - public int getCurrentWidth() { - return currentWidth; - } - } - - /** - * Interface for listening to column resize events. - */ - public interface ColumnResizeListener extends Serializable { - - /** - * This method is triggered when the column has been resized - * - * @param event - * The event which contains the column property id, the - * previous width of the column and the current width of the - * column - */ - public void columnResize(ColumnResizeEvent event); - } - - /** - * Adds a column resize listener to the Table. A column resize listener is - * called when a user resizes a columns width. - * - * @param listener - * The listener to attach to the Table - */ - public void addColumnResizeListener(ColumnResizeListener listener) { - addListener(TableConstants.COLUMN_RESIZE_EVENT_ID, - ColumnResizeEvent.class, listener, - ColumnResizeEvent.COLUMN_RESIZE_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addColumnResizeListener(ColumnResizeListener)} - **/ - @Deprecated - public void addListener(ColumnResizeListener listener) { - addColumnResizeListener(listener); - } - - /** - * Removes a column resize listener from the Table. - * - * @param listener - * The listener to remove - */ - public void removeColumnResizeListener(ColumnResizeListener listener) { - removeListener(TableConstants.COLUMN_RESIZE_EVENT_ID, - ColumnResizeEvent.class, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeColumnResizeListener(ColumnResizeListener)} - **/ - @Deprecated - public void removeListener(ColumnResizeListener listener) { - removeColumnResizeListener(listener); - } - - /** - * This event is fired when a columns are reordered by the end user user. - */ - public static class ColumnReorderEvent extends Component.Event { - public static final Method METHOD; - - static { - try { - METHOD = ColumnReorderListener.class.getDeclaredMethod( - "columnReorder", - new Class[] { ColumnReorderEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException(e); - } - } - - /** - * Constructor - * - * @param source - * The source of the event - */ - public ColumnReorderEvent(Component source) { - super(source); - } - - } - - /** - * Interface for listening to column reorder events. - */ - public interface ColumnReorderListener extends Serializable { - - /** - * This method is triggered when the column has been reordered - * - * @param event - */ - public void columnReorder(ColumnReorderEvent event); - } - - /** - * This event is fired when the collapse state of a column changes. - * - * @since 7.6 - */ - public static class ColumnCollapseEvent extends Component.Event { - - public static final Method METHOD = ReflectTools.findMethod( - ColumnCollapseListener.class, "columnCollapseStateChange", - ColumnCollapseEvent.class); - private Object propertyId; - - /** - * Constructor - * - * @param source - * The source of the event - * @param propertyId - * The id of the column - */ - public ColumnCollapseEvent(Component source, Object propertyId) { - super(source); - this.propertyId = propertyId; - } - - /** - * Gets the id of the column whose collapse state changed - * - * @return the property id of the column - */ - public Object getPropertyId() { - return propertyId; - } - } - - /** - * Interface for listening to column collapse events. - * - * @since 7.6 - */ - public interface ColumnCollapseListener extends Serializable { - - /** - * This method is triggered when the collapse state for a column has - * changed - * - * @param event - */ - public void columnCollapseStateChange(ColumnCollapseEvent event); - } - - /** - * Adds a column reorder listener to the Table. A column reorder listener is - * called when a user reorders columns. - * - * @param listener - * The listener to attach to the Table - */ - public void addColumnReorderListener(ColumnReorderListener listener) { - addListener(TableConstants.COLUMN_REORDER_EVENT_ID, - ColumnReorderEvent.class, listener, ColumnReorderEvent.METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addColumnReorderListener(ColumnReorderListener)} - **/ - @Deprecated - public void addListener(ColumnReorderListener listener) { - addColumnReorderListener(listener); - } - - /** - * Removes a column reorder listener from the Table. - * - * @param listener - * The listener to remove - */ - public void removeColumnReorderListener(ColumnReorderListener listener) { - removeListener(TableConstants.COLUMN_REORDER_EVENT_ID, - ColumnReorderEvent.class, listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeColumnReorderListener(ColumnReorderListener)} - **/ - @Deprecated - public void removeListener(ColumnReorderListener listener) { - removeColumnReorderListener(listener); - } - - /** - * Adds a column collapse listener to the Table. A column collapse listener - * is called when the collapsed state of a column changes. - * - * @since 7.6 - * - * @param listener - * The listener to attach - */ - public void addColumnCollapseListener(ColumnCollapseListener listener) { - addListener(TableConstants.COLUMN_COLLAPSE_EVENT_ID, - ColumnCollapseEvent.class, listener, - ColumnCollapseEvent.METHOD); - } - - /** - * Removes a column reorder listener from the Table. - * - * @since 7.6 - * @param listener - * The listener to remove - */ - public void removeColumnCollapseListener(ColumnCollapseListener listener) { - removeListener(TableConstants.COLUMN_COLLAPSE_EVENT_ID, - ColumnCollapseEvent.class, listener); - } - - /** - * Set the item description generator which generates tooltips for cells and - * rows in the Table - * - * @param generator - * The generator to use or null to disable - */ - public void setItemDescriptionGenerator( - ItemDescriptionGenerator generator) { - if (generator != itemDescriptionGenerator) { - itemDescriptionGenerator = generator; - // Assures the visual refresh. No need to reset the page buffer - // before as the content has not changed, only the descriptions - refreshRenderedCells(); - } - } - - /** - * Get the item description generator which generates tooltips for cells and - * rows in the Table. - */ - public ItemDescriptionGenerator getItemDescriptionGenerator() { - return itemDescriptionGenerator; - } - - /** - * Row generators can be used to replace certain items in a table with a - * generated string. The generator is called each time the table is - * rendered, which means that new strings can be generated each time. - * - * Row generators can be used for e.g. summary rows or grouping of items. - */ - public interface RowGenerator extends Serializable { - /** - * Called for every row that is painted in the Table. Returning a - * GeneratedRow object will cause the row to be painted based on the - * contents of the GeneratedRow. A generated row is by default styled - * similarly to a header or footer row. - *

    - * The GeneratedRow data object contains the text that should be - * rendered in the row. The itemId in the container thus works only as a - * placeholder. - *

    - * If GeneratedRow.setSpanColumns(true) is used, there will be one - * String spanning all columns (use setText("Spanning text")). Otherwise - * you can define one String per visible column. - *

    - * If GeneratedRow.setRenderAsHtml(true) is used, the strings can - * contain HTML markup, otherwise all strings will be rendered as text - * (the default). - *

    - * A "v-table-generated-row" CSS class is added to all generated rows. - * For custom styling of a generated row you can combine a RowGenerator - * with a CellStyleGenerator. - *

    - * - * @param table - * The Table that is being painted - * @param itemId - * The itemId for the row - * @return A GeneratedRow describing how the row should be painted or - * null to paint the row with the contents from the container - */ - public GeneratedRow generateRow(Table table, Object itemId); - } - - public static class GeneratedRow implements Serializable { - private boolean htmlContentAllowed = false; - private boolean spanColumns = false; - private String[] text = null; - - /** - * Creates a new generated row. If only one string is passed in, columns - * are automatically spanned. - * - * @param text - */ - public GeneratedRow(String... text) { - setHtmlContentAllowed(false); - setSpanColumns(text == null || text.length == 1); - setText(text); - } - - /** - * Pass one String if spanColumns is used, one String for each visible - * column otherwise - */ - public void setText(String... text) { - if (text == null || (text.length == 1 && text[0] == null)) { - text = new String[] { "" }; - } - this.text = text; - } - - protected String[] getText() { - return text; - } - - protected Object getValue() { - return getText(); - } - - protected boolean isHtmlContentAllowed() { - return htmlContentAllowed; - } - - /** - * If set to true, all strings passed to {@link #setText(String...)} - * will be rendered as HTML. - * - * @param htmlContentAllowed - */ - public void setHtmlContentAllowed(boolean htmlContentAllowed) { - this.htmlContentAllowed = htmlContentAllowed; - } - - protected boolean isSpanColumns() { - return spanColumns; - } - - /** - * If set to true, only one string will be rendered, spanning the entire - * row. - * - * @param spanColumns - */ - public void setSpanColumns(boolean spanColumns) { - this.spanColumns = spanColumns; - } - } - - /** - * Assigns a row generator to the table. The row generator will be able to - * replace rows in the table when it is rendered. - * - * @param generator - * the new row generator - */ - public void setRowGenerator(RowGenerator generator) { - rowGenerator = generator; - refreshRowCache(); - } - - /** - * @return the current row generator - */ - public RowGenerator getRowGenerator() { - return rowGenerator; - } - - /** - * Sets a converter for a property id. - *

    - * The converter is used to format the the data for the given property id - * before displaying it in the table. - *

    - * - * @param propertyId - * The propertyId to format using the converter - * @param converter - * The converter to use for the property id - */ - public void setConverter(Object propertyId, - LegacyConverter converter) { - if (!getContainerPropertyIds().contains(propertyId)) { - throw new IllegalArgumentException( - "PropertyId " + propertyId + " must be in the container"); - } - - if (!typeIsCompatible(converter.getModelType(), getType(propertyId))) { - throw new IllegalArgumentException( - "Property type (" + getType(propertyId) - + ") must match converter source type (" - + converter.getModelType() + ")"); - } - propertyValueConverters.put(propertyId, - (LegacyConverter) converter); - refreshRowCache(); - } - - /** - * Checks if there is a converter set explicitly for the given property id. - * - * @param propertyId - * The propertyId to check - * @return true if a converter has been set for the property id, false - * otherwise - */ - protected boolean hasConverter(Object propertyId) { - return propertyValueConverters.containsKey(propertyId); - } - - /** - * Returns the converter used to format the given propertyId. - * - * @param propertyId - * The propertyId to check - * @return The converter used to format the propertyId or null if no - * converter has been set - */ - public LegacyConverter getConverter(Object propertyId) { - return propertyValueConverters.get(propertyId); - } - - @Override - public void setVisible(boolean visible) { - if (visible) { - // We need to ensure that the rows are sent to the client when the - // Table is made visible if it has been rendered as invisible. - setRowCacheInvalidated(true); - } - super.setVisible(visible); - } - - @Override - public Iterator iterator() { - if (visibleComponents == null) { - Collection empty = Collections.emptyList(); - return empty.iterator(); - } - return visibleComponents.iterator(); - } - - /** - * @deprecated As of 7.0, use {@link #iterator()} instead. - */ - @Deprecated - public Iterator getComponentIterator() { - return iterator(); - } - - @Override - public void readDesign(Element design, DesignContext context) { - super.readDesign(design, context); - - if (design.hasAttr("sortable")) { - setSortEnabled(DesignAttributeHandler.readAttribute("sortable", - design.attributes(), boolean.class)); - } - - readColumns(design); - readHeader(design); - readBody(design, context); - readFooter(design); - } - - private void readColumns(Element design) { - Element colgroup = design.select("> table > colgroup").first(); - - if (colgroup != null) { - int i = 0; - List pIds = new ArrayList(); - for (Element col : colgroup.children()) { - if (!col.tagName().equals("col")) { - throw new DesignException("invalid column"); - } - - String id = DesignAttributeHandler.readAttribute("property-id", - col.attributes(), "property-" + i++, String.class); - pIds.add(id); - - addContainerProperty(id, String.class, null); - - if (col.hasAttr("width")) { - setColumnWidth(id, DesignAttributeHandler.readAttribute( - "width", col.attributes(), Integer.class)); - } - if (col.hasAttr("center")) { - setColumnAlignment(id, Align.CENTER); - } else if (col.hasAttr("right")) { - setColumnAlignment(id, Align.RIGHT); - } - if (col.hasAttr("expand")) { - if (col.attr("expand").isEmpty()) { - setColumnExpandRatio(id, 1); - } else { - setColumnExpandRatio(id, - DesignAttributeHandler.readAttribute("expand", - col.attributes(), float.class)); - } - } - if (col.hasAttr("collapsible")) { - setColumnCollapsible(id, - DesignAttributeHandler.readAttribute("collapsible", - col.attributes(), boolean.class)); - } - if (col.hasAttr("collapsed")) { - setColumnCollapsed(id, DesignAttributeHandler.readAttribute( - "collapsed", col.attributes(), boolean.class)); - } - } - setVisibleColumns(pIds.toArray()); - } - } - - private void readFooter(Element design) { - readHeaderOrFooter(design, false); - } - - private void readHeader(Element design) { - readHeaderOrFooter(design, true); - } - - @Override - protected void readItems(Element design, DesignContext context) { - // Do nothing - header/footer and inline data must be handled after - // colgroup. - } - - private void readHeaderOrFooter(Element design, boolean header) { - String selector = header ? "> table > thead" : "> table > tfoot"; - Element elem = design.select(selector).first(); - if (elem != null) { - if (!header) { - setFooterVisible(true); - } - if (elem.children().size() != 1) { - throw new DesignException( - "Table header and footer should contain exactly one element"); - } - Element tr = elem.child(0); - Elements elems = tr.children(); - Collection propertyIds = visibleColumns; - if (elems.size() != propertyIds.size()) { - throw new DesignException( - "Table header and footer should contain as many items as there" - + " are columns in the Table."); - } - Iterator propertyIt = propertyIds.iterator(); - for (Element e : elems) { - String columnValue = DesignFormatter - .decodeFromTextNode(e.html()); - Object propertyId = propertyIt.next(); - if (header) { - setColumnHeader(propertyId, columnValue); - if (e.hasAttr("icon")) { - setColumnIcon(propertyId, - DesignAttributeHandler.readAttribute("icon", - e.attributes(), Resource.class)); - } - } else { - setColumnFooter(propertyId, columnValue); - } - } - } - } - - protected void readBody(Element design, DesignContext context) { - Element tbody = design.select("> table > tbody").first(); - if (tbody == null) { - return; - } - - Set selected = new HashSet(); - for (Element tr : tbody.children()) { - readItem(tr, selected, context); - } - } - - @Override - protected Object readItem(Element tr, Set selected, - DesignContext context) { - Elements cells = tr.children(); - if (visibleColumns.size() != cells.size()) { - throw new DesignException( - "Wrong number of columns in a Table row. Expected " - + visibleColumns.size() + ", was " + cells.size() - + "."); - } - Object[] data = new String[cells.size()]; - for (int c = 0; c < cells.size(); ++c) { - data[c] = DesignFormatter.decodeFromTextNode(cells.get(c).html()); - } - - Object itemId = addItem(data, - tr.hasAttr("item-id") ? tr.attr("item-id") : null); - - if (itemId == null) { - throw new DesignException("Failed to add a Table row: " + data); - } - - return itemId; - } - - @Override - public void writeDesign(Element design, DesignContext context) { - Table def = context.getDefaultInstance(this); - - DesignAttributeHandler.writeAttribute("sortable", design.attributes(), - isSortEnabled(), def.isSortEnabled(), boolean.class); - - Element table = null; - boolean hasColumns = getVisibleColumns().length != 0; - if (hasColumns) { - table = design.appendElement("table"); - writeColumns(table, def); - writeHeader(table, def); - } - super.writeDesign(design, context); - if (hasColumns) { - writeFooter(table); - } - } - - private void writeColumns(Element table, Table def) { - Object[] columns = getVisibleColumns(); - if (columns.length == 0) { - return; - } - - Element colgroup = table.appendElement("colgroup"); - for (Object id : columns) { - Element col = colgroup.appendElement("col"); - - col.attr("property-id", id.toString()); - - if (getColumnAlignment(id) == Align.CENTER) { - col.attr("center", true); - } else if (getColumnAlignment(id) == Align.RIGHT) { - col.attr("right", true); - } - - DesignAttributeHandler.writeAttribute("width", col.attributes(), - getColumnWidth(id), def.getColumnWidth(null), int.class); - - DesignAttributeHandler.writeAttribute("expand", col.attributes(), - getColumnExpandRatio(id), def.getColumnExpandRatio(null), - float.class); - - DesignAttributeHandler.writeAttribute("collapsible", - col.attributes(), isColumnCollapsible(id), - def.isColumnCollapsible(null), boolean.class); - - DesignAttributeHandler.writeAttribute("collapsed", col.attributes(), - isColumnCollapsed(id), def.isColumnCollapsed(null), - boolean.class); - } - } - - private void writeHeader(Element table, Table def) { - Object[] columns = getVisibleColumns(); - if (columns.length == 0 - || (columnIcons.isEmpty() && columnHeaders.isEmpty())) { - return; - } - - Element header = table.appendElement("thead").appendElement("tr"); - for (Object id : columns) { - Element th = header.appendElement("th"); - th.html(getColumnHeader(id)); - DesignAttributeHandler.writeAttribute("icon", th.attributes(), - getColumnIcon(id), def.getColumnIcon(null), Resource.class); - } - - } - - private void writeFooter(Element table) { - Object[] columns = getVisibleColumns(); - if (columns.length == 0 || columnFooters.isEmpty()) { - return; - } - - Element footer = table.appendElement("tfoot").appendElement("tr"); - for (Object id : columns) { - footer.appendElement("td").text(getColumnFooter(id)); - } - } - - @Override - protected void writeItems(Element design, DesignContext context) { - if (getVisibleColumns().length == 0) { - return; - } - Element tbody = design.child(0).appendElement("tbody"); - super.writeItems(tbody, context); - } - - @Override - protected Element writeItem(Element tbody, Object itemId, - DesignContext context) { - Element tr = tbody.appendElement("tr"); - tr.attr("item-id", String.valueOf(itemId)); - Item item = getItem(itemId); - for (Object id : getVisibleColumns()) { - Element td = tr.appendElement("td"); - Object value = item.getItemProperty(id).getValue(); - td.html(value != null ? value.toString() : ""); - } - return tr; - } - - @Override - protected Collection getCustomAttributes() { - Collection result = super.getCustomAttributes(); - result.add("sortable"); - result.add("sort-enabled"); - result.add("sort-disabled"); - result.add("footer-visible"); - result.add("item-caption-mode"); - result.add("current-page-first-item-id"); - result.add("current-page-first-item-index"); - return result; - } - - /** - * ContextClickEvent for the Table Component. - * - * @since 7.6 - */ - public static class TableContextClickEvent extends ContextClickEvent { - - private final Object itemId; - private final Object propertyId; - private final Section section; - - public TableContextClickEvent(Table source, - MouseEventDetails mouseEventDetails, Object itemId, - Object propertyId, Section section) { - super(source, mouseEventDetails); - - this.itemId = itemId; - this.propertyId = propertyId; - this.section = section; - } - - /** - * Returns the item id of context clicked row. - * - * @return item id of clicked row; null if header, footer - * or empty area of Table - */ - public Object getItemId() { - return itemId; - } - - /** - * Returns the property id of context clicked column. - * - * @return property id; or null if we've clicked on the - * empty area of the Table - */ - public Object getPropertyId() { - return propertyId; - } - - /** - * Returns the clicked section of Table. - * - * @return section of Table - */ - public Section getSection() { - return section; - } - - @Override - public Table getComponent() { - return (Table) super.getComponent(); - } - } - - @Override - protected TableState getState() { - return getState(true); - } - - @Override - protected TableState getState(boolean markAsDirty) { - return (TableState) super.getState(markAsDirty); - } - - private final Logger getLogger() { - if (logger == null) { - logger = Logger.getLogger(Table.class.getName()); - } - return logger; - } - - @Override - public void setChildMeasurementHint(ChildMeasurementHint hint) { - if (hint == null) { - childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; - } else { - childMeasurementHint = hint; - } - } - - @Override - public ChildMeasurementHint getChildMeasurementHint() { - return childMeasurementHint; - } - - /** - * Sets whether only collapsible columns should be shown to the user in the - * column collapse menu. The default is - * {@link CollapseMenuContent#ALL_COLUMNS}. - * - * - * @since 7.6 - * @param content - * the desired collapsible menu content setting - */ - public void setCollapseMenuContent(CollapseMenuContent content) { - getState().collapseMenuContent = content; - } - - /** - * Checks whether only collapsible columns are shown to the user in the - * column collapse menu. The default is - * {@link CollapseMenuContent#ALL_COLUMNS} . - * - * @since 7.6 - * @return the current collapsible menu content setting - */ - public CollapseMenuContent getCollapseMenuContent() { - return getState(false).collapseMenuContent; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/TableFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/ui/TableFieldFactory.java deleted file mode 100644 index 54f8bc5110..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/TableFieldFactory.java +++ /dev/null @@ -1,56 +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 com.vaadin.data.Container; -import com.vaadin.v7.ui.LegacyField; - -/** - * Factory interface for creating new LegacyField-instances based on Container - * (datasource), item id, property id and uiContext (the component responsible - * for displaying fields). Currently this interface is used by {@link Table}, - * but might later be used by some other components for {@link LegacyField} - * generation. - * - *

    - * - * @author Vaadin Ltd. - * @since 6.0 - * @see FormFieldFactory - */ -public interface TableFieldFactory extends Serializable { - /** - * Creates a field based on the Container, item id, property id and the - * component responsible for displaying the field (most commonly - * {@link Table}). - * - * @param container - * the Container where the property belongs to. - * @param itemId - * the item Id. - * @param propertyId - * the Id of the property. - * @param uiContext - * the component where the field is presented. - * @return A field suitable for editing the specified data or null if the - * property should not be editable. - */ - LegacyField createField(Container container, Object itemId, - Object propertyId, Component uiContext); - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/TextArea.java b/compatibility-server/src/main/java/com/vaadin/ui/TextArea.java deleted file mode 100644 index 056667a696..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/TextArea.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.ui; - -import org.jsoup.nodes.Element; - -import com.vaadin.data.Property; -import com.vaadin.shared.ui.textarea.TextAreaState; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.ui.declarative.DesignFormatter; -import com.vaadin.v7.ui.LegacyAbstractTextField; - -/** - * A text field that supports multi line editing. - */ -public class TextArea extends LegacyAbstractTextField { - - /** - * Constructs an empty TextArea. - */ - public TextArea() { - setValue(""); - } - - /** - * Constructs an empty TextArea with given caption. - * - * @param caption - * the caption for the field. - */ - public TextArea(String caption) { - this(); - setCaption(caption); - } - - /** - * Constructs a TextArea with given property data source. - * - * @param dataSource - * the data source for the field - */ - public TextArea(Property dataSource) { - this(); - setPropertyDataSource(dataSource); - } - - /** - * Constructs a TextArea with given caption and property data source. - * - * @param caption - * the caption for the field - * @param dataSource - * the data source for the field - */ - public TextArea(String caption, Property dataSource) { - this(dataSource); - setCaption(caption); - } - - /** - * Constructs a TextArea with given caption and value. - * - * @param caption - * the caption for the field - * @param value - * the value for the field - */ - public TextArea(String caption, String value) { - this(caption); - setValue(value); - - } - - @Override - protected TextAreaState getState() { - return (TextAreaState) super.getState(); - } - - @Override - protected TextAreaState getState(boolean markAsDirty) { - return (TextAreaState) super.getState(markAsDirty); - } - - /** - * Sets the number of rows in the text area. - * - * @param rows - * the number of rows for this text area. - */ - public void setRows(int rows) { - if (rows < 0) { - rows = 0; - } - getState().rows = rows; - } - - /** - * Gets the number of rows in the text area. - * - * @return number of explicitly set rows. - */ - public int getRows() { - return getState(false).rows; - } - - /** - * Sets the text area's word-wrap mode on or off. - * - * @param wordwrap - * the boolean value specifying if the text area should be in - * word-wrap mode. - */ - public void setWordwrap(boolean wordwrap) { - getState().wordwrap = wordwrap; - } - - /** - * Tests if the text area is in word-wrap mode. - * - * @return true if the component is in word-wrap mode, - * false if not. - */ - public boolean isWordwrap() { - return getState(false).wordwrap; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.AbstractField#readDesign(org.jsoup.nodes.Element , - * com.vaadin.ui.declarative.DesignContext) - */ - @Override - public void readDesign(Element design, DesignContext designContext) { - super.readDesign(design, designContext); - setValue(DesignFormatter.decodeFromTextNode(design.html()), false, - true); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.AbstractTextField#writeDesign(org.jsoup.nodes.Element - * , com.vaadin.ui.declarative.DesignContext) - */ - @Override - public void writeDesign(Element design, DesignContext designContext) { - super.writeDesign(design, designContext); - design.html(DesignFormatter.encodeForTextNode(getValue())); - } - - @Override - public void clear() { - setValue(""); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/Tree.java b/compatibility-server/src/main/java/com/vaadin/ui/Tree.java deleted file mode 100644 index 5da499f94e..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/Tree.java +++ /dev/null @@ -1,1984 +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.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.StringTokenizer; - -import org.jsoup.nodes.Element; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.util.ContainerHierarchicalWrapper; -import com.vaadin.data.util.HierarchicalContainer; -import com.vaadin.event.Action; -import com.vaadin.event.Action.Handler; -import com.vaadin.event.ContextClickEvent; -import com.vaadin.event.DataBoundTransferable; -import com.vaadin.event.ItemClickEvent; -import com.vaadin.event.ItemClickEvent.ItemClickListener; -import com.vaadin.event.ItemClickEvent.ItemClickNotifier; -import com.vaadin.event.Transferable; -import com.vaadin.event.dd.DragAndDropEvent; -import com.vaadin.event.dd.DragSource; -import com.vaadin.event.dd.DropHandler; -import com.vaadin.event.dd.DropTarget; -import com.vaadin.event.dd.TargetDetails; -import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion; -import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; -import com.vaadin.event.dd.acceptcriteria.TargetDetailIs; -import com.vaadin.server.KeyMapper; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.server.Resource; -import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.ui.MultiSelectMode; -import com.vaadin.shared.ui.dd.VerticalDropLocation; -import com.vaadin.shared.ui.tree.TreeConstants; -import com.vaadin.shared.ui.tree.TreeServerRpc; -import com.vaadin.shared.ui.tree.TreeState; -import com.vaadin.ui.declarative.DesignAttributeHandler; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.ui.declarative.DesignException; -import com.vaadin.util.ReflectTools; - -/** - * Tree component. A Tree can be used to select an item (or multiple items) from - * a hierarchical set of items. - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings({ "serial", "deprecation" }) -public class Tree extends AbstractSelect implements Container.Hierarchical, - Action.Container, ItemClickNotifier, DragSource, DropTarget { - - /** - * ContextClickEvent for the Tree Component. - * - * @since 7.6 - */ - public static class TreeContextClickEvent extends ContextClickEvent { - - private final Object itemId; - - public TreeContextClickEvent(Tree source, Object itemId, - MouseEventDetails mouseEventDetails) { - super(source, mouseEventDetails); - this.itemId = itemId; - } - - @Override - public Tree getComponent() { - return (Tree) super.getComponent(); - } - - /** - * Returns the item id of context clicked row. - * - * @return item id of clicked row; null if no row is - * present at the location - */ - public Object getItemId() { - return itemId; - } - } - - /* Private members */ - - private static final String NULL_ALT_EXCEPTION_MESSAGE = "Parameter 'altText' needs to be non null"; - - /** - * Item icons alt texts. - */ - private final HashMap itemIconAlts = new HashMap(); - - /** - * Set of expanded nodes. - */ - private HashSet expanded = new HashSet(); - - /** - * List of action handlers. - */ - private LinkedList actionHandlers = null; - - /** - * Action mapper. - */ - private KeyMapper actionMapper = null; - - /** - * Is the tree selectable on the client side. - */ - private boolean selectable = true; - - /** - * Flag to indicate sub-tree loading - */ - private boolean partialUpdate = false; - - /** - * Holds a itemId which was recently expanded - */ - private Object expandedItemId; - - /** - * a flag which indicates initial paint. After this flag set true partial - * updates are allowed. - */ - private boolean initialPaint = true; - - /** - * Item tooltip generator - */ - private ItemDescriptionGenerator itemDescriptionGenerator; - - /** - * Supported drag modes for Tree. - */ - public enum TreeDragMode { - /** - * When drag mode is NONE, dragging from Tree is not supported. Browsers - * may still support selecting text/icons from Tree which can initiate - * HTML 5 style drag and drop operation. - */ - NONE, - /** - * When drag mode is NODE, users can initiate drag from Tree nodes that - * represent {@link Item}s in from the backed {@link Container}. - */ - NODE - // , SUBTREE - } - - private TreeDragMode dragMode = TreeDragMode.NONE; - - private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; - - /* Tree constructors */ - - /** - * Creates a new empty tree. - */ - public Tree() { - this(null); - - registerRpc(new TreeServerRpc() { - @Override - public void contextClick(String rowKey, MouseEventDetails details) { - fireEvent(new TreeContextClickEvent(Tree.this, - itemIdMapper.get(rowKey), details)); - } - }); - } - - /** - * Creates a new empty tree with caption. - * - * @param caption - */ - public Tree(String caption) { - this(caption, new HierarchicalContainer()); - } - - /** - * Creates a new tree with caption and connect it to a Container. - * - * @param caption - * @param dataSource - */ - public Tree(String caption, Container dataSource) { - super(caption, dataSource); - } - - @Override - public void setItemIcon(Object itemId, Resource icon) { - setItemIcon(itemId, icon, ""); - } - - /** - * Sets the icon for an item. - * - * @param itemId - * the id of the item to be assigned an icon. - * @param icon - * the icon to use or null. - * - * @param altText - * the alternative text for the icon - */ - public void setItemIcon(Object itemId, Resource icon, String altText) { - if (itemId != null) { - super.setItemIcon(itemId, icon); - - if (icon == null) { - itemIconAlts.remove(itemId); - } else if (altText == null) { - throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE); - } else { - itemIconAlts.put(itemId, altText); - } - markAsDirty(); - } - } - - /** - * Set the alternate text for an item. - * - * Used when the item has an icon. - * - * @param itemId - * the id of the item to be assigned an icon. - * @param altText - * the alternative text for the icon - */ - public void setItemIconAlternateText(Object itemId, String altText) { - if (itemId != null) { - if (altText == null) { - throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE); - } else { - itemIconAlts.put(itemId, altText); - } - } - } - - /** - * Return the alternate text of an icon in a tree item. - * - * @param itemId - * Object with the ID of the item - * @return String with the alternate text of the icon, or null when no icon - * was set - */ - public String getItemIconAlternateText(Object itemId) { - String storedAlt = itemIconAlts.get(itemId); - return storedAlt == null ? "" : storedAlt; - } - - /* Expanding and collapsing */ - - /** - * Check is an item is expanded - * - * @param itemId - * the item id. - * @return true iff the item is expanded. - */ - public boolean isExpanded(Object itemId) { - return expanded.contains(itemId); - } - - /** - * Expands an item. - * - * @param itemId - * the item id. - * @return True iff the expand operation succeeded - */ - public boolean expandItem(Object itemId) { - boolean success = expandItem(itemId, true); - markAsDirty(); - return success; - } - - /** - * Expands an item. - * - * @param itemId - * the item id. - * @param sendChildTree - * flag to indicate if client needs subtree or not (may be - * cached) - * @return True if the expand operation succeeded - */ - private boolean expandItem(Object itemId, boolean sendChildTree) { - - // Succeeds if the node is already expanded - if (isExpanded(itemId)) { - return true; - } - - // Nodes that can not have children are not expandable - if (!areChildrenAllowed(itemId)) { - return false; - } - - // Expands - expanded.add(itemId); - - expandedItemId = itemId; - if (initialPaint) { - markAsDirty(); - } else if (sendChildTree) { - requestPartialRepaint(); - } - fireExpandEvent(itemId); - - return true; - } - - @Override - public void markAsDirty() { - super.markAsDirty(); - partialUpdate = false; - } - - private void requestPartialRepaint() { - super.markAsDirty(); - partialUpdate = true; - } - - /** - * Expands the items recursively - * - * Expands all the children recursively starting from an item. Operation - * succeeds only if all expandable items are expanded. - * - * @param startItemId - * @return True iff the expand operation succeeded - */ - public boolean expandItemsRecursively(Object startItemId) { - - boolean result = true; - - // Initial stack - final Stack todo = new Stack(); - todo.add(startItemId); - - // Expands recursively - while (!todo.isEmpty()) { - final Object id = todo.pop(); - if (areChildrenAllowed(id) && !expandItem(id, false)) { - result = false; - } - if (hasChildren(id)) { - todo.addAll(getChildren(id)); - } - } - markAsDirty(); - return result; - } - - /** - * Collapses an item. - * - * @param itemId - * the item id. - * @return True iff the collapse operation succeeded - */ - public boolean collapseItem(Object itemId) { - - // Succeeds if the node is already collapsed - if (!isExpanded(itemId)) { - return true; - } - - // Collapse - expanded.remove(itemId); - markAsDirty(); - fireCollapseEvent(itemId); - - return true; - } - - /** - * Collapses the items recursively. - * - * Collapse all the children recursively starting from an item. Operation - * succeeds only if all expandable items are collapsed. - * - * @param startItemId - * @return True iff the collapse operation succeeded - */ - public boolean collapseItemsRecursively(Object startItemId) { - - boolean result = true; - - // Initial stack - final Stack todo = new Stack(); - todo.add(startItemId); - - // Collapse recursively - while (!todo.isEmpty()) { - final Object id = todo.pop(); - if (areChildrenAllowed(id) && !collapseItem(id)) { - result = false; - } - if (hasChildren(id)) { - todo.addAll(getChildren(id)); - } - } - - return result; - } - - /** - * Returns the current selectable state. Selectable determines if the a node - * can be selected on the client side. Selectable does not affect - * {@link #setValue(Object)} or {@link #select(Object)}. - * - *

    - * The tree is selectable by default. - *

    - * - * @return the current selectable state. - */ - public boolean isSelectable() { - return selectable; - } - - /** - * Sets the selectable state. Selectable determines if the a node can be - * selected on the client side. Selectable does not affect - * {@link #setValue(Object)} or {@link #select(Object)}. - * - *

    - * The tree is selectable by default. - *

    - * - * @param selectable - * The new selectable state. - */ - public void setSelectable(boolean selectable) { - if (this.selectable != selectable) { - this.selectable = selectable; - markAsDirty(); - } - } - - /** - * Sets the behavior of the multiselect mode - * - * @param mode - * The mode to set - */ - public void setMultiselectMode(MultiSelectMode mode) { - if (multiSelectMode != mode && mode != null) { - multiSelectMode = mode; - markAsDirty(); - } - } - - /** - * Returns the mode the multiselect is in. The mode controls how - * multiselection can be done. - * - * @return The mode - */ - public MultiSelectMode getMultiselectMode() { - return multiSelectMode; - } - - /* Component API */ - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.AbstractSelect#changeVariables(java.lang.Object, - * java.util.Map) - */ - @Override - public void changeVariables(Object source, Map variables) { - - if (variables.containsKey("clickedKey")) { - String key = (String) variables.get("clickedKey"); - - Object id = itemIdMapper.get(key); - MouseEventDetails details = MouseEventDetails - .deSerialize((String) variables.get("clickEvent")); - Item item = getItem(id); - if (item != null) { - fireEvent(new ItemClickEvent(this, item, id, null, details)); - } - } - - if (!isSelectable() && variables.containsKey("selected")) { - // Not-selectable is a special case, AbstractSelect does not support - // TODO could be optimized. - variables = new HashMap(variables); - variables.remove("selected"); - } - - // Collapses the nodes - if (variables.containsKey("collapse")) { - final String[] keys = (String[]) variables.get("collapse"); - for (int i = 0; i < keys.length; i++) { - final Object id = itemIdMapper.get(keys[i]); - if (id != null && isExpanded(id)) { - expanded.remove(id); - if (expandedItemId == id) { - expandedItemId = null; - } - fireCollapseEvent(id); - } - } - } - - // Expands the nodes - if (variables.containsKey("expand")) { - boolean sendChildTree = false; - if (variables.containsKey("requestChildTree")) { - sendChildTree = true; - } - final String[] keys = (String[]) variables.get("expand"); - for (int i = 0; i < keys.length; i++) { - final Object id = itemIdMapper.get(keys[i]); - if (id != null) { - expandItem(id, sendChildTree); - } - } - } - - // AbstractSelect cannot handle multiselection so we handle - // it ourself - if (variables.containsKey("selected") && isMultiSelect() - && multiSelectMode == MultiSelectMode.DEFAULT) { - handleSelectedItems(variables); - variables = new HashMap(variables); - variables.remove("selected"); - } - - // Selections are handled by the select component - super.changeVariables(source, variables); - - // Actions - if (variables.containsKey("action")) { - final StringTokenizer st = new StringTokenizer( - (String) variables.get("action"), ","); - if (st.countTokens() == 2) { - final Object itemId = itemIdMapper.get(st.nextToken()); - final Action action = actionMapper.get(st.nextToken()); - if (action != null && (itemId == null || containsId(itemId)) - && actionHandlers != null) { - for (Handler ah : actionHandlers) { - ah.handleAction(action, this, itemId); - } - } - } - } - } - - /** - * Handles the selection - * - * @param variables - * The variables sent to the server from the client - */ - private void handleSelectedItems(Map variables) { - final String[] ka = (String[]) variables.get("selected"); - - // Converts the key-array to id-set - final LinkedList s = new LinkedList(); - for (int i = 0; i < ka.length; i++) { - final Object id = itemIdMapper.get(ka[i]); - if (!isNullSelectionAllowed() - && (id == null || id == getNullSelectionItemId())) { - // skip empty selection if nullselection is not allowed - markAsDirty(); - } else if (id != null && containsId(id)) { - s.add(id); - } - } - - if (!isNullSelectionAllowed() && s.size() < 1) { - // empty selection not allowed, keep old value - markAsDirty(); - return; - } - - setValue(s, true); - } - - /** - * Paints any needed component-specific things to the given UIDL stream. - * - * @see com.vaadin.ui.AbstractComponent#paintContent(PaintTarget) - */ - @Override - public void paintContent(PaintTarget target) throws PaintException { - initialPaint = false; - - if (partialUpdate) { - target.addAttribute("partialUpdate", true); - target.addAttribute("rootKey", itemIdMapper.key(expandedItemId)); - } else { - getCaptionChangeListener().clear(); - - // The tab ordering number - if (getTabIndex() > 0) { - target.addAttribute("tabindex", getTabIndex()); - } - - // Paint tree attributes - if (isSelectable()) { - target.addAttribute("selectmode", - (isMultiSelect() ? "multi" : "single")); - if (isMultiSelect()) { - target.addAttribute("multiselectmode", - multiSelectMode.toString()); - } - } else { - target.addAttribute("selectmode", "none"); - } - if (isNewItemsAllowed()) { - target.addAttribute("allownewitem", true); - } - - if (isNullSelectionAllowed()) { - target.addAttribute("nullselect", true); - } - - if (dragMode != TreeDragMode.NONE) { - target.addAttribute("dragMode", dragMode.ordinal()); - } - - if (isHtmlContentAllowed()) { - target.addAttribute(TreeConstants.ATTRIBUTE_HTML_ALLOWED, true); - } - - } - - // Initialize variables - final Set actionSet = new LinkedHashSet(); - - // rendered selectedKeys - LinkedList selectedKeys = new LinkedList(); - - final LinkedList expandedKeys = new LinkedList(); - - // Iterates through hierarchical tree using a stack of iterators - final Stack> iteratorStack = new Stack>(); - Collection ids; - if (partialUpdate) { - ids = getChildren(expandedItemId); - } else { - ids = rootItemIds(); - } - - if (ids != null) { - iteratorStack.push(ids.iterator()); - } - - /* - * Body actions - Actions which has the target null and can be invoked - * by right clicking on the Tree body - */ - if (actionHandlers != null) { - final ArrayList keys = new ArrayList(); - for (Handler ah : actionHandlers) { - - // Getting action for the null item, which in this case - // means the body item - final Action[] aa = ah.getActions(null, this); - if (aa != null) { - for (int ai = 0; ai < aa.length; ai++) { - final String akey = actionMapper.key(aa[ai]); - actionSet.add(aa[ai]); - keys.add(akey); - } - } - } - target.addAttribute("alb", keys.toArray()); - } - - while (!iteratorStack.isEmpty()) { - - // Gets the iterator for current tree level - final Iterator i = iteratorStack.peek(); - - // If the level is finished, back to previous tree level - if (!i.hasNext()) { - - // Removes used iterator from the stack - iteratorStack.pop(); - - // Closes node - if (!iteratorStack.isEmpty()) { - target.endTag("node"); - } - } - - // Adds the item on current level - else { - final Object itemId = i.next(); - - // Starts the item / node - final boolean isNode = areChildrenAllowed(itemId); - if (isNode) { - target.startTag("node"); - } else { - target.startTag("leaf"); - } - - if (itemStyleGenerator != null) { - String stylename = itemStyleGenerator.getStyle(this, - itemId); - if (stylename != null) { - target.addAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE, - stylename); - } - } - - if (itemDescriptionGenerator != null) { - String description = itemDescriptionGenerator - .generateDescription(this, itemId, null); - if (description != null && !description.equals("")) { - target.addAttribute("descr", description); - } - } - - // Adds the attributes - target.addAttribute(TreeConstants.ATTRIBUTE_NODE_CAPTION, - getItemCaption(itemId)); - final Resource icon = getItemIcon(itemId); - if (icon != null) { - target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON, - getItemIcon(itemId)); - target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT, - getItemIconAlternateText(itemId)); - } - final String key = itemIdMapper.key(itemId); - target.addAttribute("key", key); - if (isSelected(itemId)) { - target.addAttribute("selected", true); - selectedKeys.add(key); - } - if (areChildrenAllowed(itemId) && isExpanded(itemId)) { - target.addAttribute("expanded", true); - expandedKeys.add(key); - } - - // Add caption change listener - getCaptionChangeListener().addNotifierForItem(itemId); - - // Actions - if (actionHandlers != null) { - final ArrayList keys = new ArrayList(); - final Iterator ahi = actionHandlers - .iterator(); - while (ahi.hasNext()) { - final Action[] aa = ahi.next().getActions(itemId, this); - if (aa != null) { - for (int ai = 0; ai < aa.length; ai++) { - final String akey = actionMapper.key(aa[ai]); - actionSet.add(aa[ai]); - keys.add(akey); - } - } - } - target.addAttribute("al", keys.toArray()); - } - - // Adds the children if expanded, or close the tag - if (isExpanded(itemId) && hasChildren(itemId) - && areChildrenAllowed(itemId)) { - iteratorStack.push(getChildren(itemId).iterator()); - } else { - if (isNode) { - target.endTag("node"); - } else { - target.endTag("leaf"); - } - } - } - } - - // Actions - if (!actionSet.isEmpty()) { - target.addVariable(this, "action", ""); - target.startTag("actions"); - final Iterator i = actionSet.iterator(); - while (i.hasNext()) { - final Action a = i.next(); - target.startTag("action"); - if (a.getCaption() != null) { - target.addAttribute(TreeConstants.ATTRIBUTE_ACTION_CAPTION, - a.getCaption()); - } - if (a.getIcon() != null) { - target.addAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON, - a.getIcon()); - } - target.addAttribute("key", actionMapper.key(a)); - target.endTag("action"); - } - target.endTag("actions"); - } - - if (partialUpdate) { - partialUpdate = false; - } else { - // Selected - target.addVariable(this, "selected", - selectedKeys.toArray(new String[selectedKeys.size()])); - - // Expand and collapse - target.addVariable(this, "expand", new String[] {}); - target.addVariable(this, "collapse", new String[] {}); - - // New items - target.addVariable(this, "newitem", new String[] {}); - - if (dropHandler != null) { - dropHandler.getAcceptCriterion().paint(target); - } - - } - } - - /* Container.Hierarchical API */ - - /** - * Tests if the Item with given ID can have any children. - * - * @see com.vaadin.data.Container.Hierarchical#areChildrenAllowed(Object) - */ - @Override - public boolean areChildrenAllowed(Object itemId) { - return ((Container.Hierarchical) items).areChildrenAllowed(itemId); - } - - /** - * Gets the IDs of all Items that are children of the specified Item. - * - * @see com.vaadin.data.Container.Hierarchical#getChildren(Object) - */ - @Override - public Collection getChildren(Object itemId) { - return ((Container.Hierarchical) items).getChildren(itemId); - } - - /** - * Gets the ID of the parent Item of the specified Item. - * - * @see com.vaadin.data.Container.Hierarchical#getParent(Object) - */ - @Override - public Object getParent(Object itemId) { - return ((Container.Hierarchical) items).getParent(itemId); - } - - /** - * Tests if the Item specified with itemId has child Items. - * - * @see com.vaadin.data.Container.Hierarchical#hasChildren(Object) - */ - @Override - public boolean hasChildren(Object itemId) { - return ((Container.Hierarchical) items).hasChildren(itemId); - } - - /** - * Tests if the Item specified with itemId is a root Item. - * - * @see com.vaadin.data.Container.Hierarchical#isRoot(Object) - */ - @Override - public boolean isRoot(Object itemId) { - return ((Container.Hierarchical) items).isRoot(itemId); - } - - /** - * Gets the IDs of all Items in the container that don't have a parent. - * - * @see com.vaadin.data.Container.Hierarchical#rootItemIds() - */ - @Override - public Collection rootItemIds() { - return ((Container.Hierarchical) items).rootItemIds(); - } - - /** - * Sets the given Item's capability to have children. - * - * @see com.vaadin.data.Container.Hierarchical#setChildrenAllowed(Object, - * boolean) - */ - @Override - public boolean setChildrenAllowed(Object itemId, - boolean areChildrenAllowed) { - final boolean success = ((Container.Hierarchical) items) - .setChildrenAllowed(itemId, areChildrenAllowed); - if (success) { - markAsDirty(); - } - return success; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Container.Hierarchical#setParent(java.lang.Object , - * java.lang.Object) - */ - @Override - public boolean setParent(Object itemId, Object newParentId) { - final boolean success = ((Container.Hierarchical) items) - .setParent(itemId, newParentId); - if (success) { - markAsDirty(); - } - return success; - } - - /* Overriding select behavior */ - - /** - * Sets the Container that serves as the data source of the viewer. - * - * @see com.vaadin.data.Container.Viewer#setContainerDataSource(Container) - */ - @Override - public void setContainerDataSource(Container newDataSource) { - if (newDataSource == null) { - newDataSource = new HierarchicalContainer(); - } - - // Assure that the data source is ordered by making unordered - // containers ordered by wrapping them - if (Container.Hierarchical.class - .isAssignableFrom(newDataSource.getClass())) { - super.setContainerDataSource(newDataSource); - } else { - super.setContainerDataSource( - new ContainerHierarchicalWrapper(newDataSource)); - } - - /* - * Ensure previous expanded items are cleaned up if they don't exist in - * the new container - */ - if (expanded != null) { - /* - * We need to check that the expanded-field is not null since - * setContainerDataSource() is called from the parent constructor - * (AbstractSelect()) and at that time the expanded field is not yet - * initialized. - */ - cleanupExpandedItems(); - } - - } - - @Override - public void containerItemSetChange( - com.vaadin.data.Container.ItemSetChangeEvent event) { - super.containerItemSetChange(event); - if (getContainerDataSource() instanceof Filterable) { - boolean hasFilters = !((Filterable) getContainerDataSource()) - .getContainerFilters().isEmpty(); - if (!hasFilters) { - /* - * If Container is not filtered then the itemsetchange is caused - * by either adding or removing items to the container. To - * prevent a memory leak we should cleanup the expanded list - * from items which was removed. - * - * However, there will still be a leak if the container is - * filtered to show only a subset of the items in the tree and - * later unfiltered items are removed from the container. In - * that case references to the unfiltered item ids will remain - * in the expanded list until the Tree instance is removed and - * the list is destroyed, or the container data source is - * replaced/updated. To force the removal of the removed items - * the application developer needs to a) remove the container - * filters temporarly or b) re-apply the container datasource - * using setContainerDataSource(getContainerDataSource()) - */ - cleanupExpandedItems(); - } - } - - } - - /* Expand event and listener */ - - /** - * Event to fired when a node is expanded. ExapandEvent is fired when a node - * is to be expanded. it can me used to dynamically fill the sub-nodes of - * the node. - * - * @author Vaadin Ltd. - * @since 3.0 - */ - public static class ExpandEvent extends Component.Event { - - private final Object expandedItemId; - - /** - * New instance of options change event - * - * @param source - * the Source of the event. - * @param expandedItemId - */ - public ExpandEvent(Component source, Object expandedItemId) { - super(source); - this.expandedItemId = expandedItemId; - } - - /** - * Node where the event occurred. - * - * @return the Source of the event. - */ - public Object getItemId() { - return expandedItemId; - } - } - - /** - * Expand event listener. - * - * @author Vaadin Ltd. - * @since 3.0 - */ - public interface ExpandListener extends Serializable { - - public static final Method EXPAND_METHOD = ReflectTools.findMethod( - ExpandListener.class, "nodeExpand", ExpandEvent.class); - - /** - * A node has been expanded. - * - * @param event - * the Expand event. - */ - public void nodeExpand(ExpandEvent event); - } - - /** - * Adds the expand listener. - * - * @param listener - * the Listener to be added. - */ - public void addExpandListener(ExpandListener listener) { - addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addExpandListener(ExpandListener)} - **/ - @Deprecated - public void addListener(ExpandListener listener) { - addExpandListener(listener); - } - - /** - * Removes the expand listener. - * - * @param listener - * the Listener to be removed. - */ - public void removeExpandListener(ExpandListener listener) { - removeListener(ExpandEvent.class, listener, - ExpandListener.EXPAND_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeExpandListener(ExpandListener)} - **/ - @Deprecated - public void removeListener(ExpandListener listener) { - removeExpandListener(listener); - } - - /** - * Emits the expand event. - * - * @param itemId - * the item id. - */ - protected void fireExpandEvent(Object itemId) { - fireEvent(new ExpandEvent(this, itemId)); - } - - /* Collapse event */ - - /** - * Collapse event - * - * @author Vaadin Ltd. - * @since 3.0 - */ - public static class CollapseEvent extends Component.Event { - - private final Object collapsedItemId; - - /** - * New instance of options change event. - * - * @param source - * the Source of the event. - * @param collapsedItemId - */ - public CollapseEvent(Component source, Object collapsedItemId) { - super(source); - this.collapsedItemId = collapsedItemId; - } - - /** - * Gets tge Collapsed Item id. - * - * @return the collapsed item id. - */ - public Object getItemId() { - return collapsedItemId; - } - } - - /** - * Collapse event listener. - * - * @author Vaadin Ltd. - * @since 3.0 - */ - public interface CollapseListener extends Serializable { - - public static final Method COLLAPSE_METHOD = ReflectTools.findMethod( - CollapseListener.class, "nodeCollapse", CollapseEvent.class); - - /** - * A node has been collapsed. - * - * @param event - * the Collapse event. - */ - public void nodeCollapse(CollapseEvent event); - } - - /** - * Adds the collapse listener. - * - * @param listener - * the Listener to be added. - */ - public void addCollapseListener(CollapseListener listener) { - addListener(CollapseEvent.class, listener, - CollapseListener.COLLAPSE_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addCollapseListener(CollapseListener)} - **/ - @Deprecated - public void addListener(CollapseListener listener) { - addCollapseListener(listener); - } - - /** - * Removes the collapse listener. - * - * @param listener - * the Listener to be removed. - */ - public void removeCollapseListener(CollapseListener listener) { - removeListener(CollapseEvent.class, listener, - CollapseListener.COLLAPSE_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeCollapseListener(CollapseListener)} - **/ - @Deprecated - public void removeListener(CollapseListener listener) { - removeCollapseListener(listener); - } - - /** - * Emits collapse event. - * - * @param itemId - * the item id. - */ - protected void fireCollapseEvent(Object itemId) { - fireEvent(new CollapseEvent(this, itemId)); - } - - /* Action container */ - - /** - * Adds an action handler. - * - * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler) - */ - @Override - public void addActionHandler(Action.Handler actionHandler) { - - if (actionHandler != null) { - - if (actionHandlers == null) { - actionHandlers = new LinkedList(); - actionMapper = new KeyMapper(); - } - - if (!actionHandlers.contains(actionHandler)) { - actionHandlers.add(actionHandler); - markAsDirty(); - } - } - } - - /** - * Removes an action handler. - * - * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler) - */ - @Override - public void removeActionHandler(Action.Handler actionHandler) { - - if (actionHandlers != null && actionHandlers.contains(actionHandler)) { - - actionHandlers.remove(actionHandler); - - if (actionHandlers.isEmpty()) { - actionHandlers = null; - actionMapper = null; - } - - markAsDirty(); - } - } - - /** - * Removes all action handlers - */ - public void removeAllActionHandlers() { - actionHandlers = null; - actionMapper = null; - markAsDirty(); - } - - /** - * Gets the visible item ids. - * - * @see com.vaadin.ui.Select#getVisibleItemIds() - */ - @Override - public Collection getVisibleItemIds() { - - final LinkedList visible = new LinkedList(); - - // Iterates trough hierarchical tree using a stack of iterators - final Stack> iteratorStack = new Stack>(); - final Collection ids = rootItemIds(); - if (ids != null) { - iteratorStack.push(ids.iterator()); - } - while (!iteratorStack.isEmpty()) { - - // Gets the iterator for current tree level - final Iterator i = iteratorStack.peek(); - - // If the level is finished, back to previous tree level - if (!i.hasNext()) { - - // Removes used iterator from the stack - iteratorStack.pop(); - } - - // Adds the item on current level - else { - final Object itemId = i.next(); - - visible.add(itemId); - - // Adds children if expanded, or close the tag - if (isExpanded(itemId) && hasChildren(itemId)) { - iteratorStack.push(getChildren(itemId).iterator()); - } - } - } - - return visible; - } - - /** - * Tree does not support setNullSelectionItemId. - * - * @see com.vaadin.ui.AbstractSelect#setNullSelectionItemId(java.lang.Object) - */ - @Override - public void setNullSelectionItemId(Object nullSelectionItemId) - throws UnsupportedOperationException { - if (nullSelectionItemId != null) { - throw new UnsupportedOperationException(); - } - - } - - /** - * Adding new items is not supported. - * - * @throws UnsupportedOperationException - * if set to true. - * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean) - */ - @Override - public void setNewItemsAllowed(boolean allowNewOptions) - throws UnsupportedOperationException { - if (allowNewOptions) { - throw new UnsupportedOperationException(); - } - } - - private ItemStyleGenerator itemStyleGenerator; - - private DropHandler dropHandler; - - private boolean htmlContentAllowed; - - @Override - public void addItemClickListener(ItemClickListener listener) { - addListener(TreeConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, - listener, ItemClickEvent.ITEM_CLICK_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addItemClickListener(ItemClickListener)} - **/ - @Override - @Deprecated - public void addListener(ItemClickListener listener) { - addItemClickListener(listener); - } - - @Override - public void removeItemClickListener(ItemClickListener listener) { - removeListener(TreeConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, - listener); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeItemClickListener(ItemClickListener)} - **/ - @Override - @Deprecated - public void removeListener(ItemClickListener listener) { - removeItemClickListener(listener); - } - - /** - * Sets the {@link ItemStyleGenerator} to be used with this tree. - * - * @param itemStyleGenerator - * item style generator or null to remove generator - */ - public void setItemStyleGenerator(ItemStyleGenerator itemStyleGenerator) { - if (this.itemStyleGenerator != itemStyleGenerator) { - this.itemStyleGenerator = itemStyleGenerator; - markAsDirty(); - } - } - - /** - * @return the current {@link ItemStyleGenerator} for this tree. Null if - * {@link ItemStyleGenerator} is not set. - */ - public ItemStyleGenerator getItemStyleGenerator() { - return itemStyleGenerator; - } - - /** - * ItemStyleGenerator can be used to add custom styles to tree items. The - * CSS class name that will be added to the item content is - * v-tree-node-[style name]. - */ - public interface ItemStyleGenerator extends Serializable { - - /** - * Called by Tree when an item is painted. - * - * @param source - * the source Tree - * @param itemId - * The itemId of the item to be painted - * @return The style name to add to this item. (the CSS class name will - * be v-tree-node-[style name] - */ - public abstract String getStyle(Tree source, Object itemId); - } - - // Overriden so javadoc comes from Container.Hierarchical - @Override - public boolean removeItem(Object itemId) - throws UnsupportedOperationException { - return super.removeItem(itemId); - } - - @Override - public DropHandler getDropHandler() { - return dropHandler; - } - - public void setDropHandler(DropHandler dropHandler) { - this.dropHandler = dropHandler; - } - - /** - * A {@link TargetDetails} implementation with Tree specific api. - * - * @since 6.3 - */ - public class TreeTargetDetails extends AbstractSelectTargetDetails { - - TreeTargetDetails(Map rawVariables) { - super(rawVariables); - } - - @Override - public Tree getTarget() { - return (Tree) super.getTarget(); - } - - /** - * If the event is on a node that can not have children (see - * {@link Tree#areChildrenAllowed(Object)}), this method returns the - * parent item id of the target item (see {@link #getItemIdOver()} ). - * The identifier of the parent node is also returned if the cursor is - * on the top part of node. Else this method returns the same as - * {@link #getItemIdOver()}. - *

    - * In other words this method returns the identifier of the "folder" - * into the drag operation is targeted. - *

    - * If the method returns null, the current target is on a root node or - * on other undefined area over the tree component. - *

    - * The default Tree implementation marks the targetted tree node with - * CSS classnames v-tree-node-dragfolder and - * v-tree-node-caption-dragfolder (for the caption element). - */ - public Object getItemIdInto() { - - Object itemIdOver = getItemIdOver(); - if (areChildrenAllowed(itemIdOver) - && getDropLocation() == VerticalDropLocation.MIDDLE) { - return itemIdOver; - } - return getParent(itemIdOver); - } - - /** - * If drop is targeted into "folder node" (see {@link #getItemIdInto()} - * ), this method returns the item id of the node after the drag was - * targeted. This method is useful when implementing drop into specific - * location (between specific nodes) in tree. - * - * @return the id of the item after the user targets the drop or null if - * "target" is a first item in node list (or the first in root - * node list) - */ - public Object getItemIdAfter() { - Object itemIdOver = getItemIdOver(); - Object itemIdInto2 = getItemIdInto(); - if (itemIdOver.equals(itemIdInto2)) { - return null; - } - VerticalDropLocation dropLocation = getDropLocation(); - if (VerticalDropLocation.TOP == dropLocation) { - // if on top of the caption area, add before - Collection children; - Object itemIdInto = getItemIdInto(); - if (itemIdInto != null) { - // seek the previous from child list - children = getChildren(itemIdInto); - } else { - children = rootItemIds(); - } - Object ref = null; - for (Object object : children) { - if (object.equals(itemIdOver)) { - return ref; - } - ref = object; - } - } - return itemIdOver; - } - - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map) - */ - @Override - public TreeTargetDetails translateDropTargetDetails( - Map clientVariables) { - return new TreeTargetDetails(clientVariables); - } - - /** - * Helper API for {@link TreeDropCriterion} - * - * @param itemId - * @return - */ - private String key(Object itemId) { - return itemIdMapper.key(itemId); - } - - /** - * Sets the drag mode that controls how Tree behaves as a {@link DragSource} - * . - * - * @param dragMode - */ - public void setDragMode(TreeDragMode dragMode) { - this.dragMode = dragMode; - markAsDirty(); - } - - /** - * @return the drag mode that controls how Tree behaves as a - * {@link DragSource}. - * - * @see TreeDragMode - */ - public TreeDragMode getDragMode() { - return dragMode; - } - - /** - * Concrete implementation of {@link DataBoundTransferable} for data - * transferred from a tree. - * - * @see {@link DataBoundTransferable}. - * - * @since 6.3 - */ - protected class TreeTransferable extends DataBoundTransferable { - - public TreeTransferable(Component sourceComponent, - Map rawVariables) { - super(sourceComponent, rawVariables); - } - - @Override - public Object getItemId() { - return getData("itemId"); - } - - @Override - public Object getPropertyId() { - return getItemCaptionPropertyId(); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.event.dd.DragSource#getTransferable(java.util.Map) - */ - @Override - public Transferable getTransferable(Map payload) { - TreeTransferable transferable = new TreeTransferable(this, payload); - // updating drag source variables - Object object = payload.get("itemId"); - if (object != null) { - transferable.setData("itemId", itemIdMapper.get((String) object)); - } - - return transferable; - } - - /** - * Lazy loading accept criterion for Tree. Accepted target nodes are loaded - * from server once per drag and drop operation. Developer must override one - * method that decides accepted tree nodes for the whole Tree. - * - *

    - * Initially pretty much no data is sent to client. On first required - * criterion check (per drag request) the client side data structure is - * initialized from server and no subsequent requests requests are needed - * during that drag and drop operation. - */ - public static abstract class TreeDropCriterion extends ServerSideCriterion { - - private Tree tree; - - private Set allowedItemIds; - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.acceptCriteria.ServerSideCriterion#getIdentifier - * () - */ - @Override - protected String getIdentifier() { - return TreeDropCriterion.class.getCanonicalName(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#accepts(com.vaadin - * .event.dd.DragAndDropEvent) - */ - @Override - public boolean accept(DragAndDropEvent dragEvent) { - AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent - .getTargetDetails(); - tree = (Tree) dragEvent.getTargetDetails().getTarget(); - allowedItemIds = getAllowedItemIds(dragEvent, tree); - - return allowedItemIds.contains(dropTargetData.getItemIdOver()); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#paintResponse( - * com.vaadin.server.PaintTarget) - */ - @Override - public void paintResponse(PaintTarget target) throws PaintException { - /* - * send allowed nodes to client so subsequent requests can be - * avoided - */ - Object[] array = allowedItemIds.toArray(); - for (int i = 0; i < array.length; i++) { - String key = tree.key(array[i]); - array[i] = key; - } - target.addAttribute("allowedIds", array); - } - - protected abstract Set getAllowedItemIds( - DragAndDropEvent dragEvent, Tree tree); - - } - - /** - * A criterion that accepts {@link Transferable} only directly on a tree - * node that can have children. - *

    - * Class is singleton, use {@link TargetItemAllowsChildren#get()} to get the - * instance. - * - * @see Tree#setChildrenAllowed(Object, boolean) - * - * @since 6.3 - */ - public static class TargetItemAllowsChildren extends TargetDetailIs { - - private static TargetItemAllowsChildren instance = new TargetItemAllowsChildren(); - - public static TargetItemAllowsChildren get() { - return instance; - } - - private TargetItemAllowsChildren() { - super("itemIdOverIsNode", Boolean.TRUE); - } - - /* - * Uses enhanced server side check - */ - @Override - public boolean accept(DragAndDropEvent dragEvent) { - try { - // must be over tree node and in the middle of it (not top or - // bottom - // part) - TreeTargetDetails eventDetails = (TreeTargetDetails) dragEvent - .getTargetDetails(); - - Object itemIdOver = eventDetails.getItemIdOver(); - if (!eventDetails.getTarget().areChildrenAllowed(itemIdOver)) { - return false; - } - // return true if directly over - return eventDetails - .getDropLocation() == VerticalDropLocation.MIDDLE; - } catch (Exception e) { - return false; - } - } - - } - - /** - * An accept criterion that checks the parent node (or parent hierarchy) for - * the item identifier given in constructor. If the parent is found, content - * is accepted. Criterion can be used to accepts drags on a specific sub - * tree only. - *

    - * The root items is also consider to be valid target. - */ - public class TargetInSubtree extends ClientSideCriterion { - - private Object rootId; - private int depthToCheck = -1; - - /** - * Constructs a criteria that accepts the drag if the targeted Item is a - * descendant of Item identified by given id - * - * @param parentItemId - * the item identifier of the parent node - */ - public TargetInSubtree(Object parentItemId) { - rootId = parentItemId; - } - - /** - * Constructs a criteria that accepts drops within given level below the - * subtree root identified by given id. - * - * @param rootId - * the item identifier to be sought for - * @param depthToCheck - * the depth that tree is traversed upwards to seek for the - * parent, -1 means that the whole structure should be - * checked - */ - public TargetInSubtree(Object rootId, int depthToCheck) { - this.rootId = rootId; - this.depthToCheck = depthToCheck; - } - - @Override - public boolean accept(DragAndDropEvent dragEvent) { - try { - TreeTargetDetails eventDetails = (TreeTargetDetails) dragEvent - .getTargetDetails(); - - if (eventDetails.getItemIdOver() != null) { - Object itemId = eventDetails.getItemIdOver(); - int i = 0; - while (itemId != null - && (depthToCheck == -1 || i <= depthToCheck)) { - if (itemId.equals(rootId)) { - return true; - } - itemId = getParent(itemId); - i++; - } - } - return false; - } catch (Exception e) { - return false; - } - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - super.paintContent(target); - target.addAttribute("depth", depthToCheck); - target.addAttribute("key", key(rootId)); - } - } - - /** - * Set the item description generator which generates tooltips for the tree - * items - * - * @param generator - * The generator to use or null to disable - */ - public void setItemDescriptionGenerator( - ItemDescriptionGenerator generator) { - if (generator != itemDescriptionGenerator) { - itemDescriptionGenerator = generator; - markAsDirty(); - } - } - - /** - * Get the item description generator which generates tooltips for tree - * items - */ - public ItemDescriptionGenerator getItemDescriptionGenerator() { - return itemDescriptionGenerator; - } - - private void cleanupExpandedItems() { - Set removedItemIds = new HashSet(); - for (Object expandedItemId : expanded) { - if (getItem(expandedItemId) == null) { - removedItemIds.add(expandedItemId); - if (this.expandedItemId == expandedItemId) { - this.expandedItemId = null; - } - } - } - expanded.removeAll(removedItemIds); - } - - /** - * Reads an Item from a design and inserts it into the data source. - * Recursively handles any children of the item as well. - * - * @since 7.5.0 - * @param node - * an element representing the item (tree node). - * @param selected - * A set accumulating selected items. If the item that is read is - * marked as selected, its item id should be added to this set. - * @param context - * the DesignContext instance used in parsing - * @return the item id of the new item - * - * @throws DesignException - * if the tag name of the {@code node} element is not - * {@code node}. - */ - @Override - protected String readItem(Element node, Set selected, - DesignContext context) { - - if (!"node".equals(node.tagName())) { - throw new DesignException("Unrecognized child element in " - + getClass().getSimpleName() + ": " + node.tagName()); - } - - String itemId = node.attr("text"); - addItem(itemId); - if (node.hasAttr("icon")) { - Resource icon = DesignAttributeHandler.readAttribute("icon", - node.attributes(), Resource.class); - setItemIcon(itemId, icon); - } - if (node.hasAttr("selected")) { - selected.add(itemId); - } - - for (Element child : node.children()) { - String childItemId = readItem(child, selected, context); - setParent(childItemId, itemId); - } - return itemId; - } - - /** - * Recursively writes the root items and their children to a design. - * - * @since 7.5.0 - * @param design - * the element into which to insert the items - * @param context - * the DesignContext instance used in writing - */ - @Override - protected void writeItems(Element design, DesignContext context) { - for (Object itemId : rootItemIds()) { - writeItem(design, itemId, context); - } - } - - /** - * Recursively writes a data source Item and its children to a design. - * - * @since 7.5.0 - * @param design - * the element into which to insert the item - * @param itemId - * the id of the item to write - * @param context - * the DesignContext instance used in writing - * @return - */ - @Override - protected Element writeItem(Element design, Object itemId, - DesignContext context) { - Element element = design.appendElement("node"); - - element.attr("text", itemId.toString()); - - Resource icon = getItemIcon(itemId); - if (icon != null) { - DesignAttributeHandler.writeAttribute("icon", element.attributes(), - icon, null, Resource.class); - } - - if (isSelected(itemId)) { - element.attr("selected", ""); - } - - Collection children = getChildren(itemId); - if (children != null) { - // Yeah... see #5864 - for (Object childItemId : children) { - writeItem(element, childItemId, context); - } - } - - return element; - } - - /** - * Sets whether html is allowed in the item captions. If set to - * true, the captions are passed to the browser as html and the - * developer is responsible for ensuring no harmful html is used. If set to - * false, the content is passed to the browser as plain text. - * The default setting is false - * - * @since 7.6 - * @param htmlContentAllowed - * true if the captions are used as html, - * false if used as plain text - */ - public void setHtmlContentAllowed(boolean htmlContentAllowed) { - this.htmlContentAllowed = htmlContentAllowed; - markAsDirty(); - } - - /** - * Checks whether captions are interpreted as html or plain text. - * - * @since 7.6 - * @return true if the captions are displayed as html, - * false if displayed as plain text - * @see #setHtmlContentAllowed(boolean) - */ - public boolean isHtmlContentAllowed() { - return htmlContentAllowed; - } - - @Override - protected TreeState getState() { - return (TreeState) super.getState(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/TreeTable.java b/compatibility-server/src/main/java/com/vaadin/ui/TreeTable.java deleted file mode 100644 index 2e69a0071d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/TreeTable.java +++ /dev/null @@ -1,985 +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.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jsoup.nodes.Element; - -import com.vaadin.data.Collapsible; -import com.vaadin.data.Container; -import com.vaadin.data.Container.Hierarchical; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.util.ContainerHierarchicalWrapper; -import com.vaadin.data.util.HierarchicalContainer; -import com.vaadin.data.util.HierarchicalContainerOrderedWrapper; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.server.Resource; -import com.vaadin.shared.ui.treetable.TreeTableConstants; -import com.vaadin.shared.ui.treetable.TreeTableState; -import com.vaadin.ui.Tree.CollapseEvent; -import com.vaadin.ui.Tree.CollapseListener; -import com.vaadin.ui.Tree.ExpandEvent; -import com.vaadin.ui.Tree.ExpandListener; -import com.vaadin.ui.declarative.DesignAttributeHandler; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.ui.declarative.DesignException; - -/** - * TreeTable extends the {@link Table} component so that it can also visualize a - * hierarchy of its Items in a similar manner that {@link Tree} does. The tree - * hierarchy is always displayed in the first actual column of the TreeTable. - *

    - * The TreeTable supports the usual {@link Table} features like lazy loading, so - * it should be no problem to display lots of items at once. Only required rows - * and some cache rows are sent to the client. - *

    - * TreeTable supports standard {@link Hierarchical} container interfaces, but - * also a more fine tuned version - {@link Collapsible}. A container - * implementing the {@link Collapsible} interface stores the collapsed/expanded - * state internally and can this way scale better on the server side than with - * standard Hierarchical implementations. Developer must however note that - * {@link Collapsible} containers can not be shared among several users as they - * share UI state in the container. - */ -@SuppressWarnings({ "serial" }) -public class TreeTable extends Table implements Hierarchical { - - private interface ContainerStrategy extends Serializable { - public int size(); - - public boolean isNodeOpen(Object itemId); - - public int getDepth(Object itemId); - - public void toggleChildVisibility(Object itemId); - - public Object getIdByIndex(int index); - - public int indexOfId(Object id); - - public Object nextItemId(Object itemId); - - public Object lastItemId(); - - public Object prevItemId(Object itemId); - - public boolean isLastId(Object itemId); - - public Collection getItemIds(); - - public void containerItemSetChange(ItemSetChangeEvent event); - } - - private abstract class AbstractStrategy implements ContainerStrategy { - - /** - * Consider adding getDepth to {@link Collapsible}, might help - * scalability with some container implementations. - */ - - @Override - public int getDepth(Object itemId) { - int depth = 0; - Hierarchical hierarchicalContainer = getContainerDataSource(); - while (!hierarchicalContainer.isRoot(itemId)) { - depth++; - itemId = hierarchicalContainer.getParent(itemId); - } - return depth; - } - - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - } - - } - - /** - * This strategy is used if current container implements {@link Collapsible} - * . - * - * open-collapsed logic diverted to container, otherwise use default - * implementations. - */ - private class CollapsibleStrategy extends AbstractStrategy { - - private Collapsible c() { - return (Collapsible) getContainerDataSource(); - } - - @Override - public void toggleChildVisibility(Object itemId) { - c().setCollapsed(itemId, !c().isCollapsed(itemId)); - } - - @Override - public boolean isNodeOpen(Object itemId) { - return !c().isCollapsed(itemId); - } - - @Override - public int size() { - return TreeTable.super.size(); - } - - @Override - public Object getIdByIndex(int index) { - return TreeTable.super.getIdByIndex(index); - } - - @Override - public int indexOfId(Object id) { - return TreeTable.super.indexOfId(id); - } - - @Override - public boolean isLastId(Object itemId) { - // using the default impl - return TreeTable.super.isLastId(itemId); - } - - @Override - public Object lastItemId() { - // using the default impl - return TreeTable.super.lastItemId(); - } - - @Override - public Object nextItemId(Object itemId) { - return TreeTable.super.nextItemId(itemId); - } - - @Override - public Object prevItemId(Object itemId) { - return TreeTable.super.prevItemId(itemId); - } - - @Override - public Collection getItemIds() { - return TreeTable.super.getItemIds(); - } - - } - - /** - * Strategy for Hierarchical but not Collapsible container like - * {@link HierarchicalContainer}. - * - * Store collapsed/open states internally, fool Table to use preorder when - * accessing items from container via Ordered/Indexed methods. - */ - private class HierarchicalStrategy extends AbstractStrategy { - - private final HashSet openItems = new HashSet(); - - @Override - public boolean isNodeOpen(Object itemId) { - return openItems.contains(itemId); - } - - @Override - public int size() { - return getPreOrder().size(); - } - - @Override - public Collection getItemIds() { - return Collections.unmodifiableCollection(getPreOrder()); - } - - @Override - public boolean isLastId(Object itemId) { - if (itemId == null) { - return false; - } - - return itemId.equals(lastItemId()); - } - - @Override - public Object lastItemId() { - if (getPreOrder().size() > 0) { - return getPreOrder().get(getPreOrder().size() - 1); - } else { - return null; - } - } - - @Override - public Object nextItemId(Object itemId) { - int indexOf = getPreOrder().indexOf(itemId); - if (indexOf == -1) { - return null; - } - indexOf++; - if (indexOf == getPreOrder().size()) { - return null; - } else { - return getPreOrder().get(indexOf); - } - } - - @Override - public Object prevItemId(Object itemId) { - int indexOf = getPreOrder().indexOf(itemId); - indexOf--; - if (indexOf < 0) { - return null; - } else { - return getPreOrder().get(indexOf); - } - } - - @Override - public void toggleChildVisibility(Object itemId) { - boolean removed = openItems.remove(itemId); - if (!removed) { - openItems.add(itemId); - getLogger().log(Level.FINEST, "Item {0} is now expanded", - itemId); - } else { - getLogger().log(Level.FINEST, "Item {0} is now collapsed", - itemId); - } - clearPreorderCache(); - } - - private void clearPreorderCache() { - preOrder = null; // clear preorder cache - } - - List preOrder; - - /** - * Preorder of ids currently visible - * - * @return - */ - private List getPreOrder() { - if (preOrder == null) { - preOrder = new ArrayList(); - Collection rootItemIds = getContainerDataSource() - .rootItemIds(); - for (Object id : rootItemIds) { - preOrder.add(id); - addVisibleChildTree(id); - } - } - return preOrder; - } - - private void addVisibleChildTree(Object id) { - if (isNodeOpen(id)) { - Collection children = getContainerDataSource() - .getChildren(id); - if (children != null) { - for (Object childId : children) { - preOrder.add(childId); - addVisibleChildTree(childId); - } - } - } - - } - - @Override - public int indexOfId(Object id) { - return getPreOrder().indexOf(id); - } - - @Override - public Object getIdByIndex(int index) { - return getPreOrder().get(index); - } - - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - // preorder becomes invalid on sort, item additions etc. - clearPreorderCache(); - super.containerItemSetChange(event); - } - - } - - /** - * Creates an empty TreeTable with a default container. - */ - public TreeTable() { - super(null, new HierarchicalContainer()); - } - - /** - * Creates an empty TreeTable with a default container. - * - * @param caption - * the caption for the TreeTable - */ - public TreeTable(String caption) { - this(); - setCaption(caption); - } - - /** - * Creates a TreeTable instance with given captions and data source. - * - * @param caption - * the caption for the component - * @param dataSource - * the dataSource that is used to list items in the component - */ - public TreeTable(String caption, Container dataSource) { - super(caption, dataSource); - } - - private ContainerStrategy cStrategy; - private Object focusedRowId = null; - private Object hierarchyColumnId; - - /** - * The item id that was expanded or collapsed during this request. Reset at - * the end of paint and only used for determining if a partial or full paint - * should be done. - * - * Can safely be reset to null whenever a change occurs that would prevent a - * partial update from rendering the correct result, e.g. rows added or - * removed during an expand operation. - */ - private Object toggledItemId; - private boolean animationsEnabled; - private boolean clearFocusedRowPending; - - /** - * If the container does not send item set change events, always do a full - * repaint instead of a partial update when expanding/collapsing nodes. - */ - private boolean containerSupportsPartialUpdates; - - private ContainerStrategy getContainerStrategy() { - if (cStrategy == null) { - if (getContainerDataSource() instanceof Collapsible) { - cStrategy = new CollapsibleStrategy(); - } else { - cStrategy = new HierarchicalStrategy(); - } - } - return cStrategy; - } - - @Override - protected void paintRowAttributes(PaintTarget target, Object itemId) - throws PaintException { - super.paintRowAttributes(target, itemId); - target.addAttribute("depth", getContainerStrategy().getDepth(itemId)); - if (getContainerDataSource().areChildrenAllowed(itemId)) { - target.addAttribute("ca", true); - target.addAttribute("open", - getContainerStrategy().isNodeOpen(itemId)); - } - } - - @Override - protected void paintRowIcon(PaintTarget target, Object[][] cells, - int indexInRowbuffer) throws PaintException { - // always paint if present (in parent only if row headers visible) - if (getRowHeaderMode() == ROW_HEADER_MODE_HIDDEN) { - Resource itemIcon = getItemIcon( - cells[CELL_ITEMID][indexInRowbuffer]); - if (itemIcon != null) { - target.addAttribute("icon", itemIcon); - } - } else if (cells[CELL_ICON][indexInRowbuffer] != null) { - target.addAttribute("icon", - (Resource) cells[CELL_ICON][indexInRowbuffer]); - } - } - - @Override - protected boolean rowHeadersAreEnabled() { - if (getRowHeaderMode() == RowHeaderMode.ICON_ONLY) { - return false; - } - return super.rowHeadersAreEnabled(); - } - - @Override - public void changeVariables(Object source, Map variables) { - super.changeVariables(source, variables); - - if (variables.containsKey("toggleCollapsed")) { - String object = (String) variables.get("toggleCollapsed"); - Object itemId = itemIdMapper.get(object); - toggledItemId = itemId; - toggleChildVisibility(itemId, false); - if (variables.containsKey("selectCollapsed")) { - // ensure collapsed is selected unless opened with selection - // head - if (isSelectable()) { - select(itemId); - } - } - } else if (variables.containsKey("focusParent")) { - String key = (String) variables.get("focusParent"); - Object refId = itemIdMapper.get(key); - Object itemId = getParent(refId); - focusParent(itemId); - } - } - - private void focusParent(Object itemId) { - boolean inView = false; - Object inPageId = getCurrentPageFirstItemId(); - for (int i = 0; inPageId != null && i < getPageLength(); i++) { - if (inPageId.equals(itemId)) { - inView = true; - break; - } - inPageId = nextItemId(inPageId); - i++; - } - if (!inView) { - setCurrentPageFirstItemId(itemId); - } - // Select the row if it is selectable. - if (isSelectable()) { - if (isMultiSelect()) { - setValue(Collections.singleton(itemId)); - } else { - setValue(itemId); - } - } - setFocusedRow(itemId); - } - - private void setFocusedRow(Object itemId) { - focusedRowId = itemId; - if (focusedRowId == null) { - // Must still inform the client that the focusParent request has - // been processed - clearFocusedRowPending = true; - } - markAsDirty(); - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - if (focusedRowId != null) { - target.addAttribute("focusedRow", itemIdMapper.key(focusedRowId)); - focusedRowId = null; - } else if (clearFocusedRowPending) { - // Must still inform the client that the focusParent request has - // been processed - target.addAttribute("clearFocusPending", true); - clearFocusedRowPending = false; - } - target.addAttribute("animate", animationsEnabled); - if (hierarchyColumnId != null) { - Object[] visibleColumns2 = getVisibleColumns(); - for (int i = 0; i < visibleColumns2.length; i++) { - Object object = visibleColumns2[i]; - if (hierarchyColumnId.equals(object)) { - target.addAttribute( - TreeTableConstants.ATTRIBUTE_HIERARCHY_COLUMN_INDEX, - i); - break; - } - } - } - super.paintContent(target); - toggledItemId = null; - } - - /* - * Override methods for partial row updates and additions when expanding / - * collapsing nodes. - */ - - @Override - protected boolean isPartialRowUpdate() { - return toggledItemId != null && containerSupportsPartialUpdates - && !isRowCacheInvalidated(); - } - - @Override - protected int getFirstAddedItemIndex() { - return indexOfId(toggledItemId) + 1; - } - - @Override - protected int getAddedRowCount() { - return countSubNodesRecursively(getContainerDataSource(), - toggledItemId); - } - - private int countSubNodesRecursively(Hierarchical hc, Object itemId) { - int count = 0; - // we need the number of children for toggledItemId no matter if its - // collapsed or expanded. Other items' children are only counted if the - // item is expanded. - if (getContainerStrategy().isNodeOpen(itemId) - || itemId == toggledItemId) { - Collection children = hc.getChildren(itemId); - if (children != null) { - count += children != null ? children.size() : 0; - for (Object id : children) { - count += countSubNodesRecursively(hc, id); - } - } - } - return count; - } - - @Override - protected int getFirstUpdatedItemIndex() { - return indexOfId(toggledItemId); - } - - @Override - protected int getUpdatedRowCount() { - return 1; - } - - @Override - protected boolean shouldHideAddedRows() { - return !getContainerStrategy().isNodeOpen(toggledItemId); - } - - private void toggleChildVisibility(Object itemId, - boolean forceFullRefresh) { - getContainerStrategy().toggleChildVisibility(itemId); - // ensure that page still has first item in page, DON'T clear the - // caches. - setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false); - - if (isCollapsed(itemId)) { - fireCollapseEvent(itemId); - } else { - fireExpandEvent(itemId); - } - - if (containerSupportsPartialUpdates && !forceFullRefresh) { - markAsDirty(); - } else { - // For containers that do not send item set change events, always do - // full repaint instead of partial row update. - refreshRowCache(); - } - } - - @Override - public int size() { - return getContainerStrategy().size(); - } - - @Override - public Hierarchical getContainerDataSource() { - return (Hierarchical) super.getContainerDataSource(); - } - - @Override - public void setContainerDataSource(Container newDataSource) { - cStrategy = null; - - // FIXME: This disables partial updates until TreeTable is fixed so it - // does not change component hierarchy during paint - containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier) - && false; - - if (newDataSource != null && !(newDataSource instanceof Hierarchical)) { - newDataSource = new ContainerHierarchicalWrapper(newDataSource); - } - - if (newDataSource != null && !(newDataSource instanceof Ordered)) { - newDataSource = new HierarchicalContainerOrderedWrapper( - (Hierarchical) newDataSource); - } - - super.setContainerDataSource(newDataSource); - } - - @Override - public void containerItemSetChange( - com.vaadin.data.Container.ItemSetChangeEvent event) { - // Can't do partial repaints if items are added or removed during the - // expand/collapse request - toggledItemId = null; - getContainerStrategy().containerItemSetChange(event); - super.containerItemSetChange(event); - } - - @Override - protected Object getIdByIndex(int index) { - return getContainerStrategy().getIdByIndex(index); - } - - @Override - protected int indexOfId(Object itemId) { - return getContainerStrategy().indexOfId(itemId); - } - - @Override - public Object nextItemId(Object itemId) { - return getContainerStrategy().nextItemId(itemId); - } - - @Override - public Object lastItemId() { - return getContainerStrategy().lastItemId(); - } - - @Override - public Object prevItemId(Object itemId) { - return getContainerStrategy().prevItemId(itemId); - } - - @Override - public boolean isLastId(Object itemId) { - return getContainerStrategy().isLastId(itemId); - } - - @Override - public Collection getItemIds() { - return getContainerStrategy().getItemIds(); - } - - @Override - public boolean areChildrenAllowed(Object itemId) { - return getContainerDataSource().areChildrenAllowed(itemId); - } - - @Override - public Collection getChildren(Object itemId) { - return getContainerDataSource().getChildren(itemId); - } - - @Override - public Object getParent(Object itemId) { - return getContainerDataSource().getParent(itemId); - } - - @Override - public boolean hasChildren(Object itemId) { - return getContainerDataSource().hasChildren(itemId); - } - - @Override - public boolean isRoot(Object itemId) { - return getContainerDataSource().isRoot(itemId); - } - - @Override - public Collection rootItemIds() { - return getContainerDataSource().rootItemIds(); - } - - @Override - public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) - throws UnsupportedOperationException { - return getContainerDataSource().setChildrenAllowed(itemId, - areChildrenAllowed); - } - - @Override - public boolean setParent(Object itemId, Object newParentId) - throws UnsupportedOperationException { - return getContainerDataSource().setParent(itemId, newParentId); - } - - /** - * Sets the Item specified by given identifier as collapsed or expanded. If - * the Item is collapsed, its children are not displayed to the user. - * - * @param itemId - * the identifier of the Item - * @param collapsed - * true if the Item should be collapsed, false if expanded - */ - public void setCollapsed(Object itemId, boolean collapsed) { - if (isCollapsed(itemId) != collapsed) { - if (null == toggledItemId && !isRowCacheInvalidated() - && getVisibleItemIds().contains(itemId)) { - // optimization: partial refresh if only one item is - // collapsed/expanded - toggledItemId = itemId; - toggleChildVisibility(itemId, false); - } else { - // make sure a full refresh takes place - otherwise neither - // partial nor full repaint of table content is performed - toggledItemId = null; - toggleChildVisibility(itemId, true); - } - } - } - - /** - * Checks if Item with given identifier is collapsed in the UI. - * - *

    - * - * @param itemId - * the identifier of the checked Item - * @return true if the Item with given id is collapsed - * @see Collapsible#isCollapsed(Object) - */ - public boolean isCollapsed(Object itemId) { - return !getContainerStrategy().isNodeOpen(itemId); - } - - /** - * Explicitly sets the column in which the TreeTable visualizes the - * hierarchy. If hierarchyColumnId is not set, the hierarchy is visualized - * in the first visible column. - * - * @param hierarchyColumnId - */ - public void setHierarchyColumn(Object hierarchyColumnId) { - this.hierarchyColumnId = hierarchyColumnId; - } - - /** - * @return the identifier of column into which the hierarchy will be - * visualized or null if the column is not explicitly defined. - */ - public Object getHierarchyColumnId() { - return hierarchyColumnId; - } - - /** - * Adds an expand listener. - * - * @param listener - * the Listener to be added. - */ - public void addExpandListener(ExpandListener listener) { - addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addExpandListener(ExpandListener)} - **/ - @Deprecated - public void addListener(ExpandListener listener) { - addExpandListener(listener); - } - - /** - * Removes an expand listener. - * - * @param listener - * the Listener to be removed. - */ - public void removeExpandListener(ExpandListener listener) { - removeListener(ExpandEvent.class, listener, - ExpandListener.EXPAND_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeExpandListener(ExpandListener)} - **/ - @Deprecated - public void removeListener(ExpandListener listener) { - removeExpandListener(listener); - } - - /** - * Emits an expand event. - * - * @param itemId - * the item id. - */ - protected void fireExpandEvent(Object itemId) { - fireEvent(new ExpandEvent(this, itemId)); - } - - /** - * Adds a collapse listener. - * - * @param listener - * the Listener to be added. - */ - public void addCollapseListener(CollapseListener listener) { - addListener(CollapseEvent.class, listener, - CollapseListener.COLLAPSE_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #addCollapseListener(CollapseListener)} - **/ - @Deprecated - public void addListener(CollapseListener listener) { - addCollapseListener(listener); - } - - /** - * Removes a collapse listener. - * - * @param listener - * the Listener to be removed. - */ - public void removeCollapseListener(CollapseListener listener) { - removeListener(CollapseEvent.class, listener, - CollapseListener.COLLAPSE_METHOD); - } - - /** - * @deprecated As of 7.0, replaced by - * {@link #removeCollapseListener(CollapseListener)} - **/ - @Deprecated - public void removeListener(CollapseListener listener) { - removeCollapseListener(listener); - } - - /** - * Emits a collapse event. - * - * @param itemId - * the item id. - */ - protected void fireCollapseEvent(Object itemId) { - fireEvent(new CollapseEvent(this, itemId)); - } - - /** - * @return true if animations are enabled - */ - public boolean isAnimationsEnabled() { - return animationsEnabled; - } - - /** - * Animations can be enabled by passing true to this method. Currently - * expanding rows slide in from the top and collapsing rows slide out the - * same way. NOTE! not supported in Internet Explorer 6 or 7. - * - * @param animationsEnabled - * true or false whether to enable animations or not. - */ - public void setAnimationsEnabled(boolean animationsEnabled) { - this.animationsEnabled = animationsEnabled; - markAsDirty(); - } - - private static final Logger getLogger() { - return Logger.getLogger(TreeTable.class.getName()); - } - - @Override - protected List getItemIds(int firstIndex, int rows) { - List itemIds = new ArrayList(); - for (int i = firstIndex; i < firstIndex + rows; i++) { - itemIds.add(getIdByIndex(i)); - } - return itemIds; - } - - @Override - protected void readBody(Element design, DesignContext context) { - Element tbody = design.select("> table > tbody").first(); - if (tbody == null) { - return; - } - - Set selected = new HashSet(); - Stack parents = new Stack(); - int lastDepth = -1; - - for (Element tr : tbody.children()) { - int depth = DesignAttributeHandler.readAttribute("depth", - tr.attributes(), 0, int.class); - - if (depth < 0 || depth > lastDepth + 1) { - throw new DesignException( - "Malformed TreeTable item hierarchy at " + tr - + ": last depth was " + lastDepth); - } else if (depth <= lastDepth) { - for (int d = depth; d <= lastDepth; d++) { - parents.pop(); - } - } - - Object itemId = readItem(tr, selected, context); - setParent(itemId, !parents.isEmpty() ? parents.peek() : null); - parents.push(itemId); - lastDepth = depth; - } - } - - @Override - protected Object readItem(Element tr, Set selected, - DesignContext context) { - Object itemId = super.readItem(tr, selected, context); - - if (tr.hasAttr("collapsed")) { - boolean collapsed = DesignAttributeHandler - .readAttribute("collapsed", tr.attributes(), boolean.class); - setCollapsed(itemId, collapsed); - } - - return itemId; - } - - @Override - protected void writeItems(Element design, DesignContext context) { - if (getVisibleColumns().length == 0) { - return; - } - Element tbody = design.child(0).appendElement("tbody"); - writeItems(tbody, rootItemIds(), 0, context); - } - - protected void writeItems(Element tbody, Collection itemIds, int depth, - DesignContext context) { - for (Object itemId : itemIds) { - Element tr = writeItem(tbody, itemId, context); - DesignAttributeHandler.writeAttribute("depth", tr.attributes(), - depth, 0, int.class); - - if (getChildren(itemId) != null) { - writeItems(tbody, getChildren(itemId), depth + 1, context); - } - } - } - - @Override - protected Element writeItem(Element tbody, Object itemId, - DesignContext context) { - Element tr = super.writeItem(tbody, itemId, context); - DesignAttributeHandler.writeAttribute("collapsed", tr.attributes(), - isCollapsed(itemId), true, boolean.class); - return tr; - } - - @Override - protected TreeTableState getState() { - return (TreeTableState) super.getState(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/TwinColSelect.java b/compatibility-server/src/main/java/com/vaadin/ui/TwinColSelect.java deleted file mode 100644 index 261d813ffa..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/TwinColSelect.java +++ /dev/null @@ -1,169 +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 com.vaadin.data.Container; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.shared.ui.twincolselect.TwinColSelectConstants; -import com.vaadin.shared.ui.twincolselect.TwinColSelectState; - -/** - * Multiselect component with two lists: left side for available items and right - * side for selected items. - */ -@SuppressWarnings("serial") -public class TwinColSelect extends AbstractSelect { - - private int rows = 0; - - private String leftColumnCaption; - private String rightColumnCaption; - - /** - * - */ - public TwinColSelect() { - super(); - setMultiSelect(true); - } - - /** - * @param caption - */ - public TwinColSelect(String caption) { - super(caption); - setMultiSelect(true); - } - - /** - * @param caption - * @param dataSource - */ - public TwinColSelect(String caption, Container dataSource) { - super(caption, dataSource); - setMultiSelect(true); - } - - public int getRows() { - return rows; - } - - /** - * Sets the number of rows in the editor. If the number of rows is set to 0, - * the actual number of displayed rows is determined implicitly by the - * adapter. - *

    - * If a height if set (using {@link #setHeight(String)} or - * {@link #setHeight(float, int)}) it overrides the number of rows. Leave - * the height undefined to use this method. This is the opposite of how - * {@link #setColumns(int)} work. - * - * - * @param rows - * the number of rows to set. - */ - public void setRows(int rows) { - if (rows < 0) { - rows = 0; - } - if (this.rows != rows) { - this.rows = rows; - markAsDirty(); - } - } - - /** - * @param caption - * @param options - */ - public TwinColSelect(String caption, Collection options) { - super(caption, options); - setMultiSelect(true); - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - // Adds the number of columns - // Adds the number of rows - if (rows != 0) { - target.addAttribute("rows", rows); - } - - // Right and left column captions and/or icons (if set) - String lc = getLeftColumnCaption(); - String rc = getRightColumnCaption(); - if (lc != null) { - target.addAttribute(TwinColSelectConstants.ATTRIBUTE_LEFT_CAPTION, - lc); - } - if (rc != null) { - target.addAttribute(TwinColSelectConstants.ATTRIBUTE_RIGHT_CAPTION, - rc); - } - - super.paintContent(target); - } - - /** - * Sets the text shown above the right column. - * - * @param caption - * The text to show - */ - public void setRightColumnCaption(String rightColumnCaption) { - this.rightColumnCaption = rightColumnCaption; - markAsDirty(); - } - - /** - * Returns the text shown above the right column. - * - * @return The text shown or null if not set. - */ - public String getRightColumnCaption() { - return rightColumnCaption; - } - - /** - * Sets the text shown above the left column. - * - * @param caption - * The text to show - */ - public void setLeftColumnCaption(String leftColumnCaption) { - this.leftColumnCaption = leftColumnCaption; - markAsDirty(); - } - - /** - * Returns the text shown above the left column. - * - * @return The text shown or null if not set. - */ - public String getLeftColumnCaption() { - return leftColumnCaption; - } - - @Override - protected TwinColSelectState getState() { - return (TwinColSelectState) super.getState(); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvent.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvent.java deleted file mode 100644 index f007f0cf34..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvent.java +++ /dev/null @@ -1,51 +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.components.calendar; - -import com.vaadin.ui.Calendar; -import com.vaadin.ui.Component; - -/** - * All Calendar events extends this class. - * - * @since 7.1 - * @author Vaadin Ltd. - * - */ -@SuppressWarnings("serial") -public class CalendarComponentEvent extends Component.Event { - - /** - * Set the source of the event - * - * @param source - * The source calendar - * - */ - public CalendarComponentEvent(Calendar source) { - super(source); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.Component.Event#getComponent() - */ - @Override - public Calendar getComponent() { - return (Calendar) super.getComponent(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvents.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvents.java deleted file mode 100644 index 2c4fec95d4..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarComponentEvents.java +++ /dev/null @@ -1,603 +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.components.calendar; - -import java.io.Serializable; -import java.lang.reflect.Method; -import java.util.Date; -import java.util.EventListener; - -import com.vaadin.shared.ui.calendar.CalendarEventId; -import com.vaadin.ui.Calendar; -import com.vaadin.ui.components.calendar.event.CalendarEvent; -import com.vaadin.util.ReflectTools; - -/** - * Interface for all Vaadin Calendar events. - * - * @since 7.1.0 - * @author Vaadin Ltd. - */ -public interface CalendarComponentEvents extends Serializable { - - /** - * Notifier interface for notifying listener of calendar events - */ - public interface CalendarEventNotifier extends Serializable { - /** - * Get the assigned event handler for the given eventId. - * - * @param eventId - * @return the assigned eventHandler, or null if no handler is assigned - */ - public EventListener getHandler(String eventId); - } - - /** - * Notifier interface for event drag & drops. - */ - public interface EventMoveNotifier extends CalendarEventNotifier { - - /** - * Set the EventMoveHandler. - * - * @param listener - * EventMoveHandler to be added - */ - public void setHandler(EventMoveHandler listener); - - } - - /** - * MoveEvent is sent when existing event is dragged to a new position. - */ - @SuppressWarnings("serial") - public class MoveEvent extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.EVENTMOVE; - - /** Index for the moved Schedule.Event. */ - private CalendarEvent calendarEvent; - - /** New starting date for the moved Calendar.Event. */ - private Date newStart; - - /** - * MoveEvent needs the target event and new start date. - * - * @param source - * Calendar component. - * @param calendarEvent - * Target event. - * @param newStart - * Target event's new start date. - */ - public MoveEvent(Calendar source, CalendarEvent calendarEvent, - Date newStart) { - super(source); - - this.calendarEvent = calendarEvent; - this.newStart = newStart; - } - - /** - * Get target event. - * - * @return Target event. - */ - public CalendarEvent getCalendarEvent() { - return calendarEvent; - } - - /** - * Get new start date. - * - * @return New start date. - */ - public Date getNewStart() { - return newStart; - } - } - - /** - * Handler interface for when events are being dragged on the calendar - * - */ - public interface EventMoveHandler extends EventListener, Serializable { - - /** Trigger method for the MoveEvent. */ - public static final Method eventMoveMethod = ReflectTools.findMethod( - EventMoveHandler.class, "eventMove", MoveEvent.class); - - /** - * This method will be called when event has been moved to a new - * position. - * - * @param event - * MoveEvent containing specific information of the new - * position and target event. - */ - public void eventMove(MoveEvent event); - } - - /** - * Handler interface for day or time cell drag-marking with mouse. - */ - public interface RangeSelectNotifier - extends Serializable, CalendarEventNotifier { - - /** - * Set the RangeSelectHandler that listens for drag-marking. - * - * @param listener - * RangeSelectHandler to be added. - */ - public void setHandler(RangeSelectHandler listener); - } - - /** - * RangeSelectEvent is sent when day or time cells are drag-marked with - * mouse. - */ - @SuppressWarnings("serial") - public class RangeSelectEvent extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.RANGESELECT; - - /** Calendar event's start date. */ - private Date start; - - /** Calendar event's end date. */ - private Date end; - - /** - * Defines the event's view mode. - */ - private boolean monthlyMode; - - /** - * RangeSelectEvent needs a start and end date. - * - * @param source - * Calendar component. - * @param start - * Start date. - * @param end - * End date. - * @param monthlyMode - * Calendar view mode. - */ - public RangeSelectEvent(Calendar source, Date start, Date end, - boolean monthlyMode) { - super(source); - this.start = start; - this.end = end; - this.monthlyMode = monthlyMode; - } - - /** - * Get start date. - * - * @return Start date. - */ - public Date getStart() { - return start; - } - - /** - * Get end date. - * - * @return End date. - */ - public Date getEnd() { - return end; - } - - /** - * Gets the event's view mode. Calendar can be be either in monthly or - * weekly mode, depending on the active date range. - * - * @deprecated User {@link Calendar#isMonthlyMode()} instead - * - * @return Returns true when monthly view is active. - */ - @Deprecated - public boolean isMonthlyMode() { - return monthlyMode; - } - } - - /** RangeSelectHandler handles RangeSelectEvent. */ - public interface RangeSelectHandler extends EventListener, Serializable { - - /** Trigger method for the RangeSelectEvent. */ - public static final Method rangeSelectMethod = ReflectTools.findMethod( - RangeSelectHandler.class, "rangeSelect", - RangeSelectEvent.class); - - /** - * This method will be called when day or time cells are drag-marked - * with mouse. - * - * @param event - * RangeSelectEvent that contains range start and end date. - */ - public void rangeSelect(RangeSelectEvent event); - } - - /** Notifier interface for navigation listening. */ - public interface NavigationNotifier extends Serializable { - /** - * Add a forward navigation listener. - * - * @param handler - * ForwardHandler to be added. - */ - public void setHandler(ForwardHandler handler); - - /** - * Add a backward navigation listener. - * - * @param handler - * BackwardHandler to be added. - */ - public void setHandler(BackwardHandler handler); - - /** - * Add a date click listener. - * - * @param handler - * DateClickHandler to be added. - */ - public void setHandler(DateClickHandler handler); - - /** - * Add a event click listener. - * - * @param handler - * EventClickHandler to be added. - */ - public void setHandler(EventClickHandler handler); - - /** - * Add a week click listener. - * - * @param handler - * WeekClickHandler to be added. - */ - public void setHandler(WeekClickHandler handler); - } - - /** - * ForwardEvent is sent when forward navigation button is clicked. - */ - @SuppressWarnings("serial") - public class ForwardEvent extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.FORWARD; - - /** - * ForwardEvent needs only the source component. - * - * @param source - * Calendar component. - */ - public ForwardEvent(Calendar source) { - super(source); - } - } - - /** ForwardHandler handles ForwardEvent. */ - public interface ForwardHandler extends EventListener, Serializable { - - /** Trigger method for the ForwardEvent. */ - public static final Method forwardMethod = ReflectTools.findMethod( - ForwardHandler.class, "forward", ForwardEvent.class); - - /** - * This method will be called when date range is moved forward. - * - * @param event - * ForwardEvent - */ - public void forward(ForwardEvent event); - } - - /** - * BackwardEvent is sent when backward navigation button is clicked. - */ - @SuppressWarnings("serial") - public class BackwardEvent extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.BACKWARD; - - /** - * BackwardEvent needs only the source source component. - * - * @param source - * Calendar component. - */ - public BackwardEvent(Calendar source) { - super(source); - } - } - - /** BackwardHandler handles BackwardEvent. */ - public interface BackwardHandler extends EventListener, Serializable { - - /** Trigger method for the BackwardEvent. */ - public static final Method backwardMethod = ReflectTools.findMethod( - BackwardHandler.class, "backward", BackwardEvent.class); - - /** - * This method will be called when date range is moved backwards. - * - * @param event - * BackwardEvent - */ - public void backward(BackwardEvent event); - } - - /** - * DateClickEvent is sent when a date is clicked. - */ - @SuppressWarnings("serial") - public class DateClickEvent extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.DATECLICK; - - /** Date that was clicked. */ - private Date date; - - /** DateClickEvent needs the target date that was clicked. */ - public DateClickEvent(Calendar source, Date date) { - super(source); - this.date = date; - } - - /** - * Get clicked date. - * - * @return Clicked date. - */ - public Date getDate() { - return date; - } - } - - /** DateClickHandler handles DateClickEvent. */ - public interface DateClickHandler extends EventListener, Serializable { - - /** Trigger method for the DateClickEvent. */ - public static final Method dateClickMethod = ReflectTools.findMethod( - DateClickHandler.class, "dateClick", DateClickEvent.class); - - /** - * This method will be called when a date is clicked. - * - * @param event - * DateClickEvent containing the target date. - */ - public void dateClick(DateClickEvent event); - } - - /** - * EventClick is sent when an event is clicked. - */ - @SuppressWarnings("serial") - public class EventClick extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.EVENTCLICK; - - /** Clicked source event. */ - private CalendarEvent calendarEvent; - - /** Target source event is needed for the EventClick. */ - public EventClick(Calendar source, CalendarEvent calendarEvent) { - super(source); - this.calendarEvent = calendarEvent; - } - - /** - * Get the clicked event. - * - * @return Clicked event. - */ - public CalendarEvent getCalendarEvent() { - return calendarEvent; - } - } - - /** EventClickHandler handles EventClick. */ - public interface EventClickHandler extends EventListener, Serializable { - - /** Trigger method for the EventClick. */ - public static final Method eventClickMethod = ReflectTools.findMethod( - EventClickHandler.class, "eventClick", EventClick.class); - - /** - * This method will be called when an event is clicked. - * - * @param event - * EventClick containing the target event. - */ - public void eventClick(EventClick event); - } - - /** - * WeekClick is sent when week is clicked. - */ - @SuppressWarnings("serial") - public class WeekClick extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.WEEKCLICK; - - /** Target week. */ - private int week; - - /** Target year. */ - private int year; - - /** - * WeekClick needs a target year and week. - * - * @param source - * Target source. - * @param week - * Target week. - * @param year - * Target year. - */ - public WeekClick(Calendar source, int week, int year) { - super(source); - this.week = week; - this.year = year; - } - - /** - * Get week as a integer. See {@link java.util.Calendar} for the allowed - * values. - * - * @return Week as a integer. - */ - public int getWeek() { - return week; - } - - /** - * Get year as a integer. See {@link java.util.Calendar} for the allowed - * values. - * - * @return Year as a integer - */ - public int getYear() { - return year; - } - } - - /** WeekClickHandler handles WeekClicks. */ - public interface WeekClickHandler extends EventListener, Serializable { - - /** Trigger method for the WeekClick. */ - public static final Method weekClickMethod = ReflectTools.findMethod( - WeekClickHandler.class, "weekClick", WeekClick.class); - - /** - * This method will be called when a week is clicked. - * - * @param event - * WeekClick containing the target week and year. - */ - public void weekClick(WeekClick event); - } - - /** - * EventResize is sent when an event is resized - */ - @SuppressWarnings("serial") - public class EventResize extends CalendarComponentEvent { - - public static final String EVENT_ID = CalendarEventId.EVENTRESIZE; - - private CalendarEvent calendarEvent; - - private Date startTime; - - private Date endTime; - - public EventResize(Calendar source, CalendarEvent calendarEvent, - Date startTime, Date endTime) { - super(source); - this.calendarEvent = calendarEvent; - this.startTime = startTime; - this.endTime = endTime; - } - - /** - * Get target event. - * - * @return Target event. - */ - public CalendarEvent getCalendarEvent() { - return calendarEvent; - } - - /** - * @deprecated Use {@link #getNewStart()} instead - * - * @return the new start time - */ - @Deprecated - public Date getNewStartTime() { - return startTime; - } - - /** - * Returns the updated start date/time of the event - * - * @return The new date for the event - */ - public Date getNewStart() { - return startTime; - } - - /** - * @deprecated Use {@link #getNewEnd()} instead - * - * @return the new end time - */ - @Deprecated - public Date getNewEndTime() { - return endTime; - } - - /** - * Returns the updates end date/time of the event - * - * @return The new date for the event - */ - public Date getNewEnd() { - return endTime; - } - } - - /** - * Notifier interface for event resizing. - */ - public interface EventResizeNotifier extends Serializable { - - /** - * Set a EventResizeHandler. - * - * @param handler - * EventResizeHandler to be set - */ - public void setHandler(EventResizeHandler handler); - } - - /** - * Handler for EventResize event. - */ - public interface EventResizeHandler extends EventListener, Serializable { - - /** Trigger method for the EventResize. */ - public static final Method eventResizeMethod = ReflectTools.findMethod( - EventResizeHandler.class, "eventResize", EventResize.class); - - void eventResize(EventResize event); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarDateRange.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarDateRange.java deleted file mode 100644 index 09d6c80a7f..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarDateRange.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.ui.components.calendar; - -import java.io.Serializable; -import java.util.Date; -import java.util.TimeZone; - -/** - * Class for representing a date range. - * - * @since 7.1.0 - * @author Vaadin Ltd. - * - */ -@SuppressWarnings("serial") -public class CalendarDateRange implements Serializable { - - private Date start; - - private Date end; - - private final transient TimeZone tz; - - /** - * Constructor - * - * @param start - * The start date and time of the date range - * @param end - * The end date and time of the date range - */ - public CalendarDateRange(Date start, Date end, TimeZone tz) { - super(); - this.start = start; - this.end = end; - this.tz = tz; - } - - /** - * Get the start date of the date range - * - * @return the start Date of the range - */ - public Date getStart() { - return start; - } - - /** - * Get the end date of the date range - * - * @return the end Date of the range - */ - public Date getEnd() { - return end; - } - - /** - * Is a date in the date range - * - * @param date - * The date to check - * @return true if the date range contains a date start and end of range - * inclusive; false otherwise - */ - public boolean inRange(Date date) { - if (date == null) { - return false; - } - - return date.compareTo(start) >= 0 && date.compareTo(end) <= 0; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "CalendarDateRange [start=" + start + ", end=" + end + "]"; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarTargetDetails.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarTargetDetails.java deleted file mode 100644 index 3b71ab5a00..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/CalendarTargetDetails.java +++ /dev/null @@ -1,80 +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.components.calendar; - -import java.util.Date; -import java.util.Map; - -import com.vaadin.event.dd.DropTarget; -import com.vaadin.event.dd.TargetDetailsImpl; -import com.vaadin.ui.Calendar; - -/** - * Drop details for {@link com.vaadin.ui.addon.calendar.ui.Calendar Calendar}. - * When something is dropped on the Calendar, this class contains the specific - * details of the drop point. Specifically, this class gives access to the date - * where the drop happened. If the Calendar was in weekly mode, the date also - * includes the start time of the slot. - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class CalendarTargetDetails extends TargetDetailsImpl { - - private boolean hasDropTime; - - public CalendarTargetDetails(Map rawDropData, - DropTarget dropTarget) { - super(rawDropData, dropTarget); - } - - /** - * @return true if {@link #getDropTime()} will return a date object with the - * time set to the start of the time slot where the drop happened - */ - public boolean hasDropTime() { - return hasDropTime; - } - - /** - * Does the dropped item have a time associated with it - * - * @param hasDropTime - */ - public void setHasDropTime(boolean hasDropTime) { - this.hasDropTime = hasDropTime; - } - - /** - * @return the date where the drop happened - */ - public Date getDropTime() { - if (hasDropTime) { - return (Date) getData("dropTime"); - } else { - return (Date) getData("dropDay"); - } - } - - /** - * @return the {@link com.vaadin.ui.addon.calendar.ui.Calendar Calendar} - * instance which was the target of the drop - */ - public Calendar getTargetCalendar() { - return (Calendar) getTarget(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/ContainerEventProvider.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/ContainerEventProvider.java deleted file mode 100644 index b0a6aaa95a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/ContainerEventProvider.java +++ /dev/null @@ -1,566 +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.components.calendar; - -import java.util.Collections; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.Container.ItemSetChangeEvent; -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.ValueChangeNotifier; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent; -import com.vaadin.ui.components.calendar.event.BasicEvent; -import com.vaadin.ui.components.calendar.event.CalendarEditableEventProvider; -import com.vaadin.ui.components.calendar.event.CalendarEvent; -import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeListener; -import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeNotifier; -import com.vaadin.ui.components.calendar.event.CalendarEventProvider; -import com.vaadin.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier; - -/** - * A event provider which uses a {@link Container} as a datasource. Container - * used as data source. - * - * NOTE: The data source must be sorted by date! - * - * @since 7.1.0 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class ContainerEventProvider - implements CalendarEditableEventProvider, EventSetChangeNotifier, - EventChangeNotifier, EventMoveHandler, EventResizeHandler, - Container.ItemSetChangeListener, Property.ValueChangeListener { - - // Default property ids - public static final String CAPTION_PROPERTY = "caption"; - public static final String DESCRIPTION_PROPERTY = "description"; - public static final String STARTDATE_PROPERTY = "start"; - public static final String ENDDATE_PROPERTY = "end"; - public static final String STYLENAME_PROPERTY = "styleName"; - public static final String ALL_DAY_PROPERTY = "allDay"; - - /** - * Internal class to keep the container index which item this event - * represents - * - */ - private class ContainerCalendarEvent extends BasicEvent { - private final int index; - - public ContainerCalendarEvent(int containerIndex) { - super(); - index = containerIndex; - } - - public int getContainerIndex() { - return index; - } - } - - /** - * Listeners attached to the container - */ - private final List eventSetChangeListeners = new LinkedList(); - private final List eventChangeListeners = new LinkedList(); - - /** - * The event cache contains the events previously created by - * {@link #getEvents(Date, Date)} - */ - private final List eventCache = new LinkedList(); - - /** - * The container used as datasource - */ - private Indexed container; - - /** - * Container properties. Defaults based on using the {@link BasicEvent} - * helper class. - */ - private Object captionProperty = CAPTION_PROPERTY; - private Object descriptionProperty = DESCRIPTION_PROPERTY; - private Object startDateProperty = STARTDATE_PROPERTY; - private Object endDateProperty = ENDDATE_PROPERTY; - private Object styleNameProperty = STYLENAME_PROPERTY; - private Object allDayProperty = ALL_DAY_PROPERTY; - - /** - * Constructor - * - * @param container - * Container to use as a data source. - */ - public ContainerEventProvider(Container.Indexed container) { - this.container = container; - listenToContainerEvents(); - } - - /** - * Set the container data source - * - * @param container - * The container to use as datasource - * - */ - public void setContainerDataSource(Container.Indexed container) { - // Detach the previous container - detachContainerDataSource(); - - this.container = container; - listenToContainerEvents(); - } - - /** - * Returns the container used as data source - * - */ - public Container.Indexed getContainerDataSource() { - return container; - } - - /** - * Attaches listeners to the container so container events can be processed - */ - private void listenToContainerEvents() { - if (container instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) container).addItemSetChangeListener(this); - } - if (container instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) container).addValueChangeListener(this); - } - } - - /** - * Removes listeners from the container so no events are processed - */ - private void ignoreContainerEvents() { - if (container instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) container) - .removeItemSetChangeListener(this); - } - if (container instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) container).removeValueChangeListener(this); - } - } - - /** - * Converts an event in the container to an {@link CalendarEvent} - * - * @param index - * The index of the item in the container to get the event for - * @return - */ - private CalendarEvent getEvent(int index) { - - // Check the event cache first - for (CalendarEvent e : eventCache) { - if (e instanceof ContainerCalendarEvent - && ((ContainerCalendarEvent) e) - .getContainerIndex() == index) { - return e; - } else if (container.getIdByIndex(index) == e) { - return e; - } - } - - final Object id = container.getIdByIndex(index); - Item item = container.getItem(id); - CalendarEvent event; - if (id instanceof CalendarEvent) { - /* - * If we are using the BeanItemContainer or another container which - * stores the objects as ids then just return the instances - */ - event = (CalendarEvent) id; - - } else { - /* - * Else we use the properties to create the event - */ - BasicEvent basicEvent = new ContainerCalendarEvent(index); - - // Set values from property values - if (captionProperty != null - && item.getItemPropertyIds().contains(captionProperty)) { - basicEvent.setCaption(String.valueOf( - item.getItemProperty(captionProperty).getValue())); - } - if (descriptionProperty != null && item.getItemPropertyIds() - .contains(descriptionProperty)) { - basicEvent.setDescription(String.valueOf( - item.getItemProperty(descriptionProperty).getValue())); - } - if (startDateProperty != null - && item.getItemPropertyIds().contains(startDateProperty)) { - basicEvent.setStart((Date) item - .getItemProperty(startDateProperty).getValue()); - } - if (endDateProperty != null - && item.getItemPropertyIds().contains(endDateProperty)) { - basicEvent.setEnd((Date) item.getItemProperty(endDateProperty) - .getValue()); - } - if (styleNameProperty != null - && item.getItemPropertyIds().contains(styleNameProperty)) { - basicEvent.setStyleName(String.valueOf( - item.getItemProperty(styleNameProperty).getValue())); - } - if (allDayProperty != null - && item.getItemPropertyIds().contains(allDayProperty)) { - basicEvent.setAllDay((Boolean) item - .getItemProperty(allDayProperty).getValue()); - } - event = basicEvent; - } - return event; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. - * util.Date, java.util.Date) - */ - @Override - public List getEvents(Date startDate, Date endDate) { - eventCache.clear(); - int size = container.size(); - assert size >= 0; - - for (int i = 0; i < size; i++) { - Object id = container.getIdByIndex(i); - Item item = container.getItem(id); - boolean add = true; - if (startDate != null) { - Date eventEnd = (Date) item.getItemProperty(endDateProperty) - .getValue(); - if (eventEnd.compareTo(startDate) < 0) { - add = false; - } - } - if (add && endDate != null) { - Date eventStart = (Date) item.getItemProperty(startDateProperty) - .getValue(); - if (eventStart.compareTo(endDate) >= 0) { - break; // because container is sorted, all further events - // will be even later - } - } - if (add) { - eventCache.add(getEvent(i)); - } - } - return Collections.unmodifiableList(eventCache); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEventProvider. - * EventSetChangeNotifier - * #addListener(com.vaadin.addon.calendar.event.CalendarEventProvider. - * EventSetChangeListener) - */ - @Override - public void addEventSetChangeListener(EventSetChangeListener listener) { - if (!eventSetChangeListeners.contains(listener)) { - eventSetChangeListeners.add(listener); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEventProvider. - * EventSetChangeNotifier - * #removeListener(com.vaadin.addon.calendar.event.CalendarEventProvider. - * EventSetChangeListener) - */ - @Override - public void removeEventSetChangeListener(EventSetChangeListener listener) { - eventSetChangeListeners.remove(listener); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier# - * addListener - * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener) - */ - @Override - public void addEventChangeListener(EventChangeListener listener) { - if (eventChangeListeners.contains(listener)) { - eventChangeListeners.add(listener); - } - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier# - * removeListener - * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener) - */ - @Override - public void removeEventChangeListener(EventChangeListener listener) { - eventChangeListeners.remove(listener); - } - - /** - * Get the property which provides the caption of the event - */ - public Object getCaptionProperty() { - return captionProperty; - } - - /** - * Set the property which provides the caption of the event - */ - public void setCaptionProperty(Object captionProperty) { - this.captionProperty = captionProperty; - } - - /** - * Get the property which provides the description of the event - */ - public Object getDescriptionProperty() { - return descriptionProperty; - } - - /** - * Set the property which provides the description of the event - */ - public void setDescriptionProperty(Object descriptionProperty) { - this.descriptionProperty = descriptionProperty; - } - - /** - * Get the property which provides the starting date and time of the event - */ - public Object getStartDateProperty() { - return startDateProperty; - } - - /** - * Set the property which provides the starting date and time of the event - */ - public void setStartDateProperty(Object startDateProperty) { - this.startDateProperty = startDateProperty; - } - - /** - * Get the property which provides the ending date and time of the event - */ - public Object getEndDateProperty() { - return endDateProperty; - } - - /** - * Set the property which provides the ending date and time of the event - */ - public void setEndDateProperty(Object endDateProperty) { - this.endDateProperty = endDateProperty; - } - - /** - * Get the property which provides the style name for the event - */ - public Object getStyleNameProperty() { - return styleNameProperty; - } - - /** - * Set the property which provides the style name for the event - */ - public void setStyleNameProperty(Object styleNameProperty) { - this.styleNameProperty = styleNameProperty; - } - - /** - * Set the all day property for the event - * - * @since 7.3.4 - */ - public void setAllDayProperty(Object allDayProperty) { - this.allDayProperty = allDayProperty; - } - - /** - * Get the all day property for the event - * - * @since 7.3.4 - */ - public Object getAllDayProperty() { - return allDayProperty; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange - * (com.vaadin.data.Container.ItemSetChangeEvent) - */ - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - if (event.getContainer() == container) { - // Trigger an eventset change event when the itemset changes - for (EventSetChangeListener listener : eventSetChangeListeners) { - listener.eventSetChange(new EventSetChangeEvent(this)); - } - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.Property.ValueChangeListener#valueChange(com.vaadin.data - * .Property.ValueChangeEvent) - */ - @Override - public void valueChange(ValueChangeEvent event) { - /* - * TODO Need to figure out how to get the item which triggered the the - * valuechange event and then trigger a EventChange event to the - * listeners - */ - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler - * #eventMove - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent) - */ - @Override - public void eventMove(MoveEvent event) { - CalendarEvent ce = event.getCalendarEvent(); - if (eventCache.contains(ce)) { - int index; - if (ce instanceof ContainerCalendarEvent) { - index = ((ContainerCalendarEvent) ce).getContainerIndex(); - } else { - index = container.indexOfId(ce); - } - - long eventLength = ce.getEnd().getTime() - ce.getStart().getTime(); - Date newEnd = new Date(event.getNewStart().getTime() + eventLength); - - ignoreContainerEvents(); - Item item = container.getItem(container.getIdByIndex(index)); - item.getItemProperty(startDateProperty) - .setValue(event.getNewStart()); - item.getItemProperty(endDateProperty).setValue(newEnd); - listenToContainerEvents(); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler - * #eventResize - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize) - */ - @Override - public void eventResize(EventResize event) { - CalendarEvent ce = event.getCalendarEvent(); - if (eventCache.contains(ce)) { - int index; - if (ce instanceof ContainerCalendarEvent) { - index = ((ContainerCalendarEvent) ce).getContainerIndex(); - } else { - index = container.indexOfId(ce); - } - ignoreContainerEvents(); - Item item = container.getItem(container.getIdByIndex(index)); - item.getItemProperty(startDateProperty) - .setValue(event.getNewStart()); - item.getItemProperty(endDateProperty).setValue(event.getNewEnd()); - listenToContainerEvents(); - } - } - - /** - * If you are reusing the container which previously have been attached to - * this ContainerEventProvider call this method to remove this event - * providers container listeners before attaching it to an other - * ContainerEventProvider - */ - public void detachContainerDataSource() { - ignoreContainerEvents(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent - * (com.vaadin.addon.calendar.event.CalendarEvent) - */ - @Override - public void addEvent(CalendarEvent event) { - Item item; - try { - item = container.addItem(event); - } catch (UnsupportedOperationException uop) { - // Thrown if container does not support adding items with custom - // ids. JPAContainer for example. - item = container.getItem(container.addItem()); - } - if (item != null) { - item.getItemProperty(getCaptionProperty()) - .setValue(event.getCaption()); - item.getItemProperty(getStartDateProperty()) - .setValue(event.getStart()); - item.getItemProperty(getEndDateProperty()).setValue(event.getEnd()); - item.getItemProperty(getStyleNameProperty()) - .setValue(event.getStyleName()); - item.getItemProperty(getDescriptionProperty()) - .setValue(event.getDescription()); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent - * (com.vaadin.addon.calendar.event.CalendarEvent) - */ - @Override - public void removeEvent(CalendarEvent event) { - container.removeItem(event); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEvent.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEvent.java deleted file mode 100644 index e2a580085a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEvent.java +++ /dev/null @@ -1,265 +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.components.calendar.event; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeNotifier; - -/** - * Simple implementation of {@link com.vaadin.addon.calendar.event.CalendarEvent - * CalendarEvent}. Has setters for all required fields and fires events when - * this event is changed. - * - * @since 7.1.0 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicEvent implements EditableCalendarEvent, EventChangeNotifier { - - private String caption; - private String description; - private Date end; - private Date start; - private String styleName; - private transient List listeners = new ArrayList(); - - private boolean isAllDay; - - /** - * Default constructor - */ - public BasicEvent() { - - } - - /** - * Constructor for creating an event with the same start and end date - * - * @param caption - * The caption for the event - * @param description - * The description for the event - * @param date - * The date the event occurred - */ - public BasicEvent(String caption, String description, Date date) { - this.caption = caption; - this.description = description; - start = date; - end = date; - } - - /** - * Constructor for creating an event with a start date and an end date. - * Start date should be before the end date - * - * @param caption - * The caption for the event - * @param description - * The description for the event - * @param startDate - * The start date of the event - * @param endDate - * The end date of the event - */ - public BasicEvent(String caption, String description, Date startDate, - Date endDate) { - this.caption = caption; - this.description = description; - start = startDate; - end = endDate; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent#getCaption() - */ - @Override - public String getCaption() { - return caption; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent#getDescription() - */ - @Override - public String getDescription() { - return description; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd() - */ - @Override - public Date getEnd() { - return end; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart() - */ - @Override - public Date getStart() { - return start; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName() - */ - @Override - public String getStyleName() { - return styleName; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.event.CalendarEvent#isAllDay() - */ - @Override - public boolean isAllDay() { - return isAllDay; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventEditor#setCaption(java.lang - * .String) - */ - @Override - public void setCaption(String caption) { - this.caption = caption; - fireEventChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventEditor#setDescription(java - * .lang.String) - */ - @Override - public void setDescription(String description) { - this.description = description; - fireEventChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventEditor#setEnd(java.util. - * Date) - */ - @Override - public void setEnd(Date end) { - this.end = end; - fireEventChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventEditor#setStart(java.util - * .Date) - */ - @Override - public void setStart(Date start) { - this.start = start; - fireEventChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventEditor#setStyleName(java - * .lang.String) - */ - @Override - public void setStyleName(String styleName) { - this.styleName = styleName; - fireEventChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventEditor#setAllDay(boolean) - */ - @Override - public void setAllDay(boolean isAllDay) { - this.isAllDay = isAllDay; - fireEventChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier - * #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener - * ) - */ - @Override - public void addEventChangeListener(EventChangeListener listener) { - listeners.add(listener); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier - * #removeListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener - * ) - */ - @Override - public void removeEventChangeListener(EventChangeListener listener) { - listeners.remove(listener); - } - - /** - * Fires an event change event to the listeners. Should be triggered when - * some property of the event changes. - */ - protected void fireEventChange() { - EventChangeEvent event = new EventChangeEvent(this); - - for (EventChangeListener listener : listeners) { - listener.eventChange(event); - } - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEventProvider.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEventProvider.java deleted file mode 100644 index fbf197d3eb..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/BasicEventProvider.java +++ /dev/null @@ -1,177 +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.components.calendar.event; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeEvent; -import com.vaadin.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier; - -/** - *

    - * Simple implementation of - * {@link com.vaadin.addon.calendar.event.CalendarEventProvider - * CalendarEventProvider}. Use {@link #addEvent(CalendarEvent)} and - * {@link #removeEvent(CalendarEvent)} to add / remove events. - *

    - * - *

    - * {@link com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier - * EventSetChangeNotifier} and - * {@link com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener - * EventChangeListener} are also implemented, so the Calendar is notified when - * an event is added, changed or removed. - *

    - * - * @since 7.1.0 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicEventProvider implements CalendarEditableEventProvider, - EventSetChangeNotifier, CalendarEvent.EventChangeListener { - - protected List eventList = new ArrayList(); - - private List listeners = new ArrayList(); - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. - * util.Date, java.util.Date) - */ - @Override - public List getEvents(Date startDate, Date endDate) { - ArrayList activeEvents = new ArrayList(); - - for (CalendarEvent ev : eventList) { - long from = startDate.getTime(); - long to = endDate.getTime(); - - if (ev.getStart() != null && ev.getEnd() != null) { - long f = ev.getStart().getTime(); - long t = ev.getEnd().getTime(); - // Select only events that overlaps with startDate and - // endDate. - if ((f <= to && f >= from) || (t >= from && t <= to) - || (f <= from && t >= to)) { - activeEvents.add(ev); - } - } - } - - return activeEvents; - } - - /** - * Does this event provider container this event - * - * @param event - * The event to check for - * @return If this provider has the event then true is returned, else false - */ - public boolean containsEvent(BasicEvent event) { - return eventList.contains(event); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents. - * EventSetChangeNotifier #addListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents. - * EventSetChangeListener ) - */ - @Override - public void addEventSetChangeListener(EventSetChangeListener listener) { - listeners.add(listener); - - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents. - * EventSetChangeNotifier #removeListener - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents. - * EventSetChangeListener ) - */ - @Override - public void removeEventSetChangeListener(EventSetChangeListener listener) { - listeners.remove(listener); - } - - /** - * Fires a eventsetchange event. The event is fired when either an event is - * added or removed to the event provider - */ - protected void fireEventSetChange() { - EventSetChangeEvent event = new EventSetChangeEvent(this); - - for (EventSetChangeListener listener : listeners) { - listener.eventSetChange(event); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener - * #eventChange - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChange) - */ - @Override - public void eventChange(EventChangeEvent changeEvent) { - // naive implementation - fireEventSetChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent - * (com.vaadin.addon.calendar.event.CalendarEvent) - */ - @Override - public void addEvent(CalendarEvent event) { - eventList.add(event); - if (event instanceof BasicEvent) { - ((BasicEvent) event).addEventChangeListener(this); - } - fireEventSetChange(); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent - * (com.vaadin.addon.calendar.event.CalendarEvent) - */ - @Override - public void removeEvent(CalendarEvent event) { - eventList.remove(event); - if (event instanceof BasicEvent) { - ((BasicEvent) event).removeEventChangeListener(this); - } - fireEventSetChange(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java deleted file mode 100644 index aaa76418a6..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java +++ /dev/null @@ -1,42 +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.components.calendar.event; - -/** - * An event provider which allows adding and removing events - * - * @since 7.1.0 - * @author Vaadin Ltd. - */ -public interface CalendarEditableEventProvider extends CalendarEventProvider { - - /** - * Adds an event to the event provider - * - * @param event - * The event to add - */ - void addEvent(CalendarEvent event); - - /** - * Removes an event from the event provider - * - * @param event - * The event - */ - void removeEvent(CalendarEvent event); -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEvent.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEvent.java deleted file mode 100644 index b4195cf0b1..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEvent.java +++ /dev/null @@ -1,147 +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.components.calendar.event; - -import java.io.Serializable; -import java.util.Date; - -/** - *

    - * Event in the calendar. Customize your own event by implementing this - * interface. - *

    - * - *
  • Start and end fields are mandatory.
  • - * - *
  • In "allDay" events longer than one day, starting and ending clock times - * are omitted in UI and only dates are shown.
  • - * - * @since 7.1.0 - * @author Vaadin Ltd. - * - */ -public interface CalendarEvent extends Serializable { - - /** - * Gets start date of event. - * - * @return Start date. - */ - public Date getStart(); - - /** - * Get end date of event. - * - * @return End date; - */ - public Date getEnd(); - - /** - * Gets caption of event. - * - * @return Caption text - */ - public String getCaption(); - - /** - * Gets description of event. Shown as a tooltip over the event. - * - * @return Description text. - */ - public String getDescription(); - - /** - *

    - * Gets style name of event. In the client, style name will be set to the - * event's element class name and can be styled by CSS - *

    - * Styling example:
    - * Java code:
    - * event.setStyleName("color1"); - *

    - * CSS:
    - * .v-calendar-event-color1 {
    - *    background-color: #9effae;
    }
    - * - * @return Style name. - */ - public String getStyleName(); - - /** - * An all-day event typically does not occur at a specific time but targets - * a whole day or days. The rendering of all-day events differs from normal - * events. - * - * @return true if this event is an all-day event, false otherwise - */ - public boolean isAllDay(); - - /** - * Event to signal that an event has changed. - */ - @SuppressWarnings("serial") - public class EventChangeEvent implements Serializable { - - private CalendarEvent source; - - public EventChangeEvent(CalendarEvent source) { - this.source = source; - } - - /** - * @return the {@link com.vaadin.addon.calendar.event.CalendarEvent - * CalendarEvent} that has changed - */ - public CalendarEvent getCalendarEvent() { - return source; - } - } - - /** - * Listener for EventSetChange events. - */ - public interface EventChangeListener extends Serializable { - - /** - * Called when an Event has changed. - */ - public void eventChange(EventChangeEvent eventChangeEvent); - } - - /** - * Notifier interface for EventChange events. - */ - public interface EventChangeNotifier extends Serializable { - - /** - * Add a listener to listen for EventChangeEvents. These events are - * fired when a events properties are changed. - * - * @param listener - * The listener to add - */ - void addEventChangeListener(EventChangeListener listener); - - /** - * Remove a listener from the event provider. - * - * @param listener - * The listener to remove - */ - void removeEventChangeListener(EventChangeListener listener); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java deleted file mode 100644 index 1d4fabed5a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java +++ /dev/null @@ -1,112 +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.components.calendar.event; - -import java.io.Serializable; -import java.util.Date; -import java.util.List; - -/** - * Interface for querying events. The Vaadin Calendar always has a - * CalendarEventProvider set. - * - * @since 7.1.0 - * @author Vaadin Ltd. - */ -public interface CalendarEventProvider extends Serializable { - /** - *

    - * Gets all available events in the target date range between startDate and - * endDate. The Vaadin Calendar queries the events from the range that is - * shown, which is not guaranteed to be the same as the date range that is - * set. - *

    - * - *

    - * For example, if you set the date range to be monday 22.2.2010 - wednesday - * 24.2.2010, the used Event Provider will be queried for events between - * monday 22.2.2010 00:00 and sunday 28.2.2010 23:59. Generally you can - * expect the date range to be expanded to whole days and whole weeks. - *

    - * - * @param startDate - * Start date - * @param endDate - * End date - * @return List of events - */ - public List getEvents(Date startDate, Date endDate); - - /** - * Event to signal that the set of events has changed and the calendar - * should refresh its view from the - * {@link com.vaadin.addon.calendar.event.CalendarEventProvider - * CalendarEventProvider} . - * - */ - @SuppressWarnings("serial") - public class EventSetChangeEvent implements Serializable { - - private CalendarEventProvider source; - - public EventSetChangeEvent(CalendarEventProvider source) { - this.source = source; - } - - /** - * @return the - * {@link com.vaadin.addon.calendar.event.CalendarEventProvider - * CalendarEventProvider} that has changed - */ - public CalendarEventProvider getProvider() { - return source; - } - } - - /** - * Listener for EventSetChange events. - */ - public interface EventSetChangeListener extends Serializable { - - /** - * Called when the set of Events has changed. - */ - public void eventSetChange(EventSetChangeEvent changeEvent); - } - - /** - * Notifier interface for EventSetChange events. - */ - public interface EventSetChangeNotifier extends Serializable { - - /** - * Add a listener for listening to when new events are adding or removed - * from the event provider. - * - * @param listener - * The listener to add - */ - void addEventSetChangeListener(EventSetChangeListener listener); - - /** - * Remove a listener which listens to {@link EventSetChangeEvent}-events - * - * @param listener - * The listener to remove - */ - void removeEventSetChangeListener(EventSetChangeListener listener); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java deleted file mode 100644 index 5a44cae4ac..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java +++ /dev/null @@ -1,91 +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.components.calendar.event; - -import java.util.Date; - -/** - *

    - * Extension to the basic {@link com.vaadin.addon.calendar.event.CalendarEvent - * CalendarEvent}. This interface provides setters (and thus editing - * capabilities) for all {@link com.vaadin.addon.calendar.event.CalendarEvent - * CalendarEvent} fields. For descriptions on the fields, refer to the extended - * interface. - *

    - * - *

    - * This interface is used by some of the basic Calendar event handlers in the - * com.vaadin.addon.calendar.ui.handler package to determine - * whether an event can be edited. - *

    - * - * @since 7.1 - * @author Vaadin Ltd. - */ -public interface EditableCalendarEvent extends CalendarEvent { - - /** - * Set the visible text in the calendar for the event. - * - * @param caption - * The text to show in the calendar - */ - void setCaption(String caption); - - /** - * Set the description of the event. This is shown in the calendar when - * hoovering over the event. - * - * @param description - * The text which describes the event - */ - void setDescription(String description); - - /** - * Set the end date of the event. Must be after the start date. - * - * @param end - * The end date to set - */ - void setEnd(Date end); - - /** - * Set the start date for the event. Must be before the end date - * - * @param start - * The start date of the event - */ - void setStart(Date start); - - /** - * Set the style name for the event used for styling the event cells - * - * @param styleName - * The stylename to use - * - */ - void setStyleName(String styleName); - - /** - * Does the event span the whole day. If so then set this to true - * - * @param isAllDay - * True if the event spans the whole day. In this case the start - * and end times are ignored. - */ - void setAllDay(boolean isAllDay); - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java deleted file mode 100644 index 61f738fcd0..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java +++ /dev/null @@ -1,96 +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.components.calendar.handler; - -import java.util.Calendar; -import java.util.Date; - -import com.vaadin.shared.ui.calendar.DateConstants; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler; - -/** - * Implements basic functionality needed to enable backwards navigation. - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicBackwardHandler implements BackwardHandler { - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler# - * backward - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardEvent) - */ - @Override - public void backward(BackwardEvent event) { - Date start = event.getComponent().getStartDate(); - Date end = event.getComponent().getEndDate(); - - // calculate amount to move back - int durationInDays = (int) (((end.getTime()) - start.getTime()) - / DateConstants.DAYINMILLIS); - durationInDays++; - // for week view durationInDays = -7, for day view durationInDays = -1 - durationInDays = -durationInDays; - - // set new start and end times - Calendar javaCalendar = event.getComponent().getInternalCalendar(); - javaCalendar.setTime(start); - javaCalendar.add(java.util.Calendar.DATE, durationInDays); - Date newStart = javaCalendar.getTime(); - - javaCalendar.setTime(end); - javaCalendar.add(java.util.Calendar.DATE, durationInDays); - Date newEnd = javaCalendar.getTime(); - - if (start.equals(end)) { // day view - int firstDay = event.getComponent().getFirstVisibleDayOfWeek(); - int lastDay = event.getComponent().getLastVisibleDayOfWeek(); - int dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); - - // we suppose that 7 >= lastDay >= firstDay >= 1 - while (!(firstDay <= dayOfWeek && dayOfWeek <= lastDay)) { - javaCalendar.add(java.util.Calendar.DATE, -1); - dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); - } - - newStart = javaCalendar.getTime(); - newEnd = javaCalendar.getTime(); - } - - setDates(event, newStart, newEnd); - } - - /** - * Set the start and end dates for the event - * - * @param event - * The event that the start and end dates should be set - * @param start - * The start date - * @param end - * The end date - */ - protected void setDates(BackwardEvent event, Date start, Date end) { - event.getComponent().setStartDate(start); - event.getComponent().setEndDate(end); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java deleted file mode 100644 index 0107e10e3d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java +++ /dev/null @@ -1,70 +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.components.calendar.handler; - -import java.util.Calendar; -import java.util.Date; - -import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler; - -/** - * Implements basic functionality needed to switch to day view when a single day - * is clicked. - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicDateClickHandler implements DateClickHandler { - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler - * #dateClick - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickEvent) - */ - @Override - public void dateClick(DateClickEvent event) { - Date clickedDate = event.getDate(); - - Calendar javaCalendar = event.getComponent().getInternalCalendar(); - javaCalendar.setTime(clickedDate); - - // as times are expanded, this is all that is needed to show one day - Date start = javaCalendar.getTime(); - Date end = javaCalendar.getTime(); - - setDates(event, start, end); - } - - /** - * Set the start and end dates for the event - * - * @param event - * The event that the start and end dates should be set - * @param start - * The start date - * @param end - * The end date - */ - protected void setDates(DateClickEvent event, Date start, Date end) { - event.getComponent().setStartDate(start); - event.getComponent().setEndDate(end); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java deleted file mode 100644 index 60f0016312..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java +++ /dev/null @@ -1,74 +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.components.calendar.handler; - -import java.util.Date; - -import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent; -import com.vaadin.ui.components.calendar.event.CalendarEvent; -import com.vaadin.ui.components.calendar.event.EditableCalendarEvent; - -/** - * Implements basic functionality needed to enable moving events. - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicEventMoveHandler implements EventMoveHandler { - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler - * #eventMove - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent) - */ - @Override - public void eventMove(MoveEvent event) { - CalendarEvent calendarEvent = event.getCalendarEvent(); - - if (calendarEvent instanceof EditableCalendarEvent) { - - EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent; - - Date newFromTime = event.getNewStart(); - - // Update event dates - long length = editableEvent.getEnd().getTime() - - editableEvent.getStart().getTime(); - setDates(editableEvent, newFromTime, - new Date(newFromTime.getTime() + length)); - } - } - - /** - * Set the start and end dates for the event - * - * @param event - * The event that the start and end dates should be set - * @param start - * The start date - * @param end - * The end date - */ - protected void setDates(EditableCalendarEvent event, Date start, Date end) { - event.setStart(start); - event.setEnd(end); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java deleted file mode 100644 index 51e4bc1cbc..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java +++ /dev/null @@ -1,70 +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.components.calendar.handler; - -import java.util.Date; - -import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; -import com.vaadin.ui.components.calendar.event.CalendarEvent; -import com.vaadin.ui.components.calendar.event.EditableCalendarEvent; - -/** - * Implements basic functionality needed to enable event resizing. - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicEventResizeHandler implements EventResizeHandler { - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler - * #eventResize - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize) - */ - @Override - public void eventResize(EventResize event) { - CalendarEvent calendarEvent = event.getCalendarEvent(); - - if (calendarEvent instanceof EditableCalendarEvent) { - Date newStartTime = event.getNewStart(); - Date newEndTime = event.getNewEnd(); - - EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent; - - setDates(editableEvent, newStartTime, newEndTime); - } - } - - /** - * Set the start and end dates for the event - * - * @param event - * The event that the start and end dates should be set - * @param start - * The start date - * @param end - * The end date - */ - protected void setDates(EditableCalendarEvent event, Date start, Date end) { - event.setStart(start); - event.setEnd(end); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java deleted file mode 100644 index 6cdc00bee2..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java +++ /dev/null @@ -1,94 +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.components.calendar.handler; - -import java.util.Calendar; -import java.util.Date; - -import com.vaadin.shared.ui.calendar.DateConstants; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler; - -/** - * Implements basic functionality needed to enable forward navigation. - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicForwardHandler implements ForwardHandler { - - /* - * (non-Javadoc) - * - * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler# - * forward - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardEvent) - */ - @Override - public void forward(ForwardEvent event) { - Date start = event.getComponent().getStartDate(); - Date end = event.getComponent().getEndDate(); - - // calculate amount to move forward - int durationInDays = (int) (((end.getTime()) - start.getTime()) - / DateConstants.DAYINMILLIS); - // for week view durationInDays = 7, for day view durationInDays = 1 - durationInDays++; - - // set new start and end times - Calendar javaCalendar = Calendar.getInstance(); - javaCalendar.setTime(start); - javaCalendar.add(java.util.Calendar.DATE, durationInDays); - Date newStart = javaCalendar.getTime(); - - javaCalendar.setTime(end); - javaCalendar.add(java.util.Calendar.DATE, durationInDays); - Date newEnd = javaCalendar.getTime(); - - if (start.equals(end)) { // day view - int firstDay = event.getComponent().getFirstVisibleDayOfWeek(); - int lastDay = event.getComponent().getLastVisibleDayOfWeek(); - int dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); - - // we suppose that 7 >= lastDay >= firstDay >= 1 - while (!(firstDay <= dayOfWeek && dayOfWeek <= lastDay)) { - javaCalendar.add(java.util.Calendar.DATE, 1); - dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); - } - - newStart = javaCalendar.getTime(); - newEnd = javaCalendar.getTime(); - } - - setDates(event, newStart, newEnd); - } - - /** - * Set the start and end dates for the event - * - * @param event - * The event that the start and end dates should be set - * @param start - * The start date - * @param end - * The end date - */ - protected void setDates(ForwardEvent event, Date start, Date end) { - event.getComponent().setStartDate(start); - event.getComponent().setEndDate(end); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java b/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java deleted file mode 100644 index 128c6abfbd..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.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.ui.components.calendar.handler; - -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; - -import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler; - -/** - * Implements basic functionality needed to change to week view when a week - * number is clicked. - * - * @since 7.1 - * @author Vaadin Ltd. - */ -@SuppressWarnings("serial") -public class BasicWeekClickHandler implements WeekClickHandler { - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler - * #weekClick - * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClick) - */ - @Override - public void weekClick(WeekClick event) { - int week = event.getWeek(); - int year = event.getYear(); - - // set correct year and month - Calendar javaCalendar = event.getComponent().getInternalCalendar(); - javaCalendar.set(GregorianCalendar.YEAR, year); - javaCalendar.set(GregorianCalendar.WEEK_OF_YEAR, week); - - // starting at the beginning of the week - javaCalendar.set(GregorianCalendar.DAY_OF_WEEK, - javaCalendar.getFirstDayOfWeek()); - Date start = javaCalendar.getTime(); - - // ending at the end of the week - javaCalendar.add(GregorianCalendar.DATE, 6); - Date end = javaCalendar.getTime(); - - setDates(event, start, end); - - // times are automatically expanded, no need to worry about them - } - - /** - * Set the start and end dates for the event - * - * @param event - * The event that the start and end dates should be set - * @param start - * The start date - * @param end - * The end date - */ - protected void setDates(WeekClick event, Date start, Date end) { - event.getComponent().setStartDate(start); - event.getComponent().setEndDate(end); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeEvent.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeEvent.java deleted file mode 100644 index aa703deb19..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeEvent.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.ui.components.colorpicker; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.ui.Component; -import com.vaadin.ui.Component.Event; - -/** - * The color changed event which is passed to the listeners when a color change - * occurs. - * - * @since 7.0.0 - */ -public class ColorChangeEvent extends Event { - private final Color color; - - public ColorChangeEvent(Component source, Color color) { - super(source); - - this.color = color; - } - - /** - * Returns the new color. - */ - public Color getColor() { - return color; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeListener.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeListener.java deleted file mode 100644 index b234dc3d5d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorChangeListener.java +++ /dev/null @@ -1,42 +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.components.colorpicker; - -import java.io.Serializable; - -/** - * The listener interface for receiving colorChange events. The class that is - * interested in processing a {@link ColorChangeEvent} implements this - * interface, and the object created with that class is registered with a - * component using the component's addColorChangeListener method. - * When the colorChange event occurs, that object's appropriate method is - * invoked. - * - * @since 7.0.0 - * - * @see ColorChangeEvent - */ -public interface ColorChangeListener extends Serializable { - - /** - * Called when a new color has been selected. - * - * @param event - * An event containing information about the color change. - */ - void colorChanged(ColorChangeEvent event); - -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGradient.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGradient.java deleted file mode 100644 index 23748a967a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGradient.java +++ /dev/null @@ -1,144 +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.components.colorpicker; - -import java.lang.reflect.Method; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.shared.ui.colorpicker.ColorPickerGradientServerRpc; -import com.vaadin.shared.ui.colorpicker.ColorPickerGradientState; -import com.vaadin.ui.AbstractColorPicker.Coordinates2Color; -import com.vaadin.ui.AbstractComponent; - -/** - * A component that represents a color gradient within a color picker. - * - * @since 7.0.0 - */ -public class ColorPickerGradient extends AbstractComponent - implements ColorSelector { - - private static final Method COLOR_CHANGE_METHOD; - static { - try { - COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( - "colorChanged", new Class[] { ColorChangeEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in ColorPicker"); - } - } - - private ColorPickerGradientServerRpc rpc = new ColorPickerGradientServerRpc() { - - @Override - public void select(int cursorX, int cursorY) { - x = cursorX; - y = cursorY; - color = converter.calculate(x, y); - - fireColorChanged(color); - } - }; - - /** The converter. */ - private Coordinates2Color converter; - - /** The foreground color. */ - private Color color; - - /** The x-coordinate. */ - private int x = 0; - - /** The y-coordinate. */ - private int y = 0; - - private ColorPickerGradient() { - registerRpc(rpc); - // width and height must be set here instead of in theme, otherwise - // coordinate calculations fail - getState().width = "220px"; - getState().height = "220px"; - } - - /** - * Instantiates a new color picker gradient. - * - * @param id - * the id - * @param converter - * the converter - */ - public ColorPickerGradient(String id, Coordinates2Color converter) { - this(); - addStyleName(id); - this.converter = converter; - } - - @Override - public void setColor(Color c) { - color = c; - - int[] coords = converter.calculate(c); - x = coords[0]; - y = coords[1]; - - getState().cursorX = x; - getState().cursorY = y; - - } - - @Override - public void addColorChangeListener(ColorChangeListener listener) { - addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); - } - - @Override - public void removeColorChangeListener(ColorChangeListener listener) { - removeListener(ColorChangeEvent.class, listener); - } - - /** - * Sets the background color. - * - * @param color - * the new background color - */ - public void setBackgroundColor(Color color) { - getState().bgColor = color.getCSS(); - } - - @Override - public Color getColor() { - return color; - } - - /** - * Notifies the listeners that the color has changed - * - * @param color - * The color which it changed to - */ - public void fireColorChanged(Color color) { - fireEvent(new ColorChangeEvent(this, color)); - } - - @Override - protected ColorPickerGradientState getState() { - return (ColorPickerGradientState) super.getState(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java deleted file mode 100644 index 9e5580c719..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java +++ /dev/null @@ -1,258 +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.components.colorpicker; - -import java.awt.Point; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.shared.ui.colorpicker.ColorPickerGridServerRpc; -import com.vaadin.shared.ui.colorpicker.ColorPickerGridState; -import com.vaadin.ui.AbstractComponent; - -/** - * A component that represents a color selection grid within a color picker. - * - * @since 7.0.0 - */ -public class ColorPickerGrid extends AbstractComponent - implements ColorSelector { - - private static final String STYLENAME = "v-colorpicker-grid"; - - private static final Method COLOR_CHANGE_METHOD; - static { - try { - COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( - "colorChanged", new Class[] { ColorChangeEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in ColorPicker"); - } - } - - private ColorPickerGridServerRpc rpc = new ColorPickerGridServerRpc() { - - @Override - public void select(int x, int y) { - ColorPickerGrid.this.x = x; - ColorPickerGrid.this.y = y; - - fireColorChanged(colorGrid[y][x]); - } - - @Override - public void refresh() { - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - changedColors.put(new Point(row, col), colorGrid[row][col]); - } - } - sendChangedColors(); - markAsDirty(); - } - }; - - /** The x-coordinate. */ - private int x = 0; - - /** The y-coordinate. */ - private int y = 0; - - /** The rows. */ - private int rows; - - /** The columns. */ - private int columns; - - /** The color grid. */ - private Color[][] colorGrid = new Color[1][1]; - - /** The changed colors. */ - private final Map changedColors = new HashMap(); - - /** - * Instantiates a new color picker grid. - */ - public ColorPickerGrid() { - registerRpc(rpc); - setPrimaryStyleName(STYLENAME); - setColorGrid(new Color[1][1]); - setColor(Color.WHITE); - } - - /** - * Instantiates a new color picker grid. - * - * @param rows - * the rows - * @param columns - * the columns - */ - public ColorPickerGrid(int rows, int columns) { - registerRpc(rpc); - setPrimaryStyleName(STYLENAME); - setColorGrid(new Color[rows][columns]); - setColor(Color.WHITE); - } - - /** - * Instantiates a new color picker grid. - * - * @param colors - * the colors - */ - public ColorPickerGrid(Color[][] colors) { - registerRpc(rpc); - setPrimaryStyleName(STYLENAME); - setColorGrid(colors); - } - - private void setColumnCount(int columns) { - this.columns = columns; - getState().columnCount = columns; - } - - private void setRowCount(int rows) { - this.rows = rows; - getState().rowCount = rows; - } - - private void sendChangedColors() { - if (!changedColors.isEmpty()) { - String[] colors = new String[changedColors.size()]; - String[] XCoords = new String[changedColors.size()]; - String[] YCoords = new String[changedColors.size()]; - int counter = 0; - for (Point p : changedColors.keySet()) { - Color c = changedColors.get(p); - if (c == null) { - continue; - } - - String color = c.getCSS(); - - colors[counter] = color; - XCoords[counter] = String.valueOf((int) p.getX()); - YCoords[counter] = String.valueOf((int) p.getY()); - counter++; - } - getState().changedColor = colors; - getState().changedX = XCoords; - getState().changedY = YCoords; - - changedColors.clear(); - } - } - - /** - * Sets the color grid. - * - * @param colors - * the new color grid - */ - public void setColorGrid(Color[][] colors) { - setRowCount(colors.length); - setColumnCount(colors[0].length); - colorGrid = colors; - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - changedColors.put(new Point(row, col), colorGrid[row][col]); - } - } - sendChangedColors(); - - markAsDirty(); - } - - /** - * Adds a color change listener - * - * @param listener - * The color change listener - */ - @Override - public void addColorChangeListener(ColorChangeListener listener) { - addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); - } - - @Override - public Color getColor() { - return colorGrid[x][y]; - } - - /** - * Removes a color change listener - * - * @param listener - * The listener - */ - @Override - public void removeColorChangeListener(ColorChangeListener listener) { - removeListener(ColorChangeEvent.class, listener); - } - - @Override - public void setColor(Color color) { - colorGrid[x][y] = color; - changedColors.put(new Point(x, y), color); - sendChangedColors(); - markAsDirty(); - } - - /** - * Sets the position. - * - * @param x - * the x - * @param y - * the y - */ - public void setPosition(int x, int y) { - if (x >= 0 && x < columns && y >= 0 && y < rows) { - this.x = x; - this.y = y; - } - } - - /** - * Gets the position. - * - * @return the position - */ - public int[] getPosition() { - return new int[] { x, y }; - } - - /** - * Notifies the listeners that a color change has occurred - * - * @param color - * The color which it changed to - */ - public void fireColorChanged(Color color) { - fireEvent(new ColorChangeEvent(this, color)); - } - - @Override - protected ColorPickerGridState getState() { - return (ColorPickerGridState) super.getState(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java deleted file mode 100644 index 1173faf152..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java +++ /dev/null @@ -1,217 +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.components.colorpicker; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.ui.CustomComponent; - -/** - * A component that represents color selection history within a color picker. - * - * @since 7.0.0 - */ -public class ColorPickerHistory extends CustomComponent - implements ColorSelector, ColorChangeListener { - - private static final String STYLENAME = "v-colorpicker-history"; - - private static final Method COLOR_CHANGE_METHOD; - static { - try { - COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( - "colorChanged", new Class[] { ColorChangeEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in ColorPicker"); - } - } - - /** The rows. */ - private static final int rows = 4; - - /** The columns. */ - private static final int columns = 15; - - /** Temporary color history for when the component is detached. */ - private ArrayBlockingQueue tempHistory = new ArrayBlockingQueue( - rows * columns); - - /** The grid. */ - private final ColorPickerGrid grid; - - /** - * Instantiates a new color picker history. - */ - public ColorPickerHistory() { - setPrimaryStyleName(STYLENAME); - - grid = new ColorPickerGrid(rows, columns); - grid.setWidth("100%"); - grid.setPosition(0, 0); - grid.addColorChangeListener(this); - - setCompositionRoot(grid); - } - - @Override - public void attach() { - super.attach(); - createColorHistoryIfNecessary(); - } - - private void createColorHistoryIfNecessary() { - List tempColors = new ArrayList(tempHistory); - if (getSession().getAttribute("colorPickerHistory") == null) { - getSession().setAttribute("colorPickerHistory", - new ArrayBlockingQueue(rows * columns)); - } - for (Color color : tempColors) { - setColor(color); - } - tempHistory.clear(); - } - - @SuppressWarnings("unchecked") - private ArrayBlockingQueue getColorHistory() { - if (isAttached()) { - Object colorHistory = getSession() - .getAttribute("colorPickerHistory"); - if (colorHistory instanceof ArrayBlockingQueue) { - return (ArrayBlockingQueue) colorHistory; - } - } - return tempHistory; - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - grid.setHeight(height); - } - - @Override - public void setColor(Color color) { - - ArrayBlockingQueue colorHistory = getColorHistory(); - - // Check that the color does not already exist - boolean exists = false; - Iterator iter = colorHistory.iterator(); - while (iter.hasNext()) { - if (color.equals(iter.next())) { - exists = true; - break; - } - } - - // If the color does not exist then add it - if (!exists) { - if (!colorHistory.offer(color)) { - colorHistory.poll(); - colorHistory.offer(color); - } - } - - List colorList = new ArrayList(colorHistory); - - // Invert order of colors - Collections.reverse(colorList); - - // Move the selected color to the front of the list - Collections.swap(colorList, colorList.indexOf(color), 0); - - // Create 2d color map - Color[][] colors = new Color[rows][columns]; - iter = colorList.iterator(); - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - if (iter.hasNext()) { - colors[row][col] = iter.next(); - } else { - colors[row][col] = Color.WHITE; - } - } - } - - grid.setColorGrid(colors); - grid.markAsDirty(); - } - - @Override - public Color getColor() { - return getColorHistory().peek(); - } - - /** - * Gets the history. - * - * @return the history - */ - public List getHistory() { - ArrayBlockingQueue colorHistory = getColorHistory(); - Color[] array = colorHistory.toArray(new Color[colorHistory.size()]); - return Collections.unmodifiableList(Arrays.asList(array)); - } - - /** - * Checks if the history contains given color. - * - * @param c - * the color - * - * @return true, if successful - */ - public boolean hasColor(Color c) { - return getColorHistory().contains(c); - } - - /** - * Adds a color change listener - * - * @param listener - * The listener - */ - @Override - public void addColorChangeListener(ColorChangeListener listener) { - addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); - } - - /** - * Removes a color change listener - * - * @param listener - * The listener - */ - @Override - public void removeColorChangeListener(ColorChangeListener listener) { - removeListener(ColorChangeEvent.class, listener); - } - - @Override - public void colorChanged(ColorChangeEvent event) { - fireEvent(new ColorChangeEvent(this, event.getColor())); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java deleted file mode 100644 index dbf3b18bf3..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java +++ /dev/null @@ -1,759 +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.components.colorpicker; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.shared.ui.MarginInfo; -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.ui.AbstractColorPicker.Coordinates2Color; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.ui.Button.ClickListener; -import com.vaadin.ui.Component; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.Layout; -import com.vaadin.ui.Slider; -import com.vaadin.ui.Slider.ValueOutOfBoundsException; -import com.vaadin.ui.TabSheet; -import com.vaadin.ui.VerticalLayout; -import com.vaadin.ui.Window; - -/** - * A component that represents color selection popup within a color picker. - * - * @since 7.0.0 - */ -public class ColorPickerPopup extends Window - implements ClickListener, ColorChangeListener, ColorSelector { - - private static final String STYLENAME = "v-colorpicker-popup"; - - private static final Method COLOR_CHANGE_METHOD; - static { - try { - COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( - "colorChanged", new Class[] { ColorChangeEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in ColorPicker"); - } - } - - /** The tabs. */ - private final TabSheet tabs = new TabSheet(); - - private Component rgbTab; - - private Component hsvTab; - - private Component swatchesTab; - - /** The layout. */ - private final VerticalLayout layout; - - /** The ok button. */ - private final Button ok = new Button("OK"); - - /** The cancel button. */ - private final Button cancel = new Button("Cancel"); - - /** The resize button. */ - private final Button resize = new Button("show/hide history"); - - /** The selected color. */ - private Color selectedColor = Color.WHITE; - - /** The history. */ - private ColorPickerHistory history; - - /** The history container. */ - private Layout historyContainer; - - /** The rgb gradient. */ - private ColorPickerGradient rgbGradient; - - /** The hsv gradient. */ - private ColorPickerGradient hsvGradient; - - /** The red slider. */ - private Slider redSlider; - - /** The green slider. */ - private Slider greenSlider; - - /** The blue slider. */ - private Slider blueSlider; - - /** The hue slider. */ - private Slider hueSlider; - - /** The saturation slider. */ - private Slider saturationSlider; - - /** The value slider. */ - private Slider valueSlider; - - /** The preview on the rgb tab. */ - private ColorPickerPreview rgbPreview; - - /** The preview on the hsv tab. */ - private ColorPickerPreview hsvPreview; - - /** The preview on the swatches tab. */ - private ColorPickerPreview selPreview; - - /** The color select. */ - private ColorPickerSelect colorSelect; - - /** The selectors. */ - private final Set selectors = new HashSet(); - - /** - * Set true while the slider values are updated after colorChange. When - * true, valueChange reactions from the sliders are disabled, because - * otherwise the set color may become corrupted as it is repeatedly re-set - * in valueChangeListeners using values from sliders that may not have been - * updated yet. - */ - private boolean updatingColors = false; - - private ColorPickerPopup() { - // Set the layout - layout = new VerticalLayout(); - layout.setSpacing(false); - layout.setMargin(false); - layout.setWidth("100%"); - layout.setHeight(null); - - setContent(layout); - setStyleName(STYLENAME); - setResizable(false); - setImmediate(true); - // Create the history - history = new ColorPickerHistory(); - history.addColorChangeListener(this); - } - - /** - * Instantiates a new color picker popup. - */ - public ColorPickerPopup(Color initialColor) { - this(); - selectedColor = initialColor; - initContents(); - } - - private void initContents() { - // Create the preview on the rgb tab - rgbPreview = new ColorPickerPreview(selectedColor); - rgbPreview.setWidth("240px"); - rgbPreview.setHeight("20px"); - rgbPreview.addColorChangeListener(this); - selectors.add(rgbPreview); - - // Create the preview on the hsv tab - hsvPreview = new ColorPickerPreview(selectedColor); - hsvPreview.setWidth("240px"); - hsvPreview.setHeight("20px"); - hsvPreview.addColorChangeListener(this); - selectors.add(hsvPreview); - - // Create the preview on the swatches tab - selPreview = new ColorPickerPreview(selectedColor); - selPreview.setWidth("100%"); - selPreview.setHeight("20px"); - selPreview.addColorChangeListener(this); - selectors.add(selPreview); - - // Create the tabs - rgbTab = createRGBTab(selectedColor); - tabs.addTab(rgbTab, "RGB", null); - - hsvTab = createHSVTab(selectedColor); - tabs.addTab(hsvTab, "HSV", null); - - swatchesTab = createSelectTab(); - tabs.addTab(swatchesTab, "Swatches", null); - - // Add the tabs - tabs.setWidth("100%"); - - layout.addComponent(tabs); - - // Add the history - history.setWidth("97%"); - history.setHeight("22px"); - - // Create the default colors - List defaultColors = new ArrayList(); - defaultColors.add(Color.BLACK); - defaultColors.add(Color.WHITE); - - // Create the history - VerticalLayout innerContainer = new VerticalLayout(); - innerContainer.setWidth("100%"); - innerContainer.setHeight(null); - innerContainer.addComponent(history); - - VerticalLayout outerContainer = new VerticalLayout(); - outerContainer.setWidth("99%"); - outerContainer.setHeight("27px"); - outerContainer.addComponent(innerContainer); - historyContainer = outerContainer; - - layout.addComponent(historyContainer); - - // Add the resize button for the history - resize.addClickListener(this); - resize.setData(new Boolean(false)); - resize.setWidth("100%"); - resize.setHeight("10px"); - resize.setPrimaryStyleName("resize-button"); - layout.addComponent(resize); - - // Add the buttons - ok.setWidth("70px"); - ok.addClickListener(this); - - cancel.setWidth("70px"); - cancel.addClickListener(this); - - HorizontalLayout buttons = new HorizontalLayout(); - buttons.addComponent(ok); - buttons.addComponent(cancel); - buttons.setWidth("100%"); - buttons.setHeight("30px"); - buttons.setComponentAlignment(ok, Alignment.MIDDLE_CENTER); - buttons.setComponentAlignment(cancel, Alignment.MIDDLE_CENTER); - layout.addComponent(buttons); - } - - /** - * Creates the RGB tab. - * - * @return the component - */ - private Component createRGBTab(Color color) { - VerticalLayout rgbLayout = new VerticalLayout(); - rgbLayout.setMargin(new MarginInfo(false, false, true, false)); - rgbLayout.addComponent(rgbPreview); - rgbLayout.setStyleName("rgbtab"); - - // Add the RGB color gradient - rgbGradient = new ColorPickerGradient("rgb-gradient", RGBConverter); - rgbGradient.setColor(color); - rgbGradient.addColorChangeListener(this); - rgbLayout.addComponent(rgbGradient); - selectors.add(rgbGradient); - - // Add the RGB sliders - VerticalLayout sliders = new VerticalLayout(); - sliders.setStyleName("rgb-sliders"); - - redSlider = createRGBSlider("Red", "red"); - greenSlider = createRGBSlider("Green", "green"); - blueSlider = createRGBSlider("Blue", "blue"); - setRgbSliderValues(color); - - redSlider.addValueChangeListener(e -> { - double red = e.getValue(); - if (!updatingColors) { - Color newColor = new Color((int) red, selectedColor.getGreen(), - selectedColor.getBlue()); - setColor(newColor); - } - }); - - sliders.addComponent(redSlider); - - greenSlider.addValueChangeListener(e -> { - double green = e.getValue(); - if (!updatingColors) { - Color newColor = new Color(selectedColor.getRed(), (int) green, - selectedColor.getBlue()); - setColor(newColor); - } - }); - sliders.addComponent(greenSlider); - - blueSlider.addValueChangeListener(e -> { - double blue = e.getValue(); - if (!updatingColors) { - Color newColor = new Color(selectedColor.getRed(), - selectedColor.getGreen(), (int) blue); - setColor(newColor); - } - }); - sliders.addComponent(blueSlider); - - rgbLayout.addComponent(sliders); - - return rgbLayout; - } - - private Slider createRGBSlider(String caption, String styleName) { - Slider redSlider = new Slider(caption, 0, 255); - redSlider.setImmediate(true); - redSlider.setStyleName("rgb-slider"); - redSlider.setWidth("220px"); - redSlider.addStyleName(styleName); - return redSlider; - } - - /** - * Creates the hsv tab. - * - * @return the component - */ - private Component createHSVTab(Color color) { - VerticalLayout hsvLayout = new VerticalLayout(); - hsvLayout.setMargin(new MarginInfo(false, false, true, false)); - hsvLayout.addComponent(hsvPreview); - hsvLayout.setStyleName("hsvtab"); - - // Add the hsv gradient - hsvGradient = new ColorPickerGradient("hsv-gradient", HSVConverter); - hsvGradient.setColor(color); - hsvGradient.addColorChangeListener(this); - hsvLayout.addComponent(hsvGradient); - selectors.add(hsvGradient); - - VerticalLayout sliders = new VerticalLayout(); - sliders.setStyleName("hsv-sliders"); - - hueSlider = new Slider("Hue", 0, 360); - saturationSlider = new Slider("Saturation", 0, 100); - valueSlider = new Slider("Value", 0, 100); - - float[] hsv = color.getHSV(); - setHsvSliderValues(hsv); - - hueSlider.setStyleName("hsv-slider"); - hueSlider.addStyleName("hue-slider"); - hueSlider.setWidth("220px"); - hueSlider.setImmediate(true); - hueSlider.addValueChangeListener(event -> { - if (!updatingColors) { - float hue = (Float.parseFloat(event.getValue().toString())) - / 360f; - float saturation = (Float - .parseFloat(saturationSlider.getValue().toString())) - / 100f; - float value = (Float - .parseFloat(valueSlider.getValue().toString())) / 100f; - - // Set the color - Color newColor = new Color( - Color.HSVtoRGB(hue, saturation, value)); - setColor(newColor); - - /* - * Set the background color of the hue gradient. This has to be - * done here since in the conversion the base color information - * is lost when color is black/white - */ - Color bgColor = new Color(Color.HSVtoRGB(hue, 1f, 1f)); - hsvGradient.setBackgroundColor(bgColor); - } - }); - sliders.addComponent(hueSlider); - - saturationSlider.setStyleName("hsv-slider"); - saturationSlider.setWidth("220px"); - saturationSlider.setImmediate(true); - saturationSlider.addValueChangeListener(event -> { - if (!updatingColors) { - float hue = (Float.parseFloat(hueSlider.getValue().toString())) - / 360f; - float saturation = (Float - .parseFloat(event.getValue().toString())) / 100f; - float value = (Float - .parseFloat(valueSlider.getValue().toString())) / 100f; - Color newColor = new Color( - Color.HSVtoRGB(hue, saturation, value)); - setColor(newColor); - } - }); - sliders.addComponent(saturationSlider); - - valueSlider.setStyleName("hsv-slider"); - valueSlider.setWidth("220px"); - valueSlider.setImmediate(true); - valueSlider.addValueChangeListener(event -> { - if (!updatingColors) { - float hue = (Float.parseFloat(hueSlider.getValue().toString())) - / 360f; - float saturation = (Float - .parseFloat(saturationSlider.getValue().toString())) - / 100f; - float value = (Float.parseFloat(event.getValue().toString())) - / 100f; - - Color newColor = new Color( - Color.HSVtoRGB(hue, saturation, value)); - setColor(newColor); - } - }); - - sliders.addComponent(valueSlider); - hsvLayout.addComponent(sliders); - - return hsvLayout; - } - - /** - * Creates the select tab. - * - * @return the component - */ - private Component createSelectTab() { - VerticalLayout selLayout = new VerticalLayout(); - selLayout.setMargin(new MarginInfo(false, false, true, false)); - selLayout.addComponent(selPreview); - selLayout.addStyleName("seltab"); - - colorSelect = new ColorPickerSelect(); - colorSelect.addColorChangeListener(this); - selLayout.addComponent(colorSelect); - - return selLayout; - } - - @Override - public void buttonClick(ClickEvent event) { - // History resize was clicked - if (event.getButton() == resize) { - boolean state = (Boolean) resize.getData(); - - // minimize - if (state) { - historyContainer.setHeight("27px"); - history.setHeight("22px"); - - // maximize - } else { - historyContainer.setHeight("90px"); - history.setHeight("85px"); - } - - resize.setData(new Boolean(!state)); - } - - // Ok button was clicked - else if (event.getButton() == ok) { - history.setColor(getColor()); - fireColorChanged(); - close(); - } - - // Cancel button was clicked - else if (event.getButton() == cancel) { - close(); - } - - } - - /** - * Notifies the listeners that the color changed - */ - public void fireColorChanged() { - fireEvent(new ColorChangeEvent(this, getColor())); - } - - /** - * Gets the history. - * - * @return the history - */ - public ColorPickerHistory getHistory() { - return history; - } - - @Override - public void setColor(Color color) { - if (color == null) { - return; - } - - selectedColor = color; - - hsvGradient.setColor(selectedColor); - hsvPreview.setColor(selectedColor); - - rgbGradient.setColor(selectedColor); - rgbPreview.setColor(selectedColor); - - selPreview.setColor(selectedColor); - } - - @Override - public Color getColor() { - return selectedColor; - } - - /** - * Gets the color history. - * - * @return the color history - */ - public List getColorHistory() { - return Collections.unmodifiableList(history.getHistory()); - } - - @Override - public void colorChanged(ColorChangeEvent event) { - setColor(event.getColor()); - - updatingColors = true; - - setRgbSliderValues(selectedColor); - float[] hsv = selectedColor.getHSV(); - setHsvSliderValues(hsv); - - updatingColors = false; - - for (ColorSelector s : selectors) { - if (event.getSource() != s && s != this - && s.getColor() != selectedColor) { - s.setColor(selectedColor); - } - } - } - - private void setRgbSliderValues(Color color) { - try { - redSlider.setValue(((Integer) color.getRed()).doubleValue()); - blueSlider.setValue(((Integer) color.getBlue()).doubleValue()); - greenSlider.setValue(((Integer) color.getGreen()).doubleValue()); - } catch (ValueOutOfBoundsException e) { - getLogger().log(Level.WARNING, - "Unable to set RGB color value to " + color.getRed() + "," - + color.getGreen() + "," + color.getBlue(), - e); - } - } - - private void setHsvSliderValues(float[] hsv) { - try { - hueSlider.setValue(((Float) (hsv[0] * 360f)).doubleValue()); - saturationSlider.setValue(((Float) (hsv[1] * 100f)).doubleValue()); - valueSlider.setValue(((Float) (hsv[2] * 100f)).doubleValue()); - } catch (ValueOutOfBoundsException e) { - getLogger().log(Level.WARNING, "Unable to set HSV color value to " - + hsv[0] + "," + hsv[1] + "," + hsv[2], e); - } - } - - @Override - public void addColorChangeListener(ColorChangeListener listener) { - addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); - } - - @Override - public void removeColorChangeListener(ColorChangeListener listener) { - removeListener(ColorChangeEvent.class, listener); - } - - /** - * Checks the visibility of the given tab - * - * @param tab - * The tab to check - * @return true if tab is visible, false otherwise - */ - private boolean tabIsVisible(Component tab) { - Iterator tabIterator = tabs.getComponentIterator(); - while (tabIterator.hasNext()) { - if (tabIterator.next() == tab) { - return true; - } - } - return false; - } - - /** - * How many tabs are visible - * - * @return The number of tabs visible - */ - private int tabsNumVisible() { - Iterator tabIterator = tabs.getComponentIterator(); - int tabCounter = 0; - while (tabIterator.hasNext()) { - tabIterator.next(); - tabCounter++; - } - return tabCounter; - } - - /** - * Checks if tabs are needed and hides them if not - */ - private void checkIfTabsNeeded() { - tabs.hideTabs(tabsNumVisible() == 1); - } - - /** - * Set RGB tab visibility - * - * @param visible - * The visibility of the RGB tab - */ - public void setRGBTabVisible(boolean visible) { - if (visible && !tabIsVisible(rgbTab)) { - tabs.addTab(rgbTab, "RGB", null); - checkIfTabsNeeded(); - } else if (!visible && tabIsVisible(rgbTab)) { - tabs.removeComponent(rgbTab); - checkIfTabsNeeded(); - } - } - - /** - * Set HSV tab visibility - * - * @param visible - * The visibility of the HSV tab - */ - public void setHSVTabVisible(boolean visible) { - if (visible && !tabIsVisible(hsvTab)) { - tabs.addTab(hsvTab, "HSV", null); - checkIfTabsNeeded(); - } else if (!visible && tabIsVisible(hsvTab)) { - tabs.removeComponent(hsvTab); - checkIfTabsNeeded(); - } - } - - /** - * Set Swatches tab visibility - * - * @param visible - * The visibility of the Swatches tab - */ - public void setSwatchesTabVisible(boolean visible) { - if (visible && !tabIsVisible(swatchesTab)) { - tabs.addTab(swatchesTab, "Swatches", null); - checkIfTabsNeeded(); - } else if (!visible && tabIsVisible(swatchesTab)) { - tabs.removeComponent(swatchesTab); - checkIfTabsNeeded(); - } - } - - /** - * Set the History visibility - * - * @param visible - */ - public void setHistoryVisible(boolean visible) { - historyContainer.setVisible(visible); - resize.setVisible(visible); - } - - /** - * Set the preview visibility - * - * @param visible - */ - public void setPreviewVisible(boolean visible) { - hsvPreview.setVisible(visible); - rgbPreview.setVisible(visible); - selPreview.setVisible(visible); - } - - /** RGB color converter */ - private Coordinates2Color RGBConverter = new Coordinates2Color() { - - @Override - public Color calculate(int x, int y) { - float h = (x / 220f); - float s = 1f; - float v = 1f; - - if (y < 110) { - s = y / 110f; - } else if (y > 110) { - v = 1f - (y - 110f) / 110f; - } - - return new Color(Color.HSVtoRGB(h, s, v)); - } - - @Override - public int[] calculate(Color color) { - - float[] hsv = color.getHSV(); - - int x = Math.round(hsv[0] * 220f); - int y = 0; - - // lower half - if (hsv[1] == 1f) { - y = Math.round(110f - (hsv[1] + hsv[2]) * 110f); - } else { - y = Math.round(hsv[1] * 110f); - } - - return new int[] { x, y }; - } - }; - - /** HSV color converter */ - Coordinates2Color HSVConverter = new Coordinates2Color() { - @Override - public int[] calculate(Color color) { - - float[] hsv = color.getHSV(); - - // Calculate coordinates - int x = Math.round(hsv[2] * 220.0f); - int y = Math.round(220 - hsv[1] * 220.0f); - - // Create background color of clean color - Color bgColor = new Color(Color.HSVtoRGB(hsv[0], 1f, 1f)); - hsvGradient.setBackgroundColor(bgColor); - - return new int[] { x, y }; - } - - @Override - public Color calculate(int x, int y) { - float saturation = 1f - (y / 220.0f); - float value = (x / 220.0f); - float hue = Float.parseFloat(hueSlider.getValue().toString()) - / 360f; - - Color color = new Color(Color.HSVtoRGB(hue, saturation, value)); - return color; - } - }; - - private static Logger getLogger() { - return Logger.getLogger(ColorPickerPopup.class.getName()); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java deleted file mode 100644 index dc133ce156..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerPreview.java +++ /dev/null @@ -1,198 +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.components.colorpicker; - -import java.lang.reflect.Method; - -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.ui.Component; -import com.vaadin.ui.CssLayout; -import com.vaadin.v7.ui.LegacyTextField; - -/** - * A component that represents color selection preview within a color picker. - * - * @since 7.0.0 - */ -public class ColorPickerPreview extends CssLayout - implements ColorSelector, ValueChangeListener { - - private static final String STYLE_DARK_COLOR = "v-textfield-dark"; - private static final String STYLE_LIGHT_COLOR = "v-textfield-light"; - - private static final Method COLOR_CHANGE_METHOD; - static { - try { - COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( - "colorChanged", new Class[] { ColorChangeEvent.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in ColorPicker"); - } - } - - /** The color. */ - private Color color; - - /** The field. */ - private final LegacyTextField field; - - /** The old value. */ - private String oldValue; - - private ColorPickerPreview() { - setStyleName("v-colorpicker-preview"); - setImmediate(true); - field = new LegacyTextField(); - field.setImmediate(true); - field.setSizeFull(); - field.setStyleName("v-colorpicker-preview-textfield"); - field.setData(this); - field.addValueChangeListener(this); - addComponent(field); - } - - /** - * Instantiates a new color picker preview. - */ - public ColorPickerPreview(Color color) { - this(); - setColor(color); - } - - @Override - public void setColor(Color color) { - this.color = color; - - // Unregister listener - field.removeValueChangeListener(this); - - String colorCSS = color.getCSS(); - field.setValue(colorCSS); - - if (field.isValid()) { - oldValue = colorCSS; - } else { - field.setValue(oldValue); - } - - // Re-register listener - field.addValueChangeListener(this); - - // Set the text color - field.removeStyleName(STYLE_DARK_COLOR); - field.removeStyleName(STYLE_LIGHT_COLOR); - if (this.color.getRed() + this.color.getGreen() - + this.color.getBlue() < 3 * 128) { - field.addStyleName(STYLE_DARK_COLOR); - } else { - field.addStyleName(STYLE_LIGHT_COLOR); - } - - markAsDirty(); - } - - @Override - public Color getColor() { - return color; - } - - @Override - public void addColorChangeListener(ColorChangeListener listener) { - addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); - } - - @Override - public void removeColorChangeListener(ColorChangeListener listener) { - removeListener(ColorChangeEvent.class, listener); - } - - @Override - public void valueChange(ValueChangeEvent event) { - String value = (String) event.getProperty().getValue(); - try { - if (value != null) { - /* - * Description of supported formats see - * http://www.w3schools.com/cssref/css_colors_legal.asp - */ - if (value.length() == 7 && value.startsWith("#")) { - // CSS color format (e.g. #000000) - int red = Integer.parseInt(value.substring(1, 3), 16); - int green = Integer.parseInt(value.substring(3, 5), 16); - int blue = Integer.parseInt(value.substring(5, 7), 16); - color = new Color(red, green, blue); - - } else if (value.startsWith("rgb")) { - // RGB color format rgb/rgba(255,255,255,0.1) - String[] colors = value.substring(value.indexOf("(") + 1, - value.length() - 1).split(","); - - int red = Integer.parseInt(colors[0]); - int green = Integer.parseInt(colors[1]); - int blue = Integer.parseInt(colors[2]); - if (colors.length > 3) { - int alpha = (int) (Double.parseDouble(colors[3]) - * 255d); - color = new Color(red, green, blue, alpha); - } else { - color = new Color(red, green, blue); - } - - } else if (value.startsWith("hsl")) { - // HSL color format hsl/hsla(100,50%,50%,1.0) - String[] colors = value.substring(value.indexOf("(") + 1, - value.length() - 1).split(","); - - int hue = Integer.parseInt(colors[0]); - int saturation = Integer - .parseInt(colors[1].replace("%", "")); - int lightness = Integer - .parseInt(colors[2].replace("%", "")); - int rgb = Color.HSLtoRGB(hue, saturation, lightness); - - if (colors.length > 3) { - int alpha = (int) (Double.parseDouble(colors[3]) - * 255d); - color = new Color(rgb); - color.setAlpha(alpha); - } else { - color = new Color(rgb); - } - } - - oldValue = value; - fireEvent(new ColorChangeEvent((Component) field.getData(), - color)); - } - - } catch (NumberFormatException nfe) { - // Revert value - field.setValue(oldValue); - } - } - - /** - * Called when the component is refreshing - */ - @Override - protected String getCss(Component c) { - return "background: " + color.getCSS(); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java deleted file mode 100644 index ae3dee4069..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorPickerSelect.java +++ /dev/null @@ -1,235 +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.components.colorpicker; - -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.ui.ComboBox; -import com.vaadin.ui.CustomComponent; -import com.vaadin.ui.VerticalLayout; - -/** - * A component that represents color selection swatches within a color picker. - * - * @since 7.0.0 - */ -public class ColorPickerSelect extends CustomComponent - implements ColorSelector, ValueChangeListener { - - /** The range. */ - private final ComboBox range; - - /** The grid. */ - private final ColorPickerGrid grid; - - /** - * The Enum ColorRangePropertyId. - */ - private enum ColorRangePropertyId { - ALL("All colors"), RED("Red colors"), GREEN("Green colors"), BLUE( - "Blue colors"); - - /** The caption. */ - private String caption; - - /** - * Instantiates a new color range property id. - * - * @param caption - * the caption - */ - ColorRangePropertyId(String caption) { - this.caption = caption; - } - - @Override - public String toString() { - return caption; - } - } - - /** - * Instantiates a new color picker select. - * - * @param rows - * the rows - * @param columns - * the columns - */ - public ColorPickerSelect() { - - VerticalLayout layout = new VerticalLayout(); - setCompositionRoot(layout); - - setStyleName("colorselect"); - setWidth("100%"); - - range = new ComboBox(); - range.setImmediate(true); - range.setImmediate(true); - range.setNullSelectionAllowed(false); - range.setNewItemsAllowed(false); - range.setWidth("100%"); - range.addValueChangeListener(this); - - for (ColorRangePropertyId id : ColorRangePropertyId.values()) { - range.addItem(id); - } - range.select(ColorRangePropertyId.ALL); - - layout.addComponent(range); - - grid = new ColorPickerGrid(createAllColors(14, 10)); - grid.setWidth("100%"); - - layout.addComponent(grid); - } - - /** - * Creates the all colors. - * - * @param rows - * the rows - * @param columns - * the columns - * - * @return the color[][] - */ - private Color[][] createAllColors(int rows, int columns) { - Color[][] colors = new Color[rows][columns]; - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - - // Create the color grid by varying the saturation and value - if (row < (rows - 1)) { - // Calculate new hue value - float hue = ((float) col / (float) columns); - float saturation = 1f; - float value = 1f; - - // For the upper half use value=1 and variable - // saturation - if (row < (rows / 2)) { - saturation = ((row + 1f) / (rows / 2f)); - } else { - value = 1f - ((row - (rows / 2f)) / (rows / 2f)); - } - - colors[row][col] = new Color( - Color.HSVtoRGB(hue, saturation, value)); - } - - // The last row should have the black&white gradient - else { - float hue = 0f; - float saturation = 0f; - float value = 1f - ((float) col / (float) columns); - - colors[row][col] = new Color( - Color.HSVtoRGB(hue, saturation, value)); - } - } - } - - return colors; - } - - /** - * Creates the color. - * - * @param color - * the color - * @param rows - * the rows - * @param columns - * the columns - * - * @return the color[][] - */ - private Color[][] createColors(Color color, int rows, int columns) { - Color[][] colors = new Color[rows][columns]; - - float[] hsv = color.getHSV(); - - float hue = hsv[0]; - float saturation = 1f; - float value = 1f; - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < columns; col++) { - - int index = row * columns + col; - saturation = 1f; - value = 1f; - - if (index <= ((rows * columns) / 2)) { - saturation = index - / (((float) rows * (float) columns) / 2f); - } else { - index -= ((rows * columns) / 2); - value = 1f - - index / (((float) rows * (float) columns) / 2f); - } - - colors[row][col] = new Color( - Color.HSVtoRGB(hue, saturation, value)); - } - } - - return colors; - } - - @Override - public Color getColor() { - return grid.getColor(); - } - - @Override - public void setColor(Color color) { - grid.getColor(); - } - - @Override - public void addColorChangeListener(ColorChangeListener listener) { - grid.addColorChangeListener(listener); - } - - @Override - public void removeColorChangeListener(ColorChangeListener listener) { - grid.removeColorChangeListener(listener); - } - - @Override - public void valueChange(ValueChangeEvent event) { - if (grid == null) { - return; - } - - if (event.getProperty().getValue() == ColorRangePropertyId.ALL) { - grid.setColorGrid(createAllColors(14, 10)); - } else if (event.getProperty().getValue() == ColorRangePropertyId.RED) { - grid.setColorGrid(createColors(new Color(0xFF, 0, 0), 14, 10)); - } else if (event.getProperty() - .getValue() == ColorRangePropertyId.GREEN) { - grid.setColorGrid(createColors(new Color(0, 0xFF, 0), 14, 10)); - } else if (event.getProperty() - .getValue() == ColorRangePropertyId.BLUE) { - grid.setColorGrid(createColors(new Color(0, 0, 0xFF), 14, 10)); - } - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorSelector.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorSelector.java deleted file mode 100644 index d9264745a8..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/ColorSelector.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.ui.components.colorpicker; - -import java.io.Serializable; - -import com.vaadin.shared.ui.colorpicker.Color; - -/** - * An interface for a color selector. - * - * @since 7.0.0 - */ -public interface ColorSelector extends Serializable, HasColorChangeListener { - - /** - * Sets the color. - * - * @param color - * the new color - */ - public void setColor(Color color); - - /** - * Gets the color. - * - * @return the color - */ - public Color getColor(); -} diff --git a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/HasColorChangeListener.java b/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/HasColorChangeListener.java deleted file mode 100644 index 7980111e2b..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/components/colorpicker/HasColorChangeListener.java +++ /dev/null @@ -1,36 +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.components.colorpicker; - -import java.io.Serializable; - -public interface HasColorChangeListener extends Serializable { - - /** - * Adds a {@link ColorChangeListener} to the component. - * - * @param listener - */ - void addColorChangeListener(ColorChangeListener listener); - - /** - * Removes a {@link ColorChangeListener} from the component. - * - * @param listener - */ - void removeColorChangeListener(ColorChangeListener listener); - -} 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 deleted file mode 100644 index ac9ffb0eeb..0000000000 --- a/compatibility-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.JavaScriptFunction; -import com.vaadin.ui.LegacyGrid.AbstractRenderer; - -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 deleted file mode 100644 index 3d7d5ab9d2..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/renderers/ButtonRenderer.java +++ /dev/null @@ -1,74 +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; - -/** - * 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 deleted file mode 100644 index d8fcdc3b4e..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/DateRenderer.java deleted file mode 100644 index eea18e7445..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java deleted file mode 100644 index 85d7801c41..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/renderers/HtmlRenderer.java +++ /dev/null @@ -1,48 +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; - -/** - * 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 deleted file mode 100644 index 56e319b47d..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/NumberRenderer.java deleted file mode 100644 index e54eecc6ef..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/ProgressBarRenderer.java deleted file mode 100644 index fe90dfdee0..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/renderers/Renderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/Renderer.java deleted file mode 100644 index d42e299ea8..0000000000 --- a/compatibility-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/compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java b/compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.java deleted file mode 100644 index 799e67b9e7..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/ui/renderers/TextRenderer.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; - -/** - * 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/main/java/com/vaadin/v7/data/fieldgroup/BeanFieldGroup.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/BeanFieldGroup.java new file mode 100644 index 0000000000..bee3f7c80b --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.data.fieldgroup; + +import java.beans.IntrospectionException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import com.vaadin.data.util.BeanUtil; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.util.BeanItem; +import com.vaadin.v7.data.validator.BeanValidator; +import com.vaadin.v7.ui.Field; + +public class BeanFieldGroup extends FieldGroup { + + private final Class beanType; + + private static Boolean beanValidationImplementationAvailable = null; + private final Map, BeanValidator> defaultValidators; + + public BeanFieldGroup(Class beanType) { + this.beanType = beanType; + this.defaultValidators = new HashMap, BeanValidator>(); + } + + @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(Field 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(Field field) throws BindException { + super.unbind(field); + + BeanValidator removed = defaultValidators.remove(field); + if (removed != null) { + field.removeValidator(removed); + } + } + + @Override + protected void configureField(Field field) { + super.configureField(field); + // Add Bean validators if there are annotations + if (isBeanValidationImplementationAvailable() + && !defaultValidators.containsKey(field)) { + BeanValidator validator = new BeanValidator(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 Field}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 Field}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/v7/data/fieldgroup/Caption.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/Caption.java new file mode 100644 index 0000000000..0042cd8113 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.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/v7/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/DefaultFieldGroupFieldFactory.java new file mode 100644 index 0000000000..8b0b6cfcaa --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/DefaultFieldGroupFieldFactory.java @@ -0,0 +1,251 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.fieldgroup; + +import java.util.Date; +import java.util.EnumSet; + +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.fieldgroup.FieldGroup.BindException; +import com.vaadin.v7.ui.AbstractField; +import com.vaadin.v7.ui.AbstractSelect; +import com.vaadin.v7.ui.AbstractTextField; +import com.vaadin.v7.ui.CheckBox; +import com.vaadin.v7.ui.ComboBox; +import com.vaadin.v7.ui.Field; +import com.vaadin.v7.ui.DateField; +import com.vaadin.v7.ui.InlineDateField; +import com.vaadin.v7.ui.PopupDateField; +import com.vaadin.v7.ui.ListSelect; +import com.vaadin.v7.ui.NativeSelect; +import com.vaadin.v7.ui.OptionGroup; +import com.vaadin.v7.ui.RichTextArea; +import com.vaadin.v7.ui.Table; +import com.vaadin.v7.ui.TextField; + +/** + * 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 (AbstractTextField.class.isAssignableFrom(fieldType)) { + return fieldType.cast(createAbstractTextField( + fieldType.asSubclass(AbstractTextField.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 (AbstractTextField.class.isAssignableFrom(fieldType)) { + return (T) createAbstractTextField( + (Class) fieldType); + } + + return null; + } + + private T createDateField(Class type, + Class fieldType) { + AbstractField field; + + if (InlineDateField.class.isAssignableFrom(fieldType)) { + field = new InlineDateField(); + } else if (anyField(fieldType) + || DateField.class.isAssignableFrom(fieldType)) { + field = new PopupDateField(); + } else if (AbstractTextField.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 == Field.class || fieldType == AbstractField.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(CheckBox.class)) { + CheckBox cb = new CheckBox(null); + cb.setImmediate(true); + return (T) cb; + } else if (AbstractTextField.class.isAssignableFrom(fieldType)) { + return (T) createAbstractTextField( + (Class) fieldType); + } + + return null; + } + + protected T createAbstractTextField( + Class fieldType) { + if (fieldType == AbstractTextField.class) { + fieldType = (Class) TextField.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(TextField.class)) { + return fieldType.cast(createAbstractTextField(TextField.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/v7/data/fieldgroup/FieldGroup.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java new file mode 100644 index 0000000000..07eb9f27c8 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.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.util.ReflectTools; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.data.util.TransactionalPropertyWrapper; +import com.vaadin.v7.ui.AbstractField; +import com.vaadin.v7.ui.DefaultFieldFactory; +import com.vaadin.v7.ui.Field; + +/** + * 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 (Field 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 Field#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 Field#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 (Field 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 (Field 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 (Field 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(Field 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(Field field, Object propertyId) { + if (field == null) { + throw new BindException(String.format( + "Cannot bind property id '%s' to a null field.", + propertyId)); + } + } + + private void throwIfPropertyIdAlreadyBound(Field 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(Field 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(Field 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 (Field 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 (Field 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 (Field f : fieldToPropertyId.keySet()) { + ((Property.Transactional) f.getPropertyDataSource()).commit(); + } + } + + private void rollbackTransactions() { + for (Field 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 (Field 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 Field 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(Field 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 Field#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 (Field 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 (Field 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 Field} 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 Field} 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 Field} 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 (!Field.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; + } + + Field field; + try { + // Get the field from the object + field = (Field) ReflectTools.getJavaFieldValue( + objectWithMemberFields, memberField, Field.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 Field 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 Field}. + */ + public Field buildAndBind(String caption, Object propertyId) + throws BindException { + return buildAndBind(caption, propertyId, Field.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 Field}. + */ + + 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 Field} + * 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 (Field f : getFields()) { + if (f instanceof AbstractField) { + ((AbstractField) f).clear(); + } + } + + } + +} \ No newline at end of file diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroupFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroupFieldFactory.java new file mode 100644 index 0000000000..275f96761a --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.data.fieldgroup; + +import java.io.Serializable; + +import com.vaadin.v7.ui.Field; + +/** + * 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 Field} 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/v7/data/fieldgroup/PropertyId.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/PropertyId.java new file mode 100644 index 0000000000..09426bc811 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/PropertyId.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.v7.data.fieldgroup; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.vaadin.v7.ui.Field; + +/** + * Defines the custom property name to be bound to a {@link Field} 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.Field}, 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/v7/data/util/AbstractBeanContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractBeanContainer.java new file mode 100644 index 0000000000..4b004d95fa --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractBeanContainer.java @@ -0,0 +1,929 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Container.Filterable; +import com.vaadin.v7.data.Container.PropertySetChangeNotifier; +import com.vaadin.v7.data.Container.SimpleFilterable; +import com.vaadin.v7.data.Container.Sortable; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.Property.ValueChangeEvent; +import com.vaadin.v7.data.Property.ValueChangeListener; +import com.vaadin.v7.data.Property.ValueChangeNotifier; +import com.vaadin.v7.data.util.MethodProperty.MethodException; +import com.vaadin.v7.data.util.filter.SimpleStringFilter; +import com.vaadin.v7.data.util.filter.UnsupportedFilterException; + +/** + * An abstract base class for in-memory containers for JavaBeans. + * + *

    + * The properties of the container are determined automatically by introspecting + * the used JavaBean class and explicitly adding or removing properties is not + * supported. Only beans of the same type can be added to the container. + *

    + * + *

    + * Subclasses should implement any public methods adding items to the container, + * typically calling the protected methods {@link #addItem(Object, Object)}, + * {@link #addItemAfter(Object, Object, Object)} and + * {@link #addItemAt(int, Object, Object)}. + *

    + * + * @param + * The type of the item identifier + * @param + * The type of the Bean + * + * @since 6.5 + */ +public abstract class AbstractBeanContainer + extends AbstractInMemoryContainer> + implements Filterable, SimpleFilterable, Sortable, ValueChangeListener, + PropertySetChangeNotifier { + + /** + * Resolver that maps beans to their (item) identifiers, removing the need + * to explicitly specify item identifiers when there is no need to customize + * this. + * + * Note that beans can also be added with an explicit id even if a resolver + * has been set. + * + * @param + * @param + * + * @since 6.5 + */ + public static interface BeanIdResolver + extends Serializable { + /** + * Return the item identifier for a bean. + * + * @param bean + * @return + */ + public IDTYPE getIdForBean(BEANTYPE bean); + } + + /** + * A item identifier resolver that returns the value of a bean property. + * + * The bean must have a getter for the property, and the getter must return + * an object of type IDTYPE. + */ + protected class PropertyBasedBeanIdResolver + implements BeanIdResolver { + + private final Object propertyId; + + public PropertyBasedBeanIdResolver(Object propertyId) { + if (propertyId == null) { + throw new IllegalArgumentException( + "Property identifier must not be null"); + } + this.propertyId = propertyId; + } + + @Override + @SuppressWarnings("unchecked") + public IDTYPE getIdForBean(BEANTYPE bean) + throws IllegalArgumentException { + VaadinPropertyDescriptor pd = model.get(propertyId); + if (null == pd) { + throw new IllegalStateException( + "Property " + propertyId + " not found"); + } + try { + Property property = (Property) pd + .createProperty(bean); + return property.getValue(); + } catch (MethodException e) { + throw new IllegalArgumentException(e); + } + } + + } + + /** + * The resolver that finds the item ID for a bean, or null not to use + * automatic resolving. + * + * Methods that add a bean without specifying an ID must not be called if no + * resolver has been set. + */ + private BeanIdResolver beanIdResolver = null; + + /** + * Maps all item ids in the container (including filtered) to their + * corresponding BeanItem. + */ + private final Map> itemIdToItem = new HashMap>(); + + /** + * The type of the beans in the container. + */ + private final Class type; + + /** + * A description of the properties found in beans of type {@link #type}. + * Determines the property ids that are present in the container. + */ + private final LinkedHashMap> model; + + /** + * Constructs a {@code AbstractBeanContainer} for beans of the given type. + * + * @param type + * the type of the beans that will be added to the container. + * @throws IllegalArgumentException + * If {@code type} is null + */ + protected AbstractBeanContainer(Class type) { + if (type == null) { + throw new IllegalArgumentException( + "The bean type passed to AbstractBeanContainer must not be null"); + } + this.type = type; + model = BeanItem.getPropertyDescriptors((Class) type); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getType(java.lang.Object) + */ + @Override + public Class getType(Object propertyId) { + VaadinPropertyDescriptor descriptor = model.get(propertyId); + if (descriptor == null) { + return null; + } + return descriptor.getPropertyType(); + } + + /** + * Create a BeanItem for a bean using pre-parsed bean metadata (based on + * {@link #getBeanType()}). + * + * @param bean + * @return created {@link BeanItem} or null if bean is null + */ + protected BeanItem createBeanItem(BEANTYPE bean) { + return bean == null ? null : new BeanItem(bean, model); + } + + /** + * Returns the type of beans this Container can contain. + * + * This comes from the bean type constructor parameter, and bean metadata + * (including container properties) is based on this. + * + * @return + */ + public Class getBeanType() { + return type; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getContainerPropertyIds() + */ + @Override + public Collection getContainerPropertyIds() { + return model.keySet(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeAllItems() + */ + @Override + public boolean removeAllItems() { + int origSize = size(); + IDTYPE firstItem = getFirstVisibleItem(); + + internalRemoveAllItems(); + + // detach listeners from all Items + for (Item item : itemIdToItem.values()) { + removeAllValueChangeListeners(item); + } + itemIdToItem.clear(); + + // fire event only if the visible view changed, regardless of whether + // filtered out items were removed or not + if (origSize != 0) { + fireItemsRemoved(0, firstItem, origSize); + } + + return true; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getItem(java.lang.Object) + */ + @Override + public BeanItem getItem(Object itemId) { + // TODO return only if visible? + return getUnfilteredItem(itemId); + } + + @Override + protected BeanItem getUnfilteredItem(Object itemId) { + return itemIdToItem.get(itemId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getItemIds() + */ + @Override + @SuppressWarnings("unchecked") + public List getItemIds() { + return (List) super.getItemIds(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object, + * java.lang.Object) + */ + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + Item item = getItem(itemId); + if (item == null) { + return null; + } + return item.getItemProperty(propertyId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeItem(java.lang.Object) + */ + @Override + public boolean removeItem(Object itemId) { + // TODO should also remove items that are filtered out + int origSize = size(); + Item item = getItem(itemId); + int position = indexOfId(itemId); + + if (internalRemoveItem(itemId)) { + // detach listeners from Item + removeAllValueChangeListeners(item); + + // remove item + itemIdToItem.remove(itemId); + + // fire event only if the visible view changed, regardless of + // whether filtered out items were removed or not + if (size() != origSize) { + fireItemRemoved(position, itemId); + } + + return true; + } else { + return false; + } + } + + /** + * Re-filter the container when one of the monitored properties changes. + */ + @Override + public void valueChange(ValueChangeEvent event) { + // if a property that is used in a filter is changed, refresh filtering + filterAll(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Container.Filterable#addContainerFilter(java.lang.Object, + * java.lang.String, boolean, boolean) + */ + @Override + public void addContainerFilter(Object propertyId, String filterString, + boolean ignoreCase, boolean onlyMatchPrefix) { + try { + addFilter(new SimpleStringFilter(propertyId, filterString, + ignoreCase, onlyMatchPrefix)); + } catch (UnsupportedFilterException e) { + // the filter instance created here is always valid for in-memory + // containers + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Filterable#removeAllContainerFilters() + */ + @Override + public void removeAllContainerFilters() { + if (!getFilters().isEmpty()) { + for (Item item : itemIdToItem.values()) { + removeAllValueChangeListeners(item); + } + removeAllFilters(); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Container.Filterable#removeContainerFilters(java.lang + * .Object) + */ + @Override + public void removeContainerFilters(Object propertyId) { + Collection removedFilters = super.removeFilters(propertyId); + if (!removedFilters.isEmpty()) { + // stop listening to change events for the property + for (Item item : itemIdToItem.values()) { + removeValueChangeListener(item, propertyId); + } + } + } + + @Override + public void addContainerFilter(Filter filter) + throws UnsupportedFilterException { + addFilter(filter); + } + + @Override + public void removeContainerFilter(Filter filter) { + removeFilter(filter); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#hasContainerFilters() + */ + @Override + public boolean hasContainerFilters() { + return super.hasContainerFilters(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() + */ + @Override + public Collection getContainerFilters() { + return super.getContainerFilters(); + } + + /** + * Make this container listen to the given property provided it notifies + * when its value changes. + * + * @param item + * The {@link Item} that contains the property + * @param propertyId + * The id of the property + */ + private void addValueChangeListener(Item item, Object propertyId) { + Property property = item.getItemProperty(propertyId); + if (property instanceof ValueChangeNotifier) { + // avoid multiple notifications for the same property if + // multiple filters are in use + ValueChangeNotifier notifier = (ValueChangeNotifier) property; + notifier.removeListener(this); + notifier.addListener(this); + } + } + + /** + * Remove this container as a listener for the given property. + * + * @param item + * The {@link Item} that contains the property + * @param propertyId + * The id of the property + */ + private void removeValueChangeListener(Item item, Object propertyId) { + Property property = item.getItemProperty(propertyId); + if (property instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) property).removeListener(this); + } + } + + /** + * Remove this contains as a listener for all the properties in the given + * {@link Item}. + * + * @param item + * The {@link Item} that contains the properties + */ + private void removeAllValueChangeListeners(Item item) { + for (Object propertyId : item.getItemPropertyIds()) { + removeValueChangeListener(item, propertyId); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds() + */ + @Override + public Collection getSortableContainerPropertyIds() { + return getSortablePropertyIds(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], + * boolean[]) + */ + @Override + public void sort(Object[] propertyId, boolean[] ascending) { + sortContainer(propertyId, ascending); + } + + @Override + public ItemSorter getItemSorter() { + return super.getItemSorter(); + } + + @Override + public void setItemSorter(ItemSorter itemSorter) { + super.setItemSorter(itemSorter); + } + + @Override + protected void registerNewItem(int position, IDTYPE itemId, + BeanItem item) { + itemIdToItem.put(itemId, item); + + // add listeners to be able to update filtering on property + // changes + for (Filter filter : getFilters()) { + for (String propertyId : getContainerPropertyIds()) { + if (filter.appliesToProperty(propertyId)) { + // addValueChangeListener avoids adding duplicates + addValueChangeListener(item, propertyId); + } + } + } + } + + /** + * Check that a bean can be added to the container (is of the correct type + * for the container). + * + * @param bean + * @return + */ + private boolean validateBean(BEANTYPE bean) { + return bean != null && getBeanType().isAssignableFrom(bean.getClass()); + } + + /** + * Adds the bean to the Container. + * + * Note: the behavior of this method changed in Vaadin 6.6 - now items are + * added at the very end of the unfiltered container and not after the last + * visible item if filtering is used. + * + * @see com.com.vaadin.v7.data.Container#addItem(Object) + */ + protected BeanItem addItem(IDTYPE itemId, BEANTYPE bean) { + if (!validateBean(bean)) { + return null; + } + return internalAddItemAtEnd(itemId, createBeanItem(bean), true); + } + + /** + * Adds the bean after the given bean. + * + * @see com.com.vaadin.v7.data.Container.Ordered#addItemAfter(Object, Object) + */ + protected BeanItem addItemAfter(IDTYPE previousItemId, + IDTYPE newItemId, BEANTYPE bean) { + if (!validateBean(bean)) { + return null; + } + return internalAddItemAfter(previousItemId, newItemId, + createBeanItem(bean), true); + } + + /** + * Adds a new bean at the given index. + * + * The bean is used both as the item contents and as the item identifier. + * + * @param index + * Index at which the bean should be added. + * @param newItemId + * The item id for the bean to add to the container. + * @param bean + * The bean to add to the container. + * + * @return Returns the new BeanItem or null if the operation fails. + */ + protected BeanItem addItemAt(int index, IDTYPE newItemId, + BEANTYPE bean) { + if (!validateBean(bean)) { + return null; + } + return internalAddItemAt(index, newItemId, createBeanItem(bean), true); + } + + /** + * Adds a bean to the container using the bean item id resolver to find its + * identifier. + * + * A bean id resolver must be set before calling this method. + * + * @see #addItem(Object, Object) + * + * @param bean + * the bean to add + * @return BeanItem item added or null + * @throws IllegalStateException + * if no bean identifier resolver has been set + * @throws IllegalArgumentException + * if an identifier cannot be resolved for the bean + */ + protected BeanItem addBean(BEANTYPE bean) + throws IllegalStateException, IllegalArgumentException { + if (bean == null) { + return null; + } + IDTYPE itemId = resolveBeanId(bean); + if (itemId == null) { + throw new IllegalArgumentException( + "Resolved identifier for a bean must not be null"); + } + return addItem(itemId, bean); + } + + /** + * Adds a bean to the container after a specified item identifier, using the + * bean item id resolver to find its identifier. + * + * A bean id resolver must be set before calling this method. + * + * @see #addItemAfter(Object, Object, Object) + * + * @param previousItemId + * the identifier of the bean after which this bean should be + * added, null to add to the beginning + * @param bean + * the bean to add + * @return BeanItem item added or null + * @throws IllegalStateException + * if no bean identifier resolver has been set + * @throws IllegalArgumentException + * if an identifier cannot be resolved for the bean + */ + protected BeanItem addBeanAfter(IDTYPE previousItemId, + BEANTYPE bean) + throws IllegalStateException, IllegalArgumentException { + if (bean == null) { + return null; + } + IDTYPE itemId = resolveBeanId(bean); + if (itemId == null) { + throw new IllegalArgumentException( + "Resolved identifier for a bean must not be null"); + } + return addItemAfter(previousItemId, itemId, bean); + } + + /** + * Adds a bean at a specified (filtered view) position in the container + * using the bean item id resolver to find its identifier. + * + * A bean id resolver must be set before calling this method. + * + * @see #addItemAfter(Object, Object, Object) + * + * @param index + * the index (in the filtered view) at which to add the item + * @param bean + * the bean to add + * @return BeanItem item added or null + * @throws IllegalStateException + * if no bean identifier resolver has been set + * @throws IllegalArgumentException + * if an identifier cannot be resolved for the bean + */ + protected BeanItem addBeanAt(int index, BEANTYPE bean) + throws IllegalStateException, IllegalArgumentException { + if (bean == null) { + return null; + } + IDTYPE itemId = resolveBeanId(bean); + if (itemId == null) { + throw new IllegalArgumentException( + "Resolved identifier for a bean must not be null"); + } + return addItemAt(index, itemId, bean); + } + + /** + * Adds all the beans from a {@link Collection} in one operation using the + * bean item identifier resolver. More efficient than adding them one by + * one. + * + * A bean id resolver must be set before calling this method. + * + * Note: the behavior of this method changed in Vaadin 6.6 - now items are + * added at the very end of the unfiltered container and not after the last + * visible item if filtering is used. + * + * @param collection + * The collection of beans to add. Must not be null. + * @throws IllegalStateException + * if no bean identifier resolver has been set + * @throws IllegalArgumentException + * if the resolver returns a null itemId for one of the beans in + * the collection + */ + protected void addAll(Collection collection) + throws IllegalStateException, IllegalArgumentException { + boolean modified = false; + int origSize = size(); + + for (BEANTYPE bean : collection) { + // TODO skipping invalid beans - should not allow them in javadoc? + if (bean == null + || !getBeanType().isAssignableFrom(bean.getClass())) { + continue; + } + IDTYPE itemId = resolveBeanId(bean); + if (itemId == null) { + throw new IllegalArgumentException( + "Resolved identifier for a bean must not be null"); + } + + if (internalAddItemAtEnd(itemId, createBeanItem(bean), + false) != null) { + modified = true; + } + } + + if (modified) { + // Filter the contents when all items have been added + if (isFiltered()) { + doFilterContainer(!getFilters().isEmpty()); + } + if (visibleNewItemsWasAdded(origSize)) { + // fire event about added items + int firstPosition = origSize; + IDTYPE firstItemId = getVisibleItemIds().get(firstPosition); + int affectedItems = size() - origSize; + fireItemsAdded(firstPosition, firstItemId, affectedItems); + } + } + } + + private boolean visibleNewItemsWasAdded(int origSize) { + return size() > origSize; + } + + /** + * Use the bean resolver to get the identifier for a bean. + * + * @param bean + * @return resolved bean identifier, null if could not be resolved + * @throws IllegalStateException + * if no bean resolver is set + */ + protected IDTYPE resolveBeanId(BEANTYPE bean) { + if (beanIdResolver == null) { + throw new IllegalStateException( + "Bean item identifier resolver is required."); + } + return beanIdResolver.getIdForBean(bean); + } + + /** + * Sets the resolver that finds the item id for a bean, or null not to use + * automatic resolving. + * + * Methods that add a bean without specifying an id must not be called if no + * resolver has been set. + * + * Note that methods taking an explicit id can be used whether a resolver + * has been defined or not. + * + * @param beanIdResolver + * to use or null to disable automatic id resolution + */ + protected void setBeanIdResolver( + BeanIdResolver beanIdResolver) { + this.beanIdResolver = beanIdResolver; + } + + /** + * Returns the resolver that finds the item ID for a bean. + * + * @return resolver used or null if automatic item id resolving is disabled + */ + public BeanIdResolver getBeanIdResolver() { + return beanIdResolver; + } + + /** + * Create an item identifier resolver using a named bean property. + * + * @param propertyId + * property identifier, which must map to a getter in BEANTYPE + * @return created resolver + */ + protected BeanIdResolver createBeanPropertyResolver( + Object propertyId) { + return new PropertyBasedBeanIdResolver(propertyId); + } + + /** + * @deprecated As of 7.0, replaced by {@link #addPropertySetChangeListener} + **/ + @Deprecated + @Override + public void addListener(Container.PropertySetChangeListener listener) { + addPropertySetChangeListener(listener); + } + + @Override + public void addPropertySetChangeListener( + Container.PropertySetChangeListener listener) { + super.addPropertySetChangeListener(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removePropertySetChangeListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Deprecated + @Override + public void removeListener(Container.PropertySetChangeListener listener) { + removePropertySetChangeListener(listener); + } + + @Override + public void removePropertySetChangeListener( + Container.PropertySetChangeListener listener) { + super.removePropertySetChangeListener(listener); + } + + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Use addNestedContainerProperty(String) to add container properties to a " + + getClass().getSimpleName()); + } + + /** + * Adds a property for the container and all its items. + * + * Primarily for internal use, may change in future versions. + * + * @param propertyId + * @param propertyDescriptor + * @return true if the property was added + */ + protected final boolean addContainerProperty(String propertyId, + VaadinPropertyDescriptor propertyDescriptor) { + if (null == propertyId || null == propertyDescriptor) { + return false; + } + + // Fails if the Property is already present + if (model.containsKey(propertyId)) { + return false; + } + + model.put(propertyId, propertyDescriptor); + for (BeanItem item : itemIdToItem.values()) { + item.addItemProperty(propertyId, + propertyDescriptor.createProperty(item.getBean())); + } + + // Sends a change event + fireContainerPropertySetChange(); + + return true; + } + + /** + * Adds a nested container property for the container, e.g. + * "manager.address.street". + * + * All intermediate getters must exist and should return non-null values + * when the property value is accessed. If an intermediate getter returns + * null, a null value will be returned. + * + * @see NestedMethodProperty + * + * @param propertyId + * @return true if the property was added + */ + public boolean addNestedContainerProperty(String propertyId) { + return addContainerProperty(propertyId, + new NestedPropertyDescriptor(propertyId, type)); + } + + /** + * Adds a nested container properties for all sub-properties of a named + * property to the container. The named property itself is removed from the + * model as its subproperties are added. + * + * All intermediate getters must exist and should return non-null values + * when the property value is accessed. If an intermediate getter returns + * null, a null value will be returned. + * + * @see NestedMethodProperty + * @see #addNestedContainerProperty(String) + * + * @param propertyId + */ + @SuppressWarnings("unchecked") + public void addNestedContainerBean(String propertyId) { + Class propertyType = getType(propertyId); + LinkedHashMap> pds = BeanItem + .getPropertyDescriptors((Class) propertyType); + for (String subPropertyId : pds.keySet()) { + String qualifiedPropertyId = propertyId + "." + subPropertyId; + NestedPropertyDescriptor pd = new NestedPropertyDescriptor( + qualifiedPropertyId, (Class) type); + model.put(qualifiedPropertyId, pd); + model.remove(propertyId); + for (BeanItem item : itemIdToItem.values()) { + item.addItemProperty(qualifiedPropertyId, + pd.createProperty(item.getBean())); + item.removeItemProperty(propertyId); + } + } + + // Sends a change event + fireContainerPropertySetChange(); + } + + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + // Fails if the Property is not present + if (!model.containsKey(propertyId)) { + return false; + } + + // Removes the Property to Property list and types + model.remove(propertyId); + + // If remove the Property from all Items + for (final Iterator i = getAllItemIds().iterator(); i + .hasNext();) { + getUnfilteredItem(i.next()).removeItemProperty(propertyId); + } + + // Sends a change event + fireContainerPropertySetChange(); + + return true; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractContainer.java new file mode 100644 index 0000000000..f57da653a5 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractContainer.java @@ -0,0 +1,307 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.EventObject; +import java.util.LinkedList; + +import com.vaadin.v7.data.Container; + +/** + * Abstract container class that manages event listeners and sending events to + * them ({@link PropertySetChangeNotifier}, {@link ItemSetChangeNotifier}). + * + * Note that this class provides the internal implementations for both types of + * events and notifiers as protected methods, but does not implement the + * {@link PropertySetChangeNotifier} and {@link ItemSetChangeNotifier} + * interfaces directly. This way, subclasses can choose not to implement them. + * Subclasses implementing those interfaces should also override the + * corresponding {@link #addListener()} and {@link #removeListener()} methods to + * make them public. + * + * @since 6.6 + */ +public abstract class AbstractContainer implements Container { + + /** + * List of all Property set change event listeners. + */ + private Collection propertySetChangeListeners = null; + + /** + * List of all container Item set change event listeners. + */ + private Collection itemSetChangeListeners = null; + + /** + * An event object specifying the container whose Property set + * has changed. + * + * This class does not provide information about which properties were + * concerned by the change, but subclasses can provide additional + * information about the changes. + */ + protected static class BasePropertySetChangeEvent extends EventObject + implements Container.PropertySetChangeEvent, Serializable { + + protected BasePropertySetChangeEvent(Container source) { + super(source); + } + + @Override + public Container getContainer() { + return (Container) getSource(); + } + } + + /** + * An event object specifying the container whose Item set has + * changed. + * + * This class does not provide information about the exact changes + * performed, but subclasses can add provide additional information about + * the changes. + */ + protected static class BaseItemSetChangeEvent extends EventObject + implements Container.ItemSetChangeEvent, Serializable { + + protected BaseItemSetChangeEvent(Container source) { + super(source); + } + + @Override + public Container getContainer() { + return (Container) getSource(); + } + } + + // PropertySetChangeNotifier + + /** + * Implementation of the corresponding method in + * {@link PropertySetChangeNotifier}, override with the corresponding public + * method and implement the interface to use this. + * + * @see PropertySetChangeNotifier#addListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener) + */ + protected void addPropertySetChangeListener( + Container.PropertySetChangeListener listener) { + if (getPropertySetChangeListeners() == null) { + setPropertySetChangeListeners( + new LinkedList()); + } + getPropertySetChangeListeners().add(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addPropertySetChangeListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Deprecated + protected void addListener(Container.PropertySetChangeListener listener) { + addPropertySetChangeListener(listener); + } + + /** + * Implementation of the corresponding method in + * {@link PropertySetChangeNotifier}, override with the corresponding public + * method and implement the interface to use this. + * + * @see PropertySetChangeNotifier#removeListener(com.com.vaadin.v7.data.Container. + * PropertySetChangeListener) + */ + protected void removePropertySetChangeListener( + Container.PropertySetChangeListener listener) { + if (getPropertySetChangeListeners() != null) { + getPropertySetChangeListeners().remove(listener); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removePropertySetChangeListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Deprecated + protected void removeListener( + Container.PropertySetChangeListener listener) { + removePropertySetChangeListener(listener); + } + + // ItemSetChangeNotifier + + /** + * Implementation of the corresponding method in + * {@link ItemSetChangeNotifier}, override with the corresponding public + * method and implement the interface to use this. + * + * @see ItemSetChangeNotifier#addListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener) + */ + protected void addItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (getItemSetChangeListeners() == null) { + setItemSetChangeListeners( + new LinkedList()); + } + getItemSetChangeListeners().add(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemSetChangeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Deprecated + protected void addListener(Container.ItemSetChangeListener listener) { + addItemSetChangeListener(listener); + } + + /** + * Implementation of the corresponding method in + * {@link ItemSetChangeNotifier}, override with the corresponding public + * method and implement the interface to use this. + * + * @see ItemSetChangeNotifier#removeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener) + */ + protected void removeItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (getItemSetChangeListeners() != null) { + getItemSetChangeListeners().remove(listener); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemSetChangeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Deprecated + protected void removeListener(Container.ItemSetChangeListener listener) { + removeItemSetChangeListener(listener); + } + + /** + * Sends a simple Property set change event to all interested listeners. + */ + protected void fireContainerPropertySetChange() { + fireContainerPropertySetChange(new BasePropertySetChangeEvent(this)); + } + + /** + * Sends a Property set change event to all interested listeners. + * + * Use {@link #fireContainerPropertySetChange()} instead of this method + * unless additional information about the exact changes is available and + * should be included in the event. + * + * @param event + * the property change event to send, optionally with additional + * information + */ + protected void fireContainerPropertySetChange( + Container.PropertySetChangeEvent event) { + if (getPropertySetChangeListeners() != null) { + final Object[] l = getPropertySetChangeListeners().toArray(); + for (int i = 0; i < l.length; i++) { + ((Container.PropertySetChangeListener) l[i]) + .containerPropertySetChange(event); + } + } + } + + /** + * Sends a simple Item set change event to all interested listeners, + * indicating that anything in the contents may have changed (items added, + * removed etc.). + */ + protected void fireItemSetChange() { + fireItemSetChange(new BaseItemSetChangeEvent(this)); + } + + /** + * Sends an Item set change event to all registered interested listeners. + * + * @param event + * the item set change event to send, optionally with additional + * information + */ + protected void fireItemSetChange(ItemSetChangeEvent event) { + if (getItemSetChangeListeners() != null) { + final Object[] l = getItemSetChangeListeners().toArray(); + for (int i = 0; i < l.length; i++) { + ((Container.ItemSetChangeListener) l[i]) + .containerItemSetChange(event); + } + } + } + + /** + * Sets the property set change listener collection. For internal use only. + * + * @param propertySetChangeListeners + */ + protected void setPropertySetChangeListeners( + Collection propertySetChangeListeners) { + this.propertySetChangeListeners = propertySetChangeListeners; + } + + /** + * Returns the property set change listener collection. For internal use + * only. + */ + protected Collection getPropertySetChangeListeners() { + return propertySetChangeListeners; + } + + /** + * Sets the item set change listener collection. For internal use only. + * + * @param itemSetChangeListeners + */ + protected void setItemSetChangeListeners( + Collection itemSetChangeListeners) { + this.itemSetChangeListeners = itemSetChangeListeners; + } + + /** + * Returns the item set change listener collection. For internal use only. + */ + protected Collection getItemSetChangeListeners() { + return itemSetChangeListeners; + } + + public Collection getListeners(Class eventType) { + if (Container.PropertySetChangeEvent.class + .isAssignableFrom(eventType)) { + if (propertySetChangeListeners == null) { + return Collections.EMPTY_LIST; + } else { + return Collections + .unmodifiableCollection(propertySetChangeListeners); + } + } else if (Container.ItemSetChangeEvent.class + .isAssignableFrom(eventType)) { + if (itemSetChangeListeners == null) { + return Collections.EMPTY_LIST; + } else { + return Collections + .unmodifiableCollection(itemSetChangeListeners); + } + } + + return Collections.EMPTY_LIST; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractInMemoryContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractInMemoryContainer.java new file mode 100644 index 0000000000..78c908f78a --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/AbstractInMemoryContainer.java @@ -0,0 +1,1165 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.EventObject; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Container.ItemSetChangeNotifier; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.util.filter.SimpleStringFilter; +import com.vaadin.v7.data.util.filter.UnsupportedFilterException; + +/** + * Abstract {@link Container} class that handles common functionality for + * in-memory containers. Concrete in-memory container classes can either inherit + * this class, inherit {@link AbstractContainer}, or implement the + * {@link Container} interface directly. + * + * Adding and removing items (if desired) must be implemented in subclasses by + * overriding the appropriate add*Item() and remove*Item() and removeAllItems() + * methods, calling the corresponding + * {@link #internalAddItemAfter(Object, Object, Item)}, + * {@link #internalAddItemAt(int, Object, Item)}, + * {@link #internalAddItemAtEnd(Object, Item, boolean)}, + * {@link #internalRemoveItem(Object)} and {@link #internalRemoveAllItems()} + * methods. + * + * By default, adding and removing container properties is not supported, and + * subclasses need to implement {@link #getContainerPropertyIds()}. Optionally, + * subclasses can override {@link #addContainerProperty(Object, Class, Object)} + * and {@link #removeContainerProperty(Object)} to implement them. + * + * Features: + *
      + *
    • {@link Container.Ordered} + *
    • {@link Container.Indexed} + *
    • {@link Filterable} and {@link SimpleFilterable} (internal implementation, + * does not implement the interface directly) + *
    • {@link Sortable} (internal implementation, does not implement the + * interface directly) + *
    + * + * To implement {@link Sortable}, subclasses need to implement + * {@link #getSortablePropertyIds()} and call the superclass method + * {@link #sortContainer(Object[], boolean[])} in the method + * sort(Object[], boolean[]). + * + * To implement {@link Filterable}, subclasses need to implement the methods + * {@link Filterable#addContainerFilter(com.com.vaadin.v7.data.Container.Filter)} + * (calling {@link #addFilter(Filter)}), + * {@link Filterable#removeAllContainerFilters()} (calling + * {@link #removeAllFilters()}) and + * {@link Filterable#removeContainerFilter(com.com.vaadin.v7.data.Container.Filter)} + * (calling {@link #removeFilter(com.com.vaadin.v7.data.Container.Filter)}). + * + * To implement {@link SimpleFilterable}, subclasses also need to implement the + * methods + * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)} + * and {@link SimpleFilterable#removeContainerFilters(Object)} calling + * {@link #addFilter(com.com.vaadin.v7.data.Container.Filter)} and + * {@link #removeFilters(Object)} respectively. + * + * @param + * the class of item identifiers in the container, use Object if can + * be any class + * @param + * the class of property identifiers for the items in the container, + * use Object if can be any class + * @param + * the (base) class of the Item instances in the container, use + * {@link Item} if unknown + * + * @since 6.6 + */ +public abstract class AbstractInMemoryContainer + extends AbstractContainer + implements ItemSetChangeNotifier, Container.Indexed { + + /** + * An ordered {@link List} of all item identifiers in the container, + * including those that have been filtered out. + * + * Must not be null. + */ + private List allItemIds; + + /** + * An ordered {@link List} of item identifiers in the container after + * filtering, excluding those that have been filtered out. + * + * This is what the external API of the {@link Container} interface and its + * subinterfaces shows (e.g. {@link #size()}, {@link #nextItemId(Object)}). + * + * If null, the full item id list is used instead. + */ + private List filteredItemIds; + + /** + * Filters that are applied to the container to limit the items visible in + * it + */ + private Set filters = new HashSet(); + + /** + * The item sorter which is used for sorting the container. + */ + private ItemSorter itemSorter = new DefaultItemSorter(); + + // Constructors + + /** + * Constructor for an abstract in-memory container. + */ + protected AbstractInMemoryContainer() { + setAllItemIds(new ListSet()); + } + + // Container interface methods with more specific return class + + // default implementation, can be overridden + @Override + public ITEMCLASS getItem(Object itemId) { + if (containsId(itemId)) { + return getUnfilteredItem(itemId); + } else { + return null; + } + } + + private static abstract class BaseItemAddOrRemoveEvent extends EventObject + implements Serializable { + protected Object itemId; + protected int index; + protected int count; + + public BaseItemAddOrRemoveEvent(Container source, Object itemId, + int index, int count) { + super(source); + this.itemId = itemId; + this.index = index; + this.count = count; + } + + public Container getContainer() { + return (Container) getSource(); + } + + public Object getFirstItemId() { + return itemId; + } + + public int getFirstIndex() { + return index; + } + + public int getAffectedItemsCount() { + return count; + } + } + + /** + * An Event object specifying information about the added + * items. + * + *

    + * This class provides information about the first added item and the number + * of added items. + *

    + * + * @since 7.4 + */ + protected static class BaseItemAddEvent extends BaseItemAddOrRemoveEvent + implements Container.Indexed.ItemAddEvent { + + public BaseItemAddEvent(Container source, Object itemId, int index, + int count) { + super(source, itemId, index, count); + } + + @Override + public int getAddedItemsCount() { + return getAffectedItemsCount(); + } + } + + /** + * An Event object specifying information about the removed + * items. + * + *

    + * This class provides information about the first removed item and the + * number of removed items. + *

    + * + * @since 7.4 + */ + protected static class BaseItemRemoveEvent extends BaseItemAddOrRemoveEvent + implements Container.Indexed.ItemRemoveEvent { + + public BaseItemRemoveEvent(Container source, Object itemId, int index, + int count) { + super(source, itemId, index, count); + } + + @Override + public int getRemovedItemsCount() { + return getAffectedItemsCount(); + } + } + + /** + * Get an item even if filtered out. + * + * For internal use only. + * + * @param itemId + * @return + */ + protected abstract ITEMCLASS getUnfilteredItem(Object itemId); + + // cannot override getContainerPropertyIds() and getItemIds(): if subclass + // uses Object as ITEMIDCLASS or PROPERTYIDCLASS, Collection cannot + // be cast to Collection + + // public abstract Collection getContainerPropertyIds(); + // public abstract Collection getItemIds(); + + // Container interface method implementations + + @Override + public int size() { + return getVisibleItemIds().size(); + } + + @Override + public boolean containsId(Object itemId) { + // only look at visible items after filtering + if (itemId == null) { + return false; + } else { + return getVisibleItemIds().contains(itemId); + } + } + + @Override + public List getItemIds() { + return Collections.unmodifiableList(getVisibleItemIds()); + } + + // Container.Ordered + + @Override + public ITEMIDTYPE nextItemId(Object itemId) { + int index = indexOfId(itemId); + if (index >= 0 && index < size() - 1) { + return getIdByIndex(index + 1); + } else { + // out of bounds + return null; + } + } + + @Override + public ITEMIDTYPE prevItemId(Object itemId) { + int index = indexOfId(itemId); + if (index > 0) { + return getIdByIndex(index - 1); + } else { + // out of bounds + return null; + } + } + + @Override + public ITEMIDTYPE firstItemId() { + if (size() > 0) { + return getIdByIndex(0); + } else { + return null; + } + } + + @Override + public ITEMIDTYPE lastItemId() { + if (size() > 0) { + return getIdByIndex(size() - 1); + } else { + return null; + } + } + + @Override + public boolean isFirstId(Object itemId) { + if (itemId == null) { + return false; + } + return itemId.equals(firstItemId()); + } + + @Override + public boolean isLastId(Object itemId) { + if (itemId == null) { + return false; + } + return itemId.equals(lastItemId()); + } + + // Container.Indexed + + @Override + public ITEMIDTYPE getIdByIndex(int index) { + return getVisibleItemIds().get(index); + } + + @Override + public List getItemIds(int startIndex, int numberOfIds) { + if (startIndex < 0) { + throw new IndexOutOfBoundsException( + "Start index cannot be negative! startIndex=" + startIndex); + } + + if (startIndex > getVisibleItemIds().size()) { + throw new IndexOutOfBoundsException( + "Start index exceeds container size! startIndex=" + + startIndex + " containerLastItemIndex=" + + (getVisibleItemIds().size() - 1)); + } + + if (numberOfIds < 1) { + if (numberOfIds == 0) { + return Collections.emptyList(); + } + + throw new IllegalArgumentException( + "Cannot get negative amount of items! numberOfItems=" + + numberOfIds); + } + + int endIndex = startIndex + numberOfIds; + + if (endIndex > getVisibleItemIds().size()) { + endIndex = getVisibleItemIds().size(); + } + + return Collections.unmodifiableList( + getVisibleItemIds().subList(startIndex, endIndex)); + + } + + @Override + public int indexOfId(Object itemId) { + return getVisibleItemIds().indexOf(itemId); + } + + // methods that are unsupported by default, override to support + + @Override + public Object addItemAt(int index) throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public Item addItemAt(int index, Object newItemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public Object addItemAfter(Object previousItemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public Item addItemAfter(Object previousItemId, Object newItemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public Item addItem(Object itemId) throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public Object addItem() throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Adding items not supported. Override the relevant addItem*() methods if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Removing items not supported. Override the removeItem() method if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public boolean removeAllItems() throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Removing items not supported. Override the removeAllItems() method if required as specified in AbstractInMemoryContainer javadoc."); + } + + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Adding container properties not supported. Override the addContainerProperty() method if required."); + } + + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Removing container properties not supported. Override the addContainerProperty() method if required."); + } + + // ItemSetChangeNotifier + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemSetChangeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Deprecated + @Override + public void addListener(Container.ItemSetChangeListener listener) { + addItemSetChangeListener(listener); + } + + @Override + public void addItemSetChangeListener( + Container.ItemSetChangeListener listener) { + super.addItemSetChangeListener(listener); + } + + @Override + public void removeItemSetChangeListener( + Container.ItemSetChangeListener listener) { + super.removeItemSetChangeListener(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeItemSetChangeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Deprecated + @Override + public void removeListener(Container.ItemSetChangeListener listener) { + removeItemSetChangeListener(listener); + } + + // internal methods + + // Filtering support + + /** + * Filter the view to recreate the visible item list from the unfiltered + * items, and send a notification if the set of visible items changed in any + * way. + */ + protected void filterAll() { + if (doFilterContainer(!getFilters().isEmpty())) { + fireItemSetChange(); + } + } + + /** + * Filters the data in the container and updates internal data structures. + * This method should reset any internal data structures and then repopulate + * them so {@link #getItemIds()} and other methods only return the filtered + * items. + * + * @param hasFilters + * true if filters has been set for the container, false + * otherwise + * @return true if the item set has changed as a result of the filtering + */ + protected boolean doFilterContainer(boolean hasFilters) { + if (!hasFilters) { + boolean changed = getAllItemIds().size() != getVisibleItemIds() + .size(); + setFilteredItemIds(null); + return changed; + } + + // Reset filtered list + List originalFilteredItemIds = getFilteredItemIds(); + boolean wasUnfiltered = false; + if (originalFilteredItemIds == null) { + originalFilteredItemIds = Collections.emptyList(); + wasUnfiltered = true; + } + setFilteredItemIds(new ListSet()); + + // Filter + boolean equal = true; + Iterator origIt = originalFilteredItemIds.iterator(); + for (final Iterator i = getAllItemIds().iterator(); i + .hasNext();) { + final ITEMIDTYPE id = i.next(); + if (passesFilters(id)) { + // filtered list comes from the full list, can use == + equal = equal && origIt.hasNext() && origIt.next() == id; + getFilteredItemIds().add(id); + } + } + + return (wasUnfiltered && !getAllItemIds().isEmpty()) || !equal + || origIt.hasNext(); + } + + /** + * Checks if the given itemId passes the filters set for the container. The + * caller should make sure the itemId exists in the container. For + * non-existing itemIds the behavior is undefined. + * + * @param itemId + * An itemId that exists in the container. + * @return true if the itemId passes all filters or no filters are set, + * false otherwise. + */ + protected boolean passesFilters(Object itemId) { + ITEMCLASS item = getUnfilteredItem(itemId); + if (getFilters().isEmpty()) { + return true; + } + final Iterator i = getFilters().iterator(); + while (i.hasNext()) { + final Filter f = i.next(); + if (!f.passesFilter(itemId, item)) { + return false; + } + } + return true; + } + + /** + * Adds a container filter and re-filter the view. + * + * The filter must implement Filter and its sub-filters (if any) must also + * be in-memory filterable. + * + * This can be used to implement + * {@link Filterable#addContainerFilter(com.com.vaadin.v7.data.Container.Filter)} + * and optionally also + * {@link SimpleFilterable#addContainerFilter(Object, String, boolean, boolean)} + * (with {@link SimpleStringFilter}). + * + * Note that in some cases, incompatible filters cannot be detected when + * added and an {@link UnsupportedFilterException} may occur when performing + * filtering. + * + * @throws UnsupportedFilterException + * if the filter is detected as not supported by the container + */ + protected void addFilter(Filter filter) throws UnsupportedFilterException { + getFilters().add(filter); + filterAll(); + } + + /** + * Returns true if any filters have been applied to the container. + * + * @return true if the container has filters applied, false otherwise + * @since 7.1 + */ + protected boolean hasContainerFilters() { + return !getContainerFilters().isEmpty(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Filterable#getContainerFilters() + */ + protected Collection getContainerFilters() { + return Collections.unmodifiableCollection(filters); + } + + /** + * Remove a specific container filter and re-filter the view (if necessary). + * + * This can be used to implement + * {@link Filterable#removeContainerFilter(com.com.vaadin.v7.data.Container.Filter)} + * . + */ + protected void removeFilter(Filter filter) { + for (Iterator iterator = getFilters().iterator(); iterator + .hasNext();) { + Filter f = iterator.next(); + if (f.equals(filter)) { + iterator.remove(); + filterAll(); + return; + } + } + } + + /** + * Remove all container filters for all properties and re-filter the view. + * + * This can be used to implement + * {@link Filterable#removeAllContainerFilters()}. + */ + protected void removeAllFilters() { + if (getFilters().isEmpty()) { + return; + } + getFilters().clear(); + filterAll(); + } + + /** + * Checks if there is a filter that applies to a given property. + * + * @param propertyId + * @return true if there is an active filter for the property + */ + protected boolean isPropertyFiltered(Object propertyId) { + if (getFilters().isEmpty() || propertyId == null) { + return false; + } + final Iterator i = getFilters().iterator(); + while (i.hasNext()) { + final Filter f = i.next(); + if (f.appliesToProperty(propertyId)) { + return true; + } + } + return false; + } + + /** + * Remove all container filters for a given property identifier and + * re-filter the view. This also removes filters applying to multiple + * properties including the one identified by propertyId. + * + * This can be used to implement + * {@link Filterable#removeContainerFilters(Object)}. + * + * @param propertyId + * @return Collection removed filters + */ + protected Collection removeFilters(Object propertyId) { + if (getFilters().isEmpty() || propertyId == null) { + return Collections.emptyList(); + } + List removedFilters = new LinkedList(); + for (Iterator iterator = getFilters().iterator(); iterator + .hasNext();) { + Filter f = iterator.next(); + if (f.appliesToProperty(propertyId)) { + removedFilters.add(f); + iterator.remove(); + } + } + if (!removedFilters.isEmpty()) { + filterAll(); + return removedFilters; + } + return Collections.emptyList(); + } + + // sorting + + /** + * Returns the ItemSorter used for comparing items in a sort. See + * {@link #setItemSorter(ItemSorter)} for more information. + * + * @return The ItemSorter used for comparing two items in a sort. + */ + protected ItemSorter getItemSorter() { + return itemSorter; + } + + /** + * Sets the ItemSorter used for comparing items in a sort. The + * {@link ItemSorter#compare(Object, Object)} method is called with item ids + * to perform the sorting. A default ItemSorter is used if this is not + * explicitly set. + * + * @param itemSorter + * The ItemSorter used for comparing two items in a sort (not + * null). + */ + protected void setItemSorter(ItemSorter itemSorter) { + this.itemSorter = itemSorter; + } + + /** + * Sort base implementation to be used to implement {@link Sortable}. + * + * Subclasses should call this from a public + * {@link #sort(Object[], boolean[])} method when implementing Sortable. + * + * @see com.com.vaadin.v7.data.Container.Sortable#sort(java.lang.Object[], + * boolean[]) + */ + protected void sortContainer(Object[] propertyId, boolean[] ascending) { + if (!(this instanceof Sortable)) { + throw new UnsupportedOperationException( + "Cannot sort a Container that does not implement Sortable"); + } + + // Set up the item sorter for the sort operation + getItemSorter().setSortProperties((Sortable) this, propertyId, + ascending); + + // Perform the actual sort + doSort(); + + // Post sort updates + if (isFiltered()) { + filterAll(); + } else { + fireItemSetChange(); + } + + } + + /** + * Perform the sorting of the data structures in the container. This is + * invoked when the itemSorter has been prepared for the sort + * operation. Typically this method calls + * Collections.sort(aCollection, getItemSorter()) on all arrays + * (containing item ids) that need to be sorted. + * + */ + protected void doSort() { + Collections.sort(getAllItemIds(), getItemSorter()); + } + + /** + * Returns the sortable property identifiers for the container. Can be used + * to implement {@link Sortable#getSortableContainerPropertyIds()}. + */ + protected Collection getSortablePropertyIds() { + LinkedList sortables = new LinkedList(); + for (Object propertyId : getContainerPropertyIds()) { + Class propertyType = getType(propertyId); + if (Comparable.class.isAssignableFrom(propertyType) + || propertyType.isPrimitive()) { + sortables.add(propertyId); + } + } + return sortables; + } + + // removing items + + /** + * Removes all items from the internal data structures of this class. This + * can be used to implement {@link #removeAllItems()} in subclasses. + * + * No notification is sent, the caller has to fire a suitable item set + * change notification. + */ + protected void internalRemoveAllItems() { + // Removes all Items + getAllItemIds().clear(); + if (isFiltered()) { + getFilteredItemIds().clear(); + } + } + + /** + * Removes a single item from the internal data structures of this class. + * This can be used to implement {@link #removeItem(Object)} in subclasses. + * + * No notification is sent, the caller has to fire a suitable item set + * change notification. + * + * @param itemId + * the identifier of the item to remove + * @return true if an item was successfully removed, false if failed to + * remove or no such item + */ + protected boolean internalRemoveItem(Object itemId) { + if (itemId == null) { + return false; + } + + boolean result = getAllItemIds().remove(itemId); + if (result && isFiltered()) { + getFilteredItemIds().remove(itemId); + } + + return result; + } + + // adding items + + /** + * Adds the bean to all internal data structures at the given position. + * Fails if an item with itemId is already in the container. Returns a the + * item if it was added successfully, null otherwise. + * + *

    + * Caller should initiate filtering after calling this method. + *

    + * + * For internal use only - subclasses should use + * {@link #internalAddItemAtEnd(Object, Item, boolean)}, + * {@link #internalAddItemAt(int, Object, Item, boolean)} and + * {@link #internalAddItemAfter(Object, Object, Item, boolean)} instead. + * + * @param position + * The position at which the item should be inserted in the + * unfiltered collection of items + * @param itemId + * The item identifier for the item to insert + * @param item + * The item to insert + * + * @return ITEMCLASS if the item was added successfully, null otherwise + */ + private ITEMCLASS internalAddAt(int position, ITEMIDTYPE itemId, + ITEMCLASS item) { + if (position < 0 || position > getAllItemIds().size() || itemId == null + || item == null) { + return null; + } + // Make sure that the item has not been added previously + if (getAllItemIds().contains(itemId)) { + return null; + } + + // "filteredList" will be updated in filterAll() which should be invoked + // by the caller after calling this method. + getAllItemIds().add(position, itemId); + registerNewItem(position, itemId, item); + + return item; + } + + /** + * Add an item at the end of the container, and perform filtering if + * necessary. An event is fired if the filtered view changes. + * + * @param newItemId + * @param item + * new item to add + * @param filter + * true to perform filtering and send event after adding the + * item, false to skip these operations for batch inserts - if + * false, caller needs to make sure these operations are + * performed at the end of the batch + * @return item added or null if no item was added + */ + protected ITEMCLASS internalAddItemAtEnd(ITEMIDTYPE newItemId, + ITEMCLASS item, boolean filter) { + ITEMCLASS newItem = internalAddAt(getAllItemIds().size(), newItemId, + item); + if (newItem != null && filter) { + // TODO filter only this item, use fireItemAdded() + filterAll(); + if (!isFiltered()) { + // TODO hack: does not detect change in filterAll() in this case + fireItemAdded(indexOfId(newItemId), newItemId, item); + } + } + return newItem; + } + + /** + * Add an item after a given (visible) item, and perform filtering. An event + * is fired if the filtered view changes. + * + * The new item is added at the beginning if previousItemId is null. + * + * @param previousItemId + * item id of a visible item after which to add the new item, or + * null to add at the beginning + * @param newItemId + * @param item + * new item to add + * @param filter + * true to perform filtering and send event after adding the + * item, false to skip these operations for batch inserts - if + * false, caller needs to make sure these operations are + * performed at the end of the batch + * @return item added or null if no item was added + */ + protected ITEMCLASS internalAddItemAfter(ITEMIDTYPE previousItemId, + ITEMIDTYPE newItemId, ITEMCLASS item, boolean filter) { + // only add if the previous item is visible + ITEMCLASS newItem = null; + if (previousItemId == null) { + newItem = internalAddAt(0, newItemId, item); + } else if (containsId(previousItemId)) { + newItem = internalAddAt(getAllItemIds().indexOf(previousItemId) + 1, + newItemId, item); + } + if (newItem != null && filter) { + // TODO filter only this item, use fireItemAdded() + filterAll(); + if (!isFiltered()) { + // TODO hack: does not detect change in filterAll() in this case + fireItemAdded(indexOfId(newItemId), newItemId, item); + } + } + return newItem; + } + + /** + * Add an item at a given (visible after filtering) item index, and perform + * filtering. An event is fired if the filtered view changes. + * + * @param index + * position where to add the item (visible/view index) + * @param newItemId + * @param item + * new item to add + * @param filter + * true to perform filtering and send event after adding the + * item, false to skip these operations for batch inserts - if + * false, caller needs to make sure these operations are + * performed at the end of the batch + * @return item added or null if no item was added + */ + protected ITEMCLASS internalAddItemAt(int index, ITEMIDTYPE newItemId, + ITEMCLASS item, boolean filter) { + if (index < 0 || index > size()) { + return null; + } else if (index == 0) { + // add before any item, visible or not + return internalAddItemAfter(null, newItemId, item, filter); + } else { + // if index==size(), adds immediately after last visible item + return internalAddItemAfter(getIdByIndex(index - 1), newItemId, + item, filter); + } + } + + /** + * Registers a new item as having been added to the container. This can + * involve storing the item or any relevant information about it in internal + * container-specific collections if necessary, as well as registering + * listeners etc. + * + * The full identifier list in {@link AbstractInMemoryContainer} has already + * been updated to reflect the new item when this method is called. + * + * @param position + * @param itemId + * @param item + */ + protected void registerNewItem(int position, ITEMIDTYPE itemId, + ITEMCLASS item) { + } + + // item set change notifications + + /** + * Notify item set change listeners that an item has been added to the + * container. + * + * @since 7.4 + * + * @param position + * position of the added item in the view + * @param itemId + * id of the added item + * @param item + * the added item + */ + protected void fireItemAdded(int position, ITEMIDTYPE itemId, + ITEMCLASS item) { + fireItemsAdded(position, itemId, 1); + } + + /** + * Notify item set change listeners that items has been added to the + * container. + * + * @param firstPosition + * position of the first visible added item in the view + * @param firstItemId + * id of the first visible added item + * @param numberOfItems + * the number of visible added items + */ + protected void fireItemsAdded(int firstPosition, ITEMIDTYPE firstItemId, + int numberOfItems) { + BaseItemAddEvent addEvent = new BaseItemAddEvent(this, firstItemId, + firstPosition, numberOfItems); + fireItemSetChange(addEvent); + } + + /** + * Notify item set change listeners that an item has been removed from the + * container. + * + * @since 7.4 + * + * @param position + * position of the removed item in the view prior to removal (if + * was visible) + * @param itemId + * id of the removed item, of type {@link Object} to satisfy + * {@link Container#removeItem(Object)} API + */ + protected void fireItemRemoved(int position, Object itemId) { + fireItemsRemoved(position, itemId, 1); + } + + /** + * Notify item set change listeners that items has been removed from the + * container. + * + * @param firstPosition + * position of the first visible removed item in the view prior + * to removal + * @param firstItemId + * id of the first visible removed item, of type {@link Object} + * to satisfy {@link Container#removeItem(Object)} API + * @param numberOfItems + * the number of removed visible items + * + */ + protected void fireItemsRemoved(int firstPosition, Object firstItemId, + int numberOfItems) { + BaseItemRemoveEvent removeEvent = new BaseItemRemoveEvent(this, + firstItemId, firstPosition, numberOfItems); + fireItemSetChange(removeEvent); + } + + // visible and filtered item identifier lists + + /** + * Returns the internal list of visible item identifiers after filtering. + * + * For internal use only. + */ + protected List getVisibleItemIds() { + if (isFiltered()) { + return getFilteredItemIds(); + } else { + return getAllItemIds(); + } + } + + /** + * Returns the item id of the first visible item after filtering. 'Null' is + * returned if there is no visible items. + *

    + * For internal use only. + * + * @since 7.4 + * + * @return item id of the first visible item + */ + protected ITEMIDTYPE getFirstVisibleItem() { + if (!getVisibleItemIds().isEmpty()) { + return getVisibleItemIds().get(0); + } + return null; + } + + /** + * Returns true is the container has active filters. + * + * @return true if the container is currently filtered + */ + protected boolean isFiltered() { + return filteredItemIds != null; + } + + /** + * Internal helper method to set the internal list of filtered item + * identifiers. Should not be used outside this class except for + * implementing clone(), may disappear from future versions. + * + * @param filteredItemIds + */ + @Deprecated + protected void setFilteredItemIds(List filteredItemIds) { + this.filteredItemIds = filteredItemIds; + } + + /** + * Internal helper method to get the internal list of filtered item + * identifiers. Should not be used outside this class except for + * implementing clone(), may disappear from future versions - use + * {@link #getVisibleItemIds()} in other contexts. + * + * @return List + */ + protected List getFilteredItemIds() { + return filteredItemIds; + } + + /** + * Internal helper method to set the internal list of all item identifiers. + * Should not be used outside this class except for implementing clone(), + * may disappear from future versions. + * + * @param allItemIds + */ + @Deprecated + protected void setAllItemIds(List allItemIds) { + this.allItemIds = allItemIds; + } + + /** + * Internal helper method to get the internal list of all item identifiers. + * Avoid using this method outside this class, may disappear in future + * versions. + * + * @return List + */ + protected List getAllItemIds() { + return allItemIds; + } + + /** + * Set the internal collection of filters without performing filtering. + * + * This method is mostly for internal use, use + * {@link #addFilter(com.com.vaadin.v7.data.Container.Filter)} and + * remove*Filter* (which also re-filter the container) instead + * when possible. + * + * @param filters + */ + protected void setFilters(Set filters) { + this.filters = filters; + } + + /** + * Returns the internal collection of filters. The returned collection + * should not be modified by callers outside this class. + * + * @return Set + */ + protected Set getFilters() { + return filters; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanContainer.java new file mode 100644 index 0000000000..0127adbfee --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanContainer.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util; + +import java.util.Collection; + +/** + * An in-memory container for JavaBeans. + * + *

    + * The properties of the container are determined automatically by introspecting + * the used JavaBean class. Only beans of the same type can be added to the + * container. + *

    + * + *

    + * In BeanContainer (unlike {@link BeanItemContainer}), the item IDs do not have + * to be the beans themselves. The container can be used either with explicit + * item IDs or the item IDs can be generated when adding beans. + *

    + * + *

    + * To use explicit item IDs, use the methods {@link #addItem(Object, Object)}, + * {@link #addItemAfter(Object, Object, Object)} and + * {@link #addItemAt(int, Object, Object)}. + *

    + * + *

    + * If a bean id resolver is set using + * {@link #setBeanIdResolver(com.vaadin.v7.data.util.AbstractBeanContainer.BeanIdResolver)} + * or {@link #setBeanIdProperty(Object)}, the methods {@link #addBean(Object)}, + * {@link #addBeanAfter(Object, Object)}, {@link #addBeanAt(int, Object)} and + * {@link #addAll(java.util.Collection)} can be used to add items to the + * container. If one of these methods is called, the resolver is used to + * generate an identifier for the item (must not return null). + *

    + * + *

    + * Note that explicit item identifiers can also be used when a resolver has been + * set by calling the addItem*() methods - the resolver is only used when adding + * beans using the addBean*() or {@link #addAll(Collection)} methods. + *

    + * + *

    + * It is not possible to add additional properties to the container. + *

    + * + * @param + * The type of the item identifier + * @param + * The type of the Bean + * + * @see AbstractBeanContainer + * @see BeanItemContainer + * + * @since 6.5 + */ +public class BeanContainer + extends AbstractBeanContainer { + + public BeanContainer(Class type) { + super(type); + } + + /** + * Adds the bean to the Container. + * + * @see com.com.vaadin.v7.data.Container#addItem(Object) + */ + @Override + public BeanItem addItem(IDTYPE itemId, BEANTYPE bean) { + if (itemId != null && bean != null) { + return super.addItem(itemId, bean); + } else { + return null; + } + } + + /** + * Adds the bean after the given item id. + * + * @see com.com.vaadin.v7.data.Container.Ordered#addItemAfter(Object, Object) + */ + @Override + public BeanItem addItemAfter(IDTYPE previousItemId, + IDTYPE newItemId, BEANTYPE bean) { + if (newItemId != null && bean != null) { + return super.addItemAfter(previousItemId, newItemId, bean); + } else { + return null; + } + } + + /** + * Adds a new bean at the given index. + * + * The bean is used both as the item contents and as the item identifier. + * + * @param index + * Index at which the bean should be added. + * @param newItemId + * The item id for the bean to add to the container. + * @param bean + * The bean to add to the container. + * + * @return Returns the new BeanItem or null if the operation fails. + */ + @Override + public BeanItem addItemAt(int index, IDTYPE newItemId, + BEANTYPE bean) { + if (newItemId != null && bean != null) { + return super.addItemAt(index, newItemId, bean); + } else { + return null; + } + } + + // automatic item id resolution + + /** + * Sets the bean id resolver to use a property of the beans as the + * identifier. + * + * @param propertyId + * the identifier of the property to use to find item identifiers + */ + public void setBeanIdProperty(Object propertyId) { + setBeanIdResolver(createBeanPropertyResolver(propertyId)); + } + + @Override + // overridden to make public + public void setBeanIdResolver( + BeanIdResolver beanIdResolver) { + super.setBeanIdResolver(beanIdResolver); + } + + @Override + // overridden to make public + public BeanItem addBean(BEANTYPE bean) + throws IllegalStateException, IllegalArgumentException { + return super.addBean(bean); + } + + @Override + // overridden to make public + public BeanItem addBeanAfter(IDTYPE previousItemId, BEANTYPE bean) + throws IllegalStateException, IllegalArgumentException { + return super.addBeanAfter(previousItemId, bean); + } + + @Override + // overridden to make public + public BeanItem addBeanAt(int index, BEANTYPE bean) + throws IllegalStateException, IllegalArgumentException { + return super.addBeanAt(index, bean); + } + + @Override + // overridden to make public + public void addAll(Collection collection) + throws IllegalStateException { + super.addAll(collection); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItem.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItem.java new file mode 100644 index 0000000000..78ec807fd1 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItem.java @@ -0,0 +1,266 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.util; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.vaadin.data.util.BeanUtil; + +/** + * A wrapper class for adding the Item interface to any Java Bean. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class BeanItem extends PropertysetItem { + + /** + * The bean which this Item is based on. + */ + private final BT bean; + + /** + *

    + * Creates a new instance of BeanItem and adds all properties + * of a Java Bean to it. The properties are identified by their respective + * bean names. + *

    + * + *

    + * Note : This version only supports introspectable bean properties and + * their getter and setter methods. Stand-alone is and + * are methods are not supported. + *

    + * + * @param bean + * the Java Bean to copy properties from. + * + */ + public BeanItem(BT bean) { + this(bean, (Class) bean.getClass()); + } + + /** + *

    + * Creates a new instance of BeanItem and adds all properties + * of a Java Bean to it. The properties are identified by their respective + * bean names. + *

    + * + *

    + * Note : This version only supports introspectable bean properties and + * their getter and setter methods. Stand-alone is and + * are methods are not supported. + *

    + * + * @since 7.4 + * + * @param bean + * the Java Bean to copy properties from. + * @param beanClass + * class of the {@code bean} + * + */ + public BeanItem(BT bean, Class beanClass) { + this(bean, getPropertyDescriptors(beanClass)); + } + + /** + *

    + * Creates a new instance of BeanItem using a pre-computed set + * of properties. The properties are identified by their respective bean + * names. + *

    + * + * @param bean + * the Java Bean to copy properties from. + * @param propertyDescriptors + * pre-computed property descriptors + */ + BeanItem(BT bean, + Map> propertyDescriptors) { + + this.bean = bean; + + for (VaadinPropertyDescriptor pd : propertyDescriptors.values()) { + addItemProperty(pd.getName(), pd.createProperty(bean)); + } + } + + /** + *

    + * Creates a new instance of BeanItem and adds all listed + * properties of a Java Bean to it - in specified order. The properties are + * identified by their respective bean names. + *

    + * + *

    + * Note : This version only supports introspectable bean properties and + * their getter and setter methods. Stand-alone is and + * are methods are not supported. + *

    + * + * @param bean + * the Java Bean to copy properties from. + * @param propertyIds + * id of the property. + */ + public BeanItem(BT bean, Collection propertyIds) { + + this.bean = bean; + + // Create bean information + LinkedHashMap> pds = getPropertyDescriptors( + (Class) bean.getClass()); + + // Add all the bean properties as MethodProperties to this Item + for (Object id : propertyIds) { + VaadinPropertyDescriptor pd = pds.get(id); + if (pd != null) { + addItemProperty(pd.getName(), pd.createProperty(bean)); + } + } + + } + + /** + *

    + * Creates a new instance of BeanItem and adds all listed + * properties of a Java Bean to it - in specified order. The properties are + * identified by their respective bean names. + *

    + * + *

    + * Note : This version only supports introspectable bean properties and + * their getter and setter methods. Stand-alone is and + * are methods are not supported. + *

    + * + * @param bean + * the Java Bean to copy properties from. + * @param propertyIds + * ids of the properties. + */ + public BeanItem(BT bean, String... propertyIds) { + this(bean, Arrays.asList(propertyIds)); + } + + /** + *

    + * Perform introspection on a Java Bean class to find its properties. + *

    + * + *

    + * Note : This version only supports introspectable bean properties and + * their getter and setter methods. Stand-alone is and + * are methods are not supported. + *

    + * + * @param beanClass + * the Java Bean class to get properties for. + * @return an ordered map from property names to property descriptors + */ + static LinkedHashMap> getPropertyDescriptors( + final Class beanClass) { + final LinkedHashMap> pdMap = new LinkedHashMap>(); + + // Try to introspect, if it fails, we just have an empty Item + try { + List propertyDescriptors = BeanUtil + .getBeanPropertyDescriptors(beanClass); + + // Add all the bean properties as MethodProperties to this Item + // later entries on the list overwrite earlier ones + for (PropertyDescriptor pd : propertyDescriptors) { + final Method getMethod = pd.getReadMethod(); + if ((getMethod != null) + && getMethod.getDeclaringClass() != Object.class) { + VaadinPropertyDescriptor vaadinPropertyDescriptor = new MethodPropertyDescriptor( + pd.getName(), pd.getPropertyType(), + pd.getReadMethod(), pd.getWriteMethod()); + pdMap.put(pd.getName(), vaadinPropertyDescriptor); + } + } + } catch (final java.beans.IntrospectionException ignored) { + } + + return pdMap; + } + + /** + * Expands nested bean properties by replacing a top-level property with + * some or all of its sub-properties. The expansion is not recursive. + * + * @param propertyId + * property id for the property whose sub-properties are to be + * expanded, + * @param subPropertyIds + * sub-properties to expand, all sub-properties are expanded if + * not specified + */ + public void expandProperty(String propertyId, String... subPropertyIds) { + Set subPropertySet = new HashSet( + Arrays.asList(subPropertyIds)); + + if (0 == subPropertyIds.length) { + // Enumerate all sub-properties + Class propertyType = getItemProperty(propertyId).getType(); + Map pds = getPropertyDescriptors(propertyType); + subPropertySet.addAll(pds.keySet()); + } + + for (String subproperty : subPropertySet) { + String qualifiedPropertyId = propertyId + "." + subproperty; + addNestedProperty(qualifiedPropertyId); + } + + removeItemProperty(propertyId); + } + + /** + * Adds a nested property to the item. The property must not exist in the + * item already and must of form "field1.field2" where field2 is a field in + * the object referenced to by field1. If an intermediate property returns + * null, the property will return a null value + * + * @param nestedPropertyId + * property id to add. + */ + public void addNestedProperty(String nestedPropertyId) { + addItemProperty(nestedPropertyId, + new NestedMethodProperty(getBean(), nestedPropertyId)); + } + + /** + * Gets the underlying JavaBean object. + * + * @return the bean object. + */ + public BT getBean() { + return bean; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItemContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItemContainer.java new file mode 100644 index 0000000000..1e75901b04 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/BeanItemContainer.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.v7.data.util; + +import java.util.Collection; + +/** + * An in-memory container for JavaBeans. + * + *

    + * The properties of the container are determined automatically by introspecting + * the used JavaBean class. Only beans of the same type can be added to the + * container. + *

    + * + *

    + * BeanItemContainer uses the beans themselves as identifiers. The + * {@link Object#hashCode()} of a bean is used when storing and looking up beans + * so it must not change during the lifetime of the bean (it should not depend + * on any part of the bean that can be modified). Typically this restricts the + * implementation of {@link Object#equals(Object)} as well in order for it to + * fulfill the contract between {@code equals()} and {@code hashCode()}. + *

    + * + *

    + * To add items to the container, use the methods {@link #addBean(Object)}, + * {@link #addBeanAfter(Object, Object)} and {@link #addBeanAt(int, Object)}. + * Also {@link #addItem(Object)}, {@link #addItemAfter(Object, Object)} and + * {@link #addItemAt(int, Object)} can be used as synonyms for them. + *

    + * + *

    + * It is not possible to add additional properties to the container. + *

    + * + * @param + * The type of the Bean + * + * @since 5.4 + */ +@SuppressWarnings("serial") +public class BeanItemContainer + extends AbstractBeanContainer { + + /** + * Bean identity resolver that returns the bean itself as its item + * identifier. + * + * This corresponds to the old behavior of {@link BeanItemContainer}, and + * requires suitable (identity-based) equals() and hashCode() methods on the + * beans. + * + * @param + * + * @since 6.5 + */ + private static class IdentityBeanIdResolver + implements BeanIdResolver { + + @Override + public BT getIdForBean(BT bean) { + return bean; + } + + } + + /** + * Constructs a {@code BeanItemContainer} for beans of the given type. + * + * @param type + * the type of the beans that will be added to the container. + * @throws IllegalArgumentException + * If {@code type} is null + */ + public BeanItemContainer(Class type) + throws IllegalArgumentException { + super(type); + super.setBeanIdResolver(new IdentityBeanIdResolver()); + } + + /** + * Constructs a {@code BeanItemContainer} and adds the given beans to it. + * The collection must not be empty. + * {@link BeanItemContainer#BeanItemContainer(Class)} can be used for + * creating an initially empty {@code BeanItemContainer}. + * + * Note that when using this constructor, the actual class of the first item + * in the collection is used to determine the bean properties supported by + * the container instance, and only beans of that class or its subclasses + * can be added to the collection. If this is problematic or empty + * collections need to be supported, use {@link #BeanItemContainer(Class)} + * and {@link #addAll(Collection)} instead. + * + * @param collection + * a non empty {@link Collection} of beans. + * @throws IllegalArgumentException + * If the collection is null or empty. + * + * @deprecated As of 6.5, use {@link #BeanItemContainer(Class, Collection)} + * instead + */ + @SuppressWarnings("unchecked") + @Deprecated + public BeanItemContainer(Collection collection) + throws IllegalArgumentException { + // must assume the class is BT + // the class information is erased by the compiler + this((Class) getBeanClassForCollection(collection), + collection); + } + + /** + * Internal helper method to support the deprecated {@link Collection} + * container. + * + * @param + * @param collection + * @return + * @throws IllegalArgumentException + */ + @SuppressWarnings("unchecked") + @Deprecated + private static Class getBeanClassForCollection( + Collection collection) + throws IllegalArgumentException { + if (collection == null || collection.isEmpty()) { + throw new IllegalArgumentException( + "The collection passed to BeanItemContainer constructor must not be null or empty. Use the other BeanItemContainer constructor."); + } + return (Class) collection.iterator().next().getClass(); + } + + /** + * Constructs a {@code BeanItemContainer} and adds the given beans to it. + * + * @param type + * the type of the beans that will be added to the container. + * @param collection + * a {@link Collection} of beans (can be empty or null). + * @throws IllegalArgumentException + * If {@code type} is null + */ + public BeanItemContainer(Class type, + Collection collection) + throws IllegalArgumentException { + super(type); + super.setBeanIdResolver(new IdentityBeanIdResolver()); + + if (collection != null) { + addAll(collection); + } + } + + /** + * Adds all the beans from a {@link Collection} in one go. More efficient + * than adding them one by one. + * + * @param collection + * The collection of beans to add. Must not be null. + */ + @Override + public void addAll(Collection collection) { + super.addAll(collection); + } + + /** + * Adds the bean after the given bean. + * + * The bean is used both as the item contents and as the item identifier. + * + * @param previousItemId + * the bean (of type BT) after which to add newItemId + * @param newItemId + * the bean (of type BT) to add (not null) + * + * @see com.com.vaadin.v7.data.Container.Ordered#addItemAfter(Object, Object) + */ + @Override + @SuppressWarnings("unchecked") + public BeanItem addItemAfter(Object previousItemId, + Object newItemId) throws IllegalArgumentException { + return super.addBeanAfter((BEANTYPE) previousItemId, + (BEANTYPE) newItemId); + } + + /** + * Adds a new bean at the given index. + * + * The bean is used both as the item contents and as the item identifier. + * + * @param index + * Index at which the bean should be added. + * @param newItemId + * The bean to add to the container. + * @return Returns the new BeanItem or null if the operation fails. + */ + @Override + @SuppressWarnings("unchecked") + public BeanItem addItemAt(int index, Object newItemId) + throws IllegalArgumentException { + return super.addBeanAt(index, (BEANTYPE) newItemId); + } + + /** + * Adds the bean to the Container. + * + * The bean is used both as the item contents and as the item identifier. + * + * @see com.com.vaadin.v7.data.Container#addItem(Object) + */ + @Override + @SuppressWarnings("unchecked") + public BeanItem addItem(Object itemId) { + return super.addBean((BEANTYPE) itemId); + } + + /** + * Adds the bean to the Container. + * + * The bean is used both as the item contents and as the item identifier. + * + * @see com.com.vaadin.v7.data.Container#addItem(Object) + */ + @Override + public BeanItem addBean(BEANTYPE bean) { + return addItem(bean); + } + + /** + * Unsupported in BeanItemContainer. + */ + @Override + protected void setBeanIdResolver( + AbstractBeanContainer.BeanIdResolver beanIdResolver) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "BeanItemContainer always uses an IdentityBeanIdResolver"); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/ContainerHierarchicalWrapper.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/ContainerHierarchicalWrapper.java new file mode 100644 index 0000000000..308a1e9fd2 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/ContainerHierarchicalWrapper.java @@ -0,0 +1,865 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.util; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; + +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; + +/** + *

    + * A wrapper class for adding external hierarchy to containers not implementing + * the {@link com.vaadin.v7.data.Container.Hierarchical} interface. + *

    + * + *

    + * If the wrapped container is changed directly (that is, not through the + * wrapper), and does not implement Container.ItemSetChangeNotifier and/or + * Container.PropertySetChangeNotifier the hierarchy information must be updated + * with the {@link #updateHierarchicalWrapper()} method. + *

    + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class ContainerHierarchicalWrapper implements Container.Hierarchical, + Container.ItemSetChangeNotifier, Container.PropertySetChangeNotifier { + + /** The wrapped container */ + private final Container container; + + /** Set of IDs of those contained Items that can't have children. */ + private HashSet noChildrenAllowed = null; + + /** Mapping from Item ID to parent Item ID */ + private Hashtable parent = null; + + /** Mapping from Item ID to a list of child IDs */ + private Hashtable> children = null; + + /** List that contains all root elements of the container. */ + private LinkedHashSet roots = null; + + /** Is the wrapped container hierarchical by itself ? */ + private boolean hierarchical; + + /** + * A comparator that sorts the listed items before other items. Otherwise, + * the order is undefined. + */ + private static class ListedItemsFirstComparator + implements Comparator, Serializable { + private final Collection itemIds; + + private ListedItemsFirstComparator(Collection itemIds) { + this.itemIds = itemIds; + } + + @Override + public int compare(Object o1, Object o2) { + if (o1.equals(o2)) { + return 0; + } + for (Object id : itemIds) { + if (id == o1) { + return -1; + } else if (id == o2) { + return 1; + } + } + return 0; + } + } + + /** + * Constructs a new hierarchical wrapper for an existing Container. Works + * even if the to-be-wrapped container already implements the + * Container.Hierarchical interface. + * + * @param toBeWrapped + * the container that needs to be accessed hierarchically + * @see #updateHierarchicalWrapper() + */ + public ContainerHierarchicalWrapper(Container toBeWrapped) { + + container = toBeWrapped; + hierarchical = container instanceof Container.Hierarchical; + + // Check arguments + if (container == null) { + throw new NullPointerException("Null can not be wrapped"); + } + + // Create initial order if needed + if (!hierarchical) { + noChildrenAllowed = new HashSet(); + parent = new Hashtable(); + children = new Hashtable>(); + roots = new LinkedHashSet(container.getItemIds()); + } + + updateHierarchicalWrapper(); + + } + + /** + * Updates the wrapper's internal hierarchy data to include all Items in the + * underlying container. If the contents of the wrapped container change + * without the wrapper's knowledge, this method needs to be called to update + * the hierarchy information of the Items. + */ + public void updateHierarchicalWrapper() { + + if (!hierarchical) { + + // Recreate hierarchy and data structures if missing + if (noChildrenAllowed == null || parent == null || children == null + || roots == null) { + noChildrenAllowed = new HashSet(); + parent = new Hashtable(); + children = new Hashtable>(); + roots = new LinkedHashSet(container.getItemIds()); + } + + // Check that the hierarchy is up-to-date + else { + + // ensure order of root and child lists is same as in wrapped + // container + Collection itemIds = container.getItemIds(); + Comparator basedOnOrderFromWrappedContainer = new ListedItemsFirstComparator( + itemIds); + + // Calculate the set of all items in the hierarchy + final HashSet s = new HashSet(); + s.addAll(parent.keySet()); + s.addAll(children.keySet()); + s.addAll(roots); + + // Remove unnecessary items + for (final Iterator i = s.iterator(); i.hasNext();) { + final Object id = i.next(); + if (!container.containsId(id)) { + removeFromHierarchyWrapper(id); + } + } + + // Add all the missing items + final Collection ids = container.getItemIds(); + for (final Iterator i = ids.iterator(); i.hasNext();) { + final Object id = i.next(); + if (!s.contains(id)) { + addToHierarchyWrapper(id); + s.add(id); + } + } + + Object[] array = roots.toArray(); + Arrays.sort(array, basedOnOrderFromWrappedContainer); + roots = new LinkedHashSet(); + for (int i = 0; i < array.length; i++) { + roots.add(array[i]); + } + for (Object object : children.keySet()) { + LinkedList object2 = children.get(object); + Collections.sort(object2, basedOnOrderFromWrappedContainer); + } + + } + } + } + + /** + * Removes the specified Item from the wrapper's internal hierarchy + * structure. + *

    + * Note : The Item is not removed from the underlying Container. + *

    + * + * @param itemId + * the ID of the item to remove from the hierarchy. + */ + private void removeFromHierarchyWrapper(Object itemId) { + + LinkedList oprhanedChildren = children.remove(itemId); + if (oprhanedChildren != null) { + for (Object object : oprhanedChildren) { + // make orphaned children root nodes + setParent(object, null); + } + } + + roots.remove(itemId); + final Object p = parent.get(itemId); + if (p != null) { + final LinkedList c = children.get(p); + if (c != null) { + c.remove(itemId); + } + } + parent.remove(itemId); + noChildrenAllowed.remove(itemId); + } + + /** + * Adds the specified Item specified to the internal hierarchy structure. + * The new item is added as a root Item. The underlying container is not + * modified. + * + * @param itemId + * the ID of the item to add to the hierarchy. + */ + private void addToHierarchyWrapper(Object itemId) { + roots.add(itemId); + + } + + /* + * Can the specified Item have any children? Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public boolean areChildrenAllowed(Object itemId) { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container) + .areChildrenAllowed(itemId); + } + + if (noChildrenAllowed.contains(itemId)) { + return false; + } + + return containsId(itemId); + } + + /* + * Gets the IDs of the children of the specified Item. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Collection getChildren(Object itemId) { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container).getChildren(itemId); + } + + final Collection c = children.get(itemId); + if (c == null) { + return null; + } + return Collections.unmodifiableCollection(c); + } + + /* + * Gets the ID of the parent of the specified Item. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Object getParent(Object itemId) { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container).getParent(itemId); + } + + return parent.get(itemId); + } + + /* + * Is the Item corresponding to the given ID a leaf node? Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public boolean hasChildren(Object itemId) { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container).hasChildren(itemId); + } + + LinkedList list = children.get(itemId); + return (list != null && !list.isEmpty()); + } + + /* + * Is the Item corresponding to the given ID a root node? Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public boolean isRoot(Object itemId) { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container).isRoot(itemId); + } + + if (parent.containsKey(itemId)) { + return false; + } + + return containsId(itemId); + } + + /* + * Gets the IDs of the root elements in the container. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Collection rootItemIds() { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container).rootItemIds(); + } + + return Collections.unmodifiableCollection(roots); + } + + /** + *

    + * Sets the given Item's capability to have children. If the Item identified + * with the itemId already has children and the areChildrenAllowed is false + * this method fails and false is returned; the children must + * be first explicitly removed with + * {@link #setParent(Object itemId, Object newParentId)} or + * {@link com.vaadin.v7.data.Container#removeItem(Object itemId)}. + *

    + * + * @param itemId + * the ID of the Item in the container whose child capability is + * to be set. + * @param childrenAllowed + * the boolean value specifying if the Item can have children or + * not. + * @return true if the operation succeeded, false + * if not + */ + @Override + public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container) + .setChildrenAllowed(itemId, childrenAllowed); + } + + // Check that the item is in the container + if (!containsId(itemId)) { + return false; + } + + // Update status + if (childrenAllowed) { + noChildrenAllowed.remove(itemId); + } else { + noChildrenAllowed.add(itemId); + } + + return true; + } + + /** + *

    + * Sets the parent of an Item. The new parent item must exist and be able to + * have children. (canHaveChildren(newParentId) == true). It is + * also possible to detach a node from the hierarchy (and thus make it root) + * by setting the parent null. + *

    + * + * @param itemId + * the ID of the item to be set as the child of the Item + * identified with newParentId. + * @param newParentId + * the ID of the Item that's to be the new parent of the Item + * identified with itemId. + * @return true if the operation succeeded, false + * if not + */ + @Override + public boolean setParent(Object itemId, Object newParentId) { + + // If the wrapped container implements the method directly, use it + if (hierarchical) { + return ((Container.Hierarchical) container).setParent(itemId, + newParentId); + } + + // Check that the item is in the container + if (!containsId(itemId)) { + return false; + } + + // Get the old parent + final Object oldParentId = parent.get(itemId); + + // Check if no change is necessary + if ((newParentId == null && oldParentId == null) + || (newParentId != null && newParentId.equals(oldParentId))) { + return true; + } + + // Making root + if (newParentId == null) { + + // Remove from old parents children list + final LinkedList l = children.get(oldParentId); + if (l != null) { + l.remove(itemId); + if (l.isEmpty()) { + children.remove(itemId); + } + } + + // Add to be a root + roots.add(itemId); + + // Update parent + parent.remove(itemId); + + fireItemSetChangeIfAbstractContainer(); + + return true; + } + + // Check that the new parent exists in container and can have + // children + if (!containsId(newParentId) + || noChildrenAllowed.contains(newParentId)) { + return false; + } + + // Check that setting parent doesn't result to a loop + Object o = newParentId; + while (o != null && !o.equals(itemId)) { + o = parent.get(o); + } + if (o != null) { + return false; + } + + // Update parent + parent.put(itemId, newParentId); + LinkedList pcl = children.get(newParentId); + if (pcl == null) { + pcl = new LinkedList(); + children.put(newParentId, pcl); + } + pcl.add(itemId); + + // Remove from old parent or root + if (oldParentId == null) { + roots.remove(itemId); + } else { + final LinkedList l = children.get(oldParentId); + if (l != null) { + l.remove(itemId); + if (l.isEmpty()) { + children.remove(oldParentId); + } + } + } + + fireItemSetChangeIfAbstractContainer(); + + return true; + } + + /** + * inform container (if it is instance of AbstractContainer) about the + * change in hierarchy (#15421) + */ + private void fireItemSetChangeIfAbstractContainer() { + if (container instanceof AbstractContainer) { + ((AbstractContainer) container).fireItemSetChange(); + } + } + + /** + * Creates a new Item into the Container, assigns it an automatic ID, and + * adds it to the hierarchy. + * + * @return the autogenerated ID of the new Item or null if the + * operation failed + * @throws UnsupportedOperationException + * if the addItem is not supported. + */ + @Override + public Object addItem() throws UnsupportedOperationException { + + final Object id = container.addItem(); + if (!hierarchical && id != null) { + addToHierarchyWrapper(id); + } + return id; + } + + /** + * Adds a new Item by its ID to the underlying container and to the + * hierarchy. + * + * @param itemId + * the ID of the Item to be created. + * @return the added Item or null if the operation failed. + * @throws UnsupportedOperationException + * if the addItem is not supported. + */ + @Override + public Item addItem(Object itemId) throws UnsupportedOperationException { + // Null ids are not accepted + if (itemId == null) { + return null; + } + + final Item item = container.addItem(itemId); + if (!hierarchical && item != null) { + addToHierarchyWrapper(itemId); + } + return item; + } + + /** + * Removes all items from the underlying container and from the hierarcy. + * + * @return true if the operation succeeded, false + * if not + * @throws UnsupportedOperationException + * if the removeAllItems is not supported. + */ + @Override + public boolean removeAllItems() throws UnsupportedOperationException { + + final boolean success = container.removeAllItems(); + + if (!hierarchical && success) { + roots.clear(); + parent.clear(); + children.clear(); + noChildrenAllowed.clear(); + } + return success; + } + + /** + * Removes an Item specified by the itemId from the underlying container and + * from the hierarchy. + * + * @param itemId + * the ID of the Item to be removed. + * @return true if the operation succeeded, false + * if not + * @throws UnsupportedOperationException + * if the removeItem is not supported. + */ + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + + final boolean success = container.removeItem(itemId); + + if (!hierarchical && success) { + removeFromHierarchyWrapper(itemId); + } + + return success; + } + + /** + * Removes the Item identified by given itemId and all its children. + * + * @see #removeItem(Object) + * @param itemId + * the identifier of the Item to be removed + * @return true if the operation succeeded + */ + public boolean removeItemRecursively(Object itemId) { + return HierarchicalContainer.removeItemRecursively(this, itemId); + } + + /** + * Adds a new Property to all Items in the Container. + * + * @param propertyId + * the ID of the new Property. + * @param type + * the Data type of the new Property. + * @param defaultValue + * the value all created Properties are initialized to. + * @return true if the operation succeeded, false + * if not + * @throws UnsupportedOperationException + * if the addContainerProperty is not supported. + */ + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + + return container.addContainerProperty(propertyId, type, defaultValue); + } + + /** + * Removes the specified Property from the underlying container and from the + * hierarchy. + *

    + * Note : The Property will be removed from all Items in the Container. + *

    + * + * @param propertyId + * the ID of the Property to remove. + * @return true if the operation succeeded, false + * if not + * @throws UnsupportedOperationException + * if the removeContainerProperty is not supported. + */ + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + return container.removeContainerProperty(propertyId); + } + + /* + * Does the container contain the specified Item? Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public boolean containsId(Object itemId) { + return container.containsId(itemId); + } + + /* + * Gets the specified Item from the container. Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public Item getItem(Object itemId) { + return container.getItem(itemId); + } + + /* + * Gets the ID's of all Items stored in the Container Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Collection getItemIds() { + return container.getItemIds(); + } + + /* + * Gets the Property identified by the given itemId and propertyId from the + * Container Don't add a JavaDoc comment here, we use the default + * documentation from implemented interface. + */ + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + return container.getContainerProperty(itemId, propertyId); + } + + /* + * Gets the ID's of all Properties stored in the Container Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Collection getContainerPropertyIds() { + return container.getContainerPropertyIds(); + } + + /* + * Gets the data type of all Properties identified by the given Property ID. + * Don't add a JavaDoc comment here, we use the default documentation from + * implemented interface. + */ + @Override + public Class getType(Object propertyId) { + return container.getType(propertyId); + } + + /* + * Gets the number of Items in the Container. Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public int size() { + int size = container.size(); + assert size >= 0; + return size; + } + + /* + * Registers a new Item set change listener for this Container. Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public void addItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (container instanceof Container.ItemSetChangeNotifier) { + ((Container.ItemSetChangeNotifier) container) + .addItemSetChangeListener(new PiggybackListener(listener)); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemSetChangeListener(com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Override + @Deprecated + public void addListener(Container.ItemSetChangeListener listener) { + addItemSetChangeListener(listener); + } + + /* + * Removes a Item set change listener from the object. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public void removeItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (container instanceof Container.ItemSetChangeNotifier) { + ((Container.ItemSetChangeNotifier) container) + .removeItemSetChangeListener( + new PiggybackListener(listener)); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeItemSetChangeListener(com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(Container.ItemSetChangeListener listener) { + removeItemSetChangeListener(listener); + } + + /* + * Registers a new Property set change listener for this Container. Don't + * add a JavaDoc comment here, we use the default documentation from + * implemented interface. + */ + @Override + public void addPropertySetChangeListener( + Container.PropertySetChangeListener listener) { + if (container instanceof Container.PropertySetChangeNotifier) { + ((Container.PropertySetChangeNotifier) container) + .addPropertySetChangeListener( + new PiggybackListener(listener)); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addPropertySetChangeListener(com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Override + @Deprecated + public void addListener(Container.PropertySetChangeListener listener) { + addPropertySetChangeListener(listener); + } + + /* + * Removes a Property set change listener from the object. Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public void removePropertySetChangeListener( + Container.PropertySetChangeListener listener) { + if (container instanceof Container.PropertySetChangeNotifier) { + ((Container.PropertySetChangeNotifier) container) + .removePropertySetChangeListener( + new PiggybackListener(listener)); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removePropertySetChangeListener(com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(Container.PropertySetChangeListener listener) { + removePropertySetChangeListener(listener); + } + + /** + * This listener 'piggybacks' on the real listener in order to update the + * wrapper when needed. It proxies equals() and hashCode() to the real + * listener so that the correct listener gets removed. + * + */ + private class PiggybackListener + implements Container.PropertySetChangeListener, + Container.ItemSetChangeListener { + + Object listener; + + public PiggybackListener(Object realListener) { + listener = realListener; + } + + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + updateHierarchicalWrapper(); + ((Container.ItemSetChangeListener) listener) + .containerItemSetChange(event); + + } + + @Override + public void containerPropertySetChange(PropertySetChangeEvent event) { + updateHierarchicalWrapper(); + ((Container.PropertySetChangeListener) listener) + .containerPropertySetChange(event); + + } + + @Override + public boolean equals(Object obj) { + return obj == listener || (obj != null && obj.equals(listener)); + } + + @Override + public int hashCode() { + return listener.hashCode(); + } + + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/FilesystemContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/FilesystemContainer.java new file mode 100644 index 0000000000..7800a6ff50 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/FilesystemContainer.java @@ -0,0 +1,924 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import com.vaadin.server.Resource; +import com.vaadin.util.FileTypeResolver; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; + +/** + * A hierarchical container wrapper for a filesystem. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class FilesystemContainer implements Container.Hierarchical { + + /** + * String identifier of a file's "name" property. + */ + public static String PROPERTY_NAME = "Name"; + + /** + * String identifier of a file's "size" property. + */ + public static String PROPERTY_SIZE = "Size"; + + /** + * String identifier of a file's "icon" property. + */ + public static String PROPERTY_ICON = "Icon"; + + /** + * String identifier of a file's "last modified" property. + */ + public static String PROPERTY_LASTMODIFIED = "Last Modified"; + + /** + * List of the string identifiers for the available properties. + */ + public static Collection FILE_PROPERTIES; + + private final static Method FILEITEM_LASTMODIFIED; + + private final static Method FILEITEM_NAME; + + private final static Method FILEITEM_ICON; + + private final static Method FILEITEM_SIZE; + + static { + + FILE_PROPERTIES = new ArrayList(); + FILE_PROPERTIES.add(PROPERTY_NAME); + FILE_PROPERTIES.add(PROPERTY_ICON); + FILE_PROPERTIES.add(PROPERTY_SIZE); + FILE_PROPERTIES.add(PROPERTY_LASTMODIFIED); + FILE_PROPERTIES = Collections.unmodifiableCollection(FILE_PROPERTIES); + try { + FILEITEM_LASTMODIFIED = FileItem.class.getMethod("lastModified", + new Class[] {}); + FILEITEM_NAME = FileItem.class.getMethod("getName", new Class[] {}); + FILEITEM_ICON = FileItem.class.getMethod("getIcon", new Class[] {}); + FILEITEM_SIZE = FileItem.class.getMethod("getSize", new Class[] {}); + } catch (final NoSuchMethodException e) { + throw new RuntimeException( + "Internal error finding methods in FilesystemContainer"); + } + } + + private File[] roots = new File[] {}; + + private FilenameFilter filter = null; + + private boolean recursive = true; + + /** + * Constructs a new FileSystemContainer with the specified file + * as the root of the filesystem. The files are included recursively. + * + * @param root + * the root file for the new file-system container. Null values + * are ignored. + */ + public FilesystemContainer(File root) { + if (root != null) { + roots = new File[] { root }; + } + } + + /** + * Constructs a new FileSystemContainer with the specified file + * as the root of the filesystem. The files are included recursively. + * + * @param root + * the root file for the new file-system container. + * @param recursive + * should the container recursively contain subdirectories. + */ + public FilesystemContainer(File root, boolean recursive) { + this(root); + setRecursive(recursive); + } + + /** + * Constructs a new FileSystemContainer with the specified file + * as the root of the filesystem. + * + * @param root + * the root file for the new file-system container. + * @param extension + * the Filename extension (w/o separator) to limit the files in + * container. + * @param recursive + * should the container recursively contain subdirectories. + */ + public FilesystemContainer(File root, String extension, boolean recursive) { + this(root); + this.setFilter(extension); + setRecursive(recursive); + } + + /** + * Constructs a new FileSystemContainer with the specified root + * and recursivity status. + * + * @param root + * the root file for the new file-system container. + * @param filter + * the Filename filter to limit the files in container. + * @param recursive + * should the container recursively contain subdirectories. + */ + public FilesystemContainer(File root, FilenameFilter filter, + boolean recursive) { + this(root); + this.setFilter(filter); + setRecursive(recursive); + } + + /** + * Adds new root file directory. Adds a file to be included as root file + * directory in the FilesystemContainer. + * + * @param root + * the File to be added as root directory. Null values are + * ignored. + */ + public void addRoot(File root) { + if (root != null) { + final File[] newRoots = new File[roots.length + 1]; + for (int i = 0; i < roots.length; i++) { + newRoots[i] = roots[i]; + } + newRoots[roots.length] = root; + roots = newRoots; + } + } + + /** + * Tests if the specified Item in the container may have children. Since a + * FileSystemContainer contains files and directories, this + * method returns true for directory Items only. + * + * @param itemId + * the id of the item. + * @return true if the specified Item is a directory, + * false otherwise. + */ + @Override + public boolean areChildrenAllowed(Object itemId) { + return itemId instanceof File && ((File) itemId).canRead() + && ((File) itemId).isDirectory(); + } + + /* + * Gets the ID's of all Items who are children of the specified Item. Don't + * add a JavaDoc comment here, we use the default documentation from + * implemented interface. + */ + @Override + public Collection getChildren(Object itemId) { + + if (!(itemId instanceof File)) { + return Collections.unmodifiableCollection(new LinkedList()); + } + File[] f; + if (filter != null) { + f = ((File) itemId).listFiles(filter); + } else { + f = ((File) itemId).listFiles(); + } + + if (f == null) { + return Collections.unmodifiableCollection(new LinkedList()); + } + + final List l = Arrays.asList(f); + Collections.sort(l); + + return Collections.unmodifiableCollection(l); + } + + /* + * Gets the parent item of the specified Item. Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public Object getParent(Object itemId) { + + if (!(itemId instanceof File)) { + return null; + } + return ((File) itemId).getParentFile(); + } + + /* + * Tests if the specified Item has any children. Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public boolean hasChildren(Object itemId) { + + if (!(itemId instanceof File)) { + return false; + } + String[] l; + if (filter != null) { + l = ((File) itemId).list(filter); + } else { + l = ((File) itemId).list(); + } + return (l != null) && (l.length > 0); + } + + /* + * Tests if the specified Item is the root of the filesystem. Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public boolean isRoot(Object itemId) { + + if (!(itemId instanceof File)) { + return false; + } + for (int i = 0; i < roots.length; i++) { + if (roots[i].equals(itemId)) { + return true; + } + } + return false; + } + + /* + * Gets the ID's of all root Items in the container. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Collection rootItemIds() { + + File[] f; + + // in single root case we use children + if (roots.length == 1) { + if (filter != null) { + f = roots[0].listFiles(filter); + } else { + f = roots[0].listFiles(); + } + } else { + f = roots; + } + + if (f == null) { + return Collections.unmodifiableCollection(new LinkedList()); + } + + final List l = Arrays.asList(f); + Collections.sort(l); + + return Collections.unmodifiableCollection(l); + } + + /** + * Returns false when conversion from files to directories is + * not supported. + * + * @param itemId + * the ID of the item. + * @param areChildrenAllowed + * the boolean value specifying if the Item can have children or + * not. + * @return true if the operaton is successful otherwise + * false. + * @throws UnsupportedOperationException + * if the setChildrenAllowed is not supported. + */ + @Override + public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) + throws UnsupportedOperationException { + + throw new UnsupportedOperationException( + "Conversion file to/from directory is not supported"); + } + + /** + * Returns false when moving files around in the filesystem is + * not supported. + * + * @param itemId + * the ID of the item. + * @param newParentId + * the ID of the Item that's to be the new parent of the Item + * identified with itemId. + * @return true if the operation is successful otherwise + * false. + * @throws UnsupportedOperationException + * if the setParent is not supported. + */ + @Override + public boolean setParent(Object itemId, Object newParentId) + throws UnsupportedOperationException { + + throw new UnsupportedOperationException("File moving is not supported"); + } + + /* + * Tests if the filesystem contains the specified Item. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public boolean containsId(Object itemId) { + + if (!(itemId instanceof File)) { + return false; + } + boolean val = false; + + // Try to match all roots + for (int i = 0; i < roots.length; i++) { + try { + val |= ((File) itemId).getCanonicalPath() + .startsWith(roots[i].getCanonicalPath()); + } catch (final IOException e) { + // Exception ignored + } + + } + if (val && filter != null) { + val &= filter.accept(((File) itemId).getParentFile(), + ((File) itemId).getName()); + } + return val; + } + + /* + * Gets the specified Item from the filesystem. Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public Item getItem(Object itemId) { + + if (!(itemId instanceof File)) { + return null; + } + return new FileItem((File) itemId); + } + + /** + * Internal recursive method to add the files under the specified directory + * to the collection. + * + * @param col + * the collection where the found items are added + * @param f + * the root file where to start adding files + */ + private void addItemIds(Collection col, File f) { + File[] l; + if (filter != null) { + l = f.listFiles(filter); + } else { + l = f.listFiles(); + } + if (l == null) { + // File.listFiles returns null if File does not exist or if there + // was an IO error (permission denied) + return; + } + final List ll = Arrays.asList(l); + Collections.sort(ll); + + for (final Iterator i = ll.iterator(); i.hasNext();) { + final File lf = i.next(); + col.add(lf); + if (lf.isDirectory()) { + addItemIds(col, lf); + } + } + } + + /* + * Gets the IDs of Items in the filesystem. Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public Collection getItemIds() { + + if (recursive) { + final Collection col = new ArrayList(); + for (int i = 0; i < roots.length; i++) { + addItemIds(col, roots[i]); + } + return Collections.unmodifiableCollection(col); + } else { + File[] f; + if (roots.length == 1) { + if (filter != null) { + f = roots[0].listFiles(filter); + } else { + f = roots[0].listFiles(); + } + } else { + f = roots; + } + + if (f == null) { + return Collections + .unmodifiableCollection(new LinkedList()); + } + + final List l = Arrays.asList(f); + Collections.sort(l); + return Collections.unmodifiableCollection(l); + } + + } + + /** + * Gets the specified property of the specified file Item. The available + * file properties are "Name", "Size" and "Last Modified". If propertyId is + * not one of those, null is returned. + * + * @param itemId + * the ID of the file whose property is requested. + * @param propertyId + * the property's ID. + * @return the requested property's value, or null + */ + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + + if (!(itemId instanceof File)) { + return null; + } + + if (propertyId.equals(PROPERTY_NAME)) { + return new MethodProperty(getType(propertyId), + new FileItem((File) itemId), FILEITEM_NAME, null); + } + + if (propertyId.equals(PROPERTY_ICON)) { + return new MethodProperty(getType(propertyId), + new FileItem((File) itemId), FILEITEM_ICON, null); + } + + if (propertyId.equals(PROPERTY_SIZE)) { + return new MethodProperty(getType(propertyId), + new FileItem((File) itemId), FILEITEM_SIZE, null); + } + + if (propertyId.equals(PROPERTY_LASTMODIFIED)) { + return new MethodProperty(getType(propertyId), + new FileItem((File) itemId), FILEITEM_LASTMODIFIED, null); + } + + return null; + } + + /** + * Gets the collection of available file properties. + * + * @return Unmodifiable collection containing all available file properties. + */ + @Override + public Collection getContainerPropertyIds() { + return FILE_PROPERTIES; + } + + /** + * Gets the specified property's data type. "Name" is a String, + * "Size" is a Long, "Last Modified" is a Date. If + * propertyId is not one of those, null is returned. + * + * @param propertyId + * the ID of the property whose type is requested. + * @return data type of the requested property, or null + */ + @Override + public Class getType(Object propertyId) { + + if (propertyId.equals(PROPERTY_NAME)) { + return String.class; + } + if (propertyId.equals(PROPERTY_ICON)) { + return Resource.class; + } + if (propertyId.equals(PROPERTY_SIZE)) { + return Long.class; + } + if (propertyId.equals(PROPERTY_LASTMODIFIED)) { + return Date.class; + } + return null; + } + + /** + * Internal method to recursively calculate the number of files under a root + * directory. + * + * @param f + * the root to start counting from. + */ + private int getFileCounts(File f) { + File[] l; + if (filter != null) { + l = f.listFiles(filter); + } else { + l = f.listFiles(); + } + + if (l == null) { + return 0; + } + int ret = l.length; + for (int i = 0; i < l.length; i++) { + if (l[i].isDirectory()) { + ret += getFileCounts(l[i]); + } + } + return ret; + } + + /** + * Gets the number of Items in the container. In effect, this is the + * combined amount of files and directories. + * + * @return Number of Items in the container. + */ + @Override + public int size() { + + if (recursive) { + int counts = 0; + for (int i = 0; i < roots.length; i++) { + counts += getFileCounts(roots[i]); + } + return counts; + } else { + File[] f; + if (roots.length == 1) { + if (filter != null) { + f = roots[0].listFiles(filter); + } else { + f = roots[0].listFiles(); + } + } else { + f = roots; + } + + if (f == null) { + return 0; + } + return f.length; + } + } + + /** + * A Item wrapper for files in a filesystem. + * + * @author Vaadin Ltd. + * @since 3.0 + */ + public class FileItem implements Item { + + /** + * The wrapped file. + */ + private final File file; + + /** + * Constructs a FileItem from a existing file. + */ + private FileItem(File file) { + this.file = file; + } + + /* + * Gets the specified property of this file. Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public Property getItemProperty(Object id) { + return getContainerProperty(file, id); + } + + /* + * Gets the IDs of all properties available for this item Don't add a + * JavaDoc comment here, we use the default documentation from + * implemented interface. + */ + @Override + public Collection getItemPropertyIds() { + return getContainerPropertyIds(); + } + + /** + * Calculates a integer hash-code for the Property that's unique inside + * the Item containing the Property. Two different Properties inside the + * same Item contained in the same list always have different + * hash-codes, though Properties in different Items may have identical + * hash-codes. + * + * @return A locally unique hash-code as integer + */ + @Override + public int hashCode() { + return file.hashCode() ^ FilesystemContainer.this.hashCode(); + } + + /** + * Tests if the given object is the same as the this object. Two + * Properties got from an Item with the same ID are equal. + * + * @param obj + * an object to compare with this object. + * @return true if the given object is the same as this + * object, false if not + */ + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof FileItem)) { + return false; + } + final FileItem fi = (FileItem) obj; + return fi.getHost() == getHost() && fi.file.equals(file); + } + + /** + * Gets the host of this file. + */ + private FilesystemContainer getHost() { + return FilesystemContainer.this; + } + + /** + * Gets the last modified date of this file. + * + * @return Date + */ + public Date lastModified() { + return new Date(file.lastModified()); + } + + /** + * Gets the name of this file. + * + * @return file name of this file. + */ + public String getName() { + return file.getName(); + } + + /** + * Gets the icon of this file. + * + * @return the icon of this file. + */ + public Resource getIcon() { + return FileTypeResolver.getIcon(file); + } + + /** + * Gets the size of this file. + * + * @return size + */ + public long getSize() { + if (file.isDirectory()) { + return 0; + } + return file.length(); + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + if ("".equals(file.getName())) { + return file.getAbsolutePath(); + } + return file.getName(); + } + + /** + * Filesystem container does not support adding new properties. + * + * @see com.vaadin.v7.data.Item#addItemProperty(Object, Property) + */ + @Override + public boolean addItemProperty(Object id, Property property) + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Filesystem container " + + "does not support adding new properties"); + } + + /** + * Filesystem container does not support removing properties. + * + * @see com.vaadin.v7.data.Item#removeItemProperty(Object) + */ + @Override + public boolean removeItemProperty(Object id) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Filesystem container does not support property removal"); + } + + } + + /** + * Generic file extension filter for displaying only files having certain + * extension. + * + * @author Vaadin Ltd. + * @since 3.0 + */ + public class FileExtensionFilter implements FilenameFilter, Serializable { + + private final String filter; + + /** + * Constructs a new FileExtensionFilter using given extension. + * + * @param fileExtension + * the File extension without the separator (dot). + */ + public FileExtensionFilter(String fileExtension) { + filter = "." + fileExtension; + } + + /** + * Allows only files with the extension and directories. + * + * @see java.io.FilenameFilter#accept(File, String) + */ + @Override + public boolean accept(File dir, String name) { + if (name.endsWith(filter)) { + return true; + } + return new File(dir, name).isDirectory(); + } + + } + + /** + * Returns the file filter used to limit the files in this container. + * + * @return Used filter instance or null if no filter is assigned. + */ + public FilenameFilter getFilter() { + return filter; + } + + /** + * Sets the file filter used to limit the files in this container. + * + * @param filter + * The filter to set. null disables filtering. + */ + public void setFilter(FilenameFilter filter) { + this.filter = filter; + } + + /** + * Sets the file filter used to limit the files in this container. + * + * @param extension + * the Filename extension (w/o separator) to limit the files in + * container. + */ + public void setFilter(String extension) { + filter = new FileExtensionFilter(extension); + } + + /** + * Is this container recursive filesystem. + * + * @return true if container is recursive, false + * otherwise. + */ + public boolean isRecursive() { + return recursive; + } + + /** + * Sets the container recursive property. Set this to false to limit the + * files directly under the root file. + *

    + * Note : This is meaningful only if the root really is a directory. + *

    + * + * @param recursive + * the New value for recursive property. + */ + public void setRecursive(boolean recursive) { + this.recursive = recursive; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object, + * java.lang.Class, java.lang.Object) + */ + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "File system container does not support this operation"); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#addItem() + */ + @Override + public Object addItem() throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "File system container does not support this operation"); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#addItem(java.lang.Object) + */ + @Override + public Item addItem(Object itemId) throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "File system container does not support this operation"); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeAllItems() + */ + @Override + public boolean removeAllItems() throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "File system container does not support this operation"); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeItem(java.lang.Object) + */ + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "File system container does not support this operation"); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object ) + */ + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "File system container does not support this operation"); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/GeneratedPropertyContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/GeneratedPropertyContainer.java new file mode 100644 index 0000000000..cfa061db02 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/GeneratedPropertyContainer.java @@ -0,0 +1,776 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import com.vaadin.data.sort.SortOrder; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.filter.UnsupportedFilterException; + +/** + * Container wrapper that adds support for generated properties. This container + * only supports adding new generated properties. Adding new normal properties + * should be done for the wrapped container. + * + *

    + * Removing properties from this container does not remove anything from the + * wrapped container but instead only hides them from the results. These + * properties can be returned to this container by calling + * {@link #addContainerProperty(Object, Class, Object)} with same property id + * which was removed. + * + *

    + * If wrapped container is Filterable and/or Sortable it should only be handled + * through this container as generated properties need to be handled in a + * specific way when sorting/filtering. + * + *

    + * Items returned by this container do not support adding or removing + * properties. Generated properties are always read-only. Trying to make them + * editable throws an exception. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class GeneratedPropertyContainer extends AbstractContainer + implements Container.Indexed, Container.Sortable, Container.Filterable, + Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier { + + private final Container.Indexed wrappedContainer; + private final Map> propertyGenerators; + private final Map> activeFilters; + private Sortable sortableContainer = null; + private Filterable filterableContainer = null; + + /* Removed properties which are hidden but not actually removed */ + private final Set removedProperties = new HashSet(); + + /** + * Property implementation for generated properties + */ + protected static class GeneratedProperty implements Property { + + private Item item; + private Object itemId; + private Object propertyId; + private PropertyValueGenerator generator; + + public GeneratedProperty(Item item, Object propertyId, Object itemId, + PropertyValueGenerator generator) { + this.item = item; + this.itemId = itemId; + this.propertyId = propertyId; + this.generator = generator; + } + + @Override + public T getValue() { + return generator.getValue(item, itemId, propertyId); + } + + @Override + public void setValue(T newValue) throws ReadOnlyException { + throw new ReadOnlyException("Generated properties are read only"); + } + + @Override + public Class getType() { + return generator.getType(); + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Override + public void setReadOnly(boolean newStatus) { + if (newStatus) { + // No-op + return; + } + throw new UnsupportedOperationException( + "Generated properties are read only"); + } + } + + /** + * Item implementation for generated properties, used to wrap the Item that + * belongs to the wrapped container. To reach that Item use + * {@link #getWrappedItem()} + */ + public class GeneratedPropertyItem implements Item { + + private Item wrappedItem; + private Object itemId; + + protected GeneratedPropertyItem(Object itemId, Item item) { + this.itemId = itemId; + wrappedItem = item; + } + + @Override + public Property getItemProperty(Object id) { + if (propertyGenerators.containsKey(id)) { + return createProperty(wrappedItem, id, itemId, + propertyGenerators.get(id)); + } + return wrappedItem.getItemProperty(id); + } + + @Override + public Collection getItemPropertyIds() { + Set wrappedProperties = new LinkedHashSet( + wrappedItem.getItemPropertyIds()); + wrappedProperties.removeAll(removedProperties); + wrappedProperties.addAll(propertyGenerators.keySet()); + return wrappedProperties; + } + + @Override + public boolean addItemProperty(Object id, Property property) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "GeneratedPropertyItem does not support adding properties"); + } + + @Override + public boolean removeItemProperty(Object id) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "GeneratedPropertyItem does not support removing properties"); + } + + /** + * Tests if the given object is the same as the this object. Two Items + * from the same container with the same ID are equal. + * + * @param obj + * an object to compare with this object + * @return true if the given object is the same as this + * object, false if not + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null + || !obj.getClass().equals(GeneratedPropertyItem.class)) { + return false; + } + final GeneratedPropertyItem li = (GeneratedPropertyItem) obj; + return getContainer() == li.getContainer() + && itemId.equals(li.itemId); + } + + @Override + public int hashCode() { + return itemId.hashCode(); + } + + private GeneratedPropertyContainer getContainer() { + return GeneratedPropertyContainer.this; + } + + /** + * Returns the wrapped Item that belongs to the wrapped container + * + * @return wrapped item. + * @since 7.6.8 + */ + public Item getWrappedItem() { + return wrappedItem; + } + }; + + /** + * Base implementation for item add or remove events. This is used when an + * event is fired from wrapped container and needs to be reconstructed to + * act like it actually came from this container. + */ + protected abstract class GeneratedItemAddOrRemoveEvent + implements Serializable { + + private Object firstItemId; + private int firstIndex; + private int count; + + protected GeneratedItemAddOrRemoveEvent(Object itemId, int first, + int count) { + firstItemId = itemId; + firstIndex = first; + this.count = count; + } + + public Container getContainer() { + return GeneratedPropertyContainer.this; + } + + public Object getFirstItemId() { + return firstItemId; + } + + public int getFirstIndex() { + return firstIndex; + } + + public int getAffectedItemsCount() { + return count; + } + }; + + protected class GeneratedItemRemoveEvent + extends GeneratedItemAddOrRemoveEvent implements ItemRemoveEvent { + + protected GeneratedItemRemoveEvent(ItemRemoveEvent event) { + super(event.getFirstItemId(), event.getFirstIndex(), + event.getRemovedItemsCount()); + } + + @Override + public int getRemovedItemsCount() { + return super.getAffectedItemsCount(); + } + } + + protected class GeneratedItemAddEvent extends GeneratedItemAddOrRemoveEvent + implements ItemAddEvent { + + protected GeneratedItemAddEvent(ItemAddEvent event) { + super(event.getFirstItemId(), event.getFirstIndex(), + event.getAddedItemsCount()); + } + + @Override + public int getAddedItemsCount() { + return super.getAffectedItemsCount(); + } + + } + + /** + * Constructor for GeneratedPropertyContainer. + * + * @param container + * underlying indexed container + */ + public GeneratedPropertyContainer(Container.Indexed container) { + wrappedContainer = container; + propertyGenerators = new HashMap>(); + + if (wrappedContainer instanceof Sortable) { + sortableContainer = (Sortable) wrappedContainer; + } + + if (wrappedContainer instanceof Filterable) { + activeFilters = new HashMap>(); + filterableContainer = (Filterable) wrappedContainer; + } else { + activeFilters = null; + } + + // ItemSetChangeEvents + if (wrappedContainer instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) wrappedContainer) + .addItemSetChangeListener(new ItemSetChangeListener() { + + @Override + public void containerItemSetChange( + ItemSetChangeEvent event) { + if (event instanceof ItemAddEvent) { + final ItemAddEvent addEvent = (ItemAddEvent) event; + fireItemSetChange( + new GeneratedItemAddEvent(addEvent)); + } else if (event instanceof ItemRemoveEvent) { + final ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; + fireItemSetChange(new GeneratedItemRemoveEvent( + removeEvent)); + } else { + fireItemSetChange(); + } + } + }); + } + + // PropertySetChangeEvents + if (wrappedContainer instanceof PropertySetChangeNotifier) { + ((PropertySetChangeNotifier) wrappedContainer) + .addPropertySetChangeListener( + new PropertySetChangeListener() { + + @Override + public void containerPropertySetChange( + PropertySetChangeEvent event) { + fireContainerPropertySetChange(); + } + }); + } + } + + /* Functions related to generated properties */ + + /** + * Add a new PropertyValueGenerator with given property id. This will + * override any existing properties with the same property id. Fires a + * PropertySetChangeEvent. + * + * @param propertyId + * property id + * @param generator + * a property value generator + */ + public void addGeneratedProperty(Object propertyId, + PropertyValueGenerator generator) { + propertyGenerators.put(propertyId, generator); + fireContainerPropertySetChange(); + } + + /** + * Removes any possible PropertyValueGenerator with given property id. Fires + * a PropertySetChangeEvent. + * + * @param propertyId + * property id + */ + public void removeGeneratedProperty(Object propertyId) { + if (propertyGenerators.containsKey(propertyId)) { + propertyGenerators.remove(propertyId); + fireContainerPropertySetChange(); + } + } + + private Item createGeneratedPropertyItem(final Object itemId, + final Item item) { + return new GeneratedPropertyItem(itemId, item); + } + + private Property createProperty(final Item item, + final Object propertyId, final Object itemId, + final PropertyValueGenerator generator) { + return new GeneratedProperty(item, propertyId, itemId, generator); + } + + /* Listener functionality */ + + @Override + public void addItemSetChangeListener(ItemSetChangeListener listener) { + super.addItemSetChangeListener(listener); + } + + @Override + public void addListener(ItemSetChangeListener listener) { + super.addListener(listener); + } + + @Override + public void removeItemSetChangeListener(ItemSetChangeListener listener) { + super.removeItemSetChangeListener(listener); + } + + @Override + public void removeListener(ItemSetChangeListener listener) { + super.removeListener(listener); + } + + @Override + public void addPropertySetChangeListener( + PropertySetChangeListener listener) { + super.addPropertySetChangeListener(listener); + } + + @Override + public void addListener(PropertySetChangeListener listener) { + super.addListener(listener); + } + + @Override + public void removePropertySetChangeListener( + PropertySetChangeListener listener) { + super.removePropertySetChangeListener(listener); + } + + @Override + public void removeListener(PropertySetChangeListener listener) { + super.removeListener(listener); + } + + /* Filtering functionality */ + + @Override + public void addContainerFilter(Filter filter) + throws UnsupportedFilterException { + if (filterableContainer == null) { + throw new UnsupportedOperationException( + "Wrapped container is not filterable"); + } + + List addedFilters = new ArrayList(); + for (Entry> entry : propertyGenerators + .entrySet()) { + Object property = entry.getKey(); + if (filter.appliesToProperty(property)) { + // Have generated property modify filter to fit the original + // data in the container. + Filter modifiedFilter = entry.getValue().modifyFilter(filter); + filterableContainer.addContainerFilter(modifiedFilter); + // Keep track of added filters + addedFilters.add(modifiedFilter); + } + } + + if (addedFilters.isEmpty()) { + // No generated property modified this filter, use it as is + addedFilters.add(filter); + filterableContainer.addContainerFilter(filter); + } + // Map filter to actually added filters + activeFilters.put(filter, addedFilters); + } + + @Override + public void removeContainerFilter(Filter filter) { + if (filterableContainer == null) { + throw new UnsupportedOperationException( + "Wrapped container is not filterable"); + } + + if (activeFilters.containsKey(filter)) { + for (Filter f : activeFilters.get(filter)) { + filterableContainer.removeContainerFilter(f); + } + activeFilters.remove(filter); + } + } + + @Override + public void removeAllContainerFilters() { + if (filterableContainer == null) { + throw new UnsupportedOperationException( + "Wrapped container is not filterable"); + } + filterableContainer.removeAllContainerFilters(); + activeFilters.clear(); + } + + @Override + public Collection getContainerFilters() { + if (filterableContainer == null) { + throw new UnsupportedOperationException( + "Wrapped container is not filterable"); + } + return Collections.unmodifiableSet(activeFilters.keySet()); + } + + /* Sorting functionality */ + + @Override + public void sort(Object[] propertyId, boolean[] ascending) { + if (sortableContainer == null) { + throw new UnsupportedOperationException( + "Wrapped container is not Sortable"); + } + + if (propertyId.length == 0) { + sortableContainer.sort(propertyId, ascending); + return; + } + + List actualSortProperties = new ArrayList(); + List actualSortDirections = new ArrayList(); + + for (int i = 0; i < propertyId.length; ++i) { + Object property = propertyId[i]; + SortDirection direction; + boolean isAscending = i < ascending.length ? ascending[i] : true; + if (isAscending) { + direction = SortDirection.ASCENDING; + } else { + direction = SortDirection.DESCENDING; + } + + if (propertyGenerators.containsKey(property)) { + // Sorting by a generated property. Generated property should + // modify sort orders to work with original properties in the + // container. + for (SortOrder s : propertyGenerators.get(property) + .getSortProperties( + new SortOrder(property, direction))) { + actualSortProperties.add(s.getPropertyId()); + actualSortDirections + .add(s.getDirection() == SortDirection.ASCENDING); + } + } else { + actualSortProperties.add(property); + actualSortDirections.add(isAscending); + } + } + + boolean[] actualAscending = new boolean[actualSortDirections.size()]; + for (int i = 0; i < actualAscending.length; ++i) { + actualAscending[i] = actualSortDirections.get(i); + } + + sortableContainer.sort(actualSortProperties.toArray(), actualAscending); + } + + @Override + public Collection getSortableContainerPropertyIds() { + if (sortableContainer == null) { + return Collections.emptySet(); + } + + Set sortablePropertySet = new HashSet( + sortableContainer.getSortableContainerPropertyIds()); + for (Entry> entry : propertyGenerators + .entrySet()) { + Object property = entry.getKey(); + SortOrder order = new SortOrder(property, SortDirection.ASCENDING); + if (entry.getValue().getSortProperties(order).length > 0) { + sortablePropertySet.add(property); + } else { + sortablePropertySet.remove(property); + } + } + + return sortablePropertySet; + } + + /* Item related overrides */ + + @Override + public Item addItemAfter(Object previousItemId, Object newItemId) + throws UnsupportedOperationException { + Item item = wrappedContainer.addItemAfter(previousItemId, newItemId); + if (item == null) { + return null; + } + return createGeneratedPropertyItem(newItemId, item); + } + + @Override + public Item addItem(Object itemId) throws UnsupportedOperationException { + Item item = wrappedContainer.addItem(itemId); + if (item == null) { + return null; + } + return createGeneratedPropertyItem(itemId, item); + } + + @Override + public Item addItemAt(int index, Object newItemId) + throws UnsupportedOperationException { + Item item = wrappedContainer.addItemAt(index, newItemId); + if (item == null) { + return null; + } + return createGeneratedPropertyItem(newItemId, item); + } + + @Override + public Item getItem(Object itemId) { + Item item = wrappedContainer.getItem(itemId); + if (item == null) { + return null; + } + + return createGeneratedPropertyItem(itemId, item); + } + + /* Property related overrides */ + + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + if (propertyGenerators.keySet().contains(propertyId)) { + return getItem(itemId).getItemProperty(propertyId); + } else if (!removedProperties.contains(propertyId)) { + return wrappedContainer.getContainerProperty(itemId, propertyId); + } + return null; + } + + /** + * Returns a list of propety ids available in this container. This + * collection will contain properties for generated properties. Removed + * properties will not show unless there is a generated property overriding + * those. + */ + @Override + public Collection getContainerPropertyIds() { + Set wrappedProperties = new LinkedHashSet( + wrappedContainer.getContainerPropertyIds()); + wrappedProperties.removeAll(removedProperties); + wrappedProperties.addAll(propertyGenerators.keySet()); + return wrappedProperties; + } + + /** + * Adds a previously removed property back to GeneratedPropertyContainer. + * Adding a property that is not previously removed causes an + * UnsupportedOperationException. + */ + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + if (!removedProperties.contains(propertyId)) { + throw new UnsupportedOperationException( + "GeneratedPropertyContainer does not support adding properties."); + } + removedProperties.remove(propertyId); + fireContainerPropertySetChange(); + return true; + } + + /** + * Marks the given property as hidden. This property from wrapped container + * will be removed from {@link #getContainerPropertyIds()} and is no longer + * be available in Items retrieved from this container. + */ + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + if (wrappedContainer.getContainerPropertyIds().contains(propertyId) + && removedProperties.add(propertyId)) { + fireContainerPropertySetChange(); + return true; + } + return false; + } + + /* Type related overrides */ + + @Override + public Class getType(Object propertyId) { + if (propertyGenerators.containsKey(propertyId)) { + return propertyGenerators.get(propertyId).getType(); + } else { + return wrappedContainer.getType(propertyId); + } + } + + /* Unmodified functions */ + + @Override + public Object nextItemId(Object itemId) { + return wrappedContainer.nextItemId(itemId); + } + + @Override + public Object prevItemId(Object itemId) { + return wrappedContainer.prevItemId(itemId); + } + + @Override + public Object firstItemId() { + return wrappedContainer.firstItemId(); + } + + @Override + public Object lastItemId() { + return wrappedContainer.lastItemId(); + } + + @Override + public boolean isFirstId(Object itemId) { + return wrappedContainer.isFirstId(itemId); + } + + @Override + public boolean isLastId(Object itemId) { + return wrappedContainer.isLastId(itemId); + } + + @Override + public Object addItemAfter(Object previousItemId) + throws UnsupportedOperationException { + return wrappedContainer.addItemAfter(previousItemId); + } + + @Override + public Collection getItemIds() { + return wrappedContainer.getItemIds(); + } + + @Override + public int size() { + return wrappedContainer.size(); + } + + @Override + public boolean containsId(Object itemId) { + return wrappedContainer.containsId(itemId); + } + + @Override + public Object addItem() throws UnsupportedOperationException { + return wrappedContainer.addItem(); + } + + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + return wrappedContainer.removeItem(itemId); + } + + @Override + public boolean removeAllItems() throws UnsupportedOperationException { + return wrappedContainer.removeAllItems(); + } + + @Override + public int indexOfId(Object itemId) { + return wrappedContainer.indexOfId(itemId); + } + + @Override + public Object getIdByIndex(int index) { + return wrappedContainer.getIdByIndex(index); + } + + @Override + public List getItemIds(int startIndex, int numberOfItems) { + return wrappedContainer.getItemIds(startIndex, numberOfItems); + } + + @Override + public Object addItemAt(int index) throws UnsupportedOperationException { + return wrappedContainer.addItemAt(index); + } + + /** + * Returns the original underlying container. + * + * @return the original underlying container + */ + public Container.Indexed getWrappedContainer() { + return wrappedContainer; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/HierarchicalContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/HierarchicalContainer.java new file mode 100644 index 0000000000..04940918de --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/HierarchicalContainer.java @@ -0,0 +1,860 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Item; + +/** + * A specialized Container whose contents can be accessed like it was a + * tree-like structure. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class HierarchicalContainer extends IndexedContainer + implements Container.Hierarchical { + + /** + * Set of IDs of those contained Items that can't have children. + */ + private final HashSet noChildrenAllowed = new HashSet(); + + /** + * Mapping from Item ID to parent Item ID. + */ + private final HashMap parent = new HashMap(); + + /** + * Mapping from Item ID to parent Item ID for items included in the filtered + * container. + */ + private HashMap filteredParent = null; + + /** + * Mapping from Item ID to a list of child IDs. + */ + private final HashMap> children = new HashMap>(); + + /** + * Mapping from Item ID to a list of child IDs when filtered + */ + private HashMap> filteredChildren = null; + + /** + * List that contains all root elements of the container. + */ + private final LinkedList roots = new LinkedList(); + + /** + * List that contains all filtered root elements of the container. + */ + private LinkedList filteredRoots = null; + + /** + * Determines how filtering of the container is done. + */ + private boolean includeParentsWhenFiltering = true; + + /** + * Counts how many nested contents change disable calls are in progress. + * + * Pending events are only fired when the counter reaches zero again. + */ + private int contentChangedEventsDisabledCount = 0; + + private boolean contentsChangedEventPending; + + /* + * Can the specified Item have any children? Don't add a JavaDoc comment + * here, we use the default documentation from implemented interface. + */ + @Override + public boolean areChildrenAllowed(Object itemId) { + if (noChildrenAllowed.contains(itemId)) { + return false; + } + return containsId(itemId); + } + + /* + * Gets the IDs of the children of the specified Item. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Collection getChildren(Object itemId) { + LinkedList c; + + if (filteredChildren != null) { + c = filteredChildren.get(itemId); + } else { + c = children.get(itemId); + } + + if (c == null) { + return null; + } + return Collections.unmodifiableCollection(c); + } + + /* + * Gets the ID of the parent of the specified Item. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Object getParent(Object itemId) { + if (filteredParent != null) { + return filteredParent.get(itemId); + } + return parent.get(itemId); + } + + /* + * Is the Item corresponding to the given ID a leaf node? Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public boolean hasChildren(Object itemId) { + if (filteredChildren != null) { + return filteredChildren.containsKey(itemId); + } else { + return children.containsKey(itemId); + } + } + + /* + * Is the Item corresponding to the given ID a root node? Don't add a + * JavaDoc comment here, we use the default documentation from implemented + * interface. + */ + @Override + public boolean isRoot(Object itemId) { + // If the container is filtered the itemId must be among filteredRoots + // to be a root. + if (filteredRoots != null) { + if (!filteredRoots.contains(itemId)) { + return false; + } + } else { + // Container is not filtered + if (parent.containsKey(itemId)) { + return false; + } + } + + return containsId(itemId); + } + + /* + * Gets the IDs of the root elements in the container. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public Collection rootItemIds() { + if (filteredRoots != null) { + return Collections.unmodifiableCollection(filteredRoots); + } else { + return Collections.unmodifiableCollection(roots); + } + } + + /** + *

    + * Sets the given Item's capability to have children. If the Item identified + * with the itemId already has children and the areChildrenAllowed is false + * this method fails and false is returned; the children must + * be first explicitly removed with + * {@link #setParent(Object itemId, Object newParentId)} or + * {@link com.vaadin.v7.data.Container#removeItem(Object itemId)}. + *

    + * + * @param itemId + * the ID of the Item in the container whose child capability is + * to be set. + * @param childrenAllowed + * the boolean value specifying if the Item can have children or + * not. + * @return true if the operation succeeded, false + * if not + */ + @Override + public boolean setChildrenAllowed(Object itemId, boolean childrenAllowed) { + + // Checks that the item is in the container + if (!containsId(itemId)) { + return false; + } + + // Updates status + if (childrenAllowed) { + noChildrenAllowed.remove(itemId); + } else { + noChildrenAllowed.add(itemId); + } + + return true; + } + + /** + *

    + * Sets the parent of an Item. The new parent item must exist and be able to + * have children. (canHaveChildren(newParentId) == true). It is + * also possible to detach a node from the hierarchy (and thus make it root) + * by setting the parent null. + *

    + * + * @param itemId + * the ID of the item to be set as the child of the Item + * identified with newParentId. + * @param newParentId + * the ID of the Item that's to be the new parent of the Item + * identified with itemId. + * @return true if the operation succeeded, false + * if not + */ + @Override + public boolean setParent(Object itemId, Object newParentId) { + + // Checks that the item is in the container + if (!containsId(itemId)) { + return false; + } + + // Gets the old parent + final Object oldParentId = parent.get(itemId); + + // Checks if no change is necessary + if ((newParentId == null && oldParentId == null) + || ((newParentId != null) && newParentId.equals(oldParentId))) { + return true; + } + + // Making root? + if (newParentId == null) { + // The itemId should become a root so we need to + // - Remove it from the old parent's children list + // - Add it as a root + // - Remove it from the item -> parent list (parent is null for + // roots) + + // Removes from old parents children list + final LinkedList l = children.get(oldParentId); + if (l != null) { + l.remove(itemId); + if (l.isEmpty()) { + children.remove(oldParentId); + } + + } + + // Add to be a root + roots.add(itemId); + + // Updates parent + parent.remove(itemId); + + if (hasFilters()) { + // Refilter the container if setParent is called when filters + // are applied. Changing parent can change what is included in + // the filtered version (if includeParentsWhenFiltering==true). + doFilterContainer(hasFilters()); + } + + fireItemSetChange(); + + return true; + } + + // We get here when the item should not become a root and we need to + // - Verify the new parent exists and can have children + // - Check that the new parent is not a child of the selected itemId + // - Updated the item -> parent mapping to point to the new parent + // - Remove the item from the roots list if it was a root + // - Remove the item from the old parent's children list if it was not a + // root + + // Checks that the new parent exists in container and can have + // children + if (!containsId(newParentId) + || noChildrenAllowed.contains(newParentId)) { + return false; + } + + // Checks that setting parent doesn't result to a loop + Object o = newParentId; + while (o != null && !o.equals(itemId)) { + o = parent.get(o); + } + if (o != null) { + return false; + } + + // Updates parent + parent.put(itemId, newParentId); + LinkedList pcl = children.get(newParentId); + if (pcl == null) { + // Create an empty list for holding children if one were not + // previously created + pcl = new LinkedList(); + children.put(newParentId, pcl); + } + pcl.add(itemId); + + // Removes from old parent or root + if (oldParentId == null) { + roots.remove(itemId); + } else { + final LinkedList l = children.get(oldParentId); + if (l != null) { + l.remove(itemId); + if (l.isEmpty()) { + children.remove(oldParentId); + } + } + } + + if (hasFilters()) { + // Refilter the container if setParent is called when filters + // are applied. Changing parent can change what is included in + // the filtered version (if includeParentsWhenFiltering==true). + doFilterContainer(hasFilters()); + } + + fireItemSetChange(); + + return true; + } + + private boolean hasFilters() { + return (filteredRoots != null); + } + + /** + * Moves a node (an Item) in the container immediately after a sibling node. + * The two nodes must have the same parent in the container. + * + * @param itemId + * the identifier of the moved node (Item) + * @param siblingId + * the identifier of the reference node (Item), after which the + * other node will be located + */ + public void moveAfterSibling(Object itemId, Object siblingId) { + Object parent2 = getParent(itemId); + LinkedList childrenList; + if (parent2 == null) { + childrenList = roots; + } else { + childrenList = children.get(parent2); + } + if (siblingId == null) { + childrenList.remove(itemId); + childrenList.addFirst(itemId); + + } else { + int oldIndex = childrenList.indexOf(itemId); + int indexOfSibling = childrenList.indexOf(siblingId); + if (indexOfSibling != -1 && oldIndex != -1) { + int newIndex; + if (oldIndex > indexOfSibling) { + newIndex = indexOfSibling + 1; + } else { + newIndex = indexOfSibling; + } + childrenList.remove(oldIndex); + childrenList.add(newIndex, itemId); + } else { + throw new IllegalArgumentException( + "Given identifiers no not have the same parent."); + } + } + fireItemSetChange(); + + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#addItem() + */ + @Override + public Object addItem() { + disableContentsChangeEvents(); + try { + final Object itemId = super.addItem(); + if (itemId == null) { + return null; + } + + if (!roots.contains(itemId)) { + roots.add(itemId); + if (filteredRoots != null) { + if (passesFilters(itemId)) { + filteredRoots.add(itemId); + } + } + } + return itemId; + } finally { + enableAndFireContentsChangeEvents(); + } + } + + @Override + protected void fireItemSetChange( + com.vaadin.v7.data.Container.ItemSetChangeEvent event) { + if (contentsChangeEventsOn()) { + super.fireItemSetChange(event); + } else { + contentsChangedEventPending = true; + } + } + + private boolean contentsChangeEventsOn() { + return contentChangedEventsDisabledCount == 0; + } + + private void disableContentsChangeEvents() { + contentChangedEventsDisabledCount++; + } + + private void enableAndFireContentsChangeEvents() { + if (contentChangedEventsDisabledCount <= 0) { + getLogger().log(Level.WARNING, + "Mismatched calls to disable and enable contents change events in HierarchicalContainer"); + contentChangedEventsDisabledCount = 0; + } else { + contentChangedEventsDisabledCount--; + } + if (contentChangedEventsDisabledCount == 0) { + if (contentsChangedEventPending) { + fireItemSetChange(); + } + contentsChangedEventPending = false; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#addItem(java.lang.Object) + */ + @Override + public Item addItem(Object itemId) { + disableContentsChangeEvents(); + try { + final Item item = super.addItem(itemId); + if (item == null) { + return null; + } + + roots.add(itemId); + + if (filteredRoots != null) { + if (passesFilters(itemId)) { + filteredRoots.add(itemId); + } + } + return item; + } finally { + enableAndFireContentsChangeEvents(); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#removeAllItems() + */ + @Override + public boolean removeAllItems() { + disableContentsChangeEvents(); + try { + final boolean success = super.removeAllItems(); + + if (success) { + roots.clear(); + parent.clear(); + children.clear(); + noChildrenAllowed.clear(); + if (filteredRoots != null) { + filteredRoots = null; + } + if (filteredChildren != null) { + filteredChildren = null; + } + if (filteredParent != null) { + filteredParent = null; + } + } + return success; + } finally { + enableAndFireContentsChangeEvents(); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#removeItem(java.lang.Object ) + */ + @Override + public boolean removeItem(Object itemId) { + disableContentsChangeEvents(); + try { + final boolean success = super.removeItem(itemId); + + if (success) { + // Remove from roots if this was a root + if (roots.remove(itemId)) { + + // If filtering is enabled we might need to remove it from + // the filtered list also + if (filteredRoots != null) { + filteredRoots.remove(itemId); + } + } + + // Clear the children list. Old children will now become root + // nodes + LinkedList childNodeIds = children.remove(itemId); + if (childNodeIds != null) { + if (filteredChildren != null) { + filteredChildren.remove(itemId); + } + for (Object childId : childNodeIds) { + setParent(childId, null); + } + } + + // Parent of the item that we are removing will contain the item + // id in its children list + final Object parentItemId = parent.get(itemId); + if (parentItemId != null) { + final LinkedList c = children.get(parentItemId); + if (c != null) { + c.remove(itemId); + + if (c.isEmpty()) { + children.remove(parentItemId); + } + + // Found in the children list so might also be in the + // filteredChildren list + if (filteredChildren != null) { + LinkedList f = filteredChildren + .get(parentItemId); + if (f != null) { + f.remove(itemId); + if (f.isEmpty()) { + filteredChildren.remove(parentItemId); + } + } + } + } + } + parent.remove(itemId); + if (filteredParent != null) { + // Item id no longer has a parent as the item id is not in + // the container. + filteredParent.remove(itemId); + } + noChildrenAllowed.remove(itemId); + } + + return success; + } finally { + enableAndFireContentsChangeEvents(); + } + } + + /** + * Removes the Item identified by given itemId and all its children. + * + * @see #removeItem(Object) + * @param itemId + * the identifier of the Item to be removed + * @return true if the operation succeeded + */ + public boolean removeItemRecursively(Object itemId) { + disableContentsChangeEvents(); + try { + boolean removeItemRecursively = removeItemRecursively(this, itemId); + return removeItemRecursively; + } finally { + enableAndFireContentsChangeEvents(); + } + } + + /** + * Removes the Item identified by given itemId and all its children from the + * given Container. + * + * @param container + * the container where the item is to be removed + * @param itemId + * the identifier of the Item to be removed + * @return true if the operation succeeded + */ + public static boolean removeItemRecursively( + Container.Hierarchical container, Object itemId) { + boolean success = true; + Collection children2 = container.getChildren(itemId); + if (children2 != null) { + Object[] array = children2.toArray(); + for (int i = 0; i < array.length; i++) { + boolean removeItemRecursively = removeItemRecursively(container, + array[i]); + if (!removeItemRecursively) { + success = false; + } + } + } + // remove the root of subtree if children where succesfully removed + if (success) { + success = container.removeItem(itemId); + } + return success; + + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#doSort() + */ + @Override + protected void doSort() { + super.doSort(); + + Collections.sort(roots, getItemSorter()); + for (LinkedList childList : children.values()) { + Collections.sort(childList, getItemSorter()); + } + } + + /** + * Used to control how filtering works. @see + * {@link #setIncludeParentsWhenFiltering(boolean)} for more information. + * + * @return true if all parents for items that match the filter are included + * when filtering, false if only the matching items are included + */ + public boolean isIncludeParentsWhenFiltering() { + return includeParentsWhenFiltering; + } + + /** + * Controls how the filtering of the container works. Set this to true to + * make filtering include parents for all matched items in addition to the + * items themselves. Setting this to false causes the filtering to only + * include the matching items and make items with excluded parents into root + * items. + * + * @param includeParentsWhenFiltering + * true to include all parents for items that match the filter, + * false to only include the matching items + */ + public void setIncludeParentsWhenFiltering( + boolean includeParentsWhenFiltering) { + this.includeParentsWhenFiltering = includeParentsWhenFiltering; + if (filteredRoots != null) { + // Currently filtered so needs to be re-filtered + doFilterContainer(true); + } + } + + /* + * Overridden to provide filtering for root & children items. + * + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#updateContainerFiltering() + */ + @Override + protected boolean doFilterContainer(boolean hasFilters) { + if (!hasFilters) { + // All filters removed + filteredRoots = null; + filteredChildren = null; + filteredParent = null; + + return super.doFilterContainer(hasFilters); + } + + // Reset data structures + filteredRoots = new LinkedList(); + filteredChildren = new HashMap>(); + filteredParent = new HashMap(); + + if (includeParentsWhenFiltering) { + // Filter so that parents for items that match the filter are also + // included + HashSet includedItems = new HashSet(); + for (Object rootId : roots) { + if (filterIncludingParents(rootId, includedItems)) { + filteredRoots.add(rootId); + addFilteredChildrenRecursively(rootId, includedItems); + } + } + // includedItemIds now contains all the item ids that should be + // included. Filter IndexedContainer based on this + filterOverride = includedItems; + super.doFilterContainer(hasFilters); + filterOverride = null; + + return true; + } else { + // Filter by including all items that pass the filter and make items + // with no parent new root items + + // Filter IndexedContainer first so getItemIds return the items that + // match + super.doFilterContainer(hasFilters); + + LinkedHashSet filteredItemIds = new LinkedHashSet( + getItemIds()); + + for (Object itemId : filteredItemIds) { + Object itemParent = parent.get(itemId); + if (itemParent == null + || !filteredItemIds.contains(itemParent)) { + // Parent is not included or this was a root, in both cases + // this should be a filtered root + filteredRoots.add(itemId); + } else { + // Parent is included. Add this to the children list (create + // it first if necessary) + addFilteredChild(itemParent, itemId); + } + } + + return true; + } + } + + /** + * Adds the given childItemId as a filteredChildren for the parentItemId and + * sets it filteredParent. + * + * @param parentItemId + * @param childItemId + */ + private void addFilteredChild(Object parentItemId, Object childItemId) { + LinkedList parentToChildrenList = filteredChildren + .get(parentItemId); + if (parentToChildrenList == null) { + parentToChildrenList = new LinkedList(); + filteredChildren.put(parentItemId, parentToChildrenList); + } + filteredParent.put(childItemId, parentItemId); + parentToChildrenList.add(childItemId); + + } + + /** + * Recursively adds all items in the includedItems list to the + * filteredChildren map in the same order as they are in the children map. + * Starts from parentItemId and recurses down as long as child items that + * should be included are found. + * + * @param parentItemId + * The item id to start recurse from. Not added to a + * filteredChildren list + * @param includedItems + * Set containing the item ids for the items that should be + * included in the filteredChildren map + */ + private void addFilteredChildrenRecursively(Object parentItemId, + HashSet includedItems) { + LinkedList childList = children.get(parentItemId); + if (childList == null) { + return; + } + + for (Object childItemId : childList) { + if (includedItems.contains(childItemId)) { + addFilteredChild(parentItemId, childItemId); + addFilteredChildrenRecursively(childItemId, includedItems); + } + } + } + + /** + * Scans the itemId and all its children for which items should be included + * when filtering. All items which passes the filters are included. + * Additionally all items that have a child node that should be included are + * also themselves included. + * + * @param itemId + * @param includedItems + * @return true if the itemId should be included in the filtered container. + */ + private boolean filterIncludingParents(Object itemId, + HashSet includedItems) { + boolean toBeIncluded = passesFilters(itemId); + + LinkedList childList = children.get(itemId); + if (childList != null) { + for (Object childItemId : children.get(itemId)) { + toBeIncluded |= filterIncludingParents(childItemId, + includedItems); + } + } + + if (toBeIncluded) { + includedItems.add(itemId); + } + return toBeIncluded; + } + + private Set filterOverride = null; + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.util.IndexedContainer#passesFilters(java.lang.Object) + */ + @Override + protected boolean passesFilters(Object itemId) { + if (filterOverride != null) { + return filterOverride.contains(itemId); + } else { + return super.passesFilters(itemId); + } + } + + private static final Logger getLogger() { + return Logger.getLogger(HierarchicalContainer.class.getName()); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/IndexedContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/IndexedContainer.java new file mode 100644 index 0000000000..cbec3a2327 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/IndexedContainer.java @@ -0,0 +1,1201 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EventObject; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.filter.SimpleStringFilter; +import com.vaadin.v7.data.util.filter.UnsupportedFilterException; + +/** + * An implementation of the {@link Container.Indexed} interface + * with all important features. + *

    + * + * Features: + *
      + *
    • {@link Container.Indexed} + *
    • {@link Container.Ordered} + *
    • {@link Container.Sortable} + *
    • {@link Container.Filterable} + *
    • {@link Cloneable} (deprecated, might be removed in the future) + *
    • Sends all needed events on content changes. + *
    + * + * @see com.vaadin.v7.data.Container + * + * @author Vaadin Ltd. + * @since 3.0 + */ + +@SuppressWarnings("serial") +// item type is really IndexedContainerItem, but using Item not to show it in +// public API +public class IndexedContainer + extends AbstractInMemoryContainer + implements Container.PropertySetChangeNotifier, + Property.ValueChangeNotifier, Container.Sortable, Cloneable, + Container.Filterable, Container.SimpleFilterable { + + /* Internal structure */ + + /** + * Linked list of ordered Property IDs. + */ + private ArrayList propertyIds = new ArrayList(); + + /** + * Property ID to type mapping. + */ + private Hashtable> types = new Hashtable>(); + + /** + * Hash of Items, where each Item is implemented as a mapping from Property + * ID to Property value. + */ + private Hashtable> items = new Hashtable>(); + + /** + * Set of properties that are read-only. + */ + private HashSet> readOnlyProperties = new HashSet>(); + + /** + * List of all Property value change event listeners listening all the + * properties. + */ + private LinkedList propertyValueChangeListeners = null; + + /** + * Data structure containing all listeners interested in changes to single + * Properties. The data structure is a hashtable mapping Property IDs to a + * hashtable that maps Item IDs to a linked list of listeners listening + * Property identified by given Property ID and Item ID. + */ + private Hashtable>> singlePropertyValueChangeListeners = null; + + private HashMap defaultPropertyValues; + + private int nextGeneratedItemId = 1; + + /* Container constructors */ + + public IndexedContainer() { + super(); + } + + public IndexedContainer(Collection itemIds) { + this(); + if (items != null) { + for (final Iterator i = itemIds.iterator(); i.hasNext();) { + Object itemId = i.next(); + internalAddItemAtEnd(itemId, new IndexedContainerItem(itemId), + false); + } + filterAll(); + } + } + + /* Container methods */ + + @Override + protected Item getUnfilteredItem(Object itemId) { + if (itemId != null && items.containsKey(itemId)) { + return new IndexedContainerItem(itemId); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getContainerPropertyIds() + */ + @Override + public Collection getContainerPropertyIds() { + return Collections.unmodifiableCollection(propertyIds); + } + + /** + * Gets the type of a Property stored in the list. + * + * @param id + * the ID of the Property. + * @return Type of the requested Property + */ + @Override + public Class getType(Object propertyId) { + return types.get(propertyId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object, + * java.lang.Object) + */ + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + // map lookup more efficient than propertyIds if there are many + // properties + if (!containsId(itemId) || propertyId == null + || !types.containsKey(propertyId)) { + return null; + } + + return new IndexedContainerProperty(itemId, propertyId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object, + * java.lang.Class, java.lang.Object) + */ + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) { + + // Fails, if nulls are given + if (propertyId == null || type == null) { + return false; + } + + // Fails if the Property is already present + if (propertyIds.contains(propertyId)) { + return false; + } + + // Adds the Property to Property list and types + propertyIds.add(propertyId); + types.put(propertyId, type); + + // If default value is given, set it + if (defaultValue != null) { + // for existing rows + for (final Iterator i = getAllItemIds().iterator(); i + .hasNext();) { + getItem(i.next()).getItemProperty(propertyId) + .setValue(defaultValue); + } + // store for next rows + if (defaultPropertyValues == null) { + defaultPropertyValues = new HashMap(); + } + defaultPropertyValues.put(propertyId, defaultValue); + } + + // Sends a change event + fireContainerPropertySetChange(); + + return true; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeAllItems() + */ + @Override + public boolean removeAllItems() { + int origSize = size(); + Object firstItem = getFirstVisibleItem(); + + internalRemoveAllItems(); + + items.clear(); + + // fire event only if the visible view changed, regardless of whether + // filtered out items were removed or not + if (origSize != 0) { + // Sends a change event + fireItemsRemoved(0, firstItem, origSize); + } + + return true; + } + + /** + * {@inheritDoc} + *

    + * The item ID is generated from a sequence of Integers. The id of the first + * added item is 1. + */ + @Override + public Object addItem() { + + // Creates a new id + final Object id = generateId(); + + // Adds the Item into container + addItem(id); + + return id; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#addItem(java.lang.Object) + */ + @Override + public Item addItem(Object itemId) { + Item item = internalAddItemAtEnd(itemId, + new IndexedContainerItem(itemId), false); + if (item == null) { + return null; + } else if (!isFiltered()) { + // always the last item + fireItemAdded(size() - 1, itemId, item); + } else if (passesFilters(itemId) && !containsId(itemId)) { + getFilteredItemIds().add(itemId); + // always the last item + fireItemAdded(size() - 1, itemId, item); + } + return item; + } + + /** + * Helper method to add default values for items if available + * + * @param t + * data table of added item + */ + private void addDefaultValues(Hashtable t) { + if (defaultPropertyValues != null) { + for (Object key : defaultPropertyValues.keySet()) { + t.put(key, defaultPropertyValues.get(key)); + } + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeItem(java.lang.Object) + */ + @Override + public boolean removeItem(Object itemId) { + if (itemId == null || items.remove(itemId) == null) { + return false; + } + int origSize = size(); + int position = indexOfId(itemId); + if (internalRemoveItem(itemId)) { + // fire event only if the visible view changed, regardless of + // whether filtered out items were removed or not + if (size() != origSize) { + fireItemRemoved(position, itemId); + } + + return true; + } else { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object ) + */ + @Override + public boolean removeContainerProperty(Object propertyId) { + + // Fails if the Property is not present + if (!propertyIds.contains(propertyId)) { + return false; + } + + // Removes the Property to Property list and types + propertyIds.remove(propertyId); + types.remove(propertyId); + if (defaultPropertyValues != null) { + defaultPropertyValues.remove(propertyId); + } + + // If remove the Property from all Items + for (final Iterator i = getAllItemIds().iterator(); i + .hasNext();) { + items.get(i.next()).remove(propertyId); + } + + // Sends a change event + fireContainerPropertySetChange(); + + return true; + } + + /* Container.Ordered methods */ + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object, + * java.lang.Object) + */ + @Override + public Item addItemAfter(Object previousItemId, Object newItemId) { + return internalAddItemAfter(previousItemId, newItemId, + new IndexedContainerItem(newItemId), true); + } + + /** + * {@inheritDoc} + *

    + * The item ID is generated from a sequence of Integers. The id of the first + * added item is 1. + */ + @Override + public Object addItemAfter(Object previousItemId) { + + // Creates a new id + final Object id = generateId(); + + if (addItemAfter(previousItemId, id) != null) { + return id; + } else { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object) + */ + @Override + public Item addItemAt(int index, Object newItemId) { + return internalAddItemAt(index, newItemId, + new IndexedContainerItem(newItemId), true); + } + + /** + * {@inheritDoc} + *

    + * The item ID is generated from a sequence of Integers. The id of the first + * added item is 1. + */ + @Override + public Object addItemAt(int index) { + + // Creates a new id + final Object id = generateId(); + + // Adds the Item into container + addItemAt(index, id); + + return id; + } + + /** + * Generates an unique identifier for use as an item id. Guarantees that the + * generated id is not currently used as an id. + * + * @return + */ + private Serializable generateId() { + Serializable id; + do { + id = Integer.valueOf(nextGeneratedItemId++); + } while (items.containsKey(id)); + + return id; + } + + @Override + protected void registerNewItem(int index, Object newItemId, Item item) { + Hashtable t = new Hashtable(); + items.put(newItemId, t); + addDefaultValues(t); + } + + /* Event notifiers */ + + /** + * An event object specifying the list whose Item set has + * changed. + * + * @author Vaadin Ltd. + * @since 3.0 + */ + public static class ItemSetChangeEvent extends BaseItemSetChangeEvent { + + private final int addedItemIndex; + + private ItemSetChangeEvent(IndexedContainer source, + int addedItemIndex) { + super(source); + this.addedItemIndex = addedItemIndex; + } + + /** + * Iff one item is added, gives its index. + * + * @return -1 if either multiple items are changed or some other change + * than add is done. + */ + public int getAddedItemIndex() { + return addedItemIndex; + } + + } + + /** + * An event object specifying the Property in a list whose + * value has changed. + * + * @author Vaadin Ltd. + * @since 3.0 + */ + private static class PropertyValueChangeEvent extends EventObject + implements Property.ValueChangeEvent, Serializable { + + private PropertyValueChangeEvent(Property source) { + super(source); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property.ValueChangeEvent#getProperty() + */ + @Override + public Property getProperty() { + return (Property) getSource(); + } + + } + + @Override + public void addPropertySetChangeListener( + Container.PropertySetChangeListener listener) { + super.addPropertySetChangeListener(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addPropertySetChangeListener(com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Deprecated + @Override + public void addListener(Container.PropertySetChangeListener listener) { + addPropertySetChangeListener(listener); + } + + @Override + public void removePropertySetChangeListener( + Container.PropertySetChangeListener listener) { + super.removePropertySetChangeListener(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removePropertySetChangeListener(com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Deprecated + @Override + public void removeListener(Container.PropertySetChangeListener listener) { + removePropertySetChangeListener(listener); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property.ValueChangeNotifier#addListener(com. + * vaadin.data.Property.ValueChangeListener) + */ + @Override + public void addValueChangeListener(Property.ValueChangeListener listener) { + if (propertyValueChangeListeners == null) { + propertyValueChangeListeners = new LinkedList(); + } + propertyValueChangeListeners.add(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)} + **/ + @Override + @Deprecated + public void addListener(Property.ValueChangeListener listener) { + addValueChangeListener(listener); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property.ValueChangeNotifier#removeListener(com + * .vaadin.data.Property.ValueChangeListener) + */ + @Override + public void removeValueChangeListener( + Property.ValueChangeListener listener) { + if (propertyValueChangeListeners != null) { + propertyValueChangeListeners.remove(listener); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(Property.ValueChangeListener listener) { + removeValueChangeListener(listener); + } + + /** + * Sends a Property value change event to all interested listeners. + * + * @param source + * the IndexedContainerProperty object. + */ + private void firePropertyValueChange(IndexedContainerProperty source) { + + // Sends event to listeners listening all value changes + if (propertyValueChangeListeners != null) { + final Object[] l = propertyValueChangeListeners.toArray(); + final Property.ValueChangeEvent event = new IndexedContainer.PropertyValueChangeEvent( + source); + for (int i = 0; i < l.length; i++) { + ((Property.ValueChangeListener) l[i]).valueChange(event); + } + } + + // Sends event to single property value change listeners + if (singlePropertyValueChangeListeners != null) { + final Map> propertySetToListenerListMap = singlePropertyValueChangeListeners + .get(source.propertyId); + if (propertySetToListenerListMap != null) { + final List listenerList = propertySetToListenerListMap + .get(source.itemId); + if (listenerList != null) { + final Property.ValueChangeEvent event = new IndexedContainer.PropertyValueChangeEvent( + source); + Object[] listeners = listenerList.toArray(); + for (int i = 0; i < listeners.length; i++) { + ((Property.ValueChangeListener) listeners[i]) + .valueChange(event); + } + } + } + } + + } + + @Override + public Collection getListeners(Class eventType) { + if (Property.ValueChangeEvent.class.isAssignableFrom(eventType)) { + if (propertyValueChangeListeners == null) { + return Collections.EMPTY_LIST; + } else { + return Collections + .unmodifiableCollection(propertyValueChangeListeners); + } + } + return super.getListeners(eventType); + } + + @Override + protected void fireItemAdded(int position, Object itemId, Item item) { + if (position >= 0) { + super.fireItemAdded(position, itemId, item); + } + } + + @Override + protected void fireItemSetChange() { + fireItemSetChange(new IndexedContainer.ItemSetChangeEvent(this, -1)); + } + + /** + * Adds new single Property change listener. + * + * @param propertyId + * the ID of the Property to add. + * @param itemId + * the ID of the Item . + * @param listener + * the listener to be added. + */ + private void addSinglePropertyChangeListener(Object propertyId, + Object itemId, Property.ValueChangeListener listener) { + if (listener != null) { + if (singlePropertyValueChangeListeners == null) { + singlePropertyValueChangeListeners = new Hashtable>>(); + } + Map> propertySetToListenerListMap = singlePropertyValueChangeListeners + .get(propertyId); + if (propertySetToListenerListMap == null) { + propertySetToListenerListMap = new Hashtable>(); + singlePropertyValueChangeListeners.put(propertyId, + propertySetToListenerListMap); + } + List listenerList = propertySetToListenerListMap + .get(itemId); + if (listenerList == null) { + listenerList = new LinkedList(); + propertySetToListenerListMap.put(itemId, listenerList); + } + listenerList.add(listener); + } + } + + /** + * Removes a previously registered single Property change listener. + * + * @param propertyId + * the ID of the Property to remove. + * @param itemId + * the ID of the Item. + * @param listener + * the listener to be removed. + */ + private void removeSinglePropertyChangeListener(Object propertyId, + Object itemId, Property.ValueChangeListener listener) { + if (listener != null && singlePropertyValueChangeListeners != null) { + final Map> propertySetToListenerListMap = singlePropertyValueChangeListeners + .get(propertyId); + if (propertySetToListenerListMap != null) { + final List listenerList = propertySetToListenerListMap + .get(itemId); + if (listenerList != null) { + listenerList.remove(listener); + if (listenerList.isEmpty()) { + propertySetToListenerListMap.remove(itemId); + } + } + if (propertySetToListenerListMap.isEmpty()) { + singlePropertyValueChangeListeners.remove(propertyId); + } + } + if (singlePropertyValueChangeListeners.isEmpty()) { + singlePropertyValueChangeListeners = null; + } + } + } + + /* Internal Item and Property implementations */ + + /* + * A class implementing the com.vaadin.data.Item interface to be contained + * in the list. + * + * @author Vaadin Ltd. + * + * + * @since 3.0 + */ + class IndexedContainerItem implements Item { + + /** + * Item ID in the host container for this Item. + */ + private final Object itemId; + + /** + * Constructs a new ListItem instance and connects it to a host + * container. + * + * @param itemId + * the Item ID of the new Item. + */ + private IndexedContainerItem(Object itemId) { + this.itemId = itemId; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Item#getItemProperty(java.lang.Object) + */ + @Override + public Property getItemProperty(Object id) { + if (!propertyIds.contains(id)) { + return null; + } + + return new IndexedContainerProperty(itemId, id); + } + + @Override + public Collection getItemPropertyIds() { + return Collections.unmodifiableCollection(propertyIds); + } + + /** + * Gets the String representation of the contents of the + * Item. The format of the string is a space separated catenation of the + * String representations of the values of the Properties + * contained by the Item. + * + * @return String representation of the Item contents + */ + @Override + public String toString() { + String retValue = ""; + + for (final Iterator i = propertyIds.iterator(); i.hasNext();) { + final Object propertyId = i.next(); + retValue += getItemProperty(propertyId).getValue(); + if (i.hasNext()) { + retValue += " "; + } + } + + return retValue; + } + + /** + * Calculates a integer hash-code for the Item that's unique inside the + * list. Two Items inside the same list have always different + * hash-codes, though Items in different lists may have identical + * hash-codes. + * + * @return A locally unique hash-code as integer + */ + @Override + public int hashCode() { + return itemId.hashCode(); + } + + /** + * Tests if the given object is the same as the this object. Two Items + * got from a list container with the same ID are equal. + * + * @param obj + * an object to compare with this object + * @return true if the given object is the same as this + * object, false if not + */ + @Override + public boolean equals(Object obj) { + if (obj == null + || !obj.getClass().equals(IndexedContainerItem.class)) { + return false; + } + final IndexedContainerItem li = (IndexedContainerItem) obj; + return getHost() == li.getHost() && itemId.equals(li.itemId); + } + + private IndexedContainer getHost() { + return IndexedContainer.this; + } + + /** + * IndexedContainerItem does not support adding new properties. Add + * properties at container level. See + * {@link IndexedContainer#addContainerProperty(Object, Class, Object)} + * + * @see com.vaadin.v7.data.Item#addProperty(Object, Property) + */ + @Override + public boolean addItemProperty(Object id, Property property) + throws UnsupportedOperationException { + throw new UnsupportedOperationException("Indexed container item " + + "does not support adding new properties"); + } + + /** + * Indexed container does not support removing properties. Remove + * properties at container level. See + * {@link IndexedContainer#removeContainerProperty(Object)} + * + * @see com.vaadin.v7.data.Item#removeProperty(Object) + */ + @Override + public boolean removeItemProperty(Object id) + throws UnsupportedOperationException { + throw new UnsupportedOperationException( + "Indexed container item does not support property removal"); + } + + } + + /** + * A class implementing the {@link Property} interface to be contained in + * the {@link IndexedContainerItem} contained in the + * {@link IndexedContainer}. + * + * @author Vaadin Ltd. + * + * @since 3.0 + */ + private class IndexedContainerProperty + implements Property, Property.ValueChangeNotifier { + + /** + * ID of the Item, where this property resides. + */ + private final Object itemId; + + /** + * Id of the Property. + */ + private final Object propertyId; + + /** + * Constructs a new {@link IndexedContainerProperty} object. + * + * @param itemId + * the ID of the Item to connect the new Property to. + * @param propertyId + * the Property ID of the new Property. + * @param host + * the list that contains the Item to contain the new + * Property. + */ + private IndexedContainerProperty(Object itemId, Object propertyId) { + if (itemId == null || propertyId == null) { + // Null ids are not accepted + throw new NullPointerException( + "Container item or property ids can not be null"); + } + this.propertyId = propertyId; + this.itemId = itemId; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property#getType() + */ + @Override + public Class getType() { + return (Class) types.get(propertyId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property#getValue() + */ + @Override + public T getValue() { + return (T) items.get(itemId).get(propertyId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property#isReadOnly() + */ + @Override + public boolean isReadOnly() { + return readOnlyProperties.contains(this); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property#setReadOnly(boolean) + */ + @Override + public void setReadOnly(boolean newStatus) { + if (newStatus) { + readOnlyProperties.add(this); + } else { + readOnlyProperties.remove(this); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property#setValue(java.lang.Object) + */ + @Override + public void setValue(Object newValue) + throws Property.ReadOnlyException { + // Gets the Property set + final Map propertySet = items.get(itemId); + + // Support null values on all types + if (newValue == null) { + propertySet.remove(propertyId); + } else if (getType().isAssignableFrom(newValue.getClass())) { + propertySet.put(propertyId, newValue); + } else { + throw new IllegalArgumentException( + "Value is of invalid type, got " + + newValue.getClass().getName() + " but " + + getType().getName() + " was expected"); + } + + // update the container filtering if this property is being filtered + if (isPropertyFiltered(propertyId)) { + filterAll(); + } + + firePropertyValueChange(this); + } + + /** + * Calculates a integer hash-code for the Property that's unique inside + * the Item containing the Property. Two different Properties inside the + * same Item contained in the same list always have different + * hash-codes, though Properties in different Items may have identical + * hash-codes. + * + * @return A locally unique hash-code as integer + */ + @Override + public int hashCode() { + return itemId.hashCode() ^ propertyId.hashCode(); + } + + /** + * Tests if the given object is the same as the this object. Two + * Properties got from an Item with the same ID are equal. + * + * @param obj + * an object to compare with this object + * @return true if the given object is the same as this + * object, false if not + */ + @Override + public boolean equals(Object obj) { + if (obj == null + || !obj.getClass().equals(IndexedContainerProperty.class)) { + return false; + } + final IndexedContainerProperty lp = (IndexedContainerProperty) obj; + return lp.getHost() == getHost() && lp.propertyId.equals(propertyId) + && lp.itemId.equals(itemId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property.ValueChangeNotifier#addListener( + * com.vaadin.data.Property.ValueChangeListener) + */ + @Override + public void addValueChangeListener( + Property.ValueChangeListener listener) { + addSinglePropertyChangeListener(propertyId, itemId, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)} + **/ + @Override + @Deprecated + public void addListener(Property.ValueChangeListener listener) { + addValueChangeListener(listener); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Property.ValueChangeNotifier#removeListener + * (com.vaadin.data.Property.ValueChangeListener) + */ + @Override + public void removeValueChangeListener( + Property.ValueChangeListener listener) { + removeSinglePropertyChangeListener(propertyId, itemId, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(Property.ValueChangeListener listener) { + removeValueChangeListener(listener); + } + + private IndexedContainer getHost() { + return IndexedContainer.this; + } + + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], + * boolean[]) + */ + @Override + public void sort(Object[] propertyId, boolean[] ascending) { + sortContainer(propertyId, ascending); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds + * () + */ + @Override + public Collection getSortableContainerPropertyIds() { + return getSortablePropertyIds(); + } + + @Override + public ItemSorter getItemSorter() { + return super.getItemSorter(); + } + + @Override + public void setItemSorter(ItemSorter itemSorter) { + super.setItemSorter(itemSorter); + } + + /** + * Supports cloning of the IndexedContainer cleanly. + * + * @throws CloneNotSupportedException + * if an object cannot be cloned. . + * + * @deprecated As of 6.6. Cloning support might be removed from + * IndexedContainer in the future + */ + @Deprecated + @Override + public Object clone() throws CloneNotSupportedException { + + // Creates the clone + final IndexedContainer nc = new IndexedContainer(); + + // Clone the shallow properties + nc.setAllItemIds(getAllItemIds() != null + ? (ListSet) ((ListSet) getAllItemIds()).clone() + : null); + nc.setItemSetChangeListeners(getItemSetChangeListeners() != null + ? new LinkedList( + getItemSetChangeListeners()) + : null); + nc.propertyIds = propertyIds != null + ? (ArrayList) propertyIds.clone() : null; + nc.setPropertySetChangeListeners(getPropertySetChangeListeners() != null + ? new LinkedList( + getPropertySetChangeListeners()) + : null); + nc.propertyValueChangeListeners = propertyValueChangeListeners != null + ? (LinkedList) propertyValueChangeListeners + .clone() + : null; + nc.readOnlyProperties = readOnlyProperties != null + ? (HashSet>) readOnlyProperties.clone() : null; + nc.singlePropertyValueChangeListeners = singlePropertyValueChangeListeners != null + ? (Hashtable>>) singlePropertyValueChangeListeners + .clone() + : null; + + nc.types = types != null ? (Hashtable>) types.clone() + : null; + + nc.setFilters( + (HashSet) ((HashSet) getFilters()).clone()); + + nc.setFilteredItemIds(getFilteredItemIds() == null ? null + : (ListSet) ((ListSet) getFilteredItemIds()) + .clone()); + + // Clone property-values + if (items == null) { + nc.items = null; + } else { + nc.items = new Hashtable>(); + for (final Iterator i = items.keySet().iterator(); i + .hasNext();) { + final Object id = i.next(); + final Hashtable it = (Hashtable) items + .get(id); + nc.items.put(id, (Map) it.clone()); + } + } + + return nc; + } + + @Override + public void addContainerFilter(Object propertyId, String filterString, + boolean ignoreCase, boolean onlyMatchPrefix) { + try { + addFilter(new SimpleStringFilter(propertyId, filterString, + ignoreCase, onlyMatchPrefix)); + } catch (UnsupportedFilterException e) { + // the filter instance created here is always valid for in-memory + // containers + } + } + + @Override + public void removeAllContainerFilters() { + removeAllFilters(); + } + + @Override + public void removeContainerFilters(Object propertyId) { + removeFilters(propertyId); + } + + @Override + public void addContainerFilter(Filter filter) + throws UnsupportedFilterException { + addFilter(filter); + } + + @Override + public void removeContainerFilter(Filter filter) { + removeFilter(filter); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() + */ + @Override + public boolean hasContainerFilters() { + return super.hasContainerFilters(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() + */ + @Override + public Collection getContainerFilters() { + return super.getContainerFilters(); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheFlushNotifier.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheFlushNotifier.java new file mode 100644 index 0000000000..fe9af78922 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheFlushNotifier.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.v7.data.util.sqlcontainer; + +import java.io.Serializable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.v7.data.util.sqlcontainer.query.FreeformQuery; +import com.vaadin.v7.data.util.sqlcontainer.query.QueryDelegate; +import com.vaadin.v7.data.util.sqlcontainer.query.TableQuery; + +/** + * CacheFlushNotifier is a simple static notification mechanism to inform other + * SQLContainers that the contents of their caches may have become stale. + */ +class CacheFlushNotifier implements Serializable { + /* + * SQLContainer instance reference list and dead reference queue. Used for + * the cache flush notification feature. + */ + private static List> allInstances = new ArrayList>(); + private static ReferenceQueue deadInstances = new ReferenceQueue(); + + /** + * Adds the given SQLContainer to the cache flush notification receiver list + * + * @param c + * Container to add + */ + public static void addInstance(SQLContainer c) { + removeDeadReferences(); + if (c != null) { + allInstances.add(new WeakReference(c, deadInstances)); + } + } + + /** + * Removes dead references from instance list + */ + private static void removeDeadReferences() { + java.lang.ref.Reference dead = deadInstances + .poll(); + while (dead != null) { + allInstances.remove(dead); + dead = deadInstances.poll(); + } + } + + /** + * Iterates through the instances and notifies containers which are + * connected to the same table or are using the same query string. + * + * @param c + * SQLContainer that issued the cache flush notification + */ + public static void notifyOfCacheFlush(SQLContainer c) { + removeDeadReferences(); + for (WeakReference wr : allInstances) { + if (wr.get() != null) { + SQLContainer wrc = wr.get(); + if (wrc == null) { + continue; + } + /* + * If the reference points to the container sending the + * notification, do nothing. + */ + if (wrc.equals(c)) { + continue; + } + /* Compare QueryDelegate types and tableName/queryString */ + QueryDelegate wrQd = wrc.getQueryDelegate(); + QueryDelegate qd = c.getQueryDelegate(); + if (wrQd instanceof TableQuery && qd instanceof TableQuery + && ((TableQuery) wrQd).getTableName() + .equals(((TableQuery) qd).getTableName())) { + wrc.refresh(); + } else if (wrQd instanceof FreeformQuery + && qd instanceof FreeformQuery + && ((FreeformQuery) wrQd).getQueryString().equals( + ((FreeformQuery) qd).getQueryString())) { + wrc.refresh(); + } + } + } + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheMap.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheMap.java new file mode 100644 index 0000000000..f986e7db51 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/CacheMap.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.v7.data.util.sqlcontainer; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * CacheMap extends LinkedHashMap, adding the possibility to adjust maximum + * number of items. In SQLContainer this is used for RowItem -cache. Cache size + * will be two times the page length parameter of the container. + */ +class CacheMap extends LinkedHashMap { + private static final long serialVersionUID = 679999766473555231L; + private int cacheLimit = SQLContainer.CACHE_RATIO + * SQLContainer.DEFAULT_PAGE_LENGTH; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > cacheLimit; + } + + void setCacheLimit(int limit) { + cacheLimit = limit > 0 ? limit : SQLContainer.DEFAULT_PAGE_LENGTH; + } + + int getCacheLimit() { + return cacheLimit; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ColumnProperty.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ColumnProperty.java new file mode 100644 index 0000000000..d437cc9b33 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ColumnProperty.java @@ -0,0 +1,357 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer; + +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.logging.Logger; + +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.converter.Converter.ConversionException; + +/** + * ColumnProperty represents the value of one column in a RowItem. In addition + * to the value, ColumnProperty also contains some basic column attributes such + * as nullability status, read-only status and data type. + * + * Note that depending on the QueryDelegate in use this does not necessarily map + * into an actual column in a database table. + */ +final public class ColumnProperty implements Property { + private static final long serialVersionUID = -3694463129581802457L; + + private RowItem owner; + + private String propertyId; + + private boolean readOnly; + private boolean allowReadOnlyChange = true; + private boolean nullable = true; + + private Object value; + private Object changedValue; + private Class type; + + private boolean modified; + + private boolean versionColumn; + private boolean primaryKey = false; + + /** + * Prevent instantiation without required parameters. + */ + @SuppressWarnings("unused") + private ColumnProperty() { + } + + /** + * Deprecated constructor for ColumnProperty. If this is used the primary + * keys are not identified correctly in some cases for some databases (i.e. + * Oracle). See http://dev.vaadin.com/ticket/9145. + * + * @param propertyId + * @param readOnly + * @param allowReadOnlyChange + * @param nullable + * @param value + * @param type + * + * @deprecated As of 7.0. Use + * {@link #ColumnProperty(String, boolean, boolean, boolean, boolean, Object, Class) + * instead + */ + @Deprecated + public ColumnProperty(String propertyId, boolean readOnly, + boolean allowReadOnlyChange, boolean nullable, Object value, + Class type) { + this(propertyId, readOnly, allowReadOnlyChange, nullable, false, value, + type); + } + + /** + * Creates a new ColumnProperty instance. + * + * @param propertyId + * The ID of this property. + * @param readOnly + * Whether this property is read-only. + * @param allowReadOnlyChange + * Whether the read-only status of this property can be changed. + * @param nullable + * Whether this property accepts null values. + * @param primaryKey + * Whether this property corresponds to a database primary key. + * @param value + * The value of this property. + * @param type + * The type of this property. + */ + public ColumnProperty(String propertyId, boolean readOnly, + boolean allowReadOnlyChange, boolean nullable, boolean primaryKey, + Object value, Class type) { + + if (propertyId == null) { + throw new IllegalArgumentException("Properties must be named."); + } + if (type == null) { + throw new IllegalArgumentException("Property type must be set."); + } + this.propertyId = propertyId; + this.type = type; + this.value = value; + + this.allowReadOnlyChange = allowReadOnlyChange; + this.nullable = nullable; + this.readOnly = readOnly; + this.primaryKey = primaryKey; + } + + /** + * Returns the current value for this property. To get the previous value + * (if one exists) for a modified property use {@link #getOldValue()}. + * + * @return + */ + @Override + public Object getValue() { + if (isModified()) { + return changedValue; + } + return value; + } + + /** + * Returns the original non-modified value of this property if it has been + * modified. + * + * @return The original value if isModified() is true, + * getValue() otherwise. + */ + public Object getOldValue() { + return value; + } + + @Override + public void setValue(Object newValue) + throws ReadOnlyException, ConversionException { + if (newValue == null && !nullable) { + throw new NotNullableException( + "Null values are not allowed for this property."); + } + if (readOnly) { + throw new ReadOnlyException( + "Cannot set value for read-only property."); + } + + /* Check if this property is a date property. */ + boolean isDateProperty = Time.class.equals(getType()) + || Date.class.equals(getType()) + || Timestamp.class.equals(getType()); + + if (newValue != null) { + /* Handle SQL dates, times and Timestamps given as java.util.Date */ + if (isDateProperty) { + /* + * Try to get the millisecond value from the new value of this + * property. Possible type to convert from is java.util.Date. + */ + long millis = 0; + if (newValue instanceof java.util.Date) { + millis = ((java.util.Date) newValue).getTime(); + /* + * Create the new object based on the millisecond value, + * according to the type of this property. + */ + if (Time.class.equals(getType())) { + newValue = new Time(millis); + } else if (Date.class.equals(getType())) { + newValue = new Date(millis); + } else if (Timestamp.class.equals(getType())) { + newValue = new Timestamp(millis); + } + } + } + + if (!getType().isAssignableFrom(newValue.getClass())) { + throw new IllegalArgumentException( + "Illegal value type for ColumnProperty"); + } + + /* + * If the value to be set is the same that has already been set, do + * not set it again. + */ + if (isValueAlreadySet(newValue)) { + return; + } + } + + /* Set the new value and notify container of the change. */ + changedValue = newValue; + modified = true; + owner.getContainer().itemChangeNotification(owner); + } + + private boolean isValueAlreadySet(Object newValue) { + Object referenceValue = isModified() ? changedValue : value; + + return (isNullable() && newValue == null && referenceValue == null) + || newValue.equals(referenceValue); + } + + @Override + public Class getType() { + return type; + } + + @Override + public boolean isReadOnly() { + return readOnly; + } + + /** + * Returns whether the read-only status of this property can be changed + * using {@link #setReadOnly(boolean)}. + *

    + * Used to prevent setting to read/write mode a property that is not allowed + * to be written by the underlying database. Also used for values like + * VERSION and AUTO_INCREMENT fields that might be set to read-only by the + * container but the database still allows writes. + * + * @return true if the read-only status can be changed, false otherwise. + */ + public boolean isReadOnlyChangeAllowed() { + return allowReadOnlyChange; + } + + @Override + public void setReadOnly(boolean newStatus) { + if (allowReadOnlyChange) { + readOnly = newStatus; + } + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public String getPropertyId() { + return propertyId; + } + + private static Logger getLogger() { + return Logger.getLogger(ColumnProperty.class.getName()); + } + + public void setOwner(RowItem owner) { + if (owner == null) { + throw new IllegalArgumentException("Owner can not be set to null."); + } + if (this.owner != null) { + throw new IllegalStateException( + "ColumnProperties can only be bound once."); + } + this.owner = owner; + } + + public boolean isModified() { + return modified; + } + + public boolean isVersionColumn() { + return versionColumn; + } + + public void setVersionColumn(boolean versionColumn) { + this.versionColumn = versionColumn; + } + + public boolean isNullable() { + return nullable; + } + + /** + * Return whether the value of this property should be persisted to the + * database. + * + * @return true if the value should be written to the database, false + * otherwise. + */ + public boolean isPersistent() { + if (isVersionColumn()) { + return false; + } else if (isReadOnlyChangeAllowed() && !isReadOnly()) { + return true; + } else { + return false; + } + } + + /** + * Returns whether or not this property is used as a row identifier. + * + * @return true if the property is a row identifier, false otherwise. + */ + public boolean isRowIdentifier() { + return isPrimaryKey() || isVersionColumn(); + } + + /** + * An exception that signals that a null value was passed to + * the setValue method, but the value of this property can not + * be set to null. + */ + @SuppressWarnings("serial") + public class NotNullableException extends RuntimeException { + + /** + * Constructs a new NotNullableException without a detail + * message. + */ + public NotNullableException() { + } + + /** + * Constructs a new NotNullableException with the specified + * detail message. + * + * @param msg + * the detail message + */ + public NotNullableException(String msg) { + super(msg); + } + + /** + * Constructs a new NotNullableException from another + * exception. + * + * @param cause + * The cause of the failure + */ + public NotNullableException(Throwable cause) { + super(cause); + } + } + + public void commit() { + if (isModified()) { + modified = false; + value = changedValue; + } + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/OptimisticLockException.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/OptimisticLockException.java new file mode 100644 index 0000000000..887aa9c458 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/OptimisticLockException.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.v7.data.util.sqlcontainer; + +import com.vaadin.v7.data.util.sqlcontainer.query.TableQuery; + +/** + * An OptimisticLockException is thrown when trying to update or delete a row + * that has been changed since last read from the database. + * + * OptimisticLockException is a runtime exception because optimistic locking is + * turned off by default, and as such will never be thrown in a default + * configuration. In order to turn on optimistic locking, you need to specify + * the version column in your TableQuery instance. + * + * @see TableQuery#setVersionColumn(String) + * + * @author Jonatan Kronqvist / Vaadin Ltd + */ +public class OptimisticLockException extends RuntimeException { + + private final RowId rowId; + + public OptimisticLockException(RowId rowId) { + super(); + this.rowId = rowId; + } + + public OptimisticLockException(String msg, RowId rowId) { + super(msg); + this.rowId = rowId; + } + + public RowId getRowId() { + return rowId; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ReadOnlyRowId.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ReadOnlyRowId.java new file mode 100644 index 0000000000..2a6cca6fc6 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/ReadOnlyRowId.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer; + +public class ReadOnlyRowId extends RowId { + private static final long serialVersionUID = -2626764781642012467L; + private final Integer rowNum; + + public ReadOnlyRowId(int rowNum) { + super(); + this.rowNum = rowNum; + } + + @Override + public int hashCode() { + return getRowNum(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(ReadOnlyRowId.class.equals(obj.getClass()))) { + return false; + } + return getRowNum() == (((ReadOnlyRowId) obj).getRowNum()); + } + + public int getRowNum() { + return rowNum; + } + + @Override + public String toString() { + return String.valueOf(getRowNum()); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/Reference.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/Reference.java new file mode 100644 index 0000000000..a80b07aca2 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/Reference.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.v7.data.util.sqlcontainer; + +import java.io.Serializable; + +/** + * The reference class represents a simple [usually foreign key] reference to + * another SQLContainer. Actual foreign key reference in the database is not + * required, but it is recommended to make sure that certain constraints are + * followed. + */ +@SuppressWarnings("serial") +class Reference implements Serializable { + + /** + * The SQLContainer that this reference points to. + */ + private SQLContainer referencedContainer; + + /** + * The column ID/name in the referencing SQLContainer that contains the key + * used for the reference. + */ + private String referencingColumn; + + /** + * The column ID/name in the referenced SQLContainer that contains the key + * used for the reference. + */ + private String referencedColumn; + + /** + * Constructs a new reference to be used within the SQLContainer to + * reference another SQLContainer. + */ + Reference(SQLContainer referencedContainer, String referencingColumn, + String referencedColumn) { + this.referencedContainer = referencedContainer; + this.referencingColumn = referencingColumn; + this.referencedColumn = referencedColumn; + } + + SQLContainer getReferencedContainer() { + return referencedContainer; + } + + String getReferencingColumn() { + return referencingColumn; + } + + String getReferencedColumn() { + return referencedColumn; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowId.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowId.java new file mode 100644 index 0000000000..0181dac2b3 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowId.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.v7.data.util.sqlcontainer; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * RowId represents identifiers of a single database result set row. + * + * The data structure of a RowId is an Object array which contains the values of + * the primary key columns of the identified row. This allows easy equals() + * -comparison of RowItems. + */ +public class RowId implements Serializable { + private static final long serialVersionUID = -3161778404698901258L; + protected Object[] id; + + /** + * Prevent instantiation without required parameters. + */ + protected RowId() { + } + + public RowId(Object... id) { + if (id == null) { + throw new IllegalArgumentException( + "id parameter must not be null!"); + } + this.id = id; + } + + public Object[] getId() { + return id; + } + + @Override + public int hashCode() { + return Arrays.hashCode(getId()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(RowId.class.equals(obj.getClass()))) { + return false; + } + return Arrays.equals(getId(), ((RowId) obj).getId()); + } + + @Override + public String toString() { + if (getId() == null) { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (Object id : getId()) { + builder.append(id); + builder.append('/'); + } + if (builder.length() > 0) { + return builder.substring(0, builder.length() - 1); + } + return builder.toString(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowItem.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowItem.java new file mode 100644 index 0000000000..b8039e1e68 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/RowItem.java @@ -0,0 +1,145 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; + +/** + * RowItem represents one row of a result set obtained from a QueryDelegate. + * + * Note that depending on the QueryDelegate in use this does not necessarily map + * into an actual row in a database table. + */ +public final class RowItem implements Item { + private static final long serialVersionUID = -6228966439127951408L; + private SQLContainer container; + private RowId id; + private Collection properties; + + /** + * Prevent instantiation without required parameters. + */ + @SuppressWarnings("unused") + private RowItem() { + } + + public RowItem(SQLContainer container, RowId id, + Collection properties) { + if (container == null) { + throw new IllegalArgumentException("Container cannot be null."); + } + if (id == null) { + throw new IllegalArgumentException("Row ID cannot be null."); + } + this.container = container; + this.properties = properties; + /* Set this RowItem as owner to the properties */ + if (properties != null) { + for (ColumnProperty p : properties) { + p.setOwner(this); + } + } + this.id = id; + } + + @Override + public Property getItemProperty(Object id) { + if (id instanceof String && id != null) { + for (ColumnProperty cp : properties) { + if (id.equals(cp.getPropertyId())) { + return cp; + } + } + } + return null; + } + + @Override + public Collection getItemPropertyIds() { + Collection ids = new ArrayList(properties.size()); + for (ColumnProperty cp : properties) { + ids.add(cp.getPropertyId()); + } + return Collections.unmodifiableCollection(ids); + } + + /** + * Adding properties is not supported. Properties are generated by + * SQLContainer. + */ + @Override + public boolean addItemProperty(Object id, Property property) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * Removing properties is not supported. Properties are generated by + * SQLContainer. + */ + @Override + public boolean removeItemProperty(Object id) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + public RowId getId() { + return id; + } + + public SQLContainer getContainer() { + return container; + } + + public boolean isModified() { + if (properties != null) { + for (ColumnProperty p : properties) { + if (p.isModified()) { + return true; + } + } + } + return false; + } + + @Override + public String toString() { + StringBuffer s = new StringBuffer(); + s.append("ID:"); + s.append(getId().toString()); + for (Object propId : getItemPropertyIds()) { + s.append("|"); + s.append(propId.toString()); + s.append(":"); + Object value = getItemProperty(propId).getValue(); + s.append((null != value) ? value.toString() : null); + } + return s.toString(); + } + + public void commit() { + if (properties != null) { + for (ColumnProperty p : properties) { + p.commit(); + } + } + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLContainer.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLContainer.java new file mode 100644 index 0000000000..b2d5b91746 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLContainer.java @@ -0,0 +1,1875 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer; + +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Date; +import java.util.EventObject; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.ContainerHelpers; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.filter.Compare.Equal; +import com.vaadin.v7.data.util.filter.Like; +import com.vaadin.v7.data.util.filter.UnsupportedFilterException; +import com.vaadin.v7.data.util.sqlcontainer.query.OrderBy; +import com.vaadin.v7.data.util.sqlcontainer.query.QueryDelegate; +import com.vaadin.v7.data.util.sqlcontainer.query.QueryDelegate.RowIdChangeListener; +import com.vaadin.v7.data.util.sqlcontainer.query.TableQuery; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.MSSQLGenerator; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.OracleGenerator; + +public class SQLContainer implements Container, Container.Filterable, + Container.Indexed, Container.Sortable, Container.ItemSetChangeNotifier { + + /** Query delegate */ + private QueryDelegate queryDelegate; + /** Auto commit mode, default = false */ + private boolean autoCommit = false; + + /** Page length = number of items contained in one page */ + private int pageLength = DEFAULT_PAGE_LENGTH; + public static final int DEFAULT_PAGE_LENGTH = 100; + + /** Number of items to cache = CACHE_RATIO x pageLength */ + public static final int CACHE_RATIO = 2; + + /** Amount of cache to overlap with previous page */ + private int cacheOverlap = pageLength; + + /** Item and index caches */ + private final Map itemIndexes = new HashMap(); + private final CacheMap cachedItems = new CacheMap(); + + /** Container properties = column names, data types and statuses */ + private final List propertyIds = new ArrayList(); + private final Map> propertyTypes = new HashMap>(); + private final Map propertyReadOnly = new HashMap(); + private final Map propertyPersistable = new HashMap(); + private final Map propertyNullable = new HashMap(); + private final Map propertyPrimaryKey = new HashMap(); + + /** Filters (WHERE) and sorters (ORDER BY) */ + private final List filters = new ArrayList(); + private final List sorters = new ArrayList(); + + /** + * Total number of items available in the data source using the current + * query, filters and sorters. + */ + private int size; + + /** + * Size updating logic. Do not update size from data source if it has been + * updated in the last sizeValidMilliSeconds milliseconds. + */ + private final int sizeValidMilliSeconds = 10000; + private boolean sizeDirty = true; + private Date sizeUpdated = new Date(); + + /** Starting row number of the currently fetched page */ + private int currentOffset; + + /** ItemSetChangeListeners */ + private LinkedList itemSetChangeListeners; + + /** + * Temporary storage for modified items and items to be removed and added + */ + private final Map removedItems = new HashMap(); + private final List addedItems = new ArrayList(); + private final List modifiedItems = new ArrayList(); + + /** List of references to other SQLContainers */ + private final Map references = new HashMap(); + + /** Cache flush notification system enabled. Disabled by default. */ + private boolean notificationsEnabled; + + /** + * Prevent instantiation without a QueryDelegate. + */ + @SuppressWarnings("unused") + private SQLContainer() { + } + + /** + * Creates and initializes SQLContainer using the given QueryDelegate + * + * @param delegate + * QueryDelegate implementation + * @throws SQLException + */ + public SQLContainer(QueryDelegate delegate) throws SQLException { + if (delegate == null) { + throw new IllegalArgumentException( + "QueryDelegate must not be null."); + } + queryDelegate = delegate; + getPropertyIds(); + cachedItems.setCacheLimit(CACHE_RATIO * getPageLength() + cacheOverlap); + } + + /**************************************/ + /** Methods from interface Container **/ + /**************************************/ + + /** + * Note! If auto commit mode is enabled, this method will still return the + * temporary row ID assigned for the item. Implement + * QueryDelegate.RowIdChangeListener to receive the actual Row ID value + * after the addition has been committed. + * + * {@inheritDoc} + */ + + @Override + public Object addItem() throws UnsupportedOperationException { + Object emptyKey[] = new Object[queryDelegate.getPrimaryKeyColumns() + .size()]; + RowId itemId = new TemporaryRowId(emptyKey); + // Create new empty column properties for the row item. + List itemProperties = new ArrayList(); + for (String propertyId : propertyIds) { + /* Default settings for new item properties. */ + ColumnProperty cp = new ColumnProperty(propertyId, + propertyReadOnly.get(propertyId), + propertyPersistable.get(propertyId), + propertyNullable.get(propertyId), + propertyPrimaryKey.get(propertyId), null, + getType(propertyId)); + + itemProperties.add(cp); + } + RowItem newRowItem = new RowItem(this, itemId, itemProperties); + + if (autoCommit) { + /* Add and commit instantly */ + try { + if (queryDelegate instanceof TableQuery) { + itemId = ((TableQuery) queryDelegate) + .storeRowImmediately(newRowItem); + } else { + queryDelegate.beginTransaction(); + queryDelegate.storeRow(newRowItem); + queryDelegate.commit(); + } + refresh(); + if (notificationsEnabled) { + CacheFlushNotifier.notifyOfCacheFlush(this); + } + getLogger().log(Level.FINER, "Row added to DB..."); + return itemId; + } catch (SQLException e) { + getLogger().log(Level.WARNING, + "Failed to add row to DB. Rolling back.", e); + try { + queryDelegate.rollback(); + } catch (SQLException ee) { + getLogger().log(Level.SEVERE, + "Failed to roll back row addition", e); + } + return null; + } + } else { + addedItems.add(newRowItem); + fireContentsChange(); + return itemId; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#containsId(java.lang.Object) + */ + + @Override + public boolean containsId(Object itemId) { + if (itemId == null) { + return false; + } + + if (cachedItems.containsKey(itemId)) { + return true; + } else { + for (RowItem item : addedItems) { + if (item.getId().equals(itemId)) { + return itemPassesFilters(item); + } + } + } + if (removedItems.containsKey(itemId)) { + return false; + } + + if (itemId instanceof ReadOnlyRowId) { + int rowNum = ((ReadOnlyRowId) itemId).getRowNum(); + return rowNum >= 0 && rowNum < size; + } + + if (itemId instanceof RowId && !(itemId instanceof TemporaryRowId)) { + try { + return queryDelegate + .containsRowWithKey(((RowId) itemId).getId()); + } catch (Exception e) { + /* Query failed, just return false. */ + getLogger().log(Level.WARNING, "containsId query failed", e); + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getContainerProperty(java.lang.Object, + * java.lang.Object) + */ + + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + Item item = getItem(itemId); + if (item == null) { + return null; + } + return item.getItemProperty(propertyId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getContainerPropertyIds() + */ + + @Override + public Collection getContainerPropertyIds() { + return Collections.unmodifiableCollection(propertyIds); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getItem(java.lang.Object) + */ + + @Override + public Item getItem(Object itemId) { + if (!cachedItems.containsKey(itemId)) { + int index = indexOfId(itemId); + if (index >= size) { + // The index is in the added items + int offset = index - size; + RowItem item = addedItems.get(offset); + if (itemPassesFilters(item)) { + return item; + } else { + return null; + } + } else { + // load the item into cache + updateOffsetAndCache(index); + } + } + return cachedItems.get(itemId); + } + + /** + * Bypasses in-memory filtering to return items that are cached in memory. + * NOTE: This does not bypass database-level filtering. + * + * @param itemId + * the id of the item to retrieve. + * @return the item represented by itemId. + */ + public Item getItemUnfiltered(Object itemId) { + if (!cachedItems.containsKey(itemId)) { + for (RowItem item : addedItems) { + if (item.getId().equals(itemId)) { + return item; + } + } + } + return cachedItems.get(itemId); + } + + /** + * NOTE! Do not use this method if in any way avoidable. This method doesn't + * (and cannot) use lazy loading, which means that all rows in the database + * will be loaded into memory. + * + * {@inheritDoc} + */ + + @Override + public Collection getItemIds() { + updateCount(); + ArrayList ids = new ArrayList(); + ResultSet rs = null; + try { + // Load ALL rows :( + queryDelegate.beginTransaction(); + rs = queryDelegate.getResults(0, 0); + List pKeys = queryDelegate.getPrimaryKeyColumns(); + while (rs.next()) { + RowId id = null; + if (pKeys.isEmpty()) { + /* Create a read only itemId */ + id = new ReadOnlyRowId(rs.getRow()); + } else { + /* Generate itemId for the row based on primary key(s) */ + Object[] itemId = new Object[pKeys.size()]; + for (int i = 0; i < pKeys.size(); i++) { + itemId[i] = rs.getObject(pKeys.get(i)); + } + id = new RowId(itemId); + } + if (id != null && !removedItems.containsKey(id)) { + ids.add(id); + } + } + rs.getStatement().close(); + rs.close(); + queryDelegate.commit(); + } catch (SQLException e) { + getLogger().log(Level.WARNING, "getItemIds() failed, rolling back.", + e); + try { + queryDelegate.rollback(); + } catch (SQLException e1) { + getLogger().log(Level.SEVERE, "Failed to roll back state", e1); + } + try { + rs.getStatement().close(); + rs.close(); + } catch (SQLException e1) { + getLogger().log(Level.WARNING, "Closing session failed", e1); + } + throw new RuntimeException("Failed to fetch item indexes.", e); + } + for (RowItem item : getFilteredAddedItems()) { + ids.add(item.getId()); + } + return Collections.unmodifiableCollection(ids); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#getType(java.lang.Object) + */ + + @Override + public Class getType(Object propertyId) { + if (!propertyIds.contains(propertyId)) { + return null; + } + return propertyTypes.get(propertyId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#size() + */ + + @Override + public int size() { + updateCount(); + return size + sizeOfAddedItems() - removedItems.size(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeItem(java.lang.Object) + */ + + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + if (!containsId(itemId)) { + return false; + } + for (RowItem item : addedItems) { + if (item.getId().equals(itemId)) { + addedItems.remove(item); + fireContentsChange(); + return true; + } + } + + if (autoCommit) { + /* Remove and commit instantly. */ + Item i = getItem(itemId); + if (i == null) { + return false; + } + try { + queryDelegate.beginTransaction(); + boolean success = queryDelegate.removeRow((RowItem) i); + queryDelegate.commit(); + refresh(); + if (notificationsEnabled) { + CacheFlushNotifier.notifyOfCacheFlush(this); + } + if (success) { + getLogger().log(Level.FINER, "Row removed from DB..."); + } + return success; + } catch (SQLException e) { + getLogger().log(Level.WARNING, + "Failed to remove row, rolling back", e); + try { + queryDelegate.rollback(); + } catch (SQLException ee) { + /* Nothing can be done here */ + getLogger().log(Level.SEVERE, + "Failed to rollback row removal", ee); + } + return false; + } catch (OptimisticLockException e) { + getLogger().log(Level.WARNING, + "Failed to remove row, rolling back", e); + try { + queryDelegate.rollback(); + } catch (SQLException ee) { + /* Nothing can be done here */ + getLogger().log(Level.SEVERE, + "Failed to rollback row removal", ee); + } + throw e; + } + } else { + removedItems.put((RowId) itemId, (RowItem) getItem(itemId)); + cachedItems.remove(itemId); + refresh(); + return true; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeAllItems() + */ + + @Override + public boolean removeAllItems() throws UnsupportedOperationException { + if (autoCommit) { + /* Remove and commit instantly. */ + try { + queryDelegate.beginTransaction(); + boolean success = true; + for (Object id : getItemIds()) { + if (!queryDelegate.removeRow((RowItem) getItem(id))) { + success = false; + } + } + if (success) { + queryDelegate.commit(); + getLogger().log(Level.FINER, "All rows removed from DB..."); + refresh(); + if (notificationsEnabled) { + CacheFlushNotifier.notifyOfCacheFlush(this); + } + } else { + queryDelegate.rollback(); + } + return success; + } catch (SQLException e) { + getLogger().log(Level.WARNING, + "removeAllItems() failed, rolling back", e); + try { + queryDelegate.rollback(); + } catch (SQLException ee) { + /* Nothing can be done here */ + getLogger().log(Level.SEVERE, "Failed to roll back", ee); + } + return false; + } catch (OptimisticLockException e) { + getLogger().log(Level.WARNING, + "removeAllItems() failed, rolling back", e); + try { + queryDelegate.rollback(); + } catch (SQLException ee) { + /* Nothing can be done here */ + getLogger().log(Level.SEVERE, "Failed to roll back", ee); + } + throw e; + } + } else { + for (Object id : getItemIds()) { + removedItems.put((RowId) id, (RowItem) getItem(id)); + cachedItems.remove(id); + } + refresh(); + return true; + } + } + + /*************************************************/ + /** Methods from interface Container.Filterable **/ + /*************************************************/ + + /** + * {@inheritDoc} + */ + + @Override + public void addContainerFilter(Filter filter) + throws UnsupportedFilterException { + // filter.setCaseSensitive(!ignoreCase); + + filters.add(filter); + refresh(); + } + + /** + * {@inheritDoc} + */ + + @Override + public void removeContainerFilter(Filter filter) { + filters.remove(filter); + refresh(); + } + + /** + * {@inheritDoc} + */ + public void addContainerFilter(Object propertyId, String filterString, + boolean ignoreCase, boolean onlyMatchPrefix) { + if (propertyId == null || !propertyIds.contains(propertyId)) { + return; + } + + /* Generate Filter -object */ + String likeStr = onlyMatchPrefix ? filterString + "%" + : "%" + filterString + "%"; + Like like = new Like(propertyId.toString(), likeStr); + like.setCaseSensitive(!ignoreCase); + filters.add(like); + refresh(); + } + + /** + * {@inheritDoc} + */ + public void removeContainerFilters(Object propertyId) { + ArrayList toRemove = new ArrayList(); + for (Filter f : filters) { + if (f.appliesToProperty(propertyId)) { + toRemove.add(f); + } + } + filters.removeAll(toRemove); + refresh(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeAllContainerFilters() { + filters.clear(); + refresh(); + } + + /** + * Returns true if any filters have been applied to the container. + * + * @return true if the container has filters applied, false otherwise + * @since 7.1 + */ + public boolean hasContainerFilters() { + return !getContainerFilters().isEmpty(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Filterable#getContainerFilters() + */ + @Override + public Collection getContainerFilters() { + return Collections.unmodifiableCollection(filters); + } + + /**********************************************/ + /** Methods from interface Container.Indexed **/ + /**********************************************/ + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Indexed#indexOfId(java.lang.Object) + */ + + @Override + public int indexOfId(Object itemId) { + // First check if the id is in the added items + for (int ix = 0; ix < addedItems.size(); ix++) { + RowItem item = addedItems.get(ix); + if (item.getId().equals(itemId)) { + if (itemPassesFilters(item)) { + updateCount(); + return size + ix; + } else { + return -1; + } + } + } + + if (!containsId(itemId)) { + return -1; + } + if (cachedItems.isEmpty()) { + getPage(); + } + // this protects against infinite looping + int counter = 0; + int oldIndex; + while (counter < size) { + if (itemIndexes.containsValue(itemId)) { + for (Integer idx : itemIndexes.keySet()) { + if (itemIndexes.get(idx).equals(itemId)) { + return idx; + } + } + } + oldIndex = currentOffset; + // load in the next page. + int nextIndex = currentOffset + pageLength * CACHE_RATIO + + cacheOverlap; + if (nextIndex >= size) { + // Container wrapped around, start from index 0. + nextIndex = 0; + } + updateOffsetAndCache(nextIndex); + + // Update counter + if (currentOffset > oldIndex) { + counter += currentOffset - oldIndex; + } else { + counter += size - oldIndex; + } + } + // safeguard in case item not found + return -1; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Indexed#getIdByIndex(int) + */ + + @Override + public Object getIdByIndex(int index) { + if (index < 0) { + throw new IndexOutOfBoundsException( + "Index is negative! index=" + index); + } + // make sure the size field is valid + updateCount(); + if (index < size) { + if (itemIndexes.keySet().contains(index)) { + return itemIndexes.get(index); + } + updateOffsetAndCache(index); + return itemIndexes.get(index); + } else { + // The index is in the added items + int offset = index - size; + // TODO this is very inefficient if looping - should improve + // getItemIds(int, int) + return getFilteredAddedItems().get(offset).getId(); + } + } + + @Override + public List getItemIds(int startIndex, int numberOfIds) { + // TODO create a better implementation + return (List) ContainerHelpers + .getItemIdsUsingGetIdByIndex(startIndex, numberOfIds, this); + } + + /**********************************************/ + /** Methods from interface Container.Ordered **/ + /**********************************************/ + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object) + */ + + @Override + public Object nextItemId(Object itemId) { + int index = indexOfId(itemId) + 1; + try { + return getIdByIndex(index); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object) + */ + + @Override + public Object prevItemId(Object itemId) { + int prevIndex = indexOfId(itemId) - 1; + try { + return getIdByIndex(prevIndex); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#firstItemId() + */ + + @Override + public Object firstItemId() { + updateCount(); + if (size == 0) { + if (addedItems.isEmpty()) { + return null; + } else { + int ix = -1; + do { + ix++; + } while (!itemPassesFilters(addedItems.get(ix)) + && ix < addedItems.size()); + if (ix < addedItems.size()) { + return addedItems.get(ix).getId(); + } + } + } + if (!itemIndexes.containsKey(0)) { + updateOffsetAndCache(0); + } + return itemIndexes.get(0); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#lastItemId() + */ + + @Override + public Object lastItemId() { + if (addedItems.isEmpty()) { + int lastIx = size() - 1; + if (!itemIndexes.containsKey(lastIx)) { + updateOffsetAndCache(size - 1); + } + return itemIndexes.get(lastIx); + } else { + int ix = addedItems.size(); + do { + ix--; + } while (!itemPassesFilters(addedItems.get(ix)) && ix >= 0); + if (ix >= 0) { + return addedItems.get(ix).getId(); + } else { + return null; + } + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object) + */ + + @Override + public boolean isFirstId(Object itemId) { + return firstItemId().equals(itemId); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object) + */ + + @Override + public boolean isLastId(Object itemId) { + return lastItemId().equals(itemId); + } + + /***********************************************/ + /** Methods from interface Container.Sortable **/ + /***********************************************/ + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], + * boolean[]) + */ + + @Override + public void sort(Object[] propertyId, boolean[] ascending) { + sorters.clear(); + if (propertyId == null || propertyId.length == 0) { + refresh(); + return; + } + /* Generate OrderBy -objects */ + boolean asc = true; + for (int i = 0; i < propertyId.length; i++) { + /* Check that the property id is valid */ + if (propertyId[i] instanceof String + && propertyIds.contains(propertyId[i])) { + try { + asc = ascending[i]; + } catch (Exception e) { + getLogger().log(Level.WARNING, "", e); + } + sorters.add(new OrderBy((String) propertyId[i], asc)); + } + } + refresh(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds() + */ + + @Override + public Collection getSortableContainerPropertyIds() { + return getContainerPropertyIds(); + } + + /**************************************/ + /** Methods specific to SQLContainer **/ + /**************************************/ + + /** + * Refreshes the container - clears all caches and resets size and offset. + * Does NOT remove sorting or filtering rules! + */ + public void refresh() { + refresh(true); + } + + /** + * Refreshes the container. If setSizeDirty is + * false, assumes that the current size is up to date. This is + * used in {@link #updateCount()} to refresh the contents when we know the + * size was just updated. + * + * @param setSizeDirty + */ + private void refresh(boolean setSizeDirty) { + if (setSizeDirty) { + sizeDirty = true; + } + currentOffset = 0; + cachedItems.clear(); + itemIndexes.clear(); + fireContentsChange(); + } + + /** + * Returns modify state of the container. + * + * @return true if contents of this container have been modified + */ + public boolean isModified() { + return !removedItems.isEmpty() || !addedItems.isEmpty() + || !modifiedItems.isEmpty(); + } + + /** + * Set auto commit mode enabled or disabled. Auto commit mode means that all + * changes made to items of this container will be immediately written to + * the underlying data source. + * + * @param autoCommitEnabled + * true to enable auto commit mode + */ + public void setAutoCommit(boolean autoCommitEnabled) { + autoCommit = autoCommitEnabled; + } + + /** + * Returns status of the auto commit mode. + * + * @return true if auto commit mode is enabled + */ + public boolean isAutoCommit() { + return autoCommit; + } + + /** + * Returns the currently set page length. + * + * @return current page length + */ + public int getPageLength() { + return pageLength; + } + + /** + * Sets the page length used in lazy fetching of items from the data source. + * Also resets the cache size to match the new page length. + * + * As a side effect the container will be refreshed. + * + * @param pageLength + * new page length + */ + public void setPageLength(int pageLength) { + setPageLengthInternal(pageLength); + refresh(); + } + + /** + * Sets the page length internally, without refreshing the container. + * + * @param pageLength + * the new page length + */ + private void setPageLengthInternal(int pageLength) { + this.pageLength = pageLength > 0 ? pageLength : DEFAULT_PAGE_LENGTH; + cacheOverlap = getPageLength(); + cachedItems.setCacheLimit(CACHE_RATIO * getPageLength() + cacheOverlap); + } + + /** + * Adds the given OrderBy to this container and refreshes the container + * contents with the new sorting rules. + * + * Note that orderBy.getColumn() must return a column name that exists in + * this container. + * + * @param orderBy + * OrderBy to be added to the container sorting rules + */ + public void addOrderBy(OrderBy orderBy) { + if (orderBy == null) { + return; + } + if (!propertyIds.contains(orderBy.getColumn())) { + throw new IllegalArgumentException( + "The column given for sorting does not exist in this container."); + } + sorters.add(orderBy); + refresh(); + } + + /** + * Commits all the changes, additions and removals made to the items of this + * container. + * + * @throws UnsupportedOperationException + * @throws SQLException + */ + public void commit() throws UnsupportedOperationException, SQLException { + try { + getLogger().log(Level.FINER, + "Commiting changes through delegate..."); + queryDelegate.beginTransaction(); + /* Perform buffered deletions */ + for (RowItem item : removedItems.values()) { + try { + if (!queryDelegate.removeRow(item)) { + throw new SQLException( + "Removal failed for row with ID: " + + item.getId()); + } + } catch (IllegalArgumentException e) { + throw new SQLException( + "Removal failed for row with ID: " + item.getId(), + e); + } + } + /* Perform buffered modifications */ + for (RowItem item : modifiedItems) { + if (!removedItems.containsKey(item.getId())) { + if (queryDelegate.storeRow(item) > 0) { + /* + * Also reset the modified state in the item in case it + * is reused e.g. in a form. + */ + item.commit(); + } else { + queryDelegate.rollback(); + refresh(); + throw new ConcurrentModificationException( + "Item with the ID '" + item.getId() + + "' has been externally modified."); + } + } + } + /* Perform buffered additions */ + for (RowItem item : addedItems) { + queryDelegate.storeRow(item); + } + queryDelegate.commit(); + removedItems.clear(); + addedItems.clear(); + modifiedItems.clear(); + refresh(); + if (notificationsEnabled) { + CacheFlushNotifier.notifyOfCacheFlush(this); + } + } catch (SQLException e) { + queryDelegate.rollback(); + throw e; + } catch (OptimisticLockException e) { + queryDelegate.rollback(); + throw e; + } + } + + /** + * Rolls back all the changes, additions and removals made to the items of + * this container. + * + * @throws UnsupportedOperationException + * @throws SQLException + */ + public void rollback() throws UnsupportedOperationException, SQLException { + getLogger().log(Level.FINE, "Rolling back changes..."); + removedItems.clear(); + addedItems.clear(); + modifiedItems.clear(); + refresh(); + } + + /** + * Notifies this container that a property in the given item has been + * modified. The change will be buffered or made instantaneously depending + * on auto commit mode. + * + * @param changedItem + * item that has a modified property + */ + void itemChangeNotification(RowItem changedItem) { + if (autoCommit) { + try { + queryDelegate.beginTransaction(); + if (queryDelegate.storeRow(changedItem) == 0) { + queryDelegate.rollback(); + refresh(); + throw new ConcurrentModificationException( + "Item with the ID '" + changedItem.getId() + + "' has been externally modified."); + } + queryDelegate.commit(); + if (notificationsEnabled) { + CacheFlushNotifier.notifyOfCacheFlush(this); + } + getLogger().log(Level.FINER, "Row updated to DB..."); + } catch (SQLException e) { + getLogger().log(Level.WARNING, + "itemChangeNotification failed, rolling back...", e); + try { + queryDelegate.rollback(); + } catch (SQLException ee) { + /* Nothing can be done here */ + getLogger().log(Level.SEVERE, "Rollback failed", e); + } + throw new RuntimeException(e); + } + } else { + if (!(changedItem.getId() instanceof TemporaryRowId) + && !modifiedItems.contains(changedItem)) { + modifiedItems.add(changedItem); + } + } + } + + /** + * Determines a new offset for updating the row cache. The offset is + * calculated from the given index, and will be fixed to match the start of + * a page, based on the value of pageLength. + * + * @param index + * Index of the item that was requested, but not found in cache + */ + private void updateOffsetAndCache(int index) { + + int oldOffset = currentOffset; + + currentOffset = (index / pageLength) * pageLength - cacheOverlap; + + if (currentOffset < 0) { + currentOffset = 0; + } + + if (oldOffset == currentOffset && !cachedItems.isEmpty()) { + return; + } + + getPage(); + } + + /** + * Fetches new count of rows from the data source, if needed. + */ + private void updateCount() { + if (!sizeDirty && new Date().getTime() < sizeUpdated.getTime() + + sizeValidMilliSeconds) { + return; + } + try { + try { + queryDelegate.setFilters(filters); + } catch (UnsupportedOperationException e) { + getLogger().log(Level.FINE, + "The query delegate doesn't support filtering", e); + } + try { + queryDelegate.setOrderBy(sorters); + } catch (UnsupportedOperationException e) { + getLogger().log(Level.FINE, + "The query delegate doesn't support sorting", e); + } + int newSize = queryDelegate.getCount(); + sizeUpdated = new Date(); + sizeDirty = false; + if (newSize != size) { + size = newSize; + // Size is up to date so don't set it back to dirty in refresh() + refresh(false); + } + getLogger().log(Level.FINER, "Updated row count. New count is: {0}", + size); + } catch (SQLException e) { + throw new RuntimeException("Failed to update item set size.", e); + } + } + + /** + * Fetches property id's (column names and their types) from the data + * source. + * + * @throws SQLException + */ + private void getPropertyIds() throws SQLException { + propertyIds.clear(); + propertyTypes.clear(); + queryDelegate.setFilters(null); + queryDelegate.setOrderBy(null); + ResultSet rs = null; + ResultSetMetaData rsmd = null; + try { + queryDelegate.beginTransaction(); + rs = queryDelegate.getResults(0, 1); + rsmd = rs.getMetaData(); + boolean resultExists = rs.next(); + Class type = null; + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) { + continue; + } + String colName = rsmd.getColumnLabel(i); + /* + * Make sure not to add the same colName twice. This can easily + * happen if the SQL query joins many tables with an ID column. + */ + if (!propertyIds.contains(colName)) { + propertyIds.add(colName); + } + /* Try to determine the column's JDBC class by all means. */ + if (resultExists && rs.getObject(i) != null) { + type = rs.getObject(i).getClass(); + } else { + try { + type = Class.forName(rsmd.getColumnClassName(i)); + } catch (Exception e) { + getLogger().log(Level.WARNING, "Class not found", e); + /* On failure revert to Object and hope for the best. */ + type = Object.class; + } + } + /* + * Determine read only and nullability status of the column. A + * column is read only if it is reported as either read only or + * auto increment by the database, and also it is set as the + * version column in a TableQuery delegate. + */ + boolean readOnly = rsmd.isAutoIncrement(i) + || rsmd.isReadOnly(i); + + boolean persistable = !rsmd.isReadOnly(i); + + if (queryDelegate instanceof TableQuery) { + if (rsmd.getColumnLabel(i).equals( + ((TableQuery) queryDelegate).getVersionColumn())) { + readOnly = true; + } + } + + propertyReadOnly.put(colName, readOnly); + propertyPersistable.put(colName, persistable); + propertyNullable.put(colName, + rsmd.isNullable(i) == ResultSetMetaData.columnNullable); + propertyPrimaryKey.put(colName, + queryDelegate.getPrimaryKeyColumns() + .contains(rsmd.getColumnLabel(i))); + propertyTypes.put(colName, type); + } + rs.getStatement().close(); + rs.close(); + queryDelegate.commit(); + getLogger().log(Level.FINER, "Property IDs fetched."); + } catch (SQLException e) { + getLogger().log(Level.WARNING, + "Failed to fetch property ids, rolling back", e); + try { + queryDelegate.rollback(); + } catch (SQLException e1) { + getLogger().log(Level.SEVERE, "Failed to roll back", e1); + } + try { + if (rs != null) { + if (rs.getStatement() != null) { + rs.getStatement().close(); + } + rs.close(); + } + } catch (SQLException e1) { + getLogger().log(Level.WARNING, "Failed to close session", e1); + } + throw e; + } + } + + /** + * Fetches a page from the data source based on the values of pageLength and + * currentOffset. Also updates the set of primary keys, used in + * identification of RowItems. + */ + private void getPage() { + updateCount(); + ResultSet rs = null; + ResultSetMetaData rsmd = null; + cachedItems.clear(); + itemIndexes.clear(); + try { + try { + queryDelegate.setOrderBy(sorters); + } catch (UnsupportedOperationException e) { + /* The query delegate doesn't support sorting. */ + /* No need to do anything. */ + getLogger().log(Level.FINE, + "The query delegate doesn't support sorting", e); + } + queryDelegate.beginTransaction(); + int fetchedRows = pageLength * CACHE_RATIO + cacheOverlap; + rs = queryDelegate.getResults(currentOffset, fetchedRows); + rsmd = rs.getMetaData(); + List pKeys = queryDelegate.getPrimaryKeyColumns(); + // } + /* Create new items and column properties */ + ColumnProperty cp = null; + int rowCount = currentOffset; + if (!queryDelegate.implementationRespectsPagingLimits()) { + rowCount = currentOffset = 0; + setPageLengthInternal(size); + } + while (rs.next()) { + List itemProperties = new ArrayList(); + /* Generate row itemId based on primary key(s) */ + Object[] itemId = new Object[pKeys.size()]; + for (int i = 0; i < pKeys.size(); i++) { + itemId[i] = rs.getObject(pKeys.get(i)); + } + RowId id = null; + if (pKeys.isEmpty()) { + id = new ReadOnlyRowId(rs.getRow()); + } else { + id = new RowId(itemId); + } + List propertiesToAdd = new ArrayList( + propertyIds); + if (!removedItems.containsKey(id)) { + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + if (!isColumnIdentifierValid(rsmd.getColumnLabel(i))) { + continue; + } + String colName = rsmd.getColumnLabel(i); + Object value = rs.getObject(i); + Class type = value != null ? value.getClass() + : Object.class; + if (value == null) { + for (String propName : propertyTypes.keySet()) { + if (propName.equals(rsmd.getColumnLabel(i))) { + type = propertyTypes.get(propName); + break; + } + } + } + /* + * In case there are more than one column with the same + * name, add only the first one. This can easily happen + * if you join many tables where each table has an ID + * column. + */ + if (propertiesToAdd.contains(colName)) { + + cp = new ColumnProperty(colName, + propertyReadOnly.get(colName), + propertyPersistable.get(colName), + propertyNullable.get(colName), + propertyPrimaryKey.get(colName), value, + type); + itemProperties.add(cp); + propertiesToAdd.remove(colName); + } + } + /* Cache item */ + itemIndexes.put(rowCount, id); + + // if an item with the id is contained in the modified + // cache, then use this record and add it to the cached + // items. Otherwise create a new item + int modifiedIndex = indexInModifiedCache(id); + if (modifiedIndex != -1) { + cachedItems.put(id, modifiedItems.get(modifiedIndex)); + } else { + cachedItems.put(id, + new RowItem(this, id, itemProperties)); + } + + rowCount++; + } + } + rs.getStatement().close(); + rs.close(); + queryDelegate.commit(); + getLogger().log(Level.FINER, "Fetched {0} rows starting from {1}", + new Object[] { fetchedRows, currentOffset }); + } catch (SQLException e) { + getLogger().log(Level.WARNING, "Failed to fetch rows, rolling back", + e); + try { + queryDelegate.rollback(); + } catch (SQLException e1) { + getLogger().log(Level.SEVERE, "Failed to roll back", e1); + } + try { + if (rs != null) { + if (rs.getStatement() != null) { + rs.getStatement().close(); + rs.close(); + } + } + } catch (SQLException e1) { + getLogger().log(Level.WARNING, "Failed to close session", e1); + } + throw new RuntimeException("Failed to fetch page.", e); + } + } + + /** + * Returns the index of the item with the given itemId for the modified + * cache. + * + * @param itemId + * @return the index of the item with the itemId in the modified cache. Or + * -1 if not found. + */ + private int indexInModifiedCache(Object itemId) { + for (int ix = 0; ix < modifiedItems.size(); ix++) { + RowItem item = modifiedItems.get(ix); + if (item.getId().equals(itemId)) { + return ix; + } + } + return -1; + } + + private int sizeOfAddedItems() { + return getFilteredAddedItems().size(); + } + + private List getFilteredAddedItems() { + ArrayList filtered = new ArrayList(addedItems); + if (filters != null && !filters.isEmpty()) { + for (RowItem item : addedItems) { + if (!itemPassesFilters(item)) { + filtered.remove(item); + } + } + } + return filtered; + } + + private boolean itemPassesFilters(RowItem item) { + for (Filter filter : filters) { + if (!filter.passesFilter(item.getId(), item)) { + return false; + } + } + return true; + } + + /** + * Checks is the given column identifier valid to be used with SQLContainer. + * Currently the only non-valid identifier is "rownum" when MSSQL or Oracle + * is used. This is due to the way the SELECT queries are constructed in + * order to implement paging in these databases. + * + * @param identifier + * Column identifier + * @return true if the identifier is valid + */ + private boolean isColumnIdentifierValid(String identifier) { + if (identifier.equalsIgnoreCase("rownum") + && queryDelegate instanceof TableQuery) { + TableQuery tq = (TableQuery) queryDelegate; + if (tq.getSqlGenerator() instanceof MSSQLGenerator + || tq.getSqlGenerator() instanceof OracleGenerator) { + return false; + } + } + return true; + } + + /** + * Returns the QueryDelegate set for this SQLContainer. + * + * @return current querydelegate + */ + protected QueryDelegate getQueryDelegate() { + return queryDelegate; + } + + /************************************/ + /** UNSUPPORTED CONTAINER FEATURES **/ + /************************************/ + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#addContainerProperty(java.lang.Object, + * java.lang.Class, java.lang.Object) + */ + + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeContainerProperty(java.lang.Object) + */ + + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#addItem(java.lang.Object) + */ + + @Override + public Item addItem(Object itemId) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object, + * java.lang.Object) + */ + + @Override + public Item addItemAfter(Object previousItemId, Object newItemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Indexed#addItemAt(int, java.lang.Object) + */ + + @Override + public Item addItemAt(int index, Object newItemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Indexed#addItemAt(int) + */ + + @Override + public Object addItemAt(int index) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object) + */ + + @Override + public Object addItemAfter(Object previousItemId) + throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /******************************************/ + /** ITEMSETCHANGENOTIFIER IMPLEMENTATION **/ + /******************************************/ + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin + * .data.Container.ItemSetChangeListener) + */ + + @Override + public void addItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (itemSetChangeListeners == null) { + itemSetChangeListeners = new LinkedList(); + } + itemSetChangeListeners.add(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemSetChangeListener(com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Override + @Deprecated + public void addListener(Container.ItemSetChangeListener listener) { + addItemSetChangeListener(listener); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin + * .data.Container.ItemSetChangeListener) + */ + + @Override + public void removeItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (itemSetChangeListeners != null) { + itemSetChangeListeners.remove(listener); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeItemSetChangeListener(com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(Container.ItemSetChangeListener listener) { + removeItemSetChangeListener(listener); + } + + protected void fireContentsChange() { + if (itemSetChangeListeners != null) { + final Object[] l = itemSetChangeListeners.toArray(); + final Container.ItemSetChangeEvent event = new SQLContainer.ItemSetChangeEvent( + this); + for (int i = 0; i < l.length; i++) { + ((Container.ItemSetChangeListener) l[i]) + .containerItemSetChange(event); + } + } + } + + /** + * Simple ItemSetChangeEvent implementation. + */ + @SuppressWarnings("serial") + public static class ItemSetChangeEvent extends EventObject + implements Container.ItemSetChangeEvent { + + private ItemSetChangeEvent(SQLContainer source) { + super(source); + } + + @Override + public Container getContainer() { + return (Container) getSource(); + } + } + + /**************************************************/ + /** ROWIDCHANGELISTENER PASSING TO QUERYDELEGATE **/ + /**************************************************/ + + /** + * Adds a RowIdChangeListener to the QueryDelegate + * + * @param listener + */ + public void addRowIdChangeListener(RowIdChangeListener listener) { + if (queryDelegate instanceof QueryDelegate.RowIdChangeNotifier) { + ((QueryDelegate.RowIdChangeNotifier) queryDelegate) + .addListener(listener); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addRowIdChangeListener(RowIdChangeListener)} + **/ + @Deprecated + public void addListener(RowIdChangeListener listener) { + addRowIdChangeListener(listener); + } + + /** + * Removes a RowIdChangeListener from the QueryDelegate + * + * @param listener + */ + public void removeRowIdChangeListener(RowIdChangeListener listener) { + if (queryDelegate instanceof QueryDelegate.RowIdChangeNotifier) { + ((QueryDelegate.RowIdChangeNotifier) queryDelegate) + .removeListener(listener); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeRowIdChangeListener(RowIdChangeListener)} + **/ + @Deprecated + public void removeListener(RowIdChangeListener listener) { + removeRowIdChangeListener(listener); + } + + /** + * Calling this will enable this SQLContainer to send and receive cache + * flush notifications for its lifetime. + */ + public void enableCacheFlushNotifications() { + if (!notificationsEnabled) { + notificationsEnabled = true; + CacheFlushNotifier.addInstance(this); + } + } + + /******************************************/ + /** Referencing mechanism implementation **/ + /******************************************/ + + /** + * Adds a new reference to the given SQLContainer. In addition to the + * container you must provide the column (property) names used for the + * reference in both this and the referenced SQLContainer. + * + * Note that multiple references pointing to the same SQLContainer are not + * supported. + * + * @param refdCont + * Target SQLContainer of the new reference + * @param refingCol + * Column (property) name in this container storing the (foreign + * key) reference + * @param refdCol + * Column (property) name in the referenced container storing the + * referenced key + */ + public void addReference(SQLContainer refdCont, String refingCol, + String refdCol) { + if (refdCont == null) { + throw new IllegalArgumentException( + "Referenced SQLContainer can not be null."); + } + if (!getContainerPropertyIds().contains(refingCol)) { + throw new IllegalArgumentException( + "Given referencing column name is invalid." + + " Please ensure that this container" + + " contains a property ID named: " + refingCol); + } + if (!refdCont.getContainerPropertyIds().contains(refdCol)) { + throw new IllegalArgumentException( + "Given referenced column name is invalid." + + " Please ensure that the referenced container" + + " contains a property ID named: " + refdCol); + } + if (references.keySet().contains(refdCont)) { + throw new IllegalArgumentException( + "An SQLContainer instance can only be referenced once."); + } + references.put(refdCont, new Reference(refdCont, refingCol, refdCol)); + } + + /** + * Removes the reference pointing to the given SQLContainer. + * + * @param refdCont + * Target SQLContainer of the reference + * @return true if successful, false if the reference did not exist + */ + public boolean removeReference(SQLContainer refdCont) { + if (refdCont == null) { + throw new IllegalArgumentException( + "Referenced SQLContainer can not be null."); + } + return references.remove(refdCont) == null ? false : true; + } + + /** + * Sets the referenced item. The referencing column of the item in this + * container is updated accordingly. + * + * @param itemId + * Item Id of the reference source (from this container) + * @param refdItemId + * Item Id of the reference target (from referenced container) + * @param refdCont + * Target SQLContainer of the reference + * @return true if the referenced item was successfully set, false on + * failure + */ + public boolean setReferencedItem(Object itemId, Object refdItemId, + SQLContainer refdCont) { + if (refdCont == null) { + throw new IllegalArgumentException( + "Referenced SQLContainer can not be null."); + } + Reference r = references.get(refdCont); + if (r == null) { + throw new IllegalArgumentException( + "Reference to the given SQLContainer not defined."); + } + try { + getContainerProperty(itemId, r.getReferencingColumn()) + .setValue(refdCont.getContainerProperty(refdItemId, + r.getReferencedColumn())); + return true; + } catch (Exception e) { + getLogger().log(Level.WARNING, "Setting referenced item failed.", + e); + return false; + } + } + + /** + * Fetches the Item Id of the referenced item from the target SQLContainer. + * + * @param itemId + * Item Id of the reference source (from this container) + * @param refdCont + * Target SQLContainer of the reference + * @return Item Id of the referenced item, or null if not found + */ + public Object getReferencedItemId(Object itemId, SQLContainer refdCont) { + if (refdCont == null) { + throw new IllegalArgumentException( + "Referenced SQLContainer can not be null."); + } + Reference r = references.get(refdCont); + if (r == null) { + throw new IllegalArgumentException( + "Reference to the given SQLContainer not defined."); + } + Object refKey = getContainerProperty(itemId, r.getReferencingColumn()) + .getValue(); + + refdCont.removeAllContainerFilters(); + refdCont.addContainerFilter(new Equal(r.getReferencedColumn(), refKey)); + Object toReturn = refdCont.firstItemId(); + refdCont.removeAllContainerFilters(); + return toReturn; + } + + /** + * Fetches the referenced item from the target SQLContainer. + * + * @param itemId + * Item Id of the reference source (from this container) + * @param refdCont + * Target SQLContainer of the reference + * @return The referenced item, or null if not found + */ + public Item getReferencedItem(Object itemId, SQLContainer refdCont) { + return refdCont.getItem(getReferencedItemId(itemId, refdCont)); + } + + private void writeObject(java.io.ObjectOutputStream out) + throws IOException { + out.defaultWriteObject(); + } + + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + if (notificationsEnabled) { + /* + * Register instance with CacheFlushNotifier after de-serialization + * if notifications are enabled + */ + CacheFlushNotifier.addInstance(this); + } + } + + private static final Logger getLogger() { + return Logger.getLogger(SQLContainer.class.getName()); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLUtil.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLUtil.java new file mode 100644 index 0000000000..3939f7ab08 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/SQLUtil.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer; + +import java.io.Serializable; + +public class SQLUtil implements Serializable { + /** + * Escapes different special characters in strings that are passed to SQL. + * Replaces the following: + * + * + *
  • ' is replaced with ''
  • + *
  • \x00 is removed
  • + *
  • \ is replaced with \\
  • + *
  • " is replaced with \"
  • + *
  • \x1a is removed
  • + * + * Also note! The escaping done here may or may not be enough to prevent any + * and all SQL injections so it is recommended to check user input before + * giving it to the SQLContainer/TableQuery. + * + * @param constant + * @return \\\'\' + */ + public static String escapeSQL(String constant) { + if (constant == null) { + return null; + } + String fixedConstant = constant; + fixedConstant = fixedConstant.replaceAll("\\\\x00", ""); + fixedConstant = fixedConstant.replaceAll("\\\\x1a", ""); + fixedConstant = fixedConstant.replaceAll("'", "''"); + fixedConstant = fixedConstant.replaceAll("\\\\", "\\\\\\\\"); + fixedConstant = fixedConstant.replaceAll("\\\"", "\\\\\""); + return fixedConstant; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/TemporaryRowId.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/TemporaryRowId.java new file mode 100644 index 0000000000..78c3339f05 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/TemporaryRowId.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.v7.data.util.sqlcontainer; + +public class TemporaryRowId extends RowId { + private static final long serialVersionUID = -641983830469018329L; + + public TemporaryRowId(Object... id) { + super(id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(TemporaryRowId.class.equals(obj.getClass()))) { + return false; + } + Object[] compId = ((TemporaryRowId) obj).getId(); + return id.equals(compId); + } + + @Override + public String toString() { + return "Temporary row id"; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/J2EEConnectionPool.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/J2EEConnectionPool.java new file mode 100644 index 0000000000..55ab7f041a --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/J2EEConnectionPool.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.v7.data.util.sqlcontainer.connection; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + +public class J2EEConnectionPool implements JDBCConnectionPool { + + private String dataSourceJndiName; + + private DataSource dataSource = null; + + public J2EEConnectionPool(DataSource dataSource) { + this.dataSource = dataSource; + } + + public J2EEConnectionPool(String dataSourceJndiName) { + this.dataSourceJndiName = dataSourceJndiName; + } + + @Override + public Connection reserveConnection() throws SQLException { + Connection conn = getDataSource().getConnection(); + conn.setAutoCommit(false); + + return conn; + } + + private DataSource getDataSource() throws SQLException { + if (dataSource == null) { + dataSource = lookupDataSource(); + } + return dataSource; + } + + private DataSource lookupDataSource() throws SQLException { + try { + InitialContext ic = new InitialContext(); + return (DataSource) ic.lookup(dataSourceJndiName); + } catch (NamingException e) { + throw new SQLException( + "NamingException - Cannot connect to the database. Cause: " + + e.getMessage()); + } + } + + @Override + public void releaseConnection(Connection conn) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + Logger.getLogger(J2EEConnectionPool.class.getName()) + .log(Level.FINE, "Could not release SQL connection", e); + } + } + } + + @Override + public void destroy() { + dataSource = null; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/JDBCConnectionPool.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/JDBCConnectionPool.java new file mode 100644 index 0000000000..fce1d8aa25 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/JDBCConnectionPool.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.connection; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Interface for implementing connection pools to be used with SQLContainer. + */ +public interface JDBCConnectionPool extends Serializable { + /** + * Retrieves a connection. + * + * @return a usable connection to the database + * @throws SQLException + */ + public Connection reserveConnection() throws SQLException; + + /** + * Releases a connection that was retrieved earlier. + * + * Note that depending on implementation, the transaction possibly open in + * the connection may or may not be rolled back. + * + * @param conn + * Connection to be released + */ + public void releaseConnection(Connection conn); + + /** + * Destroys the connection pool: close() is called an all the connections in + * the pool, whether available or reserved. + * + * This method was added to fix PostgreSQL -related issues with connections + * that were left hanging 'idle'. + */ + public void destroy(); +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java new file mode 100644 index 0000000000..fb4444a436 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/connection/SimpleJDBCConnectionPool.java @@ -0,0 +1,181 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.connection; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashSet; +import java.util.Set; + +/** + * Simple implementation of the JDBCConnectionPool interface. Handles loading + * the JDBC driver, setting up the connections and ensuring they are still + * usable upon release. + */ +@SuppressWarnings("serial") +public class SimpleJDBCConnectionPool implements JDBCConnectionPool { + + private int initialConnections = 5; + private int maxConnections = 20; + + private String driverName; + private String connectionUri; + private String userName; + private String password; + + private transient Set availableConnections; + private transient Set reservedConnections; + + private boolean initialized; + + public SimpleJDBCConnectionPool(String driverName, String connectionUri, + String userName, String password) throws SQLException { + if (driverName == null) { + throw new IllegalArgumentException( + "JDBC driver class name must be given."); + } + if (connectionUri == null) { + throw new IllegalArgumentException( + "Database connection URI must be given."); + } + if (userName == null) { + throw new IllegalArgumentException( + "Database username must be given."); + } + if (password == null) { + throw new IllegalArgumentException( + "Database password must be given."); + } + this.driverName = driverName; + this.connectionUri = connectionUri; + this.userName = userName; + this.password = password; + + /* Initialize JDBC driver */ + try { + Class.forName(driverName).newInstance(); + } catch (Exception ex) { + throw new RuntimeException("Specified JDBC Driver: " + driverName + + " - initialization failed.", ex); + } + } + + public SimpleJDBCConnectionPool(String driverName, String connectionUri, + String userName, String password, int initialConnections, + int maxConnections) throws SQLException { + this(driverName, connectionUri, userName, password); + this.initialConnections = initialConnections; + this.maxConnections = maxConnections; + } + + private void initializeConnections() throws SQLException { + availableConnections = new HashSet(initialConnections); + reservedConnections = new HashSet(initialConnections); + for (int i = 0; i < initialConnections; i++) { + availableConnections.add(createConnection()); + } + initialized = true; + } + + @Override + public synchronized Connection reserveConnection() throws SQLException { + if (!initialized) { + initializeConnections(); + } + if (availableConnections.isEmpty()) { + if (reservedConnections.size() < maxConnections) { + availableConnections.add(createConnection()); + } else { + throw new SQLException("Connection limit has been reached."); + } + } + + Connection c = availableConnections.iterator().next(); + availableConnections.remove(c); + reservedConnections.add(c); + + return c; + } + + @Override + public synchronized void releaseConnection(Connection conn) { + if (conn == null || !initialized) { + return; + } + /* Try to roll back if necessary */ + try { + if (!conn.getAutoCommit()) { + conn.rollback(); + } + } catch (SQLException e) { + /* Roll back failed, close and discard connection */ + try { + conn.close(); + } catch (SQLException e1) { + /* Nothing needs to be done */ + } + reservedConnections.remove(conn); + return; + } + reservedConnections.remove(conn); + availableConnections.add(conn); + } + + private Connection createConnection() throws SQLException { + Connection c = DriverManager.getConnection(connectionUri, userName, + password); + c.setAutoCommit(false); + if (driverName.toLowerCase().contains("mysql")) { + try { + Statement s = c.createStatement(); + s.execute("SET SESSION sql_mode = 'ANSI'"); + s.close(); + } catch (Exception e) { + // Failed to set ansi mode; continue + } + } + return c; + } + + @Override + public void destroy() { + for (Connection c : availableConnections) { + try { + c.close(); + } catch (SQLException e) { + // No need to do anything + } + } + for (Connection c : reservedConnections) { + try { + c.close(); + } catch (SQLException e) { + // No need to do anything + } + } + + } + + private void writeObject(java.io.ObjectOutputStream out) + throws IOException { + initialized = false; + out.defaultWriteObject(); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/AbstractTransactionalQuery.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/AbstractTransactionalQuery.java new file mode 100644 index 0000000000..dc582dd120 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/AbstractTransactionalQuery.java @@ -0,0 +1,185 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import com.vaadin.v7.data.util.sqlcontainer.connection.JDBCConnectionPool; + +/** + * Common base class for database query classes that handle connections and + * transactions. + * + * @author Vaadin Ltd + * @since 6.8.9 + */ +public abstract class AbstractTransactionalQuery implements Serializable { + + private JDBCConnectionPool connectionPool; + private transient Connection activeConnection; + + AbstractTransactionalQuery() { + } + + AbstractTransactionalQuery(JDBCConnectionPool connectionPool) { + this.connectionPool = connectionPool; + } + + /** + * Reserves a connection with auto-commit off if no transaction is in + * progress. + * + * @throws IllegalStateException + * if a transaction is already open + * @throws SQLException + * if a connection could not be obtained or configured + */ + public void beginTransaction() + throws UnsupportedOperationException, SQLException { + if (isInTransaction()) { + throw new IllegalStateException("A transaction is already active!"); + } + activeConnection = connectionPool.reserveConnection(); + activeConnection.setAutoCommit(false); + } + + /** + * Commits (if not in auto-commit mode) and releases the active connection. + * + * @throws SQLException + * if not in a transaction managed by this query + */ + public void commit() throws UnsupportedOperationException, SQLException { + if (!isInTransaction()) { + throw new SQLException("No active transaction"); + } + if (!activeConnection.getAutoCommit()) { + activeConnection.commit(); + } + connectionPool.releaseConnection(activeConnection); + activeConnection = null; + } + + /** + * Rolls back and releases the active connection. + * + * @throws SQLException + * if not in a transaction managed by this query + */ + public void rollback() throws UnsupportedOperationException, SQLException { + if (!isInTransaction()) { + throw new SQLException("No active transaction"); + } + activeConnection.rollback(); + connectionPool.releaseConnection(activeConnection); + activeConnection = null; + } + + /** + * Check that a transaction is active. + * + * @throws SQLException + * if no active transaction + */ + protected void ensureTransaction() throws SQLException { + if (!isInTransaction()) { + throw new SQLException("No active transaction!"); + } + } + + /** + * Closes a statement and a resultset, then releases the connection if it is + * not part of an active transaction. A failure in closing one of the + * parameters does not prevent closing the rest. + * + * If the statement is a {@link PreparedStatement}, its parameters are + * cleared prior to closing the statement. + * + * Although JDBC specification does state that closing a statement closes + * its result set and closing a connection closes statements and result + * sets, this method does try to close the result set and statement + * explicitly whenever not null. This can guard against bugs in certain JDBC + * drivers and reduce leaks in case e.g. closing the result set succeeds but + * closing the statement or connection fails. + * + * @param conn + * the connection to release + * @param statement + * the statement to close, may be null to skip closing + * @param rs + * the result set to close, may be null to skip closing + * @throws SQLException + * if closing the result set or the statement fails + */ + protected void releaseConnection(Connection conn, Statement statement, + ResultSet rs) throws SQLException { + try { + try { + if (null != rs) { + rs.close(); + } + } finally { + if (null != statement) { + if (statement instanceof PreparedStatement) { + try { + ((PreparedStatement) statement).clearParameters(); + } catch (Exception e) { + // will be closed below anyway + } + } + statement.close(); + } + } + } finally { + releaseConnection(conn); + } + } + + /** + * Returns the currently active connection, reserves and returns a new + * connection if no active connection. + * + * @return previously active or newly reserved connection + * @throws SQLException + */ + protected Connection getConnection() throws SQLException { + if (activeConnection != null) { + return activeConnection; + } + return connectionPool.reserveConnection(); + } + + protected boolean isInTransaction() { + return activeConnection != null; + } + + /** + * Releases the connection if it is not part of an active transaction. + * + * @param conn + * the connection to release + */ + private void releaseConnection(Connection conn) { + if (conn != activeConnection && conn != null) { + connectionPool.releaseConnection(conn); + } + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQuery.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQuery.java new file mode 100644 index 0000000000..53006ce849 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQuery.java @@ -0,0 +1,495 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.RowItem; +import com.vaadin.v7.data.util.sqlcontainer.SQLContainer; +import com.vaadin.v7.data.util.sqlcontainer.connection.JDBCConnectionPool; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.QueryBuilder; + +@SuppressWarnings("serial") +public class FreeformQuery extends AbstractTransactionalQuery + implements QueryDelegate { + + FreeformQueryDelegate delegate = null; + private String queryString; + private List primaryKeyColumns; + + /** + * Prevent no-parameters instantiation of FreeformQuery + */ + @SuppressWarnings("unused") + private FreeformQuery() { + } + + /** + * Creates a new freeform query delegate to be used with the + * {@link SQLContainer}. + * + * @param queryString + * The actual query to perform. + * @param primaryKeyColumns + * The primary key columns. Read-only mode is forced if this + * parameter is null or empty. + * @param connectionPool + * the JDBCConnectionPool to use to open connections to the SQL + * database. + * @deprecated As of 6.7, @see + * {@link FreeformQuery#FreeformQuery(String, JDBCConnectionPool, String...)} + */ + @Deprecated + public FreeformQuery(String queryString, List primaryKeyColumns, + JDBCConnectionPool connectionPool) { + super(connectionPool); + if (primaryKeyColumns == null) { + primaryKeyColumns = new ArrayList(); + } + if (primaryKeyColumns.contains("")) { + throw new IllegalArgumentException( + "The primary key columns contain an empty string!"); + } else if (queryString == null || "".equals(queryString)) { + throw new IllegalArgumentException( + "The query string may not be empty or null!"); + } else if (connectionPool == null) { + throw new IllegalArgumentException( + "The connectionPool may not be null!"); + } + this.queryString = queryString; + this.primaryKeyColumns = Collections + .unmodifiableList(primaryKeyColumns); + } + + /** + * Creates a new freeform query delegate to be used with the + * {@link SQLContainer}. + * + * @param queryString + * The actual query to perform. + * @param connectionPool + * the JDBCConnectionPool to use to open connections to the SQL + * database. + * @param primaryKeyColumns + * The primary key columns. Read-only mode is forced if none are + * provided. (optional) + */ + public FreeformQuery(String queryString, JDBCConnectionPool connectionPool, + String... primaryKeyColumns) { + this(queryString, Arrays.asList(primaryKeyColumns), connectionPool); + } + + /** + * This implementation of getCount() actually fetches all records from the + * database, which might be a performance issue. Override this method with a + * SELECT COUNT(*) ... query if this is too slow for your needs. + * + * {@inheritDoc} + */ + @Override + public int getCount() throws SQLException { + // First try the delegate + int count = countByDelegate(); + if (count < 0) { + // Couldn't use the delegate, use the bad way. + Statement statement = null; + ResultSet rs = null; + Connection conn = getConnection(); + try { + statement = conn.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, + ResultSet.CONCUR_READ_ONLY); + + rs = statement.executeQuery(queryString); + if (rs.last()) { + count = rs.getRow(); + } else { + count = 0; + } + } finally { + releaseConnection(conn, statement, rs); + } + } + return count; + } + + @SuppressWarnings("deprecation") + private int countByDelegate() throws SQLException { + int count = -1; + if (delegate == null) { + return count; + } + /* First try using prepared statement */ + if (delegate instanceof FreeformStatementDelegate) { + try { + StatementHelper sh = ((FreeformStatementDelegate) delegate) + .getCountStatement(); + PreparedStatement pstmt = null; + ResultSet rs = null; + Connection c = getConnection(); + try { + pstmt = c.prepareStatement(sh.getQueryString()); + sh.setParameterValuesToStatement(pstmt); + rs = pstmt.executeQuery(); + if (rs.next()) { + count = rs.getInt(1); + } else { + // The result can be empty when using group by and there + // are no matches (#18043) + count = 0; + } + } finally { + releaseConnection(c, pstmt, rs); + } + return count; + } catch (UnsupportedOperationException e) { + // Count statement generation not supported + } + } + /* Try using regular statement */ + try { + String countQuery = delegate.getCountQuery(); + if (countQuery != null) { + Statement statement = null; + ResultSet rs = null; + Connection conn = getConnection(); + try { + statement = conn.createStatement(); + rs = statement.executeQuery(countQuery); + if (rs.next()) { + count = rs.getInt(1); + } else { + // The result can be empty when using group by and there + // are no matches (#18043) + count = 0; + } + return count; + } finally { + releaseConnection(conn, statement, rs); + } + } + } catch (UnsupportedOperationException e) { + // Count query generation not supported + } + return count; + } + + /** + * Fetches the results for the query. This implementation always fetches the + * entire record set, ignoring the offset and page length parameters. In + * order to support lazy loading of records, you must supply a + * FreeformQueryDelegate that implements the + * FreeformQueryDelegate.getQueryString(int,int) method. + * + * @throws SQLException + * + * @see FreeformQueryDelegate#getQueryString(int, int) + */ + @Override + @SuppressWarnings({ "deprecation", "finally" }) + public ResultSet getResults(int offset, int pagelength) + throws SQLException { + ensureTransaction(); + String query = queryString; + if (delegate != null) { + /* First try using prepared statement */ + if (delegate instanceof FreeformStatementDelegate) { + try { + StatementHelper sh = ((FreeformStatementDelegate) delegate) + .getQueryStatement(offset, pagelength); + PreparedStatement pstmt = getConnection() + .prepareStatement(sh.getQueryString()); + sh.setParameterValuesToStatement(pstmt); + return pstmt.executeQuery(); + } catch (UnsupportedOperationException e) { + // Statement generation not supported, continue... + } + } + try { + query = delegate.getQueryString(offset, pagelength); + } catch (UnsupportedOperationException e) { + // This is fine, we'll just use the default queryString. + } + } + Statement statement = getConnection().createStatement(); + ResultSet rs; + try { + rs = statement.executeQuery(query); + } catch (SQLException e) { + try { + statement.close(); + } finally { + // throw the original exception even if closing the statement + // fails + throw e; + } + } + return rs; + } + + @Override + @SuppressWarnings("deprecation") + public boolean implementationRespectsPagingLimits() { + if (delegate == null) { + return false; + } + /* First try using prepared statement */ + if (delegate instanceof FreeformStatementDelegate) { + try { + StatementHelper sh = ((FreeformStatementDelegate) delegate) + .getCountStatement(); + if (sh != null && sh.getQueryString() != null + && sh.getQueryString().length() > 0) { + return true; + } + } catch (UnsupportedOperationException e) { + // Statement generation not supported, continue... + } + } + try { + String queryString = delegate.getQueryString(0, 50); + return queryString != null && queryString.length() > 0; + } catch (UnsupportedOperationException e) { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setFilters(java + * .util.List) + */ + @Override + public void setFilters(List filters) + throws UnsupportedOperationException { + if (delegate != null) { + delegate.setFilters(filters); + } else if (filters != null) { + throw new UnsupportedOperationException( + "FreeFormQueryDelegate not set!"); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#setOrderBy(java + * .util.List) + */ + @Override + public void setOrderBy(List orderBys) + throws UnsupportedOperationException { + if (delegate != null) { + delegate.setOrderBy(orderBys); + } else if (orderBys != null) { + throw new UnsupportedOperationException( + "FreeFormQueryDelegate not set!"); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.util.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin + * .data.util.sqlcontainer.RowItem) + */ + @Override + public int storeRow(RowItem row) throws SQLException { + if (!isInTransaction()) { + throw new IllegalStateException("No transaction is active!"); + } else if (primaryKeyColumns.isEmpty()) { + throw new UnsupportedOperationException( + "Cannot store items fetched with a read-only freeform query!"); + } + if (delegate != null) { + return delegate.storeRow(getConnection(), row); + } else { + throw new UnsupportedOperationException( + "FreeFormQueryDelegate not set!"); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate#removeRow(com. + * vaadin .data.util.sqlcontainer.RowItem) + */ + @Override + public boolean removeRow(RowItem row) throws SQLException { + if (!isInTransaction()) { + throw new IllegalStateException("No transaction is active!"); + } else if (primaryKeyColumns.isEmpty()) { + throw new UnsupportedOperationException( + "Cannot remove items fetched with a read-only freeform query!"); + } + if (delegate != null) { + return delegate.removeRow(getConnection(), row); + } else { + throw new UnsupportedOperationException( + "FreeFormQueryDelegate not set!"); + } + } + + @Override + public synchronized void beginTransaction() + throws UnsupportedOperationException, SQLException { + super.beginTransaction(); + } + + @Override + public synchronized void commit() + throws UnsupportedOperationException, SQLException { + super.commit(); + } + + @Override + public synchronized void rollback() + throws UnsupportedOperationException, SQLException { + super.rollback(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.sqlcontainer.query.QueryDelegate# + * getPrimaryKeyColumns () + */ + @Override + public List getPrimaryKeyColumns() { + return primaryKeyColumns; + } + + public String getQueryString() { + return queryString; + } + + public FreeformQueryDelegate getDelegate() { + return delegate; + } + + public void setDelegate(FreeformQueryDelegate delegate) { + this.delegate = delegate; + } + + /** + * This implementation of the containsRowWithKey method rewrites existing + * WHERE clauses in the query string. The logic is, however, not very + * complex and some times can do the Wrong ThingTM. For the + * situations where this logic is not enough, you can implement the + * getContainsRowQueryString method in FreeformQueryDelegate and this will + * be used instead of the logic. + * + * @see FreeformQueryDelegate#getContainsRowQueryString(Object...) + * + */ + @Override + @SuppressWarnings("deprecation") + public boolean containsRowWithKey(Object... keys) throws SQLException { + String query = null; + boolean contains = false; + if (delegate != null) { + if (delegate instanceof FreeformStatementDelegate) { + try { + StatementHelper sh = ((FreeformStatementDelegate) delegate) + .getContainsRowQueryStatement(keys); + + PreparedStatement pstmt = null; + ResultSet rs = null; + Connection c = getConnection(); + try { + pstmt = c.prepareStatement(sh.getQueryString()); + sh.setParameterValuesToStatement(pstmt); + rs = pstmt.executeQuery(); + contains = rs.next(); + return contains; + } finally { + releaseConnection(c, pstmt, rs); + } + } catch (UnsupportedOperationException e) { + // Statement generation not supported, continue... + } + } + try { + query = delegate.getContainsRowQueryString(keys); + } catch (UnsupportedOperationException e) { + query = modifyWhereClause(keys); + } + } else { + query = modifyWhereClause(keys); + } + Statement statement = null; + ResultSet rs = null; + Connection conn = getConnection(); + try { + statement = conn.createStatement(); + rs = statement.executeQuery(query); + contains = rs.next(); + } finally { + releaseConnection(conn, statement, rs); + } + return contains; + } + + private String modifyWhereClause(Object... keys) { + // Build the where rules for the provided keys + StringBuffer where = new StringBuffer(); + for (int ix = 0; ix < primaryKeyColumns.size(); ix++) { + where.append(QueryBuilder.quote(primaryKeyColumns.get(ix))); + if (keys[ix] == null) { + where.append(" IS NULL"); + } else { + where.append(" = '").append(keys[ix]).append("'"); + } + if (ix < primaryKeyColumns.size() - 1) { + where.append(" AND "); + } + } + // Is there already a WHERE clause in the query string? + int index = queryString.toLowerCase().indexOf("where "); + if (index > -1) { + // Rewrite the where clause + return queryString.substring(0, index) + "WHERE " + where + " AND " + + queryString.substring(index + 6); + } + // Append a where clause + return queryString + " WHERE " + where; + } + + private void writeObject(java.io.ObjectOutputStream out) + throws IOException { + try { + rollback(); + } catch (SQLException ignored) { + } + out.defaultWriteObject(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQueryDelegate.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQueryDelegate.java new file mode 100644 index 0000000000..7af783256f --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformQueryDelegate.java @@ -0,0 +1,130 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.RowItem; + +public interface FreeformQueryDelegate extends Serializable { + /** + * Should return the SQL query string to be performed. This method is + * responsible for gluing together the select query from the filters and the + * order by conditions if these are supported. + * + * @param offset + * the first record (row) to fetch. + * @param pagelength + * the number of records (rows) to fetch. 0 means all records + * starting from offset. + * @deprecated As of 6.7. Implement {@link FreeformStatementDelegate} + * instead of {@link FreeformQueryDelegate} + */ + @Deprecated + public String getQueryString(int offset, int limit) + throws UnsupportedOperationException; + + /** + * Generates and executes a query to determine the current row count from + * the DB. Row count will be fetched using filters that are currently set to + * the QueryDelegate. + * + * @return row count + * @throws SQLException + * @deprecated As of 6.7. Implement {@link FreeformStatementDelegate} + * instead of {@link FreeformQueryDelegate} + */ + @Deprecated + public String getCountQuery() throws UnsupportedOperationException; + + /** + * Sets the filters to apply when performing the SQL query. These are + * translated into a WHERE clause. Default filtering mode will be used. + * + * @param filters + * The filters to apply. + * @throws UnsupportedOperationException + * if the implementation doesn't support filtering. + */ + public void setFilters(List filters) + throws UnsupportedOperationException; + + /** + * Sets the order in which to retrieve rows from the database. The result + * can be ordered by zero or more columns and each column can be in + * ascending or descending order. These are translated into an ORDER BY + * clause in the SQL query. + * + * @param orderBys + * A list of the OrderBy conditions. + * @throws UnsupportedOperationException + * if the implementation doesn't support ordering. + */ + public void setOrderBy(List orderBys) + throws UnsupportedOperationException; + + /** + * Stores a row in the database. The implementation of this interface + * decides how to identify whether to store a new row or update an existing + * one. + * + * @param conn + * the JDBC connection to use + * @param row + * RowItem to be stored or updated. + * @throws UnsupportedOperationException + * if the implementation is read only. + * @throws SQLException + */ + public int storeRow(Connection conn, RowItem row) + throws UnsupportedOperationException, SQLException; + + /** + * Removes the given RowItem from the database. + * + * @param conn + * the JDBC connection to use + * @param row + * RowItem to be removed + * @return true on success + * @throws UnsupportedOperationException + * @throws SQLException + */ + public boolean removeRow(Connection conn, RowItem row) + throws UnsupportedOperationException, SQLException; + + /** + * Generates an SQL Query string that allows the user of the FreeformQuery + * class to customize the query string used by the + * FreeformQuery.containsRowWithKeys() method. This is useful for cases when + * the logic in the containsRowWithKeys method is not enough to support more + * complex free form queries. + * + * @param keys + * the values of the primary keys + * @throws UnsupportedOperationException + * to use the default logic in FreeformQuery + * @deprecated As of 6.7. Implement {@link FreeformStatementDelegate} + * instead of {@link FreeformQueryDelegate} + */ + @Deprecated + public String getContainsRowQueryString(Object... keys) + throws UnsupportedOperationException; +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformStatementDelegate.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformStatementDelegate.java new file mode 100644 index 0000000000..d658ea7188 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/FreeformStatementDelegate.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.v7.data.util.sqlcontainer.query; + +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +/** + * FreeformStatementDelegate is an extension to FreeformQueryDelegate that + * provides definitions for methods that produce StatementHelper objects instead + * of basic query strings. This allows the FreeformQuery query delegate to use + * PreparedStatements instead of regular Statement when accessing the database. + * + * Due to the injection protection and other benefits of prepared statements, it + * is advisable to implement this interface instead of the FreeformQueryDelegate + * whenever possible. + */ +public interface FreeformStatementDelegate extends FreeformQueryDelegate { + /** + * Should return a new instance of StatementHelper that contains the query + * string and parameter values required to create a PreparedStatement. This + * method is responsible for gluing together the select query from the + * filters and the order by conditions if these are supported. + * + * @param offset + * the first record (row) to fetch. + * @param pagelength + * the number of records (rows) to fetch. 0 means all records + * starting from offset. + */ + public StatementHelper getQueryStatement(int offset, int limit) + throws UnsupportedOperationException; + + /** + * Should return a new instance of StatementHelper that contains the query + * string and parameter values required to create a PreparedStatement that + * will fetch the row count from the DB. Row count should be fetched using + * filters that are currently set to the QueryDelegate. + */ + public StatementHelper getCountStatement() + throws UnsupportedOperationException; + + /** + * Should return a new instance of StatementHelper that contains the query + * string and parameter values required to create a PreparedStatement used + * by the FreeformQuery.containsRowWithKeys() method. This is useful for + * cases when the default logic in said method is not enough to support more + * complex free form queries. + * + * @param keys + * the values of the primary keys + * @throws UnsupportedOperationException + * to use the default logic in FreeformQuery + */ + public StatementHelper getContainsRowQueryStatement(Object... keys) + throws UnsupportedOperationException; +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/OrderBy.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/OrderBy.java new file mode 100644 index 0000000000..c6315f12dc --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/OrderBy.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.v7.data.util.sqlcontainer.query; + +import java.io.Serializable; + +/** + * OrderBy represents a sorting rule to be applied to a query made by the + * SQLContainer's QueryDelegate. + * + * The sorting rule is simple and contains only the affected column's name and + * the direction of the sort. + */ +public class OrderBy implements Serializable { + private String column; + private boolean isAscending; + + /** + * Prevent instantiation without required parameters. + */ + @SuppressWarnings("unused") + private OrderBy() { + } + + public OrderBy(String column, boolean isAscending) { + setColumn(column); + setAscending(isAscending); + } + + public void setColumn(String column) { + this.column = column; + } + + public String getColumn() { + return column; + } + + public void setAscending(boolean isAscending) { + this.isAscending = isAscending; + } + + public boolean isAscending() { + return isAscending; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/QueryDelegate.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/QueryDelegate.java new file mode 100644 index 0000000000..cc4c9f5668 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/QueryDelegate.java @@ -0,0 +1,239 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query; + +import java.io.Serializable; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.RowId; +import com.vaadin.v7.data.util.sqlcontainer.RowItem; + +public interface QueryDelegate extends Serializable { + /** + * Generates and executes a query to determine the current row count from + * the DB. Row count will be fetched using filters that are currently set to + * the QueryDelegate. + * + * @return row count + * @throws SQLException + */ + public int getCount() throws SQLException; + + /** + * Executes a paged SQL query and returns the ResultSet. The query is + * defined through implementations of this QueryDelegate interface. + * + * @param offset + * the first item of the page to load + * @param pagelength + * the length of the page to load + * @return a ResultSet containing the rows of the page + * @throws SQLException + * if the database access fails. + */ + public ResultSet getResults(int offset, int pagelength) throws SQLException; + + /** + * Allows the SQLContainer implementation to check whether the QueryDelegate + * implementation implements paging in the getResults method. + * + * @see QueryDelegate#getResults(int, int) + * + * @return true if the delegate implements paging + */ + public boolean implementationRespectsPagingLimits(); + + /** + * Sets the filters to apply when performing the SQL query. These are + * translated into a WHERE clause. Default filtering mode will be used. + * + * @param filters + * The filters to apply. + * @throws UnsupportedOperationException + * if the implementation doesn't support filtering. + */ + public void setFilters(List filters) + throws UnsupportedOperationException; + + /** + * Sets the order in which to retrieve rows from the database. The result + * can be ordered by zero or more columns and each column can be in + * ascending or descending order. These are translated into an ORDER BY + * clause in the SQL query. + * + * @param orderBys + * A list of the OrderBy conditions. + * @throws UnsupportedOperationException + * if the implementation doesn't support ordering. + */ + public void setOrderBy(List orderBys) + throws UnsupportedOperationException; + + /** + * Stores a row in the database. The implementation of this interface + * decides how to identify whether to store a new row or update an existing + * one. + * + * @param columnToValueMap + * A map containing the values for all columns to be stored or + * updated. + * @return the number of affected rows in the database table + * @throws UnsupportedOperationException + * if the implementation is read only. + */ + public int storeRow(RowItem row) + throws UnsupportedOperationException, SQLException; + + /** + * Removes the given RowItem from the database. + * + * @param row + * RowItem to be removed + * @return true on success + * @throws UnsupportedOperationException + * @throws SQLException + */ + public boolean removeRow(RowItem row) + throws UnsupportedOperationException, SQLException; + + /** + * Starts a new database transaction. Used when storing multiple changes. + * + * Note that if a transaction is already open, it will be rolled back when a + * new transaction is started. + * + * @throws SQLException + * if the database access fails. + */ + public void beginTransaction() throws SQLException; + + /** + * Commits a transaction. If a transaction is not open nothing should + * happen. + * + * @throws SQLException + * if the database access fails. + */ + public void commit() throws SQLException; + + /** + * Rolls a transaction back. If a transaction is not open nothing should + * happen. + * + * @throws SQLException + * if the database access fails. + */ + public void rollback() throws SQLException; + + /** + * Returns a list of primary key column names. The list is either fetched + * from the database (TableQuery) or given as an argument depending on + * implementation. + * + * @return + */ + public List getPrimaryKeyColumns(); + + /** + * Performs a query to find out whether the SQL table contains a row with + * the given set of primary keys. + * + * @param keys + * the primary keys + * @return true if the SQL table contains a row with the provided keys + * @throws SQLException + */ + public boolean containsRowWithKey(Object... keys) throws SQLException; + + /************************/ + /** ROWID CHANGE EVENT **/ + /************************/ + + /** + * An Event object specifying the old and new RowId of an added + * item after the addition has been successfully committed. + */ + public interface RowIdChangeEvent extends Serializable { + /** + * Gets the old (temporary) RowId of the added row that raised this + * event. + * + * @return old RowId + */ + public RowId getOldRowId(); + + /** + * Gets the new, possibly database assigned RowId of the added row that + * raised this event. + * + * @return new RowId + */ + public RowId getNewRowId(); + } + + /** RowId change listener interface. */ + public interface RowIdChangeListener extends Serializable { + /** + * Lets the listener know that a RowId has been changed. + * + * @param event + */ + public void rowIdChange(QueryDelegate.RowIdChangeEvent event); + } + + /** + * The interface for adding and removing RowIdChangeEvent + * listeners. By implementing this interface a class explicitly announces + * that it will generate a RowIdChangeEvent when it performs a + * database commit that may change the RowId. + */ + public interface RowIdChangeNotifier extends Serializable { + /** + * Adds a RowIdChangeListener for the object. + * + * @param listener + * listener to be added + */ + public void addRowIdChangeListener( + QueryDelegate.RowIdChangeListener listener); + + /** + * @deprecated As of 7.0, replaced by + * {@link #addRowIdChangeListener(RowIdChangeListener)} + **/ + @Deprecated + public void addListener(QueryDelegate.RowIdChangeListener listener); + + /** + * Removes the specified RowIdChangeListener from the object. + * + * @param listener + * listener to be removed + */ + public void removeRowIdChangeListener( + QueryDelegate.RowIdChangeListener listener); + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeRowIdChangeListener(RowIdChangeListener)} + **/ + @Deprecated + public void removeListener(QueryDelegate.RowIdChangeListener listener); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/TableQuery.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/TableQuery.java new file mode 100644 index 0000000000..a8a84ea086 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/TableQuery.java @@ -0,0 +1,869 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EventObject; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.Compare.Equal; +import com.vaadin.v7.data.util.sqlcontainer.ColumnProperty; +import com.vaadin.v7.data.util.sqlcontainer.OptimisticLockException; +import com.vaadin.v7.data.util.sqlcontainer.RowId; +import com.vaadin.v7.data.util.sqlcontainer.RowItem; +import com.vaadin.v7.data.util.sqlcontainer.SQLUtil; +import com.vaadin.v7.data.util.sqlcontainer.TemporaryRowId; +import com.vaadin.v7.data.util.sqlcontainer.connection.JDBCConnectionPool; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.DefaultSQLGenerator; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.MSSQLGenerator; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.SQLGenerator; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +@SuppressWarnings("serial") +public class TableQuery extends AbstractTransactionalQuery + implements QueryDelegate, QueryDelegate.RowIdChangeNotifier { + + /** + * Table name (without catalog or schema information). + */ + private String tableName; + private String catalogName; + private String schemaName; + /** + * Cached concatenated version of the table name. + */ + private String fullTableName; + /** + * Primary key column name(s) in the table. + */ + private List primaryKeyColumns; + /** + * Version column name in the table. + */ + private String versionColumn; + + /** Currently set Filters and OrderBys */ + private List filters; + private List orderBys; + + /** SQLGenerator instance to use for generating queries */ + private SQLGenerator sqlGenerator; + + /** Row ID change listeners */ + private LinkedList rowIdChangeListeners; + /** Row ID change events, stored until commit() is called */ + private final List bufferedEvents = new ArrayList(); + + /** Set to true to output generated SQL Queries to System.out */ + private final boolean debug = false; + + /** + * Creates a new TableQuery using the given connection pool, SQL generator + * and table name to fetch the data from. All parameters must be non-null. + * + * The table name must be a simple name with no catalog or schema + * information. If those are needed, use + * {@link #TableQuery(String, String, String, JDBCConnectionPool, SQLGenerator)} + * . + * + * @param tableName + * Name of the database table to connect to + * @param connectionPool + * Connection pool for accessing the database + * @param sqlGenerator + * SQL query generator implementation + */ + public TableQuery(String tableName, JDBCConnectionPool connectionPool, + SQLGenerator sqlGenerator) { + this(null, null, tableName, connectionPool, sqlGenerator); + } + + /** + * Creates a new TableQuery using the given connection pool, SQL generator + * and table name to fetch the data from. Catalog and schema names can be + * null, all other parameters must be non-null. + * + * @param catalogName + * Name of the database catalog (can be null) + * @param schemaName + * Name of the database schema (can be null) + * @param tableName + * Name of the database table to connect to + * @param connectionPool + * Connection pool for accessing the database + * @param sqlGenerator + * SQL query generator implementation + * @since 7.1 + */ + public TableQuery(String catalogName, String schemaName, String tableName, + JDBCConnectionPool connectionPool, SQLGenerator sqlGenerator) { + this(catalogName, schemaName, tableName, connectionPool, sqlGenerator, + true); + } + + /** + * Creates a new TableQuery using the given connection pool and table name + * to fetch the data from. All parameters must be non-null. The default SQL + * generator will be used for queries. + * + * The table name must be a simple name with no catalog or schema + * information. If those are needed, use + * {@link #TableQuery(String, String, String, JDBCConnectionPool, SQLGenerator)} + * . + * + * @param tableName + * Name of the database table to connect to + * @param connectionPool + * Connection pool for accessing the database + */ + public TableQuery(String tableName, JDBCConnectionPool connectionPool) { + this(tableName, connectionPool, new DefaultSQLGenerator()); + } + + /** + * Creates a new TableQuery using the given connection pool, SQL generator + * and table name to fetch the data from. Catalog and schema names can be + * null, all other parameters must be non-null. + * + * @param catalogName + * Name of the database catalog (can be null) + * @param schemaName + * Name of the database schema (can be null) + * @param tableName + * Name of the database table to connect to + * @param connectionPool + * Connection pool for accessing the database + * @param sqlGenerator + * SQL query generator implementation + * @param escapeNames + * true to escape special characters in catalog, schema and table + * names, false to use the names as-is + * @since 7.1 + */ + protected TableQuery(String catalogName, String schemaName, + String tableName, JDBCConnectionPool connectionPool, + SQLGenerator sqlGenerator, boolean escapeNames) { + super(connectionPool); + if (tableName == null || tableName.trim().length() < 1 + || connectionPool == null || sqlGenerator == null) { + throw new IllegalArgumentException( + "Table name, connection pool and SQL generator parameters must be non-null and non-empty."); + } + if (escapeNames) { + this.catalogName = SQLUtil.escapeSQL(catalogName); + this.schemaName = SQLUtil.escapeSQL(schemaName); + this.tableName = SQLUtil.escapeSQL(tableName); + } else { + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + } + this.sqlGenerator = sqlGenerator; + fetchMetaData(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getCount() + */ + @Override + public int getCount() throws SQLException { + getLogger().log(Level.FINE, "Fetching count..."); + StatementHelper sh = sqlGenerator.generateSelectQuery( + getFullTableName(), filters, null, 0, 0, "COUNT(*)"); + boolean shouldCloseTransaction = false; + if (!isInTransaction()) { + shouldCloseTransaction = true; + beginTransaction(); + } + ResultSet r = null; + int count = -1; + try { + r = executeQuery(sh); + r.next(); + count = r.getInt(1); + } finally { + try { + if (r != null) { + // Do not release connection, it is done in commit() + releaseConnection(null, r.getStatement(), r); + } + } finally { + if (shouldCloseTransaction) { + commit(); + } + } + } + return count; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#getResults(int, + * int) + */ + @Override + public ResultSet getResults(int offset, int pagelength) + throws SQLException { + StatementHelper sh; + /* + * If no ordering is explicitly set, results will be ordered by the + * first primary key column. + */ + if (orderBys == null || orderBys.isEmpty()) { + List ob = new ArrayList(); + for (int i = 0; i < primaryKeyColumns.size(); i++) { + ob.add(new OrderBy(primaryKeyColumns.get(i), true)); + } + sh = sqlGenerator.generateSelectQuery(getFullTableName(), filters, + ob, offset, pagelength, null); + } else { + sh = sqlGenerator.generateSelectQuery(getFullTableName(), filters, + orderBys, offset, pagelength, null); + } + return executeQuery(sh); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate# + * implementationRespectsPagingLimits() + */ + @Override + public boolean implementationRespectsPagingLimits() { + return true; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.sqlcontainer.query.QueryDelegate#storeRow(com.vaadin + * .addon.sqlcontainer.RowItem) + */ + @Override + public int storeRow(RowItem row) + throws UnsupportedOperationException, SQLException { + if (row == null) { + throw new IllegalArgumentException( + "Row argument must be non-null."); + } + StatementHelper sh; + int result = 0; + if (row.getId() instanceof TemporaryRowId) { + setVersionColumnFlagInProperty(row); + sh = sqlGenerator.generateInsertQuery(getFullTableName(), row); + result = executeUpdateReturnKeys(sh, row); + } else { + setVersionColumnFlagInProperty(row); + sh = sqlGenerator.generateUpdateQuery(getFullTableName(), row); + result = executeUpdate(sh); + } + if (versionColumn != null && result == 0) { + throw new OptimisticLockException( + "Someone else changed the row that was being updated.", + row.getId()); + } + return result; + } + + private void setVersionColumnFlagInProperty(RowItem row) { + ColumnProperty versionProperty = (ColumnProperty) row + .getItemProperty(versionColumn); + if (versionProperty != null) { + versionProperty.setVersionColumn(true); + } + } + + /** + * Inserts the given row in the database table immediately. Begins and + * commits the transaction needed. This method was added specifically to + * solve the problem of returning the final RowId immediately on the + * SQLContainer.addItem() call when auto commit mode is enabled in the + * SQLContainer. + * + * @param row + * RowItem to add to the database + * @return Final RowId of the added row + * @throws SQLException + */ + public RowId storeRowImmediately(RowItem row) throws SQLException { + beginTransaction(); + /* Set version column, if one is provided */ + setVersionColumnFlagInProperty(row); + /* Generate query */ + StatementHelper sh = sqlGenerator + .generateInsertQuery(getFullTableName(), row); + Connection connection = null; + PreparedStatement pstmt = null; + ResultSet generatedKeys = null; + connection = getConnection(); + try { + pstmt = connection.prepareStatement(sh.getQueryString(), + primaryKeyColumns.toArray(new String[0])); + sh.setParameterValuesToStatement(pstmt); + getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); + int result = pstmt.executeUpdate(); + RowId newId = null; + if (result > 0) { + /* + * If affected rows exist, we'll get the new RowId, commit the + * transaction and return the new RowId. + */ + generatedKeys = pstmt.getGeneratedKeys(); + newId = getNewRowId(row, generatedKeys); + } + // transaction has to be closed in any case + commit(); + return newId; + } finally { + releaseConnection(connection, pstmt, generatedKeys); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setFilters(java.util + * .List) + */ + @Override + public void setFilters(List filters) + throws UnsupportedOperationException { + if (filters == null) { + this.filters = null; + return; + } + this.filters = Collections.unmodifiableList(filters); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.sqlcontainer.query.QueryDelegate#setOrderBy(java.util + * .List) + */ + @Override + public void setOrderBy(List orderBys) + throws UnsupportedOperationException { + if (orderBys == null) { + this.orderBys = null; + return; + } + this.orderBys = Collections.unmodifiableList(orderBys); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#beginTransaction() + */ + @Override + public void beginTransaction() + throws UnsupportedOperationException, SQLException { + getLogger().log(Level.FINE, "DB -> begin transaction"); + super.beginTransaction(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#commit() + */ + @Override + public void commit() throws UnsupportedOperationException, SQLException { + getLogger().log(Level.FINE, "DB -> commit"); + super.commit(); + + /* Handle firing row ID change events */ + RowIdChangeEvent[] unFiredEvents = bufferedEvents + .toArray(new RowIdChangeEvent[] {}); + bufferedEvents.clear(); + if (rowIdChangeListeners != null && !rowIdChangeListeners.isEmpty()) { + for (RowIdChangeListener r : rowIdChangeListeners) { + for (RowIdChangeEvent e : unFiredEvents) { + r.rowIdChange(e); + } + } + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.QueryDelegate#rollback() + */ + @Override + public void rollback() throws UnsupportedOperationException, SQLException { + getLogger().log(Level.FINE, "DB -> rollback"); + super.rollback(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.sqlcontainer.query.QueryDelegate#getPrimaryKeyColumns() + */ + @Override + public List getPrimaryKeyColumns() { + return Collections.unmodifiableList(primaryKeyColumns); + } + + public String getVersionColumn() { + return versionColumn; + } + + public void setVersionColumn(String column) { + versionColumn = column; + } + + /** + * Returns the table name for the query without catalog and schema + * information. + * + * @return table name, not null + */ + public String getTableName() { + return tableName; + } + + /** + * Returns the catalog name for the query. + * + * @return catalog name, can be null + * @since 7.1 + */ + public String getCatalogName() { + return catalogName; + } + + /** + * Returns the catalog name for the query. + * + * @return catalog name, can be null + * @since 7.1 + */ + public String getSchemaName() { + return schemaName; + } + + /** + * Returns the complete table name obtained by concatenation of the catalog + * and schema names (if any) and the table name. + * + * This method can be overridden if customization is needed. + * + * @return table name in the form it should be used in query and update + * statements + * @since 7.1 + */ + protected String getFullTableName() { + if (fullTableName == null) { + StringBuilder sb = new StringBuilder(); + if (catalogName != null) { + sb.append(catalogName).append("."); + } + if (schemaName != null) { + sb.append(schemaName).append("."); + } + sb.append(tableName); + fullTableName = sb.toString(); + } + return fullTableName; + } + + public SQLGenerator getSqlGenerator() { + return sqlGenerator; + } + + /** + * Executes the given query string using either the active connection if a + * transaction is already open, or a new connection from this query's + * connection pool. + * + * @param sh + * an instance of StatementHelper, containing the query string + * and parameter values. + * @return ResultSet of the query + * @throws SQLException + */ + private ResultSet executeQuery(StatementHelper sh) throws SQLException { + ensureTransaction(); + Connection connection = getConnection(); + PreparedStatement pstmt = null; + try { + pstmt = connection.prepareStatement(sh.getQueryString()); + sh.setParameterValuesToStatement(pstmt); + getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); + return pstmt.executeQuery(); + } catch (SQLException e) { + releaseConnection(null, pstmt, null); + throw e; + } + } + + /** + * Executes the given update query string using either the active connection + * if a transaction is already open, or a new connection from this query's + * connection pool. + * + * @param sh + * an instance of StatementHelper, containing the query string + * and parameter values. + * @return Number of affected rows + * @throws SQLException + */ + private int executeUpdate(StatementHelper sh) throws SQLException { + PreparedStatement pstmt = null; + Connection connection = null; + try { + connection = getConnection(); + pstmt = connection.prepareStatement(sh.getQueryString()); + sh.setParameterValuesToStatement(pstmt); + getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); + int retval = pstmt.executeUpdate(); + return retval; + } finally { + releaseConnection(connection, pstmt, null); + } + } + + /** + * Executes the given update query string using either the active connection + * if a transaction is already open, or a new connection from this query's + * connection pool. + * + * Additionally adds a new RowIdChangeEvent to the event buffer. + * + * @param sh + * an instance of StatementHelper, containing the query string + * and parameter values. + * @param row + * the row item to update + * @return Number of affected rows + * @throws SQLException + */ + private int executeUpdateReturnKeys(StatementHelper sh, RowItem row) + throws SQLException { + PreparedStatement pstmt = null; + ResultSet genKeys = null; + Connection connection = null; + try { + connection = getConnection(); + pstmt = connection.prepareStatement(sh.getQueryString(), + primaryKeyColumns.toArray(new String[0])); + sh.setParameterValuesToStatement(pstmt); + getLogger().log(Level.FINE, "DB -> {0}", sh.getQueryString()); + int result = pstmt.executeUpdate(); + genKeys = pstmt.getGeneratedKeys(); + RowId newId = getNewRowId(row, genKeys); + bufferedEvents.add(new RowIdChangeEvent(row.getId(), newId)); + return result; + } finally { + releaseConnection(connection, pstmt, genKeys); + } + } + + /** + * Fetches name(s) of primary key column(s) from DB metadata. + * + * Also tries to get the escape string to be used in search strings. + */ + private void fetchMetaData() { + Connection connection = null; + ResultSet rs = null; + ResultSet tables = null; + try { + connection = getConnection(); + DatabaseMetaData dbmd = connection.getMetaData(); + if (dbmd != null) { + tables = dbmd.getTables(catalogName, schemaName, tableName, + null); + if (!tables.next()) { + String catalog = (catalogName != null) + ? catalogName.toUpperCase() : null; + String schema = (schemaName != null) + ? schemaName.toUpperCase() : null; + tables = dbmd.getTables(catalog, schema, + tableName.toUpperCase(), null); + if (!tables.next()) { + throw new IllegalArgumentException( + "Table with the name \"" + getFullTableName() + + "\" was not found. Check your database contents."); + } else { + catalogName = catalog; + schemaName = schema; + tableName = tableName.toUpperCase(); + } + } + tables.close(); + rs = dbmd.getPrimaryKeys(catalogName, schemaName, tableName); + List names = new ArrayList(); + while (rs.next()) { + names.add(rs.getString("COLUMN_NAME")); + } + rs.close(); + if (!names.isEmpty()) { + primaryKeyColumns = names; + } + if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) { + throw new IllegalArgumentException( + "Primary key constraints have not been defined for the table \"" + + getFullTableName() + + "\". Use FreeFormQuery to access this table."); + } + for (String colName : primaryKeyColumns) { + if (colName.equalsIgnoreCase("rownum")) { + if (getSqlGenerator() instanceof MSSQLGenerator + || getSqlGenerator() instanceof MSSQLGenerator) { + throw new IllegalArgumentException( + "When using Oracle or MSSQL, a primary key column" + + " named \'rownum\' is not allowed!"); + } + } + } + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + releaseConnection(connection, null, rs); + } catch (SQLException ignore) { + } finally { + try { + if (tables != null) { + tables.close(); + } + } catch (SQLException ignore) { + } + } + } + } + + private RowId getNewRowId(RowItem row, ResultSet genKeys) { + try { + /* Fetch primary key values and generate a map out of them. */ + Map values = new HashMap(); + ResultSetMetaData rsmd = genKeys.getMetaData(); + int colCount = rsmd.getColumnCount(); + if (genKeys.next()) { + for (int i = 1; i <= colCount; i++) { + values.put(rsmd.getColumnName(i), genKeys.getObject(i)); + } + } + /* Generate new RowId */ + List newRowId = new ArrayList(); + if (values.size() == 1) { + if (primaryKeyColumns.size() == 1) { + newRowId.add(values.get(values.keySet().iterator().next())); + } else { + for (String s : primaryKeyColumns) { + if (!((ColumnProperty) row.getItemProperty(s)) + .isReadOnlyChangeAllowed()) { + newRowId.add(values + .get(values.keySet().iterator().next())); + } else { + newRowId.add(values.get(s)); + } + } + } + } else { + for (String s : primaryKeyColumns) { + newRowId.add(values.get(s)); + } + } + return new RowId(newRowId.toArray()); + } catch (Exception e) { + getLogger().log(Level.FINE, + "Failed to fetch key values on insert: {0}", + e.getMessage()); + return null; + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.sqlcontainer.query.QueryDelegate#removeRow(com.vaadin + * .addon.sqlcontainer.RowItem) + */ + @Override + public boolean removeRow(RowItem row) + throws UnsupportedOperationException, SQLException { + if (getLogger().isLoggable(Level.FINE)) { + getLogger().log(Level.FINE, "Removing row with id: {0}", + row.getId().getId()[0]); + } + if (executeUpdate(sqlGenerator.generateDeleteQuery(getFullTableName(), + primaryKeyColumns, versionColumn, row)) == 1) { + return true; + } + if (versionColumn != null) { + throw new OptimisticLockException( + "Someone else changed the row that was being deleted.", + row.getId()); + } + return false; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.sqlcontainer.query.QueryDelegate#containsRowWithKey( + * java.lang.Object[]) + */ + @Override + public boolean containsRowWithKey(Object... keys) throws SQLException { + ArrayList filtersAndKeys = new ArrayList(); + if (filters != null) { + filtersAndKeys.addAll(filters); + } + int ix = 0; + for (String colName : primaryKeyColumns) { + filtersAndKeys.add(new Equal(colName, keys[ix])); + ix++; + } + StatementHelper sh = sqlGenerator.generateSelectQuery( + getFullTableName(), filtersAndKeys, orderBys, 0, 0, "*"); + + boolean shouldCloseTransaction = false; + if (!isInTransaction()) { + shouldCloseTransaction = true; + beginTransaction(); + } + ResultSet rs = null; + try { + rs = executeQuery(sh); + boolean contains = rs.next(); + return contains; + } finally { + try { + if (rs != null) { + // Do not release connection, it is done in commit() + releaseConnection(null, rs.getStatement(), rs); + } + } finally { + if (shouldCloseTransaction) { + commit(); + } + } + } + } + + /** + * Custom writeObject to call rollback() if object is serialized. + */ + private void writeObject(java.io.ObjectOutputStream out) + throws IOException { + try { + rollback(); + } catch (SQLException ignored) { + } + out.defaultWriteObject(); + } + + /** + * Simple RowIdChangeEvent implementation. + */ + public static class RowIdChangeEvent extends EventObject + implements QueryDelegate.RowIdChangeEvent { + private final RowId oldId; + private final RowId newId; + + private RowIdChangeEvent(RowId oldId, RowId newId) { + super(oldId); + this.oldId = oldId; + this.newId = newId; + } + + @Override + public RowId getNewRowId() { + return newId; + } + + @Override + public RowId getOldRowId() { + return oldId; + } + } + + /** + * Adds RowIdChangeListener to this query + */ + @Override + public void addRowIdChangeListener(RowIdChangeListener listener) { + if (rowIdChangeListeners == null) { + rowIdChangeListeners = new LinkedList(); + } + rowIdChangeListeners.add(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addRowIdChangeListener(com.vaadin.v7.data.util.sqlcontainer.query.QueryDelegate.RowIdChangeListener)} + **/ + @Override + @Deprecated + public void addListener(RowIdChangeListener listener) { + addRowIdChangeListener(listener); + } + + /** + * Removes the given RowIdChangeListener from this query + */ + @Override + public void removeRowIdChangeListener(RowIdChangeListener listener) { + if (rowIdChangeListeners != null) { + rowIdChangeListeners.remove(listener); + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeRowIdChangeListener(com.vaadin.v7.data.util.sqlcontainer.query.QueryDelegate.RowIdChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(RowIdChangeListener listener) { + removeRowIdChangeListener(listener); + } + + private static final Logger getLogger() { + return Logger.getLogger(TableQuery.class.getName()); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java new file mode 100644 index 0000000000..5439258f32 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/DefaultSQLGenerator.java @@ -0,0 +1,395 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.ColumnProperty; +import com.vaadin.v7.data.util.sqlcontainer.RowItem; +import com.vaadin.v7.data.util.sqlcontainer.SQLUtil; +import com.vaadin.v7.data.util.sqlcontainer.TemporaryRowId; +import com.vaadin.v7.data.util.sqlcontainer.query.OrderBy; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.QueryBuilder; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.StringDecorator; + +/** + * Generates generic SQL that is supported by HSQLDB, MySQL and PostgreSQL. + * + * @author Jonatan Kronqvist / Vaadin Ltd + */ +@SuppressWarnings("serial") +public class DefaultSQLGenerator implements SQLGenerator { + + private Class statementHelperClass = null; + + public DefaultSQLGenerator() { + + } + + /** + * Create a new DefaultSqlGenerator instance that uses the given + * implementation of {@link StatementHelper} + * + * @param statementHelper + */ + public DefaultSQLGenerator( + Class statementHelperClazz) { + this(); + statementHelperClass = statementHelperClazz; + } + + /** + * Construct a DefaultSQLGenerator with the specified identifiers for start + * and end of quoted strings. The identifiers may be different depending on + * the database engine and it's settings. + * + * @param quoteStart + * the identifier (character) denoting the start of a quoted + * string + * @param quoteEnd + * the identifier (character) denoting the end of a quoted string + */ + public DefaultSQLGenerator(String quoteStart, String quoteEnd) { + QueryBuilder + .setStringDecorator(new StringDecorator(quoteStart, quoteEnd)); + } + + /** + * Same as {@link #DefaultSQLGenerator(String, String)} but with support for + * custom {@link StatementHelper} implementation. + * + * @param quoteStart + * @param quoteEnd + * @param statementHelperClazz + */ + public DefaultSQLGenerator(String quoteStart, String quoteEnd, + Class statementHelperClazz) { + this(quoteStart, quoteEnd); + statementHelperClass = statementHelperClazz; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# + * generateSelectQuery(java.lang.String, java.util.List, java.util.List, + * int, int, java.lang.String) + */ + @Override + public StatementHelper generateSelectQuery(String tableName, + List filters, List orderBys, int offset, + int pagelength, String toSelect) { + if (tableName == null || tableName.trim().equals("")) { + throw new IllegalArgumentException("Table name must be given."); + } + toSelect = toSelect == null ? "*" : toSelect; + StatementHelper sh = getStatementHelper(); + StringBuffer query = new StringBuffer(); + query.append("SELECT " + toSelect + " FROM ") + .append(SQLUtil.escapeSQL(tableName)); + if (filters != null) { + query.append(QueryBuilder.getWhereStringForFilters(filters, sh)); + } + if (orderBys != null) { + for (OrderBy o : orderBys) { + generateOrderBy(query, o, orderBys.indexOf(o) == 0); + } + } + if (pagelength != 0) { + generateLimits(query, offset, pagelength); + } + sh.setQueryString(query.toString()); + return sh; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# + * generateUpdateQuery(java.lang.String, + * com.vaadin.addon.sqlcontainer.RowItem) + */ + @Override + public StatementHelper generateUpdateQuery(String tableName, RowItem item) { + if (tableName == null || tableName.trim().equals("")) { + throw new IllegalArgumentException("Table name must be given."); + } + if (item == null) { + throw new IllegalArgumentException("Updated item must be given."); + } + StatementHelper sh = getStatementHelper(); + StringBuffer query = new StringBuffer(); + query.append("UPDATE ").append(tableName).append(" SET"); + + /* Generate column<->value and rowidentifiers map */ + Map columnToValueMap = generateColumnToValueMap(item); + Map rowIdentifiers = generateRowIdentifiers(item); + /* Generate columns and values to update */ + boolean first = true; + for (String column : columnToValueMap.keySet()) { + if (first) { + query.append(" " + QueryBuilder.quote(column) + " = ?"); + } else { + query.append(", " + QueryBuilder.quote(column) + " = ?"); + } + sh.addParameterValue(columnToValueMap.get(column), + item.getItemProperty(column).getType()); + first = false; + } + /* Generate identifiers for the row to be updated */ + first = true; + for (String column : rowIdentifiers.keySet()) { + if (first) { + query.append(" WHERE " + QueryBuilder.quote(column) + " = ?"); + } else { + query.append(" AND " + QueryBuilder.quote(column) + " = ?"); + } + sh.addParameterValue(rowIdentifiers.get(column), + item.getItemProperty(column).getType()); + first = false; + } + sh.setQueryString(query.toString()); + return sh; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# + * generateInsertQuery(java.lang.String, + * com.vaadin.addon.sqlcontainer.RowItem) + */ + @Override + public StatementHelper generateInsertQuery(String tableName, RowItem item) { + if (tableName == null || tableName.trim().equals("")) { + throw new IllegalArgumentException("Table name must be given."); + } + if (item == null) { + throw new IllegalArgumentException("New item must be given."); + } + if (!(item.getId() instanceof TemporaryRowId)) { + throw new IllegalArgumentException( + "Cannot generate an insert query for item already in database."); + } + StatementHelper sh = getStatementHelper(); + StringBuffer query = new StringBuffer(); + query.append("INSERT INTO ").append(tableName).append(" ("); + + /* Generate column<->value map */ + Map columnToValueMap = generateColumnToValueMap(item); + /* Generate column names for insert query */ + boolean first = true; + for (String column : columnToValueMap.keySet()) { + if (!first) { + query.append(", "); + } + query.append(QueryBuilder.quote(column)); + first = false; + } + + /* Generate values for insert query */ + query.append(") VALUES ("); + first = true; + for (String column : columnToValueMap.keySet()) { + if (!first) { + query.append(", "); + } + query.append("?"); + sh.addParameterValue(columnToValueMap.get(column), + item.getItemProperty(column).getType()); + first = false; + } + query.append(")"); + sh.setQueryString(query.toString()); + return sh; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.generator.SQLGenerator# + * generateDeleteQuery(java.lang.String, + * com.vaadin.addon.sqlcontainer.RowItem) + */ + @Override + public StatementHelper generateDeleteQuery(String tableName, + List primaryKeyColumns, String versionColumn, + RowItem item) { + if (tableName == null || tableName.trim().equals("")) { + throw new IllegalArgumentException("Table name must be given."); + } + if (item == null) { + throw new IllegalArgumentException( + "Item to be deleted must be given."); + } + if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) { + throw new IllegalArgumentException( + "Valid keyColumnNames must be provided."); + } + StatementHelper sh = getStatementHelper(); + StringBuffer query = new StringBuffer(); + query.append("DELETE FROM ").append(tableName).append(" WHERE "); + int count = 1; + for (String keyColName : primaryKeyColumns) { + if ((this instanceof MSSQLGenerator + || this instanceof OracleGenerator) + && keyColName.equalsIgnoreCase("rownum")) { + count++; + continue; + } + if (count > 1) { + query.append(" AND "); + } + if (item.getItemProperty(keyColName).getValue() != null) { + query.append(QueryBuilder.quote(keyColName) + " = ?"); + sh.addParameterValue( + item.getItemProperty(keyColName).getValue(), + item.getItemProperty(keyColName).getType()); + } + count++; + } + if (versionColumn != null) { + if (!item.getItemPropertyIds().contains(versionColumn)) { + throw new IllegalArgumentException(String.format( + "Table '%s' does not contain version column '%s'.", + tableName, versionColumn)); + } + + query.append(String.format(" AND %s = ?", + QueryBuilder.quote(versionColumn))); + sh.addParameterValue(item.getItemProperty(versionColumn).getValue(), + item.getItemProperty(versionColumn).getType()); + } + + sh.setQueryString(query.toString()); + return sh; + } + + /** + * Generates sorting rules as an ORDER BY -clause + * + * @param sb + * StringBuffer to which the clause is appended. + * @param o + * OrderBy object to be added into the sb. + * @param firstOrderBy + * If true, this is the first OrderBy. + * @return + */ + protected StringBuffer generateOrderBy(StringBuffer sb, OrderBy o, + boolean firstOrderBy) { + if (firstOrderBy) { + sb.append(" ORDER BY "); + } else { + sb.append(", "); + } + sb.append(QueryBuilder.quote(o.getColumn())); + if (o.isAscending()) { + sb.append(" ASC"); + } else { + sb.append(" DESC"); + } + return sb; + } + + /** + * Generates the LIMIT and OFFSET clause. + * + * @param sb + * StringBuffer to which the clause is appended. + * @param offset + * Value for offset. + * @param pagelength + * Value for pagelength. + * @return StringBuffer with LIMIT and OFFSET clause added. + */ + protected StringBuffer generateLimits(StringBuffer sb, int offset, + int pagelength) { + sb.append(" LIMIT ").append(pagelength).append(" OFFSET ") + .append(offset); + return sb; + } + + protected Map generateColumnToValueMap(RowItem item) { + Map columnToValueMap = new HashMap(); + for (Object id : item.getItemPropertyIds()) { + ColumnProperty cp = (ColumnProperty) item.getItemProperty(id); + /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */ + if ((this instanceof MSSQLGenerator + || this instanceof OracleGenerator) + && cp.getPropertyId().equalsIgnoreCase("rownum")) { + continue; + } + if (cp.isPersistent()) { + columnToValueMap.put(cp.getPropertyId(), cp.getValue()); + } + } + return columnToValueMap; + } + + protected Map generateRowIdentifiers(RowItem item) { + Map rowIdentifiers = new HashMap(); + for (Object id : item.getItemPropertyIds()) { + ColumnProperty cp = (ColumnProperty) item.getItemProperty(id); + /* Prevent "rownum" usage as a column name if MSSQL or ORACLE */ + if ((this instanceof MSSQLGenerator + || this instanceof OracleGenerator) + && cp.getPropertyId().equalsIgnoreCase("rownum")) { + continue; + } + + if (cp.isRowIdentifier()) { + Object value; + if (cp.isPrimaryKey()) { + // If the value of a primary key has changed, its old value + // should be used to identify the row (#9145) + value = cp.getOldValue(); + } else { + value = cp.getValue(); + } + rowIdentifiers.put(cp.getPropertyId(), value); + } + } + return rowIdentifiers; + } + + /** + * Returns the statement helper for the generator. Override this to handle + * platform specific data types. + * + * @see http://dev.vaadin.com/ticket/9148 + * @return a new instance of the statement helper + */ + protected StatementHelper getStatementHelper() { + if (statementHelperClass == null) { + return new StatementHelper(); + } + + try { + return statementHelperClass.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException( + "Unable to instantiate custom StatementHelper", e); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "Unable to instantiate custom StatementHelper", e); + } + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/MSSQLGenerator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/MSSQLGenerator.java new file mode 100644 index 0000000000..fd4f0bcfbe --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/MSSQLGenerator.java @@ -0,0 +1,115 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator; + +import java.util.List; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.query.OrderBy; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.QueryBuilder; + +@SuppressWarnings("serial") +public class MSSQLGenerator extends DefaultSQLGenerator { + + public MSSQLGenerator() { + + } + + /** + * Construct a MSSQLGenerator with the specified identifiers for start and + * end of quoted strings. The identifiers may be different depending on the + * database engine and it's settings. + * + * @param quoteStart + * the identifier (character) denoting the start of a quoted + * string + * @param quoteEnd + * the identifier (character) denoting the end of a quoted string + */ + public MSSQLGenerator(String quoteStart, String quoteEnd) { + super(quoteStart, quoteEnd); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator# + * generateSelectQuery(java.lang.String, java.util.List, + * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int, + * int, java.lang.String) + */ + @Override + public StatementHelper generateSelectQuery(String tableName, + List filters, List orderBys, int offset, + int pagelength, String toSelect) { + if (tableName == null || tableName.trim().equals("")) { + throw new IllegalArgumentException("Table name must be given."); + } + /* Adjust offset and page length parameters to match "row numbers" */ + offset = pagelength > 1 ? ++offset : offset; + pagelength = pagelength > 1 ? --pagelength : pagelength; + toSelect = toSelect == null ? "*" : toSelect; + StatementHelper sh = getStatementHelper(); + StringBuffer query = new StringBuffer(); + + /* Row count request is handled here */ + if ("COUNT(*)".equalsIgnoreCase(toSelect)) { + query.append(String.format( + "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s", + QueryBuilder.quote("rowcount"), tableName)); + if (filters != null && !filters.isEmpty()) { + query.append( + QueryBuilder.getWhereStringForFilters(filters, sh)); + } + query.append(") AS t"); + sh.setQueryString(query.toString()); + return sh; + } + + /* SELECT without row number constraints */ + if (offset == 0 && pagelength == 0) { + query.append("SELECT ").append(toSelect).append(" FROM ") + .append(tableName); + if (filters != null) { + query.append( + QueryBuilder.getWhereStringForFilters(filters, sh)); + } + if (orderBys != null) { + for (OrderBy o : orderBys) { + generateOrderBy(query, o, orderBys.indexOf(o) == 0); + } + } + sh.setQueryString(query.toString()); + return sh; + } + + /* Remaining SELECT cases are handled here */ + query.append("SELECT * FROM (SELECT row_number() OVER ("); + if (orderBys != null) { + for (OrderBy o : orderBys) { + generateOrderBy(query, o, orderBys.indexOf(o) == 0); + } + } + query.append(") AS rownum, " + toSelect + " FROM ").append(tableName); + if (filters != null) { + query.append(QueryBuilder.getWhereStringForFilters(filters, sh)); + } + query.append(") AS a WHERE a.rownum BETWEEN ").append(offset) + .append(" AND ").append(Integer.toString(offset + pagelength)); + sh.setQueryString(query.toString()); + return sh; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/OracleGenerator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/OracleGenerator.java new file mode 100644 index 0000000000..64a0c22d97 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/OracleGenerator.java @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator; + +import java.util.List; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.query.OrderBy; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.filter.QueryBuilder; + +@SuppressWarnings("serial") +public class OracleGenerator extends DefaultSQLGenerator { + + public OracleGenerator() { + + } + + public OracleGenerator( + Class statementHelperClazz) { + super(statementHelperClazz); + } + + /** + * Construct an OracleSQLGenerator with the specified identifiers for start + * and end of quoted strings. The identifiers may be different depending on + * the database engine and it's settings. + * + * @param quoteStart + * the identifier (character) denoting the start of a quoted + * string + * @param quoteEnd + * the identifier (character) denoting the end of a quoted string + */ + public OracleGenerator(String quoteStart, String quoteEnd) { + super(quoteStart, quoteEnd); + } + + public OracleGenerator(String quoteStart, String quoteEnd, + Class statementHelperClazz) { + super(quoteStart, quoteEnd, statementHelperClazz); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.sqlcontainer.query.generator.DefaultSQLGenerator# + * generateSelectQuery(java.lang.String, java.util.List, + * com.vaadin.addon.sqlcontainer.query.FilteringMode, java.util.List, int, + * int, java.lang.String) + */ + @Override + public StatementHelper generateSelectQuery(String tableName, + List filters, List orderBys, int offset, + int pagelength, String toSelect) { + if (tableName == null || tableName.trim().equals("")) { + throw new IllegalArgumentException("Table name must be given."); + } + /* Adjust offset and page length parameters to match "row numbers" */ + offset = pagelength > 1 ? ++offset : offset; + pagelength = pagelength > 1 ? --pagelength : pagelength; + toSelect = toSelect == null ? "*" : toSelect; + StatementHelper sh = getStatementHelper(); + StringBuffer query = new StringBuffer(); + + /* Row count request is handled here */ + if ("COUNT(*)".equalsIgnoreCase(toSelect)) { + query.append(String.format( + "SELECT COUNT(*) AS %s FROM (SELECT * FROM %s", + QueryBuilder.quote("rowcount"), tableName)); + if (filters != null && !filters.isEmpty()) { + query.append( + QueryBuilder.getWhereStringForFilters(filters, sh)); + } + query.append(")"); + sh.setQueryString(query.toString()); + return sh; + } + + /* SELECT without row number constraints */ + if (offset == 0 && pagelength == 0) { + query.append("SELECT ").append(toSelect).append(" FROM ") + .append(tableName); + if (filters != null) { + query.append( + QueryBuilder.getWhereStringForFilters(filters, sh)); + } + if (orderBys != null) { + for (OrderBy o : orderBys) { + generateOrderBy(query, o, orderBys.indexOf(o) == 0); + } + } + sh.setQueryString(query.toString()); + return sh; + } + + /* Remaining SELECT cases are handled here */ + query.append(String.format( + "SELECT * FROM (SELECT x.*, ROWNUM AS %s FROM (SELECT %s FROM %s", + QueryBuilder.quote("rownum"), toSelect, tableName)); + if (filters != null) { + query.append(QueryBuilder.getWhereStringForFilters(filters, sh)); + } + if (orderBys != null) { + for (OrderBy o : orderBys) { + generateOrderBy(query, o, orderBys.indexOf(o) == 0); + } + } + query.append(String.format(") x) WHERE %s BETWEEN %d AND %d", + QueryBuilder.quote("rownum"), offset, offset + pagelength)); + sh.setQueryString(query.toString()); + return sh; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/SQLGenerator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/SQLGenerator.java new file mode 100644 index 0000000000..1841c0a23b --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/SQLGenerator.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator; + +import java.io.Serializable; +import java.util.List; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.RowItem; +import com.vaadin.v7.data.util.sqlcontainer.query.OrderBy; + +/** + * The SQLGenerator interface is meant to be implemented for each different SQL + * syntax that is to be supported. By default there are implementations for + * HSQLDB, MySQL, PostgreSQL, MSSQL and Oracle syntaxes. + * + * @author Jonatan Kronqvist / Vaadin Ltd + */ +public interface SQLGenerator extends Serializable { + /** + * Generates a SELECT query with the provided parameters. Uses default + * filtering mode (INCLUSIVE). + * + * @param tableName + * Name of the table queried + * @param filters + * The filters, converted into a WHERE clause + * @param orderBys + * The the ordering conditions, converted into an ORDER BY clause + * @param offset + * The offset of the first row to be included + * @param pagelength + * The number of rows to be returned when the query executes + * @param toSelect + * String containing what to select, e.g. "*", "COUNT(*)" + * @return StatementHelper instance containing the query string for a + * PreparedStatement and the values required for the parameters + */ + public StatementHelper generateSelectQuery(String tableName, + List filters, List orderBys, int offset, + int pagelength, String toSelect); + + /** + * Generates an UPDATE query with the provided parameters. + * + * @param tableName + * Name of the table queried + * @param item + * RowItem containing the updated values update. + * @return StatementHelper instance containing the query string for a + * PreparedStatement and the values required for the parameters + */ + public StatementHelper generateUpdateQuery(String tableName, RowItem item); + + /** + * Generates an INSERT query for inserting a new row with the provided + * values. + * + * @param tableName + * Name of the table queried + * @param item + * New RowItem to be inserted into the database. + * @return StatementHelper instance containing the query string for a + * PreparedStatement and the values required for the parameters + */ + public StatementHelper generateInsertQuery(String tableName, RowItem item); + + /** + * Generates a DELETE query for deleting data related to the given RowItem + * from the database. + * + * @param tableName + * Name of the table queried + * @param primaryKeyColumns + * the names of the columns holding the primary key. Usually just + * one column, but might be several. + * @param versionColumn + * the column containing the version number of the row, null if + * versioning (optimistic locking) not enabled. + * @param item + * Item to be deleted from the database + * @return StatementHelper instance containing the query string for a + * PreparedStatement and the values required for the parameters + */ + public StatementHelper generateDeleteQuery(String tableName, + List primaryKeyColumns, String versionColumn, RowItem item); +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/StatementHelper.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/StatementHelper.java new file mode 100644 index 0000000000..fe8240cd42 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/StatementHelper.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * StatementHelper is a simple helper class that assists TableQuery and the + * query generators in filling a PreparedStatement. The actual statement is + * generated by the query generator methods, but the resulting statement and all + * the parameter values are stored in an instance of StatementHelper. + * + * This class will also fill the values with correct setters into the + * PreparedStatement on request. + */ +public class StatementHelper implements Serializable { + + private String queryString; + + private List parameters = new ArrayList(); + private Map> dataTypes = new HashMap>(); + + public StatementHelper() { + } + + public void setQueryString(String queryString) { + this.queryString = queryString; + } + + public String getQueryString() { + return queryString; + } + + public void addParameterValue(Object parameter) { + if (parameter != null) { + parameters.add(parameter); + dataTypes.put(parameters.size() - 1, parameter.getClass()); + } else { + throw new IllegalArgumentException( + "You cannot add null parameters using addParamaters(Object). " + + "Use addParameters(Object,Class) instead"); + } + } + + public void addParameterValue(Object parameter, Class type) { + parameters.add(parameter); + dataTypes.put(parameters.size() - 1, type); + } + + public void setParameterValuesToStatement(PreparedStatement pstmt) + throws SQLException { + for (int i = 0; i < parameters.size(); i++) { + if (parameters.get(i) == null) { + handleNullValue(i, pstmt); + } else { + pstmt.setObject(i + 1, parameters.get(i)); + } + } + + /* + * The following list contains the data types supported by + * PreparedStatement but not supported by SQLContainer: + * + * [The list is provided as PreparedStatement method signatures] + * + * setNCharacterStream(int parameterIndex, Reader value) + * + * setNClob(int parameterIndex, NClob value) + * + * setNString(int parameterIndex, String value) + * + * setRef(int parameterIndex, Ref x) + * + * setRowId(int parameterIndex, RowId x) + * + * setSQLXML(int parameterIndex, SQLXML xmlObject) + * + * setBytes(int parameterIndex, byte[] x) + * + * setCharacterStream(int parameterIndex, Reader reader) + * + * setClob(int parameterIndex, Clob x) + * + * setURL(int parameterIndex, URL x) + * + * setArray(int parameterIndex, Array x) + * + * setAsciiStream(int parameterIndex, InputStream x) + * + * setBinaryStream(int parameterIndex, InputStream x) + * + * setBlob(int parameterIndex, Blob x) + */ + } + + private void handleNullValue(int i, PreparedStatement pstmt) + throws SQLException { + Class dataType = dataTypes.get(i); + int index = i + 1; + if (BigDecimal.class.equals(dataType)) { + pstmt.setBigDecimal(index, null); + } else if (Boolean.class.equals(dataType)) { + pstmt.setNull(index, Types.BOOLEAN); + } else if (Byte.class.equals(dataType)) { + pstmt.setNull(index, Types.SMALLINT); + } else if (Date.class.equals(dataType)) { + pstmt.setDate(index, null); + } else if (Double.class.equals(dataType)) { + pstmt.setNull(index, Types.DOUBLE); + } else if (Float.class.equals(dataType)) { + pstmt.setNull(index, Types.FLOAT); + } else if (Integer.class.equals(dataType)) { + pstmt.setNull(index, Types.INTEGER); + } else if (Long.class.equals(dataType)) { + pstmt.setNull(index, Types.BIGINT); + } else if (Short.class.equals(dataType)) { + pstmt.setNull(index, Types.SMALLINT); + } else if (String.class.equals(dataType)) { + pstmt.setString(index, null); + } else if (Time.class.equals(dataType)) { + pstmt.setTime(index, null); + } else if (Timestamp.class.equals(dataType)) { + pstmt.setTimestamp(index, null); + } else if (byte[].class.equals(dataType)) { + pstmt.setBytes(index, null); + } else { + + if (handleUnrecognizedTypeNullValue(i, pstmt, dataTypes)) { + return; + } + + throw new SQLException("Data type for parameter " + i + + " not supported by SQLContainer: " + dataType.getName()); + } + } + + /** + * Handle unrecognized null values. Override this to handle null values for + * platform specific data types that are not handled by the default + * implementation of the {@link StatementHelper}. + * + * @param i + * @param pstmt + * @param dataTypes2 + * + * @return true if handled, false otherwise + * + * @see {@link http://dev.vaadin.com/ticket/9148} + */ + protected boolean handleUnrecognizedTypeNullValue(int i, + PreparedStatement pstmt, Map> dataTypes) + throws SQLException { + return false; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/AndTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/AndTranslator.java new file mode 100644 index 0000000000..ef958459fb --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/AndTranslator.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.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.And; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class AndTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof And; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + return QueryBuilder.group(QueryBuilder + .getJoinedFilterString(((And) filter).getFilters(), "AND", sh)); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.java new file mode 100644 index 0000000000..7e491925a3 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/BetweenTranslator.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.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.Between; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class BetweenTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof Between; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + Between between = (Between) filter; + sh.addParameterValue(between.getStartValue()); + sh.addParameterValue(between.getEndValue()); + return QueryBuilder.quote(between.getPropertyId()) + " BETWEEN ? AND ?"; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/CompareTranslator.java new file mode 100644 index 0000000000..44e5a7ee14 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/CompareTranslator.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.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.Compare; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class CompareTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof Compare; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + Compare compare = (Compare) filter; + sh.addParameterValue(compare.getValue()); + String prop = QueryBuilder.quote(compare.getPropertyId()); + switch (compare.getOperation()) { + case EQUAL: + return prop + " = ?"; + case GREATER: + return prop + " > ?"; + case GREATER_OR_EQUAL: + return prop + " >= ?"; + case LESS: + return prop + " < ?"; + case LESS_OR_EQUAL: + return prop + " <= ?"; + default: + return ""; + } + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java new file mode 100644 index 0000000000..4bd1807600 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/FilterTranslator.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator.filter; + +import java.io.Serializable; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public interface FilterTranslator extends Serializable { + public boolean translatesFilter(Filter filter); + + public String getWhereStringForFilter(Filter filter, StatementHelper sh); + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.java new file mode 100644 index 0000000000..d2dda82232 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/IsNullTranslator.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.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.IsNull; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class IsNullTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof IsNull; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + IsNull in = (IsNull) filter; + return QueryBuilder.quote(in.getPropertyId()) + " IS NULL"; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java new file mode 100644 index 0000000000..bd41aa1abf --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/LikeTranslator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.Like; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class LikeTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof Like; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + Like like = (Like) filter; + if (like.isCaseSensitive()) { + sh.addParameterValue(like.getValue()); + return QueryBuilder.quote(like.getPropertyId()) + " LIKE ?"; + } else { + sh.addParameterValue(like.getValue().toUpperCase()); + return "UPPER(" + QueryBuilder.quote(like.getPropertyId()) + + ") LIKE ?"; + } + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/NotTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/NotTranslator.java new file mode 100644 index 0000000000..83110e2f41 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/NotTranslator.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.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.IsNull; +import com.vaadin.v7.data.util.filter.Not; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class NotTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof Not; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + Not not = (Not) filter; + if (not.getFilter() instanceof IsNull) { + IsNull in = (IsNull) not.getFilter(); + return QueryBuilder.quote(in.getPropertyId()) + " IS NOT NULL"; + } + return "NOT " + + QueryBuilder.getWhereStringForFilter(not.getFilter(), sh); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/OrTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/OrTranslator.java new file mode 100644 index 0000000000..61fad7b559 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/OrTranslator.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.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.Or; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class OrTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof Or; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + return QueryBuilder.group(QueryBuilder + .getJoinedFilterString(((Or) filter).getFilters(), "OR", sh)); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java new file mode 100644 index 0000000000..c7ed3f91ae --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/QueryBuilder.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator.filter; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class QueryBuilder implements Serializable { + + private static ArrayList filterTranslators = new ArrayList(); + private static StringDecorator stringDecorator = new StringDecorator("\"", + "\""); + + static { + /* Register all default filter translators */ + addFilterTranslator(new AndTranslator()); + addFilterTranslator(new OrTranslator()); + addFilterTranslator(new LikeTranslator()); + addFilterTranslator(new BetweenTranslator()); + addFilterTranslator(new CompareTranslator()); + addFilterTranslator(new NotTranslator()); + addFilterTranslator(new IsNullTranslator()); + addFilterTranslator(new SimpleStringTranslator()); + } + + public synchronized static void addFilterTranslator( + FilterTranslator translator) { + filterTranslators.add(translator); + } + + /** + * Allows specification of a custom ColumnQuoter instance that handles + * quoting of column names for the current DB dialect. + * + * @param decorator + * the ColumnQuoter instance to use. + */ + public static void setStringDecorator(StringDecorator decorator) { + stringDecorator = decorator; + } + + public static String quote(Object str) { + return stringDecorator.quote(str); + } + + public static String group(String str) { + return stringDecorator.group(str); + } + + /** + * Constructs and returns a string representing the filter that can be used + * in a WHERE clause. + * + * @param filter + * the filter to translate + * @param sh + * the statement helper to update with the value(s) of the filter + * @return a string representing the filter. + */ + public synchronized static String getWhereStringForFilter(Filter filter, + StatementHelper sh) { + for (FilterTranslator ft : filterTranslators) { + if (ft.translatesFilter(filter)) { + return ft.getWhereStringForFilter(filter, sh); + } + } + return ""; + } + + public static String getJoinedFilterString(Collection filters, + String joinString, StatementHelper sh) { + StringBuilder result = new StringBuilder(); + for (Filter f : filters) { + result.append(getWhereStringForFilter(f, sh)); + result.append(" ").append(joinString).append(" "); + } + // Remove the last instance of joinString + result.delete(result.length() - joinString.length() - 2, + result.length()); + return result.toString(); + } + + public static String getWhereStringForFilters(List filters, + StatementHelper sh) { + if (filters == null || filters.isEmpty()) { + return ""; + } + StringBuilder where = new StringBuilder(" WHERE "); + where.append(getJoinedFilterString(filters, "AND", sh)); + return where.toString(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java new file mode 100644 index 0000000000..bb47fbd826 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/SimpleStringTranslator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator.filter; + +import com.vaadin.v7.data.Container.Filter; +import com.vaadin.v7.data.util.filter.Like; +import com.vaadin.v7.data.util.filter.SimpleStringFilter; +import com.vaadin.v7.data.util.sqlcontainer.query.generator.StatementHelper; + +public class SimpleStringTranslator implements FilterTranslator { + + @Override + public boolean translatesFilter(Filter filter) { + return filter instanceof SimpleStringFilter; + } + + @Override + public String getWhereStringForFilter(Filter filter, StatementHelper sh) { + SimpleStringFilter ssf = (SimpleStringFilter) filter; + // Create a Like filter based on the SimpleStringFilter and execute the + // LikeTranslator + String likeStr = ssf.isOnlyMatchPrefix() ? ssf.getFilterString() + "%" + : "%" + ssf.getFilterString() + "%"; + Like like = new Like(ssf.getPropertyId().toString(), likeStr); + like.setCaseSensitive(!ssf.isIgnoreCase()); + return new LikeTranslator().getWhereStringForFilter(like, sh); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/StringDecorator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/StringDecorator.java new file mode 100644 index 0000000000..72c841fe0a --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/sqlcontainer/query/generator/filter/StringDecorator.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.util.sqlcontainer.query.generator.filter; + +import java.io.Serializable; + +/** + * The StringDecorator knows how to produce a quoted string using the specified + * quote start and quote end characters. It also handles grouping of a string + * (surrounding it in parenthesis). + * + * Extend this class if you need to support special characters for grouping + * (parenthesis). + * + * @author Vaadin Ltd + */ +public class StringDecorator implements Serializable { + + private final String quoteStart; + private final String quoteEnd; + + /** + * Constructs a StringDecorator that uses the quoteStart and quoteEnd + * characters to create quoted strings. + * + * @param quoteStart + * the character denoting the start of a quote. + * @param quoteEnd + * the character denoting the end of a quote. + */ + public StringDecorator(String quoteStart, String quoteEnd) { + this.quoteStart = quoteStart; + this.quoteEnd = quoteEnd; + } + + /** + * Surround a string with quote characters. + * + * @param str + * the string to quote + * @return the quoted string + */ + public String quote(Object str) { + return quoteStart + str + quoteEnd; + } + + /** + * Groups a string by surrounding it in parenthesis + * + * @param str + * the string to group + * @return the grouped string + */ + public String group(String str) { + return "(" + str + ")"; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractStringValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractStringValidator.java new file mode 100644 index 0000000000..340b34237d --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractStringValidator.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * Validator base class for validating strings. + *

    + * To include the value that failed validation in the exception message you can + * use "{0}" in the error message. This will be replaced with the failed value + * (converted to string using {@link #toString()}) or "null" if the value is + * null. + *

    + * + * @author Vaadin Ltd. + * @since 5.4 + */ +@SuppressWarnings("serial") +public abstract class AbstractStringValidator + extends AbstractValidator { + + /** + * Constructs a validator for strings. + * + *

    + * Null and empty string values are always accepted. To reject empty values, + * set the field being validated as required. + *

    + * + * @param errorMessage + * the message to be included in an {@link InvalidValueException} + * (with "{0}" replaced by the value that failed validation). + */ + public AbstractStringValidator(String errorMessage) { + super(errorMessage); + } + + @Override + public Class getType() { + return String.class; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractValidator.java new file mode 100644 index 0000000000..8f900961df --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/AbstractValidator.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.v7.data.validator; + +import com.vaadin.v7.data.Validator; + +/** + * Abstract {@link com.vaadin.v7.data.Validator Validator} implementation + * that provides a basic Validator implementation except the + * {@link #isValidValue(Object)} method. + *

    + * To include the value that failed validation in the exception message you can + * use "{0}" in the error message. This will be replaced with the failed value + * (converted to string using {@link #toString()}) or "null" if the value is + * null. + *

    + *

    + * The default implementation of AbstractValidator does not support HTML in + * error messages. To enable HTML support, override + * {@link InvalidValueException#getHtmlMessage()} and throw such exceptions from + * {@link #validate(Object)}. + *

    + *

    + * Since Vaadin 7, subclasses can either implement {@link #validate(Object)} + * directly or implement {@link #isValidValue(Object)} when migrating legacy + * applications. To check validity, {@link #validate(Object)} should be used. + *

    + * + * @param + * The type + * @author Vaadin Ltd. + * @since 5.4 + */ +public abstract class AbstractValidator implements Validator { + + /** + * Error message that is included in an {@link InvalidValueException} if + * such is thrown. + */ + private String errorMessage; + + /** + * Constructs a validator with the given error message. + * + * @param errorMessage + * the message to be included in an {@link InvalidValueException} + * (with "{0}" replaced by the value that failed validation). + */ + public AbstractValidator(String errorMessage) { + this.errorMessage = errorMessage; + } + + /** + * Since Vaadin 7, subclasses of AbstractValidator should override + * {@link #isValidValue(Object)} or {@link #validate(Object)} instead of + * {@link #isValid(Object)}. {@link #validate(Object)} should normally be + * used to check values. + * + * @param value + * @return true if the value is valid + */ + public boolean isValid(Object value) { + try { + validate(value); + return true; + } catch (InvalidValueException e) { + return false; + } + } + + /** + * Internally check the validity of a value. This method can be used to + * perform validation in subclasses if customization of the error message is + * not needed. Otherwise, subclasses should override + * {@link #validate(Object)} and the return value of this method is ignored. + * + * This method should not be called from outside the validator class itself. + * + * @param value + * @return + */ + protected abstract boolean isValidValue(T value); + + @Override + public void validate(Object value) throws InvalidValueException { + // isValidType ensures that value can safely be cast to TYPE + if (!isValidType(value) || !isValidValue((T) value)) { + String message = getErrorMessage().replace("{0}", + String.valueOf(value)); + throw new InvalidValueException(message); + } + } + + /** + * Checks the type of the value to validate to ensure it conforms with + * getType. Enables sub classes to handle the specific type instead of + * Object. + * + * @param value + * The value to check + * @return true if the value can safely be cast to the type specified by + * {@link #getType()} + */ + protected boolean isValidType(Object value) { + if (value == null) { + return true; + } + + return getType().isAssignableFrom(value.getClass()); + } + + /** + * Returns the message to be included in the exception in case the value + * does not validate. + * + * @return the error message provided in the constructor or using + * {@link #setErrorMessage(String)}. + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Sets the message to be included in the exception in case the value does + * not validate. The exception message is typically shown to the end user. + * + * @param errorMessage + * the error message. "{0}" is automatically replaced by the + * value that did not validate. + */ + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public abstract Class getType(); +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BeanValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BeanValidator.java new file mode 100644 index 0000000000..9afd816ac0 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BeanValidator.java @@ -0,0 +1,184 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.validator; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.MessageInterpolator.Context; +import javax.validation.Validation; +import javax.validation.ValidatorFactory; +import javax.validation.metadata.ConstraintDescriptor; + +import com.vaadin.v7.data.Validator; + +/** + * Vaadin {@link Validator} using the JSR-303 (javax.validation) + * annotation-based bean validation. + * + * The annotations of the fields of the beans are used to determine the + * validation to perform. + * + * Note that a JSR-303 implementation (e.g. Hibernate Validator or Apache Bean + * Validation - formerly agimatec validation) must be present on the project + * classpath when using bean validation. + * + * @since 7.0 + * + * @author Petri Hakala + * @author Henri Sara + */ +public class BeanValidator implements Validator { + + private static final long serialVersionUID = 1L; + private static ValidatorFactory factory; + + private transient javax.validation.Validator javaxBeanValidator; + private String propertyName; + private Class beanClass; + private Locale locale; + + /** + * Simple implementation of a message interpolator context that returns + * fixed values. + */ + protected static class SimpleContext implements Context, Serializable { + + private final Object value; + private final ConstraintDescriptor descriptor; + + /** + * Create a simple immutable message interpolator context. + * + * @param value + * value being validated + * @param descriptor + * ConstraintDescriptor corresponding to the constraint being + * validated + */ + public SimpleContext(Object value, ConstraintDescriptor descriptor) { + this.value = value; + this.descriptor = descriptor; + } + + @Override + public ConstraintDescriptor getConstraintDescriptor() { + return descriptor; + } + + @Override + public Object getValidatedValue() { + return value; + } + + } + + /** + * Creates a Vaadin {@link Validator} utilizing JSR-303 bean validation. + * + * @param beanClass + * bean class based on which the validation should be performed + * @param propertyName + * property to validate + */ + public BeanValidator(Class beanClass, String propertyName) { + this.beanClass = beanClass; + this.propertyName = propertyName; + locale = Locale.getDefault(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Validator#validate(java.lang.Object) + */ + @Override + public void validate(final Object value) throws InvalidValueException { + Set violations = getJavaxBeanValidator().validateValue(beanClass, + propertyName, value); + if (violations.size() > 0) { + InvalidValueException[] causes = new InvalidValueException[violations + .size()]; + int i = 0; + for (Object v : violations) { + final ConstraintViolation violation = (ConstraintViolation) v; + String msg = getJavaxBeanValidatorFactory() + .getMessageInterpolator() + .interpolate(violation.getMessageTemplate(), + new SimpleContext(value, + violation.getConstraintDescriptor()), + locale); + causes[i] = new InvalidValueException(msg); + ++i; + } + + throw new InvalidValueException(null, causes); + } + } + + /** + * Sets the locale used for validation error messages. + * + * Revalidation is not automatically triggered by setting the locale. + * + * @param locale + */ + public void setLocale(Locale locale) { + this.locale = locale; + } + + /** + * Gets the locale used for validation error messages. + * + * @return locale used for validation + */ + public Locale getLocale() { + return locale; + } + + /** + * Returns the underlying JSR-303 bean validator factory used. A factory is + * created using {@link Validation} if necessary. + * + * @return {@link ValidatorFactory} to use + */ + protected static ValidatorFactory getJavaxBeanValidatorFactory() { + if (factory == null) { + factory = Validation.buildDefaultValidatorFactory(); + } + + return factory; + } + + /** + * Returns a shared Validator instance to use. An instance is created using + * the validator factory if necessary and thereafter reused by the + * {@link BeanValidator} instance. + * + * @return the JSR-303 {@link javax.validation.Validator} to use + */ + protected javax.validation.Validator getJavaxBeanValidator() { + if (javaxBeanValidator == null) { + javaxBeanValidator = getJavaxBeanValidatorFactory().getValidator(); + } + + return javaxBeanValidator; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigDecimalRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigDecimalRangeValidator.java new file mode 100644 index 0000000000..652e33e12e --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigDecimalRangeValidator.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +import java.math.BigDecimal; + +/** + * Validator for validating that an {@link BigDecimal} is inside a given range. + * + * @author Vaadin Ltd. + * @since 7.4 + */ +@SuppressWarnings("serial") +public class BigDecimalRangeValidator + extends RangeValidator { + + /** + * Creates a validator for checking that an BigDecimal is within a given + * range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public BigDecimalRangeValidator(String errorMessage, + BigDecimal minValue, BigDecimal maxValue) { + super(errorMessage, BigDecimal.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigIntegerRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigIntegerRangeValidator.java new file mode 100644 index 0000000000..62ec012344 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/BigIntegerRangeValidator.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +import java.math.BigInteger; + +/** + * Validator for validating that an {@link BigInteger} is inside a given range. + * + * @author Vaadin Ltd. + * @since 7.4 + */ +@SuppressWarnings("serial") +public class BigIntegerRangeValidator + extends RangeValidator { + + /** + * Creates a validator for checking that an BigInteger is within a given + * range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public BigIntegerRangeValidator(String errorMessage, + BigInteger minValue, BigInteger maxValue) { + super(errorMessage, BigInteger.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/ByteRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/ByteRangeValidator.java new file mode 100644 index 0000000000..4bbb1b3290 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/ByteRangeValidator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * Validator for validating that an {@link Byte} is inside a given range. + * + * @author Vaadin Ltd. + * @since 7.4 + */ +@SuppressWarnings("serial") +public class ByteRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that an Byte is within a given range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public ByteRangeValidator(String errorMessage, Byte minValue, + Byte maxValue) { + super(errorMessage, Byte.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/CompositeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/CompositeValidator.java new file mode 100644 index 0000000000..2827c401ed --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/CompositeValidator.java @@ -0,0 +1,270 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.validator; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import com.vaadin.v7.data.Validator; + +/** + * The CompositeValidator allows you to chain (compose) many + * validators to validate one field. The contained validators may be required to + * all validate the value to validate or it may be enough that one contained + * validator validates the value. This behaviour is controlled by the modes + * AND and OR. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class CompositeValidator implements Validator { + + public enum CombinationMode { + /** + * The validators are combined with AND clause: validity of + * the composite implies validity of the all validators it is composed + * of must be valid. + */ + AND, + /** + * The validators are combined with OR clause: validity of + * the composite implies that some of validators it is composed of must + * be valid. + */ + OR; + } + + /** + * @deprecated As of 7.0, use {@link CombinationMode#AND} instead     + */ + @Deprecated + public static final CombinationMode MODE_AND = CombinationMode.AND; + /** + * @deprecated As of 7.0, use {@link CombinationMode#OR} instead     + */ + @Deprecated + public static final CombinationMode MODE_OR = CombinationMode.OR; + + private String errorMessage; + + /** + * Operation mode. + */ + private CombinationMode mode = CombinationMode.AND; + + /** + * List of contained validators. + */ + private final List validators = new LinkedList(); + + /** + * Construct a composite validator in AND mode without error + * message. + */ + public CompositeValidator() { + this(CombinationMode.AND, ""); + } + + /** + * Constructs a composite validator in given mode. + * + * @param mode + * @param errorMessage + */ + public CompositeValidator(CombinationMode mode, String errorMessage) { + setErrorMessage(errorMessage); + setMode(mode); + } + + /** + * Validates the given value. + *

    + * The value is valid, if: + *

      + *
    • MODE_AND: All of the sub-validators are valid + *
    • MODE_OR: Any of the sub-validators are valid + *
    + * + * If the value is invalid, validation error is thrown. If the error message + * is set (non-null), it is used. If the error message has not been set, the + * first error occurred is thrown. + *

    + * + * @param value + * the value to check. + * @throws Validator.InvalidValueException + * if the value is not valid. + */ + @Override + public void validate(Object value) throws Validator.InvalidValueException { + switch (mode) { + case AND: + for (Validator validator : validators) { + validator.validate(value); + } + return; + + case OR: + Validator.InvalidValueException first = null; + for (Validator v : validators) { + try { + v.validate(value); + return; + } catch (final Validator.InvalidValueException e) { + if (first == null) { + first = e; + } + } + } + if (first == null) { + return; + } + final String em = getErrorMessage(); + if (em != null) { + throw new Validator.InvalidValueException(em); + } else { + throw first; + } + } + } + + /** + * Gets the mode of the validator. + * + * @return Operation mode of the validator: {@link CombinationMode#AND} or + * {@link CombinationMode#OR}. + */ + public final CombinationMode getMode() { + return mode; + } + + /** + * Sets the mode of the validator. The valid modes are: + *
      + *
    • {@link CombinationMode#AND} (default) + *
    • {@link CombinationMode#OR} + *
    + * + * @param mode + * the mode to set. + */ + public void setMode(CombinationMode mode) { + if (mode == null) { + throw new IllegalArgumentException( + "The validator can't be set to null"); + } + this.mode = mode; + } + + /** + * Gets the error message for the composite validator. If the error message + * is null, original error messages of the sub-validators are used instead. + */ + public String getErrorMessage() { + if (errorMessage != null) { + return errorMessage; + } + + // TODO Return composite error message + + return null; + } + + /** + * Adds validator to the interface. + * + * @param validator + * the Validator object which performs validation checks on this + * set of data field values. + */ + public void addValidator(Validator validator) { + if (validator == null) { + return; + } + validators.add(validator); + } + + /** + * Removes a validator from the composite. + * + * @param validator + * the Validator object which performs validation checks on this + * set of data field values. + */ + public void removeValidator(Validator validator) { + validators.remove(validator); + } + + /** + * Gets sub-validators by class. + * + *

    + * If the component contains directly or recursively (it contains another + * composite containing the validator) validators compatible with given type + * they are returned. This only applies to AND mode composite + * validators. + *

    + * + *

    + * If the validator is in OR mode or does not contain any + * validators of given type null is returned. + *

    + * + * @param validatorType + * The type of validators to return + * + * @return Collection of validators compatible with given type + * that must apply or null if none found. + */ + public Collection getSubValidators(Class validatorType) { + if (mode != CombinationMode.AND) { + return null; + } + + final HashSet found = new HashSet(); + for (Validator v : validators) { + if (validatorType.isAssignableFrom(v.getClass())) { + found.add(v); + } + if (v instanceof CompositeValidator + && ((CompositeValidator) v).getMode() == MODE_AND) { + final Collection c = ((CompositeValidator) v) + .getSubValidators(validatorType); + if (c != null) { + found.addAll(c); + } + } + } + + return found.isEmpty() ? null : found; + } + + /** + * Sets the message to be included in the exception in case the value does + * not validate. The exception message is typically shown to the end user. + * + * @param errorMessage + * the error message. + */ + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DateRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DateRangeValidator.java new file mode 100644 index 0000000000..f46f8d989e --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DateRangeValidator.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +import java.util.Date; + +import com.vaadin.shared.ui.datefield.Resolution; + +/** + * Validator for validating that a Date is inside a given range. + * + *

    + * Note that the comparison is done directly on the Date object so take care + * that the hours/minutes/seconds/milliseconds of the min/max values are + * properly set. + *

    + * + * @author Vaadin Ltd. + * @since 7.0 + */ +public class DateRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that an Date is within a given range. + *

    + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + *

    + *

    + * Note that the comparison is done directly on the Date object so take care + * that the hours/minutes/seconds/milliseconds of the min/max values are + * properly set. + *

    + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public DateRangeValidator(String errorMessage, Date minValue, + Date maxValue, Resolution resolution) { + super(errorMessage, Date.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleRangeValidator.java new file mode 100644 index 0000000000..1a6d8cd208 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleRangeValidator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * Validator for validating that a {@link Double} is inside a given range. + * + * @author Vaadin Ltd. + * @since 7.0 + */ +@SuppressWarnings("serial") +public class DoubleRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that an Double is within a given range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public DoubleRangeValidator(String errorMessage, Double minValue, + Double maxValue) { + super(errorMessage, Double.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleValidator.java new file mode 100644 index 0000000000..0d0dd8ff40 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/DoubleValidator.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.converter.StringToDoubleConverter; + +/** + * String validator for a double precision floating point number. See + * {@link com.vaadin.v7.data.validator.AbstractStringValidator} for + * more information. + * + * @author Vaadin Ltd. + * @since 5.4 + * @deprecated As of 7.0. Use a {@link StringToDoubleConverter} converter + * on the field instead or bind the field to a {@link Property} of + * type {@link Double}. + */ +@Deprecated +@SuppressWarnings("serial") +public class DoubleValidator extends AbstractStringValidator { + + /** + * Creates a validator for checking that a string can be parsed as an + * double. + * + * @param errorMessage + * the message to display in case the value does not validate. + * @deprecated As of 7.0. Use a Double converter on the field instead and/or + * use a {@link DoubleRangeValidator} for validating that + * the value is inside a given range. + */ + @Deprecated + public DoubleValidator(String errorMessage) { + super(errorMessage); + } + + @Override + protected boolean isValidValue(String value) { + try { + Double.parseDouble(value); + return true; + } catch (Exception e) { + return false; + } + } + + @Override + public void validate(Object value) throws InvalidValueException { + if (value != null && value instanceof Double) { + // Allow Doubles to pass through the validator for easier + // migration. Otherwise a TextField connected to an double property + // with a DoubleValidator will fail. + return; + } + + super.validate(value); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/EmailValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/EmailValidator.java new file mode 100644 index 0000000000..7d87eda52e --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/EmailValidator.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.v7.data.validator; + +/** + * String validator for e-mail addresses. The e-mail address syntax is not + * complete according to RFC 822 but handles the vast majority of valid e-mail + * addresses correctly. + * + * See {@link com.vaadin.v7.data.validator.AbstractStringValidator} + * for more information. + * + *

    + * An empty string or a null is always accepted - use the required flag on + * fields or a separate validator (or override {@link #isValidValue(String)}) to + * fail on empty values. + *

    + * + * @author Vaadin Ltd. + * @since 5.4 + */ +@SuppressWarnings("serial") +public class EmailValidator extends RegexpValidator { + + /** + * Creates a validator for checking that a string is a syntactically valid + * e-mail address. + * + * @param errorMessage + * the message to display in case the value does not validate. + */ + public EmailValidator(String errorMessage) { + super("^([a-zA-Z0-9_\\.\\-+])+@(([a-zA-Z0-9-])+\\.)+([a-zA-Z0-9]{2,4})+$", + true, errorMessage); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/FloatRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/FloatRangeValidator.java new file mode 100644 index 0000000000..c63b4a51a2 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/FloatRangeValidator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * Validator for validating that a {@link Float} is inside a given range. + * + * @author Vaadin Ltd. + * @since 7.4 + */ +@SuppressWarnings("serial") +public class FloatRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that an Float is within a given range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public FloatRangeValidator(String errorMessage, Float minValue, + Float maxValue) { + super(errorMessage, Float.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerRangeValidator.java new file mode 100644 index 0000000000..48fa420f06 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerRangeValidator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * Validator for validating that an {@link Integer} is inside a given range. + * + * @author Vaadin Ltd. + * @since 5.4 + */ +@SuppressWarnings("serial") +public class IntegerRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that an Integer is within a given range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public IntegerRangeValidator(String errorMessage, Integer minValue, + Integer maxValue) { + super(errorMessage, Integer.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerValidator.java new file mode 100644 index 0000000000..b5f671b6f1 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/IntegerValidator.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.converter.StringToIntegerConverter; + +/** + * String validator for integers. See + * {@link com.vaadin.v7.data.validator.AbstractStringValidator} for + * more information. + * + * @author Vaadin Ltd. + * @since 5.4 + * @deprecated As of 7.0. Use a {@link StringToIntegerConverter} converter + * on the field instead or bind the field to a {@link Property} of + * type {@link Integer}. + */ +@SuppressWarnings("serial") +@Deprecated +public class IntegerValidator extends AbstractStringValidator { + + /** + * Creates a validator for checking that a string can be parsed as an + * integer. + * + * @param errorMessage + * the message to display in case the value does not validate. + * @deprecated As of 7.0. Use an Integer converter on the field instead + * and/or use an {@link IntegerRangeValidator} for + * validating that the value is inside a given range. + */ + @Deprecated + public IntegerValidator(String errorMessage) { + super(errorMessage); + + } + + @Override + protected boolean isValidValue(String value) { + try { + Integer.parseInt(value); + return true; + } catch (Exception e) { + return false; + } + } + + @Override + public void validate(Object value) throws InvalidValueException { + if (value != null && value instanceof Integer) { + // Allow Integers to pass through the validator for easier + // migration. Otherwise a TextField connected to an integer property + // with an IntegerValidator will fail. + return; + } + + super.validate(value); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractStringValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractStringValidator.java deleted file mode 100644 index c0eb3b5fa3..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractStringValidator.java +++ /dev/null @@ -1,54 +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.v7.data.validator; - -/** - * Validator base class for validating strings. - *

    - * To include the value that failed validation in the exception message you can - * use "{0}" in the error message. This will be replaced with the failed value - * (converted to string using {@link #toString()}) or "null" if the value is - * null. - *

    - * - * @author Vaadin Ltd. - * @since 5.4 - */ -@SuppressWarnings("serial") -public abstract class LegacyAbstractStringValidator - extends LegacyAbstractValidator { - - /** - * Constructs a validator for strings. - * - *

    - * Null and empty string values are always accepted. To reject empty values, - * set the field being validated as required. - *

    - * - * @param errorMessage - * the message to be included in an {@link InvalidValueException} - * (with "{0}" replaced by the value that failed validation). - */ - public LegacyAbstractStringValidator(String errorMessage) { - super(errorMessage); - } - - @Override - public Class getType() { - return String.class; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractValidator.java deleted file mode 100644 index 8c1cc56010..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyAbstractValidator.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.v7.data.validator; - -import com.vaadin.v7.data.Validator; - -/** - * Abstract {@link com.vaadin.v7.data.Validator Validator} implementation - * that provides a basic Validator implementation except the - * {@link #isValidValue(Object)} method. - *

    - * To include the value that failed validation in the exception message you can - * use "{0}" in the error message. This will be replaced with the failed value - * (converted to string using {@link #toString()}) or "null" if the value is - * null. - *

    - *

    - * The default implementation of AbstractValidator does not support HTML in - * error messages. To enable HTML support, override - * {@link InvalidValueException#getHtmlMessage()} and throw such exceptions from - * {@link #validate(Object)}. - *

    - *

    - * Since Vaadin 7, subclasses can either implement {@link #validate(Object)} - * directly or implement {@link #isValidValue(Object)} when migrating legacy - * applications. To check validity, {@link #validate(Object)} should be used. - *

    - * - * @param - * The type - * @author Vaadin Ltd. - * @since 5.4 - */ -public abstract class LegacyAbstractValidator implements Validator { - - /** - * Error message that is included in an {@link InvalidValueException} if - * such is thrown. - */ - private String errorMessage; - - /** - * Constructs a validator with the given error message. - * - * @param errorMessage - * the message to be included in an {@link InvalidValueException} - * (with "{0}" replaced by the value that failed validation). - */ - public LegacyAbstractValidator(String errorMessage) { - this.errorMessage = errorMessage; - } - - /** - * Since Vaadin 7, subclasses of AbstractValidator should override - * {@link #isValidValue(Object)} or {@link #validate(Object)} instead of - * {@link #isValid(Object)}. {@link #validate(Object)} should normally be - * used to check values. - * - * @param value - * @return true if the value is valid - */ - public boolean isValid(Object value) { - try { - validate(value); - return true; - } catch (InvalidValueException e) { - return false; - } - } - - /** - * Internally check the validity of a value. This method can be used to - * perform validation in subclasses if customization of the error message is - * not needed. Otherwise, subclasses should override - * {@link #validate(Object)} and the return value of this method is ignored. - * - * This method should not be called from outside the validator class itself. - * - * @param value - * @return - */ - protected abstract boolean isValidValue(T value); - - @Override - public void validate(Object value) throws InvalidValueException { - // isValidType ensures that value can safely be cast to TYPE - if (!isValidType(value) || !isValidValue((T) value)) { - String message = getErrorMessage().replace("{0}", - String.valueOf(value)); - throw new InvalidValueException(message); - } - } - - /** - * Checks the type of the value to validate to ensure it conforms with - * getType. Enables sub classes to handle the specific type instead of - * Object. - * - * @param value - * The value to check - * @return true if the value can safely be cast to the type specified by - * {@link #getType()} - */ - protected boolean isValidType(Object value) { - if (value == null) { - return true; - } - - return getType().isAssignableFrom(value.getClass()); - } - - /** - * Returns the message to be included in the exception in case the value - * does not validate. - * - * @return the error message provided in the constructor or using - * {@link #setErrorMessage(String)}. - */ - public String getErrorMessage() { - return errorMessage; - } - - /** - * Sets the message to be included in the exception in case the value does - * not validate. The exception message is typically shown to the end user. - * - * @param errorMessage - * the error message. "{0}" is automatically replaced by the - * value that did not validate. - */ - public void setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - } - - public abstract Class getType(); -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBeanValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBeanValidator.java deleted file mode 100644 index dfc107280f..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBeanValidator.java +++ /dev/null @@ -1,184 +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.v7.data.validator; - -import java.io.Serializable; -import java.util.Locale; -import java.util.Set; - -import javax.validation.ConstraintViolation; -import javax.validation.MessageInterpolator.Context; -import javax.validation.Validation; -import javax.validation.ValidatorFactory; -import javax.validation.metadata.ConstraintDescriptor; - -import com.vaadin.v7.data.Validator; - -/** - * Vaadin {@link Validator} using the JSR-303 (javax.validation) - * annotation-based bean validation. - * - * The annotations of the fields of the beans are used to determine the - * validation to perform. - * - * Note that a JSR-303 implementation (e.g. Hibernate Validator or Apache Bean - * Validation - formerly agimatec validation) must be present on the project - * classpath when using bean validation. - * - * @since 7.0 - * - * @author Petri Hakala - * @author Henri Sara - */ -public class LegacyBeanValidator implements Validator { - - private static final long serialVersionUID = 1L; - private static ValidatorFactory factory; - - private transient javax.validation.Validator javaxBeanValidator; - private String propertyName; - private Class beanClass; - private Locale locale; - - /** - * Simple implementation of a message interpolator context that returns - * fixed values. - */ - protected static class SimpleContext implements Context, Serializable { - - private final Object value; - private final ConstraintDescriptor descriptor; - - /** - * Create a simple immutable message interpolator context. - * - * @param value - * value being validated - * @param descriptor - * ConstraintDescriptor corresponding to the constraint being - * validated - */ - public SimpleContext(Object value, ConstraintDescriptor descriptor) { - this.value = value; - this.descriptor = descriptor; - } - - @Override - public ConstraintDescriptor getConstraintDescriptor() { - return descriptor; - } - - @Override - public Object getValidatedValue() { - return value; - } - - } - - /** - * Creates a Vaadin {@link Validator} utilizing JSR-303 bean validation. - * - * @param beanClass - * bean class based on which the validation should be performed - * @param propertyName - * property to validate - */ - public LegacyBeanValidator(Class beanClass, String propertyName) { - this.beanClass = beanClass; - this.propertyName = propertyName; - locale = Locale.getDefault(); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.Validator#validate(java.lang.Object) - */ - @Override - public void validate(final Object value) throws InvalidValueException { - Set violations = getJavaxBeanValidator().validateValue(beanClass, - propertyName, value); - if (violations.size() > 0) { - InvalidValueException[] causes = new InvalidValueException[violations - .size()]; - int i = 0; - for (Object v : violations) { - final ConstraintViolation violation = (ConstraintViolation) v; - String msg = getJavaxBeanValidatorFactory() - .getMessageInterpolator() - .interpolate(violation.getMessageTemplate(), - new SimpleContext(value, - violation.getConstraintDescriptor()), - locale); - causes[i] = new InvalidValueException(msg); - ++i; - } - - throw new InvalidValueException(null, causes); - } - } - - /** - * Sets the locale used for validation error messages. - * - * Revalidation is not automatically triggered by setting the locale. - * - * @param locale - */ - public void setLocale(Locale locale) { - this.locale = locale; - } - - /** - * Gets the locale used for validation error messages. - * - * @return locale used for validation - */ - public Locale getLocale() { - return locale; - } - - /** - * Returns the underlying JSR-303 bean validator factory used. A factory is - * created using {@link Validation} if necessary. - * - * @return {@link ValidatorFactory} to use - */ - protected static ValidatorFactory getJavaxBeanValidatorFactory() { - if (factory == null) { - factory = Validation.buildDefaultValidatorFactory(); - } - - return factory; - } - - /** - * Returns a shared Validator instance to use. An instance is created using - * the validator factory if necessary and thereafter reused by the - * {@link LegacyBeanValidator} instance. - * - * @return the JSR-303 {@link javax.validation.Validator} to use - */ - protected javax.validation.Validator getJavaxBeanValidator() { - if (javaxBeanValidator == null) { - javaxBeanValidator = getJavaxBeanValidatorFactory().getValidator(); - } - - return javaxBeanValidator; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigDecimalRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigDecimalRangeValidator.java deleted file mode 100644 index 72c959e773..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigDecimalRangeValidator.java +++ /dev/null @@ -1,51 +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.v7.data.validator; - -import java.math.BigDecimal; - -/** - * Validator for validating that an {@link BigDecimal} is inside a given range. - * - * @author Vaadin Ltd. - * @since 7.4 - */ -@SuppressWarnings("serial") -public class LegacyBigDecimalRangeValidator - extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an BigDecimal is within a given - * range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyBigDecimalRangeValidator(String errorMessage, - BigDecimal minValue, BigDecimal maxValue) { - super(errorMessage, BigDecimal.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigIntegerRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigIntegerRangeValidator.java deleted file mode 100644 index 363a9bb82c..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyBigIntegerRangeValidator.java +++ /dev/null @@ -1,51 +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.v7.data.validator; - -import java.math.BigInteger; - -/** - * Validator for validating that an {@link BigInteger} is inside a given range. - * - * @author Vaadin Ltd. - * @since 7.4 - */ -@SuppressWarnings("serial") -public class LegacyBigIntegerRangeValidator - extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an BigInteger is within a given - * range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyBigIntegerRangeValidator(String errorMessage, - BigInteger minValue, BigInteger maxValue) { - super(errorMessage, BigInteger.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyByteRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyByteRangeValidator.java deleted file mode 100644 index 14694c4a52..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyByteRangeValidator.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.v7.data.validator; - -/** - * Validator for validating that an {@link Byte} is inside a given range. - * - * @author Vaadin Ltd. - * @since 7.4 - */ -@SuppressWarnings("serial") -public class LegacyByteRangeValidator extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an Byte is within a given range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyByteRangeValidator(String errorMessage, Byte minValue, - Byte maxValue) { - super(errorMessage, Byte.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyCompositeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyCompositeValidator.java deleted file mode 100644 index ecf4d121f2..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyCompositeValidator.java +++ /dev/null @@ -1,270 +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.v7.data.validator; - -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; - -import com.vaadin.v7.data.Validator; - -/** - * The CompositeValidator allows you to chain (compose) many - * validators to validate one field. The contained validators may be required to - * all validate the value to validate or it may be enough that one contained - * validator validates the value. This behaviour is controlled by the modes - * AND and OR. - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class LegacyCompositeValidator implements Validator { - - public enum CombinationMode { - /** - * The validators are combined with AND clause: validity of - * the composite implies validity of the all validators it is composed - * of must be valid. - */ - AND, - /** - * The validators are combined with OR clause: validity of - * the composite implies that some of validators it is composed of must - * be valid. - */ - OR; - } - - /** - * @deprecated As of 7.0, use {@link CombinationMode#AND} instead     - */ - @Deprecated - public static final CombinationMode MODE_AND = CombinationMode.AND; - /** - * @deprecated As of 7.0, use {@link CombinationMode#OR} instead     - */ - @Deprecated - public static final CombinationMode MODE_OR = CombinationMode.OR; - - private String errorMessage; - - /** - * Operation mode. - */ - private CombinationMode mode = CombinationMode.AND; - - /** - * List of contained validators. - */ - private final List validators = new LinkedList(); - - /** - * Construct a composite validator in AND mode without error - * message. - */ - public LegacyCompositeValidator() { - this(CombinationMode.AND, ""); - } - - /** - * Constructs a composite validator in given mode. - * - * @param mode - * @param errorMessage - */ - public LegacyCompositeValidator(CombinationMode mode, String errorMessage) { - setErrorMessage(errorMessage); - setMode(mode); - } - - /** - * Validates the given value. - *

    - * The value is valid, if: - *

      - *
    • MODE_AND: All of the sub-validators are valid - *
    • MODE_OR: Any of the sub-validators are valid - *
    - * - * If the value is invalid, validation error is thrown. If the error message - * is set (non-null), it is used. If the error message has not been set, the - * first error occurred is thrown. - *

    - * - * @param value - * the value to check. - * @throws Validator.InvalidValueException - * if the value is not valid. - */ - @Override - public void validate(Object value) throws Validator.InvalidValueException { - switch (mode) { - case AND: - for (Validator validator : validators) { - validator.validate(value); - } - return; - - case OR: - Validator.InvalidValueException first = null; - for (Validator v : validators) { - try { - v.validate(value); - return; - } catch (final Validator.InvalidValueException e) { - if (first == null) { - first = e; - } - } - } - if (first == null) { - return; - } - final String em = getErrorMessage(); - if (em != null) { - throw new Validator.InvalidValueException(em); - } else { - throw first; - } - } - } - - /** - * Gets the mode of the validator. - * - * @return Operation mode of the validator: {@link CombinationMode#AND} or - * {@link CombinationMode#OR}. - */ - public final CombinationMode getMode() { - return mode; - } - - /** - * Sets the mode of the validator. The valid modes are: - *
      - *
    • {@link CombinationMode#AND} (default) - *
    • {@link CombinationMode#OR} - *
    - * - * @param mode - * the mode to set. - */ - public void setMode(CombinationMode mode) { - if (mode == null) { - throw new IllegalArgumentException( - "The validator can't be set to null"); - } - this.mode = mode; - } - - /** - * Gets the error message for the composite validator. If the error message - * is null, original error messages of the sub-validators are used instead. - */ - public String getErrorMessage() { - if (errorMessage != null) { - return errorMessage; - } - - // TODO Return composite error message - - return null; - } - - /** - * Adds validator to the interface. - * - * @param validator - * the Validator object which performs validation checks on this - * set of data field values. - */ - public void addValidator(Validator validator) { - if (validator == null) { - return; - } - validators.add(validator); - } - - /** - * Removes a validator from the composite. - * - * @param validator - * the Validator object which performs validation checks on this - * set of data field values. - */ - public void removeValidator(Validator validator) { - validators.remove(validator); - } - - /** - * Gets sub-validators by class. - * - *

    - * If the component contains directly or recursively (it contains another - * composite containing the validator) validators compatible with given type - * they are returned. This only applies to AND mode composite - * validators. - *

    - * - *

    - * If the validator is in OR mode or does not contain any - * validators of given type null is returned. - *

    - * - * @param validatorType - * The type of validators to return - * - * @return Collection of validators compatible with given type - * that must apply or null if none found. - */ - public Collection getSubValidators(Class validatorType) { - if (mode != CombinationMode.AND) { - return null; - } - - final HashSet found = new HashSet(); - for (Validator v : validators) { - if (validatorType.isAssignableFrom(v.getClass())) { - found.add(v); - } - if (v instanceof LegacyCompositeValidator - && ((LegacyCompositeValidator) v).getMode() == MODE_AND) { - final Collection c = ((LegacyCompositeValidator) v) - .getSubValidators(validatorType); - if (c != null) { - found.addAll(c); - } - } - } - - return found.isEmpty() ? null : found; - } - - /** - * Sets the message to be included in the exception in case the value does - * not validate. The exception message is typically shown to the end user. - * - * @param errorMessage - * the error message. - */ - public void setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDateRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDateRangeValidator.java deleted file mode 100644 index 94cc2f78e7..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDateRangeValidator.java +++ /dev/null @@ -1,61 +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.v7.data.validator; - -import java.util.Date; - -import com.vaadin.shared.ui.datefield.Resolution; - -/** - * Validator for validating that a Date is inside a given range. - * - *

    - * Note that the comparison is done directly on the Date object so take care - * that the hours/minutes/seconds/milliseconds of the min/max values are - * properly set. - *

    - * - * @author Vaadin Ltd. - * @since 7.0 - */ -public class LegacyDateRangeValidator extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an Date is within a given range. - *

    - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - *

    - *

    - * Note that the comparison is done directly on the Date object so take care - * that the hours/minutes/seconds/milliseconds of the min/max values are - * properly set. - *

    - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyDateRangeValidator(String errorMessage, Date minValue, - Date maxValue, Resolution resolution) { - super(errorMessage, Date.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleRangeValidator.java deleted file mode 100644 index 7416ad60e6..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleRangeValidator.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.v7.data.validator; - -/** - * Validator for validating that a {@link Double} is inside a given range. - * - * @author Vaadin Ltd. - * @since 7.0 - */ -@SuppressWarnings("serial") -public class LegacyDoubleRangeValidator extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an Double is within a given range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyDoubleRangeValidator(String errorMessage, Double minValue, - Double maxValue) { - super(errorMessage, Double.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleValidator.java deleted file mode 100644 index fcc94f6e7c..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyDoubleValidator.java +++ /dev/null @@ -1,73 +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.v7.data.validator; - -import com.vaadin.data.Property; -import com.vaadin.v7.data.util.converter.LegacyStringToDoubleConverter; - -/** - * String validator for a double precision floating point number. See - * {@link com.vaadin.v7.data.validator.LegacyAbstractStringValidator} for - * more information. - * - * @author Vaadin Ltd. - * @since 5.4 - * @deprecated As of 7.0. Use a {@link LegacyStringToDoubleConverter} converter - * on the field instead or bind the field to a {@link Property} of - * type {@link Double}. - */ -@Deprecated -@SuppressWarnings("serial") -public class LegacyDoubleValidator extends LegacyAbstractStringValidator { - - /** - * Creates a validator for checking that a string can be parsed as an - * double. - * - * @param errorMessage - * the message to display in case the value does not validate. - * @deprecated As of 7.0. Use a Double converter on the field instead and/or - * use a {@link LegacyDoubleRangeValidator} for validating that - * the value is inside a given range. - */ - @Deprecated - public LegacyDoubleValidator(String errorMessage) { - super(errorMessage); - } - - @Override - protected boolean isValidValue(String value) { - try { - Double.parseDouble(value); - return true; - } catch (Exception e) { - return false; - } - } - - @Override - public void validate(Object value) throws InvalidValueException { - if (value != null && value instanceof Double) { - // Allow Doubles to pass through the validator for easier - // migration. Otherwise a TextField connected to an double property - // with a DoubleValidator will fail. - return; - } - - super.validate(value); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyEmailValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyEmailValidator.java deleted file mode 100644 index 836ff5ff15..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyEmailValidator.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.v7.data.validator; - -/** - * String validator for e-mail addresses. The e-mail address syntax is not - * complete according to RFC 822 but handles the vast majority of valid e-mail - * addresses correctly. - * - * See {@link com.vaadin.v7.data.validator.LegacyAbstractStringValidator} - * for more information. - * - *

    - * An empty string or a null is always accepted - use the required flag on - * fields or a separate validator (or override {@link #isValidValue(String)}) to - * fail on empty values. - *

    - * - * @author Vaadin Ltd. - * @since 5.4 - */ -@SuppressWarnings("serial") -public class LegacyEmailValidator extends LegacyRegexpValidator { - - /** - * Creates a validator for checking that a string is a syntactically valid - * e-mail address. - * - * @param errorMessage - * the message to display in case the value does not validate. - */ - public LegacyEmailValidator(String errorMessage) { - super("^([a-zA-Z0-9_\\.\\-+])+@(([a-zA-Z0-9-])+\\.)+([a-zA-Z0-9]{2,4})+$", - true, errorMessage); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyFloatRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyFloatRangeValidator.java deleted file mode 100644 index 9dc27d3727..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyFloatRangeValidator.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.v7.data.validator; - -/** - * Validator for validating that a {@link Float} is inside a given range. - * - * @author Vaadin Ltd. - * @since 7.4 - */ -@SuppressWarnings("serial") -public class LegacyFloatRangeValidator extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an Float is within a given range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyFloatRangeValidator(String errorMessage, Float minValue, - Float maxValue) { - super(errorMessage, Float.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerRangeValidator.java deleted file mode 100644 index 09de051e1d..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerRangeValidator.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.v7.data.validator; - -/** - * Validator for validating that an {@link Integer} is inside a given range. - * - * @author Vaadin Ltd. - * @since 5.4 - */ -@SuppressWarnings("serial") -public class LegacyIntegerRangeValidator extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an Integer is within a given range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyIntegerRangeValidator(String errorMessage, Integer minValue, - Integer maxValue) { - super(errorMessage, Integer.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerValidator.java deleted file mode 100644 index 34d2e7e76b..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyIntegerValidator.java +++ /dev/null @@ -1,73 +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.v7.data.validator; - -import com.vaadin.data.Property; -import com.vaadin.v7.data.util.converter.LegacyStringToIntegerConverter; - -/** - * String validator for integers. See - * {@link com.vaadin.v7.data.validator.LegacyAbstractStringValidator} for - * more information. - * - * @author Vaadin Ltd. - * @since 5.4 - * @deprecated As of 7.0. Use a {@link LegacyStringToIntegerConverter} converter - * on the field instead or bind the field to a {@link Property} of - * type {@link Integer}. - */ -@SuppressWarnings("serial") -@Deprecated -public class LegacyIntegerValidator extends LegacyAbstractStringValidator { - - /** - * Creates a validator for checking that a string can be parsed as an - * integer. - * - * @param errorMessage - * the message to display in case the value does not validate. - * @deprecated As of 7.0. Use an Integer converter on the field instead - * and/or use an {@link LegacyIntegerRangeValidator} for - * validating that the value is inside a given range. - */ - @Deprecated - public LegacyIntegerValidator(String errorMessage) { - super(errorMessage); - - } - - @Override - protected boolean isValidValue(String value) { - try { - Integer.parseInt(value); - return true; - } catch (Exception e) { - return false; - } - } - - @Override - public void validate(Object value) throws InvalidValueException { - if (value != null && value instanceof Integer) { - // Allow Integers to pass through the validator for easier - // migration. Otherwise a TextField connected to an integer property - // with an IntegerValidator will fail. - return; - } - - super.validate(value); - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyLongRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyLongRangeValidator.java deleted file mode 100644 index 8e5523b46a..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyLongRangeValidator.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.v7.data.validator; - -/** - * Validator for validating that an {@link Long} is inside a given range. - * - * @author Vaadin Ltd. - * @since 7.4 - */ -@SuppressWarnings("serial") -public class LegacyLongRangeValidator extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an Long is within a given range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyLongRangeValidator(String errorMessage, Long minValue, - Long maxValue) { - super(errorMessage, Long.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyNullValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyNullValidator.java deleted file mode 100644 index 282f5baf29..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyNullValidator.java +++ /dev/null @@ -1,102 +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.v7.data.validator; - -import com.vaadin.v7.data.Validator; - -/** - * This validator is used for validating properties that do or do not allow null - * values. By default, nulls are not allowed. - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class LegacyNullValidator implements Validator { - - private boolean onlyNullAllowed; - - private String errorMessage; - - /** - * Creates a new NullValidator. - * - * @param errorMessage - * the error message to display on invalidation. - * @param onlyNullAllowed - * Are only nulls allowed? - */ - public LegacyNullValidator(String errorMessage, boolean onlyNullAllowed) { - setErrorMessage(errorMessage); - setNullAllowed(onlyNullAllowed); - } - - /** - * Validates the data given in value. - * - * @param value - * the value to validate. - * @throws Validator.InvalidValueException - * if the value was invalid. - */ - @Override - public void validate(Object value) throws Validator.InvalidValueException { - if ((onlyNullAllowed && value != null) - || (!onlyNullAllowed && value == null)) { - throw new Validator.InvalidValueException(errorMessage); - } - } - - /** - * Returns true if nulls are allowed otherwise - * false. - */ - public final boolean isNullAllowed() { - return onlyNullAllowed; - } - - /** - * Sets if nulls (and only nulls) are to be allowed. - * - * @param onlyNullAllowed - * If true, only nulls are allowed. If false only non-nulls are - * allowed. Do we allow nulls? - */ - public void setNullAllowed(boolean onlyNullAllowed) { - this.onlyNullAllowed = onlyNullAllowed; - } - - /** - * Gets the error message that is displayed in case the value is invalid. - * - * @return the Error Message. - */ - public String getErrorMessage() { - return errorMessage; - } - - /** - * Sets the error message to be displayed on invalid value. - * - * @param errorMessage - * the Error Message to set. - */ - public void setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRangeValidator.java deleted file mode 100644 index 27830be7dd..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRangeValidator.java +++ /dev/null @@ -1,198 +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.v7.data.validator; - -/** - * An base implementation for validating any objects that implement - * {@link Comparable}. - * - * Verifies that the value is of the given type and within the (optionally) - * given limits. Typically you want to use a sub class of this like - * {@link LegacyIntegerRangeValidator}, {@link LegacyDoubleRangeValidator} or - * {@link LegacyDateRangeValidator} in applications. - *

    - * Note that {@link LegacyRangeValidator} always accept null values. Make a - * field required to ensure that no empty values are accepted or override - * {@link #isValidValue(Comparable)}. - *

    - * - * @param - * The type of Number to validate. Must implement Comparable so that - * minimum and maximum checks work. - * @author Vaadin Ltd. - * @since 7.0 - */ -public class LegacyRangeValidator - extends LegacyAbstractValidator { - - private T minValue = null; - private boolean minValueIncluded = true; - private T maxValue = null; - private boolean maxValueIncluded = true; - private Class type; - - /** - * Creates a new range validator of the given type. - * - * @param errorMessage - * The error message to use if validation fails - * @param type - * The type of object the validator can validate. - * @param minValue - * The minimum value that should be accepted or null for no limit - * @param maxValue - * The maximum value that should be accepted or null for no limit - */ - public LegacyRangeValidator(String errorMessage, Class type, T minValue, - T maxValue) { - super(errorMessage); - this.type = type; - this.minValue = minValue; - this.maxValue = maxValue; - } - - /** - * Checks if the minimum value is part of the accepted range - * - * @return true if the minimum value is part of the range, false otherwise - */ - public boolean isMinValueIncluded() { - return minValueIncluded; - } - - /** - * Sets if the minimum value is part of the accepted range - * - * @param minValueIncluded - * true if the minimum value should be part of the range, false - * otherwise - */ - public void setMinValueIncluded(boolean minValueIncluded) { - this.minValueIncluded = minValueIncluded; - } - - /** - * Checks if the maximum value is part of the accepted range - * - * @return true if the maximum value is part of the range, false otherwise - */ - public boolean isMaxValueIncluded() { - return maxValueIncluded; - } - - /** - * Sets if the maximum value is part of the accepted range - * - * @param maxValueIncluded - * true if the maximum value should be part of the range, false - * otherwise - */ - public void setMaxValueIncluded(boolean maxValueIncluded) { - this.maxValueIncluded = maxValueIncluded; - } - - /** - * Gets the minimum value of the range - * - * @return the minimum value - */ - public T getMinValue() { - return minValue; - } - - /** - * Sets the minimum value of the range. Use - * {@link #setMinValueIncluded(boolean)} to control whether this value is - * part of the range or not. - * - * @param minValue - * the minimum value - */ - public void setMinValue(T minValue) { - this.minValue = minValue; - } - - /** - * Gets the maximum value of the range - * - * @return the maximum value - */ - public T getMaxValue() { - return maxValue; - } - - /** - * Sets the maximum value of the range. Use - * {@link #setMaxValueIncluded(boolean)} to control whether this value is - * part of the range or not. - * - * @param maxValue - * the maximum value - */ - public void setMaxValue(T maxValue) { - this.maxValue = maxValue; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object - * ) - */ - @Override - protected boolean isValidValue(T value) { - if (value == null - || (String.class.equals(getType()) && "".equals(value))) { - return true; - } - - if (getMinValue() != null) { - // Ensure that the min limit is ok - int result = value.compareTo(getMinValue()); - if (result < 0) { - // value less than min value - return false; - } else if (result == 0 && !isMinValueIncluded()) { - // values equal and min value not included - return false; - } - } - if (getMaxValue() != null) { - // Ensure that the Max limit is ok - int result = value.compareTo(getMaxValue()); - if (result > 0) { - // value greater than max value - return false; - } else if (result == 0 && !isMaxValueIncluded()) { - // values equal and max value not included - return false; - } - } - return true; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.data.validator.AbstractValidator#getType() - */ - @Override - public Class getType() { - return type; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRegexpValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRegexpValidator.java deleted file mode 100644 index 0649dd2f13..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyRegexpValidator.java +++ /dev/null @@ -1,116 +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.v7.data.validator; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * String validator comparing the string against a Java regular expression. Both - * complete matches and substring matches are supported. - * - *

    - * For the Java regular expression syntax, see - * {@link java.util.regex.Pattern#sum} - *

    - *

    - * See {@link com.vaadin.v7.data.validator.LegacyAbstractStringValidator} - * for more information. - *

    - *

    - * An empty string or a null is always accepted - use the required flag on - * fields or a separate validator (or override {@link #isValidValue(String)}) to - * fail on empty values. - *

    - * - * @author Vaadin Ltd. - * @since 5.4 - */ -@SuppressWarnings("serial") -public class LegacyRegexpValidator extends LegacyAbstractStringValidator { - - private Pattern pattern; - private boolean complete; - private transient Matcher matcher = null; - - /** - * Creates a validator for checking that the regular expression matches the - * complete string to validate. - * - * @param regexp - * a Java regular expression - * @param errorMessage - * the message to display in case the value does not validate. - */ - public LegacyRegexpValidator(String regexp, String errorMessage) { - this(regexp, true, errorMessage); - } - - /** - * Creates a validator for checking that the regular expression matches the - * string to validate. - * - * @param regexp - * a Java regular expression - * @param complete - * true to use check for a complete match, false to look for a - * matching substring - * @param errorMessage - * the message to display in case the value does not validate. - */ - public LegacyRegexpValidator(String regexp, boolean complete, - String errorMessage) { - super(errorMessage); - pattern = Pattern.compile(regexp); - this.complete = complete; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object - * ) - */ - @Override - protected boolean isValidValue(String value) { - if (value == null || value.isEmpty()) { - return true; - } - if (complete) { - return getMatcher(value).matches(); - } else { - return getMatcher(value).find(); - } - } - - /** - * Get a new or reused matcher for the pattern - * - * @param value - * the string to find matches in - * @return Matcher for the string - */ - private Matcher getMatcher(String value) { - if (matcher == null) { - matcher = pattern.matcher(value); - } else { - matcher.reset(value); - } - return matcher; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyShortRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyShortRangeValidator.java deleted file mode 100644 index e23575e4d9..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyShortRangeValidator.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.v7.data.validator; - -/** - * Validator for validating that an {@link Short} is inside a given range. - * - * @author Vaadin Ltd. - * @since 7.4 - */ -@SuppressWarnings("serial") -public class LegacyShortRangeValidator extends LegacyRangeValidator { - - /** - * Creates a validator for checking that an Short is within a given range. - * - * By default the range is inclusive i.e. both minValue and maxValue are - * valid values. Use {@link #setMinValueIncluded(boolean)} or - * {@link #setMaxValueIncluded(boolean)} to change it. - * - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minValue - * The minimum value to accept or null for no limit - * @param maxValue - * The maximum value to accept or null for no limit - */ - public LegacyShortRangeValidator(String errorMessage, Short minValue, - Short maxValue) { - super(errorMessage, Short.class, minValue, maxValue); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyStringLengthValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyStringLengthValidator.java deleted file mode 100644 index 4bf7d430ba..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LegacyStringLengthValidator.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.v7.data.validator; - -/** - * This StringLengthValidator is used to validate the length of - * strings. - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class LegacyStringLengthValidator extends LegacyAbstractStringValidator { - - private Integer minLength = null; - - private Integer maxLength = null; - - private boolean allowNull = true; - - /** - * Creates a new StringLengthValidator with a given error message. - * - * @param errorMessage - * the message to display in case the value does not validate. - */ - public LegacyStringLengthValidator(String errorMessage) { - super(errorMessage); - } - - /** - * Creates a new StringLengthValidator with a given error message and - * minimum and maximum length limits. - * - * @param errorMessage - * the message to display in case the value does not validate. - * @param minLength - * the minimum permissible length of the string or null for no - * limit. A negative value for no limit is also supported for - * backwards compatibility. - * @param maxLength - * the maximum permissible length of the string or null for no - * limit. A negative value for no limit is also supported for - * backwards compatibility. - * @param allowNull - * Are null strings permissible? This can be handled better by - * setting a field as required or not. - */ - public LegacyStringLengthValidator(String errorMessage, Integer minLength, - Integer maxLength, boolean allowNull) { - this(errorMessage); - setMinLength(minLength); - setMaxLength(maxLength); - setNullAllowed(allowNull); - } - - /** - * Checks if the given value is valid. - * - * @param value - * the value to validate. - * @return true for valid value, otherwise false. - */ - @Override - protected boolean isValidValue(String value) { - if (value == null) { - return allowNull; - } - final int len = value.length(); - if ((minLength != null && minLength > -1 && len < minLength) - || (maxLength != null && maxLength > -1 && len > maxLength)) { - return false; - } - return true; - } - - /** - * Returns true if null strings are allowed. - * - * @return true if allows null string, otherwise - * false. - */ - @Deprecated - public final boolean isNullAllowed() { - return allowNull; - } - - /** - * Gets the maximum permissible length of the string. - * - * @return the maximum length of the string or null if there is no limit - */ - public Integer getMaxLength() { - return maxLength; - } - - /** - * Gets the minimum permissible length of the string. - * - * @return the minimum length of the string or null if there is no limit - */ - public Integer getMinLength() { - return minLength; - } - - /** - * Sets whether null-strings are to be allowed. This can be better handled - * by setting a field as required or not. - */ - @Deprecated - public void setNullAllowed(boolean allowNull) { - this.allowNull = allowNull; - } - - /** - * Sets the maximum permissible length of the string. - * - * @param maxLength - * the maximum length to accept or null for no limit - */ - public void setMaxLength(Integer maxLength) { - this.maxLength = maxLength; - } - - /** - * Sets the minimum permissible length. - * - * @param minLength - * the minimum length to accept or null for no limit - */ - public void setMinLength(Integer minLength) { - this.minLength = minLength; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LongRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LongRangeValidator.java new file mode 100644 index 0000000000..440961430c --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/LongRangeValidator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * Validator for validating that an {@link Long} is inside a given range. + * + * @author Vaadin Ltd. + * @since 7.4 + */ +@SuppressWarnings("serial") +public class LongRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that an Long is within a given range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public LongRangeValidator(String errorMessage, Long minValue, + Long maxValue) { + super(errorMessage, Long.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/NullValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/NullValidator.java new file mode 100644 index 0000000000..60e1cb4550 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/NullValidator.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.data.validator; + +import com.vaadin.v7.data.Validator; + +/** + * This validator is used for validating properties that do or do not allow null + * values. By default, nulls are not allowed. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class NullValidator implements Validator { + + private boolean onlyNullAllowed; + + private String errorMessage; + + /** + * Creates a new NullValidator. + * + * @param errorMessage + * the error message to display on invalidation. + * @param onlyNullAllowed + * Are only nulls allowed? + */ + public NullValidator(String errorMessage, boolean onlyNullAllowed) { + setErrorMessage(errorMessage); + setNullAllowed(onlyNullAllowed); + } + + /** + * Validates the data given in value. + * + * @param value + * the value to validate. + * @throws Validator.InvalidValueException + * if the value was invalid. + */ + @Override + public void validate(Object value) throws Validator.InvalidValueException { + if ((onlyNullAllowed && value != null) + || (!onlyNullAllowed && value == null)) { + throw new Validator.InvalidValueException(errorMessage); + } + } + + /** + * Returns true if nulls are allowed otherwise + * false. + */ + public final boolean isNullAllowed() { + return onlyNullAllowed; + } + + /** + * Sets if nulls (and only nulls) are to be allowed. + * + * @param onlyNullAllowed + * If true, only nulls are allowed. If false only non-nulls are + * allowed. Do we allow nulls? + */ + public void setNullAllowed(boolean onlyNullAllowed) { + this.onlyNullAllowed = onlyNullAllowed; + } + + /** + * Gets the error message that is displayed in case the value is invalid. + * + * @return the Error Message. + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Sets the error message to be displayed on invalid value. + * + * @param errorMessage + * the Error Message to set. + */ + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/RangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/RangeValidator.java new file mode 100644 index 0000000000..c4749ae2a6 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/RangeValidator.java @@ -0,0 +1,198 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * An base implementation for validating any objects that implement + * {@link Comparable}. + * + * Verifies that the value is of the given type and within the (optionally) + * given limits. Typically you want to use a sub class of this like + * {@link IntegerRangeValidator}, {@link DoubleRangeValidator} or + * {@link DateRangeValidator} in applications. + *

    + * Note that {@link RangeValidator} always accept null values. Make a + * field required to ensure that no empty values are accepted or override + * {@link #isValidValue(Comparable)}. + *

    + * + * @param + * The type of Number to validate. Must implement Comparable so that + * minimum and maximum checks work. + * @author Vaadin Ltd. + * @since 7.0 + */ +public class RangeValidator + extends AbstractValidator { + + private T minValue = null; + private boolean minValueIncluded = true; + private T maxValue = null; + private boolean maxValueIncluded = true; + private Class type; + + /** + * Creates a new range validator of the given type. + * + * @param errorMessage + * The error message to use if validation fails + * @param type + * The type of object the validator can validate. + * @param minValue + * The minimum value that should be accepted or null for no limit + * @param maxValue + * The maximum value that should be accepted or null for no limit + */ + public RangeValidator(String errorMessage, Class type, T minValue, + T maxValue) { + super(errorMessage); + this.type = type; + this.minValue = minValue; + this.maxValue = maxValue; + } + + /** + * Checks if the minimum value is part of the accepted range + * + * @return true if the minimum value is part of the range, false otherwise + */ + public boolean isMinValueIncluded() { + return minValueIncluded; + } + + /** + * Sets if the minimum value is part of the accepted range + * + * @param minValueIncluded + * true if the minimum value should be part of the range, false + * otherwise + */ + public void setMinValueIncluded(boolean minValueIncluded) { + this.minValueIncluded = minValueIncluded; + } + + /** + * Checks if the maximum value is part of the accepted range + * + * @return true if the maximum value is part of the range, false otherwise + */ + public boolean isMaxValueIncluded() { + return maxValueIncluded; + } + + /** + * Sets if the maximum value is part of the accepted range + * + * @param maxValueIncluded + * true if the maximum value should be part of the range, false + * otherwise + */ + public void setMaxValueIncluded(boolean maxValueIncluded) { + this.maxValueIncluded = maxValueIncluded; + } + + /** + * Gets the minimum value of the range + * + * @return the minimum value + */ + public T getMinValue() { + return minValue; + } + + /** + * Sets the minimum value of the range. Use + * {@link #setMinValueIncluded(boolean)} to control whether this value is + * part of the range or not. + * + * @param minValue + * the minimum value + */ + public void setMinValue(T minValue) { + this.minValue = minValue; + } + + /** + * Gets the maximum value of the range + * + * @return the maximum value + */ + public T getMaxValue() { + return maxValue; + } + + /** + * Sets the maximum value of the range. Use + * {@link #setMaxValueIncluded(boolean)} to control whether this value is + * part of the range or not. + * + * @param maxValue + * the maximum value + */ + public void setMaxValue(T maxValue) { + this.maxValue = maxValue; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object + * ) + */ + @Override + protected boolean isValidValue(T value) { + if (value == null + || (String.class.equals(getType()) && "".equals(value))) { + return true; + } + + if (getMinValue() != null) { + // Ensure that the min limit is ok + int result = value.compareTo(getMinValue()); + if (result < 0) { + // value less than min value + return false; + } else if (result == 0 && !isMinValueIncluded()) { + // values equal and min value not included + return false; + } + } + if (getMaxValue() != null) { + // Ensure that the Max limit is ok + int result = value.compareTo(getMaxValue()); + if (result > 0) { + // value greater than max value + return false; + } else if (result == 0 && !isMaxValueIncluded()) { + // values equal and max value not included + return false; + } + } + return true; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.validator.AbstractValidator#getType() + */ + @Override + public Class getType() { + return type; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/RegexpValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/RegexpValidator.java new file mode 100644 index 0000000000..a9af0d0167 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/RegexpValidator.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * String validator comparing the string against a Java regular expression. Both + * complete matches and substring matches are supported. + * + *

    + * For the Java regular expression syntax, see + * {@link java.util.regex.Pattern#sum} + *

    + *

    + * See {@link com.vaadin.v7.data.validator.AbstractStringValidator} + * for more information. + *

    + *

    + * An empty string or a null is always accepted - use the required flag on + * fields or a separate validator (or override {@link #isValidValue(String)}) to + * fail on empty values. + *

    + * + * @author Vaadin Ltd. + * @since 5.4 + */ +@SuppressWarnings("serial") +public class RegexpValidator extends AbstractStringValidator { + + private Pattern pattern; + private boolean complete; + private transient Matcher matcher = null; + + /** + * Creates a validator for checking that the regular expression matches the + * complete string to validate. + * + * @param regexp + * a Java regular expression + * @param errorMessage + * the message to display in case the value does not validate. + */ + public RegexpValidator(String regexp, String errorMessage) { + this(regexp, true, errorMessage); + } + + /** + * Creates a validator for checking that the regular expression matches the + * string to validate. + * + * @param regexp + * a Java regular expression + * @param complete + * true to use check for a complete match, false to look for a + * matching substring + * @param errorMessage + * the message to display in case the value does not validate. + */ + public RegexpValidator(String regexp, boolean complete, + String errorMessage) { + super(errorMessage); + pattern = Pattern.compile(regexp); + this.complete = complete; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.validator.AbstractValidator#isValidValue(java.lang.Object + * ) + */ + @Override + protected boolean isValidValue(String value) { + if (value == null || value.isEmpty()) { + return true; + } + if (complete) { + return getMatcher(value).matches(); + } else { + return getMatcher(value).find(); + } + } + + /** + * Get a new or reused matcher for the pattern + * + * @param value + * the string to find matches in + * @return Matcher for the string + */ + private Matcher getMatcher(String value) { + if (matcher == null) { + matcher = pattern.matcher(value); + } else { + matcher.reset(value); + } + return matcher; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/ShortRangeValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/ShortRangeValidator.java new file mode 100644 index 0000000000..2bf89cbca8 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/ShortRangeValidator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.data.validator; + +/** + * Validator for validating that an {@link Short} is inside a given range. + * + * @author Vaadin Ltd. + * @since 7.4 + */ +@SuppressWarnings("serial") +public class ShortRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that an Short is within a given range. + * + * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + * + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public ShortRangeValidator(String errorMessage, Short minValue, + Short maxValue) { + super(errorMessage, Short.class, minValue, maxValue); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/validator/StringLengthValidator.java b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/StringLengthValidator.java new file mode 100644 index 0000000000..dc863ab31a --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/validator/StringLengthValidator.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.v7.data.validator; + +/** + * This StringLengthValidator is used to validate the length of + * strings. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class StringLengthValidator extends AbstractStringValidator { + + private Integer minLength = null; + + private Integer maxLength = null; + + private boolean allowNull = true; + + /** + * Creates a new StringLengthValidator with a given error message. + * + * @param errorMessage + * the message to display in case the value does not validate. + */ + public StringLengthValidator(String errorMessage) { + super(errorMessage); + } + + /** + * Creates a new StringLengthValidator with a given error message and + * minimum and maximum length limits. + * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minLength + * the minimum permissible length of the string or null for no + * limit. A negative value for no limit is also supported for + * backwards compatibility. + * @param maxLength + * the maximum permissible length of the string or null for no + * limit. A negative value for no limit is also supported for + * backwards compatibility. + * @param allowNull + * Are null strings permissible? This can be handled better by + * setting a field as required or not. + */ + public StringLengthValidator(String errorMessage, Integer minLength, + Integer maxLength, boolean allowNull) { + this(errorMessage); + setMinLength(minLength); + setMaxLength(maxLength); + setNullAllowed(allowNull); + } + + /** + * Checks if the given value is valid. + * + * @param value + * the value to validate. + * @return true for valid value, otherwise false. + */ + @Override + protected boolean isValidValue(String value) { + if (value == null) { + return allowNull; + } + final int len = value.length(); + if ((minLength != null && minLength > -1 && len < minLength) + || (maxLength != null && maxLength > -1 && len > maxLength)) { + return false; + } + return true; + } + + /** + * Returns true if null strings are allowed. + * + * @return true if allows null string, otherwise + * false. + */ + @Deprecated + public final boolean isNullAllowed() { + return allowNull; + } + + /** + * Gets the maximum permissible length of the string. + * + * @return the maximum length of the string or null if there is no limit + */ + public Integer getMaxLength() { + return maxLength; + } + + /** + * Gets the minimum permissible length of the string. + * + * @return the minimum length of the string or null if there is no limit + */ + public Integer getMinLength() { + return minLength; + } + + /** + * Sets whether null-strings are to be allowed. This can be better handled + * by setting a field as required or not. + */ + @Deprecated + public void setNullAllowed(boolean allowNull) { + this.allowNull = allowNull; + } + + /** + * Sets the maximum permissible length of the string. + * + * @param maxLength + * the maximum length to accept or null for no limit + */ + public void setMaxLength(Integer maxLength) { + this.maxLength = maxLength; + } + + /** + * Sets the minimum permissible length. + * + * @param minLength + * the minimum length to accept or null for no limit + */ + public void setMinLength(Integer minLength) { + this.minLength = minLength; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/DataGenerator.java b/compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/DataGenerator.java new file mode 100644 index 0000000000..ee71b587f9 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.server.communication.data; + +import java.io.Serializable; + +import com.vaadin.v7.data.Item; +import com.vaadin.v7.ui.Grid.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/v7/server/communication/data/RpcDataProviderExtension.java b/compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/RpcDataProviderExtension.java new file mode 100644 index 0000000000..dce3eb1cfa --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.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.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.v7.data.Container; +import com.vaadin.v7.data.Container.Indexed; +import com.vaadin.v7.data.Container.Indexed.ItemAddEvent; +import com.vaadin.v7.data.Container.Indexed.ItemRemoveEvent; +import com.vaadin.v7.data.Container.ItemSetChangeEvent; +import com.vaadin.v7.data.Container.ItemSetChangeListener; +import com.vaadin.v7.data.Container.ItemSetChangeNotifier; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.Property.ValueChangeEvent; +import com.vaadin.v7.data.Property.ValueChangeListener; +import com.vaadin.v7.data.Property.ValueChangeNotifier; +import com.vaadin.v7.ui.Grid; +import com.vaadin.v7.ui.Grid.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 Grid#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 Grid#addValueChangeListener(Container, Object, Object) + * @see Grid#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 Grid} component. + * + * @param component + * the remote data grid component to extend + * @param columnKeys + * the key mapper for columns + */ + public void extend(Grid 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 Grid)) { + 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 Grid getGrid() { + return (Grid) getParent(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractColorPicker.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractColorPicker.java new file mode 100644 index 0000000000..da03136593 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractColorPicker.java @@ -0,0 +1,590 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Collection; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc; +import com.vaadin.shared.ui.colorpicker.ColorPickerState; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.UI; +import com.vaadin.ui.Window.CloseEvent; +import com.vaadin.ui.Window.CloseListener; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.v7.ui.components.colorpicker.ColorChangeEvent; +import com.vaadin.v7.ui.components.colorpicker.ColorChangeListener; +import com.vaadin.v7.ui.components.colorpicker.ColorPickerPopup; +import com.vaadin.v7.ui.components.colorpicker.ColorSelector; + +/** + * An abstract class that defines default implementation for a color picker + * component. + * + * @since 7.0.0 + */ +public abstract class AbstractColorPicker extends AbstractComponent + implements CloseListener, ColorSelector { + private static final Method COLOR_CHANGE_METHOD; + static { + try { + COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( + "colorChanged", new Class[] { ColorChangeEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException( + "Internal error finding methods in ColorPicker"); + } + } + + /** + * Interface for converting 2d-coordinates to a Color + */ + public interface Coordinates2Color extends Serializable { + + /** + * Calculate color from coordinates + * + * @param x + * the x-coordinate + * @param y + * the y-coordinate + * + * @return the color + */ + public Color calculate(int x, int y); + + /** + * Calculate coordinates from color + * + * @param c + * the c + * + * @return the integer array with the coordinates + */ + public int[] calculate(Color c); + } + + public enum PopupStyle { + POPUP_NORMAL("normal"), POPUP_SIMPLE("simple"); + + private String style; + + PopupStyle(String styleName) { + style = styleName; + } + + @Override + public String toString() { + return style; + } + } + + private ColorPickerServerRpc rpc = new ColorPickerServerRpc() { + + @Override + public void openPopup(boolean open) { + showPopup(open); + } + }; + + protected static final String STYLENAME_DEFAULT = "v-colorpicker"; + protected static final String STYLENAME_BUTTON = "v-button"; + protected static final String STYLENAME_AREA = "v-colorpicker-area"; + + protected PopupStyle popupStyle = PopupStyle.POPUP_NORMAL; + + /** The popup window. */ + private ColorPickerPopup window; + + /** The color. */ + protected Color color; + + /** The UI. */ + private UI parent; + + protected String popupCaption = null; + private int positionX = 0; + private int positionY = 0; + + protected boolean rgbVisible = true; + protected boolean hsvVisible = true; + protected boolean swatchesVisible = true; + protected boolean historyVisible = true; + protected boolean textfieldVisible = true; + + /** + * Instantiates a new color picker. + */ + public AbstractColorPicker() { + this("Colors", Color.WHITE); + } + + /** + * Instantiates a new color picker. + * + * @param popupCaption + * the caption of the popup window + */ + public AbstractColorPicker(String popupCaption) { + this(popupCaption, Color.WHITE); + } + + /** + * Instantiates a new color picker. + * + * @param popupCaption + * the caption of the popup window + * @param initialColor + * the initial color + */ + public AbstractColorPicker(String popupCaption, Color initialColor) { + super(); + registerRpc(rpc); + setColor(initialColor); + this.popupCaption = popupCaption; + setDefaultStyles(); + setCaption(""); + } + + @Override + public void setColor(Color color) { + this.color = color; + + if (window != null) { + window.setColor(color); + } + getState().color = color.getCSS(); + } + + @Override + public Color getColor() { + return color; + } + + /** + * Set true if the component should show a default caption (css-code for the + * currently selected color, e.g. #ffffff) when no other caption is + * available. + * + * @param enabled + */ + public void setDefaultCaptionEnabled(boolean enabled) { + getState().showDefaultCaption = enabled; + } + + /** + * Returns true if the component shows the default caption (css-code for the + * currently selected color, e.g. #ffffff) if no other caption is available. + */ + public boolean isDefaultCaptionEnabled() { + return getState(false).showDefaultCaption; + } + + /** + * Sets the position of the popup window + * + * @param x + * the x-coordinate + * @param y + * the y-coordinate + */ + public void setPosition(int x, int y) { + positionX = x; + positionY = y; + + if (window != null) { + window.setPositionX(x); + window.setPositionY(y); + } + } + + @Override + public void addColorChangeListener(ColorChangeListener listener) { + addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); + } + + @Override + public void removeColorChangeListener(ColorChangeListener listener) { + removeListener(ColorChangeEvent.class, listener); + } + + @Override + public void windowClose(CloseEvent e) { + if (e.getWindow() == window) { + getState().popupVisible = false; + } + } + + /** + * Fired when a color change event occurs + * + * @param event + * The color change event + */ + protected void colorChanged(ColorChangeEvent event) { + setColor(event.getColor()); + fireColorChanged(); + } + + /** + * Notifies the listeners that the selected color has changed + */ + public void fireColorChanged() { + fireEvent(new ColorChangeEvent(this, color)); + } + + /** + * The style for the popup window + * + * @param style + * The style + */ + public void setPopupStyle(PopupStyle style) { + popupStyle = style; + + switch (style) { + case POPUP_NORMAL: { + setRGBVisibility(true); + setHSVVisibility(true); + setSwatchesVisibility(true); + setHistoryVisibility(true); + setTextfieldVisibility(true); + break; + } + + case POPUP_SIMPLE: { + setRGBVisibility(false); + setHSVVisibility(false); + setSwatchesVisibility(true); + setHistoryVisibility(false); + setTextfieldVisibility(false); + break; + } + } + } + + /** + * Gets the style for the popup window + * + * @since 7.5.0 + * @return popup window style + */ + public PopupStyle getPopupStyle() { + return popupStyle; + } + + /** + * Set the visibility of the RGB Tab + * + * @param visible + * The visibility + */ + public void setRGBVisibility(boolean visible) { + + if (!visible && !hsvVisible && !swatchesVisible) { + throw new IllegalArgumentException("Cannot hide all tabs."); + } + + rgbVisible = visible; + if (window != null) { + window.setRGBTabVisible(visible); + } + } + + /** + * Gets the visibility of the RGB Tab + * + * @since 7.5.0 + * @return visibility of the RGB tab + */ + public boolean getRGBVisibility() { + return rgbVisible; + } + + /** + * Set the visibility of the HSV Tab + * + * @param visible + * The visibility + */ + public void setHSVVisibility(boolean visible) { + if (!visible && !rgbVisible && !swatchesVisible) { + throw new IllegalArgumentException("Cannot hide all tabs."); + } + + hsvVisible = visible; + if (window != null) { + window.setHSVTabVisible(visible); + } + } + + /** + * Gets the visibility of the HSV Tab + * + * @since 7.5.0 + * @return visibility of the HSV tab + */ + public boolean getHSVVisibility() { + return hsvVisible; + } + + /** + * Set the visibility of the Swatches Tab + * + * @param visible + * The visibility + */ + public void setSwatchesVisibility(boolean visible) { + if (!visible && !hsvVisible && !rgbVisible) { + throw new IllegalArgumentException("Cannot hide all tabs."); + } + + swatchesVisible = visible; + if (window != null) { + window.setSwatchesTabVisible(visible); + } + } + + /** + * Gets the visibility of the Swatches Tab + * + * @since 7.5.0 + * @return visibility of the swatches tab + */ + public boolean getSwatchesVisibility() { + return swatchesVisible; + } + + /** + * Sets the visibility of the Color History + * + * @param visible + * The visibility + */ + public void setHistoryVisibility(boolean visible) { + historyVisible = visible; + if (window != null) { + window.setHistoryVisible(visible); + } + } + + /** + * Gets the visibility of the Color History + * + * @since 7.5.0 + * @return visibility of color history + */ + public boolean getHistoryVisibility() { + return historyVisible; + } + + /** + * Sets the visibility of the CSS color code text field + * + * @param visible + * The visibility + */ + public void setTextfieldVisibility(boolean visible) { + textfieldVisible = visible; + if (window != null) { + window.setPreviewVisible(visible); + } + } + + /** + * Gets the visibility of CSS color code text field + * + * @since 7.5.0 + * @return visibility of css color code text field + */ + public boolean getTextfieldVisibility() { + return textfieldVisible; + } + + @Override + protected ColorPickerState getState() { + return (ColorPickerState) super.getState(); + } + + @Override + protected ColorPickerState getState(boolean markAsDirty) { + return (ColorPickerState) super.getState(markAsDirty); + } + + /** + * Sets the default styles of the component + * + */ + abstract protected void setDefaultStyles(); + + /** + * Shows a popup-window for color selection. + */ + public void showPopup() { + showPopup(true); + } + + /** + * Hides a popup-window for color selection. + */ + public void hidePopup() { + showPopup(false); + } + + /** + * Shows or hides popup-window depending on the given parameter. If there is + * no such window yet, one is created. + * + * @param open + */ + protected void showPopup(boolean open) { + if (open && !isReadOnly()) { + if (parent == null) { + parent = getUI(); + } + + if (window == null) { + + // Create the popup + window = new ColorPickerPopup(color); + window.setCaption(popupCaption); + + window.setRGBTabVisible(rgbVisible); + window.setHSVTabVisible(hsvVisible); + window.setSwatchesTabVisible(swatchesVisible); + window.setHistoryVisible(historyVisible); + window.setPreviewVisible(textfieldVisible); + + window.setImmediate(true); + window.addCloseListener(this); + window.addColorChangeListener(new ColorChangeListener() { + @Override + public void colorChanged(ColorChangeEvent event) { + AbstractColorPicker.this.colorChanged(event); + } + }); + + window.getHistory().setColor(color); + parent.addWindow(window); + window.setVisible(true); + window.setPositionX(positionX); + window.setPositionY(positionY); + + } else if (!parent.equals(window.getParent())) { + + window.setRGBTabVisible(rgbVisible); + window.setHSVTabVisible(hsvVisible); + window.setSwatchesTabVisible(swatchesVisible); + window.setHistoryVisible(historyVisible); + window.setPreviewVisible(textfieldVisible); + + window.setColor(color); + window.getHistory().setColor(color); + window.setVisible(true); + parent.addWindow(window); + } + + } else if (window != null) { + window.setVisible(false); + parent.removeWindow(window); + } + getState().popupVisible = open; + } + + /** + * Set whether the caption text is rendered as HTML or not. You might need + * to re-theme component to allow higher content than the original text + * style. + * + * If set to true, the captions are passed to the browser as html and the + * developer is responsible for ensuring no harmful html is used. If set to + * false, the content is passed to the browser as plain text. + * + * @param htmlContentAllowed + * true if caption is rendered as HTML, + * false otherwise + * @deprecated as of , use {@link #setCaptionAsHtml(boolean)} instead + */ + @Deprecated + public void setHtmlContentAllowed(boolean htmlContentAllowed) { + setCaptionAsHtml(htmlContentAllowed); + } + + /** + * Return HTML rendering setting + * + * @return true if the caption text is to be rendered as HTML, + * false otherwise + * @deprecated as of , use {@link #isCaptionAsHtml()} instead + */ + @Deprecated + public boolean isHtmlContentAllowed() { + return isCaptionAsHtml(); + } + + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + Attributes attributes = design.attributes(); + if (design.hasAttr("color")) { + // Ignore the # character + String hexColor = DesignAttributeHandler + .readAttribute("color", attributes, String.class) + .substring(1); + setColor(new Color(Integer.parseInt(hexColor, 16))); + } + if (design.hasAttr("popup-style")) { + setPopupStyle(PopupStyle.valueOf( + "POPUP_" + attributes.get("popup-style").toUpperCase())); + } + if (design.hasAttr("position")) { + String[] position = attributes.get("position").split(","); + setPosition(Integer.parseInt(position[0]), + Integer.parseInt(position[1])); + } + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + Attributes attribute = design.attributes(); + DesignAttributeHandler.writeAttribute("color", attribute, + color.getCSS(), Color.WHITE.getCSS(), String.class); + DesignAttributeHandler.writeAttribute("popup-style", attribute, + (popupStyle == PopupStyle.POPUP_NORMAL ? "normal" : "simple"), + "normal", String.class); + DesignAttributeHandler.writeAttribute("position", attribute, + positionX + "," + positionY, "0,0", String.class); + } + + @Override + protected Collection getCustomAttributes() { + Collection result = super.getCustomAttributes(); + result.add("color"); + result.add("position"); + result.add("popup-style"); + return result; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractSelect.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractSelect.java new file mode 100644 index 0000000000..b7b241598b --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/AbstractSelect.java @@ -0,0 +1,2355 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EventObject; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jsoup.nodes.Element; + +import com.vaadin.event.DataBoundTransferable; +import com.vaadin.event.Transferable; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DropTarget; +import com.vaadin.event.dd.TargetDetailsImpl; +import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion; +import com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor; +import com.vaadin.event.dd.acceptcriteria.TargetDetailIs; +import com.vaadin.server.KeyMapper; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.server.Resource; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.ui.combobox.FilteringMode; +import com.vaadin.shared.ui.dd.VerticalDropLocation; +import com.vaadin.shared.ui.select.AbstractSelectState; +import com.vaadin.ui.Component; +import com.vaadin.ui.LegacyComponent; +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.v7.data.Container; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.data.util.IndexedContainer; +import com.vaadin.v7.data.util.converter.Converter; +import com.vaadin.v7.data.util.converter.Converter.ConversionException; +import com.vaadin.v7.data.util.converter.ConverterUtil; + +/** + *

    + * A class representing a selection of items the user has selected in a UI. The + * set of choices is presented as a set of {@link com.com.vaadin.v7.data.Item}s + * in a {@link com.com.vaadin.v7.data.Container}. + *

    + * + *

    + * A Select component may be in single- or multiselect mode. + * Multiselect mode means that more than one item can be selected + * simultaneously. + *

    + * + * @author Vaadin Ltd. + * @since 5.0 + */ +@SuppressWarnings("serial") +// TODO currently cannot specify type more precisely in case of multi-select +public abstract class AbstractSelect extends AbstractField + implements Container, Container.Viewer, + Container.PropertySetChangeListener, + Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier, + Container.ItemSetChangeListener, LegacyComponent { + + public enum ItemCaptionMode { + /** + * Item caption mode: Item's ID converted to a String using + * {@link VaadinSession#getConverterFactory()} is used as caption. + */ + ID, + /** + * Item caption mode: Item's ID's String representation is + * used as caption. + * + * @since 7.5.6 + */ + ID_TOSTRING, + /** + * Item caption mode: Item's String representation is used + * as caption. + */ + ITEM, + /** + * Item caption mode: Index of the item is used as caption. The index + * mode can only be used with the containers implementing the + * {@link com.vaadin.data.Container.Indexed} interface. + */ + INDEX, + /** + * Item caption mode: If an Item has a caption it's used, if not, Item's + * ID converted to a String using + * {@link VaadinSession#getConverterFactory()} is used as caption. + * This is the default. + */ + EXPLICIT_DEFAULTS_ID, + /** + * Item caption mode: Captions must be explicitly specified. + */ + EXPLICIT, + /** + * Item caption mode: Only icons are shown, captions are hidden. + */ + ICON_ONLY, + /** + * Item caption mode: Item captions are read from property specified + * with setItemCaptionPropertyId. + */ + PROPERTY; + } + + /** + * @deprecated As of 7.0, use {@link ItemCaptionMode#ID} instead + */ + @Deprecated + public static final ItemCaptionMode ITEM_CAPTION_MODE_ID = ItemCaptionMode.ID; + + /** + * @deprecated As of 7.0, use {@link ItemCaptionMode#ITEM} instead + */ + @Deprecated + public static final ItemCaptionMode ITEM_CAPTION_MODE_ITEM = ItemCaptionMode.ITEM; + + /** + * @deprecated As of 7.0, use {@link ItemCaptionMode#INDEX} instead + */ + @Deprecated + public static final ItemCaptionMode ITEM_CAPTION_MODE_INDEX = ItemCaptionMode.INDEX; + + /** + * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID} + * instead + */ + @Deprecated + public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = ItemCaptionMode.EXPLICIT_DEFAULTS_ID; + + /** + * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT} instead + */ + @Deprecated + public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT = ItemCaptionMode.EXPLICIT; + + /** + * @deprecated As of 7.0, use {@link ItemCaptionMode#ICON_ONLY} instead + */ + @Deprecated + public static final ItemCaptionMode ITEM_CAPTION_MODE_ICON_ONLY = ItemCaptionMode.ICON_ONLY; + + /** + * @deprecated As of 7.0, use {@link ItemCaptionMode#PROPERTY} instead + */ + @Deprecated + public static final ItemCaptionMode ITEM_CAPTION_MODE_PROPERTY = ItemCaptionMode.PROPERTY; + + /** + * Interface for option filtering, used to filter options based on user + * entered value. The value is matched to the item caption. + * FilteringMode.OFF (0) turns the filtering off. + * FilteringMode.STARTSWITH (1) matches from the start of the + * caption. FilteringMode.CONTAINS (1) matches anywhere in the + * caption. + */ + public interface Filtering extends Serializable { + + /** + * @deprecated As of 7.0, use {@link FilteringMode#OFF} instead + */ + @Deprecated + public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF; + /** + * @deprecated As of 7.0, use {@link FilteringMode#STARTSWITH} instead + */ + @Deprecated + public static final FilteringMode FILTERINGMODE_STARTSWITH = FilteringMode.STARTSWITH; + /** + * @deprecated As of 7.0, use {@link FilteringMode#CONTAINS} instead + */ + @Deprecated + public static final FilteringMode FILTERINGMODE_CONTAINS = FilteringMode.CONTAINS; + + /** + * Sets the option filtering mode. + * + * @param filteringMode + * the filtering mode to use + */ + public void setFilteringMode(FilteringMode filteringMode); + + /** + * Gets the current filtering mode. + * + * @return the filtering mode in use + */ + public FilteringMode getFilteringMode(); + + } + + /** + * Select options. + */ + protected Container items; + + /** + * Is the user allowed to add new options? + */ + private boolean allowNewOptions; + + /** + * Keymapper used to map key values. + */ + protected KeyMapper itemIdMapper = new KeyMapper(); + + /** + * Item icons. + */ + private final HashMap itemIcons = new HashMap(); + + /** + * Item captions. + */ + private final HashMap itemCaptions = new HashMap(); + + /** + * Item caption mode. + */ + private ItemCaptionMode itemCaptionMode = ItemCaptionMode.EXPLICIT_DEFAULTS_ID; + + /** + * Item caption source property id. + */ + private Object itemCaptionPropertyId = null; + + /** + * Item icon source property id. + */ + private Object itemIconPropertyId = null; + + /** + * List of property set change event listeners. + */ + private Set propertySetEventListeners = null; + + /** + * List of item set change event listeners. + */ + private Set itemSetEventListeners = null; + + /** + * Item id that represents null selection of this select. + * + *

    + * Data interface does not support nulls as item ids. Selecting the item + * identified by this id is the same as selecting no items at all. This + * setting only affects the single select mode. + *

    + */ + private Object nullSelectionItemId = null; + + // Null (empty) selection is enabled by default + private boolean nullSelectionAllowed = true; + private NewItemHandler newItemHandler; + + // Caption (Item / Property) change listeners + CaptionChangeListener captionChangeListener; + + /* Constructors */ + + /** + * Creates an empty Select. The caption is not used. + */ + public AbstractSelect() { + setContainerDataSource(new IndexedContainer()); + } + + /** + * Creates an empty Select with caption. + */ + public AbstractSelect(String caption) { + setContainerDataSource(new IndexedContainer()); + setCaption(caption); + } + + /** + * Creates a new select that is connected to a data-source. + * + * @param caption + * the Caption of the component. + * @param dataSource + * the Container datasource to be selected from by this select. + */ + public AbstractSelect(String caption, Container dataSource) { + setCaption(caption); + setContainerDataSource(dataSource); + } + + /** + * Creates a new select that is filled from a collection of option values. + * + * @param caption + * the Caption of this field. + * @param options + * the Collection containing the options. + */ + public AbstractSelect(String caption, Collection options) { + + // Creates the options container and add given options to it + final Container c = new IndexedContainer(); + if (options != null) { + for (final Iterator i = options.iterator(); i.hasNext();) { + c.addItem(i.next()); + } + } + + setCaption(caption); + setContainerDataSource(c); + } + + /* Component methods */ + + /** + * Paints the content of this component. + * + * @param target + * the Paint Event. + * @throws PaintException + * if the paint operation failed. + */ + @Override + public void paintContent(PaintTarget target) throws PaintException { + + // Paints select attributes + if (isNewItemsAllowed()) { + target.addAttribute("allownewitem", true); + } + if (isNullSelectionAllowed()) { + target.addAttribute("nullselect", true); + if (getNullSelectionItemId() != null) { + target.addAttribute("nullselectitem", true); + } + } + + // Constructs selected keys array + String[] selectedKeys; + if (isMultiSelect()) { + selectedKeys = new String[((Set) getValue()).size()]; + } else { + selectedKeys = new String[(getValue() == null + && getNullSelectionItemId() == null ? 0 : 1)]; + } + + // == + // first remove all previous item/property listeners + getCaptionChangeListener().clear(); + // Paints the options and create array of selected id keys + + target.startTag("options"); + int keyIndex = 0; + // Support for external null selection item id + final Collection ids = getItemIds(); + if (isNullSelectionAllowed() && getNullSelectionItemId() != null + && !ids.contains(getNullSelectionItemId())) { + final Object id = getNullSelectionItemId(); + // Paints option + target.startTag("so"); + paintItem(target, id); + if (isSelected(id)) { + selectedKeys[keyIndex++] = itemIdMapper.key(id); + } + target.endTag("so"); + } + + final Iterator i = getItemIds().iterator(); + // Paints the available selection options from data source + while (i.hasNext()) { + // Gets the option attribute values + final Object id = i.next(); + if (!isNullSelectionAllowed() && id != null + && id.equals(getNullSelectionItemId())) { + // Remove item if it's the null selection item but null + // selection is not allowed + continue; + } + final String key = itemIdMapper.key(id); + // add listener for each item, to cause repaint if an item changes + getCaptionChangeListener().addNotifierForItem(id); + target.startTag("so"); + paintItem(target, id); + if (isSelected(id) && keyIndex < selectedKeys.length) { + selectedKeys[keyIndex++] = key; + } + target.endTag("so"); + } + target.endTag("options"); + // == + + // Paint variables + target.addVariable(this, "selected", selectedKeys); + if (isNewItemsAllowed()) { + target.addVariable(this, "newitem", ""); + } + + } + + protected void paintItem(PaintTarget target, Object itemId) + throws PaintException { + final String key = itemIdMapper.key(itemId); + final String caption = getItemCaption(itemId); + final Resource icon = getItemIcon(itemId); + if (icon != null) { + target.addAttribute("icon", icon); + } + target.addAttribute("caption", caption); + if (itemId != null && itemId.equals(getNullSelectionItemId())) { + target.addAttribute("nullselection", true); + } + target.addAttribute("key", key); + if (isSelected(itemId)) { + target.addAttribute("selected", true); + } + } + + /** + * Invoked when the value of a variable has changed. + * + * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object, + * java.util.Map) + */ + @Override + public void changeVariables(Object source, Map variables) { + + // New option entered (and it is allowed) + if (isNewItemsAllowed()) { + final String newitem = (String) variables.get("newitem"); + if (newitem != null && newitem.length() > 0) { + getNewItemHandler().addNewItem(newitem); + } + } + + // Selection change + if (variables.containsKey("selected")) { + final String[] clientSideSelectedKeys = (String[]) variables + .get("selected"); + + // Multiselect mode + if (isMultiSelect()) { + + // TODO Optimize by adding repaintNotNeeded when applicable + + // Converts the key-array to id-set + final LinkedList acceptedSelections = new LinkedList(); + for (int i = 0; i < clientSideSelectedKeys.length; i++) { + final Object id = itemIdMapper + .get(clientSideSelectedKeys[i]); + if (!isNullSelectionAllowed() + && (id == null || id == getNullSelectionItemId())) { + // skip empty selection if nullselection is not allowed + markAsDirty(); + } else if (id != null && containsId(id)) { + acceptedSelections.add(id); + } + } + + if (!isNullSelectionAllowed() + && acceptedSelections.size() < 1) { + // empty selection not allowed, keep old value + markAsDirty(); + return; + } + + // Limits the deselection to the set of visible items + // (non-visible items can not be deselected) + Collection visibleNotSelected = getVisibleItemIds(); + if (visibleNotSelected != null) { + visibleNotSelected = new HashSet( + visibleNotSelected); + // Don't remove those that will be added to preserve order + visibleNotSelected.removeAll(acceptedSelections); + + @SuppressWarnings("unchecked") + Set newsel = (Set) getValue(); + if (newsel == null) { + newsel = new LinkedHashSet(); + } else { + newsel = new LinkedHashSet(newsel); + } + newsel.removeAll(visibleNotSelected); + newsel.addAll(acceptedSelections); + setValue(newsel, true); + } + } else { + // Single select mode + if (!isNullSelectionAllowed() + && (clientSideSelectedKeys.length == 0 + || clientSideSelectedKeys[0] == null + || clientSideSelectedKeys[0] == getNullSelectionItemId())) { + markAsDirty(); + return; + } + if (clientSideSelectedKeys.length == 0) { + // Allows deselection only if the deselected item is + // visible + final Object current = getValue(); + final Collection visible = getVisibleItemIds(); + if (visible != null && visible.contains(current)) { + setValue(null, true); + } + } else { + String clientSelectedKey = clientSideSelectedKeys[0]; + if ("null".equals(clientSelectedKey) + || itemIdMapper.containsKey(clientSelectedKey)) { + // Happens to work for nullselection + // (get ("null") -> null)) + final Object id = itemIdMapper.get(clientSelectedKey); + + if (!isNullSelectionAllowed() && id == null) { + markAsDirty(); + } else if (id != null + && id.equals(getNullSelectionItemId())) { + setValue(null, true); + } else { + setValue(id, true); + } + } + } + } + } + } + + /** + * TODO refine doc Setter for new item handler that is called when user adds + * new item in newItemAllowed mode. + * + * @param newItemHandler + */ + public void setNewItemHandler(NewItemHandler newItemHandler) { + this.newItemHandler = newItemHandler; + } + + /** + * TODO refine doc + * + * @return + */ + public NewItemHandler getNewItemHandler() { + if (newItemHandler == null) { + newItemHandler = new DefaultNewItemHandler(); + } + return newItemHandler; + } + + public interface NewItemHandler extends Serializable { + void addNewItem(String newItemCaption); + } + + /** + * TODO refine doc + * + * This is a default class that handles adding new items that are typed by + * user to selects container. + * + * By extending this class one may implement some logic on new item addition + * like database inserts. + * + */ + public class DefaultNewItemHandler implements NewItemHandler { + @Override + public void addNewItem(String newItemCaption) { + // Checks for readonly + if (isReadOnly()) { + throw new Property.ReadOnlyException(); + } + + // Adds new option + if (addItem(newItemCaption) != null) { + + // Sets the caption property, if used + if (getItemCaptionPropertyId() != null) { + getContainerProperty(newItemCaption, + getItemCaptionPropertyId()) + .setValue(newItemCaption); + } + if (isMultiSelect()) { + Set values = new HashSet((Collection) getValue()); + values.add(newItemCaption); + setValue(values); + } else { + setValue(newItemCaption); + } + } + } + } + + /** + * Gets the visible item ids. In Select, this returns list of all item ids, + * but can be overriden in subclasses if they paint only part of the items + * to the terminal or null if no items is visible. + */ + public Collection getVisibleItemIds() { + return getItemIds(); + } + + /* Property methods */ + + /** + * Returns the type of the property. getValue and + * setValue methods must be compatible with this type: one can + * safely cast getValue to given type and pass any variable + * assignable to this type as a parameter to setValue. + * + * @return the Type of the property. + */ + @Override + public Class getType() { + if (isMultiSelect()) { + return Set.class; + } else { + return Object.class; + } + } + + /** + * Gets the selected item id or in multiselect mode a set of selected ids. + * + * @see com.vaadin.v7.ui.AbstractField#getValue() + */ + @Override + public Object getValue() { + final Object retValue = super.getValue(); + + if (isMultiSelect()) { + + // If the return value is not a set + if (retValue == null) { + return new HashSet(); + } + if (retValue instanceof Set) { + return Collections.unmodifiableSet((Set) retValue); + } else if (retValue instanceof Collection) { + return new HashSet((Collection) retValue); + } else { + final Set s = new HashSet(); + if (items.containsId(retValue)) { + s.add(retValue); + } + return s; + } + + } else { + return retValue; + } + } + + /** + * Sets the visible value of the property. + * + *

    + * The value of the select is the selected item id. If the select is in + * multiselect-mode, the value is a set of selected item keys. In + * multiselect mode all collections of id:s can be assigned. + *

    + * + * @param newValue + * the New selected item or collection of selected items. + * @see com.vaadin.v7.ui.AbstractField#setValue(java.lang.Object) + */ + @Override + public void setValue(Object newValue) throws Property.ReadOnlyException { + if (newValue == getNullSelectionItemId()) { + newValue = null; + } + + setValue(newValue, false); + } + + /** + * Sets the visible value of the property. + * + *

    + * The value of the select is the selected item id. If the select is in + * multiselect-mode, the value is a set of selected item keys. In + * multiselect mode all collections of id:s can be assigned. + *

    + * + * @since 7.5.7 + * @param newValue + * the New selected item or collection of selected items. + * @param repaintIsNotNeeded + * True if caller is sure that repaint is not needed. + * @param ignoreReadOnly + * True if read-only check should be omitted. + * @see com.vaadin.v7.ui.AbstractField#setValue(java.lang.Object, + * java.lang.Boolean) + */ + @Override + protected void setValue(Object newFieldValue, boolean repaintIsNotNeeded, + boolean ignoreReadOnly) + throws com.vaadin.v7.data.Property.ReadOnlyException, + ConversionException, InvalidValueException { + if (isMultiSelect()) { + if (newFieldValue == null) { + super.setValue(new LinkedHashSet(), repaintIsNotNeeded, + ignoreReadOnly); + } else if (Collection.class + .isAssignableFrom(newFieldValue.getClass())) { + super.setValue( + new LinkedHashSet( + (Collection) newFieldValue), + repaintIsNotNeeded, ignoreReadOnly); + } + } else if (newFieldValue == null || items.containsId(newFieldValue)) { + super.setValue(newFieldValue, repaintIsNotNeeded, ignoreReadOnly); + } + } + + /* Container methods */ + + /** + * Gets the item from the container with given id. If the container does not + * contain the requested item, null is returned. + * + * @param itemId + * the item id. + * @return the item from the container. + */ + @Override + public Item getItem(Object itemId) { + return items.getItem(itemId); + } + + /** + * Gets the item Id collection from the container. + * + * @return the Collection of item ids. + */ + @Override + public Collection getItemIds() { + return items.getItemIds(); + } + + /** + * Gets the property Id collection from the container. + * + * @return the Collection of property ids. + */ + @Override + public Collection getContainerPropertyIds() { + return items.getContainerPropertyIds(); + } + + /** + * Gets the property type. + * + * @param propertyId + * the Id identifying the property. + * @see com.com.vaadin.v7.data.Container#getType(java.lang.Object) + */ + @Override + public Class getType(Object propertyId) { + return items.getType(propertyId); + } + + /* + * Gets the number of items in the container. + * + * @return the Number of items in the container. + * + * @see com.vaadin.data.Container#size() + */ + @Override + public int size() { + int size = items.size(); + assert size >= 0; + return size; + } + + /** + * Tests, if the collection contains an item with given id. + * + * @param itemId + * the Id the of item to be tested. + */ + @Override + public boolean containsId(Object itemId) { + if (itemId != null) { + return items.containsId(itemId); + } else { + return false; + } + } + + /** + * Gets the Property identified by the given itemId and propertyId from the + * Container + * + * @see com.com.vaadin.v7.data.Container#getContainerProperty(Object, + * Object) + */ + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + return items.getContainerProperty(itemId, propertyId); + } + + /** + * Adds the new property to all items. Adds a property with given id, type + * and default value to all items in the container. + * + * This functionality is optional. If the function is unsupported, it always + * returns false. + * + * @return True if the operation succeeded. + * @see com.com.vaadin.v7.data.Container#addContainerProperty(java.lang.Object, + * java.lang.Class, java.lang.Object) + */ + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + + final boolean retval = items.addContainerProperty(propertyId, type, + defaultValue); + if (retval && !(items instanceof Container.PropertySetChangeNotifier)) { + firePropertySetChange(); + } + return retval; + } + + /** + * Removes all items from the container. + * + * This functionality is optional. If the function is unsupported, it always + * returns false. + * + * @return True if the operation succeeded. + * @see com.com.vaadin.v7.data.Container#removeAllItems() + */ + @Override + public boolean removeAllItems() throws UnsupportedOperationException { + + final boolean retval = items.removeAllItems(); + itemIdMapper.removeAll(); + if (retval) { + setValue(null); + if (!(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + } + return retval; + } + + /** + * Creates a new item into container with container managed id. The id of + * the created new item is returned. The item can be fetched with getItem() + * method. if the creation fails, null is returned. + * + * @return the Id of the created item or null in case of failure. + * @see com.com.vaadin.v7.data.Container#addItem() + */ + @Override + public Object addItem() throws UnsupportedOperationException { + + final Object retval = items.addItem(); + if (retval != null + && !(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + return retval; + } + + /** + * Create a new item into container. The created new item is returned and + * ready for setting property values. if the creation fails, null is + * returned. In case the container already contains the item, null is + * returned. + * + * This functionality is optional. If the function is unsupported, it always + * returns null. + * + * @param itemId + * the Identification of the item to be created. + * @return the Created item with the given id, or null in case of failure. + * @see com.com.vaadin.v7.data.Container#addItem(java.lang.Object) + */ + @Override + public Item addItem(Object itemId) throws UnsupportedOperationException { + + final Item retval = items.addItem(itemId); + if (retval != null + && !(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + return retval; + } + + /** + * Adds given items with given item ids to container. + * + * @since 7.2 + * @param itemId + * item identifiers to be added to underlying container + * @throws UnsupportedOperationException + * if the underlying container don't support adding items with + * identifiers + */ + public void addItems(Object... itemId) + throws UnsupportedOperationException { + for (Object id : itemId) { + addItem(id); + } + } + + /** + * Adds given items with given item ids to container. + * + * @since 7.2 + * @param itemIds + * item identifiers to be added to underlying container + * @throws UnsupportedOperationException + * if the underlying container don't support adding items with + * identifiers + */ + public void addItems(Collection itemIds) + throws UnsupportedOperationException { + addItems(itemIds.toArray()); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container#removeItem(java.lang.Object) + */ + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + + unselect(itemId); + final boolean retval = items.removeItem(itemId); + itemIdMapper.remove(itemId); + if (retval && !(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + return retval; + } + + /** + * Checks that the current selection is valid, i.e. the selected item ids + * exist in the container. Updates the selection if one or several selected + * item ids are no longer available in the container. + */ + @SuppressWarnings("unchecked") + public void sanitizeSelection() { + Object value = getValue(); + if (value == null) { + return; + } + + boolean changed = false; + + if (isMultiSelect()) { + Collection valueAsCollection = (Collection) value; + List newSelection = new ArrayList( + valueAsCollection.size()); + for (Object subValue : valueAsCollection) { + if (containsId(subValue)) { + newSelection.add(subValue); + } else { + changed = true; + } + } + if (changed) { + setValue(newSelection); + } + } else { + if (!containsId(value)) { + setValue(null); + } + } + + } + + /** + * Removes the property from all items. Removes a property with given id + * from all the items in the container. + * + * This functionality is optional. If the function is unsupported, it always + * returns false. + * + * @return True if the operation succeeded. + * @see com.com.vaadin.v7.data.Container#removeContainerProperty(java.lang.Object) + */ + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + + final boolean retval = items.removeContainerProperty(propertyId); + if (retval && !(items instanceof Container.PropertySetChangeNotifier)) { + firePropertySetChange(); + } + return retval; + } + + /* Container.Viewer methods */ + + /** + * Sets the Container that serves as the data source of the viewer. + * + * As a side-effect the fields value (selection) is set to null due old + * selection not necessary exists in new Container. + * + * @see com.com.vaadin.v7.data.Container.Viewer#setContainerDataSource(Container) + * + * @param newDataSource + * the new data source. + */ + @Override + public void setContainerDataSource(Container newDataSource) { + if (newDataSource == null) { + newDataSource = new IndexedContainer(); + } + + getCaptionChangeListener().clear(); + + if (items != newDataSource) { + + // Removes listeners from the old datasource + if (items != null) { + if (items instanceof Container.ItemSetChangeNotifier) { + ((Container.ItemSetChangeNotifier) items) + .removeItemSetChangeListener(this); + } + if (items instanceof Container.PropertySetChangeNotifier) { + ((Container.PropertySetChangeNotifier) items) + .removePropertySetChangeListener(this); + } + } + + // Assigns new data source + items = newDataSource; + + // Clears itemIdMapper also + itemIdMapper.removeAll(); + + // Adds listeners + if (items != null) { + if (items instanceof Container.ItemSetChangeNotifier) { + ((Container.ItemSetChangeNotifier) items) + .addItemSetChangeListener(this); + } + if (items instanceof Container.PropertySetChangeNotifier) { + ((Container.PropertySetChangeNotifier) items) + .addPropertySetChangeListener(this); + } + } + + /* + * We expect changing the data source should also clean value. See + * #810, #4607, #5281 + */ + setValue(null); + + markAsDirty(); + + } + } + + /** + * Gets the viewing data-source container. + * + * @see com.com.vaadin.v7.data.Container.Viewer#getContainerDataSource() + */ + @Override + public Container getContainerDataSource() { + return items; + } + + /* Select attributes */ + + /** + * Is the select in multiselect mode? In multiselect mode + * + * @return the Value of property multiSelect. + */ + public boolean isMultiSelect() { + return getState(false).multiSelect; + } + + /** + * Sets the multiselect mode. Setting multiselect mode false may lose + * selection information: if selected items set contains one or more + * selected items, only one of the selected items is kept as selected. + * + * Subclasses of AbstractSelect can choose not to support changing the + * multiselect mode, and may throw {@link UnsupportedOperationException}. + * + * @param multiSelect + * the New value of property multiSelect. + */ + public void setMultiSelect(boolean multiSelect) { + if (multiSelect && getNullSelectionItemId() != null) { + throw new IllegalStateException( + "Multiselect and NullSelectionItemId can not be set at the same time."); + } + if (multiSelect != getState(false).multiSelect) { + + // Selection before mode change + final Object oldValue = getValue(); + + getState().multiSelect = multiSelect; + + // Convert the value type + if (multiSelect) { + final Set s = new HashSet(); + if (oldValue != null) { + s.add(oldValue); + } + setValue(s); + } else { + final Set s = (Set) oldValue; + if (s == null || s.isEmpty()) { + setValue(null); + } else { + // Set the single select to contain only the first + // selected value in the multiselect + setValue(s.iterator().next()); + } + } + + markAsDirty(); + } + } + + /** + * Does the select allow adding new options by the user. If true, the new + * options can be added to the Container. The text entered by the user is + * used as id. Note that data-source must allow adding new items. + * + * @return True if additions are allowed. + */ + public boolean isNewItemsAllowed() { + return allowNewOptions; + } + + /** + * Enables or disables possibility to add new options by the user. + * + * @param allowNewOptions + * the New value of property allowNewOptions. + */ + public void setNewItemsAllowed(boolean allowNewOptions) { + + // Only handle change requests + if (this.allowNewOptions != allowNewOptions) { + + this.allowNewOptions = allowNewOptions; + + markAsDirty(); + } + } + + /** + * Override the caption of an item. Setting caption explicitly overrides id, + * item and index captions. + * + * @param itemId + * the id of the item to be recaptioned. + * @param caption + * the New caption. + */ + public void setItemCaption(Object itemId, String caption) { + if (itemId != null) { + itemCaptions.put(itemId, caption); + markAsDirty(); + } + } + + /** + * Gets the caption of an item. The caption is generated as specified by the + * item caption mode. See setItemCaptionMode() for more + * details. + * + * @param itemId + * the id of the item to be queried. + * @return the caption for specified item. + */ + public String getItemCaption(Object itemId) { + + // Null items can not be found + if (itemId == null) { + return null; + } + + String caption = null; + + switch (getItemCaptionMode()) { + + case ID: + caption = idToCaption(itemId); + break; + case ID_TOSTRING: + caption = itemId.toString(); + break; + case INDEX: + if (items instanceof Container.Indexed) { + caption = String + .valueOf(((Container.Indexed) items).indexOfId(itemId)); + } else { + caption = "ERROR: Container is not indexed"; + } + break; + + case ITEM: + final Item i = getItem(itemId); + if (i != null) { + caption = i.toString(); + } + break; + + case EXPLICIT: + caption = itemCaptions.get(itemId); + break; + + case EXPLICIT_DEFAULTS_ID: + caption = itemCaptions.get(itemId); + if (caption == null) { + caption = idToCaption(itemId); + } + break; + + case PROPERTY: + final Property p = getContainerProperty(itemId, + getItemCaptionPropertyId()); + if (p != null) { + Object value = p.getValue(); + if (value != null) { + caption = value.toString(); + } + } + break; + } + + // All items must have some captions + return caption != null ? caption : ""; + } + + private String idToCaption(Object itemId) { + try { + Converter c = (Converter) ConverterUtil + .getConverter(String.class, itemId.getClass(), + getSession()); + return ConverterUtil.convertFromModel(itemId, String.class, c, + getLocale()); + } catch (Exception e) { + return itemId.toString(); + } + } + + /** + * Sets the icon for an item. + * + * @param itemId + * the id of the item to be assigned an icon. + * @param icon + * the icon to use or null. + */ + public void setItemIcon(Object itemId, Resource icon) { + if (itemId != null) { + if (icon == null) { + itemIcons.remove(itemId); + } else { + itemIcons.put(itemId, icon); + } + markAsDirty(); + } + } + + /** + * Gets the item icon. + * + * @param itemId + * the id of the item to be assigned an icon. + * @return the icon for the item or null, if not specified. + */ + public Resource getItemIcon(Object itemId) { + final Resource explicit = itemIcons.get(itemId); + if (explicit != null) { + return explicit; + } + + if (getItemIconPropertyId() == null) { + return null; + } + + final Property ip = getContainerProperty(itemId, + getItemIconPropertyId()); + if (ip == null) { + return null; + } + final Object icon = ip.getValue(); + if (icon instanceof Resource) { + return (Resource) icon; + } + + return null; + } + + /** + * Sets the item caption mode. + * + * See {@link ItemCaptionMode} for a description of the modes. + *

    + * {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID} is the default mode. + *

    + * + * @param mode + * the One of the modes listed above. + */ + public void setItemCaptionMode(ItemCaptionMode mode) { + if (mode != null) { + itemCaptionMode = mode; + markAsDirty(); + } + } + + /** + * Gets the item caption mode. + * + *

    + * The mode can be one of the following ones: + *

      + *
    • ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID : Items + * Id-objects toString is used as item caption. If caption is + * explicitly specified, it overrides the id-caption. + *
    • ITEM_CAPTION_MODE_ID : Items Id-objects + * toString is used as item caption.
    • + *
    • ITEM_CAPTION_MODE_ITEM : Item-objects + * toString is used as item caption.
    • + *
    • ITEM_CAPTION_MODE_INDEX : The index of the item is used + * as item caption. The index mode can only be used with the containers + * implementing Container.Indexed interface.
    • + *
    • ITEM_CAPTION_MODE_EXPLICIT : The item captions must be + * explicitly specified.
    • + *
    • ITEM_CAPTION_MODE_PROPERTY : The item captions are read + * from property, that must be specified with + * setItemCaptionPropertyId.
    • + *
    + * The ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID is the default + * mode. + *

    + * + * @return the One of the modes listed above. + */ + public ItemCaptionMode getItemCaptionMode() { + return itemCaptionMode; + } + + /** + * Sets the item caption property. + * + *

    + * Setting the id to a existing property implicitly sets the item caption + * mode to ITEM_CAPTION_MODE_PROPERTY. If the object is in + * ITEM_CAPTION_MODE_PROPERTY mode, setting caption property id + * null resets the item caption mode to + * ITEM_CAPTION_EXPLICIT_DEFAULTS_ID. + *

    + *

    + * Note that the type of the property used for caption must be String + *

    + *

    + * Setting the property id to null disables this feature. The id is null by + * default + *

    + * . + * + * @param propertyId + * the id of the property. + * + */ + public void setItemCaptionPropertyId(Object propertyId) { + if (propertyId != null) { + itemCaptionPropertyId = propertyId; + setItemCaptionMode(ITEM_CAPTION_MODE_PROPERTY); + markAsDirty(); + } else { + itemCaptionPropertyId = null; + if (getItemCaptionMode() == ITEM_CAPTION_MODE_PROPERTY) { + setItemCaptionMode(ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID); + } + markAsDirty(); + } + } + + /** + * Gets the item caption property. + * + * @return the Id of the property used as item caption source. + */ + public Object getItemCaptionPropertyId() { + return itemCaptionPropertyId; + } + + /** + * Sets the item icon property. + * + *

    + * If the property id is set to a valid value, each item is given an icon + * got from the given property of the items. The type of the property must + * be assignable to Resource. + *

    + * + *

    + * Note : The icons set with setItemIcon function override the + * icons from the property. + *

    + * + *

    + * Setting the property id to null disables this feature. The id is null by + * default + *

    + * . + * + * @param propertyId + * the id of the property that specifies icons for items or null + * @throws IllegalArgumentException + * If the propertyId is not in the container or is not of a + * valid type + */ + public void setItemIconPropertyId(Object propertyId) + throws IllegalArgumentException { + if (propertyId == null) { + itemIconPropertyId = null; + } else if (!getContainerPropertyIds().contains(propertyId)) { + throw new IllegalArgumentException( + "Property id not found in the container"); + } else if (Resource.class.isAssignableFrom(getType(propertyId))) { + itemIconPropertyId = propertyId; + } else { + throw new IllegalArgumentException( + "Property type must be assignable to Resource"); + } + markAsDirty(); + } + + /** + * Gets the item icon property. + * + *

    + * If the property id is set to a valid value, each item is given an icon + * got from the given property of the items. The type of the property must + * be assignable to Icon. + *

    + * + *

    + * Note : The icons set with setItemIcon function override the + * icons from the property. + *

    + * + *

    + * Setting the property id to null disables this feature. The id is null by + * default + *

    + * . + * + * @return the Id of the property containing the item icons. + */ + public Object getItemIconPropertyId() { + return itemIconPropertyId; + } + + /** + * Tests if an item is selected. + * + *

    + * In single select mode testing selection status of the item identified by + * {@link #getNullSelectionItemId()} returns true if the value of the + * property is null. + *

    + * + * @param itemId + * the Id the of the item to be tested. + * @see #getNullSelectionItemId() + * @see #setNullSelectionItemId(Object) + * + */ + public boolean isSelected(Object itemId) { + if (itemId == null) { + return false; + } + if (isMultiSelect()) { + return ((Set) getValue()).contains(itemId); + } else { + final Object value = getValue(); + return itemId + .equals(value == null ? getNullSelectionItemId() : value); + } + } + + /** + * Selects an item. + * + *

    + * In single select mode selecting item identified by + * {@link #getNullSelectionItemId()} sets the value of the property to null. + *

    + * + * @param itemId + * the identifier of Item to be selected. + * @see #getNullSelectionItemId() + * @see #setNullSelectionItemId(Object) + * + */ + public void select(Object itemId) { + if (!isMultiSelect()) { + setValue(itemId); + } else if (!isSelected(itemId) && itemId != null + && items.containsId(itemId)) { + final Set s = new HashSet((Set) getValue()); + s.add(itemId); + setValue(s); + } + } + + /** + * Unselects an item. + * + * @param itemId + * the identifier of the Item to be unselected. + * @see #getNullSelectionItemId() + * @see #setNullSelectionItemId(Object) + * + */ + public void unselect(Object itemId) { + if (isSelected(itemId)) { + if (isMultiSelect()) { + final Set s = new HashSet((Set) getValue()); + s.remove(itemId); + setValue(s); + } else { + setValue(null); + } + } + } + + /** + * Notifies this listener that the Containers contents has changed. + * + * @see com.com.vaadin.v7.data.Container.PropertySetChangeListener#containerPropertySetChange(com.com.vaadin.v7.data.Container.PropertySetChangeEvent) + */ + @Override + public void containerPropertySetChange( + Container.PropertySetChangeEvent event) { + firePropertySetChange(); + } + + /** + * Adds a new Property set change listener for this Container. + * + * @see com.com.vaadin.v7.data.Container.PropertySetChangeNotifier#addListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener) + */ + @Override + public void addPropertySetChangeListener( + Container.PropertySetChangeListener listener) { + if (propertySetEventListeners == null) { + propertySetEventListeners = new LinkedHashSet(); + } + propertySetEventListeners.add(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addPropertySetChangeListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Override + @Deprecated + public void addListener(Container.PropertySetChangeListener listener) { + addPropertySetChangeListener(listener); + } + + /** + * Removes a previously registered Property set change listener. + * + * @see com.com.vaadin.v7.data.Container.PropertySetChangeNotifier#removeListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener) + */ + @Override + public void removePropertySetChangeListener( + Container.PropertySetChangeListener listener) { + if (propertySetEventListeners != null) { + propertySetEventListeners.remove(listener); + if (propertySetEventListeners.isEmpty()) { + propertySetEventListeners = null; + } + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removePropertySetChangeListener(com.com.vaadin.v7.data.Container.PropertySetChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(Container.PropertySetChangeListener listener) { + removePropertySetChangeListener(listener); + } + + /** + * Adds an Item set change listener for the object. + * + * @see com.com.vaadin.v7.data.Container.ItemSetChangeNotifier#addListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener) + */ + @Override + public void addItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (itemSetEventListeners == null) { + itemSetEventListeners = new LinkedHashSet(); + } + itemSetEventListeners.add(listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemSetChangeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Override + @Deprecated + public void addListener(Container.ItemSetChangeListener listener) { + addItemSetChangeListener(listener); + } + + /** + * Removes the Item set change listener from the object. + * + * @see com.com.vaadin.v7.data.Container.ItemSetChangeNotifier#removeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener) + */ + @Override + public void removeItemSetChangeListener( + Container.ItemSetChangeListener listener) { + if (itemSetEventListeners != null) { + itemSetEventListeners.remove(listener); + if (itemSetEventListeners.isEmpty()) { + itemSetEventListeners = null; + } + } + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeItemSetChangeListener(com.com.vaadin.v7.data.Container.ItemSetChangeListener)} + **/ + @Override + @Deprecated + public void removeListener(Container.ItemSetChangeListener listener) { + removeItemSetChangeListener(listener); + } + + @Override + public Collection getListeners(Class eventType) { + if (Container.ItemSetChangeEvent.class.isAssignableFrom(eventType)) { + if (itemSetEventListeners == null) { + return Collections.EMPTY_LIST; + } else { + return Collections + .unmodifiableCollection(itemSetEventListeners); + } + } else if (Container.PropertySetChangeEvent.class + .isAssignableFrom(eventType)) { + if (propertySetEventListeners == null) { + return Collections.EMPTY_LIST; + } else { + return Collections + .unmodifiableCollection(propertySetEventListeners); + } + } + + return super.getListeners(eventType); + } + + /** + * Lets the listener know a Containers Item set has changed. + * + * @see com.com.vaadin.v7.data.Container.ItemSetChangeListener#containerItemSetChange(com.com.vaadin.v7.data.Container.ItemSetChangeEvent) + */ + @Override + public void containerItemSetChange(Container.ItemSetChangeEvent event) { + // Clears the item id mapping table + itemIdMapper.removeAll(); + + // Notify all listeners + fireItemSetChange(); + } + + /** + * Fires the property set change event. + */ + protected void firePropertySetChange() { + if (propertySetEventListeners != null + && !propertySetEventListeners.isEmpty()) { + final Container.PropertySetChangeEvent event = new PropertySetChangeEvent( + this); + final Object[] listeners = propertySetEventListeners.toArray(); + for (int i = 0; i < listeners.length; i++) { + ((Container.PropertySetChangeListener) listeners[i]) + .containerPropertySetChange(event); + } + } + markAsDirty(); + } + + /** + * Fires the item set change event. + */ + protected void fireItemSetChange() { + if (itemSetEventListeners != null && !itemSetEventListeners.isEmpty()) { + final Container.ItemSetChangeEvent event = new ItemSetChangeEvent( + this); + final Object[] listeners = itemSetEventListeners.toArray(); + for (int i = 0; i < listeners.length; i++) { + ((Container.ItemSetChangeListener) listeners[i]) + .containerItemSetChange(event); + } + } + markAsDirty(); + } + + /** + * Implementation of item set change event. + */ + private static class ItemSetChangeEvent extends EventObject + implements Serializable, Container.ItemSetChangeEvent { + + private ItemSetChangeEvent(Container source) { + super(source); + } + + /** + * Gets the Property where the event occurred. + * + * @see com.com.vaadin.v7.data.Container.ItemSetChangeEvent#getContainer() + */ + @Override + public Container getContainer() { + return (Container) getSource(); + } + + } + + /** + * Implementation of property set change event. + */ + private static class PropertySetChangeEvent extends EventObject + implements Container.PropertySetChangeEvent, Serializable { + + private PropertySetChangeEvent(Container source) { + super(source); + } + + /** + * Retrieves the Container whose contents have been modified. + * + * @see com.com.vaadin.v7.data.Container.PropertySetChangeEvent#getContainer() + */ + @Override + public Container getContainer() { + return (Container) getSource(); + } + + } + + /** + * For multi-selectable fields, also an empty collection of values is + * considered to be an empty field. + * + * @see LegacyAbstractField#isEmpty(). + */ + @Override + public boolean isEmpty() { + if (!isMultiSelect()) { + return super.isEmpty(); + } else { + Object value = getValue(); + return super.isEmpty() || (value instanceof Collection + && ((Collection) value).isEmpty()); + } + } + + /** + * Allow or disallow empty selection by the user. If the select is in + * single-select mode, you can make an item represent the empty selection by + * calling setNullSelectionItemId(). This way you can for + * instance set an icon and caption for the null selection item. + * + * @param nullSelectionAllowed + * whether or not to allow empty selection + * @see #setNullSelectionItemId(Object) + * @see #isNullSelectionAllowed() + */ + public void setNullSelectionAllowed(boolean nullSelectionAllowed) { + if (nullSelectionAllowed != this.nullSelectionAllowed) { + this.nullSelectionAllowed = nullSelectionAllowed; + markAsDirty(); + } + } + + /** + * Checks if null empty selection is allowed by the user. + * + * @return whether or not empty selection is allowed + * @see #setNullSelectionAllowed(boolean) + */ + public boolean isNullSelectionAllowed() { + return nullSelectionAllowed; + } + + /** + * Returns the item id that represents null value of this select in single + * select mode. + * + *

    + * Data interface does not support nulls as item ids. Selecting the item + * identified by this id is the same as selecting no items at all. This + * setting only affects the single select mode. + *

    + * + * @return the Object Null value item id. + * @see #setNullSelectionItemId(Object) + * @see #isSelected(Object) + * @see #select(Object) + */ + public Object getNullSelectionItemId() { + return nullSelectionItemId; + } + + /** + * Sets the item id that represents null value of this select. + * + *

    + * Data interface does not support nulls as item ids. Selecting the item + * identified by this id is the same as selecting no items at all. This + * setting only affects the single select mode. + *

    + * + * @param nullSelectionItemId + * the nullSelectionItemId to set. + * @see #getNullSelectionItemId() + * @see #isSelected(Object) + * @see #select(Object) + */ + public void setNullSelectionItemId(Object nullSelectionItemId) { + if (nullSelectionItemId != null && isMultiSelect()) { + throw new IllegalStateException( + "Multiselect and NullSelectionItemId can not be set at the same time."); + } + this.nullSelectionItemId = nullSelectionItemId; + } + + /** + * Notifies the component that it is connected to an application. + * + * @see com.vaadin.v7.ui.AbstractField#attach() + */ + @Override + public void attach() { + super.attach(); + } + + /** + * Detaches the component from application. + * + * @see com.vaadin.ui.AbstractComponent#detach() + */ + @Override + public void detach() { + getCaptionChangeListener().clear(); + super.detach(); + } + + // Caption change listener + protected CaptionChangeListener getCaptionChangeListener() { + if (captionChangeListener == null) { + captionChangeListener = new CaptionChangeListener(); + } + return captionChangeListener; + } + + /** + * This is a listener helper for Item and Property changes that should cause + * a repaint. It should be attached to all items that are displayed, and the + * default implementation does this in paintContent(). Especially + * "lazyloading" components should take care to add and remove listeners as + * appropriate. Call addNotifierForItem() for each painted item (and + * remember to clear). + * + * NOTE: singleton, use getCaptionChangeListener(). + * + */ + protected class CaptionChangeListener implements + Item.PropertySetChangeListener, Property.ValueChangeListener { + + // TODO clean this up - type is either Item.PropertySetChangeNotifier or + // Property.ValueChangeNotifier + HashSet captionChangeNotifiers = new HashSet(); + + public void addNotifierForItem(Object itemId) { + switch (getItemCaptionMode()) { + case ITEM: + final Item i = getItem(itemId); + if (i == null) { + return; + } + if (i instanceof Item.PropertySetChangeNotifier) { + ((Item.PropertySetChangeNotifier) i) + .addPropertySetChangeListener( + getCaptionChangeListener()); + captionChangeNotifiers.add(i); + } + Collection pids = i.getItemPropertyIds(); + if (pids != null) { + for (Iterator it = pids.iterator(); it.hasNext();) { + Property p = i.getItemProperty(it.next()); + if (p != null + && p instanceof Property.ValueChangeNotifier) { + ((Property.ValueChangeNotifier) p) + .addValueChangeListener( + getCaptionChangeListener()); + captionChangeNotifiers.add(p); + } + } + + } + break; + case PROPERTY: + final Property p = getContainerProperty(itemId, + getItemCaptionPropertyId()); + if (p != null && p instanceof Property.ValueChangeNotifier) { + ((Property.ValueChangeNotifier) p) + .addValueChangeListener(getCaptionChangeListener()); + captionChangeNotifiers.add(p); + } + break; + + } + if (getItemIconPropertyId() != null) { + final Property p = getContainerProperty(itemId, + getItemIconPropertyId()); + if (p != null && p instanceof Property.ValueChangeNotifier) { + ((Property.ValueChangeNotifier) p) + .addValueChangeListener(getCaptionChangeListener()); + captionChangeNotifiers.add(p); + } + } + } + + public void clear() { + for (Iterator it = captionChangeNotifiers.iterator(); it + .hasNext();) { + Object notifier = it.next(); + if (notifier instanceof Item.PropertySetChangeNotifier) { + ((Item.PropertySetChangeNotifier) notifier) + .removePropertySetChangeListener( + getCaptionChangeListener()); + } else { + ((Property.ValueChangeNotifier) notifier) + .removeValueChangeListener( + getCaptionChangeListener()); + } + } + captionChangeNotifiers.clear(); + } + + @Override + public void valueChange( + com.vaadin.v7.data.Property.ValueChangeEvent event) { + markAsDirty(); + } + + @Override + public void itemPropertySetChange( + com.vaadin.v7.data.Item.PropertySetChangeEvent event) { + markAsDirty(); + } + + } + + /** + * Criterion which accepts a drop only if the drop target is (one of) the + * given Item identifier(s). Criterion can be used only on a drop targets + * that extends AbstractSelect like {@link Table} and {@link Tree}. The + * target and identifiers of valid Items are given in constructor. + * + * @since 6.3 + */ + public static class TargetItemIs extends AbstractItemSetCriterion { + + /** + * @param select + * the select implementation that is used as a drop target + * @param itemId + * the identifier(s) that are valid drop locations + */ + public TargetItemIs(AbstractSelect select, Object... itemId) { + super(select, itemId); + } + + @Override + public boolean accept(DragAndDropEvent dragEvent) { + AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent + .getTargetDetails(); + if (dropTargetData.getTarget() != select) { + return false; + } + return itemIds.contains(dropTargetData.getItemIdOver()); + } + + } + + /** + * Abstract helper class to implement item id based criterion. + * + * Note, inner class used not to open itemIdMapper for public access. + * + * @since 6.3 + * + */ + private static abstract class AbstractItemSetCriterion + extends ClientSideCriterion { + protected final Collection itemIds = new HashSet(); + protected AbstractSelect select; + + public AbstractItemSetCriterion(AbstractSelect select, + Object... itemId) { + if (itemIds == null || select == null) { + throw new IllegalArgumentException( + "Accepted item identifiers must be accepted."); + } + Collections.addAll(itemIds, itemId); + this.select = select; + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + super.paintContent(target); + String[] keys = new String[itemIds.size()]; + int i = 0; + for (Object itemId : itemIds) { + String key = select.itemIdMapper.key(itemId); + keys[i++] = key; + } + target.addAttribute("keys", keys); + target.addAttribute("s", select); + } + + } + + /** + * This criterion accepts a only a {@link Transferable} that contains given + * Item (practically its identifier) from a specific AbstractSelect. + * + * @since 6.3 + */ + public static class AcceptItem extends AbstractItemSetCriterion { + + /** + * @param select + * the select from which the item id's are checked + * @param itemId + * the item identifier(s) of the select that are accepted + */ + public AcceptItem(AbstractSelect select, Object... itemId) { + super(select, itemId); + } + + @Override + public boolean accept(DragAndDropEvent dragEvent) { + DataBoundTransferable transferable = (DataBoundTransferable) dragEvent + .getTransferable(); + if (transferable.getSourceComponent() != select) { + return false; + } + return itemIds.contains(transferable.getItemId()); + } + + /** + * A simple accept criterion which ensures that {@link Transferable} + * contains an {@link Item} (or actually its identifier). In other words + * the criterion check that drag is coming from a {@link Container} like + * {@link Tree} or {@link Table}. + */ + public static final ClientSideCriterion ALL = new ContainsDataFlavor( + "itemId"); + + } + + /** + * TargetDetails implementation for subclasses of {@link AbstractSelect} + * that implement {@link DropTarget}. + * + * @since 6.3 + */ + public class AbstractSelectTargetDetails extends TargetDetailsImpl { + + /** + * The item id over which the drag event happened. + */ + protected Object idOver; + + /** + * Constructor that automatically converts itemIdOver key to + * corresponding item Id + * + */ + protected AbstractSelectTargetDetails( + Map rawVariables) { + super(rawVariables, (DropTarget) AbstractSelect.this); + // eagar fetch itemid, mapper may be emptied + String keyover = (String) getData("itemIdOver"); + if (keyover != null) { + idOver = itemIdMapper.get(keyover); + } + } + + /** + * If the drag operation is currently over an {@link Item}, this method + * returns the identifier of that {@link Item}. + * + */ + public Object getItemIdOver() { + return idOver; + } + + /** + * Returns a detailed vertical location where the drop happened on Item. + */ + public VerticalDropLocation getDropLocation() { + String detail = (String) getData("detail"); + if (detail == null) { + return null; + } + return VerticalDropLocation.valueOf(detail); + } + + } + + /** + * An accept criterion to accept drops only on a specific vertical location + * of an item. + *

    + * This accept criterion is currently usable in Tree and Table + * implementations. + */ + public static class VerticalLocationIs extends TargetDetailIs { + public static VerticalLocationIs TOP = new VerticalLocationIs( + VerticalDropLocation.TOP); + public static VerticalLocationIs BOTTOM = new VerticalLocationIs( + VerticalDropLocation.BOTTOM); + public static VerticalLocationIs MIDDLE = new VerticalLocationIs( + VerticalDropLocation.MIDDLE); + + private VerticalLocationIs(VerticalDropLocation l) { + super("detail", l.name()); + } + } + + /** + * Implement this interface and pass it to Tree.setItemDescriptionGenerator + * or Table.setItemDescriptionGenerator to generate mouse over descriptions + * ("tooltips") for the rows and cells in Table or for the items in Tree. + */ + public interface ItemDescriptionGenerator extends Serializable { + + /** + * Called by Table when a cell (and row) is painted or a item is painted + * in Tree + * + * @param source + * The source of the generator, the Tree or Table the + * generator is attached to + * @param itemId + * The itemId of the painted cell + * @param propertyId + * The propertyId of the cell, null when getting row + * description + * @return The description or "tooltip" of the item. + */ + public String generateDescription(Component source, Object itemId, + Object propertyId); + } + + @Override + public void readDesign(Element design, DesignContext context) { + // handle default attributes + super.readDesign(design, context); + // handle children specifying selectable items (

    + * Vaadin Calendar is for visualizing events in a calendar. Calendar events can + * be visualized in the variable length view depending on the start and end + * dates. + *

    + * + *
  • You can set the viewable date range with the {@link #setStartDate(Date)} + * and {@link #setEndDate(Date)} methods. Calendar has a default date range of + * one week
  • + * + *
  • Calendar has two kind of views: monthly and weekly view
  • + * + *
  • If date range is seven days or shorter, the weekly view is used.
  • + * + *
  • Calendar queries its events by using a + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider}. By default, a + * {@link com.vaadin.addon.calendar.event.BasicEventProvider BasicEventProvider} + * is used.
  • + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class Calendar extends AbstractComponent + implements CalendarComponentEvents.NavigationNotifier, + CalendarComponentEvents.EventMoveNotifier, + CalendarComponentEvents.RangeSelectNotifier, + CalendarComponentEvents.EventResizeNotifier, + CalendarEventProvider.EventSetChangeListener, DropTarget, + CalendarEditableEventProvider, Action.Container, LegacyComponent { + + /** + * Calendar can use either 12 hours clock or 24 hours clock. + */ + public enum TimeFormat { + + Format12H(), Format24H(); + } + + /** Defines currently active format for time. 12H/24H. */ + protected TimeFormat currentTimeFormat; + + /** Internal calendar data source. */ + protected java.util.Calendar currentCalendar = java.util.Calendar + .getInstance(); + + /** Defines the component's active time zone. */ + protected TimeZone timezone; + + /** Defines the calendar's date range starting point. */ + protected Date startDate = null; + + /** Defines the calendar's date range ending point. */ + protected Date endDate = null; + + /** Event provider. */ + private CalendarEventProvider calendarEventProvider; + + /** + * Internal buffer for the events that are retrieved from the event + * provider. + */ + protected List events; + + /** Date format that will be used in the UIDL for dates. */ + protected DateFormat df_date = new SimpleDateFormat("yyyy-MM-dd"); + + /** Time format that will be used in the UIDL for time. */ + protected DateFormat df_time = new SimpleDateFormat("HH:mm:ss"); + + /** Date format that will be used in the UIDL for both date and time. */ + protected DateFormat df_date_time = new SimpleDateFormat( + DateConstants.CLIENT_DATE_FORMAT + "-" + + DateConstants.CLIENT_TIME_FORMAT); + + /** + * Week view's scroll position. Client sends updates to this value so that + * scroll position wont reset all the time. + */ + private int scrollTop = 0; + + /** Caption format for the weekly view */ + private String weeklyCaptionFormat = null; + + /** Map from event ids to event handlers */ + private final Map handlers; + + /** + * Drop Handler for Vaadin DD. By default null. + */ + private DropHandler dropHandler; + + /** + * First day to show for a week + */ + private int firstDay = 1; + + /** + * Last day to show for a week + */ + private int lastDay = 7; + + /** + * First hour to show for a day + */ + private int firstHour = 0; + + /** + * Last hour to show for a day + */ + private int lastHour = 23; + + /** + * List of action handlers. + */ + private LinkedList actionHandlers = null; + + /** + * Action mapper. + */ + private KeyMapper actionMapper = null; + + /** + * + */ + private CalendarServerRpcImpl rpc = new CalendarServerRpcImpl(); + + private Integer customFirstDayOfWeek; + + /** + * Returns the logger for the calendar + */ + protected Logger getLogger() { + return Logger.getLogger(Calendar.class.getName()); + } + + /** + * Construct a Vaadin Calendar with a BasicEventProvider and no caption. + * Default date range is one week. + */ + public Calendar() { + this(null, new BasicEventProvider()); + } + + /** + * Construct a Vaadin Calendar with a BasicEventProvider and the provided + * caption. Default date range is one week. + * + * @param caption + */ + public Calendar(String caption) { + this(caption, new BasicEventProvider()); + } + + /** + *

    + * Construct a Vaadin Calendar with event provider. Event provider is + * obligatory, because calendar component will query active events through + * it. + *

    + * + *

    + * By default, Vaadin Calendar will show dates from the start of the current + * week to the end of the current week. Use {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)} to change this. + *

    + * + * @param eventProvider + * Event provider, cannot be null. + */ + public Calendar(CalendarEventProvider eventProvider) { + this(null, eventProvider); + } + + /** + *

    + * Construct a Vaadin Calendar with event provider and a caption. Event + * provider is obligatory, because calendar component will query active + * events through it. + *

    + * + *

    + * By default, Vaadin Calendar will show dates from the start of the current + * week to the end of the current week. Use {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)} to change this. + *

    + * + * @param eventProvider + * Event provider, cannot be null. + */ + // this is the constructor every other constructor calls + public Calendar(String caption, CalendarEventProvider eventProvider) { + registerRpc(rpc); + setCaption(caption); + handlers = new HashMap(); + setDefaultHandlers(); + currentCalendar.setTime(new Date()); + setEventProvider(eventProvider); + getState().firstDayOfWeek = firstDay; + getState().lastVisibleDayOfWeek = lastDay; + getState().firstHourOfDay = firstHour; + getState().lastHourOfDay = lastHour; + setTimeFormat(null); + + } + + @Override + public CalendarState getState() { + return (CalendarState) super.getState(); + } + + @Override + protected CalendarState getState(boolean markAsDirty) { + return (CalendarState) super.getState(markAsDirty); + } + + @Override + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); + + initCalendarWithLocale(); + + getState().format24H = TimeFormat.Format24H == getTimeFormat(); + setupDaysAndActions(); + setupCalendarEvents(); + rpc.scroll(scrollTop); + } + + /** + * Set all the wanted default handlers here. This is always called after + * constructing this object. All other events have default handlers except + * range and event click. + */ + protected void setDefaultHandlers() { + setHandler(new BasicBackwardHandler()); + setHandler(new BasicForwardHandler()); + setHandler(new BasicWeekClickHandler()); + setHandler(new BasicDateClickHandler()); + setHandler(new BasicEventMoveHandler()); + setHandler(new BasicEventResizeHandler()); + } + + /** + * Gets the calendar's start date. + * + * @return First visible date. + */ + public Date getStartDate() { + if (startDate == null) { + currentCalendar.set(java.util.Calendar.MILLISECOND, 0); + currentCalendar.set(java.util.Calendar.SECOND, 0); + currentCalendar.set(java.util.Calendar.MINUTE, 0); + currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 0); + currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, + currentCalendar.getFirstDayOfWeek()); + return currentCalendar.getTime(); + } + return startDate; + } + + /** + * Sets start date for the calendar. This and {@link #setEndDate(Date)} + * control the range of dates visible on the component. The default range is + * one week. + * + * @param date + * First visible date to show. + */ + public void setStartDate(Date date) { + if (!date.equals(startDate)) { + startDate = date; + markAsDirty(); + } + } + + /** + * Gets the calendar's end date. + * + * @return Last visible date. + */ + public Date getEndDate() { + if (endDate == null) { + currentCalendar.set(java.util.Calendar.MILLISECOND, 0); + currentCalendar.set(java.util.Calendar.SECOND, 59); + currentCalendar.set(java.util.Calendar.MINUTE, 59); + currentCalendar.set(java.util.Calendar.HOUR_OF_DAY, 23); + currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, + currentCalendar.getFirstDayOfWeek() + 6); + return currentCalendar.getTime(); + } + return endDate; + } + + /** + * Sets end date for the calendar. Starting from startDate, only six weeks + * will be shown if duration to endDate is longer than six weeks. + * + * This and {@link #setStartDate(Date)} control the range of dates visible + * on the component. The default range is one week. + * + * @param date + * Last visible date to show. + */ + public void setEndDate(Date date) { + if (startDate != null && startDate.after(date)) { + startDate = (Date) date.clone(); + markAsDirty(); + } else if (!date.equals(endDate)) { + endDate = date; + markAsDirty(); + } + } + + /** + * Sets the locale to be used in the Calendar component. + * + * @see com.vaadin.ui.AbstractComponent#setLocale(java.util.Locale) + */ + @Override + public void setLocale(Locale newLocale) { + super.setLocale(newLocale); + initCalendarWithLocale(); + } + + /** + * Initialize the java calendar instance with the current locale and + * timezone. + */ + private void initCalendarWithLocale() { + if (timezone != null) { + currentCalendar = java.util.Calendar.getInstance(timezone, + getLocale()); + + } else { + currentCalendar = java.util.Calendar.getInstance(getLocale()); + } + + if (customFirstDayOfWeek != null) { + currentCalendar.setFirstDayOfWeek(customFirstDayOfWeek); + } + } + + private void setupCalendarEvents() { + int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) + / DateConstants.DAYINMILLIS); + durationInDays++; + if (durationInDays > 60) { + throw new RuntimeException( + "Daterange is too big (max 60) = " + durationInDays); + } + + Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); + Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); + + currentCalendar.setTime(firstDateToShow); + events = getEventProvider().getEvents(firstDateToShow, lastDateToShow); + + List calendarStateEvents = new ArrayList(); + if (events != null) { + for (int i = 0; i < events.size(); i++) { + CalendarEvent e = events.get(i); + CalendarState.Event event = new CalendarState.Event(); + event.index = i; + event.caption = e.getCaption() == null ? "" : e.getCaption(); + event.dateFrom = df_date.format(e.getStart()); + event.dateTo = df_date.format(e.getEnd()); + event.timeFrom = df_time.format(e.getStart()); + event.timeTo = df_time.format(e.getEnd()); + event.description = e.getDescription() == null ? "" + : e.getDescription(); + event.styleName = e.getStyleName() == null ? "" + : e.getStyleName(); + event.allDay = e.isAllDay(); + calendarStateEvents.add(event); + } + } + getState().events = calendarStateEvents; + } + + private void setupDaysAndActions() { + // Make sure we have a up-to-date locale + initCalendarWithLocale(); + + CalendarState state = getState(); + + state.firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + + // If only one is null, throw exception + // If both are null, set defaults + if (startDate == null ^ endDate == null) { + String message = "Schedule cannot be painted without a proper date range.\n"; + if (startDate == null) { + throw new IllegalStateException(message + + "You must set a start date using setStartDate(Date)."); + + } else { + throw new IllegalStateException(message + + "You must set an end date using setEndDate(Date)."); + } + + } else if (startDate == null && endDate == null) { + // set defaults + startDate = getStartDate(); + endDate = getEndDate(); + } + + int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) + / DateConstants.DAYINMILLIS); + durationInDays++; + if (durationInDays > 60) { + throw new RuntimeException( + "Daterange is too big (max 60) = " + durationInDays); + } + + state.dayNames = getDayNamesShort(); + state.monthNames = getMonthNamesShort(); + + // Use same timezone in all dates this component handles. + // Show "now"-marker in browser within given timezone. + Date now = new Date(); + currentCalendar.setTime(now); + now = currentCalendar.getTime(); + + // Reset time zones for custom date formats + df_date.setTimeZone(currentCalendar.getTimeZone()); + df_time.setTimeZone(currentCalendar.getTimeZone()); + + state.now = (df_date.format(now) + " " + df_time.format(now)); + + Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); + Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); + + currentCalendar.setTime(firstDateToShow); + + DateFormat weeklyCaptionFormatter = getWeeklyCaptionFormatter(); + weeklyCaptionFormatter.setTimeZone(currentCalendar.getTimeZone()); + + Map> actionMap = new HashMap>(); + + List days = new ArrayList(); + + // Send all dates to client from server. This + // approach was taken because gwt doesn't + // support date localization properly. + while (currentCalendar.getTime().compareTo(lastDateToShow) < 1) { + final Date date = currentCalendar.getTime(); + final CalendarState.Day day = new CalendarState.Day(); + day.date = df_date.format(date); + day.localizedDateFormat = weeklyCaptionFormatter.format(date); + day.dayOfWeek = getDowByLocale(currentCalendar); + day.week = getWeek(currentCalendar); + day.yearOfWeek = getYearOfWeek(currentCalendar); + + days.add(day); + + // Get actions for a specific date + if (actionHandlers != null) { + for (Action.Handler actionHandler : actionHandlers) { + + // Create calendar which omits time + GregorianCalendar cal = new GregorianCalendar(getTimeZone(), + getLocale()); + cal.clear(); + cal.set(currentCalendar.get(java.util.Calendar.YEAR), + currentCalendar.get(java.util.Calendar.MONTH), + currentCalendar.get(java.util.Calendar.DATE)); + + // Get day start and end times + Date start = cal.getTime(); + cal.add(java.util.Calendar.DATE, 1); + cal.add(java.util.Calendar.SECOND, -1); + Date end = cal.getTime(); + + boolean monthView = (durationInDays > 7); + + /** + * If in day or week view add actions for each half-an-hour. + * If in month view add actions for each day + */ + if (monthView) { + setActionsForDay(actionMap, start, end, actionHandler); + } else { + setActionsForEachHalfHour(actionMap, start, end, + actionHandler); + } + + } + } + + currentCalendar.add(java.util.Calendar.DATE, 1); + } + state.days = days; + state.actions = createActionsList(actionMap); + } + + private int getWeek(java.util.Calendar calendar) { + return calendar.get(java.util.Calendar.WEEK_OF_YEAR); + } + + private int getYearOfWeek(java.util.Calendar calendar) { + // Would use calendar.getWeekYear() but it's only available since 1.7. + int week = getWeek(calendar); + int month = calendar.get(java.util.Calendar.MONTH); + int year = calendar.get(java.util.Calendar.YEAR); + + if (week == 1 && month == java.util.Calendar.DECEMBER) { + return year + 1; + } + + return year; + } + + private void setActionsForEachHalfHour( + Map> actionMap, Date start, Date end, + Action.Handler actionHandler) { + GregorianCalendar cal = new GregorianCalendar(getTimeZone(), + getLocale()); + cal.setTime(start); + while (cal.getTime().before(end)) { + Date s = cal.getTime(); + cal.add(java.util.Calendar.MINUTE, 30); + Date e = cal.getTime(); + CalendarDateRange range = new CalendarDateRange(s, e, + getTimeZone()); + Action[] actions = actionHandler.getActions(range, this); + if (actions != null) { + Set actionSet = new LinkedHashSet( + Arrays.asList(actions)); + actionMap.put(range, actionSet); + } + } + } + + private void setActionsForDay(Map> actionMap, + Date start, Date end, Action.Handler actionHandler) { + CalendarDateRange range = new CalendarDateRange(start, end, + getTimeZone()); + Action[] actions = actionHandler.getActions(range, this); + if (actions != null) { + Set actionSet = new LinkedHashSet( + Arrays.asList(actions)); + actionMap.put(range, actionSet); + } + } + + private List createActionsList( + Map> actionMap) { + if (actionMap.isEmpty()) { + return null; + } + + List calendarActions = new ArrayList(); + + SimpleDateFormat formatter = new SimpleDateFormat( + DateConstants.ACTION_DATE_FORMAT_PATTERN); + formatter.setTimeZone(getTimeZone()); + + for (Entry> entry : actionMap + .entrySet()) { + CalendarDateRange range = entry.getKey(); + Set actions = entry.getValue(); + for (Action action : actions) { + String key = actionMapper.key(action); + CalendarState.Action calendarAction = new CalendarState.Action(); + calendarAction.actionKey = key; + calendarAction.caption = action.getCaption(); + setResource(key, action.getIcon()); + calendarAction.iconKey = key; + calendarAction.startDate = formatter.format(range.getStart()); + calendarAction.endDate = formatter.format(range.getEnd()); + calendarActions.add(calendarAction); + } + } + + return calendarActions; + } + + /** + * Gets currently active time format. Value is either TimeFormat.Format12H + * or TimeFormat.Format24H. + * + * @return TimeFormat Format for the time. + */ + public TimeFormat getTimeFormat() { + if (currentTimeFormat == null) { + SimpleDateFormat f; + if (getLocale() == null) { + f = (SimpleDateFormat) SimpleDateFormat + .getTimeInstance(SimpleDateFormat.SHORT); + } else { + f = (SimpleDateFormat) SimpleDateFormat + .getTimeInstance(SimpleDateFormat.SHORT, getLocale()); + } + String p = f.toPattern(); + if (p.indexOf("HH") != -1 || p.indexOf("H") != -1) { + return TimeFormat.Format24H; + } + return TimeFormat.Format12H; + } + return currentTimeFormat; + } + + /** + * Example: setTimeFormat(TimeFormat.Format12H);
    + * Set to null, if you want the format being defined by the locale. + * + * @param format + * Set 12h or 24h format. Default is defined by the locale. + */ + public void setTimeFormat(TimeFormat format) { + currentTimeFormat = format; + markAsDirty(); + } + + /** + * Returns a time zone that is currently used by this component. + * + * @return Component's Time zone + */ + public TimeZone getTimeZone() { + if (timezone == null) { + return currentCalendar.getTimeZone(); + } + return timezone; + } + + /** + * Set time zone that this component will use. Null value sets the default + * time zone. + * + * @param zone + * Time zone to use + */ + public void setTimeZone(TimeZone zone) { + timezone = zone; + if (!currentCalendar.getTimeZone().equals(zone)) { + if (zone == null) { + zone = TimeZone.getDefault(); + } + currentCalendar.setTimeZone(zone); + df_date_time.setTimeZone(zone); + markAsDirty(); + } + } + + /** + * Get the internally used Calendar instance. This is the currently used + * instance of {@link java.util.Calendar} but is bound to change during the + * lifetime of the component. + * + * @return the currently used java calendar + */ + public java.util.Calendar getInternalCalendar() { + return currentCalendar; + } + + /** + *

    + * This method restricts the weekdays that are shown. This affects both the + * monthly and the weekly view. The general contract is that firstDay < + * lastDay. + *

    + * + *

    + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + *

    + * + * @param firstDay + * the first day of the week to show, between 1 and 7 + */ + public void setFirstVisibleDayOfWeek(int firstDay) { + if (this.firstDay != firstDay && firstDay >= 1 && firstDay <= 7 + && getLastVisibleDayOfWeek() >= firstDay) { + this.firstDay = firstDay; + getState().firstVisibleDayOfWeek = firstDay; + } + } + + /** + * Get the first visible day of the week. Returns the weekdays as integers + * represented by {@link java.util.Calendar#DAY_OF_WEEK} + * + * @return An integer representing the week day according to + * {@link java.util.Calendar#DAY_OF_WEEK} + */ + public int getFirstVisibleDayOfWeek() { + return firstDay; + } + + /** + *

    + * This method restricts the weekdays that are shown. This affects both the + * monthly and the weekly view. The general contract is that firstDay < + * lastDay. + *

    + * + *

    + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + *

    + * + * @param lastDay + * the first day of the week to show, between 1 and 7 + */ + public void setLastVisibleDayOfWeek(int lastDay) { + if (this.lastDay != lastDay && lastDay >= 1 && lastDay <= 7 + && getFirstVisibleDayOfWeek() <= lastDay) { + this.lastDay = lastDay; + getState().lastVisibleDayOfWeek = lastDay; + } + } + + /** + * Get the last visible day of the week. Returns the weekdays as integers + * represented by {@link java.util.Calendar#DAY_OF_WEEK} + * + * @return An integer representing the week day according to + * {@link java.util.Calendar#DAY_OF_WEEK} + */ + public int getLastVisibleDayOfWeek() { + return lastDay; + } + + /** + *

    + * This method restricts the hours that are shown per day. This affects the + * weekly view. The general contract is that firstHour < lastHour. + *

    + * + *

    + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + *

    + * + * @param firstHour + * the first hour of the day to show, between 0 and 23 + */ + public void setFirstVisibleHourOfDay(int firstHour) { + if (this.firstHour != firstHour && firstHour >= 0 && firstHour <= 23 + && firstHour <= getLastVisibleHourOfDay()) { + this.firstHour = firstHour; + getState().firstHourOfDay = firstHour; + } + } + + /** + * Returns the first visible hour in the week view. Returns the hour using a + * 24h time format + * + */ + public int getFirstVisibleHourOfDay() { + return firstHour; + } + + /** + *

    + * This method restricts the hours that are shown per day. This affects the + * weekly view. The general contract is that firstHour < lastHour. + *

    + * + *

    + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + *

    + * + * @param lastHour + * the first hour of the day to show, between 0 and 23 + */ + public void setLastVisibleHourOfDay(int lastHour) { + if (this.lastHour != lastHour && lastHour >= 0 && lastHour <= 23 + && lastHour >= getFirstVisibleHourOfDay()) { + this.lastHour = lastHour; + getState().lastHourOfDay = lastHour; + } + } + + /** + * Returns the last visible hour in the week view. Returns the hour using a + * 24h time format + * + */ + public int getLastVisibleHourOfDay() { + return lastHour; + } + + /** + * Gets the date caption format for the weekly view. + * + * @return The pattern used in caption of dates in weekly view. + */ + public String getWeeklyCaptionFormat() { + return weeklyCaptionFormat; + } + + /** + * Sets custom date format for the weekly view. This is the caption of the + * date. Format could be like "mmm MM/dd". + * + * @param dateFormatPattern + * The date caption pattern. + */ + public void setWeeklyCaptionFormat(String dateFormatPattern) { + if ((weeklyCaptionFormat == null && dateFormatPattern != null) + || (weeklyCaptionFormat != null + && !weeklyCaptionFormat.equals(dateFormatPattern))) { + weeklyCaptionFormat = dateFormatPattern; + markAsDirty(); + } + } + + private DateFormat getWeeklyCaptionFormatter() { + if (weeklyCaptionFormat != null) { + return new SimpleDateFormat(weeklyCaptionFormat, getLocale()); + } else { + return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT, + getLocale()); + } + } + + /** + * Get the day of week by the given calendar and its locale + * + * @param calendar + * The calendar to use + * @return + */ + private static int getDowByLocale(java.util.Calendar calendar) { + int fow = calendar.get(java.util.Calendar.DAY_OF_WEEK); + + // monday first + if (calendar.getFirstDayOfWeek() == java.util.Calendar.MONDAY) { + fow = (fow == java.util.Calendar.SUNDAY) ? 7 : fow - 1; + } + + return fow; + } + + /** + * Is the user allowed to trigger events which alters the events + * + * @return true if the client is allowed to send changes to server + * @see #isEventClickAllowed() + */ + protected boolean isClientChangeAllowed() { + return !isReadOnly(); + } + + /** + * Is the user allowed to trigger click events. Returns {@code true} by + * default. Subclass can override this method to disallow firing event + * clicks got from the client side. + * + * @return true if the client is allowed to click events + * @see #isClientChangeAllowed() + * @deprecated As of 7.4, override {@link #fireEventClick(Integer)} instead. + */ + @Deprecated + protected boolean isEventClickAllowed() { + return true; + } + + /** + * Fires an event when the user selecing moving forward/backward in the + * calendar. + * + * @param forward + * True if the calendar moved forward else backward is assumed. + */ + protected void fireNavigationEvent(boolean forward) { + if (forward) { + fireEvent(new ForwardEvent(this)); + } else { + fireEvent(new BackwardEvent(this)); + } + } + + /** + * Fires an event move event to all server side move listerners + * + * @param index + * The index of the event in the events list + * @param newFromDatetime + * The changed from date time + */ + protected void fireEventMove(int index, Date newFromDatetime) { + MoveEvent event = new MoveEvent(this, events.get(index), + newFromDatetime); + + if (calendarEventProvider instanceof EventMoveHandler) { + // Notify event provider if it is an event move handler + ((EventMoveHandler) calendarEventProvider).eventMove(event); + } + + // Notify event move handler attached by using the + // setHandler(EventMoveHandler) method + fireEvent(event); + } + + /** + * Fires event when a week was clicked in the calendar. + * + * @param week + * The week that was clicked + * @param year + * The year of the week + */ + protected void fireWeekClick(int week, int year) { + fireEvent(new WeekClick(this, week, year)); + } + + /** + * Fires event when a date was clicked in the calendar. Uses an existing + * event from the event cache. + * + * @param index + * The index of the event in the event cache. + */ + protected void fireEventClick(Integer index) { + fireEvent(new EventClick(this, events.get(index))); + } + + /** + * Fires event when a date was clicked in the calendar. Creates a new event + * for the date and passes it to the listener. + * + * @param date + * The date and time that was clicked + */ + protected void fireDateClick(Date date) { + fireEvent(new DateClickEvent(this, date)); + } + + /** + * Fires an event range selected event. The event is fired when a user + * highlights an area in the calendar. The highlighted areas start and end + * dates are returned as arguments. + * + * @param from + * The start date and time of the highlighted area + * @param to + * The end date and time of the highlighted area + * @param monthlyMode + * Is the calendar in monthly mode + */ + protected void fireRangeSelect(Date from, Date to, boolean monthlyMode) { + fireEvent(new RangeSelectEvent(this, from, to, monthlyMode)); + } + + /** + * Fires an event resize event. The event is fired when a user resizes the + * event in the calendar causing the time range of the event to increase or + * decrease. The new start and end times are returned as arguments to this + * method. + * + * @param index + * The index of the event in the event cache + * @param startTime + * The new start date and time of the event + * @param endTime + * The new end date and time of the event + */ + protected void fireEventResize(int index, Date startTime, Date endTime) { + EventResize event = new EventResize(this, events.get(index), startTime, + endTime); + + if (calendarEventProvider instanceof EventResizeHandler) { + // Notify event provider if it is an event resize handler + ((EventResizeHandler) calendarEventProvider).eventResize(event); + } + + // Notify event resize handler attached by using the + // setHandler(EventMoveHandler) method + fireEvent(event); + } + + /** + * Localized display names for week days starting from sunday. Returned + * array's length is always 7. + * + * @return Array of localized weekday names. + */ + protected String[] getDayNamesShort() { + DateFormatSymbols s = new DateFormatSymbols(getLocale()); + return Arrays.copyOfRange(s.getWeekdays(), 1, 8); + } + + /** + * Localized display names for months starting from January. Returned + * array's length is always 12. + * + * @return Array of localized month names. + */ + protected String[] getMonthNamesShort() { + DateFormatSymbols s = new DateFormatSymbols(getLocale()); + return Arrays.copyOf(s.getShortMonths(), 12); + } + + /** + * Gets a date that is first day in the week that target given date belongs + * to. + * + * @param date + * Target date + * @return Date that is first date in same week that given date is. + */ + protected Date getFirstDateForWeek(Date date) { + int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + currentCalendar.setTime(date); + while (firstDayOfWeek != currentCalendar + .get(java.util.Calendar.DAY_OF_WEEK)) { + currentCalendar.add(java.util.Calendar.DATE, -1); + } + return currentCalendar.getTime(); + } + + /** + * Gets a date that is last day in the week that target given date belongs + * to. + * + * @param date + * Target date + * @return Date that is last date in same week that given date is. + */ + protected Date getLastDateForWeek(Date date) { + currentCalendar.setTime(date); + currentCalendar.add(java.util.Calendar.DATE, 1); + int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + // Roll to weeks last day using firstdayofweek. Roll until FDofW is + // found and then roll back one day. + while (firstDayOfWeek != currentCalendar + .get(java.util.Calendar.DAY_OF_WEEK)) { + currentCalendar.add(java.util.Calendar.DATE, 1); + } + currentCalendar.add(java.util.Calendar.DATE, -1); + return currentCalendar.getTime(); + } + + /** + * Calculates the end time of the day using the given calendar and date + * + * @param date + * @param calendar + * the calendar instance to be used in the calculation. The given + * instance is unchanged in this operation. + * @return the given date, with time set to the end of the day + */ + private static Date getEndOfDay(java.util.Calendar calendar, Date date) { + java.util.Calendar calendarClone = (java.util.Calendar) calendar + .clone(); + + calendarClone.setTime(date); + calendarClone.set(java.util.Calendar.MILLISECOND, + calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND)); + calendarClone.set(java.util.Calendar.SECOND, + calendarClone.getActualMaximum(java.util.Calendar.SECOND)); + calendarClone.set(java.util.Calendar.MINUTE, + calendarClone.getActualMaximum(java.util.Calendar.MINUTE)); + calendarClone.set(java.util.Calendar.HOUR, + calendarClone.getActualMaximum(java.util.Calendar.HOUR)); + calendarClone.set(java.util.Calendar.HOUR_OF_DAY, + calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY)); + + return calendarClone.getTime(); + } + + /** + * Calculates the end time of the day using the given calendar and date + * + * @param date + * @param calendar + * the calendar instance to be used in the calculation. The given + * instance is unchanged in this operation. + * @return the given date, with time set to the end of the day + */ + private static Date getStartOfDay(java.util.Calendar calendar, Date date) { + java.util.Calendar calendarClone = (java.util.Calendar) calendar + .clone(); + + calendarClone.setTime(date); + calendarClone.set(java.util.Calendar.MILLISECOND, 0); + calendarClone.set(java.util.Calendar.SECOND, 0); + calendarClone.set(java.util.Calendar.MINUTE, 0); + calendarClone.set(java.util.Calendar.HOUR, 0); + calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0); + + return calendarClone.getTime(); + } + + /** + * Finds the first day of the week and returns a day representing the start + * of that day + * + * @param start + * The actual date + * @param expandToFullWeek + * Should the returned date be moved to the start of the week + * @return If expandToFullWeek is set then it returns the first day of the + * week, else it returns a clone of the actual date with the time + * set to the start of the day + */ + protected Date expandStartDate(Date start, boolean expandToFullWeek) { + // If the duration is more than week, use monthly view and get startweek + // and endweek. Example if views daterange is from tuesday to next weeks + // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = + // monday + if (expandToFullWeek) { + start = getFirstDateForWeek(start); + + } else { + start = (Date) start.clone(); + } + + // Always expand to the start of the first day to the end of the last + // day + start = getStartOfDay(currentCalendar, start); + + return start; + } + + /** + * Finds the last day of the week and returns a day representing the end of + * that day + * + * @param end + * The actual date + * @param expandToFullWeek + * Should the returned date be moved to the end of the week + * @return If expandToFullWeek is set then it returns the last day of the + * week, else it returns a clone of the actual date with the time + * set to the end of the day + */ + protected Date expandEndDate(Date end, boolean expandToFullWeek) { + // If the duration is more than week, use monthly view and get startweek + // and endweek. Example if views daterange is from tuesday to next weeks + // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = + // monday + if (expandToFullWeek) { + end = getLastDateForWeek(end); + + } else { + end = (Date) end.clone(); + } + + // Always expand to the start of the first day to the end of the last + // day + end = getEndOfDay(currentCalendar, end); + + return end; + } + + /** + * Set the {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} to be used with this calendar. The EventProvider + * is used to query for events to show, and must be non-null. By default a + * {@link com.vaadin.addon.calendar.event.BasicEventProvider + * BasicEventProvider} is used. + * + * @param calendarEventProvider + * the calendarEventProvider to set. Cannot be null. + */ + public void setEventProvider(CalendarEventProvider calendarEventProvider) { + if (calendarEventProvider == null) { + throw new IllegalArgumentException( + "Calendar event provider cannot be null"); + } + + // remove old listener + if (getEventProvider() instanceof EventSetChangeNotifier) { + ((EventSetChangeNotifier) getEventProvider()) + .removeEventSetChangeListener(this); + } + + this.calendarEventProvider = calendarEventProvider; + + // add new listener + if (calendarEventProvider instanceof EventSetChangeNotifier) { + ((EventSetChangeNotifier) calendarEventProvider) + .addEventSetChangeListener(this); + } + } + + /** + * @return the {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} currently used + */ + public CalendarEventProvider getEventProvider() { + return calendarEventProvider; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.ui.CalendarEvents.EventChangeListener# + * eventChange (com.vaadin.addon.calendar.ui.CalendarEvents.EventChange) + */ + @Override + public void eventSetChange(EventSetChangeEvent changeEvent) { + // sanity check + if (calendarEventProvider == changeEvent.getProvider()) { + markAsDirty(); + } + } + + /** + * Set the handler for the given type information. Mirrors + * {@link #addListener(String, Class, Object, Method) addListener} from + * AbstractComponent + * + * @param eventId + * A unique id for the event. Usually one of + * {@link CalendarEventId} + * @param eventType + * The class of the event, most likely a subclass of + * {@link CalendarComponentEvent} + * @param listener + * A listener that listens to the given event + * @param listenerMethod + * The method on the lister to call when the event is triggered + */ + protected void setHandler(String eventId, Class eventType, + EventListener listener, Method listenerMethod) { + if (handlers.get(eventId) != null) { + removeListener(eventId, eventType, handlers.get(eventId)); + handlers.remove(eventId); + } + + if (listener != null) { + addListener(eventId, eventType, listener, listenerMethod); + handlers.put(eventId, listener); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler) + */ + @Override + public void setHandler(ForwardHandler listener) { + setHandler(ForwardEvent.EVENT_ID, ForwardEvent.class, listener, + ForwardHandler.forwardMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler) + */ + @Override + public void setHandler(BackwardHandler listener) { + setHandler(BackwardEvent.EVENT_ID, BackwardEvent.class, listener, + BackwardHandler.backwardMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler) + */ + @Override + public void setHandler(DateClickHandler listener) { + setHandler(DateClickEvent.EVENT_ID, DateClickEvent.class, listener, + DateClickHandler.dateClickMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventClickHandler) + */ + @Override + public void setHandler(EventClickHandler listener) { + setHandler(EventClick.EVENT_ID, EventClick.class, listener, + EventClickHandler.eventClickMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler) + */ + @Override + public void setHandler(WeekClickHandler listener) { + setHandler(WeekClick.EVENT_ID, WeekClick.class, listener, + WeekClickHandler.weekClickMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler + * ) + */ + @Override + public void setHandler(EventResizeHandler listener) { + setHandler(EventResize.EVENT_ID, EventResize.class, listener, + EventResizeHandler.eventResizeMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectHandler + * ) + */ + @Override + public void setHandler(RangeSelectHandler listener) { + setHandler(RangeSelectEvent.EVENT_ID, RangeSelectEvent.class, listener, + RangeSelectHandler.rangeSelectMethod); + + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler) + */ + @Override + public void setHandler(EventMoveHandler listener) { + setHandler(MoveEvent.EVENT_ID, MoveEvent.class, listener, + EventMoveHandler.eventMoveMethod); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents. + * CalendarEventNotifier #getHandler(java.lang.String) + */ + @Override + public EventListener getHandler(String eventId) { + return handlers.get(eventId); + } + + /** + * Get the currently active drop handler + */ + @Override + public DropHandler getDropHandler() { + return dropHandler; + } + + /** + * Set the drop handler for the calendar See {@link DropHandler} for + * implementation details. + * + * @param dropHandler + * The drop handler to set + */ + public void setDropHandler(DropHandler dropHandler) { + this.dropHandler = dropHandler; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map) + */ + @Override + public TargetDetails translateDropTargetDetails( + Map clientVariables) { + Map serverVariables = new HashMap(); + + if (clientVariables.containsKey("dropSlotIndex")) { + int slotIndex = (Integer) clientVariables.get("dropSlotIndex"); + int dayIndex = (Integer) clientVariables.get("dropDayIndex"); + + currentCalendar.setTime(getStartOfDay(currentCalendar, startDate)); + currentCalendar.add(java.util.Calendar.DATE, dayIndex); + + // change this if slot length is modified + currentCalendar.add(java.util.Calendar.MINUTE, slotIndex * 30); + + serverVariables.put("dropTime", currentCalendar.getTime()); + + } else { + int dayIndex = (Integer) clientVariables.get("dropDayIndex"); + currentCalendar.setTime(expandStartDate(startDate, true)); + currentCalendar.add(java.util.Calendar.DATE, dayIndex); + serverVariables.put("dropDay", currentCalendar.getTime()); + } + serverVariables.put("mouseEvent", clientVariables.get("mouseEvent")); + + CalendarTargetDetails td = new CalendarTargetDetails(serverVariables, + this); + td.setHasDropTime(clientVariables.containsKey("dropSlotIndex")); + + return td; + } + + /** + * Sets a container as a data source for the events in the calendar. + * Equivalent for doing + * Calendar.setEventProvider(new ContainerEventProvider(container)) + * + * Use this method if you are adding a container which uses the default + * property ids like {@link BeanItemContainer} for instance. If you are + * using custom properties instead use + * {@link Calendar#setContainerDataSource(com.vaadin.v7.data.Container.Indexed, Object, Object, Object, Object, Object)} + * + * Please note that the container must be sorted by date! + * + * @param container + * The container to use as a datasource + */ + public void setContainerDataSource(Container.Indexed container) { + ContainerEventProvider provider = new ContainerEventProvider(container); + provider.addEventSetChangeListener( + new CalendarEventProvider.EventSetChangeListener() { + @Override + public void eventSetChange( + EventSetChangeEvent changeEvent) { + // Repaint if events change + markAsDirty(); + } + }); + provider.addEventChangeListener(new EventChangeListener() { + @Override + public void eventChange(EventChangeEvent changeEvent) { + // Repaint if event changes + markAsDirty(); + } + }); + setEventProvider(provider); + } + + /** + * Sets a container as a data source for the events in the calendar. + * Equivalent for doing + * Calendar.setEventProvider(new ContainerEventProvider(container)) + * + * Please note that the container must be sorted by date! + * + * @param container + * The container to use as a data source + * @param captionProperty + * The property that has the caption, null if no caption property + * is present + * @param descriptionProperty + * The property that has the description, null if no description + * property is present + * @param startDateProperty + * The property that has the starting date + * @param endDateProperty + * The property that has the ending date + * @param styleNameProperty + * The property that has the stylename, null if no stylname + * property is present + */ + public void setContainerDataSource(Container.Indexed container, + Object captionProperty, Object descriptionProperty, + Object startDateProperty, Object endDateProperty, + Object styleNameProperty) { + ContainerEventProvider provider = new ContainerEventProvider(container); + provider.setCaptionProperty(captionProperty); + provider.setDescriptionProperty(descriptionProperty); + provider.setStartDateProperty(startDateProperty); + provider.setEndDateProperty(endDateProperty); + provider.setStyleNameProperty(styleNameProperty); + provider.addEventSetChangeListener( + new CalendarEventProvider.EventSetChangeListener() { + @Override + public void eventSetChange( + EventSetChangeEvent changeEvent) { + // Repaint if events change + markAsDirty(); + } + }); + provider.addEventChangeListener(new EventChangeListener() { + @Override + public void eventChange(EventChangeEvent changeEvent) { + // Repaint if event changes + markAsDirty(); + } + }); + setEventProvider(provider); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. + * util.Date, java.util.Date) + */ + @Override + public List getEvents(Date startDate, Date endDate) { + return getEventProvider().getEvents(startDate, endDate); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void addEvent(CalendarEvent event) { + if (getEventProvider() instanceof CalendarEditableEventProvider) { + CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); + provider.addEvent(event); + markAsDirty(); + } else { + throw new UnsupportedOperationException( + "Event provider does not support adding events"); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void removeEvent(CalendarEvent event) { + if (getEventProvider() instanceof CalendarEditableEventProvider) { + CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); + provider.removeEvent(event); + markAsDirty(); + } else { + throw new UnsupportedOperationException( + "Event provider does not support removing events"); + } + } + + /** + * Adds an action handler to the calender that handles event produced by the + * context menu. + * + *

    + * The {@link Handler#getActions(Object, Object)} parameters depend on what + * view the Calendar is in: + *

      + *
    • If the Calendar is in Day or Week View then the target + * parameter will be a {@link CalendarDateRange} with a range of + * half-an-hour. The {@link Handler#getActions(Object, Object)} method will + * be called once per half-hour slot.
    • + *
    • If the Calendar is in Month View then the target parameter + * will be a {@link CalendarDateRange} with a range of one day. The + * {@link Handler#getActions(Object, Object)} will be called once for each + * day. + *
    + * The Dates passed into the {@link CalendarDateRange} are in the same + * timezone as the calendar is. + *

    + * + *

    + * The {@link Handler#handleAction(Action, Object, Object)} parameters + * depend on what the context menu is called upon: + *

      + *
    • If the context menu is called upon an event then the target parameter + * is the event, i.e. instanceof {@link CalendarEvent}
    • + *
    • If the context menu is called upon an empty slot then the target is a + * {@link Date} representing that slot + *
    + *

    + */ + @Override + public void addActionHandler(Handler actionHandler) { + if (actionHandler != null) { + if (actionHandlers == null) { + actionHandlers = new LinkedList(); + actionMapper = new KeyMapper(); + } + if (!actionHandlers.contains(actionHandler)) { + actionHandlers.add(actionHandler); + markAsDirty(); + } + } + } + + /** + * Is the calendar in a mode where all days of the month is shown + * + * @return Returns true if calendar is in monthly mode and false if it is in + * weekly mode + */ + public boolean isMonthlyMode() { + CalendarState state = getState(false); + if (state.days != null) { + return state.days.size() > 7; + } else { + // Default mode + return true; + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.Action.Container#removeActionHandler(com.vaadin.event + * .Action.Handler) + */ + @Override + public void removeActionHandler(Handler actionHandler) { + if (actionHandlers != null && actionHandlers.contains(actionHandler)) { + actionHandlers.remove(actionHandler); + if (actionHandlers.isEmpty()) { + actionHandlers = null; + actionMapper = null; + } + markAsDirty(); + } + } + + private class CalendarServerRpcImpl implements CalendarServerRpc { + + @Override + public void eventMove(int eventIndex, String newDate) { + if (!isClientChangeAllowed()) { + return; + } + if (newDate != null) { + try { + Date d = df_date_time.parse(newDate); + if (eventIndex >= 0 && eventIndex < events.size() + && events.get(eventIndex) != null) { + fireEventMove(eventIndex, d); + } + } catch (ParseException e) { + getLogger().log(Level.WARNING, e.getMessage()); + } + } + } + + @Override + public void rangeSelect(String range) { + if (!isClientChangeAllowed()) { + return; + } + + if (range != null && range.length() > 14 && range.contains("TO")) { + String[] dates = range.split("TO"); + try { + Date d1 = df_date.parse(dates[0]); + Date d2 = df_date.parse(dates[1]); + + fireRangeSelect(d1, d2, true); + + } catch (ParseException e) { + // NOP + } + } else if (range != null && range.length() > 12 + && range.contains(":")) { + String[] dates = range.split(":"); + if (dates.length == 3) { + try { + Date d = df_date.parse(dates[0]); + currentCalendar.setTime(d); + int startMinutes = Integer.parseInt(dates[1]); + int endMinutes = Integer.parseInt(dates[2]); + currentCalendar.add(java.util.Calendar.MINUTE, + startMinutes); + Date start = currentCalendar.getTime(); + currentCalendar.add(java.util.Calendar.MINUTE, + endMinutes - startMinutes); + Date end = currentCalendar.getTime(); + fireRangeSelect(start, end, false); + } catch (ParseException e) { + // NOP + } catch (NumberFormatException e) { + // NOP + } + } + } + } + + @Override + public void forward() { + fireEvent(new ForwardEvent(Calendar.this)); + } + + @Override + public void backward() { + fireEvent(new BackwardEvent(Calendar.this)); + } + + @Override + public void dateClick(String date) { + if (date != null && date.length() > 6) { + try { + Date d = df_date.parse(date); + fireDateClick(d); + } catch (ParseException e) { + } + } + } + + @Override + public void weekClick(String event) { + if (event.length() > 0 && event.contains("w")) { + String[] splitted = event.split("w"); + if (splitted.length == 2) { + try { + int yr = Integer.parseInt(splitted[0]); + int week = Integer.parseInt(splitted[1]); + fireWeekClick(week, yr); + } catch (NumberFormatException e) { + // NOP + } + } + } + } + + @Override + public void eventClick(int eventIndex) { + if (!isEventClickAllowed()) { + return; + } + if (eventIndex >= 0 && eventIndex < events.size() + && events.get(eventIndex) != null) { + fireEventClick(eventIndex); + } + } + + @Override + public void eventResize(int eventIndex, String newStartDate, + String newEndDate) { + if (!isClientChangeAllowed()) { + return; + } + if (newStartDate != null && !"".equals(newStartDate) + && newEndDate != null && !"".equals(newEndDate)) { + try { + Date newStartTime = df_date_time.parse(newStartDate); + Date newEndTime = df_date_time.parse(newEndDate); + + fireEventResize(eventIndex, newStartTime, newEndTime); + } catch (ParseException e) { + // NOOP + } + } + } + + @Override + public void scroll(int scrollPosition) { + scrollTop = scrollPosition; + markAsDirty(); + } + + @Override + public void actionOnEmptyCell(String actionKey, String startDate, + String endDate) { + Action action = actionMapper.get(actionKey); + SimpleDateFormat formatter = new SimpleDateFormat( + DateConstants.ACTION_DATE_FORMAT_PATTERN); + formatter.setTimeZone(getTimeZone()); + try { + Date start = formatter.parse(startDate); + for (Action.Handler ah : actionHandlers) { + ah.handleAction(action, Calendar.this, start); + } + + } catch (ParseException e) { + getLogger().log(Level.WARNING, + "Could not parse action date string"); + } + + } + + @Override + public void actionOnEvent(String actionKey, String startDate, + String endDate, int eventIndex) { + Action action = actionMapper.get(actionKey); + SimpleDateFormat formatter = new SimpleDateFormat( + DateConstants.ACTION_DATE_FORMAT_PATTERN); + formatter.setTimeZone(getTimeZone()); + for (Action.Handler ah : actionHandlers) { + ah.handleAction(action, Calendar.this, events.get(eventIndex)); + } + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.server.VariableOwner#changeVariables(java.lang.Object, + * java.util.Map) + */ + @Override + public void changeVariables(Object source, Map variables) { + /* + * Only defined to fulfill the LegacyComponent interface used for + * calendar drag & drop. No implementation required. + */ + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.LegacyComponent#paintContent(com.vaadin.server.PaintTarget) + */ + @Override + public void paintContent(PaintTarget target) throws PaintException { + if (dropHandler != null) { + dropHandler.getAcceptCriterion().paint(target); + } + } + + /** + * Sets whether the event captions are rendered as HTML. + *

    + * If set to true, the captions are rendered in the browser as HTML and the + * developer is responsible for ensuring no harmful HTML is used. If set to + * false, the caption is rendered in the browser as plain text. + *

    + * The default is false, i.e. to render that caption as plain text. + * + * @param captionAsHtml + * true if the captions are rendered as HTML, false if rendered + * as plain text + */ + public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) { + getState().eventCaptionAsHtml = eventCaptionAsHtml; + } + + /** + * Checks whether event captions are rendered as HTML + *

    + * The default is false, i.e. to render that caption as plain text. + * + * @return true if the captions are rendered as HTML, false if rendered as + * plain text + */ + public boolean isEventCaptionAsHtml() { + return getState(false).eventCaptionAsHtml; + } + + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + Attributes attr = design.attributes(); + if (design.hasAttr("time-format")) { + setTimeFormat(TimeFormat.valueOf( + "Format" + design.attr("time-format").toUpperCase())); + } + + if (design.hasAttr("start-date")) { + setStartDate(DesignAttributeHandler.readAttribute("start-date", + attr, Date.class)); + } + if (design.hasAttr("end-date")) { + setEndDate(DesignAttributeHandler.readAttribute("end-date", attr, + Date.class)); + } + }; + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + if (currentTimeFormat != null) { + design.attr("time-format", + (currentTimeFormat == TimeFormat.Format12H ? "12h" + : "24h")); + } + if (startDate != null) { + design.attr("start-date", df_date.format(getStartDate())); + } + if (endDate != null) { + design.attr("end-date", df_date.format(getEndDate())); + } + if (!getTimeZone().equals(TimeZone.getDefault())) { + design.attr("time-zone", getTimeZone().getID()); + } + } + + @Override + protected Collection getCustomAttributes() { + Collection customAttributes = super.getCustomAttributes(); + customAttributes.add("time-format"); + customAttributes.add("start-date"); + customAttributes.add("end-date"); + return customAttributes; + } + + /** + * Allow setting first day of week independent of Locale. Set to null if you + * want first day of week being defined by the locale + * + * @since 7.6 + * @param dayOfWeek + * any of java.util.Calendar.SUNDAY..java.util.Calendar.SATURDAY + * or null to revert to default first day of week by locale + */ + public void setFirstDayOfWeek(Integer dayOfWeek) { + int minimalSupported = java.util.Calendar.SUNDAY; + int maximalSupported = java.util.Calendar.SATURDAY; + if (dayOfWeek != null && (dayOfWeek < minimalSupported + || dayOfWeek > maximalSupported)) { + throw new IllegalArgumentException(String.format( + "Day of week must be between %s and %s. Actually received: %s", + minimalSupported, maximalSupported, dayOfWeek)); + } + customFirstDayOfWeek = dayOfWeek; + markAsDirty(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPicker.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPicker.java new file mode 100644 index 0000000000..34b03a2447 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPicker.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui; + +import com.vaadin.shared.ui.colorpicker.Color; + +/** + * A class that defines default (button-like) implementation for a color picker + * component. + * + * @since 7.0.0 + * + * @see ColorPickerArea + * + */ +public class ColorPicker extends AbstractColorPicker { + + /** + * Instantiates a new color picker. + */ + public ColorPicker() { + super(); + } + + /** + * Instantiates a new color picker. + * + * @param popupCaption + * caption of the color select popup + */ + public ColorPicker(String popupCaption) { + super(popupCaption); + } + + /** + * Instantiates a new color picker. + * + * @param popupCaption + * caption of the color select popup + * @param initialColor + * the initial color + */ + public ColorPicker(String popupCaption, Color initialColor) { + super(popupCaption, initialColor); + setDefaultCaptionEnabled(true); + } + + @Override + protected void setDefaultStyles() { + setPrimaryStyleName(STYLENAME_BUTTON); + addStyleName(STYLENAME_DEFAULT); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPickerArea.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPickerArea.java new file mode 100644 index 0000000000..624d567ec7 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/ColorPickerArea.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui; + +import com.vaadin.shared.ui.colorpicker.Color; + +/** + * A class that defines area-like implementation for a color picker component. + * + * @since 7.0.0 + * + * @see ColorPicker + * + */ +public class ColorPickerArea extends AbstractColorPicker { + + /** + * Instantiates a new color picker. + */ + public ColorPickerArea() { + super(); + } + + /** + * Instantiates a new color picker. + * + * @param popupCaption + * caption of the color select popup + */ + public ColorPickerArea(String popupCaption) { + super(popupCaption); + } + + /** + * Instantiates a new color picker. + * + * @param popupCaption + * caption of the color select popup + * @param initialColor + * the initial color + */ + public ColorPickerArea(String popupCaption, Color initialColor) { + super(popupCaption, initialColor); + setDefaultCaptionEnabled(false); + } + + @Override + protected void setDefaultStyles() { + // state already has correct default + } + + @Override + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); + + if ("".equals(getState().height)) { + getState().height = "30px"; + } + if ("".equals(getState().width)) { + getState().width = "30px"; + } + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/ComboBox.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/ComboBox.java new file mode 100644 index 0000000000..50b6ac505c --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/ComboBox.java @@ -0,0 +1,926 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.vaadin.event.FieldEvents; +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.server.Resource; +import com.vaadin.shared.ui.combobox.ComboBoxServerRpc; +import com.vaadin.shared.ui.combobox.ComboBoxState; +import com.vaadin.shared.ui.combobox.FilteringMode; +import com.vaadin.ui.Component; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.util.filter.SimpleStringFilter; + +/** + * A filtering dropdown single-select. Suitable for newItemsAllowed, but it's + * turned of by default to avoid mistakes. Items are filtered based on user + * input, and loaded dynamically ("lazy-loading") from the server. You can turn + * on newItemsAllowed and change filtering mode (and also turn it off), but you + * can not turn on multi-select mode. + * + */ +@SuppressWarnings("serial") +public class ComboBox extends AbstractSelect + implements AbstractSelect.Filtering, FieldEvents.BlurNotifier, + FieldEvents.FocusNotifier { + + /** + * ItemStyleGenerator can be used to add custom styles to combo box items + * shown in the popup. The CSS class name that will be added to the item + * style names is v-filterselect-item-[style name]. + * + * @since 7.5.6 + * @see ComboBox#setItemStyleGenerator(ItemStyleGenerator) + */ + public interface ItemStyleGenerator extends Serializable { + + /** + * Called by ComboBox when an item is painted. + * + * @param source + * the source combo box + * @param itemId + * The itemId of the item to be painted. Can be + * null if null selection is allowed. + * @return The style name to add to this item. (the CSS class name will + * be v-filterselect-item-[style name] + */ + public String getStyle(ComboBox source, Object itemId); + } + + private ComboBoxServerRpc rpc = new ComboBoxServerRpc() { + @Override + public void createNewItem(String itemValue) { + if (isNewItemsAllowed()) { + // New option entered (and it is allowed) + if (itemValue != null && itemValue.length() > 0) { + getNewItemHandler().addNewItem(itemValue); + // rebuild list + filterstring = null; + prevfilterstring = null; + } + } + } + + @Override + public void setSelectedItem(String item) { + if (item == null) { + setValue(null, true); + } else { + final Object id = itemIdMapper.get(item); + if (id != null && id.equals(getNullSelectionItemId())) { + setValue(null, true); + } else { + setValue(id, true); + } + } + } + + @Override + public void requestPage(String filter, int page) { + filterstring = filter; + if (filterstring != null) { + filterstring = filterstring.toLowerCase(getLocale()); + } + currentPage = page; + + // TODO this should trigger a data-only update instead of a full + // repaint + requestRepaint(); + } + }; + + FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl( + this) { + @Override + protected void fireEvent(Component.Event event) { + ComboBox.this.fireEvent(event); + } + }; + + // Current page when the user is 'paging' trough options + private int currentPage = -1; + + private String filterstring; + private String prevfilterstring; + + /** + * Number of options that pass the filter, excluding the null item if any. + */ + private int filteredSize; + + /** + * Cache of filtered options, used only by the in-memory filtering system. + */ + private List filteredOptions; + + /** + * Flag to indicate that request repaint is called by filter request only + */ + private boolean optionRequest; + + /** + * True while painting to suppress item set change notifications that could + * be caused by temporary filtering. + */ + private boolean isPainting; + + /** + * Flag to indicate whether to scroll the selected item visible (select the + * page on which it is) when opening the popup or not. Only applies to + * single select mode. + * + * This requires finding the index of the item, which can be expensive in + * many large lazy loading containers. + */ + private boolean scrollToSelectedItem = true; + + private ItemStyleGenerator itemStyleGenerator = null; + + public ComboBox() { + init(); + } + + public ComboBox(String caption, Collection options) { + super(caption, options); + init(); + } + + public ComboBox(String caption, Container dataSource) { + super(caption, dataSource); + init(); + } + + public ComboBox(String caption) { + super(caption); + init(); + } + + /** + * Initialize the ComboBox with default settings and register client to + * server RPC implementation. + */ + private void init() { + registerRpc(rpc); + registerRpc(focusBlurRpc); + + setNewItemsAllowed(false); + setImmediate(true); + } + + /** + * Gets the current input prompt. + * + * @see #setInputPrompt(String) + * @return the current input prompt, or null if not enabled + */ + public String getInputPrompt() { + return getState(false).inputPrompt; + } + + /** + * Sets the input prompt - a textual prompt that is displayed when the + * select would otherwise be empty, to prompt the user for input. + * + * @param inputPrompt + * the desired input prompt, or null to disable + */ + public void setInputPrompt(String inputPrompt) { + getState().inputPrompt = inputPrompt; + } + + private boolean isFilteringNeeded() { + return filterstring != null && filterstring.length() > 0 + && getFilteringMode() != FilteringMode.OFF; + } + + /** + * A class representing an item in a ComboBox for server to client + * communication. This class is for internal use only and subject to change. + * + * @since + */ + private static class ComboBoxItem implements Serializable { + String key = ""; + String caption = ""; + String style = null; + Resource icon = null; + + // constructor for a null item + public ComboBoxItem() { + } + + public ComboBoxItem(String key, String caption, String style, + Resource icon) { + this.key = key; + this.caption = caption; + this.style = style; + this.icon = icon; + } + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + isPainting = true; + try { + // clear caption change listeners + getCaptionChangeListener().clear(); + + if (isNewItemsAllowed()) { + target.addAttribute("allownewitem", true); + } + + boolean needNullSelectOption = false; + if (isNullSelectionAllowed()) { + target.addAttribute("nullselect", true); + needNullSelectOption = (getNullSelectionItemId() == null); + if (!needNullSelectOption) { + target.addAttribute("nullselectitem", true); + } + } + + // Constructs selected keys array + String[] selectedKeys = new String[(getValue() == null + && getNullSelectionItemId() == null ? 0 : 1)]; + + // Paints the options and create array of selected id keys + int keyIndex = 0; + + if (currentPage < 0) { + optionRequest = false; + currentPage = 0; + filterstring = ""; + } + + boolean nullFilteredOut = isFilteringNeeded(); + // null option is needed and not filtered out, even if not on + // current page + boolean nullOptionVisible = needNullSelectOption + && !nullFilteredOut; + + // first try if using container filters is possible + List options = getOptionsWithFilter(nullOptionVisible); + if (null == options) { + // not able to use container filters, perform explicit in-memory + // filtering + options = getFilteredOptions(); + filteredSize = options.size(); + options = sanitetizeList(options, nullOptionVisible); + } + + final boolean paintNullSelection = needNullSelectOption + && currentPage == 0 && !nullFilteredOut; + + List items = new ArrayList(); + + if (paintNullSelection) { + ComboBoxItem item = new ComboBoxItem(); + item.style = getItemStyle(null); + items.add(item); + } + + final Iterator i = options.iterator(); + // Paints the available selection options from data source + + while (i.hasNext()) { + + final Object id = i.next(); + + if (!isNullSelectionAllowed() && id != null + && id.equals(getNullSelectionItemId()) + && !isSelected(id)) { + continue; + } + + // Gets the option attribute values + final String key = itemIdMapper.key(id); + final String caption = getItemCaption(id); + final Resource icon = getItemIcon(id); + + getCaptionChangeListener().addNotifierForItem(id); + + // Prepare to paint the option + ComboBoxItem item = new ComboBoxItem(key, caption, + getItemStyle(id), icon); + items.add(item); + if (keyIndex < selectedKeys.length && isSelected(id)) { + // at most one item can be selected at a time + selectedKeys[keyIndex++] = key; + } + } + + // paint the items + target.startTag("options"); + for (ComboBoxItem item : items) { + target.startTag("so"); + if (item.icon != null) { + target.addAttribute("icon", item.icon); + } + target.addAttribute("caption", item.caption); + target.addAttribute("key", item.key); + if (item.style != null) { + target.addAttribute("style", item.style); + } + + target.endTag("so"); + } + target.endTag("options"); + + target.addAttribute("totalitems", + size() + (needNullSelectOption ? 1 : 0)); + if (filteredSize > 0 || nullOptionVisible) { + target.addAttribute("totalMatches", + filteredSize + (nullOptionVisible ? 1 : 0)); + } + + // Paint variables + target.addVariable(this, "selected", selectedKeys); + if (getValue() != null && selectedKeys[0] == null) { + // not always available, e.g. scrollToSelectedIndex=false + // Give the caption for selected item still, not to make it look + // like there is no selection at all + target.addAttribute("selectedCaption", + getItemCaption(getValue())); + } + if (isNewItemsAllowed()) { + target.addVariable(this, "newitem", ""); + } + + target.addVariable(this, "filter", filterstring); + target.addVariable(this, "page", currentPage); + + currentPage = -1; // current page is always set by client + + optionRequest = true; + } finally { + isPainting = false; + } + + } + + private String getItemStyle(Object itemId) throws PaintException { + if (itemStyleGenerator != null) { + return itemStyleGenerator.getStyle(this, itemId); + } + return null; + } + + /** + * Sets whether it is possible to input text into the field or whether the + * field area of the component is just used to show what is selected. By + * disabling text input, the comboBox will work in the same way as a + * {@link NativeSelect} + * + * @see #isTextInputAllowed() + * + * @param textInputAllowed + * true to allow entering text, false to just show the current + * selection + */ + public void setTextInputAllowed(boolean textInputAllowed) { + getState().textInputAllowed = textInputAllowed; + } + + /** + * Returns true if the user can enter text into the field to either filter + * the selections or enter a new value if {@link #isNewItemsAllowed()} + * returns true. If text input is disabled, the comboBox will work in the + * same way as a {@link NativeSelect} + * + * @return + */ + public boolean isTextInputAllowed() { + return getState(false).textInputAllowed; + } + + @Override + protected ComboBoxState getState() { + return (ComboBoxState) super.getState(); + } + + @Override + protected ComboBoxState getState(boolean markAsDirty) { + return (ComboBoxState) super.getState(markAsDirty); + } + + /** + * Returns the filtered options for the current page using a container + * filter. + * + * As a size effect, {@link #filteredSize} is set to the total number of + * items passing the filter. + * + * The current container must be {@link Filterable} and {@link Indexed}, and + * the filtering mode must be suitable for container filtering (tested with + * {@link #canUseContainerFilter()}). + * + * Use {@link #getFilteredOptions()} and + * {@link #sanitetizeList(List, boolean)} if this is not the case. + * + * @param needNullSelectOption + * @return filtered list of options (may be empty) or null if cannot use + * container filters + */ + protected List getOptionsWithFilter(boolean needNullSelectOption) { + Container container = getContainerDataSource(); + + if (getPageLength() == 0 && !isFilteringNeeded()) { + // no paging or filtering: return all items + filteredSize = container.size(); + assert filteredSize >= 0; + return new ArrayList(container.getItemIds()); + } + + if (!(container instanceof Filterable) + || !(container instanceof Indexed) + || getItemCaptionMode() != ITEM_CAPTION_MODE_PROPERTY) { + return null; + } + + Filterable filterable = (Filterable) container; + + Filter filter = buildFilter(filterstring, getFilteringMode()); + + // adding and removing filters leads to extraneous item set + // change events from the underlying container, but the ComboBox does + // not process or propagate them based on the flag filteringContainer + if (filter != null) { + filterable.addContainerFilter(filter); + } + + // try-finally to ensure that the filter is removed from container even + // if a exception is thrown... + try { + Indexed indexed = (Indexed) container; + + int indexToEnsureInView = -1; + + // if not an option request (item list when user changes page), go + // to page with the selected item after filtering if accepted by + // filter + Object selection = getValue(); + if (isScrollToSelectedItem() && !optionRequest + && selection != null) { + // ensure proper page + indexToEnsureInView = indexed.indexOfId(selection); + } + + filteredSize = container.size(); + assert filteredSize >= 0; + currentPage = adjustCurrentPage(currentPage, needNullSelectOption, + indexToEnsureInView, filteredSize); + int first = getFirstItemIndexOnCurrentPage(needNullSelectOption, + filteredSize); + int last = getLastItemIndexOnCurrentPage(needNullSelectOption, + filteredSize, first); + + // Compute the number of items to fetch from the indexes given or + // based on the filtered size of the container + int lastItemToFetch = Math.min(last, filteredSize - 1); + int nrOfItemsToFetch = (lastItemToFetch + 1) - first; + + List options = indexed.getItemIds(first, nrOfItemsToFetch); + + return options; + } finally { + // to the outside, filtering should not be visible + if (filter != null) { + filterable.removeContainerFilter(filter); + } + } + } + + /** + * Constructs a filter instance to use when using a Filterable container in + * the ITEM_CAPTION_MODE_PROPERTY mode. + * + * Note that the client side implementation expects the filter string to + * apply to the item caption string it sees, so changing the behavior of + * this method can cause problems. + * + * @param filterString + * @param filteringMode + * @return + */ + protected Filter buildFilter(String filterString, + FilteringMode filteringMode) { + Filter filter = null; + + if (null != filterString && !"".equals(filterString)) { + switch (filteringMode) { + case OFF: + break; + case STARTSWITH: + filter = new SimpleStringFilter(getItemCaptionPropertyId(), + filterString, true, true); + break; + case CONTAINS: + filter = new SimpleStringFilter(getItemCaptionPropertyId(), + filterString, true, false); + break; + } + } + return filter; + } + + @Override + public void containerItemSetChange(Container.ItemSetChangeEvent event) { + if (!isPainting) { + super.containerItemSetChange(event); + } + } + + /** + * Makes correct sublist of given list of options. + * + * If paint is not an option request (affected by page or filter change), + * page will be the one where possible selection exists. + * + * Detects proper first and last item in list to return right page of + * options. Also, if the current page is beyond the end of the list, it will + * be adjusted. + * + * @param options + * @param needNullSelectOption + * flag to indicate if nullselect option needs to be taken into + * consideration + */ + private List sanitetizeList(List options, + boolean needNullSelectOption) { + + if (getPageLength() != 0 && options.size() > getPageLength()) { + + int indexToEnsureInView = -1; + + // if not an option request (item list when user changes page), go + // to page with the selected item after filtering if accepted by + // filter + Object selection = getValue(); + if (isScrollToSelectedItem() && !optionRequest + && selection != null) { + // ensure proper page + indexToEnsureInView = options.indexOf(selection); + } + + int size = options.size(); + currentPage = adjustCurrentPage(currentPage, needNullSelectOption, + indexToEnsureInView, size); + int first = getFirstItemIndexOnCurrentPage(needNullSelectOption, + size); + int last = getLastItemIndexOnCurrentPage(needNullSelectOption, size, + first); + return options.subList(first, last + 1); + } else { + return options; + } + } + + /** + * Returns the index of the first item on the current page. The index is to + * the underlying (possibly filtered) contents. The null item, if any, does + * not have an index but takes up a slot on the first page. + * + * @param needNullSelectOption + * true if a null option should be shown before any other options + * (takes up the first slot on the first page, not counted in + * index) + * @param size + * number of items after filtering (not including the null item, + * if any) + * @return first item to show on the UI (index to the filtered list of + * options, not taking the null item into consideration if any) + */ + private int getFirstItemIndexOnCurrentPage(boolean needNullSelectOption, + int size) { + // Not all options are visible, find out which ones are on the + // current "page". + int first = currentPage * getPageLength(); + if (needNullSelectOption && currentPage > 0) { + first--; + } + return first; + } + + /** + * Returns the index of the last item on the current page. The index is to + * the underlying (possibly filtered) contents. If needNullSelectOption is + * true, the null item takes up the first slot on the first page, + * effectively reducing the first page size by one. + * + * @param needNullSelectOption + * true if a null option should be shown before any other options + * (takes up the first slot on the first page, not counted in + * index) + * @param size + * number of items after filtering (not including the null item, + * if any) + * @param first + * index in the filtered view of the first item of the page + * @return index in the filtered view of the last item on the page + */ + private int getLastItemIndexOnCurrentPage(boolean needNullSelectOption, + int size, int first) { + // page length usable for non-null items + int effectivePageLength = getPageLength() + - (needNullSelectOption && (currentPage == 0) ? 1 : 0); + return Math.min(size - 1, first + effectivePageLength - 1); + } + + /** + * Adjusts the index of the current page if necessary: make sure the current + * page is not after the end of the contents, and optionally go to the page + * containg a specific item. There are no side effects but the adjusted page + * index is returned. + * + * @param page + * page number to use as the starting point + * @param needNullSelectOption + * true if a null option should be shown before any other options + * (takes up the first slot on the first page, not counted in + * index) + * @param indexToEnsureInView + * index of an item that should be included on the page (in the + * data set, not counting the null item if any), -1 for none + * @param size + * number of items after filtering (not including the null item, + * if any) + */ + private int adjustCurrentPage(int page, boolean needNullSelectOption, + int indexToEnsureInView, int size) { + if (indexToEnsureInView != -1) { + int newPage = (indexToEnsureInView + (needNullSelectOption ? 1 : 0)) + / getPageLength(); + page = newPage; + } + // adjust the current page if beyond the end of the list + if (page * getPageLength() > size) { + page = (size + (needNullSelectOption ? 1 : 0)) / getPageLength(); + } + return page; + } + + /** + * Filters the options in memory and returns the full filtered list. + * + * This can be less efficient than using container filters, so use + * {@link #getOptionsWithFilter(boolean)} if possible (filterable container + * and suitable item caption mode etc.). + * + * @return + */ + protected List getFilteredOptions() { + if (!isFilteringNeeded()) { + prevfilterstring = null; + filteredOptions = new LinkedList(getItemIds()); + return filteredOptions; + } + + if (filterstring.equals(prevfilterstring)) { + return filteredOptions; + } + + Collection items; + if (prevfilterstring != null + && filterstring.startsWith(prevfilterstring)) { + items = filteredOptions; + } else { + items = getItemIds(); + } + prevfilterstring = filterstring; + + filteredOptions = new LinkedList(); + for (final Iterator it = items.iterator(); it.hasNext();) { + final Object itemId = it.next(); + String caption = getItemCaption(itemId); + if (caption == null || caption.equals("")) { + continue; + } else { + caption = caption.toLowerCase(getLocale()); + } + switch (getFilteringMode()) { + case CONTAINS: + if (caption.indexOf(filterstring) > -1) { + filteredOptions.add(itemId); + } + break; + case STARTSWITH: + default: + if (caption.startsWith(filterstring)) { + filteredOptions.add(itemId); + } + break; + } + } + + return filteredOptions; + } + + /** + * Invoked when the value of a variable has changed. + * + * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object, + * java.util.Map) + */ + @Override + public void changeVariables(Object source, Map variables) { + // Not calling super.changeVariables due the history of select + // component hierarchy + + // all the client to server requests are now handled by RPC + } + + @Override + public void setFilteringMode(FilteringMode filteringMode) { + getState().filteringMode = filteringMode; + } + + @Override + public FilteringMode getFilteringMode() { + return getState(false).filteringMode; + } + + @Override + public void addBlurListener(BlurListener listener) { + addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, + BlurListener.blurMethod); + } + + @Override + public void removeBlurListener(BlurListener listener) { + removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); + } + + @Override + public void addFocusListener(FocusListener listener) { + addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, + FocusListener.focusMethod); + } + + @Override + public void removeFocusListener(FocusListener listener) { + removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); + } + + /** + * ComboBox does not support multi select mode. + * + * @deprecated As of 7.0, use {@link ListSelect}, {@link OptionGroup} or + * {@link TwinColSelect} instead + * @see com.vaadin.v7.ui.AbstractSelect#setMultiSelect(boolean) + * @throws UnsupportedOperationException + * if trying to activate multiselect mode + */ + @Deprecated + @Override + public void setMultiSelect(boolean multiSelect) { + if (multiSelect) { + throw new UnsupportedOperationException( + "Multiselect not supported"); + } + } + + /** + * ComboBox does not support multi select mode. + * + * @deprecated As of 7.0, use {@link ListSelect}, {@link OptionGroup} or + * {@link TwinColSelect} instead + * + * @see com.vaadin.v7.ui.AbstractSelect#isMultiSelect() + * + * @return false + */ + @Deprecated + @Override + public boolean isMultiSelect() { + return false; + } + + /** + * Returns the page length of the suggestion popup. + * + * @return the pageLength + */ + public int getPageLength() { + return getState(false).pageLength; + } + + /** + * Returns the suggestion pop-up's width as a CSS string. + * + * @see #setPopupWidth + * @since 7.7 + */ + public String getPopupWidth() { + return getState(false).suggestionPopupWidth; + } + + /** + * Sets the page length for the suggestion popup. Setting the page length to + * 0 will disable suggestion popup paging (all items visible). + * + * @param pageLength + * the pageLength to set + */ + public void setPageLength(int pageLength) { + getState().pageLength = pageLength; + } + + /** + * Sets the suggestion pop-up's width as a CSS string. By using relative + * units (e.g. "50%") it's possible to set the popup's width relative to the + * ComboBox itself. + * + * @see #getPopupWidth() + * @since 7.7 + * @param width + * the width + */ + public void setPopupWidth(String width) { + getState().suggestionPopupWidth = width; + } + + /** + * Sets whether to scroll the selected item visible (directly open the page + * on which it is) when opening the combo box popup or not. Only applies to + * single select mode. + * + * This requires finding the index of the item, which can be expensive in + * many large lazy loading containers. + * + * @param scrollToSelectedItem + * true to find the page with the selected item when opening the + * selection popup + */ + public void setScrollToSelectedItem(boolean scrollToSelectedItem) { + this.scrollToSelectedItem = scrollToSelectedItem; + } + + /** + * Returns true if the select should find the page with the selected item + * when opening the popup (single select combo box only). + * + * @see #setScrollToSelectedItem(boolean) + * + * @return true if the page with the selected item will be shown when + * opening the popup + */ + public boolean isScrollToSelectedItem() { + return scrollToSelectedItem; + } + + /** + * Sets the item style generator that is used to produce custom styles for + * showing items in the popup. The CSS class name that will be added to the + * item style names is v-filterselect-item-[style name]. + * + * @param itemStyleGenerator + * the item style generator to set, or null to not + * use any custom item styles + * @since 7.5.6 + */ + public void setItemStyleGenerator(ItemStyleGenerator itemStyleGenerator) { + this.itemStyleGenerator = itemStyleGenerator; + markAsDirty(); + } + + /** + * Gets the currently used item style generator. + * + * @return the itemStyleGenerator the currently used item style generator, + * or null if no generator is used + * @since 7.5.6 + */ + public ItemStyleGenerator getItemStyleGenerator() { + return itemStyleGenerator; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/DateField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/DateField.java new file mode 100644 index 0000000000..b02e7b87a4 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/DateField.java @@ -0,0 +1,1010 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.logging.Logger; + +import org.jsoup.nodes.Element; + +import com.vaadin.event.FieldEvents; +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.shared.ui.datefield.DateFieldConstants; +import com.vaadin.shared.ui.datefield.Resolution; +import com.vaadin.shared.ui.datefield.TextualDateFieldState; +import com.vaadin.ui.Component; +import com.vaadin.ui.LegacyComponent; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.Validator; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.data.util.converter.Converter; +import com.vaadin.v7.data.validator.DateRangeValidator; + +/** + *

    + * A date editor component that can be bound to any {@link Property} that is + * compatible with java.util.Date. + *

    + *

    + * Since DateField extends LegacyAbstractField it + * implements the {@link com.vaadin.v7.data.Buffered}interface. + *

    + *

    + * A DateField is in write-through mode by default, so + * {@link com.vaadin.v7.ui.AbstractField#setWriteThrough(boolean)}must + * be called to enable buffering. + *

    + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings("serial") +public class DateField extends AbstractField implements + FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, LegacyComponent { + + /** + * Resolution identifier: seconds. + * + * @deprecated As of 7.0, use {@link Resolution#SECOND} + */ + @Deprecated + public static final Resolution RESOLUTION_SEC = Resolution.SECOND; + + /** + * Resolution identifier: minutes. + * + * @deprecated As of 7.0, use {@link Resolution#MINUTE} + */ + @Deprecated + public static final Resolution RESOLUTION_MIN = Resolution.MINUTE; + + /** + * Resolution identifier: hours. + * + * @deprecated As of 7.0, use {@link Resolution#HOUR} + */ + @Deprecated + public static final Resolution RESOLUTION_HOUR = Resolution.HOUR; + + /** + * Resolution identifier: days. + * + * @deprecated As of 7.0, use {@link Resolution#DAY} + */ + @Deprecated + public static final Resolution RESOLUTION_DAY = Resolution.DAY; + + /** + * Resolution identifier: months. + * + * @deprecated As of 7.0, use {@link Resolution#MONTH} + */ + @Deprecated + public static final Resolution RESOLUTION_MONTH = Resolution.MONTH; + + /** + * Resolution identifier: years. + * + * @deprecated As of 7.0, use {@link Resolution#YEAR} + */ + @Deprecated + public static final Resolution RESOLUTION_YEAR = Resolution.YEAR; + + /** + * Specified smallest modifiable unit for the date field. + */ + private Resolution resolution = Resolution.DAY; + + /** + * The internal calendar to be used in java.utl.Date conversions. + */ + private transient Calendar calendar; + + /** + * Overridden format string + */ + private String dateFormat; + + private boolean lenient = false; + + private String dateString = null; + + /** + * Was the last entered string parsable? If this flag is false, datefields + * internal validator does not pass. + */ + private boolean uiHasValidDateString = true; + + /** + * Determines if week numbers are shown in the date selector. + */ + private boolean showISOWeekNumbers = false; + + private String currentParseErrorMessage; + + private String defaultParseErrorMessage = "Date format not recognized"; + + private TimeZone timeZone = null; + + private static Map variableNameForResolution = new HashMap<>(); + + private String dateOutOfRangeMessage = "Date is out of allowed range"; + + private DateRangeValidator currentRangeValidator; + + /** + * Determines whether the ValueChangeEvent should be fired. Used to prevent + * firing the event when UI has invalid string until uiHasValidDateString + * flag is set + */ + private boolean preventValueChangeEvent = false; + + static { + variableNameForResolution.put(Resolution.SECOND, "sec"); + variableNameForResolution.put(Resolution.MINUTE, "min"); + variableNameForResolution.put(Resolution.HOUR, "hour"); + variableNameForResolution.put(Resolution.DAY, "day"); + variableNameForResolution.put(Resolution.MONTH, "month"); + variableNameForResolution.put(Resolution.YEAR, "year"); + } + + /* Constructors */ + + /** + * Constructs an empty DateField with no caption. + */ + public DateField() { + } + + /** + * Constructs an empty DateField with caption. + * + * @param caption + * the caption of the datefield. + */ + public DateField(String caption) { + setCaption(caption); + } + + /** + * Constructs a new DateField that's bound to the specified + * Property and has the given caption String. + * + * @param caption + * the caption String for the editor. + * @param dataSource + * the Property to be edited with this editor. + */ + public DateField(String caption, Property dataSource) { + this(dataSource); + setCaption(caption); + } + + /** + * Constructs a new DateField that's bound to the specified + * Property and has no caption. + * + * @param dataSource + * the Property to be edited with this editor. + */ + public DateField(Property dataSource) + throws IllegalArgumentException { + if (!Date.class.isAssignableFrom(dataSource.getType())) { + throw new IllegalArgumentException( + "Can't use " + dataSource.getType().getName() + + " typed property as datasource"); + } + + setPropertyDataSource(dataSource); + } + + /** + * Constructs a new DateField with the given caption and + * initial text contents. The editor constructed this way will not be bound + * to a Property unless + * {@link com.vaadin.v7.data.Property.Viewer#setPropertyDataSource(Property)} + * is called to bind it. + * + * @param caption + * the caption String for the editor. + * @param value + * the Date value. + */ + public DateField(String caption, Date value) { + setValue(value); + setCaption(caption); + } + + /* Component basic features */ + + /* + * Paints this component. Don't add a JavaDoc comment here, we use the + * default documentation from implemented interface. + */ + @Override + public void paintContent(PaintTarget target) throws PaintException { + + // Adds the locale as attribute + final Locale l = getLocale(); + if (l != null) { + target.addAttribute("locale", l.toString()); + } + + if (getDateFormat() != null) { + target.addAttribute("format", dateFormat); + } + + if (!isLenient()) { + target.addAttribute("strict", true); + } + + target.addAttribute(DateFieldConstants.ATTR_WEEK_NUMBERS, + isShowISOWeekNumbers()); + target.addAttribute("parsable", uiHasValidDateString); + /* + * TODO communicate back the invalid date string? E.g. returning back to + * app or refresh. + */ + + // Gets the calendar + final Calendar calendar = getCalendar(); + final Date currentDate = getValue(); + + // Only paint variables for the resolution and up, e.g. Resolution DAY + // paints DAY,MONTH,YEAR + for (Resolution res : Resolution + .getResolutionsHigherOrEqualTo(resolution)) { + int value = -1; + if (currentDate != null) { + value = calendar.get(res.getCalendarField()); + if (res == Resolution.MONTH) { + // Calendar month is zero based + value++; + } + } + target.addVariable(this, variableNameForResolution.get(res), value); + } + } + + @Override + protected boolean shouldHideErrors() { + return super.shouldHideErrors() && uiHasValidDateString; + } + + @Override + protected TextualDateFieldState getState() { + return (TextualDateFieldState) super.getState(); + } + + @Override + protected TextualDateFieldState getState(boolean markAsDirty) { + return (TextualDateFieldState) super.getState(markAsDirty); + } + + /** + * Sets the start range for this component. If the value is set before this + * date (taking the resolution into account), the component will not + * validate. If startDate is set to null, any + * value before endDate will be accepted by the range + * + * @param startDate + * - the allowed range's start date + */ + public void setRangeStart(Date startDate) { + if (startDate != null && getState().rangeEnd != null + && startDate.after(getState().rangeEnd)) { + throw new IllegalStateException( + "startDate cannot be later than endDate"); + } + + // Create a defensive copy against issues when using java.sql.Date (and + // also against mutable Date). + getState().rangeStart = startDate != null + ? new Date(startDate.getTime()) : null; + updateRangeValidator(); + } + + /** + * Sets the current error message if the range validation fails. + * + * @param dateOutOfRangeMessage + * - Localizable message which is shown when value (the date) is + * set outside allowed range + */ + public void setDateOutOfRangeMessage(String dateOutOfRangeMessage) { + this.dateOutOfRangeMessage = dateOutOfRangeMessage; + updateRangeValidator(); + } + + /** + * Gets the end range for a certain resolution. The range is inclusive, so + * if rangeEnd is set to zero milliseconds past year n and resolution is set + * to YEAR, any date in year n will be accepted. Resolutions lower than DAY + * will be interpreted on a DAY level. That is, everything below DATE is + * cleared + * + * @param forResolution + * - the range conforms to the resolution + * @return + */ + private Date getRangeEnd(Resolution forResolution) { + // We need to set the correct resolution for the dates, + // otherwise the range validator will complain + + Date rangeEnd = getState(false).rangeEnd; + if (rangeEnd == null) { + return null; + } + + Calendar endCal = Calendar.getInstance(); + endCal.setTime(rangeEnd); + + if (forResolution == Resolution.YEAR) { + // Adding one year (minresolution) and clearing the rest. + endCal.set(endCal.get(Calendar.YEAR) + 1, 0, 1, 0, 0, 0); + } else if (forResolution == Resolution.MONTH) { + // Adding one month (minresolution) and clearing the rest. + endCal.set(endCal.get(Calendar.YEAR), + endCal.get(Calendar.MONTH) + 1, 1, 0, 0, 0); + } else { + endCal.set(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH), + endCal.get(Calendar.DATE) + 1, 0, 0, 0); + } + // removing one millisecond will now get the endDate to return to + // current resolution's set time span (year or month) + endCal.set(Calendar.MILLISECOND, -1); + return endCal.getTime(); + } + + /** + * Gets the start range for a certain resolution. The range is inclusive, so + * if rangeStart is set to one millisecond before year n and + * resolution is set to YEAR, any date in year n - 1 will be accepted. + * Lowest supported resolution is DAY. + * + * @param forResolution + * - the range conforms to the resolution + * @return + */ + private Date getRangeStart(Resolution forResolution) { + if (getState(false).rangeStart == null) { + return null; + } + Calendar startCal = Calendar.getInstance(); + startCal.setTime(getState(false).rangeStart); + + if (forResolution == Resolution.YEAR) { + startCal.set(startCal.get(Calendar.YEAR), 0, 1, 0, 0, 0); + } else if (forResolution == Resolution.MONTH) { + startCal.set(startCal.get(Calendar.YEAR), + startCal.get(Calendar.MONTH), 1, 0, 0, 0); + } else { + startCal.set(startCal.get(Calendar.YEAR), + startCal.get(Calendar.MONTH), startCal.get(Calendar.DATE), + 0, 0, 0); + } + + startCal.set(Calendar.MILLISECOND, 0); + return startCal.getTime(); + } + + private void updateRangeValidator() { + if (currentRangeValidator != null) { + removeValidator(currentRangeValidator); + currentRangeValidator = null; + } + if (getRangeStart() != null || getRangeEnd() != null) { + currentRangeValidator = new DateRangeValidator( + dateOutOfRangeMessage, getRangeStart(resolution), + getRangeEnd(resolution), null); + addValidator(currentRangeValidator); + } + } + + /** + * Sets the end range for this component. If the value is set after this + * date (taking the resolution into account), the component will not + * validate. If endDate is set to null, any value + * after startDate will be accepted by the range. + * + * @param endDate + * - the allowed range's end date (inclusive, based on the + * current resolution) + */ + public void setRangeEnd(Date endDate) { + if (endDate != null && getState().rangeStart != null + && getState().rangeStart.after(endDate)) { + throw new IllegalStateException( + "endDate cannot be earlier than startDate"); + } + + // Create a defensive copy against issues when using java.sql.Date (and + // also against mutable Date). + getState().rangeEnd = endDate != null ? new Date(endDate.getTime()) + : null; + updateRangeValidator(); + } + + /** + * Returns the precise rangeStart used. + * + * @param startDate + * + */ + public Date getRangeStart() { + return getState(false).rangeStart; + } + + /** + * Returns the precise rangeEnd used. + * + * @param startDate + */ + public Date getRangeEnd() { + return getState(false).rangeEnd; + } + + /* + * Invoked when a variable of the component changes. Don't add a JavaDoc + * comment here, we use the default documentation from implemented + * interface. + */ + @Override + public void changeVariables(Object source, Map variables) { + + if (!isReadOnly() && (variables.containsKey("year") + || variables.containsKey("month") + || variables.containsKey("day") || variables.containsKey("hour") + || variables.containsKey("min") || variables.containsKey("sec") + || variables.containsKey("msec") + || variables.containsKey("dateString"))) { + + // Old and new dates + final Date oldDate = getValue(); + Date newDate = null; + + // this enables analyzing invalid input on the server + final String newDateString = (String) variables.get("dateString"); + dateString = newDateString; + + // Gets the new date in parts + boolean hasChanges = false; + Map calendarFieldChanges = new HashMap<>(); + + for (Resolution r : Resolution + .getResolutionsHigherOrEqualTo(resolution)) { + // Only handle what the client is allowed to send. The same + // resolutions that are painted + String variableName = variableNameForResolution.get(r); + + if (variables.containsKey(variableName)) { + Integer value = (Integer) variables.get(variableName); + if (r == Resolution.MONTH) { + // Calendar MONTH is zero based + value--; + } + if (value >= 0) { + hasChanges = true; + calendarFieldChanges.put(r, value); + } + } + } + + // If no new variable values were received, use the previous value + if (!hasChanges) { + newDate = null; + } else { + // Clone the calendar for date operation + final Calendar cal = getCalendar(); + + // Update the value based on the received info + // Must set in this order to avoid invalid dates (or wrong + // dates if lenient is true) in calendar + for (int r = Resolution.YEAR.ordinal(); r >= 0; r--) { + Resolution res = Resolution.values()[r]; + if (calendarFieldChanges.containsKey(res)) { + + // Field resolution should be included. Others are + // skipped so that client can not make unexpected + // changes (e.g. day change even though resolution is + // year). + Integer newValue = calendarFieldChanges.get(res); + cal.set(res.getCalendarField(), newValue); + } + } + newDate = cal.getTime(); + } + + if (newDate == null && dateString != null + && !"".equals(dateString)) { + try { + Date parsedDate = handleUnparsableDateString(dateString); + setValue(parsedDate, true); + + /* + * Ensure the value is sent to the client if the value is + * set to the same as the previous (#4304). Does not repaint + * if handleUnparsableDateString throws an exception. In + * this case the invalid text remains in the DateField. + */ + markAsDirty(); + } catch (Converter.ConversionException e) { + + /* + * Datefield now contains some text that could't be parsed + * into date. ValueChangeEvent is fired after the value is + * changed and the flags are set + */ + if (oldDate != null) { + /* + * Set the logic value to null without firing the + * ValueChangeEvent + */ + preventValueChangeEvent = true; + try { + setValue(null); + } finally { + preventValueChangeEvent = false; + } + + /* + * Reset the dateString (overridden to null by setValue) + */ + dateString = newDateString; + } + + /* + * Saves the localized message of parse error. This can be + * overridden in handleUnparsableDateString. The message + * will later be used to show a validation error. + */ + currentParseErrorMessage = e.getLocalizedMessage(); + + /* + * The value of the DateField should be null if an invalid + * value has been given. Not using setValue() since we do + * not want to cause the client side value to change. + */ + uiHasValidDateString = false; + + /* + * If value was changed fire the ValueChangeEvent + */ + if (oldDate != null) { + fireValueChange(false); + } + + markAsDirty(); + } + } else if (newDate != oldDate + && (newDate == null || !newDate.equals(oldDate))) { + setValue(newDate, true); // Don't require a repaint, client + // updates itself + } else if (!uiHasValidDateString) { // oldDate == + // newDate == null + // Empty value set, previously contained unparsable date string, + // clear related internal fields + setValue(null); + } + } + + if (variables.containsKey(FocusEvent.EVENT_ID)) { + fireEvent(new FocusEvent(this)); + } + + if (variables.containsKey(BlurEvent.EVENT_ID)) { + fireEvent(new BlurEvent(this)); + } + } + + /* + * only fires the event if preventValueChangeEvent flag is false + */ + @Override + protected void fireValueChange(boolean repaintIsNotNeeded) { + if (!preventValueChangeEvent) { + super.fireValueChange(repaintIsNotNeeded); + } + } + + /** + * This method is called to handle a non-empty date string from the client + * if the client could not parse it as a Date. + * + * By default, a Converter.ConversionException is thrown, and the current + * value is not modified. + * + * This can be overridden to handle conversions, to return null (equivalent + * to empty input), to throw an exception or to fire an event. + * + * @param dateString + * @return parsed Date + * @throws Converter.ConversionException + * to keep the old value and indicate an error + */ + protected Date handleUnparsableDateString(String dateString) + throws Converter.ConversionException { + currentParseErrorMessage = null; + throw new Converter.ConversionException(getParseErrorMessage()); + } + + /* Property features */ + + /* + * Gets the edited property's type. Don't add a JavaDoc comment here, we use + * the default documentation from implemented interface. + */ + @Override + public Class getType() { + return Date.class; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractField#setValue(java.lang.Object, boolean) + */ + @Override + protected void setValue(Date newValue, boolean repaintIsNotNeeded) + throws Property.ReadOnlyException { + + /* + * First handle special case when the client side component have a date + * string but value is null (e.g. unparsable date string typed in by the + * user). No value changes should happen, but we need to do some + * internal housekeeping. + */ + if (newValue == null && !uiHasValidDateString) { + /* + * Side-effects of setInternalValue clears possible previous strings + * and flags about invalid input. + */ + setInternalValue(null); + markAsDirty(); + return; + } + + super.setValue(newValue, repaintIsNotNeeded); + } + + @Override + protected void setInternalValue(Date newValue) { + // Also set the internal dateString + if (newValue != null) { + dateString = newValue.toString(); + } else { + dateString = null; + } + + if (!uiHasValidDateString) { + // clear component error and parsing flag + setComponentError(null); + uiHasValidDateString = true; + currentParseErrorMessage = null; + } + + super.setInternalValue(newValue); + } + + /** + * Gets the resolution. + * + * @return int + */ + public Resolution getResolution() { + return resolution; + } + + /** + * Sets the resolution of the DateField. + * + * The default resolution is {@link Resolution#DAY} since Vaadin 7.0. + * + * @param resolution + * the resolution to set. + */ + public void setResolution(Resolution resolution) { + this.resolution = resolution; + updateRangeValidator(); + markAsDirty(); + } + + /** + * Returns new instance calendar used in Date conversions. + * + * Returns new clone of the calendar object initialized using the the + * current date (if available) + * + * If this is no calendar is assigned the Calendar.getInstance + * is used. + * + * @return the Calendar. + * @see #setCalendar(Calendar) + */ + private Calendar getCalendar() { + + // Makes sure we have an calendar instance + if (calendar == null) { + calendar = Calendar.getInstance(); + // Start by a zeroed calendar to avoid having values for lower + // resolution variables e.g. time when resolution is day + int min, field; + for (Resolution r : Resolution + .getResolutionsLowerThan(resolution)) { + field = r.getCalendarField(); + min = calendar.getActualMinimum(field); + calendar.set(field, min); + } + calendar.set(Calendar.MILLISECOND, 0); + } + + // Clone the instance + final Calendar newCal = (Calendar) calendar.clone(); + + final TimeZone currentTimeZone = getTimeZone(); + if (currentTimeZone != null) { + newCal.setTimeZone(currentTimeZone); + } + + final Date currentDate = getValue(); + if (currentDate != null) { + newCal.setTime(currentDate); + } + return newCal; + } + + /** + * Sets formatting used by some component implementations. See + * {@link SimpleDateFormat} for format details. + * + * By default it is encouraged to used default formatting defined by Locale, + * but due some JVM bugs it is sometimes necessary to use this method to + * override formatting. See Vaadin issue #2200. + * + * @param dateFormat + * the dateFormat to set + * + * @see com.vaadin.ui.AbstractComponent#setLocale(Locale)) + */ + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + markAsDirty(); + } + + /** + * Returns a format string used to format date value on client side or null + * if default formatting from {@link Component#getLocale()} is used. + * + * @return the dateFormat + */ + public String getDateFormat() { + return dateFormat; + } + + /** + * Specifies whether or not date/time interpretation in component is to be + * lenient. + * + * @see Calendar#setLenient(boolean) + * @see #isLenient() + * + * @param lenient + * true if the lenient mode is to be turned on; false if it is to + * be turned off. + */ + public void setLenient(boolean lenient) { + this.lenient = lenient; + markAsDirty(); + } + + /** + * Returns whether date/time interpretation is to be lenient. + * + * @see #setLenient(boolean) + * + * @return true if the interpretation mode of this calendar is lenient; + * false otherwise. + */ + public boolean isLenient() { + return lenient; + } + + @Override + public void addFocusListener(FocusListener listener) { + addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, + FocusListener.focusMethod); + } + + @Override + public void removeFocusListener(FocusListener listener) { + removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); + } + + @Override + public void addBlurListener(BlurListener listener) { + addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, + BlurListener.blurMethod); + } + + @Override + public void removeBlurListener(BlurListener listener) { + removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); + } + + /** + * Checks whether ISO 8601 week numbers are shown in the date selector. + * + * @return true if week numbers are shown, false otherwise. + */ + public boolean isShowISOWeekNumbers() { + return showISOWeekNumbers; + } + + /** + * Sets the visibility of ISO 8601 week numbers in the date selector. ISO + * 8601 defines that a week always starts with a Monday so the week numbers + * are only shown if this is the case. + * + * @param showWeekNumbers + * true if week numbers should be shown, false otherwise. + */ + public void setShowISOWeekNumbers(boolean showWeekNumbers) { + showISOWeekNumbers = showWeekNumbers; + markAsDirty(); + } + + /** + * Validates the current value against registered validators if the field is + * not empty. Note that DateField is considered empty (value == null) and + * invalid if it contains text typed in by the user that couldn't be parsed + * into a Date value. + * + * @see com.vaadin.v7.ui.AbstractField#validate() + */ + @Override + public void validate() throws InvalidValueException { + /* + * To work properly in form we must throw exception if there is + * currently a parsing error in the datefield. Parsing error is kind of + * an internal validator. + */ + if (!uiHasValidDateString) { + throw new UnparsableDateString(currentParseErrorMessage); + } + super.validate(); + } + + /** + * Return the error message that is shown if the user inputted value can't + * be parsed into a Date object. If + * {@link #handleUnparsableDateString(String)} is overridden and it throws a + * custom exception, the message returned by + * {@link Exception#getLocalizedMessage()} will be used instead of the value + * returned by this method. + * + * @see #setParseErrorMessage(String) + * + * @return the error message that the DateField uses when it can't parse the + * textual input from user to a Date object + */ + public String getParseErrorMessage() { + return defaultParseErrorMessage; + } + + /** + * Sets the default error message used if the DateField cannot parse the + * text input by user to a Date field. Note that if the + * {@link #handleUnparsableDateString(String)} method is overridden, the + * localized message from its exception is used. + * + * @see #getParseErrorMessage() + * @see #handleUnparsableDateString(String) + * @param parsingErrorMessage + */ + public void setParseErrorMessage(String parsingErrorMessage) { + defaultParseErrorMessage = parsingErrorMessage; + } + + /** + * Sets the time zone used by this date field. The time zone is used to + * convert the absolute time in a Date object to a logical time displayed in + * the selector and to convert the select time back to a Date object. + * + * If no time zone has been set, the current default time zone returned by + * {@code TimeZone.getDefault()} is used. + * + * @see #getTimeZone() + * @param timeZone + * the time zone to use for time calculations. + */ + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + markAsDirty(); + } + + /** + * Gets the time zone used by this field. The time zone is used to convert + * the absolute time in a Date object to a logical time displayed in the + * selector and to convert the select time back to a Date object. + * + * If {@code null} is returned, the current default time zone returned by + * {@code TimeZone.getDefault()} is used. + * + * @return the current time zone + */ + public TimeZone getTimeZone() { + return timeZone; + } + + public static class UnparsableDateString + extends Validator.InvalidValueException { + + public UnparsableDateString(String message) { + super(message); + } + + } + + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + if (design.hasAttr("value") && !design.attr("value").isEmpty()) { + Date date = DesignAttributeHandler.getFormatter() + .parse(design.attr("value"), Date.class); + // formatting will return null if it cannot parse the string + if (date == null) { + Logger.getLogger(DateField.class.getName()).info( + "cannot parse " + design.attr("value") + " as date"); + } + this.setValue(date, false, true); + } + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + if (getValue() != null) { + design.attr("value", + DesignAttributeHandler.getFormatter().format(getValue())); + } + } + + /** + * Returns current date-out-of-range error message. + * + * @see #setDateOutOfRangeMessage(String) + * @since 7.4 + * @return Current error message for dates out of range. + */ + public String getDateOutOfRangeMessage() { + return dateOutOfRangeMessage; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/DefaultFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/DefaultFieldFactory.java new file mode 100644 index 0000000000..53035ba087 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/DefaultFieldFactory.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui; + +import java.text.Normalizer.Form; +import java.util.Date; + +import com.vaadin.shared.util.SharedUtil; +import com.vaadin.ui.Component; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Property; + +/** + * This class contains a basic implementation for {@link TableFieldFactory}. The + * class is singleton, use {@link #get()} method to get reference to the + * instance. + * + *

    + * There are also some static helper methods available for custom built field + * factories. + * + */ +public class DefaultFieldFactory implements TableFieldFactory { + + private static final DefaultFieldFactory instance = new DefaultFieldFactory(); + + /** + * Singleton method to get an instance of DefaultFieldFactory. + * + * @return an instance of DefaultFieldFactory + */ + public static DefaultFieldFactory get() { + return instance; + } + + protected DefaultFieldFactory() { + } + + @Override + public Field createField(Container container, Object itemId, + Object propertyId, Component uiContext) { + Property containerProperty = container.getContainerProperty(itemId, + propertyId); + Class type = containerProperty.getType(); + Field field = createFieldByPropertyType(type); + field.setCaption(createCaptionByPropertyId(propertyId)); + return field; + } + + /** + * If name follows method naming conventions, convert the name to spaced + * upper case text. For example, convert "firstName" to "First Name" + * + * @param propertyId + * @return the formatted caption string + */ + public static String createCaptionByPropertyId(Object propertyId) { + return SharedUtil.propertyIdToHumanFriendly(propertyId); + } + + /** + * Creates fields based on the property type. + *

    + * The default field type is {@link TextField}. Other field types generated + * by this method: + *

    + * Boolean: {@link CheckBox}.
    + * Date: {@link DateField}(resolution: day).
    + * Item: {@link Form}.
    + * default field type: {@link TextField}. + *

    + * + * @param type + * the type of the property + * @return the most suitable generic {@link Field} for given type + */ + public static Field createFieldByPropertyType(Class type) { + // Null typed properties can not be edited + if (type == null) { + return null; + } + + // Date field + if (Date.class.isAssignableFrom(type)) { + final DateField df = new DateField(); + df.setResolution(DateField.RESOLUTION_DAY); + return df; + } + + // Boolean field + if (Boolean.class.isAssignableFrom(type)) { + return new CheckBox(); + } + + return new TextField(); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java new file mode 100644 index 0000000000..0a5e5b40a4 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java @@ -0,0 +1,7355 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.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.sort.Sort; +import com.vaadin.data.sort.SortOrder; +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.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.AbstractFocusable; +import com.vaadin.ui.Component; +import com.vaadin.ui.ConnectorTracker; +import com.vaadin.ui.SelectiveRenderer; +import com.vaadin.ui.UI; +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.util.ReflectTools; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Container.Indexed; +import com.vaadin.v7.data.Container.ItemSetChangeEvent; +import com.vaadin.v7.data.Container.ItemSetChangeListener; +import com.vaadin.v7.data.Container.ItemSetChangeNotifier; +import com.vaadin.v7.data.Container.PropertySetChangeEvent; +import com.vaadin.v7.data.Container.PropertySetChangeListener; +import com.vaadin.v7.data.Container.PropertySetChangeNotifier; +import com.vaadin.v7.data.Container.Sortable; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.Validator.InvalidValueException; +import com.vaadin.v7.data.fieldgroup.DefaultFieldGroupFieldFactory; +import com.vaadin.v7.data.fieldgroup.FieldGroup; +import com.vaadin.v7.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.v7.data.fieldgroup.FieldGroupFieldFactory; +import com.vaadin.v7.data.util.IndexedContainer; +import com.vaadin.v7.data.util.converter.Converter; +import com.vaadin.v7.data.util.converter.ConverterUtil; +import com.vaadin.v7.server.communication.data.DataGenerator; +import com.vaadin.v7.server.communication.data.RpcDataProviderExtension; +import com.vaadin.v7.ui.renderers.HtmlRenderer; +import com.vaadin.v7.ui.renderers.Renderer; +import com.vaadin.v7.ui.renderers.TextRenderer; + +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.Converter 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 Grid 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(Grid 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(Grid grid) { + this(grid, DetailsGenerator.NULL); + } + + public DetailComponentManager(Grid 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 Grid}. + * + * @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 Grid}. + * + * @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 CheckBox) { + 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(Grid 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 Grid#getHeaderRow(int)} and {@link Grid#getFooterRow(int)}. + * + * @return row index in section + */ + public int getRowIndex() { + return rowIndex; + } + + @Override + public Grid getComponent() { + return (Grid) 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(Grid 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 Grid getComponent() { + return (Grid) 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(Grid 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(Grid 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(Grid 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(Grid 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(Grid 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(Grid 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; + Field firstErrorField = null; + + FieldGroup fieldGroup = event.getCause().getFieldGroup(); + for (Column column : getColumns()) { + Object propertyId = column.getPropertyId(); + Field 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(Grid.this).error( + new ConnectorErrorEvent(Grid.this, event.getCause())); + } + } + + private Object getFirstPropertyId(FieldGroup fieldGroup, + Set> keySet) { + for (Column c : getColumns()) { + Object propertyId = c.getPropertyId(); + Field 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 Grid}. + *

    + * Passing one of these enums into + * {@link Grid#setSelectionMode(SelectionMode)} is equivalent to calling + * {@link Grid#setSelectionModel(SelectionModel)} with one of the built-in + * implementations of {@link SelectionModel}. + * + * @see Grid#setSelectionMode(SelectionMode) + * @see Grid#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 Grid} 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(Grid 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 Grid 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 Grid}. + *

    + * 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 Grid grid; + + private Object itemId; + + /** + * Creates a new row reference for the given grid. + * + * @param grid + * the grid that the row belongs to + */ + public RowReference(Grid 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 Grid 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 Grid}. + *

    + * 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 Grid 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 Grid#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 Grid#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 Grid#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 Grid#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(Grid.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()); + Converter 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 (Grid.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 Grid 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(Grid 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(Grid 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 Grid#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 Grid grid; + + /** + * Backing property for column + */ + private final Object propertyId; + + private Converter converter; + + /** + * A check for allowing the + * {@link #Column(Grid, GridColumnState, Object) constructor} to call + * {@link #setConverter(Converter)} 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(Grid 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 Grid#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 ConverterUtil#getConverter(Class, Class, VaadinSession) + * @see #setConverter(Converter) + */ + 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, + Converter 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(Converter 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") + Converter castConverter = (Converter) 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 Converter getConverter() { + return converter; + } + + private boolean internalSetRenderer(Renderer renderer) { + + Converter converter; + if (isCompatibleWithProperty(renderer, getConverter())) { + // Use the existing converter (possibly none) if types + // compatible + converter = (Converter) getConverter(); + } else { + converter = ConverterUtil.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, + Converter 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 Grid#editItem(Object) + * @see Grid#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 Grid#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(Field 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.v7.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 Field 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.v7.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 Grid.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, Converter 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 Converter.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") + Converter safeConverter = (Converter) 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(Grid 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 Grid getParentGrid() { + if (getParent() instanceof Grid) { + Grid grid = (Grid) 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 Grid() { + this(null, null); + } + + /** + * Creates a new Grid using the given data source. + * + * @param dataSource + * the indexed container to use as a data source + */ + public Grid(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 Grid(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 Grid(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(Grid.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(Grid.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(Grid.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(Grid.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(Grid.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(Grid.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(Grid.this) + .error(new ConnectorErrorEvent(Grid.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 Grid} 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 Grid}. + * + * @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 Grid} 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(Grid.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 Field getEditorField(Object propertyId) { + checkColumnExists(propertyId); + + if (!getColumn(propertyId).isEditable()) { + return null; + } + + Field 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() != Grid.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 (Field f : getEditorFields()) { + f.markAsDirtyRecursive(); + } + + if (datasource instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) datasource) + .addItemSetChangeListener(editorClosingItemSetListener); + } + } + + private void setEditorField(Object propertyId, Field field) { + checkColumnExists(propertyId); + + Field 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 (Field 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(); + Grid 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/v7/ui/InlineDateField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/InlineDateField.java new file mode 100644 index 0000000000..1bec2bc61e --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/InlineDateField.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.v7.ui; + +import java.util.Date; + +import com.vaadin.v7.data.Property; + +/** + *

    + * A date entry component, which displays the actual date selector inline. + * + *

    + * + * @see DateField + * @see PopupDateField + * @author Vaadin Ltd. + * @since 5.0 + */ +public class InlineDateField extends DateField { + + public InlineDateField() { + super(); + } + + public InlineDateField(Property dataSource) + throws IllegalArgumentException { + super(dataSource); + } + + public InlineDateField(String caption, Date value) { + super(caption, value); + } + + public InlineDateField(String caption, Property dataSource) { + super(caption, dataSource); + } + + public InlineDateField(String caption) { + super(caption); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyDateField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyDateField.java deleted file mode 100644 index 0d094c28bf..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyDateField.java +++ /dev/null @@ -1,1010 +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.v7.ui; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; -import java.util.logging.Logger; - -import org.jsoup.nodes.Element; - -import com.vaadin.data.Property; -import com.vaadin.event.FieldEvents; -import com.vaadin.event.FieldEvents.BlurEvent; -import com.vaadin.event.FieldEvents.BlurListener; -import com.vaadin.event.FieldEvents.FocusEvent; -import com.vaadin.event.FieldEvents.FocusListener; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.shared.ui.datefield.DateFieldConstants; -import com.vaadin.shared.ui.datefield.Resolution; -import com.vaadin.shared.ui.datefield.TextualDateFieldState; -import com.vaadin.ui.Component; -import com.vaadin.ui.LegacyComponent; -import com.vaadin.ui.declarative.DesignAttributeHandler; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.v7.data.Validator; -import com.vaadin.v7.data.Validator.InvalidValueException; -import com.vaadin.v7.data.util.converter.LegacyConverter; -import com.vaadin.v7.data.validator.LegacyDateRangeValidator; - -/** - *

    - * A date editor component that can be bound to any {@link Property} that is - * compatible with java.util.Date. - *

    - *

    - * Since DateField extends LegacyAbstractField it - * implements the {@link com.vaadin.data.Buffered}interface. - *

    - *

    - * A DateField is in write-through mode by default, so - * {@link com.vaadin.v7.ui.LegacyAbstractField#setWriteThrough(boolean)}must - * be called to enable buffering. - *

    - * - * @author Vaadin Ltd. - * @since 3.0 - */ -@SuppressWarnings("serial") -public class LegacyDateField extends LegacyAbstractField implements - FieldEvents.BlurNotifier, FieldEvents.FocusNotifier, LegacyComponent { - - /** - * Resolution identifier: seconds. - * - * @deprecated As of 7.0, use {@link Resolution#SECOND} - */ - @Deprecated - public static final Resolution RESOLUTION_SEC = Resolution.SECOND; - - /** - * Resolution identifier: minutes. - * - * @deprecated As of 7.0, use {@link Resolution#MINUTE} - */ - @Deprecated - public static final Resolution RESOLUTION_MIN = Resolution.MINUTE; - - /** - * Resolution identifier: hours. - * - * @deprecated As of 7.0, use {@link Resolution#HOUR} - */ - @Deprecated - public static final Resolution RESOLUTION_HOUR = Resolution.HOUR; - - /** - * Resolution identifier: days. - * - * @deprecated As of 7.0, use {@link Resolution#DAY} - */ - @Deprecated - public static final Resolution RESOLUTION_DAY = Resolution.DAY; - - /** - * Resolution identifier: months. - * - * @deprecated As of 7.0, use {@link Resolution#MONTH} - */ - @Deprecated - public static final Resolution RESOLUTION_MONTH = Resolution.MONTH; - - /** - * Resolution identifier: years. - * - * @deprecated As of 7.0, use {@link Resolution#YEAR} - */ - @Deprecated - public static final Resolution RESOLUTION_YEAR = Resolution.YEAR; - - /** - * Specified smallest modifiable unit for the date field. - */ - private Resolution resolution = Resolution.DAY; - - /** - * The internal calendar to be used in java.utl.Date conversions. - */ - private transient Calendar calendar; - - /** - * Overridden format string - */ - private String dateFormat; - - private boolean lenient = false; - - private String dateString = null; - - /** - * Was the last entered string parsable? If this flag is false, datefields - * internal validator does not pass. - */ - private boolean uiHasValidDateString = true; - - /** - * Determines if week numbers are shown in the date selector. - */ - private boolean showISOWeekNumbers = false; - - private String currentParseErrorMessage; - - private String defaultParseErrorMessage = "Date format not recognized"; - - private TimeZone timeZone = null; - - private static Map variableNameForResolution = new HashMap<>(); - - private String dateOutOfRangeMessage = "Date is out of allowed range"; - - private LegacyDateRangeValidator currentRangeValidator; - - /** - * Determines whether the ValueChangeEvent should be fired. Used to prevent - * firing the event when UI has invalid string until uiHasValidDateString - * flag is set - */ - private boolean preventValueChangeEvent = false; - - static { - variableNameForResolution.put(Resolution.SECOND, "sec"); - variableNameForResolution.put(Resolution.MINUTE, "min"); - variableNameForResolution.put(Resolution.HOUR, "hour"); - variableNameForResolution.put(Resolution.DAY, "day"); - variableNameForResolution.put(Resolution.MONTH, "month"); - variableNameForResolution.put(Resolution.YEAR, "year"); - } - - /* Constructors */ - - /** - * Constructs an empty DateField with no caption. - */ - public LegacyDateField() { - } - - /** - * Constructs an empty DateField with caption. - * - * @param caption - * the caption of the datefield. - */ - public LegacyDateField(String caption) { - setCaption(caption); - } - - /** - * Constructs a new DateField that's bound to the specified - * Property and has the given caption String. - * - * @param caption - * the caption String for the editor. - * @param dataSource - * the Property to be edited with this editor. - */ - public LegacyDateField(String caption, Property dataSource) { - this(dataSource); - setCaption(caption); - } - - /** - * Constructs a new DateField that's bound to the specified - * Property and has no caption. - * - * @param dataSource - * the Property to be edited with this editor. - */ - public LegacyDateField(Property dataSource) - throws IllegalArgumentException { - if (!Date.class.isAssignableFrom(dataSource.getType())) { - throw new IllegalArgumentException( - "Can't use " + dataSource.getType().getName() - + " typed property as datasource"); - } - - setPropertyDataSource(dataSource); - } - - /** - * Constructs a new DateField with the given caption and - * initial text contents. The editor constructed this way will not be bound - * to a Property unless - * {@link com.vaadin.data.Property.Viewer#setPropertyDataSource(Property)} - * is called to bind it. - * - * @param caption - * the caption String for the editor. - * @param value - * the Date value. - */ - public LegacyDateField(String caption, Date value) { - setValue(value); - setCaption(caption); - } - - /* Component basic features */ - - /* - * Paints this component. Don't add a JavaDoc comment here, we use the - * default documentation from implemented interface. - */ - @Override - public void paintContent(PaintTarget target) throws PaintException { - - // Adds the locale as attribute - final Locale l = getLocale(); - if (l != null) { - target.addAttribute("locale", l.toString()); - } - - if (getDateFormat() != null) { - target.addAttribute("format", dateFormat); - } - - if (!isLenient()) { - target.addAttribute("strict", true); - } - - target.addAttribute(DateFieldConstants.ATTR_WEEK_NUMBERS, - isShowISOWeekNumbers()); - target.addAttribute("parsable", uiHasValidDateString); - /* - * TODO communicate back the invalid date string? E.g. returning back to - * app or refresh. - */ - - // Gets the calendar - final Calendar calendar = getCalendar(); - final Date currentDate = getValue(); - - // Only paint variables for the resolution and up, e.g. Resolution DAY - // paints DAY,MONTH,YEAR - for (Resolution res : Resolution - .getResolutionsHigherOrEqualTo(resolution)) { - int value = -1; - if (currentDate != null) { - value = calendar.get(res.getCalendarField()); - if (res == Resolution.MONTH) { - // Calendar month is zero based - value++; - } - } - target.addVariable(this, variableNameForResolution.get(res), value); - } - } - - @Override - protected boolean shouldHideErrors() { - return super.shouldHideErrors() && uiHasValidDateString; - } - - @Override - protected TextualDateFieldState getState() { - return (TextualDateFieldState) super.getState(); - } - - @Override - protected TextualDateFieldState getState(boolean markAsDirty) { - return (TextualDateFieldState) super.getState(markAsDirty); - } - - /** - * Sets the start range for this component. If the value is set before this - * date (taking the resolution into account), the component will not - * validate. If startDate is set to null, any - * value before endDate will be accepted by the range - * - * @param startDate - * - the allowed range's start date - */ - public void setRangeStart(Date startDate) { - if (startDate != null && getState().rangeEnd != null - && startDate.after(getState().rangeEnd)) { - throw new IllegalStateException( - "startDate cannot be later than endDate"); - } - - // Create a defensive copy against issues when using java.sql.Date (and - // also against mutable Date). - getState().rangeStart = startDate != null - ? new Date(startDate.getTime()) : null; - updateRangeValidator(); - } - - /** - * Sets the current error message if the range validation fails. - * - * @param dateOutOfRangeMessage - * - Localizable message which is shown when value (the date) is - * set outside allowed range - */ - public void setDateOutOfRangeMessage(String dateOutOfRangeMessage) { - this.dateOutOfRangeMessage = dateOutOfRangeMessage; - updateRangeValidator(); - } - - /** - * Gets the end range for a certain resolution. The range is inclusive, so - * if rangeEnd is set to zero milliseconds past year n and resolution is set - * to YEAR, any date in year n will be accepted. Resolutions lower than DAY - * will be interpreted on a DAY level. That is, everything below DATE is - * cleared - * - * @param forResolution - * - the range conforms to the resolution - * @return - */ - private Date getRangeEnd(Resolution forResolution) { - // We need to set the correct resolution for the dates, - // otherwise the range validator will complain - - Date rangeEnd = getState(false).rangeEnd; - if (rangeEnd == null) { - return null; - } - - Calendar endCal = Calendar.getInstance(); - endCal.setTime(rangeEnd); - - if (forResolution == Resolution.YEAR) { - // Adding one year (minresolution) and clearing the rest. - endCal.set(endCal.get(Calendar.YEAR) + 1, 0, 1, 0, 0, 0); - } else if (forResolution == Resolution.MONTH) { - // Adding one month (minresolution) and clearing the rest. - endCal.set(endCal.get(Calendar.YEAR), - endCal.get(Calendar.MONTH) + 1, 1, 0, 0, 0); - } else { - endCal.set(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH), - endCal.get(Calendar.DATE) + 1, 0, 0, 0); - } - // removing one millisecond will now get the endDate to return to - // current resolution's set time span (year or month) - endCal.set(Calendar.MILLISECOND, -1); - return endCal.getTime(); - } - - /** - * Gets the start range for a certain resolution. The range is inclusive, so - * if rangeStart is set to one millisecond before year n and - * resolution is set to YEAR, any date in year n - 1 will be accepted. - * Lowest supported resolution is DAY. - * - * @param forResolution - * - the range conforms to the resolution - * @return - */ - private Date getRangeStart(Resolution forResolution) { - if (getState(false).rangeStart == null) { - return null; - } - Calendar startCal = Calendar.getInstance(); - startCal.setTime(getState(false).rangeStart); - - if (forResolution == Resolution.YEAR) { - startCal.set(startCal.get(Calendar.YEAR), 0, 1, 0, 0, 0); - } else if (forResolution == Resolution.MONTH) { - startCal.set(startCal.get(Calendar.YEAR), - startCal.get(Calendar.MONTH), 1, 0, 0, 0); - } else { - startCal.set(startCal.get(Calendar.YEAR), - startCal.get(Calendar.MONTH), startCal.get(Calendar.DATE), - 0, 0, 0); - } - - startCal.set(Calendar.MILLISECOND, 0); - return startCal.getTime(); - } - - private void updateRangeValidator() { - if (currentRangeValidator != null) { - removeValidator(currentRangeValidator); - currentRangeValidator = null; - } - if (getRangeStart() != null || getRangeEnd() != null) { - currentRangeValidator = new LegacyDateRangeValidator( - dateOutOfRangeMessage, getRangeStart(resolution), - getRangeEnd(resolution), null); - addValidator(currentRangeValidator); - } - } - - /** - * Sets the end range for this component. If the value is set after this - * date (taking the resolution into account), the component will not - * validate. If endDate is set to null, any value - * after startDate will be accepted by the range. - * - * @param endDate - * - the allowed range's end date (inclusive, based on the - * current resolution) - */ - public void setRangeEnd(Date endDate) { - if (endDate != null && getState().rangeStart != null - && getState().rangeStart.after(endDate)) { - throw new IllegalStateException( - "endDate cannot be earlier than startDate"); - } - - // Create a defensive copy against issues when using java.sql.Date (and - // also against mutable Date). - getState().rangeEnd = endDate != null ? new Date(endDate.getTime()) - : null; - updateRangeValidator(); - } - - /** - * Returns the precise rangeStart used. - * - * @param startDate - * - */ - public Date getRangeStart() { - return getState(false).rangeStart; - } - - /** - * Returns the precise rangeEnd used. - * - * @param startDate - */ - public Date getRangeEnd() { - return getState(false).rangeEnd; - } - - /* - * Invoked when a variable of the component changes. Don't add a JavaDoc - * comment here, we use the default documentation from implemented - * interface. - */ - @Override - public void changeVariables(Object source, Map variables) { - - if (!isReadOnly() && (variables.containsKey("year") - || variables.containsKey("month") - || variables.containsKey("day") || variables.containsKey("hour") - || variables.containsKey("min") || variables.containsKey("sec") - || variables.containsKey("msec") - || variables.containsKey("dateString"))) { - - // Old and new dates - final Date oldDate = getValue(); - Date newDate = null; - - // this enables analyzing invalid input on the server - final String newDateString = (String) variables.get("dateString"); - dateString = newDateString; - - // Gets the new date in parts - boolean hasChanges = false; - Map calendarFieldChanges = new HashMap<>(); - - for (Resolution r : Resolution - .getResolutionsHigherOrEqualTo(resolution)) { - // Only handle what the client is allowed to send. The same - // resolutions that are painted - String variableName = variableNameForResolution.get(r); - - if (variables.containsKey(variableName)) { - Integer value = (Integer) variables.get(variableName); - if (r == Resolution.MONTH) { - // Calendar MONTH is zero based - value--; - } - if (value >= 0) { - hasChanges = true; - calendarFieldChanges.put(r, value); - } - } - } - - // If no new variable values were received, use the previous value - if (!hasChanges) { - newDate = null; - } else { - // Clone the calendar for date operation - final Calendar cal = getCalendar(); - - // Update the value based on the received info - // Must set in this order to avoid invalid dates (or wrong - // dates if lenient is true) in calendar - for (int r = Resolution.YEAR.ordinal(); r >= 0; r--) { - Resolution res = Resolution.values()[r]; - if (calendarFieldChanges.containsKey(res)) { - - // Field resolution should be included. Others are - // skipped so that client can not make unexpected - // changes (e.g. day change even though resolution is - // year). - Integer newValue = calendarFieldChanges.get(res); - cal.set(res.getCalendarField(), newValue); - } - } - newDate = cal.getTime(); - } - - if (newDate == null && dateString != null - && !"".equals(dateString)) { - try { - Date parsedDate = handleUnparsableDateString(dateString); - setValue(parsedDate, true); - - /* - * Ensure the value is sent to the client if the value is - * set to the same as the previous (#4304). Does not repaint - * if handleUnparsableDateString throws an exception. In - * this case the invalid text remains in the DateField. - */ - markAsDirty(); - } catch (LegacyConverter.ConversionException e) { - - /* - * Datefield now contains some text that could't be parsed - * into date. ValueChangeEvent is fired after the value is - * changed and the flags are set - */ - if (oldDate != null) { - /* - * Set the logic value to null without firing the - * ValueChangeEvent - */ - preventValueChangeEvent = true; - try { - setValue(null); - } finally { - preventValueChangeEvent = false; - } - - /* - * Reset the dateString (overridden to null by setValue) - */ - dateString = newDateString; - } - - /* - * Saves the localized message of parse error. This can be - * overridden in handleUnparsableDateString. The message - * will later be used to show a validation error. - */ - currentParseErrorMessage = e.getLocalizedMessage(); - - /* - * The value of the DateField should be null if an invalid - * value has been given. Not using setValue() since we do - * not want to cause the client side value to change. - */ - uiHasValidDateString = false; - - /* - * If value was changed fire the ValueChangeEvent - */ - if (oldDate != null) { - fireValueChange(false); - } - - markAsDirty(); - } - } else if (newDate != oldDate - && (newDate == null || !newDate.equals(oldDate))) { - setValue(newDate, true); // Don't require a repaint, client - // updates itself - } else if (!uiHasValidDateString) { // oldDate == - // newDate == null - // Empty value set, previously contained unparsable date string, - // clear related internal fields - setValue(null); - } - } - - if (variables.containsKey(FocusEvent.EVENT_ID)) { - fireEvent(new FocusEvent(this)); - } - - if (variables.containsKey(BlurEvent.EVENT_ID)) { - fireEvent(new BlurEvent(this)); - } - } - - /* - * only fires the event if preventValueChangeEvent flag is false - */ - @Override - protected void fireValueChange(boolean repaintIsNotNeeded) { - if (!preventValueChangeEvent) { - super.fireValueChange(repaintIsNotNeeded); - } - } - - /** - * This method is called to handle a non-empty date string from the client - * if the client could not parse it as a Date. - * - * By default, a Converter.ConversionException is thrown, and the current - * value is not modified. - * - * This can be overridden to handle conversions, to return null (equivalent - * to empty input), to throw an exception or to fire an event. - * - * @param dateString - * @return parsed Date - * @throws Converter.ConversionException - * to keep the old value and indicate an error - */ - protected Date handleUnparsableDateString(String dateString) - throws LegacyConverter.ConversionException { - currentParseErrorMessage = null; - throw new LegacyConverter.ConversionException(getParseErrorMessage()); - } - - /* Property features */ - - /* - * Gets the edited property's type. Don't add a JavaDoc comment here, we use - * the default documentation from implemented interface. - */ - @Override - public Class getType() { - return Date.class; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.AbstractField#setValue(java.lang.Object, boolean) - */ - @Override - protected void setValue(Date newValue, boolean repaintIsNotNeeded) - throws Property.ReadOnlyException { - - /* - * First handle special case when the client side component have a date - * string but value is null (e.g. unparsable date string typed in by the - * user). No value changes should happen, but we need to do some - * internal housekeeping. - */ - if (newValue == null && !uiHasValidDateString) { - /* - * Side-effects of setInternalValue clears possible previous strings - * and flags about invalid input. - */ - setInternalValue(null); - markAsDirty(); - return; - } - - super.setValue(newValue, repaintIsNotNeeded); - } - - @Override - protected void setInternalValue(Date newValue) { - // Also set the internal dateString - if (newValue != null) { - dateString = newValue.toString(); - } else { - dateString = null; - } - - if (!uiHasValidDateString) { - // clear component error and parsing flag - setComponentError(null); - uiHasValidDateString = true; - currentParseErrorMessage = null; - } - - super.setInternalValue(newValue); - } - - /** - * Gets the resolution. - * - * @return int - */ - public Resolution getResolution() { - return resolution; - } - - /** - * Sets the resolution of the DateField. - * - * The default resolution is {@link Resolution#DAY} since Vaadin 7.0. - * - * @param resolution - * the resolution to set. - */ - public void setResolution(Resolution resolution) { - this.resolution = resolution; - updateRangeValidator(); - markAsDirty(); - } - - /** - * Returns new instance calendar used in Date conversions. - * - * Returns new clone of the calendar object initialized using the the - * current date (if available) - * - * If this is no calendar is assigned the Calendar.getInstance - * is used. - * - * @return the Calendar. - * @see #setCalendar(Calendar) - */ - private Calendar getCalendar() { - - // Makes sure we have an calendar instance - if (calendar == null) { - calendar = Calendar.getInstance(); - // Start by a zeroed calendar to avoid having values for lower - // resolution variables e.g. time when resolution is day - int min, field; - for (Resolution r : Resolution - .getResolutionsLowerThan(resolution)) { - field = r.getCalendarField(); - min = calendar.getActualMinimum(field); - calendar.set(field, min); - } - calendar.set(Calendar.MILLISECOND, 0); - } - - // Clone the instance - final Calendar newCal = (Calendar) calendar.clone(); - - final TimeZone currentTimeZone = getTimeZone(); - if (currentTimeZone != null) { - newCal.setTimeZone(currentTimeZone); - } - - final Date currentDate = getValue(); - if (currentDate != null) { - newCal.setTime(currentDate); - } - return newCal; - } - - /** - * Sets formatting used by some component implementations. See - * {@link SimpleDateFormat} for format details. - * - * By default it is encouraged to used default formatting defined by Locale, - * but due some JVM bugs it is sometimes necessary to use this method to - * override formatting. See Vaadin issue #2200. - * - * @param dateFormat - * the dateFormat to set - * - * @see com.vaadin.ui.AbstractComponent#setLocale(Locale)) - */ - public void setDateFormat(String dateFormat) { - this.dateFormat = dateFormat; - markAsDirty(); - } - - /** - * Returns a format string used to format date value on client side or null - * if default formatting from {@link Component#getLocale()} is used. - * - * @return the dateFormat - */ - public String getDateFormat() { - return dateFormat; - } - - /** - * Specifies whether or not date/time interpretation in component is to be - * lenient. - * - * @see Calendar#setLenient(boolean) - * @see #isLenient() - * - * @param lenient - * true if the lenient mode is to be turned on; false if it is to - * be turned off. - */ - public void setLenient(boolean lenient) { - this.lenient = lenient; - markAsDirty(); - } - - /** - * Returns whether date/time interpretation is to be lenient. - * - * @see #setLenient(boolean) - * - * @return true if the interpretation mode of this calendar is lenient; - * false otherwise. - */ - public boolean isLenient() { - return lenient; - } - - @Override - public void addFocusListener(FocusListener listener) { - addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, - FocusListener.focusMethod); - } - - @Override - public void removeFocusListener(FocusListener listener) { - removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); - } - - @Override - public void addBlurListener(BlurListener listener) { - addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, - BlurListener.blurMethod); - } - - @Override - public void removeBlurListener(BlurListener listener) { - removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); - } - - /** - * Checks whether ISO 8601 week numbers are shown in the date selector. - * - * @return true if week numbers are shown, false otherwise. - */ - public boolean isShowISOWeekNumbers() { - return showISOWeekNumbers; - } - - /** - * Sets the visibility of ISO 8601 week numbers in the date selector. ISO - * 8601 defines that a week always starts with a Monday so the week numbers - * are only shown if this is the case. - * - * @param showWeekNumbers - * true if week numbers should be shown, false otherwise. - */ - public void setShowISOWeekNumbers(boolean showWeekNumbers) { - showISOWeekNumbers = showWeekNumbers; - markAsDirty(); - } - - /** - * Validates the current value against registered validators if the field is - * not empty. Note that DateField is considered empty (value == null) and - * invalid if it contains text typed in by the user that couldn't be parsed - * into a Date value. - * - * @see com.vaadin.v7.ui.LegacyAbstractField#validate() - */ - @Override - public void validate() throws InvalidValueException { - /* - * To work properly in form we must throw exception if there is - * currently a parsing error in the datefield. Parsing error is kind of - * an internal validator. - */ - if (!uiHasValidDateString) { - throw new UnparsableDateString(currentParseErrorMessage); - } - super.validate(); - } - - /** - * Return the error message that is shown if the user inputted value can't - * be parsed into a Date object. If - * {@link #handleUnparsableDateString(String)} is overridden and it throws a - * custom exception, the message returned by - * {@link Exception#getLocalizedMessage()} will be used instead of the value - * returned by this method. - * - * @see #setParseErrorMessage(String) - * - * @return the error message that the DateField uses when it can't parse the - * textual input from user to a Date object - */ - public String getParseErrorMessage() { - return defaultParseErrorMessage; - } - - /** - * Sets the default error message used if the DateField cannot parse the - * text input by user to a Date field. Note that if the - * {@link #handleUnparsableDateString(String)} method is overridden, the - * localized message from its exception is used. - * - * @see #getParseErrorMessage() - * @see #handleUnparsableDateString(String) - * @param parsingErrorMessage - */ - public void setParseErrorMessage(String parsingErrorMessage) { - defaultParseErrorMessage = parsingErrorMessage; - } - - /** - * Sets the time zone used by this date field. The time zone is used to - * convert the absolute time in a Date object to a logical time displayed in - * the selector and to convert the select time back to a Date object. - * - * If no time zone has been set, the current default time zone returned by - * {@code TimeZone.getDefault()} is used. - * - * @see #getTimeZone() - * @param timeZone - * the time zone to use for time calculations. - */ - public void setTimeZone(TimeZone timeZone) { - this.timeZone = timeZone; - markAsDirty(); - } - - /** - * Gets the time zone used by this field. The time zone is used to convert - * the absolute time in a Date object to a logical time displayed in the - * selector and to convert the select time back to a Date object. - * - * If {@code null} is returned, the current default time zone returned by - * {@code TimeZone.getDefault()} is used. - * - * @return the current time zone - */ - public TimeZone getTimeZone() { - return timeZone; - } - - public static class UnparsableDateString - extends Validator.InvalidValueException { - - public UnparsableDateString(String message) { - super(message); - } - - } - - @Override - public void readDesign(Element design, DesignContext designContext) { - super.readDesign(design, designContext); - if (design.hasAttr("value") && !design.attr("value").isEmpty()) { - Date date = DesignAttributeHandler.getFormatter() - .parse(design.attr("value"), Date.class); - // formatting will return null if it cannot parse the string - if (date == null) { - Logger.getLogger(LegacyDateField.class.getName()).info( - "cannot parse " + design.attr("value") + " as date"); - } - this.setValue(date, false, true); - } - } - - @Override - public void writeDesign(Element design, DesignContext designContext) { - super.writeDesign(design, designContext); - if (getValue() != null) { - design.attr("value", - DesignAttributeHandler.getFormatter().format(getValue())); - } - } - - /** - * Returns current date-out-of-range error message. - * - * @see #setDateOutOfRangeMessage(String) - * @since 7.4 - * @return Current error message for dates out of range. - */ - public String getDateOutOfRangeMessage() { - return dateOutOfRangeMessage; - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyInlineDateField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyInlineDateField.java deleted file mode 100644 index 4e1ad7e997..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyInlineDateField.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.v7.ui; - -import java.util.Date; - -import com.vaadin.data.Property; - -/** - *

    - * A date entry component, which displays the actual date selector inline. - * - *

    - * - * @see LegacyDateField - * @see LegacyPopupDateField - * @author Vaadin Ltd. - * @since 5.0 - */ -public class LegacyInlineDateField extends LegacyDateField { - - public LegacyInlineDateField() { - super(); - } - - public LegacyInlineDateField(Property dataSource) - throws IllegalArgumentException { - super(dataSource); - } - - public LegacyInlineDateField(String caption, Date value) { - super(caption, value); - } - - public LegacyInlineDateField(String caption, Property dataSource) { - super(caption, dataSource); - } - - public LegacyInlineDateField(String caption) { - super(caption); - } - -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyPopupDateField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyPopupDateField.java deleted file mode 100644 index c2470daf26..0000000000 --- a/compatibility-server/src/main/java/com/vaadin/v7/ui/LegacyPopupDateField.java +++ /dev/null @@ -1,147 +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.v7.ui; - -import java.util.Date; - -import com.vaadin.data.Property; -import com.vaadin.server.PaintException; -import com.vaadin.server.PaintTarget; -import com.vaadin.shared.ui.datefield.PopupDateFieldState; - -/** - *

    - * A date entry component, which displays the actual date selector as a popup. - * - *

    - * - * @see LegacyDateField - * @see LegacyInlineDateField - * @author Vaadin Ltd. - * @since 5.0 - */ -public class LegacyPopupDateField extends LegacyDateField { - - private String inputPrompt = null; - - public LegacyPopupDateField() { - super(); - } - - public LegacyPopupDateField(Property dataSource) - throws IllegalArgumentException { - super(dataSource); - } - - public LegacyPopupDateField(String caption, Date value) { - super(caption, value); - } - - public LegacyPopupDateField(String caption, Property dataSource) { - super(caption, dataSource); - } - - public LegacyPopupDateField(String caption) { - super(caption); - } - - @Override - public void paintContent(PaintTarget target) throws PaintException { - super.paintContent(target); - - if (inputPrompt != null) { - target.addAttribute("prompt", inputPrompt); - } - } - - /** - * Gets the current input prompt. - * - * @see #setInputPrompt(String) - * @return the current input prompt, or null if not enabled - */ - public String getInputPrompt() { - return inputPrompt; - } - - /** - * Sets the input prompt - a textual prompt that is displayed when the field - * would otherwise be empty, to prompt the user for input. - * - * @param inputPrompt - */ - public void setInputPrompt(String inputPrompt) { - this.inputPrompt = inputPrompt; - markAsDirty(); - } - - @Override - protected PopupDateFieldState getState() { - return (PopupDateFieldState) super.getState(); - } - - @Override - protected PopupDateFieldState getState(boolean markAsDirty) { - return (PopupDateFieldState) super.getState(markAsDirty); - } - - /** - * Checks whether the text field is enabled (default) or not. - * - * @see PopupDateField#setTextFieldEnabled(boolean); - * - * @return true if the text field is enabled, false otherwise. - */ - public boolean isTextFieldEnabled() { - return getState(false).textFieldEnabled; - } - - /** - * Enables or disables the text field. By default the text field is enabled. - * Disabling it causes only the button for date selection to be active, thus - * preventing the user from entering invalid dates. - * - * See {@link http://dev.vaadin.com/ticket/6790}. - * - * @param state - * true to enable text field, false to disable it. - */ - public void setTextFieldEnabled(boolean state) { - getState().textFieldEnabled = state; - } - - /** - * Set a description that explains the usage of the Widget for users of - * assistive devices. - * - * @param description - * String with the description - */ - public void setAssistiveText(String description) { - getState().descriptionForAssistiveDevices = description; - } - - /** - * Get the description that explains the usage of the Widget for users of - * assistive devices. - * - * @return String with the description - */ - public String getAssistiveText() { - return getState(false).descriptionForAssistiveDevices; - } -} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/ListSelect.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/ListSelect.java new file mode 100644 index 0000000000..3b5683f55d --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/ListSelect.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.util.Collection; + +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.v7.data.Container; + +/** + * This is a simple list select without, for instance, support for new items, + * lazyloading, and other advanced features. + */ +@SuppressWarnings("serial") +public class ListSelect extends AbstractSelect { + + private int rows = 0; + + public ListSelect() { + super(); + } + + public ListSelect(String caption, Collection options) { + super(caption, options); + } + + public ListSelect(String caption, Container dataSource) { + super(caption, dataSource); + } + + public ListSelect(String caption) { + super(caption); + } + + public int getRows() { + return rows; + } + + /** + * Sets the number of rows in the editor. If the number of rows is set 0, + * the actual number of displayed rows is determined implicitly by the + * adapter. + * + * @param rows + * the number of rows to set. + */ + public void setRows(int rows) { + if (rows < 0) { + rows = 0; + } + if (this.rows != rows) { + this.rows = rows; + markAsDirty(); + } + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + // Adds the number of rows + if (rows != 0) { + target.addAttribute("rows", rows); + } + super.paintContent(target); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/NativeSelect.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/NativeSelect.java new file mode 100644 index 0000000000..d68f184d43 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/NativeSelect.java @@ -0,0 +1,108 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.util.Collection; + +import com.vaadin.event.FieldEvents; +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.v7.data.Container; + +/** + * This is a simple drop-down select without, for instance, support for + * multiselect, new items, lazyloading, and other advanced features. Sometimes + * "native" select without all the bells-and-whistles of the ComboBox is a + * better choice. + */ +@SuppressWarnings("serial") +public class NativeSelect extends AbstractSelect + implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { + + FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl( + this) { + + @Override + protected void fireEvent(Event event) { + NativeSelect.this.fireEvent(event); + } + }; + + public NativeSelect() { + super(); + registerRpc(focusBlurRpc); + } + + public NativeSelect(String caption, Collection options) { + super(caption, options); + registerRpc(focusBlurRpc); + } + + public NativeSelect(String caption, Container dataSource) { + super(caption, dataSource); + registerRpc(focusBlurRpc); + } + + public NativeSelect(String caption) { + super(caption); + registerRpc(focusBlurRpc); + } + + @Override + public void setMultiSelect(boolean multiSelect) + throws UnsupportedOperationException { + if (multiSelect == true) { + throw new UnsupportedOperationException( + "Multiselect not supported"); + } + } + + @Override + public void setNewItemsAllowed(boolean allowNewOptions) + throws UnsupportedOperationException { + if (allowNewOptions == true) { + throw new UnsupportedOperationException( + "newItemsAllowed not supported"); + } + } + + @Override + public void addFocusListener(FocusListener listener) { + addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, + FocusListener.focusMethod); + } + + @Override + public void removeFocusListener(FocusListener listener) { + removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); + } + + @Override + public void addBlurListener(BlurListener listener) { + addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, + BlurListener.blurMethod); + } + + @Override + public void removeBlurListener(BlurListener listener) { + removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/OptionGroup.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/OptionGroup.java new file mode 100644 index 0000000000..f4e3415cf5 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/OptionGroup.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.v7.ui; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.jsoup.nodes.Element; + +import com.vaadin.event.FieldEvents; +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.shared.ui.optiongroup.OptionGroupConstants; +import com.vaadin.shared.ui.optiongroup.OptionGroupState; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignFormatter; +import com.vaadin.v7.data.Container; + +/** + * Configures select to be used as an option group. + */ +@SuppressWarnings("serial") +public class OptionGroup extends AbstractSelect + implements FieldEvents.BlurNotifier, FieldEvents.FocusNotifier { + + private Set disabledItemIds = new HashSet(); + + public OptionGroup() { + super(); + } + + public OptionGroup(String caption, Collection options) { + super(caption, options); + } + + public OptionGroup(String caption, Container dataSource) { + super(caption, dataSource); + } + + public OptionGroup(String caption) { + super(caption); + } + + @Override + protected void paintItem(PaintTarget target, Object itemId) + throws PaintException { + super.paintItem(target, itemId); + if (!isItemEnabled(itemId)) { + target.addAttribute(OptionGroupConstants.ATTRIBUTE_OPTION_DISABLED, + true); + } + } + + @Override + public void changeVariables(Object source, Map variables) { + super.changeVariables(source, variables); + + if (variables.containsKey(FocusEvent.EVENT_ID)) { + fireEvent(new FocusEvent(this)); + } + if (variables.containsKey(BlurEvent.EVENT_ID)) { + fireEvent(new BlurEvent(this)); + } + } + + @Override + public void addBlurListener(BlurListener listener) { + addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, + BlurListener.blurMethod); + } + + @Override + public void removeBlurListener(BlurListener listener) { + removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); + } + + @Override + public void addFocusListener(FocusListener listener) { + addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, + FocusListener.focusMethod); + } + + @Override + public void removeFocusListener(FocusListener listener) { + removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); + + } + + @Override + protected void setValue(Object newValue, boolean repaintIsNotNeeded) { + if (repaintIsNotNeeded) { + /* + * Check that value from changeVariables() doesn't contain unallowed + * selections: In the multi select mode, the user has selected or + * deselected a disabled item. In the single select mode, the user + * has selected a disabled item. + */ + if (isMultiSelect()) { + Set currentValueSet = (Set) getValue(); + Set newValueSet = (Set) newValue; + for (Object itemId : currentValueSet) { + if (!isItemEnabled(itemId) + && !newValueSet.contains(itemId)) { + markAsDirty(); + return; + } + } + for (Object itemId : newValueSet) { + if (!isItemEnabled(itemId) + && !currentValueSet.contains(itemId)) { + markAsDirty(); + return; + } + } + } else { + if (newValue == null) { + newValue = getNullSelectionItemId(); + } + if (!isItemEnabled(newValue)) { + markAsDirty(); + return; + } + } + } + super.setValue(newValue, repaintIsNotNeeded); + } + + /** + * Sets an item disabled or enabled. In the multiselect mode, a disabled + * item cannot be selected or deselected by the user. In the single + * selection mode, a disable item cannot be selected. + * + * However, programmatical selection or deselection of an disable item is + * possible. By default, items are enabled. + * + * @param itemId + * the id of the item to be disabled or enabled + * @param enabled + * if true the item is enabled, otherwise the item is disabled + */ + public void setItemEnabled(Object itemId, boolean enabled) { + if (itemId != null) { + if (enabled) { + disabledItemIds.remove(itemId); + } else { + disabledItemIds.add(itemId); + } + markAsDirty(); + } + } + + /** + * Returns true if the item is enabled. + * + * @param itemId + * the id of the item to be checked + * @return true if the item is enabled, false otherwise + * @see #setItemEnabled(Object, boolean) + */ + public boolean isItemEnabled(Object itemId) { + if (itemId != null) { + return !disabledItemIds.contains(itemId); + } + return true; + } + + /** + * Sets whether html is allowed in the item captions. If set to true, the + * captions are passed to the browser as html and the developer is + * responsible for ensuring no harmful html is used. If set to false, the + * content is passed to the browser as plain text. + * + * @param htmlContentAllowed + * true if the captions are used as html, false if used as plain + * text + */ + public void setHtmlContentAllowed(boolean htmlContentAllowed) { + getState().htmlContentAllowed = htmlContentAllowed; + } + + /** + * Checks whether captions are interpreted as html or plain text. + * + * @return true if the captions are used as html, false if used as plain + * text + * @see #setHtmlContentAllowed(boolean) + */ + public boolean isHtmlContentAllowed() { + return getState(false).htmlContentAllowed; + } + + @Override + protected Object readItem(Element child, Set selected, + DesignContext context) { + Object itemId = super.readItem(child, selected, context); + + if (child.hasAttr("disabled")) { + setItemEnabled(itemId, false); + } + + return itemId; + } + + @Override + protected Element writeItem(Element design, Object itemId, + DesignContext context) { + Element elem = super.writeItem(design, itemId, context); + + if (!isItemEnabled(itemId)) { + elem.attr("disabled", ""); + } + if (isHtmlContentAllowed()) { + // need to unencode HTML entities. AbstractSelect.writeDesign can't + // check if HTML content is allowed, so it always encodes entities + // like '>', '<' and '&'; in case HTML content is allowed this is + // undesirable so we need to unencode entities. Entities other than + // '<' and '>' will be taken care by Jsoup. + elem.html(DesignFormatter.decodeFromTextNode(elem.html())); + } + + return elem; + } + + @Override + protected OptionGroupState getState() { + return (OptionGroupState) super.getState(); + } + + @Override + protected OptionGroupState getState(boolean markAsDirty) { + return (OptionGroupState) super.getState(markAsDirty); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/PopupDateField.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/PopupDateField.java new file mode 100644 index 0000000000..1ae0f79b30 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/PopupDateField.java @@ -0,0 +1,147 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.util.Date; + +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.shared.ui.datefield.PopupDateFieldState; +import com.vaadin.v7.data.Property; + +/** + *

    + * A date entry component, which displays the actual date selector as a popup. + * + *

    + * + * @see DateField + * @see InlineDateField + * @author Vaadin Ltd. + * @since 5.0 + */ +public class PopupDateField extends DateField { + + private String inputPrompt = null; + + public PopupDateField() { + super(); + } + + public PopupDateField(Property dataSource) + throws IllegalArgumentException { + super(dataSource); + } + + public PopupDateField(String caption, Date value) { + super(caption, value); + } + + public PopupDateField(String caption, Property dataSource) { + super(caption, dataSource); + } + + public PopupDateField(String caption) { + super(caption); + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + super.paintContent(target); + + if (inputPrompt != null) { + target.addAttribute("prompt", inputPrompt); + } + } + + /** + * Gets the current input prompt. + * + * @see #setInputPrompt(String) + * @return the current input prompt, or null if not enabled + */ + public String getInputPrompt() { + return inputPrompt; + } + + /** + * Sets the input prompt - a textual prompt that is displayed when the field + * would otherwise be empty, to prompt the user for input. + * + * @param inputPrompt + */ + public void setInputPrompt(String inputPrompt) { + this.inputPrompt = inputPrompt; + markAsDirty(); + } + + @Override + protected PopupDateFieldState getState() { + return (PopupDateFieldState) super.getState(); + } + + @Override + protected PopupDateFieldState getState(boolean markAsDirty) { + return (PopupDateFieldState) super.getState(markAsDirty); + } + + /** + * Checks whether the text field is enabled (default) or not. + * + * @see PopupDateField#setTextFieldEnabled(boolean); + * + * @return true if the text field is enabled, false otherwise. + */ + public boolean isTextFieldEnabled() { + return getState(false).textFieldEnabled; + } + + /** + * Enables or disables the text field. By default the text field is enabled. + * Disabling it causes only the button for date selection to be active, thus + * preventing the user from entering invalid dates. + * + * See {@link http://dev.vaadin.com/ticket/6790}. + * + * @param state + * true to enable text field, false to disable it. + */ + public void setTextFieldEnabled(boolean state) { + getState().textFieldEnabled = state; + } + + /** + * Set a description that explains the usage of the Widget for users of + * assistive devices. + * + * @param description + * String with the description + */ + public void setAssistiveText(String description) { + getState().descriptionForAssistiveDevices = description; + } + + /** + * Get the description that explains the usage of the Widget for users of + * assistive devices. + * + * @return String with the description + */ + public String getAssistiveText() { + return getState(false).descriptionForAssistiveDevices; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/RichTextArea.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/RichTextArea.java new file mode 100644 index 0000000000..e7a790a2cc --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/RichTextArea.java @@ -0,0 +1,317 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.util.Map; + +import org.jsoup.nodes.Element; + +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.shared.ui.textarea.RichTextAreaState; +import com.vaadin.ui.LegacyComponent; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.v7.data.Property; + +/** + * A simple RichTextArea to edit HTML format text. + * + * Note, that using {@link TextField#setMaxLength(int)} method in + * {@link RichTextArea} may produce unexpected results as formatting is counted + * into length of field. + */ +public class RichTextArea extends AbstractField + implements LegacyComponent { + + /** + * Null representation. + */ + private String nullRepresentation = "null"; + + /** + * Is setting to null from non-null value allowed by setting with null + * representation . + */ + private boolean nullSettingAllowed = false; + + /** + * Temporary flag that indicates all content will be selected after the next + * paint. Reset to false after painted. + */ + private boolean selectAll = false; + + /** + * Constructs an empty RichTextArea with no caption. + */ + public RichTextArea() { + setValue(""); + } + + /** + * + * Constructs an empty RichTextArea with the given caption. + * + * @param caption + * the caption for the editor. + */ + public RichTextArea(String caption) { + this(); + setCaption(caption); + } + + /** + * Constructs a new RichTextArea that's bound to the specified + * Property and has no caption. + * + * @param dataSource + * the data source for the editor value + */ + public RichTextArea(Property dataSource) { + setPropertyDataSource(dataSource); + } + + /** + * Constructs a new RichTextArea that's bound to the specified + * Property and has the given caption. + * + * @param caption + * the caption for the editor. + * @param dataSource + * the data source for the editor value + */ + public RichTextArea(String caption, Property dataSource) { + this(dataSource); + setCaption(caption); + } + + /** + * Constructs a new RichTextArea with the given caption and + * initial text contents. + * + * @param caption + * the caption for the editor. + * @param value + * the initial text content of the editor. + */ + public RichTextArea(String caption, String value) { + setValue(value); + setCaption(caption); + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + if (selectAll) { + target.addAttribute("selectAll", true); + selectAll = false; + } + + // Adds the content as variable + String value = getValue(); + if (value == null) { + value = getNullRepresentation(); + } + if (value == null) { + throw new IllegalStateException( + "Null values are not allowed if the null-representation is null"); + } + target.addVariable(this, "text", value); + + } + + @Override + public void setReadOnly(boolean readOnly) { + super.setReadOnly(readOnly); + // IE6 cannot support multi-classname selectors properly + // TODO Can be optimized now that support for I6 is dropped + if (readOnly) { + addStyleName("v-richtextarea-readonly"); + } else { + removeStyleName("v-richtextarea-readonly"); + } + } + + /** + * Selects all text in the rich text area. As a side effect, focuses the + * rich text area. + * + * @since 6.5 + */ + public void selectAll() { + /* + * Set selection range functionality is currently being + * planned/developed for GWT RTA. Only selecting all is currently + * supported. Consider moving selectAll and other selection related + * functions to AbstractTextField at that point to share the + * implementation. Some third party components extending + * AbstractTextField might however not want to support them. + */ + selectAll = true; + focus(); + markAsDirty(); + } + + @Override + public void changeVariables(Object source, Map variables) { + // Sets the text + if (variables.containsKey("text") && !isReadOnly()) { + + // Only do the setting if the string representation of the value + // has been updated + String newValue = (String) variables.get("text"); + + final String oldValue = getValue(); + if (newValue != null && (oldValue == null || isNullSettingAllowed()) + && newValue.equals(getNullRepresentation())) { + newValue = null; + } + if (newValue != oldValue + && (newValue == null || !newValue.equals(oldValue))) { + boolean wasModified = isModified(); + setValue(newValue, true); + + // If the modified status changes, + // repaint is needed after all. + if (wasModified != isModified()) { + markAsDirty(); + } + } + } + + } + + @Override + public Class getType() { + return String.class; + } + + /** + * Gets the null-string representation. + * + *

    + * The null-valued strings are represented on the user interface by + * replacing the null value with this string. If the null representation is + * set null (not 'null' string), painting null value throws exception. + *

    + * + *

    + * The default value is string 'null'. + *

    + * + * @return the String Textual representation for null strings. + * @see TextField#isNullSettingAllowed() + */ + public String getNullRepresentation() { + return nullRepresentation; + } + + /** + * Is setting nulls with null-string representation allowed. + * + *

    + * If this property is true, writing null-representation string to text + * field always sets the field value to real null. If this property is + * false, null setting is not made, but the null values are maintained. + * Maintenance of null-values is made by only converting the textfield + * contents to real null, if the text field matches the null-string + * representation and the current value of the field is null. + *

    + * + *

    + * By default this setting is false + *

    + * + * @return boolean Should the null-string represenation be always converted + * to null-values. + * @see TextField#getNullRepresentation() + */ + public boolean isNullSettingAllowed() { + return nullSettingAllowed; + } + + /** + * Sets the null-string representation. + * + *

    + * The null-valued strings are represented on the user interface by + * replacing the null value with this string. If the null representation is + * set null (not 'null' string), painting null value throws exception. + *

    + * + *

    + * The default value is string 'null' + *

    + * + * @param nullRepresentation + * Textual representation for null strings. + * @see TextField#setNullSettingAllowed(boolean) + */ + public void setNullRepresentation(String nullRepresentation) { + this.nullRepresentation = nullRepresentation; + } + + /** + * Sets the null conversion mode. + * + *

    + * If this property is true, writing null-representation string to text + * field always sets the field value to real null. If this property is + * false, null setting is not made, but the null values are maintained. + * Maintenance of null-values is made by only converting the textfield + * contents to real null, if the text field matches the null-string + * representation and the current value of the field is null. + *

    + * + *

    + * By default this setting is false. + *

    + * + * @param nullSettingAllowed + * Should the null-string represenation be always converted to + * null-values. + * @see TextField#getNullRepresentation() + */ + public void setNullSettingAllowed(boolean nullSettingAllowed) { + this.nullSettingAllowed = nullSettingAllowed; + } + + @Override + public boolean isEmpty() { + return super.isEmpty() || getValue().length() == 0; + } + + @Override + public void clear() { + setValue(""); + } + + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + setValue(design.html(), false, true); + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + design.html(getValue()); + } + + @Override + protected RichTextAreaState getState() { + return (RichTextAreaState) super.getState(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Select.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Select.java new file mode 100644 index 0000000000..2710287445 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Select.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.v7.ui; + +import java.util.Collection; + +import com.vaadin.v7.data.Container; + +/** + *

    + * A class representing a selection of items the user has selected in a UI. The + * set of choices is presented as a set of {@link com.vaadin.v7.data.Item}s in a + * {@link com.vaadin.v7.data.Container}. + *

    + * + *

    + * A Select component may be in single- or multiselect mode. + * Multiselect mode means that more than one item can be selected + * simultaneously. + *

    + * + * @author Vaadin Ltd. + * @since 3.0 + * @deprecated As of 7.0. Use {@link ComboBox} instead. + */ +@Deprecated +public class Select extends ComboBox { + /* Component methods */ + + public Select() { + super(); + } + + public Select(String caption, Collection options) { + super(caption, options); + } + + public Select(String caption, Container dataSource) { + super(caption, dataSource); + } + + public Select(String caption) { + super(caption); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Table.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Table.java new file mode 100644 index 0000000000..760940a482 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Table.java @@ -0,0 +1,6536 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +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.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import com.vaadin.event.Action; +import com.vaadin.event.Action.Handler; +import com.vaadin.event.ContextClickEvent; +import com.vaadin.event.DataBoundTransferable; +import com.vaadin.event.ItemClickEvent; +import com.vaadin.event.ItemClickEvent.ItemClickListener; +import com.vaadin.event.ItemClickEvent.ItemClickNotifier; +import com.vaadin.event.MouseEvents.ClickEvent; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DragSource; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.DropTarget; +import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; +import com.vaadin.server.KeyMapper; +import com.vaadin.server.LegacyCommunicationManager; +import com.vaadin.server.LegacyPaint; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.server.Resource; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.ui.MultiSelectMode; +import com.vaadin.shared.ui.table.CollapseMenuContent; +import com.vaadin.shared.ui.table.TableConstants; +import com.vaadin.shared.ui.table.TableConstants.Section; +import com.vaadin.shared.ui.table.TableServerRpc; +import com.vaadin.shared.ui.table.TableState; +import com.vaadin.shared.util.SharedUtil; +import com.vaadin.ui.Component; +import com.vaadin.ui.HasChildMeasurementHint; +import com.vaadin.ui.HasComponents; +import com.vaadin.ui.UniqueSerializable; +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.util.ReflectTools; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.ContainerOrderedWrapper; +import com.vaadin.v7.data.util.IndexedContainer; +import com.vaadin.v7.data.util.converter.Converter; +import com.vaadin.v7.data.util.converter.ConverterUtil; + +/** + *

    + * Table is used for representing data or components in a pageable + * and selectable table. + *

    + * + *

    + * Scalability of the Table is largely dictated by the container. A table does + * not have a limit for the number of items and is just as fast with hundreds of + * thousands of items as with just a few. The current GWT implementation with + * scrolling however limits the number of rows to around 500000, depending on + * the browser and the pixel height of rows. + *

    + * + *

    + * Components in a Table will not have their caption nor icon rendered. + *

    + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings({ "deprecation" }) +public class Table extends AbstractSelect implements Action.Container, + Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource, + DropTarget, HasComponents, HasChildMeasurementHint { + + private transient Logger logger = null; + + /** + * Modes that Table support as drag sourse. + */ + public enum TableDragMode { + /** + * Table does not start drag and drop events. HTM5 style events started + * by browser may still happen. + */ + NONE, + /** + * Table starts drag with a one row only. + */ + ROW, + /** + * Table drags selected rows, if drag starts on a selected rows. Else it + * starts like in ROW mode. Note, that in Transferable there will still + * be only the row on which the drag started, other dragged rows need to + * be checked from the source Table. + */ + MULTIROW + } + + protected static final int CELL_KEY = 0; + + protected static final int CELL_HEADER = 1; + + protected static final int CELL_ICON = 2; + + protected static final int CELL_ITEMID = 3; + + protected static final int CELL_GENERATED_ROW = 4; + + protected static final int CELL_FIRSTCOL = 5; + + public enum Align { + /** + * Left column alignment. This is the default behaviour. + */ + LEFT("b"), + + /** + * Center column alignment. + */ + CENTER("c"), + + /** + * Right column alignment. + */ + RIGHT("e"); + + private String alignment; + + private Align(String alignment) { + this.alignment = alignment; + } + + @Override + public String toString() { + return alignment; + } + + public Align convertStringToAlign(String string) { + if (string == null) { + return null; + } + if (string.equals("b")) { + return Align.LEFT; + } else if (string.equals("c")) { + return Align.CENTER; + } else if (string.equals("e")) { + return Align.RIGHT; + } else { + return null; + } + } + } + + /** + * @deprecated As of 7.0, use {@link Align#LEFT} instead + */ + @Deprecated + public static final Align ALIGN_LEFT = Align.LEFT; + + /** + * @deprecated As of 7.0, use {@link Align#CENTER} instead + */ + @Deprecated + public static final Align ALIGN_CENTER = Align.CENTER; + + /** + * @deprecated As of 7.0, use {@link Align#RIGHT} instead + */ + @Deprecated + public static final Align ALIGN_RIGHT = Align.RIGHT; + + public enum ColumnHeaderMode { + /** + * Column headers are hidden. + */ + HIDDEN, + /** + * Property ID:s are used as column headers. + */ + ID, + /** + * Column headers are explicitly specified with + * {@link #setColumnHeaders(String[])}. + */ + EXPLICIT, + /** + * Column headers are explicitly specified with + * {@link #setColumnHeaders(String[])}. If a header is not specified for + * a given property, its property id is used instead. + *

    + * This is the default behavior. + */ + EXPLICIT_DEFAULTS_ID + } + + /** + * @deprecated As of 7.0, use {@link ColumnHeaderMode#HIDDEN} instead + */ + @Deprecated + public static final ColumnHeaderMode COLUMN_HEADER_MODE_HIDDEN = ColumnHeaderMode.HIDDEN; + + /** + * @deprecated As of 7.0, use {@link ColumnHeaderMode#ID} instead + */ + @Deprecated + public static final ColumnHeaderMode COLUMN_HEADER_MODE_ID = ColumnHeaderMode.ID; + + /** + * @deprecated As of 7.0, use {@link ColumnHeaderMode#EXPLICIT} instead + */ + @Deprecated + public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT = ColumnHeaderMode.EXPLICIT; + + /** + * @deprecated As of 7.0, use {@link ColumnHeaderMode#EXPLICIT_DEFAULTS_ID} + * instead + */ + @Deprecated + public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID; + + public enum RowHeaderMode { + /** + * Row caption mode: The row headers are hidden. This is the default + * mode. + */ + HIDDEN(null), + /** + * Row caption mode: Items Id-objects toString is used as row caption. + */ + ID(ItemCaptionMode.ID), + /** + * Row caption mode: Item-objects toString is used as row caption. + */ + ITEM(ItemCaptionMode.ITEM), + /** + * Row caption mode: Index of the item is used as item caption. The + * index mode can only be used with the containers implementing the + * {@link com.vaadin.data.Container.Indexed} interface. + */ + INDEX(ItemCaptionMode.INDEX), + /** + * Row caption mode: Item captions are explicitly specified, but if the + * caption is missing, the item id objects toString() is + * used instead. + */ + EXPLICIT_DEFAULTS_ID(ItemCaptionMode.EXPLICIT_DEFAULTS_ID), + /** + * Row caption mode: Item captions are explicitly specified. + */ + EXPLICIT(ItemCaptionMode.EXPLICIT), + /** + * Row caption mode: Only icons are shown, the captions are hidden. + */ + ICON_ONLY(ItemCaptionMode.ICON_ONLY), + /** + * Row caption mode: Item captions are read from property specified with + * {@link #setItemCaptionPropertyId(Object)} . + */ + PROPERTY(ItemCaptionMode.PROPERTY); + + ItemCaptionMode mode; + + private RowHeaderMode(ItemCaptionMode mode) { + this.mode = mode; + } + + public ItemCaptionMode getItemCaptionMode() { + return mode; + } + } + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#HIDDEN} instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_HIDDEN = RowHeaderMode.HIDDEN; + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#ID} instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_ID = RowHeaderMode.ID; + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#ITEM} instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_ITEM = RowHeaderMode.ITEM; + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#INDEX} instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_INDEX = RowHeaderMode.INDEX; + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#EXPLICIT_DEFAULTS_ID} + * instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = RowHeaderMode.EXPLICIT_DEFAULTS_ID; + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#EXPLICIT} instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT = RowHeaderMode.EXPLICIT; + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#ICON_ONLY} instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_ICON_ONLY = RowHeaderMode.ICON_ONLY; + + /** + * @deprecated As of 7.0, use {@link RowHeaderMode#PROPERTY} instead + */ + @Deprecated + public static final RowHeaderMode ROW_HEADER_MODE_PROPERTY = RowHeaderMode.PROPERTY; + + /** + * The default rate that table caches rows for smooth scrolling. + */ + private static final double CACHE_RATE_DEFAULT = 2; + + private static final String ROW_HEADER_COLUMN_KEY = "0"; + private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new UniqueSerializable() { + }; + + /** + * How layout manager should behave when measuring Table's child components + */ + private ChildMeasurementHint childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; + + /* Private table extensions to Select */ + + /** + * True if column collapsing is allowed. + */ + private boolean columnCollapsingAllowed = false; + + /** + * True if reordering of columns is allowed on the client side. + */ + private boolean columnReorderingAllowed = false; + + /** + * Keymapper for column ids. + */ + private final KeyMapper columnIdMap = new KeyMapper(); + + /** + * Holds visible column propertyIds - in order. + */ + private LinkedList visibleColumns = new LinkedList(); + + /** + * Holds noncollapsible columns. + */ + private HashSet noncollapsibleColumns = new HashSet(); + + /** + * Holds propertyIds of currently collapsed columns. + */ + private final HashSet collapsedColumns = new HashSet(); + + /** + * Holds headers for visible columns (by propertyId). + */ + private final HashMap columnHeaders = new HashMap(); + + /** + * Holds footers for visible columns (by propertyId). + */ + private final HashMap columnFooters = new HashMap(); + + /** + * Holds icons for visible columns (by propertyId). + */ + private final HashMap columnIcons = new HashMap(); + + /** + * Holds alignments for visible columns (by propertyId). + */ + private HashMap columnAlignments = new HashMap(); + + /** + * Holds column widths in pixels for visible columns (by propertyId). + */ + private final HashMap columnWidths = new HashMap(); + + /** + * Holds column expand rations for visible columns (by propertyId). + */ + private final HashMap columnExpandRatios = new HashMap(); + + /** + * Holds column generators + */ + private final HashMap columnGenerators = new LinkedHashMap(); + + /** + * Holds value of property pageLength. 0 disables paging. + */ + private int pageLength = 15; + + /** + * Id the first item on the current page. + */ + private Object currentPageFirstItemId = null; + + /* + * If all rows get removed then scroll position of the previous container + * can be restored after re-adding/replacing rows via addAll(). This + * resolves #14581. + */ + private int repairOnReAddAllRowsDataScrollPositionItemIndex = -1; + + /** + * Index of the first item on the current page. + */ + private int currentPageFirstItemIndex = 0; + + /** + * Index of the "first" item on the last page if a user has used + * setCurrentPageFirstItemIndex to scroll down. -1 if not set. + */ + private int currentPageFirstItemIndexOnLastPage = -1; + + /** + * Holds value of property selectable. + */ + private Boolean selectable; + + /** + * Holds value of property columnHeaderMode. + */ + private ColumnHeaderMode columnHeaderMode = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID; + + /** + * Holds value of property rowHeaderMode. + */ + private RowHeaderMode rowHeaderMode = RowHeaderMode.EXPLICIT_DEFAULTS_ID; + + /** + * Should the Table footer be visible? + */ + private boolean columnFootersVisible = false; + + /** + * Page contents buffer used in buffered mode. + */ + private Object[][] pageBuffer = null; + + /** + * Set of properties listened - the list is kept to release the listeners + * later. + */ + private HashSet> listenedProperties = null; + + /** + * Set of visible components - the is used for needsRepaint calculation. + */ + private HashSet visibleComponents = null; + + /** + * List of action handlers. + */ + private LinkedList actionHandlers = null; + + /** + * Action mapper. + */ + private KeyMapper actionMapper = null; + + /** + * Table cell editor factory. + */ + private TableFieldFactory fieldFactory = DefaultFieldFactory.get(); + + /** + * Is table editable. + */ + private boolean editable = false; + + /** + * Current sorting direction. + */ + private boolean sortAscending = true; + + /** + * Currently table is sorted on this propertyId. + */ + private Object sortContainerPropertyId = null; + + /** + * Is table sorting by the user enabled. + */ + private boolean sortEnabled = true; + + /** + * Number of rows explicitly requested by the client to be painted on next + * paint. This is -1 if no request by the client is made. Painting the + * component will automatically reset this to -1. + */ + private int reqRowsToPaint = -1; + + /** + * Index of the first rows explicitly requested by the client to be painted. + * This is -1 if no request by the client is made. Painting the component + * will automatically reset this to -1. + */ + private int reqFirstRowToPaint = -1; + + private int firstToBeRenderedInClient = -1; + + private int lastToBeRenderedInClient = -1; + + private boolean isContentRefreshesEnabled = true; + + private int pageBufferFirstIndex; + + private boolean containerChangeToBeRendered = false; + + /** + * Table cell specific style generator + */ + private CellStyleGenerator cellStyleGenerator = null; + + /** + * Table cell specific tooltip generator + */ + private ItemDescriptionGenerator itemDescriptionGenerator; + + /* + * EXPERIMENTAL feature: will tell the client to re-calculate column widths + * if set to true. Currently no setter: extend to enable. + */ + protected boolean alwaysRecalculateColumnWidths = false; + + private double cacheRate = CACHE_RATE_DEFAULT; + + private TableDragMode dragMode = TableDragMode.NONE; + + private DropHandler dropHandler; + + private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; + + private boolean rowCacheInvalidated; + + private RowGenerator rowGenerator = null; + + private final Map, Property> associatedProperties = new HashMap, Property>(); + + private boolean painted = false; + + private HashMap> propertyValueConverters = new HashMap>(); + + /** + * Set to true if the client-side should be informed that the key mapper has + * been reset so it can avoid sending back references to keys that are no + * longer present. + */ + private boolean keyMapperReset; + + private List exceptionsDuringCachePopulation = new ArrayList(); + + private boolean isBeingPainted; + + /* Table constructors */ + + /** + * Creates a new empty table. + */ + public Table() { + setRowHeaderMode(ROW_HEADER_MODE_HIDDEN); + + registerRpc(new TableServerRpc() { + + @Override + public void contextClick(String rowKey, String colKey, + Section section, MouseEventDetails details) { + Object itemId = itemIdMapper.get(rowKey); + Object propertyId = columnIdMap.get(colKey); + fireEvent(new TableContextClickEvent(Table.this, details, + itemId, propertyId, section)); + } + }); + } + + /** + * Creates a new empty table with caption. + * + * @param caption + */ + public Table(String caption) { + this(); + setCaption(caption); + } + + /** + * Creates a new table with caption and connect it to a Container. + * + * @param caption + * @param dataSource + */ + public Table(String caption, Container dataSource) { + this(); + setCaption(caption); + setContainerDataSource(dataSource); + } + + /* Table functionality */ + + /** + * Gets the array of visible column id:s, including generated columns. + * + *

    + * The columns are show in the order of their appearance in this array. + *

    + * + * @return an array of currently visible propertyIds and generated column + * ids. + */ + public Object[] getVisibleColumns() { + if (visibleColumns == null) { + return null; + } + return visibleColumns.toArray(); + } + + /** + * Sets the array of visible column property id:s. + * + *

    + * The columns are show in the order of their appearance in this array. + *

    + * + * @param visibleColumns + * the Array of shown property id:s. + */ + public void setVisibleColumns(Object... visibleColumns) { + + // Visible columns must exist + if (visibleColumns == null) { + throw new NullPointerException( + "Can not set visible columns to null value"); + } + + final LinkedList newVC = new LinkedList(); + + // Checks that the new visible columns contains no nulls, properties + // exist and that there are no duplicates before adding them to newVC. + final Collection properties = getContainerPropertyIds(); + for (int i = 0; i < visibleColumns.length; i++) { + if (visibleColumns[i] == null) { + throw new NullPointerException("Ids must be non-nulls"); + } else if (!properties.contains(visibleColumns[i]) + && !columnGenerators.containsKey(visibleColumns[i])) { + throw new IllegalArgumentException( + "Ids must exist in the Container or as a generated column, missing id: " + + visibleColumns[i]); + } else if (newVC.contains(visibleColumns[i])) { + throw new IllegalArgumentException( + "Ids must be unique, duplicate id: " + + visibleColumns[i]); + } else { + newVC.add(visibleColumns[i]); + } + } + + this.visibleColumns = newVC; + + // Assures visual refresh + refreshRowCache(); + } + + /** + * Gets the headers of the columns. + * + *

    + * The headers match the property id:s given by the set visible column + * headers. The table must be set in either + * {@link #COLUMN_HEADER_MODE_EXPLICIT} or + * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the + * headers. In the defaults mode any nulls in the headers array are replaced + * with id.toString(). + *

    + * + * @return the Array of column headers. + */ + public String[] getColumnHeaders() { + if (columnHeaders == null) { + return null; + } + final String[] headers = new String[visibleColumns.size()]; + int i = 0; + for (final Iterator it = visibleColumns.iterator(); it + .hasNext(); i++) { + headers[i] = getColumnHeader(it.next()); + } + return headers; + } + + /** + * Sets the headers of the columns. + * + *

    + * The headers match the property id:s given by the set visible column + * headers. The table must be set in either + * {@link #COLUMN_HEADER_MODE_EXPLICIT} or + * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the + * headers. In the defaults mode any nulls in the headers array are replaced + * with id.toString() outputs when rendering. + *

    + * + * @param columnHeaders + * the Array of column headers that match the + * {@link #getVisibleColumns()} method. + */ + public void setColumnHeaders(String... columnHeaders) { + + if (columnHeaders.length != visibleColumns.size()) { + throw new IllegalArgumentException( + "The length of the headers array must match the number of visible columns"); + } + + this.columnHeaders.clear(); + int i = 0; + for (final Iterator it = visibleColumns.iterator(); it.hasNext() + && i < columnHeaders.length; i++) { + this.columnHeaders.put(it.next(), columnHeaders[i]); + } + + markAsDirty(); + } + + /** + * Gets the icons of the columns. + * + *

    + * The icons in headers match the property id:s given by the set visible + * column headers. The table must be set in either + * {@link #COLUMN_HEADER_MODE_EXPLICIT} or + * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers + * with icons. + *

    + * + * @return the Array of icons that match the {@link #getVisibleColumns()}. + */ + public Resource[] getColumnIcons() { + if (columnIcons == null) { + return null; + } + final Resource[] icons = new Resource[visibleColumns.size()]; + int i = 0; + for (final Iterator it = visibleColumns.iterator(); it + .hasNext(); i++) { + icons[i] = columnIcons.get(it.next()); + } + + return icons; + } + + /** + * Sets the icons of the columns. + * + *

    + * The icons in headers match the property id:s given by the set visible + * column headers. The table must be set in either + * {@link #COLUMN_HEADER_MODE_EXPLICIT} or + * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers + * with icons. + *

    + * + * @param columnIcons + * the Array of icons that match the {@link #getVisibleColumns()} + * . + */ + public void setColumnIcons(Resource... columnIcons) { + + if (columnIcons.length != visibleColumns.size()) { + throw new IllegalArgumentException( + "The length of the icons array must match the number of visible columns"); + } + + this.columnIcons.clear(); + int i = 0; + for (final Iterator it = visibleColumns.iterator(); it.hasNext() + && i < columnIcons.length; i++) { + this.columnIcons.put(it.next(), columnIcons[i]); + } + + markAsDirty(); + } + + /** + * Gets the array of column alignments. + * + *

    + * The items in the array must match the properties identified by + * {@link #getVisibleColumns()}. The possible values for the alignments + * include: + *

      + *
    • {@link Align#LEFT}: Left alignment
    • + *
    • {@link Align#CENTER}: Centered
    • + *
    • {@link Align#RIGHT}: Right alignment
    • + *
    + * The alignments default to {@link Align#LEFT}: any null values are + * rendered as align lefts. + *

    + * + * @return the Column alignments array. + */ + public Align[] getColumnAlignments() { + if (columnAlignments == null) { + return null; + } + final Align[] alignments = new Align[visibleColumns.size()]; + int i = 0; + for (final Iterator it = visibleColumns.iterator(); it + .hasNext(); i++) { + alignments[i] = getColumnAlignment(it.next()); + } + + return alignments; + } + + /** + * Sets the column alignments. + * + *

    + * The amount of items in the array must match the amount of properties + * identified by {@link #getVisibleColumns()}. The possible values for the + * alignments include: + *

      + *
    • {@link Align#LEFT}: Left alignment
    • + *
    • {@link Align#CENTER}: Centered
    • + *
    • {@link Align#RIGHT}: Right alignment
    • + *
    + * The alignments default to {@link Align#LEFT} + *

    + * + * @param columnAlignments + * the Column alignments array. + */ + public void setColumnAlignments(Align... columnAlignments) { + + if (columnAlignments.length != visibleColumns.size()) { + throw new IllegalArgumentException( + "The length of the alignments array must match the number of visible columns"); + } + + // Resets the alignments + final HashMap newCA = new HashMap(); + int i = 0; + for (final Iterator it = visibleColumns.iterator(); it.hasNext() + && i < columnAlignments.length; i++) { + newCA.put(it.next(), columnAlignments[i]); + } + this.columnAlignments = newCA; + + // Assures the visual refresh. No need to reset the page buffer before + // as the content has not changed, only the alignments. + refreshRenderedCells(); + } + + /** + * Sets columns width (in pixels). Theme may not necessarily respect very + * small or very big values. Setting width to -1 (default) means that theme + * will make decision of width. + * + *

    + * Column can either have a fixed width or expand ratio. The latter one set + * is used. See @link {@link #setColumnExpandRatio(Object, float)}. + * + * @param propertyId + * columns property id + * @param width + * width to be reserved for columns content + * @since 4.0.3 + */ + public void setColumnWidth(Object propertyId, int width) { + if (propertyId == null) { + // Since propertyId is null, this is the row header. Use the magic + // id to store the width of the row header. + propertyId = ROW_HEADER_FAKE_PROPERTY_ID; + } + + // Setting column width should remove any expand ratios as well + columnExpandRatios.remove(propertyId); + + if (width < 0) { + columnWidths.remove(propertyId); + } else { + columnWidths.put(propertyId, width); + } + markAsDirty(); + } + + /** + * Sets the column expand ratio for given column. + *

    + * Expand ratios can be defined to customize the way how excess space is + * divided among columns. Table can have excess space if it has its width + * defined and there is horizontally more space than columns consume + * naturally. Excess space is the space that is not used by columns with + * explicit width (see {@link #setColumnWidth(Object, int)}) or with natural + * width (no width nor expand ratio). + * + *

    + * By default (without expand ratios) the excess space is divided + * proportionally to columns natural widths. + * + *

    + * Only expand ratios of visible columns are used in final calculations. + * + *

    + * Column can either have a fixed width or expand ratio. The latter one set + * is used. + * + *

    + * A column with expand ratio is considered to be minimum width by default + * (if no excess space exists). The minimum width is defined by terminal + * implementation. + * + *

    + * If terminal implementation supports re-sizable columns the column becomes + * fixed width column if users resizes the column. + * + * @param propertyId + * columns property id + * @param expandRatio + * the expandRatio used to divide excess space for this column + */ + public void setColumnExpandRatio(Object propertyId, float expandRatio) { + if (propertyId == null) { + // Since propertyId is null, this is the row header. Use the magic + // id to store the width of the row header. + propertyId = ROW_HEADER_FAKE_PROPERTY_ID; + } + + // Setting the column expand ratio should remove and defined column + // width + columnWidths.remove(propertyId); + + if (expandRatio < 0) { + columnExpandRatios.remove(propertyId); + } else { + columnExpandRatios.put(propertyId, expandRatio); + } + + requestRepaint(); + } + + /** + * Gets the column expand ratio for a column. See + * {@link #setColumnExpandRatio(Object, float)} + * + * @param propertyId + * columns property id + * @return the expandRatio used to divide excess space for this column + */ + public float getColumnExpandRatio(Object propertyId) { + final Float width = columnExpandRatios.get(propertyId); + if (width == null) { + return -1; + } + return width.floatValue(); + } + + /** + * Gets the pixel width of column + * + * @param propertyId + * @return width of column or -1 when value not set + */ + public int getColumnWidth(Object propertyId) { + if (propertyId == null) { + // Since propertyId is null, this is the row header. Use the magic + // id to retrieve the width of the row header. + propertyId = ROW_HEADER_FAKE_PROPERTY_ID; + } + final Integer width = columnWidths.get(propertyId); + if (width == null) { + return -1; + } + return width.intValue(); + } + + /** + * Gets the page length. + * + *

    + * Setting page length 0 disables paging. + *

    + * + * @return the Length of one page. + */ + public int getPageLength() { + return pageLength; + } + + /** + * Sets the page length. + * + *

    + * Setting page length 0 disables paging. The page length defaults to 15. + *

    + * + *

    + * If Table has height set ({@link #setHeight(float, Unit)} ) the client + * side may update the page length automatically the correct value. + *

    + * + * @param pageLength + * the length of one page. + */ + public void setPageLength(int pageLength) { + if (pageLength >= 0 && this.pageLength != pageLength) { + this.pageLength = pageLength; + // Assures the visual refresh + refreshRowCache(); + } + } + + /** + * This method adjusts a possible caching mechanism of table implementation. + * + *

    + * Table component may fetch and render some rows outside visible area. With + * complex tables (for example containing layouts and components), the + * client side may become unresponsive. Setting the value lower, UI will + * become more responsive. With higher values scrolling in client will hit + * server less frequently. + * + *

    + * The amount of cached rows will be cacheRate multiplied with pageLength ( + * {@link #setPageLength(int)} both below and above visible area.. + * + * @param cacheRate + * a value over 0 (fastest rendering time). Higher value will + * cache more rows on server (smoother scrolling). Default value + * is 2. + */ + public void setCacheRate(double cacheRate) { + if (cacheRate < 0) { + throw new IllegalArgumentException( + "cacheRate cannot be less than zero"); + } + if (this.cacheRate != cacheRate) { + this.cacheRate = cacheRate; + markAsDirty(); + } + } + + /** + * @see #setCacheRate(double) + * + * @return the current cache rate value + */ + public double getCacheRate() { + return cacheRate; + } + + /** + * Getter for property currentPageFirstItem. + * + * @return the Value of property currentPageFirstItem. + */ + public Object getCurrentPageFirstItemId() { + + // Prioritise index over id if indexes are supported + if (items instanceof Container.Indexed) { + final int index = getCurrentPageFirstItemIndex(); + Object id = null; + if (index >= 0 && index < size()) { + id = getIdByIndex(index); + } + if (id != null && !id.equals(currentPageFirstItemId)) { + currentPageFirstItemId = id; + } + } + + // If there is no item id at all, use the first one + if (currentPageFirstItemId == null) { + currentPageFirstItemId = firstItemId(); + } + + return currentPageFirstItemId; + } + + /** + * Returns the item ID for the item represented by the index given. Assumes + * that the current container implements {@link Container.Indexed}. + * + * See {@link Container.Indexed#getIdByIndex(int)} for more information + * about the exceptions that can be thrown. + * + * @param index + * the index for which the item ID should be fetched + * @return the item ID for the given index + * + * @throws ClassCastException + * if container does not implement {@link Container.Indexed} + * @throws IndexOutOfBoundsException + * thrown by {@link Container.Indexed#getIdByIndex(int)} if the + * index is invalid + */ + protected Object getIdByIndex(int index) { + return ((Container.Indexed) items).getIdByIndex(index); + } + + /** + * Setter for property currentPageFirstItemId. + * + * @param currentPageFirstItemId + * the New value of property currentPageFirstItemId. + */ + public void setCurrentPageFirstItemId(Object currentPageFirstItemId) { + + // Gets the corresponding index + int index = -1; + if (items instanceof Container.Indexed) { + index = indexOfId(currentPageFirstItemId); + } else { + // If the table item container does not have index, we have to + // calculates the index by hand + Object id = firstItemId(); + while (id != null && !id.equals(currentPageFirstItemId)) { + index++; + id = nextItemId(id); + } + if (id == null) { + index = -1; + } + } + + // If the search for item index was successful + if (index >= 0) { + /* + * The table is not capable of displaying an item in the container + * as the first if there are not enough items following the selected + * item so the whole table (pagelength) is filled. + */ + int maxIndex = size() - pageLength; + if (maxIndex < 0) { + maxIndex = 0; + } + + if (index > maxIndex) { + // Note that we pass index, not maxIndex, letting + // setCurrentPageFirstItemIndex handle the situation. + setCurrentPageFirstItemIndex(index); + return; + } + + this.currentPageFirstItemId = currentPageFirstItemId; + currentPageFirstItemIndex = index; + } + + // Assures the visual refresh + refreshRowCache(); + + } + + protected int indexOfId(Object itemId) { + return ((Container.Indexed) items).indexOfId(itemId); + } + + /** + * Gets the icon Resource for the specified column. + * + * @param propertyId + * the propertyId identifying the column. + * @return the icon for the specified column; null if the column has no icon + * set, or if the column is not visible. + */ + public Resource getColumnIcon(Object propertyId) { + return columnIcons.get(propertyId); + } + + /** + * Sets the icon Resource for the specified column. + *

    + * Throws IllegalArgumentException if the specified column is not visible. + *

    + * + * @param propertyId + * the propertyId identifying the column. + * @param icon + * the icon Resource to set. + */ + public void setColumnIcon(Object propertyId, Resource icon) { + + if (icon == null) { + columnIcons.remove(propertyId); + } else { + columnIcons.put(propertyId, icon); + } + + markAsDirty(); + } + + /** + * Gets the header for the specified column. + * + * @param propertyId + * the propertyId identifying the column. + * @return the header for the specified column if it has one. + */ + public String getColumnHeader(Object propertyId) { + if (getColumnHeaderMode() == ColumnHeaderMode.HIDDEN) { + return null; + } + + String header = columnHeaders.get(propertyId); + if ((header == null + && getColumnHeaderMode() == ColumnHeaderMode.EXPLICIT_DEFAULTS_ID) + || getColumnHeaderMode() == ColumnHeaderMode.ID) { + header = propertyId.toString(); + } + + return header; + } + + /** + * Sets the column header for the specified column; + * + * @param propertyId + * the propertyId identifying the column. + * @param header + * the header to set. + */ + public void setColumnHeader(Object propertyId, String header) { + + if (header == null) { + columnHeaders.remove(propertyId); + } else { + columnHeaders.put(propertyId, header); + } + + markAsDirty(); + } + + /** + * Gets the specified column's alignment. + * + * @param propertyId + * the propertyID identifying the column. + * @return the specified column's alignment if it as one; {@link Align#LEFT} + * otherwise. + */ + public Align getColumnAlignment(Object propertyId) { + final Align a = columnAlignments.get(propertyId); + return a == null ? Align.LEFT : a; + } + + /** + * Sets the specified column's alignment. + * + *

    + * Throws IllegalArgumentException if the alignment is not one of the + * following: {@link Align#LEFT}, {@link Align#CENTER} or + * {@link Align#RIGHT} + *

    + * + * @param propertyId + * the propertyID identifying the column. + * @param alignment + * the desired alignment. + */ + public void setColumnAlignment(Object propertyId, Align alignment) { + if (alignment == null || alignment == Align.LEFT) { + columnAlignments.remove(propertyId); + } else { + columnAlignments.put(propertyId, alignment); + } + + // Assures the visual refresh. No need to reset the page buffer before + // as the content has not changed, only the alignments. + refreshRenderedCells(); + } + + /** + * Checks if the specified column is collapsed. + * + * @param propertyId + * the propertyID identifying the column. + * @return true if the column is collapsed; false otherwise; + */ + public boolean isColumnCollapsed(Object propertyId) { + return collapsedColumns != null + && collapsedColumns.contains(propertyId); + } + + /** + * Sets whether the specified column is collapsed or not. + * + * + * @param propertyId + * the propertyID identifying the column. + * @param collapsed + * the desired collapsedness. + * @throws IllegalStateException + * if column collapsing is not allowed + * @throws IllegalArgumentException + * if the property id does not exist + */ + public void setColumnCollapsed(Object propertyId, boolean collapsed) + throws IllegalStateException { + if (!isColumnCollapsingAllowed()) { + throw new IllegalStateException("Column collapsing not allowed!"); + } + if (collapsed && noncollapsibleColumns.contains(propertyId)) { + throw new IllegalStateException("The column is noncollapsible!"); + } + if (!getContainerPropertyIds().contains(propertyId) + && !columnGenerators.containsKey(propertyId)) { + throw new IllegalArgumentException("Property '" + propertyId + + "' was not found in the container"); + } + + if (collapsed) { + if (collapsedColumns.add(propertyId)) { + fireColumnCollapseEvent(propertyId); + } + } else { + if (collapsedColumns.remove(propertyId)) { + fireColumnCollapseEvent(propertyId); + } + } + + // Assures the visual refresh + refreshRowCache(); + } + + /** + * Checks if column collapsing is allowed. + * + * @return true if columns can be collapsed; false otherwise. + */ + public boolean isColumnCollapsingAllowed() { + return columnCollapsingAllowed; + } + + /** + * Sets whether column collapsing is allowed or not. + * + * @param collapsingAllowed + * specifies whether column collapsing is allowed. + */ + public void setColumnCollapsingAllowed(boolean collapsingAllowed) { + columnCollapsingAllowed = collapsingAllowed; + + if (!collapsingAllowed) { + collapsedColumns.clear(); + } + + // Assures the visual refresh. No need to reset the page buffer before + // as the content has not changed, only the alignments. + refreshRenderedCells(); + } + + /** + * Sets whether the given column is collapsible. Note that collapsible + * columns can only be actually collapsed (via UI or with + * {@link #setColumnCollapsed(Object, boolean) setColumnCollapsed()}) if + * {@link #isColumnCollapsingAllowed()} is true. By default all columns are + * collapsible. + * + * @param propertyId + * the propertyID identifying the column. + * @param collapsible + * true if the column should be collapsible, false otherwise. + */ + public void setColumnCollapsible(Object propertyId, boolean collapsible) { + if (collapsible) { + noncollapsibleColumns.remove(propertyId); + } else { + noncollapsibleColumns.add(propertyId); + collapsedColumns.remove(propertyId); + } + refreshRowCache(); + } + + /** + * Checks if the given column is collapsible. Note that even if this method + * returns true, the column can only be actually collapsed (via + * UI or with {@link #setColumnCollapsed(Object, boolean) + * setColumnCollapsed()}) if {@link #isColumnCollapsingAllowed()} is also + * true. + * + * @return true if the column can be collapsed; false otherwise. + */ + public boolean isColumnCollapsible(Object propertyId) { + return !noncollapsibleColumns.contains(propertyId); + } + + /** + * Checks if column reordering is allowed. + * + * @return true if columns can be reordered; false otherwise. + */ + public boolean isColumnReorderingAllowed() { + return columnReorderingAllowed; + } + + /** + * Sets whether column reordering is allowed or not. + * + * @param columnReorderingAllowed + * specifies whether column reordering is allowed. + */ + public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { + if (columnReorderingAllowed != this.columnReorderingAllowed) { + this.columnReorderingAllowed = columnReorderingAllowed; + markAsDirty(); + } + } + + /* + * Arranges visible columns according to given columnOrder. Silently ignores + * colimnId:s that are not visible columns, and keeps the internal order of + * visible columns left out of the ordering (trailing). Silently does + * nothing if columnReordering is not allowed. + */ + private void setColumnOrder(Object[] columnOrder) { + if (columnOrder == null || !isColumnReorderingAllowed()) { + return; + } + final LinkedList newOrder = new LinkedList(); + for (int i = 0; i < columnOrder.length; i++) { + if (columnOrder[i] != null + && visibleColumns.contains(columnOrder[i])) { + visibleColumns.remove(columnOrder[i]); + newOrder.add(columnOrder[i]); + } + } + for (final Iterator it = visibleColumns.iterator(); it + .hasNext();) { + final Object columnId = it.next(); + if (!newOrder.contains(columnId)) { + newOrder.add(columnId); + } + } + visibleColumns = newOrder; + + // Assure visual refresh + refreshRowCache(); + } + + /** + * Getter for property currentPageFirstItem. + * + * @return the Value of property currentPageFirstItem. + */ + public int getCurrentPageFirstItemIndex() { + return currentPageFirstItemIndex; + } + + void setCurrentPageFirstItemIndex(int newIndex, + boolean needsPageBufferReset) { + + if (newIndex < 0) { + newIndex = 0; + } + + /* + * minimize Container.size() calls which may be expensive. For example + * it may cause sql query. + */ + final int size = size(); + + /* + * The table is not capable of displaying an item in the container as + * the first if there are not enough items following the selected item + * so the whole table (pagelength) is filled. + */ + int maxIndex = size - pageLength; + if (maxIndex < 0) { + maxIndex = 0; + } + + /* + * If the new index is on the last page we set the index to be the first + * item on that last page and make a note of the real index for the + * client side to be able to move the scroll position to the correct + * position. + */ + int indexOnLastPage = -1; + if (newIndex > maxIndex) { + indexOnLastPage = newIndex; + newIndex = maxIndex; + } + + // Refresh first item id + if (items instanceof Container.Indexed) { + try { + currentPageFirstItemId = getIdByIndex(newIndex); + } catch (final IndexOutOfBoundsException e) { + currentPageFirstItemId = null; + } + currentPageFirstItemIndex = newIndex; + + if (needsPageBufferReset) { + /* + * The flag currentPageFirstItemIndexOnLastPage denotes a user + * set scrolling position on the last page via + * setCurrentPageFirstItemIndex() and shouldn't be changed by + * the table component internally changing the firstvisible item + * on lazy row fetching. Doing so would make the scrolling + * position not be updated correctly when the lazy rows are + * finally rendered. + */ + + boolean isLastRowPossiblyPartiallyVisible = true; + if (indexOnLastPage != -1) { + /* + * If the requested row was greater than maxIndex, the last + * row should be fully visible (See + * TestCurrentPageFirstItem). + */ + isLastRowPossiblyPartiallyVisible = false; + } + + int extraRows = isLastRowPossiblyPartiallyVisible ? 0 : 1; + currentPageFirstItemIndexOnLastPage = currentPageFirstItemIndex + + extraRows; + } else { + currentPageFirstItemIndexOnLastPage = -1; + } + + } else { + + // For containers not supporting indexes, we must iterate the + // container forwards / backwards + // next available item forward or backward + + currentPageFirstItemId = firstItemId(); + + // Go forwards in the middle of the list (respect borders) + while (currentPageFirstItemIndex < newIndex + && !isLastId(currentPageFirstItemId)) { + currentPageFirstItemIndex++; + currentPageFirstItemId = nextItemId(currentPageFirstItemId); + } + + // If we did hit the border + if (isLastId(currentPageFirstItemId)) { + currentPageFirstItemIndex = size - 1; + } + + // Go backwards in the middle of the list (respect borders) + while (currentPageFirstItemIndex > newIndex + && !isFirstId(currentPageFirstItemId)) { + currentPageFirstItemIndex--; + currentPageFirstItemId = prevItemId(currentPageFirstItemId); + } + + // If we did hit the border + if (isFirstId(currentPageFirstItemId)) { + currentPageFirstItemIndex = 0; + } + + // Go forwards once more + while (currentPageFirstItemIndex < newIndex + && !isLastId(currentPageFirstItemId)) { + currentPageFirstItemIndex++; + currentPageFirstItemId = nextItemId(currentPageFirstItemId); + } + + // If for some reason we do hit border again, override + // the user index request + if (isLastId(currentPageFirstItemId)) { + newIndex = currentPageFirstItemIndex = size - 1; + } + } + + if (needsPageBufferReset) { + // Assures the visual refresh + refreshRowCache(); + } + } + + /** + * Setter for property currentPageFirstItem. + * + * @param newIndex + * the New value of property currentPageFirstItem. + */ + public void setCurrentPageFirstItemIndex(int newIndex) { + setCurrentPageFirstItemIndex(newIndex, true); + } + + /** + * Returns whether table is selectable. + * + *

    + * The table is not selectable until it's explicitly set as selectable or at + * least one {@link ValueChangeListener} is added. + *

    + * + * @return whether table is selectable. + */ + public boolean isSelectable() { + if (selectable == null) { + return hasListeners(ValueChangeEvent.class); + } + return selectable; + } + + /** + * Setter for property selectable. + * + *

    + * The table is not selectable until it's explicitly set as selectable via + * this method or alternatively at least one {@link ValueChangeListener} is + * added. + *

    + * + * @param selectable + * the New value of property selectable. + */ + public void setSelectable(boolean selectable) { + if (!SharedUtil.equals(this.selectable, selectable)) { + this.selectable = selectable; + markAsDirty(); + } + } + + /** + * Getter for property columnHeaderMode. + * + * @return the Value of property columnHeaderMode. + */ + public ColumnHeaderMode getColumnHeaderMode() { + return columnHeaderMode; + } + + /** + * Setter for property columnHeaderMode. + * + * @param columnHeaderMode + * the New value of property columnHeaderMode. + */ + public void setColumnHeaderMode(ColumnHeaderMode columnHeaderMode) { + if (columnHeaderMode == null) { + throw new IllegalArgumentException( + "Column header mode can not be null"); + } + if (columnHeaderMode != this.columnHeaderMode) { + this.columnHeaderMode = columnHeaderMode; + markAsDirty(); + } + + } + + /** + * Refreshes the rows in the internal cache. Only if + * {@link #resetPageBuffer()} is called before this then all values are + * guaranteed to be recreated. + */ + protected void refreshRenderedCells() { + if (!isAttached()) { + return; + } + + if (!isContentRefreshesEnabled) { + return; + } + + // Collects the basic facts about the table page + final int pagelen = getPageLength(); + int rows, totalRows; + rows = totalRows = size(); + int firstIndex = Math.min(getCurrentPageFirstItemIndex(), + totalRows - 1); + if (rows > 0 && firstIndex >= 0) { + rows -= firstIndex; + } + if (pagelen > 0 && pagelen < rows) { + rows = pagelen; + } + + // If "to be painted next" variables are set, use them + if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) { + rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1; + } + if (firstToBeRenderedInClient >= 0) { + if (firstToBeRenderedInClient < totalRows) { + firstIndex = firstToBeRenderedInClient; + } else { + firstIndex = totalRows - 1; + } + } else { + // initial load + + // #8805 send one extra row in the beginning in case a partial + // row is shown on the UI + if (firstIndex > 0) { + firstIndex = firstIndex - 1; + rows = rows + 1; + } + firstToBeRenderedInClient = firstIndex; + } + if (totalRows > 0) { + if (rows + firstIndex > totalRows) { + rows = totalRows - firstIndex; + } + } else { + rows = 0; + } + + // Saves the results to internal buffer + pageBuffer = getVisibleCellsNoCache(firstIndex, rows, true); + + if (rows > 0) { + pageBufferFirstIndex = firstIndex; + } + if (getPageLength() != 0) { + removeUnnecessaryRows(); + } + + setRowCacheInvalidated(true); + markAsDirty(); + maybeThrowCacheUpdateExceptions(); + + } + + private void maybeThrowCacheUpdateExceptions() { + if (!exceptionsDuringCachePopulation.isEmpty()) { + Throwable[] causes = new Throwable[exceptionsDuringCachePopulation + .size()]; + exceptionsDuringCachePopulation.toArray(causes); + + exceptionsDuringCachePopulation.clear(); + throw new CacheUpdateException(this, + "Error during Table cache update.", causes); + } + + } + + /** + * Exception thrown when one or more exceptions occurred during updating of + * the Table cache. + *

    + * Contains all exceptions which occurred during the cache update. The first + * occurred exception is set as the cause of this exception. All occurred + * exceptions can be accessed using {@link #getCauses()}. + *

    + * + */ + public static class CacheUpdateException extends RuntimeException { + private Throwable[] causes; + private Table table; + + public CacheUpdateException(Table table, String message, + Throwable[] causes) { + super(maybeSupplementMessage(message, causes.length), causes[0]); + this.table = table; + this.causes = causes; + } + + private static String maybeSupplementMessage(String message, + int causeCount) { + if (causeCount > 1) { + return message + " Additional causes not shown."; + } else { + return message; + } + } + + /** + * Returns the cause(s) for this exception + * + * @return the exception(s) which caused this exception + */ + public Throwable[] getCauses() { + return causes; + } + + public Table getTable() { + return table; + } + + } + + /** + * Removes rows that fall outside the required cache. + */ + private void removeUnnecessaryRows() { + int minPageBufferIndex = getMinPageBufferIndex(); + int maxPageBufferIndex = getMaxPageBufferIndex(); + + int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1; + + /* + * Number of rows that were previously cached. This is not necessarily + * the same as pageLength if we do not have enough rows in the + * container. + */ + int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length; + + if (currentlyCachedRowCount <= maxBufferSize) { + // removal unnecessary + return; + } + + /* Figure out which rows to get rid of. */ + int firstCacheRowToRemoveInPageBuffer = -1; + if (minPageBufferIndex > pageBufferFirstIndex) { + firstCacheRowToRemoveInPageBuffer = pageBufferFirstIndex; + } else if (maxPageBufferIndex < pageBufferFirstIndex + + currentlyCachedRowCount) { + firstCacheRowToRemoveInPageBuffer = maxPageBufferIndex + 1; + } + + if (firstCacheRowToRemoveInPageBuffer + - pageBufferFirstIndex < currentlyCachedRowCount) { + /* + * Unregister all components that fall beyond the cache limits after + * inserting the new rows. + */ + unregisterComponentsAndPropertiesInRows( + firstCacheRowToRemoveInPageBuffer, currentlyCachedRowCount + - firstCacheRowToRemoveInPageBuffer); + } + } + + /** + * Requests that the Table should be repainted as soon as possible. + * + * Note that a {@code Table} does not necessarily repaint its contents when + * this method has been called. See {@link #refreshRowCache()} for forcing + * an update of the contents. + * + * @deprecated As of 7.0, use {@link #markAsDirty()} instead + */ + + @Deprecated + @Override + public void requestRepaint() { + markAsDirty(); + } + + /** + * Requests that the Table should be repainted as soon as possible. + * + * Note that a {@code Table} does not necessarily repaint its contents when + * this method has been called. See {@link #refreshRowCache()} for forcing + * an update of the contents. + */ + + @Override + public void markAsDirty() { + // Overridden only for javadoc + super.markAsDirty(); + } + + @Override + public void markAsDirtyRecursive() { + super.markAsDirtyRecursive(); + + // Avoid sending a partial repaint (#8714) + refreshRowCache(); + } + + private void removeRowsFromCacheAndFillBottom(int firstIndex, int rows) { + int totalCachedRows = pageBuffer[CELL_ITEMID].length; + int totalRows = size(); + int firstIndexInPageBuffer = firstIndex - pageBufferFirstIndex; + + /* + * firstIndexInPageBuffer is the first row to be removed. "rows" rows + * after that should be removed. If the page buffer does not contain + * that many rows, we only remove the rows that actually are in the page + * buffer. + */ + if (firstIndexInPageBuffer + rows > totalCachedRows) { + rows = totalCachedRows - firstIndexInPageBuffer; + } + + /* + * Unregister components that will no longer be in the page buffer to + * make sure that no components leak. + */ + unregisterComponentsAndPropertiesInRows(firstIndex, rows); + + /* + * The number of rows that should be in the cache after this operation + * is done (pageBuffer currently contains the expanded items). + */ + int newCachedRowCount = totalCachedRows; + if (newCachedRowCount + pageBufferFirstIndex > totalRows) { + newCachedRowCount = totalRows - pageBufferFirstIndex; + } + + /* + * The index at which we should render the first row that does not come + * from the previous page buffer. + */ + int firstAppendedRowInPageBuffer = totalCachedRows - rows; + int firstAppendedRow = firstAppendedRowInPageBuffer + + pageBufferFirstIndex; + + /* + * Calculate the maximum number of new rows that we can add to the page + * buffer. Less than the rows we removed if the container does not + * contain that many items afterwards. + */ + int maxRowsToRender = (totalRows - firstAppendedRow); + int rowsToAdd = rows; + if (rowsToAdd > maxRowsToRender) { + rowsToAdd = maxRowsToRender; + } + + Object[][] cells = null; + if (rowsToAdd > 0) { + cells = getVisibleCellsNoCache(firstAppendedRow, rowsToAdd, false); + } + /* + * Create the new cache buffer by copying the first rows from the old + * buffer, moving the following rows upwards and appending more rows if + * applicable. + */ + Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount]; + + for (int i = 0; i < pageBuffer.length; i++) { + for (int row = 0; row < firstIndexInPageBuffer; row++) { + // Copy the first rows + newPageBuffer[i][row] = pageBuffer[i][row]; + } + for (int row = firstIndexInPageBuffer; row < firstAppendedRowInPageBuffer; row++) { + // Move the rows that were after the expanded rows + newPageBuffer[i][row] = pageBuffer[i][row + rows]; + } + for (int row = firstAppendedRowInPageBuffer; row < newCachedRowCount; row++) { + // Add the newly rendered rows. Only used if rowsToAdd > 0 + // (cells != null) + newPageBuffer[i][row] = cells[i][row + - firstAppendedRowInPageBuffer]; + } + } + pageBuffer = newPageBuffer; + } + + private Object[][] getVisibleCellsUpdateCacheRows(int firstIndex, + int rows) { + Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false); + int cacheIx = firstIndex - pageBufferFirstIndex; + // update the new rows in the cache. + int totalCachedRows = pageBuffer[CELL_ITEMID].length; + int end = Math.min(cacheIx + rows, totalCachedRows); + for (int ix = cacheIx; ix < end; ix++) { + for (int i = 0; i < pageBuffer.length; i++) { + pageBuffer[i][ix] = cells[i][ix - cacheIx]; + } + } + return cells; + } + + /** + * @param firstIndex + * The position where new rows should be inserted + * @param rows + * The maximum number of rows that should be inserted at position + * firstIndex. Less rows will be inserted if the page buffer is + * too small. + * @return + */ + private Object[][] getVisibleCellsInsertIntoCache(int firstIndex, + int rows) { + getLogger().log(Level.FINEST, + "Insert {0} rows at index {1} to existing page buffer requested", + new Object[] { rows, firstIndex }); + + int minPageBufferIndex = getMinPageBufferIndex(); + int maxPageBufferIndex = getMaxPageBufferIndex(); + + int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1; + + if (getPageLength() == 0) { + // If pageLength == 0 then all rows should be rendered + maxBufferSize = pageBuffer[CELL_ITEMID].length + rows; + } + /* + * Number of rows that were previously cached. This is not necessarily + * the same as maxBufferSize. + */ + int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length; + + /* If rows > size available in page buffer */ + if (firstIndex + rows - 1 > maxPageBufferIndex) { + rows = maxPageBufferIndex - firstIndex + 1; + } + + /* + * "rows" rows will be inserted at firstIndex. Find out how many old + * rows fall outside the new buffer so we can unregister components in + * the cache. + */ + + /* + * if there are rows before the new pageBuffer limits they must be + * removed + */ + int lastCacheRowToRemove = minPageBufferIndex - 1; + int rowsFromBeginning = lastCacheRowToRemove - pageBufferFirstIndex + 1; + if (lastCacheRowToRemove >= pageBufferFirstIndex) { + unregisterComponentsAndPropertiesInRows(pageBufferFirstIndex, + rowsFromBeginning); + } else { + rowsFromBeginning = 0; + } + + /* + * the rows that fall outside of the new pageBuffer limits after the new + * rows are inserted must also be removed + */ + int firstCacheRowToRemove = firstIndex; + /* + * IF there is space remaining in the buffer after the rows have been + * inserted, we can keep more rows. + */ + int numberOfOldRowsAfterInsertedRows = Math.min( + pageBufferFirstIndex + currentlyCachedRowCount + rows, + maxPageBufferIndex + 1) - (firstIndex + rows - 1); + if (numberOfOldRowsAfterInsertedRows > 0) { + firstCacheRowToRemove += numberOfOldRowsAfterInsertedRows; + } + int rowsFromAfter = currentlyCachedRowCount + - (firstCacheRowToRemove - pageBufferFirstIndex); + + if (rowsFromAfter > 0) { + /* + * Unregister all components that fall beyond the cache limits after + * inserting the new rows. + */ + unregisterComponentsAndPropertiesInRows(firstCacheRowToRemove, + rowsFromAfter); + } + + // Calculate the new cache size + int newCachedRowCount = maxBufferSize; + if (pageBufferFirstIndex + currentlyCachedRowCount + rows + - 1 < maxPageBufferIndex) { + // there aren't enough rows to fill the whole potential -> use what + // there is + newCachedRowCount -= maxPageBufferIndex - (pageBufferFirstIndex + + currentlyCachedRowCount + rows - 1); + } else if (minPageBufferIndex < pageBufferFirstIndex) { + newCachedRowCount -= pageBufferFirstIndex - minPageBufferIndex; + } + /* + * calculate the internal location of the new rows within the new cache + */ + int firstIndexInNewPageBuffer = firstIndex - pageBufferFirstIndex + - rowsFromBeginning; + + /* Paint the new rows into a separate buffer */ + Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false); + + /* + * Create the new cache buffer and fill it with the data from the old + * buffer as well as the inserted rows. + */ + Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount]; + + for (int i = 0; i < pageBuffer.length; i++) { + for (int row = 0; row < firstIndexInNewPageBuffer; row++) { + // Copy the first rows + newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row]; + } + for (int row = firstIndexInNewPageBuffer; row < firstIndexInNewPageBuffer + + rows; row++) { + // Copy the newly created rows + newPageBuffer[i][row] = cells[i][row + - firstIndexInNewPageBuffer]; + } + for (int row = firstIndexInNewPageBuffer + + rows; row < newCachedRowCount; row++) { + // Move the old rows down below the newly inserted rows + newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row + - rows]; + } + } + pageBuffer = newPageBuffer; + pageBufferFirstIndex = Math.max( + pageBufferFirstIndex + rowsFromBeginning, minPageBufferIndex); + if (getLogger().isLoggable(Level.FINEST)) { + getLogger().log(Level.FINEST, + "Page Buffer now contains {0} rows ({1}-{2})", + new Object[] { pageBuffer[CELL_ITEMID].length, + pageBufferFirstIndex, (pageBufferFirstIndex + + pageBuffer[CELL_ITEMID].length - 1) }); + } + return cells; + } + + private int getMaxPageBufferIndex() { + int total = size(); + if (getPageLength() == 0) { + // everything is shown at once, no caching + return total - 1; + } + // Page buffer must not become larger than pageLength*cacheRate after + // the current page + int maxPageBufferIndex = getCurrentPageFirstItemIndex() + + (int) (getPageLength() * (1 + getCacheRate())); + if (shouldHideNullSelectionItem()) { + --total; + } + if (maxPageBufferIndex >= total) { + maxPageBufferIndex = total - 1; + } + return maxPageBufferIndex; + } + + private int getMinPageBufferIndex() { + if (getPageLength() == 0) { + // everything is shown at once, no caching + return 0; + } + // Page buffer must not become larger than pageLength*cacheRate before + // the current page + int minPageBufferIndex = getCurrentPageFirstItemIndex() + - (int) (getPageLength() * getCacheRate()); + if (minPageBufferIndex < 0) { + minPageBufferIndex = 0; + } + return minPageBufferIndex; + } + + /** + * Render rows with index "firstIndex" to "firstIndex+rows-1" to a new + * buffer. + * + * Reuses values from the current page buffer if the rows are found there. + * + * @param firstIndex + * @param rows + * @param replaceListeners + * @return + */ + private Object[][] getVisibleCellsNoCache(int firstIndex, int rows, + boolean replaceListeners) { + if (getLogger().isLoggable(Level.FINEST)) { + getLogger().log(Level.FINEST, + "Render visible cells for rows {0}-{1}", + new Object[] { firstIndex, (firstIndex + rows - 1) }); + } + final Object[] colids = getVisibleColumns(); + final int cols = colids.length; + + HashSet> oldListenedProperties = listenedProperties; + HashSet oldVisibleComponents = visibleComponents; + + if (replaceListeners) { + // initialize the listener collections, this should only be done if + // the entire cache is refreshed (through refreshRenderedCells) + listenedProperties = new HashSet>(); + visibleComponents = new HashSet(); + } + + Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows]; + if (rows == 0) { + unregisterPropertiesAndComponents(oldListenedProperties, + oldVisibleComponents); + return cells; + } + + final RowHeaderMode headmode = getRowHeaderMode(); + final boolean[] iscomponent = new boolean[cols]; + for (int i = 0; i < cols; i++) { + iscomponent[i] = columnGenerators.containsKey(colids[i]) + || Component.class.isAssignableFrom(getType(colids[i])); + } + int firstIndexNotInCache; + if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) { + firstIndexNotInCache = pageBufferFirstIndex + + pageBuffer[CELL_ITEMID].length; + } else { + firstIndexNotInCache = -1; + } + + // Creates the page contents + int filledRows = 0; + if (items instanceof Container.Indexed) { + // more efficient implementation for containers supporting access by + // index + + List itemIds = getItemIds(firstIndex, rows); + for (int i = 0; i < rows && i < itemIds.size(); i++) { + Object id = itemIds.get(i); + if (id == null) { + throw new IllegalStateException( + "Null itemId returned from container"); + } + // Start by parsing the values, id should already be set + parseItemIdToCells(cells, id, i, firstIndex, headmode, cols, + colids, firstIndexNotInCache, iscomponent, + oldListenedProperties); + + filledRows++; + } + } else { + // slow back-up implementation for cases where the container does + // not support access by index + + // Gets the first item id + Object id = firstItemId(); + for (int i = 0; i < firstIndex; i++) { + id = nextItemId(id); + } + for (int i = 0; i < rows && id != null; i++) { + // Start by parsing the values, id should already be set + parseItemIdToCells(cells, id, i, firstIndex, headmode, cols, + colids, firstIndexNotInCache, iscomponent, + oldListenedProperties); + + // Gets the next item id for non indexed container + id = nextItemId(id); + + filledRows++; + } + } + + // Assures that all the rows of the cell-buffer are valid + if (filledRows != cells[0].length) { + final Object[][] temp = new Object[cells.length][filledRows]; + for (int i = 0; i < cells.length; i++) { + for (int j = 0; j < filledRows; j++) { + temp[i][j] = cells[i][j]; + } + } + cells = temp; + } + + unregisterPropertiesAndComponents(oldListenedProperties, + oldVisibleComponents); + + return cells; + } + + protected List getItemIds(int firstIndex, int rows) { + return (List) ((Container.Indexed) items).getItemIds(firstIndex, + rows); + } + + /** + * Update a cache array for a row, register any relevant listeners etc. + * + * This is an internal method extracted from + * {@link #getVisibleCellsNoCache(int, int, boolean)} and should be removed + * when the Table is rewritten. + */ + private void parseItemIdToCells(Object[][] cells, Object id, int i, + int firstIndex, RowHeaderMode headmode, int cols, Object[] colids, + int firstIndexNotInCache, boolean[] iscomponent, + HashSet> oldListenedProperties) { + + cells[CELL_ITEMID][i] = id; + cells[CELL_KEY][i] = itemIdMapper.key(id); + if (headmode != ROW_HEADER_MODE_HIDDEN) { + switch (headmode) { + case INDEX: + cells[CELL_HEADER][i] = String.valueOf(i + firstIndex + 1); + break; + default: + try { + cells[CELL_HEADER][i] = getItemCaption(id); + } catch (Exception e) { + exceptionsDuringCachePopulation.add(e); + cells[CELL_HEADER][i] = ""; + } + } + try { + cells[CELL_ICON][i] = getItemIcon(id); + } catch (Exception e) { + exceptionsDuringCachePopulation.add(e); + cells[CELL_ICON][i] = null; + } + } + + GeneratedRow generatedRow = rowGenerator != null + ? rowGenerator.generateRow(this, id) : null; + cells[CELL_GENERATED_ROW][i] = generatedRow; + + for (int j = 0; j < cols; j++) { + if (isColumnCollapsed(colids[j])) { + continue; + } + Property p = null; + Object value = ""; + boolean isGeneratedRow = generatedRow != null; + boolean isGeneratedColumn = columnGenerators.containsKey(colids[j]); + boolean isGenerated = isGeneratedRow || isGeneratedColumn; + + if (!isGenerated) { + try { + p = getContainerProperty(id, colids[j]); + } catch (Exception e) { + exceptionsDuringCachePopulation.add(e); + value = null; + } + } + + if (isGeneratedRow) { + if (generatedRow.isSpanColumns() && j > 0) { + value = null; + } else if (generatedRow.isSpanColumns() && j == 0 + && generatedRow.getValue() instanceof Component) { + value = generatedRow.getValue(); + } else if (generatedRow.getText().length > j) { + value = generatedRow.getText()[j]; + } + } else { + // check if current pageBuffer already has row + int index = firstIndex + i; + if (p != null || isGenerated) { + int indexInOldBuffer = index - pageBufferFirstIndex; + if (index < firstIndexNotInCache + && index >= pageBufferFirstIndex + && pageBuffer[CELL_GENERATED_ROW][indexInOldBuffer] == null + && id.equals( + pageBuffer[CELL_ITEMID][indexInOldBuffer])) { + // we already have data in our cache, + // recycle it instead of fetching it via + // getValue/getPropertyValue + value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer]; + if (!isGeneratedColumn && iscomponent[j] + || !(value instanceof Component)) { + listenProperty(p, oldListenedProperties); + } + } else { + if (isGeneratedColumn) { + ColumnGenerator cg = columnGenerators + .get(colids[j]); + try { + value = cg.generateCell(this, id, colids[j]); + } catch (Exception e) { + exceptionsDuringCachePopulation.add(e); + value = null; + } + if (value != null && !(value instanceof Component) + && !(value instanceof String)) { + // Avoid errors if a generator returns + // something + // other than a Component or a String + value = value.toString(); + } + } else if (iscomponent[j]) { + try { + value = p.getValue(); + } catch (Exception e) { + exceptionsDuringCachePopulation.add(e); + value = null; + } + listenProperty(p, oldListenedProperties); + } else if (p != null) { + try { + value = getPropertyValue(id, colids[j], p); + } catch (Exception e) { + exceptionsDuringCachePopulation.add(e); + value = null; + } + /* + * If returned value is Component (via fieldfactory + * or overridden getPropertyValue) we expect it to + * listen property value changes. Otherwise if + * property emits value change events, table will + * start to listen them and refresh content when + * needed. + */ + if (!(value instanceof Component)) { + listenProperty(p, oldListenedProperties); + } + } else { + try { + value = getPropertyValue(id, colids[j], null); + } catch (Exception e) { + exceptionsDuringCachePopulation.add(e); + value = null; + } + } + } + } + } + + if (value instanceof Component) { + registerComponent((Component) value); + } + cells[CELL_FIRSTCOL + j][i] = value; + } + } + + protected void registerComponent(Component component) { + getLogger().log(Level.FINEST, "Registered {0}: {1}", new Object[] { + component.getClass().getSimpleName(), component.getCaption() }); + if (!equals(component.getParent())) { + component.setParent(this); + } + visibleComponents.add(component); + } + + private void listenProperty(Property p, + HashSet> oldListenedProperties) { + if (p instanceof Property.ValueChangeNotifier) { + if (oldListenedProperties == null + || !oldListenedProperties.contains(p)) { + ((Property.ValueChangeNotifier) p).addListener(this); + } + /* + * register listened properties, so we can do proper cleanup to free + * memory. Essential if table has loads of data and it is used for a + * long time. + */ + listenedProperties.add(p); + + } + } + + /** + * @param firstIx + * Index of the first row to process. Global index, not relative + * to page buffer. + * @param count + */ + private void unregisterComponentsAndPropertiesInRows(int firstIx, + int count) { + if (getLogger().isLoggable(Level.FINEST)) { + getLogger().log(Level.FINEST, + "Unregistering components in rows {0}-{1}", + new Object[] { firstIx, (firstIx + count - 1) }); + } + Object[] colids = getVisibleColumns(); + if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) { + int bufSize = pageBuffer[CELL_ITEMID].length; + int ix = firstIx - pageBufferFirstIndex; + ix = ix < 0 ? 0 : ix; + if (ix < bufSize) { + count = count > bufSize - ix ? bufSize - ix : count; + for (int i = 0; i < count; i++) { + for (int c = 0; c < colids.length; c++) { + Object cellVal = pageBuffer[CELL_FIRSTCOL + c][i + ix]; + if (cellVal instanceof Component + && visibleComponents.contains(cellVal)) { + visibleComponents.remove(cellVal); + unregisterComponent((Component) cellVal); + } else { + Property p = getContainerProperty( + pageBuffer[CELL_ITEMID][i + ix], colids[c]); + if (p instanceof ValueChangeNotifier + && listenedProperties.contains(p)) { + listenedProperties.remove(p); + ((ValueChangeNotifier) p).removeListener(this); + } + } + } + } + } + } + } + + /** + * Helper method to remove listeners and maintain correct component + * hierarchy. Detaches properties and components if those are no more + * rendered in client. + * + * @param oldListenedProperties + * set of properties that where listened in last render + * @param oldVisibleComponents + * set of components that where attached in last render + */ + private void unregisterPropertiesAndComponents( + HashSet> oldListenedProperties, + HashSet oldVisibleComponents) { + if (oldVisibleComponents != null) { + for (final Iterator i = oldVisibleComponents + .iterator(); i.hasNext();) { + Component c = i.next(); + if (!visibleComponents.contains(c)) { + unregisterComponent(c); + } + } + } + + if (oldListenedProperties != null) { + for (final Iterator> i = oldListenedProperties + .iterator(); i.hasNext();) { + Property.ValueChangeNotifier o = (ValueChangeNotifier) i.next(); + if (!listenedProperties.contains(o)) { + o.removeListener(this); + } + } + } + } + + /** + * This method cleans up a Component that has been generated when Table is + * in editable mode. The component needs to be detached from its parent and + * if it is a field, it needs to be detached from its property data source + * in order to allow garbage collection to take care of removing the unused + * component from memory. + * + * Override this method and getPropertyValue(Object, Object, Property) with + * custom logic if you need to deal with buffered fields. + * + * @see #getPropertyValue(Object, Object, Property) + * + * @param component + * component that should be unregistered. + */ + protected void unregisterComponent(Component component) { + getLogger().log(Level.FINEST, "Unregistered {0}: {1}", new Object[] { + component.getClass().getSimpleName(), component.getCaption() }); + component.setParent(null); + /* + * Also remove property data sources to unregister listeners keeping the + * fields in memory. + */ + if (component instanceof Field) { + Field field = (Field) component; + Property associatedProperty = associatedProperties + .remove(component); + if (associatedProperty != null + && field.getPropertyDataSource() == associatedProperty) { + // Remove the property data source only if it's the one we + // added in getPropertyValue + field.setPropertyDataSource(null); + } + } + } + + /** + * Sets the row header mode. + *

    + * The mode can be one of the following ones: + *

      + *
    • {@link #ROW_HEADER_MODE_HIDDEN}: The row captions are hidden.
    • + *
    • {@link #ROW_HEADER_MODE_ID}: Items Id-objects toString() + * is used as row caption. + *
    • {@link #ROW_HEADER_MODE_ITEM}: Item-objects toString() + * is used as row caption. + *
    • {@link #ROW_HEADER_MODE_PROPERTY}: Property set with + * {@link #setItemCaptionPropertyId(Object)} is used as row header. + *
    • {@link #ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID}: Items Id-objects + * toString() is used as row header. If caption is explicitly + * specified, it overrides the id-caption. + *
    • {@link #ROW_HEADER_MODE_EXPLICIT}: The row headers must be explicitly + * specified.
    • + *
    • {@link #ROW_HEADER_MODE_INDEX}: The index of the item is used as row + * caption. The index mode can only be used with the containers implementing + * Container.Indexed interface.
    • + *
    + * The default value is {@link #ROW_HEADER_MODE_HIDDEN} + *

    + * + * @param mode + * the One of the modes listed above. + */ + public void setRowHeaderMode(RowHeaderMode mode) { + if (mode != null) { + rowHeaderMode = mode; + if (mode != RowHeaderMode.HIDDEN) { + setItemCaptionMode(mode.getItemCaptionMode()); + } + // Assures the visual refresh. No need to reset the page buffer + // before + // as the content has not changed, only the alignments. + refreshRenderedCells(); + } + } + + /** + * Gets the row header mode. + * + * @return the Row header mode. + * @see #setRowHeaderMode + */ + public RowHeaderMode getRowHeaderMode() { + return rowHeaderMode; + } + + /** + * Adds the new row to table and fill the visible cells (except generated + * columns) with given values. + * + * @param cells + * the Object array that is used for filling the visible cells + * new row. The types must be settable to visible column property + * types. + * @param itemId + * the Id the new row. If null, a new id is automatically + * assigned. If given, the table cannot already have a item with + * given id. + * @return Returns item id for the new row. Returns null if operation fails. + */ + public Object addItem(Object[] cells, Object itemId) + throws UnsupportedOperationException { + + // remove generated columns from the list of columns being assigned + final LinkedList availableCols = new LinkedList(); + for (Iterator it = visibleColumns.iterator(); it.hasNext();) { + Object id = it.next(); + if (!columnGenerators.containsKey(id)) { + availableCols.add(id); + } + } + // Checks that a correct number of cells are given + if (cells.length != availableCols.size()) { + return null; + } + + // Creates new item + Item item; + if (itemId == null) { + itemId = items.addItem(); + if (itemId == null) { + return null; + } + item = items.getItem(itemId); + } else { + item = items.addItem(itemId); + } + if (item == null) { + return null; + } + + // Fills the item properties + for (int i = 0; i < availableCols.size(); i++) { + item.getItemProperty(availableCols.get(i)).setValue(cells[i]); + } + + if (!(items instanceof Container.ItemSetChangeNotifier)) { + refreshRowCache(); + } + + return itemId; + } + + /** + * Discards and recreates the internal row cache. Call this if you make + * changes that affect the rows but the information about the changes are + * not automatically propagated to the Table. + *

    + * Do not call this e.g. if you have updated the data model through a + * Property. These types of changes are automatically propagated to the + * Table. + *

    + * A typical case when this is needed is if you update a generator (e.g. + * CellStyleGenerator) and want to ensure that the rows are redrawn with new + * styles. + *

    + * Note that calling this method is not cheap so avoid calling it + * unnecessarily. + * + * @since 6.7.2 + */ + public void refreshRowCache() { + resetPageBuffer(); + refreshRenderedCells(); + } + + /** + * Sets the Container that serves as the data source of the viewer. As a + * side-effect the table's selection value is set to null as the old + * selection might not exist in new Container.
    + *
    + * All rows and columns are generated as visible using this method. If the + * new container contains properties that are not meant to be shown you + * should use {@link Table#setContainerDataSource(Container, Collection)} + * instead, especially if the table is editable. + *

    + * Keeps propertyValueConverters if the corresponding id exists in the new + * data source and is of a compatible type. + *

    + * + * @param newDataSource + * the new data source. + */ + @Override + public void setContainerDataSource(Container newDataSource) { + if (newDataSource == null) { + newDataSource = new IndexedContainer(); + } + + Collection generated; + if (columnGenerators != null) { + generated = columnGenerators.keySet(); + } else { + generated = Collections.emptyList(); + } + List visibleIds = new ArrayList(); + if (generated.isEmpty()) { + visibleIds.addAll(newDataSource.getContainerPropertyIds()); + } else { + for (Object id : newDataSource.getContainerPropertyIds()) { + // don't add duplicates + if (!generated.contains(id)) { + visibleIds.add(id); + } + } + // generated columns to the end + visibleIds.addAll(generated); + } + setContainerDataSource(newDataSource, visibleIds); + } + + /** + * Sets the container data source and the columns that will be visible. + * Columns are shown in the collection's iteration order. + *

    + * Keeps propertyValueConverters if the corresponding id exists in the new + * data source and is of a compatible type. + *

    + * + * @see Table#setContainerDataSource(Container) + * @see Table#setVisibleColumns(Object[]) + * @see Table#setConverter(Object, Converter) + * + * @param newDataSource + * the new data source. + * @param visibleIds + * IDs of the visible columns + */ + public void setContainerDataSource(Container newDataSource, + Collection visibleIds) { + + disableContentRefreshing(); + + if (newDataSource == null) { + newDataSource = new IndexedContainer(); + } + if (visibleIds == null) { + visibleIds = new ArrayList(); + } + + // Retain propertyValueConverters if their corresponding ids are + // properties of the new + // data source and are of a compatible type + if (propertyValueConverters != null) { + Collection newPropertyIds = newDataSource + .getContainerPropertyIds(); + LinkedList retainableValueConverters = new LinkedList(); + for (Object propertyId : newPropertyIds) { + Converter converter = getConverter(propertyId); + if (converter != null) { + if (typeIsCompatible(converter.getModelType(), + newDataSource.getType(propertyId))) { + retainableValueConverters.add(propertyId); + } + } + } + propertyValueConverters.keySet() + .retainAll(retainableValueConverters); + } + + // Assures that the data source is ordered by making unordered + // containers ordered by wrapping them + if (newDataSource instanceof Container.Ordered) { + super.setContainerDataSource(newDataSource); + } else { + super.setContainerDataSource( + new ContainerOrderedWrapper(newDataSource)); + } + + // Resets page position + currentPageFirstItemId = null; + currentPageFirstItemIndex = 0; + + // Resets column properties + if (collapsedColumns != null) { + collapsedColumns.clear(); + } + + // don't add the same id twice + Collection col = new LinkedList(); + for (Iterator it = visibleIds.iterator(); it.hasNext();) { + Object id = it.next(); + if (!col.contains(id)) { + col.add(id); + } + } + + setVisibleColumns(col.toArray()); + + // Assure visual refresh + resetPageBuffer(); + + enableContentRefreshing(true); + } + + /** + * Checks if class b can be safely assigned to class a. + * + * @param a + * @param b + * @return + */ + private boolean typeIsCompatible(Class a, Class b) { + // TODO Implement this check properly + // Basically we need to do a a.isAssignableFrom(b) + // with special considerations for primitive types. + return true; + } + + /** + * Gets items ids from a range of key values + * + * @param itemId + * The start key + * @param length + * amount of items to be retrieved + * @return + */ + private LinkedHashSet getItemIdsInRange(Object itemId, + final int length) { + LinkedHashSet ids = new LinkedHashSet(); + for (int i = 0; i < length; i++) { + assert itemId != null; // should not be null unless client-server + // are out of sync + ids.add(itemId); + itemId = nextItemId(itemId); + } + return ids; + } + + /** + * Handles selection if selection is a multiselection + * + * @param variables + * The variables + */ + private void handleSelectedItems(Map variables) { + final String[] ka = (String[]) variables.get("selected"); + final String[] ranges = (String[]) variables.get("selectedRanges"); + + Set renderedButNotSelectedItemIds = getCurrentlyRenderedItemIds(); + + @SuppressWarnings("unchecked") + HashSet newValue = new LinkedHashSet( + (Collection) getValue()); + + if (variables.containsKey("clearSelections")) { + // the client side has instructed to swipe all previous selections + newValue.clear(); + } + + /* + * Then add (possibly some of them back) rows that are currently + * selected on the client side (the ones that the client side is aware + * of). + */ + for (int i = 0; i < ka.length; i++) { + // key to id + final Object id = itemIdMapper.get(ka[i]); + if (!isNullSelectionAllowed() + && (id == null || id == getNullSelectionItemId())) { + // skip empty selection if nullselection is not allowed + markAsDirty(); + } else if (id != null && containsId(id)) { + newValue.add(id); + renderedButNotSelectedItemIds.remove(id); + } + } + + /* Add range items aka shift clicked multiselection areas */ + if (ranges != null) { + for (String range : ranges) { + String[] split = range.split("-"); + Object startItemId = itemIdMapper.get(split[0]); + int length = Integer.valueOf(split[1]); + LinkedHashSet itemIdsInRange = getItemIdsInRange( + startItemId, length); + newValue.addAll(itemIdsInRange); + renderedButNotSelectedItemIds.removeAll(itemIdsInRange); + } + } + /* + * finally clear all currently rendered rows (the ones that the client + * side counterpart is aware of) that the client didn't send as selected + */ + newValue.removeAll(renderedButNotSelectedItemIds); + + if (!isNullSelectionAllowed() && newValue.isEmpty()) { + // empty selection not allowed, keep old value + markAsDirty(); + return; + } + + setValue(newValue, true); + + } + + private Set getCurrentlyRenderedItemIds() { + HashSet ids = new HashSet(); + if (pageBuffer != null) { + for (int i = 0; i < pageBuffer[CELL_ITEMID].length; i++) { + ids.add(pageBuffer[CELL_ITEMID][i]); + } + } + return ids; + } + + /* Component basics */ + + /** + * Invoked when the value of a variable has changed. + * + * @see com.vaadin.v7.ui.Select#changeVariables(java.lang.Object, + * java.util.Map) + */ + + @Override + public void changeVariables(Object source, Map variables) { + + boolean clientNeedsContentRefresh = false; + + handleClickEvent(variables); + + handleColumnResizeEvent(variables); + + handleColumnWidthUpdates(variables); + + disableContentRefreshing(); + + if (!isSelectable() && variables.containsKey("selected")) { + // Not-selectable is a special case, AbstractSelect does not support + // TODO could be optimized. + variables = new HashMap(variables); + variables.remove("selected"); + } + + /* + * The AbstractSelect cannot handle the multiselection properly, instead + * we handle it ourself + */ + else if (isSelectable() && isMultiSelect() + && variables.containsKey("selected") + && multiSelectMode == MultiSelectMode.DEFAULT) { + handleSelectedItems(variables); + variables = new HashMap(variables); + variables.remove("selected"); + } + + super.changeVariables(source, variables); + + // Client might update the pagelength if Table height is fixed + if (variables.containsKey("pagelength")) { + // Sets pageLength directly to avoid repaint that setter causes + pageLength = (Integer) variables.get("pagelength"); + } + + // Page start index + if (variables.containsKey("firstvisible")) { + final Integer value = (Integer) variables.get("firstvisible"); + if (value != null) { + setCurrentPageFirstItemIndex(value.intValue(), false); + } + } + + // Sets requested firstrow and rows for the next paint + if (variables.containsKey("reqfirstrow") + || variables.containsKey("reqrows")) { + + try { + firstToBeRenderedInClient = ((Integer) variables + .get("firstToBeRendered")).intValue(); + lastToBeRenderedInClient = ((Integer) variables + .get("lastToBeRendered")).intValue(); + } catch (Exception e) { + // FIXME: Handle exception + getLogger().log(Level.FINER, + "Could not parse the first and/or last rows.", e); + } + + // respect suggested rows only if table is not otherwise updated + // (row caches emptied by other event) + if (!containerChangeToBeRendered) { + Integer value = (Integer) variables.get("reqfirstrow"); + if (value != null) { + reqFirstRowToPaint = value.intValue(); + } + + value = (Integer) variables.get("reqrows"); + if (value != null) { + reqRowsToPaint = value.intValue(); + int size = size(); + // sanity check + + if (reqFirstRowToPaint >= size) { + reqFirstRowToPaint = size; + } + + if (reqFirstRowToPaint + reqRowsToPaint > size) { + reqRowsToPaint = size - reqFirstRowToPaint; + } + } + } + if (getLogger().isLoggable(Level.FINEST)) { + getLogger().log(Level.FINEST, "Client wants rows {0}-{1}", + new Object[] { reqFirstRowToPaint, + (reqFirstRowToPaint + reqRowsToPaint - 1) }); + } + clientNeedsContentRefresh = true; + } + + if (isSortEnabled()) { + // Sorting + boolean doSort = false; + if (variables.containsKey("sortcolumn")) { + final String colId = (String) variables.get("sortcolumn"); + if (colId != null && !"".equals(colId) + && !"null".equals(colId)) { + final Object id = columnIdMap.get(colId); + setSortContainerPropertyId(id, false); + doSort = true; + } + } + if (variables.containsKey("sortascending")) { + final boolean state = ((Boolean) variables.get("sortascending")) + .booleanValue(); + if (state != sortAscending) { + setSortAscending(state, false); + doSort = true; + } + } + if (doSort) { + this.sort(); + resetPageBuffer(); + } + } + + // Dynamic column hide/show and order + // Update visible columns + if (isColumnCollapsingAllowed()) { + if (variables.containsKey("collapsedcolumns")) { + try { + final Object[] ids = (Object[]) variables + .get("collapsedcolumns"); + Set idSet = new HashSet(); + for (Object id : ids) { + idSet.add(columnIdMap.get(id.toString())); + } + for (final Iterator it = visibleColumns + .iterator(); it.hasNext();) { + Object propertyId = it.next(); + if (isColumnCollapsed(propertyId)) { + if (!idSet.contains(propertyId)) { + setColumnCollapsed(propertyId, false); + } + } else if (idSet.contains(propertyId)) { + setColumnCollapsed(propertyId, true); + } + } + } catch (final Exception e) { + // FIXME: Handle exception + getLogger().log(Level.FINER, + "Could not determine column collapsing state", e); + } + clientNeedsContentRefresh = true; + } + } + if (isColumnReorderingAllowed()) { + if (variables.containsKey("columnorder")) { + try { + final Object[] ids = (Object[]) variables + .get("columnorder"); + // need a real Object[], ids can be a String[] + final Object[] idsTemp = new Object[ids.length]; + for (int i = 0; i < ids.length; i++) { + idsTemp[i] = columnIdMap.get(ids[i].toString()); + } + setColumnOrder(idsTemp); + if (hasListeners(ColumnReorderEvent.class)) { + fireEvent(new ColumnReorderEvent(this)); + } + } catch (final Exception e) { + // FIXME: Handle exception + getLogger().log(Level.FINER, + "Could not determine column reordering state", e); + } + clientNeedsContentRefresh = true; + } + } + + enableContentRefreshing(clientNeedsContentRefresh); + + // Actions + if (variables.containsKey("action")) { + final StringTokenizer st = new StringTokenizer( + (String) variables.get("action"), ","); + if (st.countTokens() == 2) { + final Object itemId = itemIdMapper.get(st.nextToken()); + final Action action = actionMapper.get(st.nextToken()); + + if (action != null && (itemId == null || containsId(itemId)) + && actionHandlers != null) { + for (Handler ah : actionHandlers) { + ah.handleAction(action, this, itemId); + } + } + } + } + + } + + /** + * Handles click event + * + * @param variables + */ + private void handleClickEvent(Map variables) { + + // Item click event + if (variables.containsKey("clickEvent")) { + String key = (String) variables.get("clickedKey"); + Object itemId = itemIdMapper.get(key); + Object propertyId = null; + String colkey = (String) variables.get("clickedColKey"); + // click is not necessary on a property + if (colkey != null) { + propertyId = columnIdMap.get(colkey); + } + MouseEventDetails evt = MouseEventDetails + .deSerialize((String) variables.get("clickEvent")); + Item item = getItem(itemId); + if (item != null) { + fireEvent(new ItemClickEvent(this, item, itemId, propertyId, + evt)); + } + } + + // Header click event + else if (variables.containsKey("headerClickEvent")) { + + MouseEventDetails details = MouseEventDetails + .deSerialize((String) variables.get("headerClickEvent")); + + Object cid = variables.get("headerClickCID"); + Object propertyId = null; + if (cid != null) { + propertyId = columnIdMap.get(cid.toString()); + } + fireEvent(new HeaderClickEvent(this, propertyId, details)); + } + + // Footer click event + else if (variables.containsKey("footerClickEvent")) { + MouseEventDetails details = MouseEventDetails + .deSerialize((String) variables.get("footerClickEvent")); + + Object cid = variables.get("footerClickCID"); + Object propertyId = null; + if (cid != null) { + propertyId = columnIdMap.get(cid.toString()); + } + fireEvent(new FooterClickEvent(this, propertyId, details)); + } + } + + /** + * Handles the column resize event sent by the client. + * + * @param variables + */ + private void handleColumnResizeEvent(Map variables) { + if (variables.containsKey("columnResizeEventColumn")) { + Object cid = variables.get("columnResizeEventColumn"); + Object propertyId = null; + if (cid != null) { + propertyId = columnIdMap.get(cid.toString()); + + Object prev = variables.get("columnResizeEventPrev"); + int previousWidth = -1; + if (prev != null) { + previousWidth = Integer.valueOf(prev.toString()); + } + + Object curr = variables.get("columnResizeEventCurr"); + int currentWidth = -1; + if (curr != null) { + currentWidth = Integer.valueOf(curr.toString()); + } + + fireColumnResizeEvent(propertyId, previousWidth, currentWidth); + } + } + } + + private void fireColumnCollapseEvent(Object propertyId) { + fireEvent(new ColumnCollapseEvent(this, propertyId)); + } + + private void fireColumnResizeEvent(Object propertyId, int previousWidth, + int currentWidth) { + /* + * Update the sizes on the server side. If a column previously had a + * expand ratio and the user resized the column then the expand ratio + * will be turned into a static pixel size. + */ + setColumnWidth(propertyId, currentWidth); + + fireEvent(new ColumnResizeEvent(this, propertyId, previousWidth, + currentWidth)); + } + + private void handleColumnWidthUpdates(Map variables) { + if (variables.containsKey("columnWidthUpdates")) { + String[] events = (String[]) variables.get("columnWidthUpdates"); + for (String str : events) { + String[] eventDetails = str.split(":"); + Object propertyId = columnIdMap.get(eventDetails[0]); + if (propertyId == null) { + propertyId = ROW_HEADER_FAKE_PROPERTY_ID; + } + int width = Integer.valueOf(eventDetails[1]); + setColumnWidth(propertyId, width); + } + } + } + + /** + * Go to mode where content updates are not done. This is due we want to + * bypass expensive content for some reason (like when we know we may have + * other content changes on their way). + * + * @return true if content refresh flag was enabled prior this call + */ + protected boolean disableContentRefreshing() { + boolean wasDisabled = isContentRefreshesEnabled; + isContentRefreshesEnabled = false; + return wasDisabled; + } + + /** + * Go to mode where content content refreshing has effect. + * + * @param refreshContent + * true if content refresh needs to be done + */ + protected void enableContentRefreshing(boolean refreshContent) { + isContentRefreshesEnabled = true; + if (refreshContent) { + refreshRenderedCells(); + // Ensure that client gets a response + markAsDirty(); + } + } + + @Override + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); + + // Ensure pageBuffer is filled before sending the response to avoid + // calls to markAsDirty during paint + getVisibleCells(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin. + * terminal.PaintTarget) + */ + + @Override + public void paintContent(PaintTarget target) throws PaintException { + isBeingPainted = true; + try { + doPaintContent(target); + } finally { + isBeingPainted = false; + } + } + + private void doPaintContent(PaintTarget target) throws PaintException { + /* + * Body actions - Actions which has the target null and can be invoked + * by right clicking on the table body. + */ + final Set actionSet = findAndPaintBodyActions(target); + + final Object[][] cells = getVisibleCells(); + int rows = findNumRowsToPaint(target, cells); + + int total = size(); + if (shouldHideNullSelectionItem()) { + total--; + rows--; + } + + // Table attributes + paintTableAttributes(target, rows, total); + + paintVisibleColumnOrder(target); + + // Rows + if (isPartialRowUpdate() && painted && !target.isFullRepaint()) { + paintPartialRowUpdate(target, actionSet); + } else if (target.isFullRepaint() || isRowCacheInvalidated()) { + paintRows(target, cells, actionSet); + setRowCacheInvalidated(false); + } + + /* + * Send the page buffer indexes to ensure that the client side stays in + * sync. Otherwise we _might_ have the situation where the client side + * discards too few or too many rows, causing out of sync issues. + */ + int pageBufferLastIndex = pageBufferFirstIndex + + pageBuffer[CELL_ITEMID].length - 1; + target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST, + pageBufferFirstIndex); + target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_LAST, + pageBufferLastIndex); + + paintSorting(target); + + resetVariablesAndPageBuffer(target); + + // Actions + paintActions(target, actionSet); + + paintColumnOrder(target); + + // Available columns + paintAvailableColumns(target); + + paintVisibleColumns(target); + + if (keyMapperReset) { + keyMapperReset = false; + target.addAttribute(TableConstants.ATTRIBUTE_KEY_MAPPER_RESET, + true); + } + + if (dropHandler != null) { + dropHandler.getAcceptCriterion().paint(target); + } + + painted = true; + } + + private void setRowCacheInvalidated(boolean invalidated) { + rowCacheInvalidated = invalidated; + } + + protected boolean isRowCacheInvalidated() { + return rowCacheInvalidated; + } + + private void paintPartialRowUpdate(PaintTarget target, + Set actionSet) throws PaintException { + paintPartialRowUpdates(target, actionSet); + paintPartialRowAdditions(target, actionSet); + } + + private void paintPartialRowUpdates(PaintTarget target, + Set actionSet) throws PaintException { + final boolean[] iscomponent = findCellsWithComponents(); + + int firstIx = getFirstUpdatedItemIndex(); + int count = getUpdatedRowCount(); + + target.startTag("urows"); + target.addAttribute("firsturowix", firstIx); + target.addAttribute("numurows", count); + + // Partial row updates bypass the normal caching mechanism. + Object[][] cells = getVisibleCellsUpdateCacheRows(firstIx, count); + for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) { + final Object itemId = cells[CELL_ITEMID][indexInRowbuffer]; + + if (shouldHideNullSelectionItem()) { + // Remove null selection item if null selection is not allowed + continue; + } + + paintRow(target, cells, isEditable(), actionSet, iscomponent, + indexInRowbuffer, itemId); + } + target.endTag("urows"); + maybeThrowCacheUpdateExceptions(); + } + + private void paintPartialRowAdditions(PaintTarget target, + Set actionSet) throws PaintException { + final boolean[] iscomponent = findCellsWithComponents(); + + int firstIx = getFirstAddedItemIndex(); + int count = getAddedRowCount(); + + target.startTag("prows"); + + if (!shouldHideAddedRows()) { + getLogger().log(Level.FINEST, + "Paint rows for add. Index: {0}, count: {1}.", + new Object[] { firstIx, count }); + + // Partial row additions bypass the normal caching mechanism. + Object[][] cells = getVisibleCellsInsertIntoCache(firstIx, count); + if (cells[0].length < count) { + // delete the rows below, since they will fall beyond the cache + // page. + target.addAttribute("delbelow", true); + count = cells[0].length; + } + + for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) { + final Object itemId = cells[CELL_ITEMID][indexInRowbuffer]; + if (shouldHideNullSelectionItem()) { + // Remove null selection item if null selection is not + // allowed + continue; + } + + paintRow(target, cells, isEditable(), actionSet, iscomponent, + indexInRowbuffer, itemId); + } + } else { + getLogger().log(Level.FINEST, + "Paint rows for remove. Index: {0}, count: {1}.", + new Object[] { firstIx, count }); + removeRowsFromCacheAndFillBottom(firstIx, count); + target.addAttribute("hide", true); + } + + target.addAttribute("firstprowix", firstIx); + target.addAttribute("numprows", count); + target.endTag("prows"); + maybeThrowCacheUpdateExceptions(); + } + + /** + * Subclass and override this to enable partial row updates and additions, + * which bypass the normal caching mechanism. This is useful for e.g. + * TreeTable. + * + * @return true if this update is a partial row update, false if not. For + * plain Table it is always false. + */ + protected boolean isPartialRowUpdate() { + return false; + } + + /** + * Subclass and override this to enable partial row additions, bypassing the + * normal caching mechanism. This is useful for e.g. TreeTable, where + * expanding a node should only fetch and add the items inside of that node. + * + * @return The index of the first added item. For plain Table it is always + * 0. + */ + protected int getFirstAddedItemIndex() { + return 0; + } + + /** + * Subclass and override this to enable partial row additions, bypassing the + * normal caching mechanism. This is useful for e.g. TreeTable, where + * expanding a node should only fetch and add the items inside of that node. + * + * @return the number of rows to be added, starting at the index returned by + * {@link #getFirstAddedItemIndex()}. For plain Table it is always + * 0. + */ + protected int getAddedRowCount() { + return 0; + } + + /** + * Subclass and override this to enable removing of rows, bypassing the + * normal caching and lazy loading mechanism. This is useful for e.g. + * TreeTable, when you need to hide certain rows as a node is collapsed. + * + * This should return true if the rows pointed to by + * {@link #getFirstAddedItemIndex()} and {@link #getAddedRowCount()} should + * be hidden instead of added. + * + * @return whether the rows to add (see {@link #getFirstAddedItemIndex()} + * and {@link #getAddedRowCount()}) should be added or hidden. For + * plain Table it is always false. + */ + protected boolean shouldHideAddedRows() { + return false; + } + + /** + * Subclass and override this to enable partial row updates, bypassing the + * normal caching and lazy loading mechanism. This is useful for updating + * the state of certain rows, e.g. in the TreeTable the collapsed state of a + * single node is updated using this mechanism. + * + * @return the index of the first item to be updated. For plain Table it is + * always 0. + */ + protected int getFirstUpdatedItemIndex() { + return 0; + } + + /** + * Subclass and override this to enable partial row updates, bypassing the + * normal caching and lazy loading mechanism. This is useful for updating + * the state of certain rows, e.g. in the TreeTable the collapsed state of a + * single node is updated using this mechanism. + * + * @return the number of rows to update, starting at the index returned by + * {@link #getFirstUpdatedItemIndex()}. For plain table it is always + * 0. + */ + protected int getUpdatedRowCount() { + return 0; + } + + private void paintTableAttributes(PaintTarget target, int rows, int total) + throws PaintException { + paintTabIndex(target); + paintDragMode(target); + paintSelectMode(target); + paintTableChildLayoutMeasureMode(target); + + if (cacheRate != CACHE_RATE_DEFAULT) { + target.addAttribute("cr", cacheRate); + } + + target.addAttribute("cols", getVisibleColumns().length); + target.addAttribute("rows", rows); + + target.addAttribute("firstrow", (reqFirstRowToPaint >= 0 + ? reqFirstRowToPaint : firstToBeRenderedInClient)); + target.addAttribute("totalrows", total); + if (getPageLength() != 0) { + target.addAttribute("pagelength", getPageLength()); + } + if (areColumnHeadersEnabled()) { + target.addAttribute("colheaders", true); + } + if (rowHeadersAreEnabled()) { + target.addAttribute("rowheaders", true); + } + + target.addAttribute("colfooters", columnFootersVisible); + + // The cursors are only shown on pageable table + if (getCurrentPageFirstItemIndex() != 0 || getPageLength() > 0) { + target.addVariable(this, "firstvisible", + getCurrentPageFirstItemIndex()); + target.addVariable(this, "firstvisibleonlastpage", + currentPageFirstItemIndexOnLastPage); + } + } + + /** + * Resets and paints "to be painted next" variables. Also reset pageBuffer + */ + private void resetVariablesAndPageBuffer(PaintTarget target) + throws PaintException { + reqFirstRowToPaint = -1; + reqRowsToPaint = -1; + containerChangeToBeRendered = false; + target.addVariable(this, "reqrows", reqRowsToPaint); + target.addVariable(this, "reqfirstrow", reqFirstRowToPaint); + } + + private boolean areColumnHeadersEnabled() { + return getColumnHeaderMode() != ColumnHeaderMode.HIDDEN; + } + + private void paintVisibleColumns(PaintTarget target) throws PaintException { + target.startTag("visiblecolumns"); + if (rowHeadersAreEnabled()) { + target.startTag("column"); + target.addAttribute("cid", ROW_HEADER_COLUMN_KEY); + paintColumnWidth(target, ROW_HEADER_FAKE_PROPERTY_ID); + paintColumnExpandRatio(target, ROW_HEADER_FAKE_PROPERTY_ID); + target.endTag("column"); + } + final Collection sortables = getSortableContainerPropertyIds(); + for (Object colId : visibleColumns) { + if (colId != null) { + target.startTag("column"); + target.addAttribute("cid", columnIdMap.key(colId)); + final String head = getColumnHeader(colId); + target.addAttribute("caption", (head != null ? head : "")); + final String foot = getColumnFooter(colId); + target.addAttribute("fcaption", (foot != null ? foot : "")); + if (isColumnCollapsed(colId)) { + target.addAttribute("collapsed", true); + } + if (areColumnHeadersEnabled()) { + if (getColumnIcon(colId) != null) { + target.addAttribute("icon", getColumnIcon(colId)); + } + if (sortables.contains(colId)) { + target.addAttribute("sortable", true); + } + } + if (!Align.LEFT.equals(getColumnAlignment(colId))) { + target.addAttribute("align", + getColumnAlignment(colId).toString()); + } + paintColumnWidth(target, colId); + paintColumnExpandRatio(target, colId); + target.endTag("column"); + } + } + target.endTag("visiblecolumns"); + } + + private void paintAvailableColumns(PaintTarget target) + throws PaintException { + if (columnCollapsingAllowed) { + final HashSet collapsedCols = new HashSet(); + for (Object colId : visibleColumns) { + if (isColumnCollapsed(colId)) { + collapsedCols.add(colId); + } + } + final String[] collapsedKeys = new String[collapsedCols.size()]; + int nextColumn = 0; + for (Object colId : visibleColumns) { + if (isColumnCollapsed(colId)) { + collapsedKeys[nextColumn++] = columnIdMap.key(colId); + } + } + target.addVariable(this, "collapsedcolumns", collapsedKeys); + + final String[] noncollapsibleKeys = new String[noncollapsibleColumns + .size()]; + nextColumn = 0; + for (Object colId : noncollapsibleColumns) { + noncollapsibleKeys[nextColumn++] = columnIdMap.key(colId); + } + target.addVariable(this, "noncollapsiblecolumns", + noncollapsibleKeys); + } + + } + + private void paintActions(PaintTarget target, final Set actionSet) + throws PaintException { + if (!actionSet.isEmpty()) { + target.addVariable(this, "action", ""); + target.startTag("actions"); + for (Action a : actionSet) { + target.startTag("action"); + if (a.getCaption() != null) { + target.addAttribute("caption", a.getCaption()); + } + if (a.getIcon() != null) { + target.addAttribute("icon", a.getIcon()); + } + target.addAttribute("key", actionMapper.key(a)); + target.endTag("action"); + } + target.endTag("actions"); + } + } + + private void paintColumnOrder(PaintTarget target) throws PaintException { + if (columnReorderingAllowed) { + final String[] colorder = new String[visibleColumns.size()]; + int i = 0; + for (Object colId : visibleColumns) { + colorder[i++] = columnIdMap.key(colId); + } + target.addVariable(this, "columnorder", colorder); + } + } + + private void paintSorting(PaintTarget target) throws PaintException { + // Sorting + if (getContainerDataSource() instanceof Container.Sortable) { + target.addVariable(this, "sortcolumn", + columnIdMap.key(sortContainerPropertyId)); + target.addVariable(this, "sortascending", sortAscending); + } + } + + private void paintRows(PaintTarget target, final Object[][] cells, + final Set actionSet) throws PaintException { + final boolean[] iscomponent = findCellsWithComponents(); + + target.startTag("rows"); + // cells array contains all that are supposed to be visible on client, + // but we'll start from the one requested by client + int start = 0; + if (reqFirstRowToPaint != -1 && firstToBeRenderedInClient != -1) { + start = reqFirstRowToPaint - firstToBeRenderedInClient; + } + int end = cells[0].length; + if (reqRowsToPaint != -1) { + end = start + reqRowsToPaint; + } + // sanity check + if (lastToBeRenderedInClient != -1 && lastToBeRenderedInClient < end) { + end = lastToBeRenderedInClient + 1; + } + if (start > cells[CELL_ITEMID].length || start < 0) { + start = 0; + } + if (end > cells[CELL_ITEMID].length) { + end = cells[CELL_ITEMID].length; + } + + for (int indexInRowbuffer = start; indexInRowbuffer < end; indexInRowbuffer++) { + final Object itemId = cells[CELL_ITEMID][indexInRowbuffer]; + + if (shouldHideNullSelectionItem()) { + // Remove null selection item if null selection is not allowed + continue; + } + + paintRow(target, cells, isEditable(), actionSet, iscomponent, + indexInRowbuffer, itemId); + } + target.endTag("rows"); + } + + private boolean[] findCellsWithComponents() { + final boolean[] isComponent = new boolean[visibleColumns.size()]; + int ix = 0; + for (Object columnId : visibleColumns) { + if (columnGenerators.containsKey(columnId)) { + isComponent[ix++] = true; + } else { + final Class colType = getType(columnId); + isComponent[ix++] = colType != null + && Component.class.isAssignableFrom(colType); + } + } + return isComponent; + } + + private void paintVisibleColumnOrder(PaintTarget target) { + // Visible column order + final ArrayList visibleColOrder = new ArrayList(); + for (Object columnId : visibleColumns) { + if (!isColumnCollapsed(columnId)) { + visibleColOrder.add(columnIdMap.key(columnId)); + } + } + target.addAttribute("vcolorder", visibleColOrder.toArray()); + } + + private Set findAndPaintBodyActions(PaintTarget target) { + Set actionSet = new LinkedHashSet(); + if (actionHandlers != null) { + final ArrayList keys = new ArrayList(); + for (Handler ah : actionHandlers) { + // Getting actions for the null item, which in this case means + // the body item + final Action[] actions = ah.getActions(null, this); + if (actions != null) { + for (Action action : actions) { + actionSet.add(action); + keys.add(actionMapper.key(action)); + } + } + } + target.addAttribute("alb", keys.toArray()); + } + return actionSet; + } + + private boolean shouldHideNullSelectionItem() { + return !isNullSelectionAllowed() && getNullSelectionItemId() != null + && containsId(getNullSelectionItemId()); + } + + private int findNumRowsToPaint(PaintTarget target, final Object[][] cells) + throws PaintException { + int rows; + if (reqRowsToPaint >= 0) { + rows = reqRowsToPaint; + } else { + rows = cells[0].length; + if (alwaysRecalculateColumnWidths) { + // TODO experimental feature for now: tell the client to + // recalculate column widths. + // We'll only do this for paints that do not originate from + // table scroll/cache requests (i.e when reqRowsToPaint<0) + target.addAttribute("recalcWidths", true); + } + } + return rows; + } + + private void paintSelectMode(PaintTarget target) throws PaintException { + if (multiSelectMode != MultiSelectMode.DEFAULT) { + target.addAttribute("multiselectmode", multiSelectMode.ordinal()); + } + if (isSelectable()) { + target.addAttribute("selectmode", + (isMultiSelect() ? "multi" : "single")); + } else { + target.addAttribute("selectmode", "none"); + } + if (!isNullSelectionAllowed()) { + target.addAttribute("nsa", false); + } + + // selection support + // The select variable is only enabled if selectable + if (isSelectable()) { + target.addVariable(this, "selected", findSelectedKeys()); + } + } + + private String[] findSelectedKeys() { + LinkedList selectedKeys = new LinkedList(); + if (isMultiSelect()) { + HashSet sel = new HashSet((Set) getValue()); + Collection vids = getVisibleItemIds(); + for (Iterator it = vids.iterator(); it.hasNext();) { + Object id = it.next(); + if (sel.contains(id)) { + selectedKeys.add(itemIdMapper.key(id)); + } + } + } else { + Object value = getValue(); + if (value == null) { + value = getNullSelectionItemId(); + } + if (value != null) { + selectedKeys.add(itemIdMapper.key(value)); + } + } + return selectedKeys.toArray(new String[selectedKeys.size()]); + } + + private void paintDragMode(PaintTarget target) throws PaintException { + if (dragMode != TableDragMode.NONE) { + target.addAttribute("dragmode", dragMode.ordinal()); + } + } + + private void paintTabIndex(PaintTarget target) throws PaintException { + // The tab ordering number + if (getTabIndex() > 0) { + target.addAttribute("tabindex", getTabIndex()); + } + } + + private void paintColumnWidth(PaintTarget target, final Object columnId) + throws PaintException { + if (columnWidths.containsKey(columnId)) { + target.addAttribute("width", getColumnWidth(columnId)); + } + } + + private void paintColumnExpandRatio(PaintTarget target, + final Object columnId) throws PaintException { + if (columnExpandRatios.containsKey(columnId)) { + target.addAttribute("er", getColumnExpandRatio(columnId)); + } + } + + private void paintTableChildLayoutMeasureMode(PaintTarget target) + throws PaintException { + target.addAttribute("measurehint", getChildMeasurementHint().ordinal()); + } + + /** + * Checks whether row headers are visible. + * + * @return {@code false} if row headers are hidden, {@code true} otherwise + * @since 7.3.9 + */ + protected boolean rowHeadersAreEnabled() { + return getRowHeaderMode() != RowHeaderMode.HIDDEN; + } + + private void paintRow(PaintTarget target, final Object[][] cells, + final boolean iseditable, final Set actionSet, + final boolean[] iscomponent, int indexInRowbuffer, + final Object itemId) throws PaintException { + target.startTag("tr"); + + paintRowAttributes(target, cells, actionSet, indexInRowbuffer, itemId); + + // cells + int currentColumn = 0; + for (final Iterator it = visibleColumns.iterator(); it + .hasNext(); currentColumn++) { + final Object columnId = it.next(); + if (columnId == null || isColumnCollapsed(columnId)) { + continue; + } + /* + * For each cell, if a cellStyleGenerator is specified, get the + * specific style for the cell. If there is any, add it to the + * target. + */ + if (cellStyleGenerator != null) { + String cellStyle = cellStyleGenerator.getStyle(this, itemId, + columnId); + if (cellStyle != null && !cellStyle.equals("")) { + target.addAttribute("style-" + columnIdMap.key(columnId), + cellStyle); + } + } + + if ((iscomponent[currentColumn] || iseditable + || cells[CELL_GENERATED_ROW][indexInRowbuffer] != null) + && Component.class.isInstance(cells[CELL_FIRSTCOL + + currentColumn][indexInRowbuffer])) { + final Component c = (Component) cells[CELL_FIRSTCOL + + currentColumn][indexInRowbuffer]; + if (c == null || !LegacyCommunicationManager + .isComponentVisibleToClient(c)) { + target.addText(""); + } else { + LegacyPaint.paint(c, target); + } + } else { + target.addText((String) cells[CELL_FIRSTCOL + + currentColumn][indexInRowbuffer]); + } + paintCellTooltips(target, itemId, columnId); + } + + target.endTag("tr"); + } + + private void paintCellTooltips(PaintTarget target, Object itemId, + Object columnId) throws PaintException { + if (itemDescriptionGenerator != null) { + String itemDescription = itemDescriptionGenerator + .generateDescription(this, itemId, columnId); + if (itemDescription != null && !itemDescription.equals("")) { + target.addAttribute("descr-" + columnIdMap.key(columnId), + itemDescription); + } + } + } + + private void paintRowTooltips(PaintTarget target, Object itemId) + throws PaintException { + if (itemDescriptionGenerator != null) { + String rowDescription = itemDescriptionGenerator + .generateDescription(this, itemId, null); + if (rowDescription != null && !rowDescription.equals("")) { + target.addAttribute("rowdescr", rowDescription); + } + } + } + + private void paintRowAttributes(PaintTarget target, final Object[][] cells, + final Set actionSet, int indexInRowbuffer, + final Object itemId) throws PaintException { + // tr attributes + + paintRowIcon(target, cells, indexInRowbuffer); + paintRowHeader(target, cells, indexInRowbuffer); + paintGeneratedRowInfo(target, cells, indexInRowbuffer); + target.addAttribute("key", + Integer.parseInt(cells[CELL_KEY][indexInRowbuffer].toString())); + + if (isSelected(itemId)) { + target.addAttribute("selected", true); + } + + // Actions + if (actionHandlers != null) { + final ArrayList keys = new ArrayList(); + for (Handler ah : actionHandlers) { + final Action[] aa = ah.getActions(itemId, this); + if (aa != null) { + for (int ai = 0; ai < aa.length; ai++) { + final String key = actionMapper.key(aa[ai]); + actionSet.add(aa[ai]); + keys.add(key); + } + } + } + target.addAttribute("al", keys.toArray()); + } + + /* + * For each row, if a cellStyleGenerator is specified, get the specific + * style for the cell, using null as propertyId. If there is any, add it + * to the target. + */ + if (cellStyleGenerator != null) { + String rowStyle = cellStyleGenerator.getStyle(this, itemId, null); + if (rowStyle != null && !rowStyle.equals("")) { + target.addAttribute("rowstyle", rowStyle); + } + } + + paintRowTooltips(target, itemId); + + paintRowAttributes(target, itemId); + } + + private void paintGeneratedRowInfo(PaintTarget target, Object[][] cells, + int indexInRowBuffer) throws PaintException { + GeneratedRow generatedRow = (GeneratedRow) cells[CELL_GENERATED_ROW][indexInRowBuffer]; + if (generatedRow != null) { + target.addAttribute("gen_html", + generatedRow.isHtmlContentAllowed()); + target.addAttribute("gen_span", generatedRow.isSpanColumns()); + target.addAttribute("gen_widget", + generatedRow.getValue() instanceof Component); + } + } + + protected void paintRowHeader(PaintTarget target, Object[][] cells, + int indexInRowbuffer) throws PaintException { + if (rowHeadersAreEnabled()) { + if (cells[CELL_HEADER][indexInRowbuffer] != null) { + target.addAttribute("caption", + (String) cells[CELL_HEADER][indexInRowbuffer]); + } + } + + } + + protected void paintRowIcon(PaintTarget target, final Object[][] cells, + int indexInRowbuffer) throws PaintException { + if (rowHeadersAreEnabled() + && cells[CELL_ICON][indexInRowbuffer] != null) { + target.addAttribute("icon", + (Resource) cells[CELL_ICON][indexInRowbuffer]); + } + } + + /** + * A method where extended Table implementations may add their custom + * attributes for rows. + * + * @param target + * @param itemId + */ + protected void paintRowAttributes(PaintTarget target, Object itemId) + throws PaintException { + + } + + /** + * Gets the cached visible table contents. + * + * @return the cached visible table contents. + */ + private Object[][] getVisibleCells() { + if (pageBuffer == null) { + refreshRenderedCells(); + } + return pageBuffer; + } + + /** + * Gets the value of property. + * + * By default if the table is editable the fieldFactory is used to create + * editors for table cells. Otherwise formatPropertyValue is used to format + * the value representation. + * + * @param rowId + * the Id of the row (same as item Id). + * @param colId + * the Id of the column. + * @param property + * the Property to be presented. + * @return Object Either formatted value or Component for field. + * @see #setTableFieldFactory(TableFieldFactory) + */ + protected Object getPropertyValue(Object rowId, Object colId, + Property property) { + if (isEditable() && fieldFactory != null) { + final Field f = fieldFactory + .createField(getContainerDataSource(), rowId, colId, this); + if (f != null) { + // Remember that we have made this association so we can remove + // it when the component is removed + associatedProperties.put(f, property); + bindPropertyToField(rowId, colId, property, f); + return f; + } + } + + return formatPropertyValue(rowId, colId, property); + } + + /** + * Binds an item property to a field generated by TableFieldFactory. The + * default behavior is to bind property straight to LegacyField. If + * Property.Viewer type property (e.g. PropertyFormatter) is already set for + * field, the property is bound to that Property.Viewer. + * + * @param rowId + * @param colId + * @param property + * @param field + * @since 6.7.3 + */ + protected void bindPropertyToField(Object rowId, Object colId, + Property property, Field field) { + // check if field has a property that is Viewer set. In that case we + // expect developer has e.g. PropertyFormatter that he wishes to use and + // assign the property to the Viewer instead. + boolean hasFilterProperty = field.getPropertyDataSource() != null + && (field.getPropertyDataSource() instanceof Property.Viewer); + if (hasFilterProperty) { + ((Property.Viewer) field.getPropertyDataSource()) + .setPropertyDataSource(property); + } else { + field.setPropertyDataSource(property); + } + } + + /** + * Formats table cell property values. By default the property.toString() + * and return a empty string for null properties. + * + * @param rowId + * the Id of the row (same as item Id). + * @param colId + * the Id of the column. + * @param property + * the Property to be formatted. + * @return the String representation of property and its value. + * @since 3.1 + */ + protected String formatPropertyValue(Object rowId, Object colId, + Property property) { + if (property == null) { + return ""; + } + Converter converter = null; + + if (hasConverter(colId)) { + converter = getConverter(colId); + } else { + converter = (Converter) ConverterUtil.getConverter( + String.class, property.getType(), getSession()); + } + Object value = property.getValue(); + if (converter != null) { + return converter.convertToPresentation(value, String.class, + getLocale()); + } + return (null != value) ? value.toString() : ""; + } + + /* Action container */ + + /** + * Registers a new action handler for this container + * + * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler) + */ + + @Override + public void addActionHandler(Action.Handler actionHandler) { + + if (actionHandler != null) { + + if (actionHandlers == null) { + actionHandlers = new LinkedList(); + actionMapper = new KeyMapper(); + } + + if (!actionHandlers.contains(actionHandler)) { + actionHandlers.add(actionHandler); + // Assures the visual refresh. No need to reset the page buffer + // before as the content has not changed, only the action + // handlers. + refreshRenderedCells(); + } + + } + } + + /** + * Removes a previously registered action handler for the contents of this + * container. + * + * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler) + */ + + @Override + public void removeActionHandler(Action.Handler actionHandler) { + + if (actionHandlers != null && actionHandlers.contains(actionHandler)) { + + actionHandlers.remove(actionHandler); + + if (actionHandlers.isEmpty()) { + actionHandlers = null; + actionMapper = null; + } + + // Assures the visual refresh. No need to reset the page buffer + // before as the content has not changed, only the action + // handlers. + refreshRenderedCells(); + } + } + + /** + * Removes all action handlers + */ + public void removeAllActionHandlers() { + actionHandlers = null; + actionMapper = null; + // Assures the visual refresh. No need to reset the page buffer + // before as the content has not changed, only the action + // handlers. + refreshRenderedCells(); + } + + /* Property value change listening support */ + + /** + * Notifies this listener that the Property's value has changed. + * + * Also listens changes in rendered items to refresh content area. + * + * @see com.vaadin.v7.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent) + */ + + @Override + public void valueChange(Property.ValueChangeEvent event) { + if (equals(event.getProperty()) + || event.getProperty() == getPropertyDataSource()) { + super.valueChange(event); + } else { + refreshRowCache(); + containerChangeToBeRendered = true; + } + markAsDirty(); + } + + /** + * Clears the current page buffer. Call this before + * {@link #refreshRenderedCells()} to ensure that all content is updated + * from the properties. + */ + protected void resetPageBuffer() { + firstToBeRenderedInClient = -1; + lastToBeRenderedInClient = -1; + reqFirstRowToPaint = -1; + reqRowsToPaint = -1; + pageBuffer = null; + } + + /** + * Notifies the component that it is connected to an application. + * + * @see com.vaadin.ui.Component#attach() + */ + + @Override + public void attach() { + super.attach(); + + refreshRenderedCells(); + } + + /** + * Notifies the component that it is detached from the application + * + * @see com.vaadin.ui.Component#detach() + */ + + @Override + public void detach() { + super.detach(); + } + + /** + * Removes all Items from the Container. + * + * @see com.vaadin.v7.data.Container#removeAllItems() + */ + + @Override + public boolean removeAllItems() { + currentPageFirstItemId = null; + currentPageFirstItemIndex = 0; + return super.removeAllItems(); + } + + /** + * Removes the Item identified by ItemId from the Container. + * + * @see com.vaadin.v7.data.Container#removeItem(Object) + */ + + @Override + public boolean removeItem(Object itemId) { + final Object nextItemId = nextItemId(itemId); + final boolean ret = super.removeItem(itemId); + if (ret && (itemId != null) + && (itemId.equals(currentPageFirstItemId))) { + currentPageFirstItemId = nextItemId; + } + if (!(items instanceof Container.ItemSetChangeNotifier)) { + refreshRowCache(); + } + return ret; + } + + /** + * Removes a Property specified by the given Property ID from the Container. + * + * @see com.vaadin.v7.data.Container#removeContainerProperty(Object) + */ + + @Override + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + + // If a visible property is removed, remove the corresponding column + visibleColumns.remove(propertyId); + columnAlignments.remove(propertyId); + columnIcons.remove(propertyId); + columnHeaders.remove(propertyId); + columnFooters.remove(propertyId); + // If a propertyValueConverter was defined for the property, remove it. + propertyValueConverters.remove(propertyId); + + return super.removeContainerProperty(propertyId); + } + + /** + * Adds a new property to the table and show it as a visible column. + * + * @param propertyId + * the Id of the property. + * @param type + * the class of the property. + * @param defaultValue + * the default value given for all existing items. + * @see com.vaadin.v7.data.Container#addContainerProperty(Object, Class, + * Object) + */ + + @Override + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + + boolean visibleColAdded = false; + if (!visibleColumns.contains(propertyId)) { + visibleColumns.add(propertyId); + visibleColAdded = true; + } + + if (!super.addContainerProperty(propertyId, type, defaultValue)) { + if (visibleColAdded) { + visibleColumns.remove(propertyId); + } + return false; + } + if (!(items instanceof Container.PropertySetChangeNotifier)) { + refreshRowCache(); + } + return true; + } + + /** + * Adds a new property to the table and show it as a visible column. + * + * @param propertyId + * the Id of the property + * @param type + * the class of the property + * @param defaultValue + * the default value given for all existing items + * @param columnHeader + * the Explicit header of the column. If explicit header is not + * needed, this should be set null. + * @param columnIcon + * the Icon of the column. If icon is not needed, this should be + * set null. + * @param columnAlignment + * the Alignment of the column. Null implies align left. + * @throws UnsupportedOperationException + * if the operation is not supported. + * @see com.vaadin.v7.data.Container#addContainerProperty(Object, Class, + * Object) + */ + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue, String columnHeader, Resource columnIcon, + Align columnAlignment) throws UnsupportedOperationException { + if (!this.addContainerProperty(propertyId, type, defaultValue)) { + return false; + } + setColumnAlignment(propertyId, columnAlignment); + setColumnHeader(propertyId, columnHeader); + setColumnIcon(propertyId, columnIcon); + return true; + } + + /** + * Adds a generated column to the Table. + *

    + * A generated column is a column that exists only in the Table, not as a + * property in the underlying Container. It shows up just as a regular + * column. + *

    + *

    + * A generated column will override a property with the same id, so that the + * generated column is shown instead of the column representing the + * property. Note that getContainerProperty() will still get the real + * property. + *

    + *

    + * Table will not listen to value change events from properties overridden + * by generated columns. If the content of your generated column depends on + * properties that are not directly visible in the table, attach value + * change listener to update the content on all depended properties. + * Otherwise your UI might not get updated as expected. + *

    + *

    + * Also note that getVisibleColumns() will return the generated columns, + * while getContainerPropertyIds() will not. + *

    + * + * @param id + * the id of the column to be added + * @param generatedColumn + * the {@link ColumnGenerator} to use for this column + */ + public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) { + if (generatedColumn == null) { + throw new IllegalArgumentException( + "Can not add null as a GeneratedColumn"); + } + if (columnGenerators.containsKey(id)) { + throw new IllegalArgumentException( + "Can not add the same GeneratedColumn twice, id:" + id); + } else { + columnGenerators.put(id, generatedColumn); + /* + * add to visible column list unless already there (overriding + * column from DS) + */ + if (!visibleColumns.contains(id)) { + visibleColumns.add(id); + } + refreshRowCache(); + } + } + + /** + * Returns the ColumnGenerator used to generate the given column. + * + * @param columnId + * The id of the generated column + * @return The ColumnGenerator used for the given columnId or null. + */ + public ColumnGenerator getColumnGenerator(Object columnId) + throws IllegalArgumentException { + return columnGenerators.get(columnId); + } + + /** + * Removes a generated column previously added with addGeneratedColumn. + * + * @param columnId + * id of the generated column to remove + * @return true if the column could be removed (existed in the Table) + */ + public boolean removeGeneratedColumn(Object columnId) { + if (columnGenerators.containsKey(columnId)) { + columnGenerators.remove(columnId); + // remove column from visibleColumns list unless it exists in + // container (generator previously overrode this column) + if (!items.getContainerPropertyIds().contains(columnId)) { + visibleColumns.remove(columnId); + } + refreshRowCache(); + return true; + } else { + return false; + } + } + + /** + * Returns item identifiers of the items which are currently rendered on the + * client. + *

    + * Note, that some due to historical reasons the name of the method is bit + * misleading. Some items may be partly or totally out of the viewport of + * the table's scrollable area. Actually detecting rows which can be + * actually seen by the end user may be problematic due to the client server + * architecture. Using {@link #getCurrentPageFirstItemId()} combined with + * {@link #getPageLength()} may produce good enough estimates in some + * situations. + * + * @see com.vaadin.v7.ui.Select#getVisibleItemIds() + */ + + @Override + public Collection getVisibleItemIds() { + + final LinkedList visible = new LinkedList(); + + final Object[][] cells = getVisibleCells(); + // may be null if the table has not been rendered yet (e.g. not attached + // to a layout) + if (null != cells) { + for (int i = 0; i < cells[CELL_ITEMID].length; i++) { + visible.add(cells[CELL_ITEMID][i]); + } + } + + return visible; + } + + /** + * Container datasource item set change. Table must flush its buffers on + * change. + * + * @see com.vaadin.v7.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.v7.data.Container.ItemSetChangeEvent) + */ + + @Override + public void containerItemSetChange(Container.ItemSetChangeEvent event) { + if (isBeingPainted) { + return; + } + + super.containerItemSetChange(event); + + // super method clears the key map, must inform client about this to + // avoid getting invalid keys back (#8584) + keyMapperReset = true; + + int currentFirstItemIndex = getCurrentPageFirstItemIndex(); + + if (event.getContainer().size() == 0) { + repairOnReAddAllRowsDataScrollPositionItemIndex = getCurrentPageFirstItemIndex(); + } else { + if (repairOnReAddAllRowsDataScrollPositionItemIndex != -1) { + currentFirstItemIndex = repairOnReAddAllRowsDataScrollPositionItemIndex; + /* + * Reset repairOnReAddAllRowsDataScrollPositionItemIndex. + * + * Next string should be commented (removed) if we want to have + * possibility to restore scroll position during adding items to + * container one by one via add() but not only addAll(). The + * problem in this case: we cannot track what happened between + * add() and add()... So it is ambiguous where to stop restore + * scroll position. + */ + repairOnReAddAllRowsDataScrollPositionItemIndex = -1; + } + } + + // ensure that page still has first item in page, ignore buffer refresh + // (forced in this method) + setCurrentPageFirstItemIndex(currentFirstItemIndex, false); + refreshRowCache(); + } + + /** + * Container datasource property set change. Table must flush its buffers on + * change. + * + * @see com.vaadin.v7.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.v7.data.Container.PropertySetChangeEvent) + */ + + @Override + public void containerPropertySetChange( + Container.PropertySetChangeEvent event) { + if (isBeingPainted) { + return; + } + + disableContentRefreshing(); + super.containerPropertySetChange(event); + + // sanitize visibleColumns. note that we are not adding previously + // non-existing properties as columns + Collection containerPropertyIds = getContainerDataSource() + .getContainerPropertyIds(); + + LinkedList newVisibleColumns = new LinkedList( + visibleColumns); + for (Iterator iterator = newVisibleColumns.iterator(); iterator + .hasNext();) { + Object id = iterator.next(); + if (!(containerPropertyIds.contains(id) + || columnGenerators.containsKey(id))) { + iterator.remove(); + } + } + setVisibleColumns(newVisibleColumns.toArray()); + // same for collapsed columns + for (Iterator iterator = collapsedColumns.iterator(); iterator + .hasNext();) { + Object id = iterator.next(); + if (!(containerPropertyIds.contains(id) + || columnGenerators.containsKey(id))) { + iterator.remove(); + } + } + + resetPageBuffer(); + enableContentRefreshing(true); + } + + /** + * Adding new items is not supported. + * + * @throws UnsupportedOperationException + * if set to true. + * @see com.vaadin.v7.ui.Select#setNewItemsAllowed(boolean) + */ + + @Override + public void setNewItemsAllowed(boolean allowNewOptions) + throws UnsupportedOperationException { + if (allowNewOptions) { + throw new UnsupportedOperationException(); + } + } + + /** + * Gets the ID of the Item following the Item that corresponds to itemId. + * + * @see com.vaadin.v7.data.Container.Ordered#nextItemId(java.lang.Object) + */ + + @Override + public Object nextItemId(Object itemId) { + return ((Container.Ordered) items).nextItemId(itemId); + } + + /** + * Gets the ID of the Item preceding the Item that corresponds to the + * itemId. + * + * @see com.vaadin.v7.data.Container.Ordered#prevItemId(java.lang.Object) + */ + + @Override + public Object prevItemId(Object itemId) { + return ((Container.Ordered) items).prevItemId(itemId); + } + + /** + * Gets the ID of the first Item in the Container. + * + * @see com.vaadin.v7.data.Container.Ordered#firstItemId() + */ + + @Override + public Object firstItemId() { + return ((Container.Ordered) items).firstItemId(); + } + + /** + * Gets the ID of the last Item in the Container. + * + * @see com.vaadin.v7.data.Container.Ordered#lastItemId() + */ + + @Override + public Object lastItemId() { + return ((Container.Ordered) items).lastItemId(); + } + + /** + * Tests if the Item corresponding to the given Item ID is the first Item in + * the Container. + * + * @see com.vaadin.v7.data.Container.Ordered#isFirstId(java.lang.Object) + */ + + @Override + public boolean isFirstId(Object itemId) { + return ((Container.Ordered) items).isFirstId(itemId); + } + + /** + * Tests if the Item corresponding to the given Item ID is the last Item in + * the Container. + * + * @see com.vaadin.v7.data.Container.Ordered#isLastId(java.lang.Object) + */ + + @Override + public boolean isLastId(Object itemId) { + return ((Container.Ordered) items).isLastId(itemId); + } + + /** + * Adds new item after the given item. + * + * @see com.vaadin.v7.data.Container.Ordered#addItemAfter(java.lang.Object) + */ + + @Override + public Object addItemAfter(Object previousItemId) + throws UnsupportedOperationException { + Object itemId = ((Container.Ordered) items) + .addItemAfter(previousItemId); + if (!(items instanceof Container.ItemSetChangeNotifier)) { + refreshRowCache(); + } + return itemId; + } + + /** + * Adds new item after the given item. + * + * @see com.vaadin.v7.data.Container.Ordered#addItemAfter(java.lang.Object, + * java.lang.Object) + */ + + @Override + public Item addItemAfter(Object previousItemId, Object newItemId) + throws UnsupportedOperationException { + Item item = ((Container.Ordered) items).addItemAfter(previousItemId, + newItemId); + if (!(items instanceof Container.ItemSetChangeNotifier)) { + refreshRowCache(); + } + return item; + } + + /** + * Sets the TableFieldFactory that is used to create editor for table cells. + * + * The TableFieldFactory is only used if the Table is editable. By default + * the DefaultFieldFactory is used. + * + * @param fieldFactory + * the field factory to set. + * @see #isEditable + * @see DefaultFieldFactory + */ + public void setTableFieldFactory(TableFieldFactory fieldFactory) { + this.fieldFactory = fieldFactory; + + // Assure visual refresh + refreshRowCache(); + } + + /** + * Gets the TableFieldFactory that is used to create editor for table cells. + * + * The FieldFactory is only used if the Table is editable. + * + * @return TableFieldFactory used to create the LegacyField instances. + * @see #isEditable + */ + public TableFieldFactory getTableFieldFactory() { + return fieldFactory; + } + + /** + * Is table editable. + * + * If table is editable a editor of type LegacyField is created for each + * table cell. The assigned FieldFactory is used to create the instances. + * + * To provide custom editors for table cells create a class implementing the + * FieldFactory interface, and assign it to table, and set the editable + * property to true. + * + * @return true if table is editable, false otherwise. + * @see Field + * @see FieldFactory + * + */ + public boolean isEditable() { + return editable; + } + + /** + * Sets the editable property. + * + * If table is editable a editor of type LegacyField is created for each + * table cell. The assigned FieldFactory is used to create the instances. + * + * To provide custom editors for table cells create a class implementing the + * FieldFactory interface, and assign it to table, and set the editable + * property to true. + * + * @param editable + * true if table should be editable by user. + * @see Field + * @see FieldFactory + * + */ + public void setEditable(boolean editable) { + this.editable = editable; + + // Assure visual refresh + refreshRowCache(); + } + + /** + * Sorts the table. + * + * @throws UnsupportedOperationException + * if the container data source does not implement + * Container.Sortable + * @see com.vaadin.v7.data.Container.Sortable#sort(java.lang.Object[], + * boolean[]) + * + */ + + @Override + public void sort(Object[] propertyId, boolean[] ascending) + throws UnsupportedOperationException { + final Container c = getContainerDataSource(); + if (c instanceof Container.Sortable) { + final int pageIndex = getCurrentPageFirstItemIndex(); + boolean refreshingPreviouslyEnabled = disableContentRefreshing(); + ((Container.Sortable) c).sort(propertyId, ascending); + setCurrentPageFirstItemIndex(pageIndex); + if (refreshingPreviouslyEnabled) { + enableContentRefreshing(true); + } + if (propertyId.length > 0 && ascending.length > 0) { + // The first propertyId is the primary sorting criterion, + // therefore the sort indicator should be there + sortAscending = ascending[0]; + sortContainerPropertyId = propertyId[0]; + } else { + sortAscending = true; + sortContainerPropertyId = null; + } + } else if (c != null) { + throw new UnsupportedOperationException( + "Underlying Data does not allow sorting"); + } + } + + /** + * Sorts the table by currently selected sorting column. + * + * @throws UnsupportedOperationException + * if the container data source does not implement + * Container.Sortable + */ + public void sort() { + if (getSortContainerPropertyId() == null) { + return; + } + sort(new Object[] { sortContainerPropertyId }, + new boolean[] { sortAscending }); + } + + /** + * Gets the container property IDs, which can be used to sort the item. + *

    + * Note that the {@link #isSortEnabled()} state affects what this method + * returns. Disabling sorting causes this method to always return an empty + * collection. + *

    + * + * @see com.vaadin.v7.data.Container.Sortable#getSortableContainerPropertyIds() + */ + + @Override + public Collection getSortableContainerPropertyIds() { + final Container c = getContainerDataSource(); + if (c instanceof Container.Sortable && isSortEnabled()) { + return ((Container.Sortable) c).getSortableContainerPropertyIds(); + } else { + return Collections.EMPTY_LIST; + } + } + + /** + * Gets the currently sorted column property ID. + * + * @return the Container property id of the currently sorted column. + */ + public Object getSortContainerPropertyId() { + return sortContainerPropertyId; + } + + /** + * Sets the currently sorted column property id. + * + * @param propertyId + * the Container property id of the currently sorted column. + */ + public void setSortContainerPropertyId(Object propertyId) { + setSortContainerPropertyId(propertyId, true); + } + + /** + * Internal method to set currently sorted column property id. With doSort + * flag actual sorting may be bypassed. + * + * @param propertyId + * @param doSort + */ + private void setSortContainerPropertyId(Object propertyId, boolean doSort) { + if ((sortContainerPropertyId != null + && !sortContainerPropertyId.equals(propertyId)) + || (sortContainerPropertyId == null && propertyId != null)) { + sortContainerPropertyId = propertyId; + + if (doSort) { + sort(); + // Assures the visual refresh. This should not be necessary as + // sort() calls refreshRowCache + refreshRenderedCells(); + } + } + } + + /** + * Is the table currently sorted in ascending order. + * + * @return true if ascending, false if descending. + */ + public boolean isSortAscending() { + return sortAscending; + } + + /** + * Sets the table in ascending order. + * + * @param ascending + * true if ascending, false if + * descending. + */ + public void setSortAscending(boolean ascending) { + setSortAscending(ascending, true); + } + + /** + * Internal method to set sort ascending. With doSort flag actual sort can + * be bypassed. + * + * @param ascending + * @param doSort + */ + private void setSortAscending(boolean ascending, boolean doSort) { + if (sortAscending != ascending) { + sortAscending = ascending; + if (doSort) { + sort(); + // Assures the visual refresh. This should not be necessary as + // sort() calls refreshRowCache + refreshRenderedCells(); + } + } + } + + /** + * Is sorting disabled altogether. + * + * True iff no sortable columns are given even in the case where data source + * would support this. + * + * @return True iff sorting is disabled. + * @deprecated As of 7.0, use {@link #isSortEnabled()} instead + */ + @Deprecated + public boolean isSortDisabled() { + return !isSortEnabled(); + } + + /** + * Checks if sorting is enabled. + * + * @return true if sorting by the user is allowed, false otherwise + */ + public boolean isSortEnabled() { + return sortEnabled; + } + + /** + * Disables the sorting by the user altogether. + * + * @param sortDisabled + * True iff sorting is disabled. + * @deprecated As of 7.0, use {@link #setSortEnabled(boolean)} instead + */ + @Deprecated + public void setSortDisabled(boolean sortDisabled) { + setSortEnabled(!sortDisabled); + } + + /** + * Enables or disables sorting. + *

    + * Setting this to false disallows sorting by the user. It is still possible + * to call {@link #sort()}. + *

    + * + * @param sortEnabled + * true to allow the user to sort the table, false to disallow it + */ + public void setSortEnabled(boolean sortEnabled) { + if (this.sortEnabled != sortEnabled) { + this.sortEnabled = sortEnabled; + markAsDirty(); + } + } + + /** + * Used to create "generated columns"; columns that exist only in the Table, + * not in the underlying Container. Implement this interface and pass it to + * Table.addGeneratedColumn along with an id for the column to be generated. + * + */ + public interface ColumnGenerator extends Serializable { + + /** + * Called by Table when a cell in a generated column needs to be + * generated. + * + * @param source + * the source Table + * @param itemId + * the itemId (aka rowId) for the of the cell to be generated + * @param columnId + * the id for the generated column (as specified in + * addGeneratedColumn) + * @return A {@link Component} that should be rendered in the cell or a + * {@link String} that should be displayed in the cell. Other + * return values are not supported. + */ + public abstract Object generateCell(Table source, Object itemId, + Object columnId); + } + + /** + * Set cell style generator for Table. + * + * @param cellStyleGenerator + * New cell style generator or null to remove generator. + */ + public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) { + this.cellStyleGenerator = cellStyleGenerator; + // Assures the visual refresh. No need to reset the page buffer + // before as the content has not changed, only the style generators + refreshRenderedCells(); + + } + + /** + * Get the current cell style generator. + * + */ + public CellStyleGenerator getCellStyleGenerator() { + return cellStyleGenerator; + } + + /** + * Allow to define specific style on cells (and rows) contents. Implements + * this interface and pass it to Table.setCellStyleGenerator. Row styles are + * generated when porpertyId is null. The CSS class name that will be added + * to the cell content is v-table-cell-content-[style name], and + * the row style will be v-table-row-[style name]. + */ + public interface CellStyleGenerator extends Serializable { + + /** + * Called by Table when a cell (and row) is painted. + * + * @param source + * the source Table + * @param itemId + * The itemId of the painted cell + * @param propertyId + * The propertyId of the cell, null when getting row style + * @return The style name to add to this cell or row. (the CSS class + * name will be v-table-cell-content-[style name], or + * v-table-row-[style name] for rows) + */ + public abstract String getStyle(Table source, Object itemId, + Object propertyId); + } + + @Override + public void addItemClickListener(ItemClickListener listener) { + addListener(TableConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, + listener, ItemClickEvent.ITEM_CLICK_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemClickListener(ItemClickListener)} + **/ + @Override + @Deprecated + public void addListener(ItemClickListener listener) { + addItemClickListener(listener); + } + + @Override + public void removeItemClickListener(ItemClickListener listener) { + removeListener(TableConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, + listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeItemClickListener(ItemClickListener)} + **/ + @Override + @Deprecated + public void removeListener(ItemClickListener listener) { + removeItemClickListener(listener); + } + + // Identical to AbstractCompoenentContainer.setEnabled(); + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + if (getParent() != null && !getParent().isEnabled()) { + // some ancestor still disabled, don't update children + return; + } else { + markAsDirtyRecursive(); + } + } + + /** + * Sets the drag start mode of the Table. Drag start mode controls how Table + * behaves as a drag source. + * + * @param newDragMode + */ + public void setDragMode(TableDragMode newDragMode) { + dragMode = newDragMode; + markAsDirty(); + } + + /** + * @return the current start mode of the Table. Drag start mode controls how + * Table behaves as a drag source. + */ + public TableDragMode getDragMode() { + return dragMode; + } + + /** + * Concrete implementation of {@link DataBoundTransferable} for data + * transferred from a table. + * + * @see {@link DataBoundTransferable}. + * + * @since 6.3 + */ + public class TableTransferable extends DataBoundTransferable { + + protected TableTransferable(Map rawVariables) { + super(Table.this, rawVariables); + Object object = rawVariables.get("itemId"); + if (object != null) { + setData("itemId", itemIdMapper.get((String) object)); + } + object = rawVariables.get("propertyId"); + if (object != null) { + setData("propertyId", columnIdMap.get((String) object)); + } + } + + @Override + public Object getItemId() { + return getData("itemId"); + } + + @Override + public Object getPropertyId() { + return getData("propertyId"); + } + + @Override + public Table getSourceComponent() { + return (Table) super.getSourceComponent(); + } + + } + + @Override + public TableTransferable getTransferable(Map rawVariables) { + TableTransferable transferable = new TableTransferable(rawVariables); + return transferable; + } + + @Override + public DropHandler getDropHandler() { + return dropHandler; + } + + public void setDropHandler(DropHandler dropHandler) { + this.dropHandler = dropHandler; + } + + @Override + public AbstractSelectTargetDetails translateDropTargetDetails( + Map clientVariables) { + return new AbstractSelectTargetDetails(clientVariables); + } + + /** + * Sets the behavior of how the multi-select mode should behave when the + * table is both selectable and in multi-select mode. + *

    + * Note, that on some clients the mode may not be respected. E.g. on touch + * based devices CTRL/SHIFT base selection method is invalid, so touch based + * browsers always use the {@link MultiSelectMode#SIMPLE}. + * + * @param mode + * The select mode of the table + */ + public void setMultiSelectMode(MultiSelectMode mode) { + multiSelectMode = mode; + markAsDirty(); + } + + /** + * Returns the select mode in which multi-select is used. + * + * @return The multi select mode + */ + public MultiSelectMode getMultiSelectMode() { + return multiSelectMode; + } + + /** + * Lazy loading accept criterion for Table. Accepted target rows are loaded + * from server once per drag and drop operation. Developer must override one + * method that decides on which rows the currently dragged data can be + * dropped. + * + *

    + * Initially pretty much no data is sent to client. On first required + * criterion check (per drag request) the client side data structure is + * initialized from server and no subsequent requests requests are needed + * during that drag and drop operation. + */ + public static abstract class TableDropCriterion + extends ServerSideCriterion { + + private Table table; + + private Set allowedItemIds; + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.acceptcriteria.ServerSideCriterion#getIdentifier + * () + */ + + @Override + protected String getIdentifier() { + return TableDropCriterion.class.getCanonicalName(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#accepts(com.vaadin + * .event.dd.DragAndDropEvent) + */ + @Override + @SuppressWarnings("unchecked") + public boolean accept(DragAndDropEvent dragEvent) { + AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent + .getTargetDetails(); + table = (Table) dragEvent.getTargetDetails().getTarget(); + Collection visibleItemIds = table.getVisibleItemIds(); + allowedItemIds = getAllowedItemIds(dragEvent, table, + (Collection) visibleItemIds); + + return allowedItemIds.contains(dropTargetData.getItemIdOver()); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#paintResponse( + * com.vaadin.server.PaintTarget) + */ + + @Override + public void paintResponse(PaintTarget target) throws PaintException { + /* + * send allowed nodes to client so subsequent requests can be + * avoided + */ + Object[] array = allowedItemIds.toArray(); + for (int i = 0; i < array.length; i++) { + String key = table.itemIdMapper.key(array[i]); + array[i] = key; + } + target.addAttribute("allowedIds", array); + } + + /** + * @param dragEvent + * @param table + * the table for which the allowed item identifiers are + * defined + * @param visibleItemIds + * the list of currently rendered item identifiers, accepted + * item id's need to be detected only for these visible items + * @return the set of identifiers for items on which the dragEvent will + * be accepted + */ + protected abstract Set getAllowedItemIds( + DragAndDropEvent dragEvent, Table table, + Collection visibleItemIds); + + } + + /** + * Click event fired when clicking on the Table headers. The event includes + * a reference the the Table the event originated from, the property id of + * the column which header was pressed and details about the mouse event + * itself. + */ + public static class HeaderClickEvent extends ClickEvent { + public static final Method HEADER_CLICK_METHOD; + + static { + try { + // Set the header click method + HEADER_CLICK_METHOD = HeaderClickListener.class + .getDeclaredMethod("headerClick", + new Class[] { HeaderClickEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException(e); + } + } + + // The property id of the column which header was pressed + private final Object columnPropertyId; + + public HeaderClickEvent(Component source, Object propertyId, + MouseEventDetails details) { + super(source, details); + columnPropertyId = propertyId; + } + + /** + * Gets the property id of the column which header was pressed + * + * @return The column property id + */ + public Object getPropertyId() { + return columnPropertyId; + } + } + + /** + * Click event fired when clicking on the Table footers. The event includes + * a reference the the Table the event originated from, the property id of + * the column which header was pressed and details about the mouse event + * itself. + */ + public static class FooterClickEvent extends ClickEvent { + public static final Method FOOTER_CLICK_METHOD; + + static { + try { + // Set the header click method + FOOTER_CLICK_METHOD = FooterClickListener.class + .getDeclaredMethod("footerClick", + new Class[] { FooterClickEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException(e); + } + } + + // The property id of the column which header was pressed + private final Object columnPropertyId; + + /** + * Constructor + * + * @param source + * The source of the component + * @param propertyId + * The propertyId of the column + * @param details + * The mouse details of the click + */ + public FooterClickEvent(Component source, Object propertyId, + MouseEventDetails details) { + super(source, details); + columnPropertyId = propertyId; + } + + /** + * Gets the property id of the column which header was pressed + * + * @return The column property id + */ + public Object getPropertyId() { + return columnPropertyId; + } + } + + /** + * Interface for the listener for column header mouse click events. The + * headerClick method is called when the user presses a header column cell. + */ + public interface HeaderClickListener extends Serializable { + + /** + * Called when a user clicks a header column cell + * + * @param event + * The event which contains information about the column and + * the mouse click event + */ + public void headerClick(HeaderClickEvent event); + } + + /** + * Interface for the listener for column footer mouse click events. The + * footerClick method is called when the user presses a footer column cell. + */ + public interface FooterClickListener extends Serializable { + + /** + * Called when a user clicks a footer column cell + * + * @param event + * The event which contains information about the column and + * the mouse click event + */ + public void footerClick(FooterClickEvent event); + } + + /** + * Adds a header click listener which handles the click events when the user + * clicks on a column header cell in the Table. + *

    + * The listener will receive events which contain information about which + * column was clicked and some details about the mouse event. + *

    + * + * @param listener + * The handler which should handle the header click events. + */ + public void addHeaderClickListener(HeaderClickListener listener) { + addListener(TableConstants.HEADER_CLICK_EVENT_ID, + HeaderClickEvent.class, listener, + HeaderClickEvent.HEADER_CLICK_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addHeaderClickListener(HeaderClickListener)} + **/ + @Deprecated + public void addListener(HeaderClickListener listener) { + addHeaderClickListener(listener); + } + + /** + * Removes a header click listener + * + * @param listener + * The listener to remove. + */ + public void removeHeaderClickListener(HeaderClickListener listener) { + removeListener(TableConstants.HEADER_CLICK_EVENT_ID, + HeaderClickEvent.class, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeHeaderClickListener(HeaderClickListener)} + **/ + @Deprecated + public void removeListener(HeaderClickListener listener) { + removeHeaderClickListener(listener); + } + + /** + * Adds a footer click listener which handles the click events when the user + * clicks on a column footer cell in the Table. + *

    + * The listener will receive events which contain information about which + * column was clicked and some details about the mouse event. + *

    + * + * @param listener + * The handler which should handle the footer click events. + */ + public void addFooterClickListener(FooterClickListener listener) { + addListener(TableConstants.FOOTER_CLICK_EVENT_ID, + FooterClickEvent.class, listener, + FooterClickEvent.FOOTER_CLICK_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addFooterClickListener(FooterClickListener)} + **/ + @Deprecated + public void addListener(FooterClickListener listener) { + addFooterClickListener(listener); + } + + /** + * Removes a footer click listener + * + * @param listener + * The listener to remove. + */ + public void removeFooterClickListener(FooterClickListener listener) { + removeListener(TableConstants.FOOTER_CLICK_EVENT_ID, + FooterClickEvent.class, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeFooterClickListener(FooterClickListener)} + **/ + @Deprecated + public void removeListener(FooterClickListener listener) { + removeFooterClickListener(listener); + } + + /** + * Gets the footer caption beneath the rows + * + * @param propertyId + * The propertyId of the column * + * @return The caption of the footer or NULL if not set + */ + public String getColumnFooter(Object propertyId) { + return columnFooters.get(propertyId); + } + + /** + * Sets the column footer caption. The column footer caption is the text + * displayed beneath the column if footers have been set visible. + * + * @param propertyId + * The properyId of the column + * + * @param footer + * The caption of the footer + */ + public void setColumnFooter(Object propertyId, String footer) { + if (footer == null) { + columnFooters.remove(propertyId); + } else { + columnFooters.put(propertyId, footer); + } + + markAsDirty(); + } + + /** + * Sets the footer visible in the bottom of the table. + *

    + * The footer can be used to add column related data like sums to the bottom + * of the Table using setColumnFooter(Object propertyId, String footer). + *

    + * + * @param visible + * Should the footer be visible + */ + public void setFooterVisible(boolean visible) { + if (visible != columnFootersVisible) { + columnFootersVisible = visible; + markAsDirty(); + } + } + + /** + * Is the footer currently visible? + * + * @return Returns true if visible else false + */ + public boolean isFooterVisible() { + return columnFootersVisible; + } + + /** + * This event is fired when a column is resized. The event contains the + * columns property id which was fired, the previous width of the column and + * the width of the column after the resize. + */ + public static class ColumnResizeEvent extends Component.Event { + public static final Method COLUMN_RESIZE_METHOD; + + static { + try { + COLUMN_RESIZE_METHOD = ColumnResizeListener.class + .getDeclaredMethod("columnResize", + new Class[] { ColumnResizeEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException(e); + } + } + + private final int previousWidth; + private final int currentWidth; + private final Object columnPropertyId; + + /** + * Constructor + * + * @param source + * The source of the event + * @param propertyId + * The columns property id + * @param previous + * The width in pixels of the column before the resize event + * @param current + * The width in pixels of the column after the resize event + */ + public ColumnResizeEvent(Component source, Object propertyId, + int previous, int current) { + super(source); + previousWidth = previous; + currentWidth = current; + columnPropertyId = propertyId; + } + + /** + * Get the column property id of the column that was resized. + * + * @return The column property id + */ + public Object getPropertyId() { + return columnPropertyId; + } + + /** + * Get the width in pixels of the column before the resize event + * + * @return Width in pixels + */ + public int getPreviousWidth() { + return previousWidth; + } + + /** + * Get the width in pixels of the column after the resize event + * + * @return Width in pixels + */ + public int getCurrentWidth() { + return currentWidth; + } + } + + /** + * Interface for listening to column resize events. + */ + public interface ColumnResizeListener extends Serializable { + + /** + * This method is triggered when the column has been resized + * + * @param event + * The event which contains the column property id, the + * previous width of the column and the current width of the + * column + */ + public void columnResize(ColumnResizeEvent event); + } + + /** + * Adds a column resize listener to the Table. A column resize listener is + * called when a user resizes a columns width. + * + * @param listener + * The listener to attach to the Table + */ + public void addColumnResizeListener(ColumnResizeListener listener) { + addListener(TableConstants.COLUMN_RESIZE_EVENT_ID, + ColumnResizeEvent.class, listener, + ColumnResizeEvent.COLUMN_RESIZE_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addColumnResizeListener(ColumnResizeListener)} + **/ + @Deprecated + public void addListener(ColumnResizeListener listener) { + addColumnResizeListener(listener); + } + + /** + * Removes a column resize listener from the Table. + * + * @param listener + * The listener to remove + */ + public void removeColumnResizeListener(ColumnResizeListener listener) { + removeListener(TableConstants.COLUMN_RESIZE_EVENT_ID, + ColumnResizeEvent.class, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeColumnResizeListener(ColumnResizeListener)} + **/ + @Deprecated + public void removeListener(ColumnResizeListener listener) { + removeColumnResizeListener(listener); + } + + /** + * This event is fired when a columns are reordered by the end user user. + */ + public static class ColumnReorderEvent extends Component.Event { + public static final Method METHOD; + + static { + try { + METHOD = ColumnReorderListener.class.getDeclaredMethod( + "columnReorder", + new Class[] { ColumnReorderEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException(e); + } + } + + /** + * Constructor + * + * @param source + * The source of the event + */ + public ColumnReorderEvent(Component source) { + super(source); + } + + } + + /** + * Interface for listening to column reorder events. + */ + public interface ColumnReorderListener extends Serializable { + + /** + * This method is triggered when the column has been reordered + * + * @param event + */ + public void columnReorder(ColumnReorderEvent event); + } + + /** + * This event is fired when the collapse state of a column changes. + * + * @since 7.6 + */ + public static class ColumnCollapseEvent extends Component.Event { + + public static final Method METHOD = ReflectTools.findMethod( + ColumnCollapseListener.class, "columnCollapseStateChange", + ColumnCollapseEvent.class); + private Object propertyId; + + /** + * Constructor + * + * @param source + * The source of the event + * @param propertyId + * The id of the column + */ + public ColumnCollapseEvent(Component source, Object propertyId) { + super(source); + this.propertyId = propertyId; + } + + /** + * Gets the id of the column whose collapse state changed + * + * @return the property id of the column + */ + public Object getPropertyId() { + return propertyId; + } + } + + /** + * Interface for listening to column collapse events. + * + * @since 7.6 + */ + public interface ColumnCollapseListener extends Serializable { + + /** + * This method is triggered when the collapse state for a column has + * changed + * + * @param event + */ + public void columnCollapseStateChange(ColumnCollapseEvent event); + } + + /** + * Adds a column reorder listener to the Table. A column reorder listener is + * called when a user reorders columns. + * + * @param listener + * The listener to attach to the Table + */ + public void addColumnReorderListener(ColumnReorderListener listener) { + addListener(TableConstants.COLUMN_REORDER_EVENT_ID, + ColumnReorderEvent.class, listener, ColumnReorderEvent.METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addColumnReorderListener(ColumnReorderListener)} + **/ + @Deprecated + public void addListener(ColumnReorderListener listener) { + addColumnReorderListener(listener); + } + + /** + * Removes a column reorder listener from the Table. + * + * @param listener + * The listener to remove + */ + public void removeColumnReorderListener(ColumnReorderListener listener) { + removeListener(TableConstants.COLUMN_REORDER_EVENT_ID, + ColumnReorderEvent.class, listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeColumnReorderListener(ColumnReorderListener)} + **/ + @Deprecated + public void removeListener(ColumnReorderListener listener) { + removeColumnReorderListener(listener); + } + + /** + * Adds a column collapse listener to the Table. A column collapse listener + * is called when the collapsed state of a column changes. + * + * @since 7.6 + * + * @param listener + * The listener to attach + */ + public void addColumnCollapseListener(ColumnCollapseListener listener) { + addListener(TableConstants.COLUMN_COLLAPSE_EVENT_ID, + ColumnCollapseEvent.class, listener, + ColumnCollapseEvent.METHOD); + } + + /** + * Removes a column reorder listener from the Table. + * + * @since 7.6 + * @param listener + * The listener to remove + */ + public void removeColumnCollapseListener(ColumnCollapseListener listener) { + removeListener(TableConstants.COLUMN_COLLAPSE_EVENT_ID, + ColumnCollapseEvent.class, listener); + } + + /** + * Set the item description generator which generates tooltips for cells and + * rows in the Table + * + * @param generator + * The generator to use or null to disable + */ + public void setItemDescriptionGenerator( + ItemDescriptionGenerator generator) { + if (generator != itemDescriptionGenerator) { + itemDescriptionGenerator = generator; + // Assures the visual refresh. No need to reset the page buffer + // before as the content has not changed, only the descriptions + refreshRenderedCells(); + } + } + + /** + * Get the item description generator which generates tooltips for cells and + * rows in the Table. + */ + public ItemDescriptionGenerator getItemDescriptionGenerator() { + return itemDescriptionGenerator; + } + + /** + * Row generators can be used to replace certain items in a table with a + * generated string. The generator is called each time the table is + * rendered, which means that new strings can be generated each time. + * + * Row generators can be used for e.g. summary rows or grouping of items. + */ + public interface RowGenerator extends Serializable { + /** + * Called for every row that is painted in the Table. Returning a + * GeneratedRow object will cause the row to be painted based on the + * contents of the GeneratedRow. A generated row is by default styled + * similarly to a header or footer row. + *

    + * The GeneratedRow data object contains the text that should be + * rendered in the row. The itemId in the container thus works only as a + * placeholder. + *

    + * If GeneratedRow.setSpanColumns(true) is used, there will be one + * String spanning all columns (use setText("Spanning text")). Otherwise + * you can define one String per visible column. + *

    + * If GeneratedRow.setRenderAsHtml(true) is used, the strings can + * contain HTML markup, otherwise all strings will be rendered as text + * (the default). + *

    + * A "v-table-generated-row" CSS class is added to all generated rows. + * For custom styling of a generated row you can combine a RowGenerator + * with a CellStyleGenerator. + *

    + * + * @param table + * The Table that is being painted + * @param itemId + * The itemId for the row + * @return A GeneratedRow describing how the row should be painted or + * null to paint the row with the contents from the container + */ + public GeneratedRow generateRow(Table table, Object itemId); + } + + public static class GeneratedRow implements Serializable { + private boolean htmlContentAllowed = false; + private boolean spanColumns = false; + private String[] text = null; + + /** + * Creates a new generated row. If only one string is passed in, columns + * are automatically spanned. + * + * @param text + */ + public GeneratedRow(String... text) { + setHtmlContentAllowed(false); + setSpanColumns(text == null || text.length == 1); + setText(text); + } + + /** + * Pass one String if spanColumns is used, one String for each visible + * column otherwise + */ + public void setText(String... text) { + if (text == null || (text.length == 1 && text[0] == null)) { + text = new String[] { "" }; + } + this.text = text; + } + + protected String[] getText() { + return text; + } + + protected Object getValue() { + return getText(); + } + + protected boolean isHtmlContentAllowed() { + return htmlContentAllowed; + } + + /** + * If set to true, all strings passed to {@link #setText(String...)} + * will be rendered as HTML. + * + * @param htmlContentAllowed + */ + public void setHtmlContentAllowed(boolean htmlContentAllowed) { + this.htmlContentAllowed = htmlContentAllowed; + } + + protected boolean isSpanColumns() { + return spanColumns; + } + + /** + * If set to true, only one string will be rendered, spanning the entire + * row. + * + * @param spanColumns + */ + public void setSpanColumns(boolean spanColumns) { + this.spanColumns = spanColumns; + } + } + + /** + * Assigns a row generator to the table. The row generator will be able to + * replace rows in the table when it is rendered. + * + * @param generator + * the new row generator + */ + public void setRowGenerator(RowGenerator generator) { + rowGenerator = generator; + refreshRowCache(); + } + + /** + * @return the current row generator + */ + public RowGenerator getRowGenerator() { + return rowGenerator; + } + + /** + * Sets a converter for a property id. + *

    + * The converter is used to format the the data for the given property id + * before displaying it in the table. + *

    + * + * @param propertyId + * The propertyId to format using the converter + * @param converter + * The converter to use for the property id + */ + public void setConverter(Object propertyId, + Converter converter) { + if (!getContainerPropertyIds().contains(propertyId)) { + throw new IllegalArgumentException( + "PropertyId " + propertyId + " must be in the container"); + } + + if (!typeIsCompatible(converter.getModelType(), getType(propertyId))) { + throw new IllegalArgumentException( + "Property type (" + getType(propertyId) + + ") must match converter source type (" + + converter.getModelType() + ")"); + } + propertyValueConverters.put(propertyId, + (Converter) converter); + refreshRowCache(); + } + + /** + * Checks if there is a converter set explicitly for the given property id. + * + * @param propertyId + * The propertyId to check + * @return true if a converter has been set for the property id, false + * otherwise + */ + protected boolean hasConverter(Object propertyId) { + return propertyValueConverters.containsKey(propertyId); + } + + /** + * Returns the converter used to format the given propertyId. + * + * @param propertyId + * The propertyId to check + * @return The converter used to format the propertyId or null if no + * converter has been set + */ + public Converter getConverter(Object propertyId) { + return propertyValueConverters.get(propertyId); + } + + @Override + public void setVisible(boolean visible) { + if (visible) { + // We need to ensure that the rows are sent to the client when the + // Table is made visible if it has been rendered as invisible. + setRowCacheInvalidated(true); + } + super.setVisible(visible); + } + + @Override + public Iterator iterator() { + if (visibleComponents == null) { + Collection empty = Collections.emptyList(); + return empty.iterator(); + } + return visibleComponents.iterator(); + } + + /** + * @deprecated As of 7.0, use {@link #iterator()} instead. + */ + @Deprecated + public Iterator getComponentIterator() { + return iterator(); + } + + @Override + public void readDesign(Element design, DesignContext context) { + super.readDesign(design, context); + + if (design.hasAttr("sortable")) { + setSortEnabled(DesignAttributeHandler.readAttribute("sortable", + design.attributes(), boolean.class)); + } + + readColumns(design); + readHeader(design); + readBody(design, context); + readFooter(design); + } + + private void readColumns(Element design) { + Element colgroup = design.select("> table > colgroup").first(); + + if (colgroup != null) { + int i = 0; + List pIds = new ArrayList(); + for (Element col : colgroup.children()) { + if (!col.tagName().equals("col")) { + throw new DesignException("invalid column"); + } + + String id = DesignAttributeHandler.readAttribute("property-id", + col.attributes(), "property-" + i++, String.class); + pIds.add(id); + + addContainerProperty(id, String.class, null); + + if (col.hasAttr("width")) { + setColumnWidth(id, DesignAttributeHandler.readAttribute( + "width", col.attributes(), Integer.class)); + } + if (col.hasAttr("center")) { + setColumnAlignment(id, Align.CENTER); + } else if (col.hasAttr("right")) { + setColumnAlignment(id, Align.RIGHT); + } + if (col.hasAttr("expand")) { + if (col.attr("expand").isEmpty()) { + setColumnExpandRatio(id, 1); + } else { + setColumnExpandRatio(id, + DesignAttributeHandler.readAttribute("expand", + col.attributes(), float.class)); + } + } + if (col.hasAttr("collapsible")) { + setColumnCollapsible(id, + DesignAttributeHandler.readAttribute("collapsible", + col.attributes(), boolean.class)); + } + if (col.hasAttr("collapsed")) { + setColumnCollapsed(id, DesignAttributeHandler.readAttribute( + "collapsed", col.attributes(), boolean.class)); + } + } + setVisibleColumns(pIds.toArray()); + } + } + + private void readFooter(Element design) { + readHeaderOrFooter(design, false); + } + + private void readHeader(Element design) { + readHeaderOrFooter(design, true); + } + + @Override + protected void readItems(Element design, DesignContext context) { + // Do nothing - header/footer and inline data must be handled after + // colgroup. + } + + private void readHeaderOrFooter(Element design, boolean header) { + String selector = header ? "> table > thead" : "> table > tfoot"; + Element elem = design.select(selector).first(); + if (elem != null) { + if (!header) { + setFooterVisible(true); + } + if (elem.children().size() != 1) { + throw new DesignException( + "Table header and footer should contain exactly one element"); + } + Element tr = elem.child(0); + Elements elems = tr.children(); + Collection propertyIds = visibleColumns; + if (elems.size() != propertyIds.size()) { + throw new DesignException( + "Table header and footer should contain as many items as there" + + " are columns in the Table."); + } + Iterator propertyIt = propertyIds.iterator(); + for (Element e : elems) { + String columnValue = DesignFormatter + .decodeFromTextNode(e.html()); + Object propertyId = propertyIt.next(); + if (header) { + setColumnHeader(propertyId, columnValue); + if (e.hasAttr("icon")) { + setColumnIcon(propertyId, + DesignAttributeHandler.readAttribute("icon", + e.attributes(), Resource.class)); + } + } else { + setColumnFooter(propertyId, columnValue); + } + } + } + } + + protected void readBody(Element design, DesignContext context) { + Element tbody = design.select("> table > tbody").first(); + if (tbody == null) { + return; + } + + Set selected = new HashSet(); + for (Element tr : tbody.children()) { + readItem(tr, selected, context); + } + } + + @Override + protected Object readItem(Element tr, Set selected, + DesignContext context) { + Elements cells = tr.children(); + if (visibleColumns.size() != cells.size()) { + throw new DesignException( + "Wrong number of columns in a Table row. Expected " + + visibleColumns.size() + ", was " + cells.size() + + "."); + } + Object[] data = new String[cells.size()]; + for (int c = 0; c < cells.size(); ++c) { + data[c] = DesignFormatter.decodeFromTextNode(cells.get(c).html()); + } + + Object itemId = addItem(data, + tr.hasAttr("item-id") ? tr.attr("item-id") : null); + + if (itemId == null) { + throw new DesignException("Failed to add a Table row: " + data); + } + + return itemId; + } + + @Override + public void writeDesign(Element design, DesignContext context) { + Table def = context.getDefaultInstance(this); + + DesignAttributeHandler.writeAttribute("sortable", design.attributes(), + isSortEnabled(), def.isSortEnabled(), boolean.class); + + Element table = null; + boolean hasColumns = getVisibleColumns().length != 0; + if (hasColumns) { + table = design.appendElement("table"); + writeColumns(table, def); + writeHeader(table, def); + } + super.writeDesign(design, context); + if (hasColumns) { + writeFooter(table); + } + } + + private void writeColumns(Element table, Table def) { + Object[] columns = getVisibleColumns(); + if (columns.length == 0) { + return; + } + + Element colgroup = table.appendElement("colgroup"); + for (Object id : columns) { + Element col = colgroup.appendElement("col"); + + col.attr("property-id", id.toString()); + + if (getColumnAlignment(id) == Align.CENTER) { + col.attr("center", true); + } else if (getColumnAlignment(id) == Align.RIGHT) { + col.attr("right", true); + } + + DesignAttributeHandler.writeAttribute("width", col.attributes(), + getColumnWidth(id), def.getColumnWidth(null), int.class); + + DesignAttributeHandler.writeAttribute("expand", col.attributes(), + getColumnExpandRatio(id), def.getColumnExpandRatio(null), + float.class); + + DesignAttributeHandler.writeAttribute("collapsible", + col.attributes(), isColumnCollapsible(id), + def.isColumnCollapsible(null), boolean.class); + + DesignAttributeHandler.writeAttribute("collapsed", col.attributes(), + isColumnCollapsed(id), def.isColumnCollapsed(null), + boolean.class); + } + } + + private void writeHeader(Element table, Table def) { + Object[] columns = getVisibleColumns(); + if (columns.length == 0 + || (columnIcons.isEmpty() && columnHeaders.isEmpty())) { + return; + } + + Element header = table.appendElement("thead").appendElement("tr"); + for (Object id : columns) { + Element th = header.appendElement("th"); + th.html(getColumnHeader(id)); + DesignAttributeHandler.writeAttribute("icon", th.attributes(), + getColumnIcon(id), def.getColumnIcon(null), Resource.class); + } + + } + + private void writeFooter(Element table) { + Object[] columns = getVisibleColumns(); + if (columns.length == 0 || columnFooters.isEmpty()) { + return; + } + + Element footer = table.appendElement("tfoot").appendElement("tr"); + for (Object id : columns) { + footer.appendElement("td").text(getColumnFooter(id)); + } + } + + @Override + protected void writeItems(Element design, DesignContext context) { + if (getVisibleColumns().length == 0) { + return; + } + Element tbody = design.child(0).appendElement("tbody"); + super.writeItems(tbody, context); + } + + @Override + protected Element writeItem(Element tbody, Object itemId, + DesignContext context) { + Element tr = tbody.appendElement("tr"); + tr.attr("item-id", String.valueOf(itemId)); + Item item = getItem(itemId); + for (Object id : getVisibleColumns()) { + Element td = tr.appendElement("td"); + Object value = item.getItemProperty(id).getValue(); + td.html(value != null ? value.toString() : ""); + } + return tr; + } + + @Override + protected Collection getCustomAttributes() { + Collection result = super.getCustomAttributes(); + result.add("sortable"); + result.add("sort-enabled"); + result.add("sort-disabled"); + result.add("footer-visible"); + result.add("item-caption-mode"); + result.add("current-page-first-item-id"); + result.add("current-page-first-item-index"); + return result; + } + + /** + * ContextClickEvent for the Table Component. + * + * @since 7.6 + */ + public static class TableContextClickEvent extends ContextClickEvent { + + private final Object itemId; + private final Object propertyId; + private final Section section; + + public TableContextClickEvent(Table source, + MouseEventDetails mouseEventDetails, Object itemId, + Object propertyId, Section section) { + super(source, mouseEventDetails); + + this.itemId = itemId; + this.propertyId = propertyId; + this.section = section; + } + + /** + * Returns the item id of context clicked row. + * + * @return item id of clicked row; null if header, footer + * or empty area of Table + */ + public Object getItemId() { + return itemId; + } + + /** + * Returns the property id of context clicked column. + * + * @return property id; or null if we've clicked on the + * empty area of the Table + */ + public Object getPropertyId() { + return propertyId; + } + + /** + * Returns the clicked section of Table. + * + * @return section of Table + */ + public Section getSection() { + return section; + } + + @Override + public Table getComponent() { + return (Table) super.getComponent(); + } + } + + @Override + protected TableState getState() { + return getState(true); + } + + @Override + protected TableState getState(boolean markAsDirty) { + return (TableState) super.getState(markAsDirty); + } + + private final Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger(Table.class.getName()); + } + return logger; + } + + @Override + public void setChildMeasurementHint(ChildMeasurementHint hint) { + if (hint == null) { + childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; + } else { + childMeasurementHint = hint; + } + } + + @Override + public ChildMeasurementHint getChildMeasurementHint() { + return childMeasurementHint; + } + + /** + * Sets whether only collapsible columns should be shown to the user in the + * column collapse menu. The default is + * {@link CollapseMenuContent#ALL_COLUMNS}. + * + * + * @since 7.6 + * @param content + * the desired collapsible menu content setting + */ + public void setCollapseMenuContent(CollapseMenuContent content) { + getState().collapseMenuContent = content; + } + + /** + * Checks whether only collapsible columns are shown to the user in the + * column collapse menu. The default is + * {@link CollapseMenuContent#ALL_COLUMNS} . + * + * @since 7.6 + * @return the current collapsible menu content setting + */ + public CollapseMenuContent getCollapseMenuContent() { + return getState(false).collapseMenuContent; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/TableFieldFactory.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/TableFieldFactory.java new file mode 100644 index 0000000000..1ed286738d --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/TableFieldFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui; + +import java.io.Serializable; + +import com.vaadin.ui.Component; +import com.vaadin.v7.data.Container; + +/** + * Factory interface for creating new LegacyField-instances based on Container + * (datasource), item id, property id and uiContext (the component responsible + * for displaying fields). Currently this interface is used by {@link Table}, + * but might later be used by some other components for {@link Field} + * generation. + * + *

    + * + * @author Vaadin Ltd. + * @since 6.0 + * @see FormFieldFactory + */ +public interface TableFieldFactory extends Serializable { + /** + * Creates a field based on the Container, item id, property id and the + * component responsible for displaying the field (most commonly + * {@link Table}). + * + * @param container + * the Container where the property belongs to. + * @param itemId + * the item Id. + * @param propertyId + * the Id of the property. + * @param uiContext + * the component where the field is presented. + * @return A field suitable for editing the specified data or null if the + * property should not be editable. + */ + Field createField(Container container, Object itemId, + Object propertyId, Component uiContext); + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/TextArea.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/TextArea.java new file mode 100644 index 0000000000..e4cd20a59b --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/TextArea.java @@ -0,0 +1,170 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import org.jsoup.nodes.Element; + +import com.vaadin.shared.ui.textarea.TextAreaState; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignFormatter; +import com.vaadin.v7.data.Property; + +/** + * A text field that supports multi line editing. + */ +public class TextArea extends AbstractTextField { + + /** + * Constructs an empty TextArea. + */ + public TextArea() { + setValue(""); + } + + /** + * Constructs an empty TextArea with given caption. + * + * @param caption + * the caption for the field. + */ + public TextArea(String caption) { + this(); + setCaption(caption); + } + + /** + * Constructs a TextArea with given property data source. + * + * @param dataSource + * the data source for the field + */ + public TextArea(Property dataSource) { + this(); + setPropertyDataSource(dataSource); + } + + /** + * Constructs a TextArea with given caption and property data source. + * + * @param caption + * the caption for the field + * @param dataSource + * the data source for the field + */ + public TextArea(String caption, Property dataSource) { + this(dataSource); + setCaption(caption); + } + + /** + * Constructs a TextArea with given caption and value. + * + * @param caption + * the caption for the field + * @param value + * the value for the field + */ + public TextArea(String caption, String value) { + this(caption); + setValue(value); + + } + + @Override + protected TextAreaState getState() { + return (TextAreaState) super.getState(); + } + + @Override + protected TextAreaState getState(boolean markAsDirty) { + return (TextAreaState) super.getState(markAsDirty); + } + + /** + * Sets the number of rows in the text area. + * + * @param rows + * the number of rows for this text area. + */ + public void setRows(int rows) { + if (rows < 0) { + rows = 0; + } + getState().rows = rows; + } + + /** + * Gets the number of rows in the text area. + * + * @return number of explicitly set rows. + */ + public int getRows() { + return getState(false).rows; + } + + /** + * Sets the text area's word-wrap mode on or off. + * + * @param wordwrap + * the boolean value specifying if the text area should be in + * word-wrap mode. + */ + public void setWordwrap(boolean wordwrap) { + getState().wordwrap = wordwrap; + } + + /** + * Tests if the text area is in word-wrap mode. + * + * @return true if the component is in word-wrap mode, + * false if not. + */ + public boolean isWordwrap() { + return getState(false).wordwrap; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractField#readDesign(org.jsoup.nodes.Element , + * com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + setValue(DesignFormatter.decodeFromTextNode(design.html()), false, + true); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractTextField#writeDesign(org.jsoup.nodes.Element + * , com.vaadin.ui.declarative.DesignContext) + */ + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + design.html(DesignFormatter.encodeForTextNode(getValue())); + } + + @Override + public void clear() { + setValue(""); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Tree.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Tree.java new file mode 100644 index 0000000000..83f805ffb4 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Tree.java @@ -0,0 +1,1985 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.StringTokenizer; + +import org.jsoup.nodes.Element; + +import com.vaadin.event.Action; +import com.vaadin.event.Action.Handler; +import com.vaadin.event.ContextClickEvent; +import com.vaadin.event.DataBoundTransferable; +import com.vaadin.event.ItemClickEvent; +import com.vaadin.event.ItemClickEvent.ItemClickListener; +import com.vaadin.event.ItemClickEvent.ItemClickNotifier; +import com.vaadin.event.Transferable; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DragSource; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.DropTarget; +import com.vaadin.event.dd.TargetDetails; +import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion; +import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion; +import com.vaadin.event.dd.acceptcriteria.TargetDetailIs; +import com.vaadin.server.KeyMapper; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.server.Resource; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.ui.MultiSelectMode; +import com.vaadin.shared.ui.dd.VerticalDropLocation; +import com.vaadin.shared.ui.tree.TreeConstants; +import com.vaadin.shared.ui.tree.TreeServerRpc; +import com.vaadin.shared.ui.tree.TreeState; +import com.vaadin.ui.Component; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; +import com.vaadin.util.ReflectTools; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.util.ContainerHierarchicalWrapper; +import com.vaadin.v7.data.util.HierarchicalContainer; + +/** + * Tree component. A Tree can be used to select an item (or multiple items) from + * a hierarchical set of items. + * + * @author Vaadin Ltd. + * @since 3.0 + */ +@SuppressWarnings({ "serial", "deprecation" }) +public class Tree extends AbstractSelect implements Container.Hierarchical, + Action.Container, ItemClickNotifier, DragSource, DropTarget { + + /** + * ContextClickEvent for the Tree Component. + * + * @since 7.6 + */ + public static class TreeContextClickEvent extends ContextClickEvent { + + private final Object itemId; + + public TreeContextClickEvent(Tree source, Object itemId, + MouseEventDetails mouseEventDetails) { + super(source, mouseEventDetails); + this.itemId = itemId; + } + + @Override + public Tree getComponent() { + return (Tree) super.getComponent(); + } + + /** + * Returns the item id of context clicked row. + * + * @return item id of clicked row; null if no row is + * present at the location + */ + public Object getItemId() { + return itemId; + } + } + + /* Private members */ + + private static final String NULL_ALT_EXCEPTION_MESSAGE = "Parameter 'altText' needs to be non null"; + + /** + * Item icons alt texts. + */ + private final HashMap itemIconAlts = new HashMap(); + + /** + * Set of expanded nodes. + */ + private HashSet expanded = new HashSet(); + + /** + * List of action handlers. + */ + private LinkedList actionHandlers = null; + + /** + * Action mapper. + */ + private KeyMapper actionMapper = null; + + /** + * Is the tree selectable on the client side. + */ + private boolean selectable = true; + + /** + * Flag to indicate sub-tree loading + */ + private boolean partialUpdate = false; + + /** + * Holds a itemId which was recently expanded + */ + private Object expandedItemId; + + /** + * a flag which indicates initial paint. After this flag set true partial + * updates are allowed. + */ + private boolean initialPaint = true; + + /** + * Item tooltip generator + */ + private ItemDescriptionGenerator itemDescriptionGenerator; + + /** + * Supported drag modes for Tree. + */ + public enum TreeDragMode { + /** + * When drag mode is NONE, dragging from Tree is not supported. Browsers + * may still support selecting text/icons from Tree which can initiate + * HTML 5 style drag and drop operation. + */ + NONE, + /** + * When drag mode is NODE, users can initiate drag from Tree nodes that + * represent {@link Item}s in from the backed {@link Container}. + */ + NODE + // , SUBTREE + } + + private TreeDragMode dragMode = TreeDragMode.NONE; + + private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT; + + /* Tree constructors */ + + /** + * Creates a new empty tree. + */ + public Tree() { + this(null); + + registerRpc(new TreeServerRpc() { + @Override + public void contextClick(String rowKey, MouseEventDetails details) { + fireEvent(new TreeContextClickEvent(Tree.this, + itemIdMapper.get(rowKey), details)); + } + }); + } + + /** + * Creates a new empty tree with caption. + * + * @param caption + */ + public Tree(String caption) { + this(caption, new HierarchicalContainer()); + } + + /** + * Creates a new tree with caption and connect it to a Container. + * + * @param caption + * @param dataSource + */ + public Tree(String caption, Container dataSource) { + super(caption, dataSource); + } + + @Override + public void setItemIcon(Object itemId, Resource icon) { + setItemIcon(itemId, icon, ""); + } + + /** + * Sets the icon for an item. + * + * @param itemId + * the id of the item to be assigned an icon. + * @param icon + * the icon to use or null. + * + * @param altText + * the alternative text for the icon + */ + public void setItemIcon(Object itemId, Resource icon, String altText) { + if (itemId != null) { + super.setItemIcon(itemId, icon); + + if (icon == null) { + itemIconAlts.remove(itemId); + } else if (altText == null) { + throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE); + } else { + itemIconAlts.put(itemId, altText); + } + markAsDirty(); + } + } + + /** + * Set the alternate text for an item. + * + * Used when the item has an icon. + * + * @param itemId + * the id of the item to be assigned an icon. + * @param altText + * the alternative text for the icon + */ + public void setItemIconAlternateText(Object itemId, String altText) { + if (itemId != null) { + if (altText == null) { + throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE); + } else { + itemIconAlts.put(itemId, altText); + } + } + } + + /** + * Return the alternate text of an icon in a tree item. + * + * @param itemId + * Object with the ID of the item + * @return String with the alternate text of the icon, or null when no icon + * was set + */ + public String getItemIconAlternateText(Object itemId) { + String storedAlt = itemIconAlts.get(itemId); + return storedAlt == null ? "" : storedAlt; + } + + /* Expanding and collapsing */ + + /** + * Check is an item is expanded + * + * @param itemId + * the item id. + * @return true iff the item is expanded. + */ + public boolean isExpanded(Object itemId) { + return expanded.contains(itemId); + } + + /** + * Expands an item. + * + * @param itemId + * the item id. + * @return True iff the expand operation succeeded + */ + public boolean expandItem(Object itemId) { + boolean success = expandItem(itemId, true); + markAsDirty(); + return success; + } + + /** + * Expands an item. + * + * @param itemId + * the item id. + * @param sendChildTree + * flag to indicate if client needs subtree or not (may be + * cached) + * @return True if the expand operation succeeded + */ + private boolean expandItem(Object itemId, boolean sendChildTree) { + + // Succeeds if the node is already expanded + if (isExpanded(itemId)) { + return true; + } + + // Nodes that can not have children are not expandable + if (!areChildrenAllowed(itemId)) { + return false; + } + + // Expands + expanded.add(itemId); + + expandedItemId = itemId; + if (initialPaint) { + markAsDirty(); + } else if (sendChildTree) { + requestPartialRepaint(); + } + fireExpandEvent(itemId); + + return true; + } + + @Override + public void markAsDirty() { + super.markAsDirty(); + partialUpdate = false; + } + + private void requestPartialRepaint() { + super.markAsDirty(); + partialUpdate = true; + } + + /** + * Expands the items recursively + * + * Expands all the children recursively starting from an item. Operation + * succeeds only if all expandable items are expanded. + * + * @param startItemId + * @return True iff the expand operation succeeded + */ + public boolean expandItemsRecursively(Object startItemId) { + + boolean result = true; + + // Initial stack + final Stack todo = new Stack(); + todo.add(startItemId); + + // Expands recursively + while (!todo.isEmpty()) { + final Object id = todo.pop(); + if (areChildrenAllowed(id) && !expandItem(id, false)) { + result = false; + } + if (hasChildren(id)) { + todo.addAll(getChildren(id)); + } + } + markAsDirty(); + return result; + } + + /** + * Collapses an item. + * + * @param itemId + * the item id. + * @return True iff the collapse operation succeeded + */ + public boolean collapseItem(Object itemId) { + + // Succeeds if the node is already collapsed + if (!isExpanded(itemId)) { + return true; + } + + // Collapse + expanded.remove(itemId); + markAsDirty(); + fireCollapseEvent(itemId); + + return true; + } + + /** + * Collapses the items recursively. + * + * Collapse all the children recursively starting from an item. Operation + * succeeds only if all expandable items are collapsed. + * + * @param startItemId + * @return True iff the collapse operation succeeded + */ + public boolean collapseItemsRecursively(Object startItemId) { + + boolean result = true; + + // Initial stack + final Stack todo = new Stack(); + todo.add(startItemId); + + // Collapse recursively + while (!todo.isEmpty()) { + final Object id = todo.pop(); + if (areChildrenAllowed(id) && !collapseItem(id)) { + result = false; + } + if (hasChildren(id)) { + todo.addAll(getChildren(id)); + } + } + + return result; + } + + /** + * Returns the current selectable state. Selectable determines if the a node + * can be selected on the client side. Selectable does not affect + * {@link #setValue(Object)} or {@link #select(Object)}. + * + *

    + * The tree is selectable by default. + *

    + * + * @return the current selectable state. + */ + public boolean isSelectable() { + return selectable; + } + + /** + * Sets the selectable state. Selectable determines if the a node can be + * selected on the client side. Selectable does not affect + * {@link #setValue(Object)} or {@link #select(Object)}. + * + *

    + * The tree is selectable by default. + *

    + * + * @param selectable + * The new selectable state. + */ + public void setSelectable(boolean selectable) { + if (this.selectable != selectable) { + this.selectable = selectable; + markAsDirty(); + } + } + + /** + * Sets the behavior of the multiselect mode + * + * @param mode + * The mode to set + */ + public void setMultiselectMode(MultiSelectMode mode) { + if (multiSelectMode != mode && mode != null) { + multiSelectMode = mode; + markAsDirty(); + } + } + + /** + * Returns the mode the multiselect is in. The mode controls how + * multiselection can be done. + * + * @return The mode + */ + public MultiSelectMode getMultiselectMode() { + return multiSelectMode; + } + + /* Component API */ + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractSelect#changeVariables(java.lang.Object, + * java.util.Map) + */ + @Override + public void changeVariables(Object source, Map variables) { + + if (variables.containsKey("clickedKey")) { + String key = (String) variables.get("clickedKey"); + + Object id = itemIdMapper.get(key); + MouseEventDetails details = MouseEventDetails + .deSerialize((String) variables.get("clickEvent")); + Item item = getItem(id); + if (item != null) { + fireEvent(new ItemClickEvent(this, item, id, null, details)); + } + } + + if (!isSelectable() && variables.containsKey("selected")) { + // Not-selectable is a special case, AbstractSelect does not support + // TODO could be optimized. + variables = new HashMap(variables); + variables.remove("selected"); + } + + // Collapses the nodes + if (variables.containsKey("collapse")) { + final String[] keys = (String[]) variables.get("collapse"); + for (int i = 0; i < keys.length; i++) { + final Object id = itemIdMapper.get(keys[i]); + if (id != null && isExpanded(id)) { + expanded.remove(id); + if (expandedItemId == id) { + expandedItemId = null; + } + fireCollapseEvent(id); + } + } + } + + // Expands the nodes + if (variables.containsKey("expand")) { + boolean sendChildTree = false; + if (variables.containsKey("requestChildTree")) { + sendChildTree = true; + } + final String[] keys = (String[]) variables.get("expand"); + for (int i = 0; i < keys.length; i++) { + final Object id = itemIdMapper.get(keys[i]); + if (id != null) { + expandItem(id, sendChildTree); + } + } + } + + // AbstractSelect cannot handle multiselection so we handle + // it ourself + if (variables.containsKey("selected") && isMultiSelect() + && multiSelectMode == MultiSelectMode.DEFAULT) { + handleSelectedItems(variables); + variables = new HashMap(variables); + variables.remove("selected"); + } + + // Selections are handled by the select component + super.changeVariables(source, variables); + + // Actions + if (variables.containsKey("action")) { + final StringTokenizer st = new StringTokenizer( + (String) variables.get("action"), ","); + if (st.countTokens() == 2) { + final Object itemId = itemIdMapper.get(st.nextToken()); + final Action action = actionMapper.get(st.nextToken()); + if (action != null && (itemId == null || containsId(itemId)) + && actionHandlers != null) { + for (Handler ah : actionHandlers) { + ah.handleAction(action, this, itemId); + } + } + } + } + } + + /** + * Handles the selection + * + * @param variables + * The variables sent to the server from the client + */ + private void handleSelectedItems(Map variables) { + final String[] ka = (String[]) variables.get("selected"); + + // Converts the key-array to id-set + final LinkedList s = new LinkedList(); + for (int i = 0; i < ka.length; i++) { + final Object id = itemIdMapper.get(ka[i]); + if (!isNullSelectionAllowed() + && (id == null || id == getNullSelectionItemId())) { + // skip empty selection if nullselection is not allowed + markAsDirty(); + } else if (id != null && containsId(id)) { + s.add(id); + } + } + + if (!isNullSelectionAllowed() && s.size() < 1) { + // empty selection not allowed, keep old value + markAsDirty(); + return; + } + + setValue(s, true); + } + + /** + * Paints any needed component-specific things to the given UIDL stream. + * + * @see com.vaadin.ui.AbstractComponent#paintContent(PaintTarget) + */ + @Override + public void paintContent(PaintTarget target) throws PaintException { + initialPaint = false; + + if (partialUpdate) { + target.addAttribute("partialUpdate", true); + target.addAttribute("rootKey", itemIdMapper.key(expandedItemId)); + } else { + getCaptionChangeListener().clear(); + + // The tab ordering number + if (getTabIndex() > 0) { + target.addAttribute("tabindex", getTabIndex()); + } + + // Paint tree attributes + if (isSelectable()) { + target.addAttribute("selectmode", + (isMultiSelect() ? "multi" : "single")); + if (isMultiSelect()) { + target.addAttribute("multiselectmode", + multiSelectMode.toString()); + } + } else { + target.addAttribute("selectmode", "none"); + } + if (isNewItemsAllowed()) { + target.addAttribute("allownewitem", true); + } + + if (isNullSelectionAllowed()) { + target.addAttribute("nullselect", true); + } + + if (dragMode != TreeDragMode.NONE) { + target.addAttribute("dragMode", dragMode.ordinal()); + } + + if (isHtmlContentAllowed()) { + target.addAttribute(TreeConstants.ATTRIBUTE_HTML_ALLOWED, true); + } + + } + + // Initialize variables + final Set actionSet = new LinkedHashSet(); + + // rendered selectedKeys + LinkedList selectedKeys = new LinkedList(); + + final LinkedList expandedKeys = new LinkedList(); + + // Iterates through hierarchical tree using a stack of iterators + final Stack> iteratorStack = new Stack>(); + Collection ids; + if (partialUpdate) { + ids = getChildren(expandedItemId); + } else { + ids = rootItemIds(); + } + + if (ids != null) { + iteratorStack.push(ids.iterator()); + } + + /* + * Body actions - Actions which has the target null and can be invoked + * by right clicking on the Tree body + */ + if (actionHandlers != null) { + final ArrayList keys = new ArrayList(); + for (Handler ah : actionHandlers) { + + // Getting action for the null item, which in this case + // means the body item + final Action[] aa = ah.getActions(null, this); + if (aa != null) { + for (int ai = 0; ai < aa.length; ai++) { + final String akey = actionMapper.key(aa[ai]); + actionSet.add(aa[ai]); + keys.add(akey); + } + } + } + target.addAttribute("alb", keys.toArray()); + } + + while (!iteratorStack.isEmpty()) { + + // Gets the iterator for current tree level + final Iterator i = iteratorStack.peek(); + + // If the level is finished, back to previous tree level + if (!i.hasNext()) { + + // Removes used iterator from the stack + iteratorStack.pop(); + + // Closes node + if (!iteratorStack.isEmpty()) { + target.endTag("node"); + } + } + + // Adds the item on current level + else { + final Object itemId = i.next(); + + // Starts the item / node + final boolean isNode = areChildrenAllowed(itemId); + if (isNode) { + target.startTag("node"); + } else { + target.startTag("leaf"); + } + + if (itemStyleGenerator != null) { + String stylename = itemStyleGenerator.getStyle(this, + itemId); + if (stylename != null) { + target.addAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE, + stylename); + } + } + + if (itemDescriptionGenerator != null) { + String description = itemDescriptionGenerator + .generateDescription(this, itemId, null); + if (description != null && !description.equals("")) { + target.addAttribute("descr", description); + } + } + + // Adds the attributes + target.addAttribute(TreeConstants.ATTRIBUTE_NODE_CAPTION, + getItemCaption(itemId)); + final Resource icon = getItemIcon(itemId); + if (icon != null) { + target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON, + getItemIcon(itemId)); + target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT, + getItemIconAlternateText(itemId)); + } + final String key = itemIdMapper.key(itemId); + target.addAttribute("key", key); + if (isSelected(itemId)) { + target.addAttribute("selected", true); + selectedKeys.add(key); + } + if (areChildrenAllowed(itemId) && isExpanded(itemId)) { + target.addAttribute("expanded", true); + expandedKeys.add(key); + } + + // Add caption change listener + getCaptionChangeListener().addNotifierForItem(itemId); + + // Actions + if (actionHandlers != null) { + final ArrayList keys = new ArrayList(); + final Iterator ahi = actionHandlers + .iterator(); + while (ahi.hasNext()) { + final Action[] aa = ahi.next().getActions(itemId, this); + if (aa != null) { + for (int ai = 0; ai < aa.length; ai++) { + final String akey = actionMapper.key(aa[ai]); + actionSet.add(aa[ai]); + keys.add(akey); + } + } + } + target.addAttribute("al", keys.toArray()); + } + + // Adds the children if expanded, or close the tag + if (isExpanded(itemId) && hasChildren(itemId) + && areChildrenAllowed(itemId)) { + iteratorStack.push(getChildren(itemId).iterator()); + } else { + if (isNode) { + target.endTag("node"); + } else { + target.endTag("leaf"); + } + } + } + } + + // Actions + if (!actionSet.isEmpty()) { + target.addVariable(this, "action", ""); + target.startTag("actions"); + final Iterator i = actionSet.iterator(); + while (i.hasNext()) { + final Action a = i.next(); + target.startTag("action"); + if (a.getCaption() != null) { + target.addAttribute(TreeConstants.ATTRIBUTE_ACTION_CAPTION, + a.getCaption()); + } + if (a.getIcon() != null) { + target.addAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON, + a.getIcon()); + } + target.addAttribute("key", actionMapper.key(a)); + target.endTag("action"); + } + target.endTag("actions"); + } + + if (partialUpdate) { + partialUpdate = false; + } else { + // Selected + target.addVariable(this, "selected", + selectedKeys.toArray(new String[selectedKeys.size()])); + + // Expand and collapse + target.addVariable(this, "expand", new String[] {}); + target.addVariable(this, "collapse", new String[] {}); + + // New items + target.addVariable(this, "newitem", new String[] {}); + + if (dropHandler != null) { + dropHandler.getAcceptCriterion().paint(target); + } + + } + } + + /* Container.Hierarchical API */ + + /** + * Tests if the Item with given ID can have any children. + * + * @see com.vaadin.v7.data.Container.Hierarchical#areChildrenAllowed(Object) + */ + @Override + public boolean areChildrenAllowed(Object itemId) { + return ((Container.Hierarchical) items).areChildrenAllowed(itemId); + } + + /** + * Gets the IDs of all Items that are children of the specified Item. + * + * @see com.vaadin.v7.data.Container.Hierarchical#getChildren(Object) + */ + @Override + public Collection getChildren(Object itemId) { + return ((Container.Hierarchical) items).getChildren(itemId); + } + + /** + * Gets the ID of the parent Item of the specified Item. + * + * @see com.vaadin.v7.data.Container.Hierarchical#getParent(Object) + */ + @Override + public Object getParent(Object itemId) { + return ((Container.Hierarchical) items).getParent(itemId); + } + + /** + * Tests if the Item specified with itemId has child Items. + * + * @see com.vaadin.v7.data.Container.Hierarchical#hasChildren(Object) + */ + @Override + public boolean hasChildren(Object itemId) { + return ((Container.Hierarchical) items).hasChildren(itemId); + } + + /** + * Tests if the Item specified with itemId is a root Item. + * + * @see com.vaadin.v7.data.Container.Hierarchical#isRoot(Object) + */ + @Override + public boolean isRoot(Object itemId) { + return ((Container.Hierarchical) items).isRoot(itemId); + } + + /** + * Gets the IDs of all Items in the container that don't have a parent. + * + * @see com.vaadin.v7.data.Container.Hierarchical#rootItemIds() + */ + @Override + public Collection rootItemIds() { + return ((Container.Hierarchical) items).rootItemIds(); + } + + /** + * Sets the given Item's capability to have children. + * + * @see com.vaadin.v7.data.Container.Hierarchical#setChildrenAllowed(Object, + * boolean) + */ + @Override + public boolean setChildrenAllowed(Object itemId, + boolean areChildrenAllowed) { + final boolean success = ((Container.Hierarchical) items) + .setChildrenAllowed(itemId, areChildrenAllowed); + if (success) { + markAsDirty(); + } + return success; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Hierarchical#setParent(java.lang.Object , + * java.lang.Object) + */ + @Override + public boolean setParent(Object itemId, Object newParentId) { + final boolean success = ((Container.Hierarchical) items) + .setParent(itemId, newParentId); + if (success) { + markAsDirty(); + } + return success; + } + + /* Overriding select behavior */ + + /** + * Sets the Container that serves as the data source of the viewer. + * + * @see com.vaadin.v7.data.Container.Viewer#setContainerDataSource(Container) + */ + @Override + public void setContainerDataSource(Container newDataSource) { + if (newDataSource == null) { + newDataSource = new HierarchicalContainer(); + } + + // Assure that the data source is ordered by making unordered + // containers ordered by wrapping them + if (Container.Hierarchical.class + .isAssignableFrom(newDataSource.getClass())) { + super.setContainerDataSource(newDataSource); + } else { + super.setContainerDataSource( + new ContainerHierarchicalWrapper(newDataSource)); + } + + /* + * Ensure previous expanded items are cleaned up if they don't exist in + * the new container + */ + if (expanded != null) { + /* + * We need to check that the expanded-field is not null since + * setContainerDataSource() is called from the parent constructor + * (AbstractSelect()) and at that time the expanded field is not yet + * initialized. + */ + cleanupExpandedItems(); + } + + } + + @Override + public void containerItemSetChange( + com.vaadin.v7.data.Container.ItemSetChangeEvent event) { + super.containerItemSetChange(event); + if (getContainerDataSource() instanceof Filterable) { + boolean hasFilters = !((Filterable) getContainerDataSource()) + .getContainerFilters().isEmpty(); + if (!hasFilters) { + /* + * If Container is not filtered then the itemsetchange is caused + * by either adding or removing items to the container. To + * prevent a memory leak we should cleanup the expanded list + * from items which was removed. + * + * However, there will still be a leak if the container is + * filtered to show only a subset of the items in the tree and + * later unfiltered items are removed from the container. In + * that case references to the unfiltered item ids will remain + * in the expanded list until the Tree instance is removed and + * the list is destroyed, or the container data source is + * replaced/updated. To force the removal of the removed items + * the application developer needs to a) remove the container + * filters temporarly or b) re-apply the container datasource + * using setContainerDataSource(getContainerDataSource()) + */ + cleanupExpandedItems(); + } + } + + } + + /* Expand event and listener */ + + /** + * Event to fired when a node is expanded. ExapandEvent is fired when a node + * is to be expanded. it can me used to dynamically fill the sub-nodes of + * the node. + * + * @author Vaadin Ltd. + * @since 3.0 + */ + public static class ExpandEvent extends Component.Event { + + private final Object expandedItemId; + + /** + * New instance of options change event + * + * @param source + * the Source of the event. + * @param expandedItemId + */ + public ExpandEvent(Component source, Object expandedItemId) { + super(source); + this.expandedItemId = expandedItemId; + } + + /** + * Node where the event occurred. + * + * @return the Source of the event. + */ + public Object getItemId() { + return expandedItemId; + } + } + + /** + * Expand event listener. + * + * @author Vaadin Ltd. + * @since 3.0 + */ + public interface ExpandListener extends Serializable { + + public static final Method EXPAND_METHOD = ReflectTools.findMethod( + ExpandListener.class, "nodeExpand", ExpandEvent.class); + + /** + * A node has been expanded. + * + * @param event + * the Expand event. + */ + public void nodeExpand(ExpandEvent event); + } + + /** + * Adds the expand listener. + * + * @param listener + * the Listener to be added. + */ + public void addExpandListener(ExpandListener listener) { + addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addExpandListener(ExpandListener)} + **/ + @Deprecated + public void addListener(ExpandListener listener) { + addExpandListener(listener); + } + + /** + * Removes the expand listener. + * + * @param listener + * the Listener to be removed. + */ + public void removeExpandListener(ExpandListener listener) { + removeListener(ExpandEvent.class, listener, + ExpandListener.EXPAND_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeExpandListener(ExpandListener)} + **/ + @Deprecated + public void removeListener(ExpandListener listener) { + removeExpandListener(listener); + } + + /** + * Emits the expand event. + * + * @param itemId + * the item id. + */ + protected void fireExpandEvent(Object itemId) { + fireEvent(new ExpandEvent(this, itemId)); + } + + /* Collapse event */ + + /** + * Collapse event + * + * @author Vaadin Ltd. + * @since 3.0 + */ + public static class CollapseEvent extends Component.Event { + + private final Object collapsedItemId; + + /** + * New instance of options change event. + * + * @param source + * the Source of the event. + * @param collapsedItemId + */ + public CollapseEvent(Component source, Object collapsedItemId) { + super(source); + this.collapsedItemId = collapsedItemId; + } + + /** + * Gets tge Collapsed Item id. + * + * @return the collapsed item id. + */ + public Object getItemId() { + return collapsedItemId; + } + } + + /** + * Collapse event listener. + * + * @author Vaadin Ltd. + * @since 3.0 + */ + public interface CollapseListener extends Serializable { + + public static final Method COLLAPSE_METHOD = ReflectTools.findMethod( + CollapseListener.class, "nodeCollapse", CollapseEvent.class); + + /** + * A node has been collapsed. + * + * @param event + * the Collapse event. + */ + public void nodeCollapse(CollapseEvent event); + } + + /** + * Adds the collapse listener. + * + * @param listener + * the Listener to be added. + */ + public void addCollapseListener(CollapseListener listener) { + addListener(CollapseEvent.class, listener, + CollapseListener.COLLAPSE_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addCollapseListener(CollapseListener)} + **/ + @Deprecated + public void addListener(CollapseListener listener) { + addCollapseListener(listener); + } + + /** + * Removes the collapse listener. + * + * @param listener + * the Listener to be removed. + */ + public void removeCollapseListener(CollapseListener listener) { + removeListener(CollapseEvent.class, listener, + CollapseListener.COLLAPSE_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeCollapseListener(CollapseListener)} + **/ + @Deprecated + public void removeListener(CollapseListener listener) { + removeCollapseListener(listener); + } + + /** + * Emits collapse event. + * + * @param itemId + * the item id. + */ + protected void fireCollapseEvent(Object itemId) { + fireEvent(new CollapseEvent(this, itemId)); + } + + /* Action container */ + + /** + * Adds an action handler. + * + * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler) + */ + @Override + public void addActionHandler(Action.Handler actionHandler) { + + if (actionHandler != null) { + + if (actionHandlers == null) { + actionHandlers = new LinkedList(); + actionMapper = new KeyMapper(); + } + + if (!actionHandlers.contains(actionHandler)) { + actionHandlers.add(actionHandler); + markAsDirty(); + } + } + } + + /** + * Removes an action handler. + * + * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler) + */ + @Override + public void removeActionHandler(Action.Handler actionHandler) { + + if (actionHandlers != null && actionHandlers.contains(actionHandler)) { + + actionHandlers.remove(actionHandler); + + if (actionHandlers.isEmpty()) { + actionHandlers = null; + actionMapper = null; + } + + markAsDirty(); + } + } + + /** + * Removes all action handlers + */ + public void removeAllActionHandlers() { + actionHandlers = null; + actionMapper = null; + markAsDirty(); + } + + /** + * Gets the visible item ids. + * + * @see com.vaadin.v7.ui.Select#getVisibleItemIds() + */ + @Override + public Collection getVisibleItemIds() { + + final LinkedList visible = new LinkedList(); + + // Iterates trough hierarchical tree using a stack of iterators + final Stack> iteratorStack = new Stack>(); + final Collection ids = rootItemIds(); + if (ids != null) { + iteratorStack.push(ids.iterator()); + } + while (!iteratorStack.isEmpty()) { + + // Gets the iterator for current tree level + final Iterator i = iteratorStack.peek(); + + // If the level is finished, back to previous tree level + if (!i.hasNext()) { + + // Removes used iterator from the stack + iteratorStack.pop(); + } + + // Adds the item on current level + else { + final Object itemId = i.next(); + + visible.add(itemId); + + // Adds children if expanded, or close the tag + if (isExpanded(itemId) && hasChildren(itemId)) { + iteratorStack.push(getChildren(itemId).iterator()); + } + } + } + + return visible; + } + + /** + * Tree does not support setNullSelectionItemId. + * + * @see com.vaadin.v7.ui.AbstractSelect#setNullSelectionItemId(java.lang.Object) + */ + @Override + public void setNullSelectionItemId(Object nullSelectionItemId) + throws UnsupportedOperationException { + if (nullSelectionItemId != null) { + throw new UnsupportedOperationException(); + } + + } + + /** + * Adding new items is not supported. + * + * @throws UnsupportedOperationException + * if set to true. + * @see com.vaadin.v7.ui.Select#setNewItemsAllowed(boolean) + */ + @Override + public void setNewItemsAllowed(boolean allowNewOptions) + throws UnsupportedOperationException { + if (allowNewOptions) { + throw new UnsupportedOperationException(); + } + } + + private ItemStyleGenerator itemStyleGenerator; + + private DropHandler dropHandler; + + private boolean htmlContentAllowed; + + @Override + public void addItemClickListener(ItemClickListener listener) { + addListener(TreeConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, + listener, ItemClickEvent.ITEM_CLICK_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addItemClickListener(ItemClickListener)} + **/ + @Override + @Deprecated + public void addListener(ItemClickListener listener) { + addItemClickListener(listener); + } + + @Override + public void removeItemClickListener(ItemClickListener listener) { + removeListener(TreeConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, + listener); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeItemClickListener(ItemClickListener)} + **/ + @Override + @Deprecated + public void removeListener(ItemClickListener listener) { + removeItemClickListener(listener); + } + + /** + * Sets the {@link ItemStyleGenerator} to be used with this tree. + * + * @param itemStyleGenerator + * item style generator or null to remove generator + */ + public void setItemStyleGenerator(ItemStyleGenerator itemStyleGenerator) { + if (this.itemStyleGenerator != itemStyleGenerator) { + this.itemStyleGenerator = itemStyleGenerator; + markAsDirty(); + } + } + + /** + * @return the current {@link ItemStyleGenerator} for this tree. Null if + * {@link ItemStyleGenerator} is not set. + */ + public ItemStyleGenerator getItemStyleGenerator() { + return itemStyleGenerator; + } + + /** + * ItemStyleGenerator can be used to add custom styles to tree items. The + * CSS class name that will be added to the item content is + * v-tree-node-[style name]. + */ + public interface ItemStyleGenerator extends Serializable { + + /** + * Called by Tree when an item is painted. + * + * @param source + * the source Tree + * @param itemId + * The itemId of the item to be painted + * @return The style name to add to this item. (the CSS class name will + * be v-tree-node-[style name] + */ + public abstract String getStyle(Tree source, Object itemId); + } + + // Overriden so javadoc comes from Container.Hierarchical + @Override + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + return super.removeItem(itemId); + } + + @Override + public DropHandler getDropHandler() { + return dropHandler; + } + + public void setDropHandler(DropHandler dropHandler) { + this.dropHandler = dropHandler; + } + + /** + * A {@link TargetDetails} implementation with Tree specific api. + * + * @since 6.3 + */ + public class TreeTargetDetails extends AbstractSelectTargetDetails { + + TreeTargetDetails(Map rawVariables) { + super(rawVariables); + } + + @Override + public Tree getTarget() { + return (Tree) super.getTarget(); + } + + /** + * If the event is on a node that can not have children (see + * {@link Tree#areChildrenAllowed(Object)}), this method returns the + * parent item id of the target item (see {@link #getItemIdOver()} ). + * The identifier of the parent node is also returned if the cursor is + * on the top part of node. Else this method returns the same as + * {@link #getItemIdOver()}. + *

    + * In other words this method returns the identifier of the "folder" + * into the drag operation is targeted. + *

    + * If the method returns null, the current target is on a root node or + * on other undefined area over the tree component. + *

    + * The default Tree implementation marks the targetted tree node with + * CSS classnames v-tree-node-dragfolder and + * v-tree-node-caption-dragfolder (for the caption element). + */ + public Object getItemIdInto() { + + Object itemIdOver = getItemIdOver(); + if (areChildrenAllowed(itemIdOver) + && getDropLocation() == VerticalDropLocation.MIDDLE) { + return itemIdOver; + } + return getParent(itemIdOver); + } + + /** + * If drop is targeted into "folder node" (see {@link #getItemIdInto()} + * ), this method returns the item id of the node after the drag was + * targeted. This method is useful when implementing drop into specific + * location (between specific nodes) in tree. + * + * @return the id of the item after the user targets the drop or null if + * "target" is a first item in node list (or the first in root + * node list) + */ + public Object getItemIdAfter() { + Object itemIdOver = getItemIdOver(); + Object itemIdInto2 = getItemIdInto(); + if (itemIdOver.equals(itemIdInto2)) { + return null; + } + VerticalDropLocation dropLocation = getDropLocation(); + if (VerticalDropLocation.TOP == dropLocation) { + // if on top of the caption area, add before + Collection children; + Object itemIdInto = getItemIdInto(); + if (itemIdInto != null) { + // seek the previous from child list + children = getChildren(itemIdInto); + } else { + children = rootItemIds(); + } + Object ref = null; + for (Object object : children) { + if (object.equals(itemIdOver)) { + return ref; + } + ref = object; + } + } + return itemIdOver; + } + + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map) + */ + @Override + public TreeTargetDetails translateDropTargetDetails( + Map clientVariables) { + return new TreeTargetDetails(clientVariables); + } + + /** + * Helper API for {@link TreeDropCriterion} + * + * @param itemId + * @return + */ + private String key(Object itemId) { + return itemIdMapper.key(itemId); + } + + /** + * Sets the drag mode that controls how Tree behaves as a {@link DragSource} + * . + * + * @param dragMode + */ + public void setDragMode(TreeDragMode dragMode) { + this.dragMode = dragMode; + markAsDirty(); + } + + /** + * @return the drag mode that controls how Tree behaves as a + * {@link DragSource}. + * + * @see TreeDragMode + */ + public TreeDragMode getDragMode() { + return dragMode; + } + + /** + * Concrete implementation of {@link DataBoundTransferable} for data + * transferred from a tree. + * + * @see {@link DataBoundTransferable}. + * + * @since 6.3 + */ + protected class TreeTransferable extends DataBoundTransferable { + + public TreeTransferable(Component sourceComponent, + Map rawVariables) { + super(sourceComponent, rawVariables); + } + + @Override + public Object getItemId() { + return getData("itemId"); + } + + @Override + public Object getPropertyId() { + return getItemCaptionPropertyId(); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.event.dd.DragSource#getTransferable(java.util.Map) + */ + @Override + public Transferable getTransferable(Map payload) { + TreeTransferable transferable = new TreeTransferable(this, payload); + // updating drag source variables + Object object = payload.get("itemId"); + if (object != null) { + transferable.setData("itemId", itemIdMapper.get((String) object)); + } + + return transferable; + } + + /** + * Lazy loading accept criterion for Tree. Accepted target nodes are loaded + * from server once per drag and drop operation. Developer must override one + * method that decides accepted tree nodes for the whole Tree. + * + *

    + * Initially pretty much no data is sent to client. On first required + * criterion check (per drag request) the client side data structure is + * initialized from server and no subsequent requests requests are needed + * during that drag and drop operation. + */ + public static abstract class TreeDropCriterion extends ServerSideCriterion { + + private Tree tree; + + private Set allowedItemIds; + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.acceptCriteria.ServerSideCriterion#getIdentifier + * () + */ + @Override + protected String getIdentifier() { + return TreeDropCriterion.class.getCanonicalName(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#accepts(com.vaadin + * .event.dd.DragAndDropEvent) + */ + @Override + public boolean accept(DragAndDropEvent dragEvent) { + AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent + .getTargetDetails(); + tree = (Tree) dragEvent.getTargetDetails().getTarget(); + allowedItemIds = getAllowedItemIds(dragEvent, tree); + + return allowedItemIds.contains(dropTargetData.getItemIdOver()); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#paintResponse( + * com.vaadin.server.PaintTarget) + */ + @Override + public void paintResponse(PaintTarget target) throws PaintException { + /* + * send allowed nodes to client so subsequent requests can be + * avoided + */ + Object[] array = allowedItemIds.toArray(); + for (int i = 0; i < array.length; i++) { + String key = tree.key(array[i]); + array[i] = key; + } + target.addAttribute("allowedIds", array); + } + + protected abstract Set getAllowedItemIds( + DragAndDropEvent dragEvent, Tree tree); + + } + + /** + * A criterion that accepts {@link Transferable} only directly on a tree + * node that can have children. + *

    + * Class is singleton, use {@link TargetItemAllowsChildren#get()} to get the + * instance. + * + * @see Tree#setChildrenAllowed(Object, boolean) + * + * @since 6.3 + */ + public static class TargetItemAllowsChildren extends TargetDetailIs { + + private static TargetItemAllowsChildren instance = new TargetItemAllowsChildren(); + + public static TargetItemAllowsChildren get() { + return instance; + } + + private TargetItemAllowsChildren() { + super("itemIdOverIsNode", Boolean.TRUE); + } + + /* + * Uses enhanced server side check + */ + @Override + public boolean accept(DragAndDropEvent dragEvent) { + try { + // must be over tree node and in the middle of it (not top or + // bottom + // part) + TreeTargetDetails eventDetails = (TreeTargetDetails) dragEvent + .getTargetDetails(); + + Object itemIdOver = eventDetails.getItemIdOver(); + if (!eventDetails.getTarget().areChildrenAllowed(itemIdOver)) { + return false; + } + // return true if directly over + return eventDetails + .getDropLocation() == VerticalDropLocation.MIDDLE; + } catch (Exception e) { + return false; + } + } + + } + + /** + * An accept criterion that checks the parent node (or parent hierarchy) for + * the item identifier given in constructor. If the parent is found, content + * is accepted. Criterion can be used to accepts drags on a specific sub + * tree only. + *

    + * The root items is also consider to be valid target. + */ + public class TargetInSubtree extends ClientSideCriterion { + + private Object rootId; + private int depthToCheck = -1; + + /** + * Constructs a criteria that accepts the drag if the targeted Item is a + * descendant of Item identified by given id + * + * @param parentItemId + * the item identifier of the parent node + */ + public TargetInSubtree(Object parentItemId) { + rootId = parentItemId; + } + + /** + * Constructs a criteria that accepts drops within given level below the + * subtree root identified by given id. + * + * @param rootId + * the item identifier to be sought for + * @param depthToCheck + * the depth that tree is traversed upwards to seek for the + * parent, -1 means that the whole structure should be + * checked + */ + public TargetInSubtree(Object rootId, int depthToCheck) { + this.rootId = rootId; + this.depthToCheck = depthToCheck; + } + + @Override + public boolean accept(DragAndDropEvent dragEvent) { + try { + TreeTargetDetails eventDetails = (TreeTargetDetails) dragEvent + .getTargetDetails(); + + if (eventDetails.getItemIdOver() != null) { + Object itemId = eventDetails.getItemIdOver(); + int i = 0; + while (itemId != null + && (depthToCheck == -1 || i <= depthToCheck)) { + if (itemId.equals(rootId)) { + return true; + } + itemId = getParent(itemId); + i++; + } + } + return false; + } catch (Exception e) { + return false; + } + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + super.paintContent(target); + target.addAttribute("depth", depthToCheck); + target.addAttribute("key", key(rootId)); + } + } + + /** + * Set the item description generator which generates tooltips for the tree + * items + * + * @param generator + * The generator to use or null to disable + */ + public void setItemDescriptionGenerator( + ItemDescriptionGenerator generator) { + if (generator != itemDescriptionGenerator) { + itemDescriptionGenerator = generator; + markAsDirty(); + } + } + + /** + * Get the item description generator which generates tooltips for tree + * items + */ + public ItemDescriptionGenerator getItemDescriptionGenerator() { + return itemDescriptionGenerator; + } + + private void cleanupExpandedItems() { + Set removedItemIds = new HashSet(); + for (Object expandedItemId : expanded) { + if (getItem(expandedItemId) == null) { + removedItemIds.add(expandedItemId); + if (this.expandedItemId == expandedItemId) { + this.expandedItemId = null; + } + } + } + expanded.removeAll(removedItemIds); + } + + /** + * Reads an Item from a design and inserts it into the data source. + * Recursively handles any children of the item as well. + * + * @since 7.5.0 + * @param node + * an element representing the item (tree node). + * @param selected + * A set accumulating selected items. If the item that is read is + * marked as selected, its item id should be added to this set. + * @param context + * the DesignContext instance used in parsing + * @return the item id of the new item + * + * @throws DesignException + * if the tag name of the {@code node} element is not + * {@code node}. + */ + @Override + protected String readItem(Element node, Set selected, + DesignContext context) { + + if (!"node".equals(node.tagName())) { + throw new DesignException("Unrecognized child element in " + + getClass().getSimpleName() + ": " + node.tagName()); + } + + String itemId = node.attr("text"); + addItem(itemId); + if (node.hasAttr("icon")) { + Resource icon = DesignAttributeHandler.readAttribute("icon", + node.attributes(), Resource.class); + setItemIcon(itemId, icon); + } + if (node.hasAttr("selected")) { + selected.add(itemId); + } + + for (Element child : node.children()) { + String childItemId = readItem(child, selected, context); + setParent(childItemId, itemId); + } + return itemId; + } + + /** + * Recursively writes the root items and their children to a design. + * + * @since 7.5.0 + * @param design + * the element into which to insert the items + * @param context + * the DesignContext instance used in writing + */ + @Override + protected void writeItems(Element design, DesignContext context) { + for (Object itemId : rootItemIds()) { + writeItem(design, itemId, context); + } + } + + /** + * Recursively writes a data source Item and its children to a design. + * + * @since 7.5.0 + * @param design + * the element into which to insert the item + * @param itemId + * the id of the item to write + * @param context + * the DesignContext instance used in writing + * @return + */ + @Override + protected Element writeItem(Element design, Object itemId, + DesignContext context) { + Element element = design.appendElement("node"); + + element.attr("text", itemId.toString()); + + Resource icon = getItemIcon(itemId); + if (icon != null) { + DesignAttributeHandler.writeAttribute("icon", element.attributes(), + icon, null, Resource.class); + } + + if (isSelected(itemId)) { + element.attr("selected", ""); + } + + Collection children = getChildren(itemId); + if (children != null) { + // Yeah... see #5864 + for (Object childItemId : children) { + writeItem(element, childItemId, context); + } + } + + return element; + } + + /** + * Sets whether html is allowed in the item captions. If set to + * true, the captions are passed to the browser as html and the + * developer is responsible for ensuring no harmful html is used. If set to + * false, the content is passed to the browser as plain text. + * The default setting is false + * + * @since 7.6 + * @param htmlContentAllowed + * true if the captions are used as html, + * false if used as plain text + */ + public void setHtmlContentAllowed(boolean htmlContentAllowed) { + this.htmlContentAllowed = htmlContentAllowed; + markAsDirty(); + } + + /** + * Checks whether captions are interpreted as html or plain text. + * + * @since 7.6 + * @return true if the captions are displayed as html, + * false if displayed as plain text + * @see #setHtmlContentAllowed(boolean) + */ + public boolean isHtmlContentAllowed() { + return htmlContentAllowed; + } + + @Override + protected TreeState getState() { + return (TreeState) super.getState(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/TreeTable.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/TreeTable.java new file mode 100644 index 0000000000..446c9c271c --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/TreeTable.java @@ -0,0 +1,985 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jsoup.nodes.Element; + +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.server.Resource; +import com.vaadin.shared.ui.treetable.TreeTableConstants; +import com.vaadin.shared.ui.treetable.TreeTableState; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; +import com.vaadin.v7.data.Collapsible; +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Container.Hierarchical; +import com.vaadin.v7.data.Container.ItemSetChangeEvent; +import com.vaadin.v7.data.util.ContainerHierarchicalWrapper; +import com.vaadin.v7.data.util.HierarchicalContainer; +import com.vaadin.v7.data.util.HierarchicalContainerOrderedWrapper; +import com.vaadin.v7.ui.Tree.CollapseEvent; +import com.vaadin.v7.ui.Tree.CollapseListener; +import com.vaadin.v7.ui.Tree.ExpandEvent; +import com.vaadin.v7.ui.Tree.ExpandListener; + +/** + * TreeTable extends the {@link Table} component so that it can also visualize a + * hierarchy of its Items in a similar manner that {@link Tree} does. The tree + * hierarchy is always displayed in the first actual column of the TreeTable. + *

    + * The TreeTable supports the usual {@link Table} features like lazy loading, so + * it should be no problem to display lots of items at once. Only required rows + * and some cache rows are sent to the client. + *

    + * TreeTable supports standard {@link Hierarchical} container interfaces, but + * also a more fine tuned version - {@link Collapsible}. A container + * implementing the {@link Collapsible} interface stores the collapsed/expanded + * state internally and can this way scale better on the server side than with + * standard Hierarchical implementations. Developer must however note that + * {@link Collapsible} containers can not be shared among several users as they + * share UI state in the container. + */ +@SuppressWarnings({ "serial" }) +public class TreeTable extends Table implements Hierarchical { + + private interface ContainerStrategy extends Serializable { + public int size(); + + public boolean isNodeOpen(Object itemId); + + public int getDepth(Object itemId); + + public void toggleChildVisibility(Object itemId); + + public Object getIdByIndex(int index); + + public int indexOfId(Object id); + + public Object nextItemId(Object itemId); + + public Object lastItemId(); + + public Object prevItemId(Object itemId); + + public boolean isLastId(Object itemId); + + public Collection getItemIds(); + + public void containerItemSetChange(ItemSetChangeEvent event); + } + + private abstract class AbstractStrategy implements ContainerStrategy { + + /** + * Consider adding getDepth to {@link Collapsible}, might help + * scalability with some container implementations. + */ + + @Override + public int getDepth(Object itemId) { + int depth = 0; + Hierarchical hierarchicalContainer = getContainerDataSource(); + while (!hierarchicalContainer.isRoot(itemId)) { + depth++; + itemId = hierarchicalContainer.getParent(itemId); + } + return depth; + } + + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + } + + } + + /** + * This strategy is used if current container implements {@link Collapsible} + * . + * + * open-collapsed logic diverted to container, otherwise use default + * implementations. + */ + private class CollapsibleStrategy extends AbstractStrategy { + + private Collapsible c() { + return (Collapsible) getContainerDataSource(); + } + + @Override + public void toggleChildVisibility(Object itemId) { + c().setCollapsed(itemId, !c().isCollapsed(itemId)); + } + + @Override + public boolean isNodeOpen(Object itemId) { + return !c().isCollapsed(itemId); + } + + @Override + public int size() { + return TreeTable.super.size(); + } + + @Override + public Object getIdByIndex(int index) { + return TreeTable.super.getIdByIndex(index); + } + + @Override + public int indexOfId(Object id) { + return TreeTable.super.indexOfId(id); + } + + @Override + public boolean isLastId(Object itemId) { + // using the default impl + return TreeTable.super.isLastId(itemId); + } + + @Override + public Object lastItemId() { + // using the default impl + return TreeTable.super.lastItemId(); + } + + @Override + public Object nextItemId(Object itemId) { + return TreeTable.super.nextItemId(itemId); + } + + @Override + public Object prevItemId(Object itemId) { + return TreeTable.super.prevItemId(itemId); + } + + @Override + public Collection getItemIds() { + return TreeTable.super.getItemIds(); + } + + } + + /** + * Strategy for Hierarchical but not Collapsible container like + * {@link HierarchicalContainer}. + * + * Store collapsed/open states internally, fool Table to use preorder when + * accessing items from container via Ordered/Indexed methods. + */ + private class HierarchicalStrategy extends AbstractStrategy { + + private final HashSet openItems = new HashSet(); + + @Override + public boolean isNodeOpen(Object itemId) { + return openItems.contains(itemId); + } + + @Override + public int size() { + return getPreOrder().size(); + } + + @Override + public Collection getItemIds() { + return Collections.unmodifiableCollection(getPreOrder()); + } + + @Override + public boolean isLastId(Object itemId) { + if (itemId == null) { + return false; + } + + return itemId.equals(lastItemId()); + } + + @Override + public Object lastItemId() { + if (getPreOrder().size() > 0) { + return getPreOrder().get(getPreOrder().size() - 1); + } else { + return null; + } + } + + @Override + public Object nextItemId(Object itemId) { + int indexOf = getPreOrder().indexOf(itemId); + if (indexOf == -1) { + return null; + } + indexOf++; + if (indexOf == getPreOrder().size()) { + return null; + } else { + return getPreOrder().get(indexOf); + } + } + + @Override + public Object prevItemId(Object itemId) { + int indexOf = getPreOrder().indexOf(itemId); + indexOf--; + if (indexOf < 0) { + return null; + } else { + return getPreOrder().get(indexOf); + } + } + + @Override + public void toggleChildVisibility(Object itemId) { + boolean removed = openItems.remove(itemId); + if (!removed) { + openItems.add(itemId); + getLogger().log(Level.FINEST, "Item {0} is now expanded", + itemId); + } else { + getLogger().log(Level.FINEST, "Item {0} is now collapsed", + itemId); + } + clearPreorderCache(); + } + + private void clearPreorderCache() { + preOrder = null; // clear preorder cache + } + + List preOrder; + + /** + * Preorder of ids currently visible + * + * @return + */ + private List getPreOrder() { + if (preOrder == null) { + preOrder = new ArrayList(); + Collection rootItemIds = getContainerDataSource() + .rootItemIds(); + for (Object id : rootItemIds) { + preOrder.add(id); + addVisibleChildTree(id); + } + } + return preOrder; + } + + private void addVisibleChildTree(Object id) { + if (isNodeOpen(id)) { + Collection children = getContainerDataSource() + .getChildren(id); + if (children != null) { + for (Object childId : children) { + preOrder.add(childId); + addVisibleChildTree(childId); + } + } + } + + } + + @Override + public int indexOfId(Object id) { + return getPreOrder().indexOf(id); + } + + @Override + public Object getIdByIndex(int index) { + return getPreOrder().get(index); + } + + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + // preorder becomes invalid on sort, item additions etc. + clearPreorderCache(); + super.containerItemSetChange(event); + } + + } + + /** + * Creates an empty TreeTable with a default container. + */ + public TreeTable() { + super(null, new HierarchicalContainer()); + } + + /** + * Creates an empty TreeTable with a default container. + * + * @param caption + * the caption for the TreeTable + */ + public TreeTable(String caption) { + this(); + setCaption(caption); + } + + /** + * Creates a TreeTable instance with given captions and data source. + * + * @param caption + * the caption for the component + * @param dataSource + * the dataSource that is used to list items in the component + */ + public TreeTable(String caption, Container dataSource) { + super(caption, dataSource); + } + + private ContainerStrategy cStrategy; + private Object focusedRowId = null; + private Object hierarchyColumnId; + + /** + * The item id that was expanded or collapsed during this request. Reset at + * the end of paint and only used for determining if a partial or full paint + * should be done. + * + * Can safely be reset to null whenever a change occurs that would prevent a + * partial update from rendering the correct result, e.g. rows added or + * removed during an expand operation. + */ + private Object toggledItemId; + private boolean animationsEnabled; + private boolean clearFocusedRowPending; + + /** + * If the container does not send item set change events, always do a full + * repaint instead of a partial update when expanding/collapsing nodes. + */ + private boolean containerSupportsPartialUpdates; + + private ContainerStrategy getContainerStrategy() { + if (cStrategy == null) { + if (getContainerDataSource() instanceof Collapsible) { + cStrategy = new CollapsibleStrategy(); + } else { + cStrategy = new HierarchicalStrategy(); + } + } + return cStrategy; + } + + @Override + protected void paintRowAttributes(PaintTarget target, Object itemId) + throws PaintException { + super.paintRowAttributes(target, itemId); + target.addAttribute("depth", getContainerStrategy().getDepth(itemId)); + if (getContainerDataSource().areChildrenAllowed(itemId)) { + target.addAttribute("ca", true); + target.addAttribute("open", + getContainerStrategy().isNodeOpen(itemId)); + } + } + + @Override + protected void paintRowIcon(PaintTarget target, Object[][] cells, + int indexInRowbuffer) throws PaintException { + // always paint if present (in parent only if row headers visible) + if (getRowHeaderMode() == ROW_HEADER_MODE_HIDDEN) { + Resource itemIcon = getItemIcon( + cells[CELL_ITEMID][indexInRowbuffer]); + if (itemIcon != null) { + target.addAttribute("icon", itemIcon); + } + } else if (cells[CELL_ICON][indexInRowbuffer] != null) { + target.addAttribute("icon", + (Resource) cells[CELL_ICON][indexInRowbuffer]); + } + } + + @Override + protected boolean rowHeadersAreEnabled() { + if (getRowHeaderMode() == RowHeaderMode.ICON_ONLY) { + return false; + } + return super.rowHeadersAreEnabled(); + } + + @Override + public void changeVariables(Object source, Map variables) { + super.changeVariables(source, variables); + + if (variables.containsKey("toggleCollapsed")) { + String object = (String) variables.get("toggleCollapsed"); + Object itemId = itemIdMapper.get(object); + toggledItemId = itemId; + toggleChildVisibility(itemId, false); + if (variables.containsKey("selectCollapsed")) { + // ensure collapsed is selected unless opened with selection + // head + if (isSelectable()) { + select(itemId); + } + } + } else if (variables.containsKey("focusParent")) { + String key = (String) variables.get("focusParent"); + Object refId = itemIdMapper.get(key); + Object itemId = getParent(refId); + focusParent(itemId); + } + } + + private void focusParent(Object itemId) { + boolean inView = false; + Object inPageId = getCurrentPageFirstItemId(); + for (int i = 0; inPageId != null && i < getPageLength(); i++) { + if (inPageId.equals(itemId)) { + inView = true; + break; + } + inPageId = nextItemId(inPageId); + i++; + } + if (!inView) { + setCurrentPageFirstItemId(itemId); + } + // Select the row if it is selectable. + if (isSelectable()) { + if (isMultiSelect()) { + setValue(Collections.singleton(itemId)); + } else { + setValue(itemId); + } + } + setFocusedRow(itemId); + } + + private void setFocusedRow(Object itemId) { + focusedRowId = itemId; + if (focusedRowId == null) { + // Must still inform the client that the focusParent request has + // been processed + clearFocusedRowPending = true; + } + markAsDirty(); + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + if (focusedRowId != null) { + target.addAttribute("focusedRow", itemIdMapper.key(focusedRowId)); + focusedRowId = null; + } else if (clearFocusedRowPending) { + // Must still inform the client that the focusParent request has + // been processed + target.addAttribute("clearFocusPending", true); + clearFocusedRowPending = false; + } + target.addAttribute("animate", animationsEnabled); + if (hierarchyColumnId != null) { + Object[] visibleColumns2 = getVisibleColumns(); + for (int i = 0; i < visibleColumns2.length; i++) { + Object object = visibleColumns2[i]; + if (hierarchyColumnId.equals(object)) { + target.addAttribute( + TreeTableConstants.ATTRIBUTE_HIERARCHY_COLUMN_INDEX, + i); + break; + } + } + } + super.paintContent(target); + toggledItemId = null; + } + + /* + * Override methods for partial row updates and additions when expanding / + * collapsing nodes. + */ + + @Override + protected boolean isPartialRowUpdate() { + return toggledItemId != null && containerSupportsPartialUpdates + && !isRowCacheInvalidated(); + } + + @Override + protected int getFirstAddedItemIndex() { + return indexOfId(toggledItemId) + 1; + } + + @Override + protected int getAddedRowCount() { + return countSubNodesRecursively(getContainerDataSource(), + toggledItemId); + } + + private int countSubNodesRecursively(Hierarchical hc, Object itemId) { + int count = 0; + // we need the number of children for toggledItemId no matter if its + // collapsed or expanded. Other items' children are only counted if the + // item is expanded. + if (getContainerStrategy().isNodeOpen(itemId) + || itemId == toggledItemId) { + Collection children = hc.getChildren(itemId); + if (children != null) { + count += children != null ? children.size() : 0; + for (Object id : children) { + count += countSubNodesRecursively(hc, id); + } + } + } + return count; + } + + @Override + protected int getFirstUpdatedItemIndex() { + return indexOfId(toggledItemId); + } + + @Override + protected int getUpdatedRowCount() { + return 1; + } + + @Override + protected boolean shouldHideAddedRows() { + return !getContainerStrategy().isNodeOpen(toggledItemId); + } + + private void toggleChildVisibility(Object itemId, + boolean forceFullRefresh) { + getContainerStrategy().toggleChildVisibility(itemId); + // ensure that page still has first item in page, DON'T clear the + // caches. + setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false); + + if (isCollapsed(itemId)) { + fireCollapseEvent(itemId); + } else { + fireExpandEvent(itemId); + } + + if (containerSupportsPartialUpdates && !forceFullRefresh) { + markAsDirty(); + } else { + // For containers that do not send item set change events, always do + // full repaint instead of partial row update. + refreshRowCache(); + } + } + + @Override + public int size() { + return getContainerStrategy().size(); + } + + @Override + public Hierarchical getContainerDataSource() { + return (Hierarchical) super.getContainerDataSource(); + } + + @Override + public void setContainerDataSource(Container newDataSource) { + cStrategy = null; + + // FIXME: This disables partial updates until TreeTable is fixed so it + // does not change component hierarchy during paint + containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier) + && false; + + if (newDataSource != null && !(newDataSource instanceof Hierarchical)) { + newDataSource = new ContainerHierarchicalWrapper(newDataSource); + } + + if (newDataSource != null && !(newDataSource instanceof Ordered)) { + newDataSource = new HierarchicalContainerOrderedWrapper( + (Hierarchical) newDataSource); + } + + super.setContainerDataSource(newDataSource); + } + + @Override + public void containerItemSetChange( + com.vaadin.v7.data.Container.ItemSetChangeEvent event) { + // Can't do partial repaints if items are added or removed during the + // expand/collapse request + toggledItemId = null; + getContainerStrategy().containerItemSetChange(event); + super.containerItemSetChange(event); + } + + @Override + protected Object getIdByIndex(int index) { + return getContainerStrategy().getIdByIndex(index); + } + + @Override + protected int indexOfId(Object itemId) { + return getContainerStrategy().indexOfId(itemId); + } + + @Override + public Object nextItemId(Object itemId) { + return getContainerStrategy().nextItemId(itemId); + } + + @Override + public Object lastItemId() { + return getContainerStrategy().lastItemId(); + } + + @Override + public Object prevItemId(Object itemId) { + return getContainerStrategy().prevItemId(itemId); + } + + @Override + public boolean isLastId(Object itemId) { + return getContainerStrategy().isLastId(itemId); + } + + @Override + public Collection getItemIds() { + return getContainerStrategy().getItemIds(); + } + + @Override + public boolean areChildrenAllowed(Object itemId) { + return getContainerDataSource().areChildrenAllowed(itemId); + } + + @Override + public Collection getChildren(Object itemId) { + return getContainerDataSource().getChildren(itemId); + } + + @Override + public Object getParent(Object itemId) { + return getContainerDataSource().getParent(itemId); + } + + @Override + public boolean hasChildren(Object itemId) { + return getContainerDataSource().hasChildren(itemId); + } + + @Override + public boolean isRoot(Object itemId) { + return getContainerDataSource().isRoot(itemId); + } + + @Override + public Collection rootItemIds() { + return getContainerDataSource().rootItemIds(); + } + + @Override + public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) + throws UnsupportedOperationException { + return getContainerDataSource().setChildrenAllowed(itemId, + areChildrenAllowed); + } + + @Override + public boolean setParent(Object itemId, Object newParentId) + throws UnsupportedOperationException { + return getContainerDataSource().setParent(itemId, newParentId); + } + + /** + * Sets the Item specified by given identifier as collapsed or expanded. If + * the Item is collapsed, its children are not displayed to the user. + * + * @param itemId + * the identifier of the Item + * @param collapsed + * true if the Item should be collapsed, false if expanded + */ + public void setCollapsed(Object itemId, boolean collapsed) { + if (isCollapsed(itemId) != collapsed) { + if (null == toggledItemId && !isRowCacheInvalidated() + && getVisibleItemIds().contains(itemId)) { + // optimization: partial refresh if only one item is + // collapsed/expanded + toggledItemId = itemId; + toggleChildVisibility(itemId, false); + } else { + // make sure a full refresh takes place - otherwise neither + // partial nor full repaint of table content is performed + toggledItemId = null; + toggleChildVisibility(itemId, true); + } + } + } + + /** + * Checks if Item with given identifier is collapsed in the UI. + * + *

    + * + * @param itemId + * the identifier of the checked Item + * @return true if the Item with given id is collapsed + * @see Collapsible#isCollapsed(Object) + */ + public boolean isCollapsed(Object itemId) { + return !getContainerStrategy().isNodeOpen(itemId); + } + + /** + * Explicitly sets the column in which the TreeTable visualizes the + * hierarchy. If hierarchyColumnId is not set, the hierarchy is visualized + * in the first visible column. + * + * @param hierarchyColumnId + */ + public void setHierarchyColumn(Object hierarchyColumnId) { + this.hierarchyColumnId = hierarchyColumnId; + } + + /** + * @return the identifier of column into which the hierarchy will be + * visualized or null if the column is not explicitly defined. + */ + public Object getHierarchyColumnId() { + return hierarchyColumnId; + } + + /** + * Adds an expand listener. + * + * @param listener + * the Listener to be added. + */ + public void addExpandListener(ExpandListener listener) { + addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addExpandListener(ExpandListener)} + **/ + @Deprecated + public void addListener(ExpandListener listener) { + addExpandListener(listener); + } + + /** + * Removes an expand listener. + * + * @param listener + * the Listener to be removed. + */ + public void removeExpandListener(ExpandListener listener) { + removeListener(ExpandEvent.class, listener, + ExpandListener.EXPAND_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeExpandListener(ExpandListener)} + **/ + @Deprecated + public void removeListener(ExpandListener listener) { + removeExpandListener(listener); + } + + /** + * Emits an expand event. + * + * @param itemId + * the item id. + */ + protected void fireExpandEvent(Object itemId) { + fireEvent(new ExpandEvent(this, itemId)); + } + + /** + * Adds a collapse listener. + * + * @param listener + * the Listener to be added. + */ + public void addCollapseListener(CollapseListener listener) { + addListener(CollapseEvent.class, listener, + CollapseListener.COLLAPSE_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #addCollapseListener(CollapseListener)} + **/ + @Deprecated + public void addListener(CollapseListener listener) { + addCollapseListener(listener); + } + + /** + * Removes a collapse listener. + * + * @param listener + * the Listener to be removed. + */ + public void removeCollapseListener(CollapseListener listener) { + removeListener(CollapseEvent.class, listener, + CollapseListener.COLLAPSE_METHOD); + } + + /** + * @deprecated As of 7.0, replaced by + * {@link #removeCollapseListener(CollapseListener)} + **/ + @Deprecated + public void removeListener(CollapseListener listener) { + removeCollapseListener(listener); + } + + /** + * Emits a collapse event. + * + * @param itemId + * the item id. + */ + protected void fireCollapseEvent(Object itemId) { + fireEvent(new CollapseEvent(this, itemId)); + } + + /** + * @return true if animations are enabled + */ + public boolean isAnimationsEnabled() { + return animationsEnabled; + } + + /** + * Animations can be enabled by passing true to this method. Currently + * expanding rows slide in from the top and collapsing rows slide out the + * same way. NOTE! not supported in Internet Explorer 6 or 7. + * + * @param animationsEnabled + * true or false whether to enable animations or not. + */ + public void setAnimationsEnabled(boolean animationsEnabled) { + this.animationsEnabled = animationsEnabled; + markAsDirty(); + } + + private static final Logger getLogger() { + return Logger.getLogger(TreeTable.class.getName()); + } + + @Override + protected List getItemIds(int firstIndex, int rows) { + List itemIds = new ArrayList(); + for (int i = firstIndex; i < firstIndex + rows; i++) { + itemIds.add(getIdByIndex(i)); + } + return itemIds; + } + + @Override + protected void readBody(Element design, DesignContext context) { + Element tbody = design.select("> table > tbody").first(); + if (tbody == null) { + return; + } + + Set selected = new HashSet(); + Stack parents = new Stack(); + int lastDepth = -1; + + for (Element tr : tbody.children()) { + int depth = DesignAttributeHandler.readAttribute("depth", + tr.attributes(), 0, int.class); + + if (depth < 0 || depth > lastDepth + 1) { + throw new DesignException( + "Malformed TreeTable item hierarchy at " + tr + + ": last depth was " + lastDepth); + } else if (depth <= lastDepth) { + for (int d = depth; d <= lastDepth; d++) { + parents.pop(); + } + } + + Object itemId = readItem(tr, selected, context); + setParent(itemId, !parents.isEmpty() ? parents.peek() : null); + parents.push(itemId); + lastDepth = depth; + } + } + + @Override + protected Object readItem(Element tr, Set selected, + DesignContext context) { + Object itemId = super.readItem(tr, selected, context); + + if (tr.hasAttr("collapsed")) { + boolean collapsed = DesignAttributeHandler + .readAttribute("collapsed", tr.attributes(), boolean.class); + setCollapsed(itemId, collapsed); + } + + return itemId; + } + + @Override + protected void writeItems(Element design, DesignContext context) { + if (getVisibleColumns().length == 0) { + return; + } + Element tbody = design.child(0).appendElement("tbody"); + writeItems(tbody, rootItemIds(), 0, context); + } + + protected void writeItems(Element tbody, Collection itemIds, int depth, + DesignContext context) { + for (Object itemId : itemIds) { + Element tr = writeItem(tbody, itemId, context); + DesignAttributeHandler.writeAttribute("depth", tr.attributes(), + depth, 0, int.class); + + if (getChildren(itemId) != null) { + writeItems(tbody, getChildren(itemId), depth + 1, context); + } + } + } + + @Override + protected Element writeItem(Element tbody, Object itemId, + DesignContext context) { + Element tr = super.writeItem(tbody, itemId, context); + DesignAttributeHandler.writeAttribute("collapsed", tr.attributes(), + isCollapsed(itemId), true, boolean.class); + return tr; + } + + @Override + protected TreeTableState getState() { + return (TreeTableState) super.getState(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/TwinColSelect.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/TwinColSelect.java new file mode 100644 index 0000000000..68853ddaa1 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/TwinColSelect.java @@ -0,0 +1,169 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui; + +import java.util.Collection; + +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.shared.ui.twincolselect.TwinColSelectConstants; +import com.vaadin.shared.ui.twincolselect.TwinColSelectState; +import com.vaadin.v7.data.Container; + +/** + * Multiselect component with two lists: left side for available items and right + * side for selected items. + */ +@SuppressWarnings("serial") +public class TwinColSelect extends AbstractSelect { + + private int rows = 0; + + private String leftColumnCaption; + private String rightColumnCaption; + + /** + * + */ + public TwinColSelect() { + super(); + setMultiSelect(true); + } + + /** + * @param caption + */ + public TwinColSelect(String caption) { + super(caption); + setMultiSelect(true); + } + + /** + * @param caption + * @param dataSource + */ + public TwinColSelect(String caption, Container dataSource) { + super(caption, dataSource); + setMultiSelect(true); + } + + public int getRows() { + return rows; + } + + /** + * Sets the number of rows in the editor. If the number of rows is set to 0, + * the actual number of displayed rows is determined implicitly by the + * adapter. + *

    + * If a height if set (using {@link #setHeight(String)} or + * {@link #setHeight(float, int)}) it overrides the number of rows. Leave + * the height undefined to use this method. This is the opposite of how + * {@link #setColumns(int)} work. + * + * + * @param rows + * the number of rows to set. + */ + public void setRows(int rows) { + if (rows < 0) { + rows = 0; + } + if (this.rows != rows) { + this.rows = rows; + markAsDirty(); + } + } + + /** + * @param caption + * @param options + */ + public TwinColSelect(String caption, Collection options) { + super(caption, options); + setMultiSelect(true); + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + // Adds the number of columns + // Adds the number of rows + if (rows != 0) { + target.addAttribute("rows", rows); + } + + // Right and left column captions and/or icons (if set) + String lc = getLeftColumnCaption(); + String rc = getRightColumnCaption(); + if (lc != null) { + target.addAttribute(TwinColSelectConstants.ATTRIBUTE_LEFT_CAPTION, + lc); + } + if (rc != null) { + target.addAttribute(TwinColSelectConstants.ATTRIBUTE_RIGHT_CAPTION, + rc); + } + + super.paintContent(target); + } + + /** + * Sets the text shown above the right column. + * + * @param caption + * The text to show + */ + public void setRightColumnCaption(String rightColumnCaption) { + this.rightColumnCaption = rightColumnCaption; + markAsDirty(); + } + + /** + * Returns the text shown above the right column. + * + * @return The text shown or null if not set. + */ + public String getRightColumnCaption() { + return rightColumnCaption; + } + + /** + * Sets the text shown above the left column. + * + * @param caption + * The text to show + */ + public void setLeftColumnCaption(String leftColumnCaption) { + this.leftColumnCaption = leftColumnCaption; + markAsDirty(); + } + + /** + * Returns the text shown above the left column. + * + * @return The text shown or null if not set. + */ + public String getLeftColumnCaption() { + return leftColumnCaption; + } + + @Override + protected TwinColSelectState getState() { + return (TwinColSelectState) super.getState(); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvent.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvent.java new file mode 100644 index 0000000000..230d87b7f7 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvent.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar; + +import com.vaadin.ui.Component; +import com.vaadin.v7.ui.Calendar; + +/** + * All Calendar events extends this class. + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +@SuppressWarnings("serial") +public class CalendarComponentEvent extends Component.Event { + + /** + * Set the source of the event + * + * @param source + * The source calendar + * + */ + public CalendarComponentEvent(Calendar source) { + super(source); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Component.Event#getComponent() + */ + @Override + public Calendar getComponent() { + return (Calendar) super.getComponent(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvents.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvents.java new file mode 100644 index 0000000000..14494eedbe --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarComponentEvents.java @@ -0,0 +1,603 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Date; +import java.util.EventListener; + +import com.vaadin.shared.ui.calendar.CalendarEventId; +import com.vaadin.util.ReflectTools; +import com.vaadin.v7.ui.Calendar; +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent; + +/** + * Interface for all Vaadin Calendar events. + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +public interface CalendarComponentEvents extends Serializable { + + /** + * Notifier interface for notifying listener of calendar events + */ + public interface CalendarEventNotifier extends Serializable { + /** + * Get the assigned event handler for the given eventId. + * + * @param eventId + * @return the assigned eventHandler, or null if no handler is assigned + */ + public EventListener getHandler(String eventId); + } + + /** + * Notifier interface for event drag & drops. + */ + public interface EventMoveNotifier extends CalendarEventNotifier { + + /** + * Set the EventMoveHandler. + * + * @param listener + * EventMoveHandler to be added + */ + public void setHandler(EventMoveHandler listener); + + } + + /** + * MoveEvent is sent when existing event is dragged to a new position. + */ + @SuppressWarnings("serial") + public class MoveEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.EVENTMOVE; + + /** Index for the moved Schedule.Event. */ + private CalendarEvent calendarEvent; + + /** New starting date for the moved Calendar.Event. */ + private Date newStart; + + /** + * MoveEvent needs the target event and new start date. + * + * @param source + * Calendar component. + * @param calendarEvent + * Target event. + * @param newStart + * Target event's new start date. + */ + public MoveEvent(Calendar source, CalendarEvent calendarEvent, + Date newStart) { + super(source); + + this.calendarEvent = calendarEvent; + this.newStart = newStart; + } + + /** + * Get target event. + * + * @return Target event. + */ + public CalendarEvent getCalendarEvent() { + return calendarEvent; + } + + /** + * Get new start date. + * + * @return New start date. + */ + public Date getNewStart() { + return newStart; + } + } + + /** + * Handler interface for when events are being dragged on the calendar + * + */ + public interface EventMoveHandler extends EventListener, Serializable { + + /** Trigger method for the MoveEvent. */ + public static final Method eventMoveMethod = ReflectTools.findMethod( + EventMoveHandler.class, "eventMove", MoveEvent.class); + + /** + * This method will be called when event has been moved to a new + * position. + * + * @param event + * MoveEvent containing specific information of the new + * position and target event. + */ + public void eventMove(MoveEvent event); + } + + /** + * Handler interface for day or time cell drag-marking with mouse. + */ + public interface RangeSelectNotifier + extends Serializable, CalendarEventNotifier { + + /** + * Set the RangeSelectHandler that listens for drag-marking. + * + * @param listener + * RangeSelectHandler to be added. + */ + public void setHandler(RangeSelectHandler listener); + } + + /** + * RangeSelectEvent is sent when day or time cells are drag-marked with + * mouse. + */ + @SuppressWarnings("serial") + public class RangeSelectEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.RANGESELECT; + + /** Calendar event's start date. */ + private Date start; + + /** Calendar event's end date. */ + private Date end; + + /** + * Defines the event's view mode. + */ + private boolean monthlyMode; + + /** + * RangeSelectEvent needs a start and end date. + * + * @param source + * Calendar component. + * @param start + * Start date. + * @param end + * End date. + * @param monthlyMode + * Calendar view mode. + */ + public RangeSelectEvent(Calendar source, Date start, Date end, + boolean monthlyMode) { + super(source); + this.start = start; + this.end = end; + this.monthlyMode = monthlyMode; + } + + /** + * Get start date. + * + * @return Start date. + */ + public Date getStart() { + return start; + } + + /** + * Get end date. + * + * @return End date. + */ + public Date getEnd() { + return end; + } + + /** + * Gets the event's view mode. Calendar can be be either in monthly or + * weekly mode, depending on the active date range. + * + * @deprecated User {@link Calendar#isMonthlyMode()} instead + * + * @return Returns true when monthly view is active. + */ + @Deprecated + public boolean isMonthlyMode() { + return monthlyMode; + } + } + + /** RangeSelectHandler handles RangeSelectEvent. */ + public interface RangeSelectHandler extends EventListener, Serializable { + + /** Trigger method for the RangeSelectEvent. */ + public static final Method rangeSelectMethod = ReflectTools.findMethod( + RangeSelectHandler.class, "rangeSelect", + RangeSelectEvent.class); + + /** + * This method will be called when day or time cells are drag-marked + * with mouse. + * + * @param event + * RangeSelectEvent that contains range start and end date. + */ + public void rangeSelect(RangeSelectEvent event); + } + + /** Notifier interface for navigation listening. */ + public interface NavigationNotifier extends Serializable { + /** + * Add a forward navigation listener. + * + * @param handler + * ForwardHandler to be added. + */ + public void setHandler(ForwardHandler handler); + + /** + * Add a backward navigation listener. + * + * @param handler + * BackwardHandler to be added. + */ + public void setHandler(BackwardHandler handler); + + /** + * Add a date click listener. + * + * @param handler + * DateClickHandler to be added. + */ + public void setHandler(DateClickHandler handler); + + /** + * Add a event click listener. + * + * @param handler + * EventClickHandler to be added. + */ + public void setHandler(EventClickHandler handler); + + /** + * Add a week click listener. + * + * @param handler + * WeekClickHandler to be added. + */ + public void setHandler(WeekClickHandler handler); + } + + /** + * ForwardEvent is sent when forward navigation button is clicked. + */ + @SuppressWarnings("serial") + public class ForwardEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.FORWARD; + + /** + * ForwardEvent needs only the source component. + * + * @param source + * Calendar component. + */ + public ForwardEvent(Calendar source) { + super(source); + } + } + + /** ForwardHandler handles ForwardEvent. */ + public interface ForwardHandler extends EventListener, Serializable { + + /** Trigger method for the ForwardEvent. */ + public static final Method forwardMethod = ReflectTools.findMethod( + ForwardHandler.class, "forward", ForwardEvent.class); + + /** + * This method will be called when date range is moved forward. + * + * @param event + * ForwardEvent + */ + public void forward(ForwardEvent event); + } + + /** + * BackwardEvent is sent when backward navigation button is clicked. + */ + @SuppressWarnings("serial") + public class BackwardEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.BACKWARD; + + /** + * BackwardEvent needs only the source source component. + * + * @param source + * Calendar component. + */ + public BackwardEvent(Calendar source) { + super(source); + } + } + + /** BackwardHandler handles BackwardEvent. */ + public interface BackwardHandler extends EventListener, Serializable { + + /** Trigger method for the BackwardEvent. */ + public static final Method backwardMethod = ReflectTools.findMethod( + BackwardHandler.class, "backward", BackwardEvent.class); + + /** + * This method will be called when date range is moved backwards. + * + * @param event + * BackwardEvent + */ + public void backward(BackwardEvent event); + } + + /** + * DateClickEvent is sent when a date is clicked. + */ + @SuppressWarnings("serial") + public class DateClickEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.DATECLICK; + + /** Date that was clicked. */ + private Date date; + + /** DateClickEvent needs the target date that was clicked. */ + public DateClickEvent(Calendar source, Date date) { + super(source); + this.date = date; + } + + /** + * Get clicked date. + * + * @return Clicked date. + */ + public Date getDate() { + return date; + } + } + + /** DateClickHandler handles DateClickEvent. */ + public interface DateClickHandler extends EventListener, Serializable { + + /** Trigger method for the DateClickEvent. */ + public static final Method dateClickMethod = ReflectTools.findMethod( + DateClickHandler.class, "dateClick", DateClickEvent.class); + + /** + * This method will be called when a date is clicked. + * + * @param event + * DateClickEvent containing the target date. + */ + public void dateClick(DateClickEvent event); + } + + /** + * EventClick is sent when an event is clicked. + */ + @SuppressWarnings("serial") + public class EventClick extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.EVENTCLICK; + + /** Clicked source event. */ + private CalendarEvent calendarEvent; + + /** Target source event is needed for the EventClick. */ + public EventClick(Calendar source, CalendarEvent calendarEvent) { + super(source); + this.calendarEvent = calendarEvent; + } + + /** + * Get the clicked event. + * + * @return Clicked event. + */ + public CalendarEvent getCalendarEvent() { + return calendarEvent; + } + } + + /** EventClickHandler handles EventClick. */ + public interface EventClickHandler extends EventListener, Serializable { + + /** Trigger method for the EventClick. */ + public static final Method eventClickMethod = ReflectTools.findMethod( + EventClickHandler.class, "eventClick", EventClick.class); + + /** + * This method will be called when an event is clicked. + * + * @param event + * EventClick containing the target event. + */ + public void eventClick(EventClick event); + } + + /** + * WeekClick is sent when week is clicked. + */ + @SuppressWarnings("serial") + public class WeekClick extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.WEEKCLICK; + + /** Target week. */ + private int week; + + /** Target year. */ + private int year; + + /** + * WeekClick needs a target year and week. + * + * @param source + * Target source. + * @param week + * Target week. + * @param year + * Target year. + */ + public WeekClick(Calendar source, int week, int year) { + super(source); + this.week = week; + this.year = year; + } + + /** + * Get week as a integer. See {@link java.util.Calendar} for the allowed + * values. + * + * @return Week as a integer. + */ + public int getWeek() { + return week; + } + + /** + * Get year as a integer. See {@link java.util.Calendar} for the allowed + * values. + * + * @return Year as a integer + */ + public int getYear() { + return year; + } + } + + /** WeekClickHandler handles WeekClicks. */ + public interface WeekClickHandler extends EventListener, Serializable { + + /** Trigger method for the WeekClick. */ + public static final Method weekClickMethod = ReflectTools.findMethod( + WeekClickHandler.class, "weekClick", WeekClick.class); + + /** + * This method will be called when a week is clicked. + * + * @param event + * WeekClick containing the target week and year. + */ + public void weekClick(WeekClick event); + } + + /** + * EventResize is sent when an event is resized + */ + @SuppressWarnings("serial") + public class EventResize extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.EVENTRESIZE; + + private CalendarEvent calendarEvent; + + private Date startTime; + + private Date endTime; + + public EventResize(Calendar source, CalendarEvent calendarEvent, + Date startTime, Date endTime) { + super(source); + this.calendarEvent = calendarEvent; + this.startTime = startTime; + this.endTime = endTime; + } + + /** + * Get target event. + * + * @return Target event. + */ + public CalendarEvent getCalendarEvent() { + return calendarEvent; + } + + /** + * @deprecated Use {@link #getNewStart()} instead + * + * @return the new start time + */ + @Deprecated + public Date getNewStartTime() { + return startTime; + } + + /** + * Returns the updated start date/time of the event + * + * @return The new date for the event + */ + public Date getNewStart() { + return startTime; + } + + /** + * @deprecated Use {@link #getNewEnd()} instead + * + * @return the new end time + */ + @Deprecated + public Date getNewEndTime() { + return endTime; + } + + /** + * Returns the updates end date/time of the event + * + * @return The new date for the event + */ + public Date getNewEnd() { + return endTime; + } + } + + /** + * Notifier interface for event resizing. + */ + public interface EventResizeNotifier extends Serializable { + + /** + * Set a EventResizeHandler. + * + * @param handler + * EventResizeHandler to be set + */ + public void setHandler(EventResizeHandler handler); + } + + /** + * Handler for EventResize event. + */ + public interface EventResizeHandler extends EventListener, Serializable { + + /** Trigger method for the EventResize. */ + public static final Method eventResizeMethod = ReflectTools.findMethod( + EventResizeHandler.class, "eventResize", EventResize.class); + + void eventResize(EventResize event); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarDateRange.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarDateRange.java new file mode 100644 index 0000000000..69a4123c1b --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarDateRange.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.v7.ui.components.calendar; + +import java.io.Serializable; +import java.util.Date; +import java.util.TimeZone; + +/** + * Class for representing a date range. + * + * @since 7.1.0 + * @author Vaadin Ltd. + * + */ +@SuppressWarnings("serial") +public class CalendarDateRange implements Serializable { + + private Date start; + + private Date end; + + private final transient TimeZone tz; + + /** + * Constructor + * + * @param start + * The start date and time of the date range + * @param end + * The end date and time of the date range + */ + public CalendarDateRange(Date start, Date end, TimeZone tz) { + super(); + this.start = start; + this.end = end; + this.tz = tz; + } + + /** + * Get the start date of the date range + * + * @return the start Date of the range + */ + public Date getStart() { + return start; + } + + /** + * Get the end date of the date range + * + * @return the end Date of the range + */ + public Date getEnd() { + return end; + } + + /** + * Is a date in the date range + * + * @param date + * The date to check + * @return true if the date range contains a date start and end of range + * inclusive; false otherwise + */ + public boolean inRange(Date date) { + if (date == null) { + return false; + } + + return date.compareTo(start) >= 0 && date.compareTo(end) <= 0; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "CalendarDateRange [start=" + start + ", end=" + end + "]"; + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarTargetDetails.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarTargetDetails.java new file mode 100644 index 0000000000..f9f4100e53 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/CalendarTargetDetails.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar; + +import java.util.Date; +import java.util.Map; + +import com.vaadin.event.dd.DropTarget; +import com.vaadin.event.dd.TargetDetailsImpl; +import com.vaadin.v7.ui.Calendar; + +/** + * Drop details for {@link com.vaadin.v7.ui.addon.calendar.ui.Calendar Calendar}. + * When something is dropped on the Calendar, this class contains the specific + * details of the drop point. Specifically, this class gives access to the date + * where the drop happened. If the Calendar was in weekly mode, the date also + * includes the start time of the slot. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class CalendarTargetDetails extends TargetDetailsImpl { + + private boolean hasDropTime; + + public CalendarTargetDetails(Map rawDropData, + DropTarget dropTarget) { + super(rawDropData, dropTarget); + } + + /** + * @return true if {@link #getDropTime()} will return a date object with the + * time set to the start of the time slot where the drop happened + */ + public boolean hasDropTime() { + return hasDropTime; + } + + /** + * Does the dropped item have a time associated with it + * + * @param hasDropTime + */ + public void setHasDropTime(boolean hasDropTime) { + this.hasDropTime = hasDropTime; + } + + /** + * @return the date where the drop happened + */ + public Date getDropTime() { + if (hasDropTime) { + return (Date) getData("dropTime"); + } else { + return (Date) getData("dropDay"); + } + } + + /** + * @return the {@link com.vaadin.v7.ui.addon.calendar.ui.Calendar Calendar} + * instance which was the target of the drop + */ + public Calendar getTargetCalendar() { + return (Calendar) getTarget(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/ContainerEventProvider.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/ContainerEventProvider.java new file mode 100644 index 0000000000..961b2d8fec --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/ContainerEventProvider.java @@ -0,0 +1,566 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar; + +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import com.vaadin.v7.data.Container; +import com.vaadin.v7.data.Container.Indexed; +import com.vaadin.v7.data.Container.ItemSetChangeEvent; +import com.vaadin.v7.data.Container.ItemSetChangeNotifier; +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.Property.ValueChangeEvent; +import com.vaadin.v7.data.Property.ValueChangeNotifier; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventResize; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.MoveEvent; +import com.vaadin.v7.ui.components.calendar.event.BasicEvent; +import com.vaadin.v7.ui.components.calendar.event.CalendarEditableEventProvider; +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent; +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent.EventChangeListener; +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent.EventChangeNotifier; +import com.vaadin.v7.ui.components.calendar.event.CalendarEventProvider; +import com.vaadin.v7.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier; + +/** + * A event provider which uses a {@link Container} as a datasource. Container + * used as data source. + * + * NOTE: The data source must be sorted by date! + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class ContainerEventProvider + implements CalendarEditableEventProvider, EventSetChangeNotifier, + EventChangeNotifier, EventMoveHandler, EventResizeHandler, + Container.ItemSetChangeListener, Property.ValueChangeListener { + + // Default property ids + public static final String CAPTION_PROPERTY = "caption"; + public static final String DESCRIPTION_PROPERTY = "description"; + public static final String STARTDATE_PROPERTY = "start"; + public static final String ENDDATE_PROPERTY = "end"; + public static final String STYLENAME_PROPERTY = "styleName"; + public static final String ALL_DAY_PROPERTY = "allDay"; + + /** + * Internal class to keep the container index which item this event + * represents + * + */ + private class ContainerCalendarEvent extends BasicEvent { + private final int index; + + public ContainerCalendarEvent(int containerIndex) { + super(); + index = containerIndex; + } + + public int getContainerIndex() { + return index; + } + } + + /** + * Listeners attached to the container + */ + private final List eventSetChangeListeners = new LinkedList(); + private final List eventChangeListeners = new LinkedList(); + + /** + * The event cache contains the events previously created by + * {@link #getEvents(Date, Date)} + */ + private final List eventCache = new LinkedList(); + + /** + * The container used as datasource + */ + private Indexed container; + + /** + * Container properties. Defaults based on using the {@link BasicEvent} + * helper class. + */ + private Object captionProperty = CAPTION_PROPERTY; + private Object descriptionProperty = DESCRIPTION_PROPERTY; + private Object startDateProperty = STARTDATE_PROPERTY; + private Object endDateProperty = ENDDATE_PROPERTY; + private Object styleNameProperty = STYLENAME_PROPERTY; + private Object allDayProperty = ALL_DAY_PROPERTY; + + /** + * Constructor + * + * @param container + * Container to use as a data source. + */ + public ContainerEventProvider(Container.Indexed container) { + this.container = container; + listenToContainerEvents(); + } + + /** + * Set the container data source + * + * @param container + * The container to use as datasource + * + */ + public void setContainerDataSource(Container.Indexed container) { + // Detach the previous container + detachContainerDataSource(); + + this.container = container; + listenToContainerEvents(); + } + + /** + * Returns the container used as data source + * + */ + public Container.Indexed getContainerDataSource() { + return container; + } + + /** + * Attaches listeners to the container so container events can be processed + */ + private void listenToContainerEvents() { + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container).addItemSetChangeListener(this); + } + if (container instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) container).addValueChangeListener(this); + } + } + + /** + * Removes listeners from the container so no events are processed + */ + private void ignoreContainerEvents() { + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container) + .removeItemSetChangeListener(this); + } + if (container instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) container).removeValueChangeListener(this); + } + } + + /** + * Converts an event in the container to an {@link CalendarEvent} + * + * @param index + * The index of the item in the container to get the event for + * @return + */ + private CalendarEvent getEvent(int index) { + + // Check the event cache first + for (CalendarEvent e : eventCache) { + if (e instanceof ContainerCalendarEvent + && ((ContainerCalendarEvent) e) + .getContainerIndex() == index) { + return e; + } else if (container.getIdByIndex(index) == e) { + return e; + } + } + + final Object id = container.getIdByIndex(index); + Item item = container.getItem(id); + CalendarEvent event; + if (id instanceof CalendarEvent) { + /* + * If we are using the BeanItemContainer or another container which + * stores the objects as ids then just return the instances + */ + event = (CalendarEvent) id; + + } else { + /* + * Else we use the properties to create the event + */ + BasicEvent basicEvent = new ContainerCalendarEvent(index); + + // Set values from property values + if (captionProperty != null + && item.getItemPropertyIds().contains(captionProperty)) { + basicEvent.setCaption(String.valueOf( + item.getItemProperty(captionProperty).getValue())); + } + if (descriptionProperty != null && item.getItemPropertyIds() + .contains(descriptionProperty)) { + basicEvent.setDescription(String.valueOf( + item.getItemProperty(descriptionProperty).getValue())); + } + if (startDateProperty != null + && item.getItemPropertyIds().contains(startDateProperty)) { + basicEvent.setStart((Date) item + .getItemProperty(startDateProperty).getValue()); + } + if (endDateProperty != null + && item.getItemPropertyIds().contains(endDateProperty)) { + basicEvent.setEnd((Date) item.getItemProperty(endDateProperty) + .getValue()); + } + if (styleNameProperty != null + && item.getItemPropertyIds().contains(styleNameProperty)) { + basicEvent.setStyleName(String.valueOf( + item.getItemProperty(styleNameProperty).getValue())); + } + if (allDayProperty != null + && item.getItemPropertyIds().contains(allDayProperty)) { + basicEvent.setAllDay((Boolean) item + .getItemProperty(allDayProperty).getValue()); + } + event = basicEvent; + } + return event; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. + * util.Date, java.util.Date) + */ + @Override + public List getEvents(Date startDate, Date endDate) { + eventCache.clear(); + int size = container.size(); + assert size >= 0; + + for (int i = 0; i < size; i++) { + Object id = container.getIdByIndex(i); + Item item = container.getItem(id); + boolean add = true; + if (startDate != null) { + Date eventEnd = (Date) item.getItemProperty(endDateProperty) + .getValue(); + if (eventEnd.compareTo(startDate) < 0) { + add = false; + } + } + if (add && endDate != null) { + Date eventStart = (Date) item.getItemProperty(startDateProperty) + .getValue(); + if (eventStart.compareTo(endDate) >= 0) { + break; // because container is sorted, all further events + // will be even later + } + } + if (add) { + eventCache.add(getEvent(i)); + } + } + return Collections.unmodifiableList(eventCache); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEventProvider. + * EventSetChangeNotifier + * #addListener(com.vaadin.addon.calendar.event.CalendarEventProvider. + * EventSetChangeListener) + */ + @Override + public void addEventSetChangeListener(EventSetChangeListener listener) { + if (!eventSetChangeListeners.contains(listener)) { + eventSetChangeListeners.add(listener); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEventProvider. + * EventSetChangeNotifier + * #removeListener(com.vaadin.addon.calendar.event.CalendarEventProvider. + * EventSetChangeListener) + */ + @Override + public void removeEventSetChangeListener(EventSetChangeListener listener) { + eventSetChangeListeners.remove(listener); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier# + * addListener + * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener) + */ + @Override + public void addEventChangeListener(EventChangeListener listener) { + if (eventChangeListeners.contains(listener)) { + eventChangeListeners.add(listener); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier# + * removeListener + * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener) + */ + @Override + public void removeEventChangeListener(EventChangeListener listener) { + eventChangeListeners.remove(listener); + } + + /** + * Get the property which provides the caption of the event + */ + public Object getCaptionProperty() { + return captionProperty; + } + + /** + * Set the property which provides the caption of the event + */ + public void setCaptionProperty(Object captionProperty) { + this.captionProperty = captionProperty; + } + + /** + * Get the property which provides the description of the event + */ + public Object getDescriptionProperty() { + return descriptionProperty; + } + + /** + * Set the property which provides the description of the event + */ + public void setDescriptionProperty(Object descriptionProperty) { + this.descriptionProperty = descriptionProperty; + } + + /** + * Get the property which provides the starting date and time of the event + */ + public Object getStartDateProperty() { + return startDateProperty; + } + + /** + * Set the property which provides the starting date and time of the event + */ + public void setStartDateProperty(Object startDateProperty) { + this.startDateProperty = startDateProperty; + } + + /** + * Get the property which provides the ending date and time of the event + */ + public Object getEndDateProperty() { + return endDateProperty; + } + + /** + * Set the property which provides the ending date and time of the event + */ + public void setEndDateProperty(Object endDateProperty) { + this.endDateProperty = endDateProperty; + } + + /** + * Get the property which provides the style name for the event + */ + public Object getStyleNameProperty() { + return styleNameProperty; + } + + /** + * Set the property which provides the style name for the event + */ + public void setStyleNameProperty(Object styleNameProperty) { + this.styleNameProperty = styleNameProperty; + } + + /** + * Set the all day property for the event + * + * @since 7.3.4 + */ + public void setAllDayProperty(Object allDayProperty) { + this.allDayProperty = allDayProperty; + } + + /** + * Get the all day property for the event + * + * @since 7.3.4 + */ + public Object getAllDayProperty() { + return allDayProperty; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange + * (com.vaadin.data.Container.ItemSetChangeEvent) + */ + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + if (event.getContainer() == container) { + // Trigger an eventset change event when the itemset changes + for (EventSetChangeListener listener : eventSetChangeListeners) { + listener.eventSetChange(new EventSetChangeEvent(this)); + } + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Property.ValueChangeListener#valueChange(com.vaadin.data + * .Property.ValueChangeEvent) + */ + @Override + public void valueChange(ValueChangeEvent event) { + /* + * TODO Need to figure out how to get the item which triggered the the + * valuechange event and then trigger a EventChange event to the + * listeners + */ + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler + * #eventMove + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent) + */ + @Override + public void eventMove(MoveEvent event) { + CalendarEvent ce = event.getCalendarEvent(); + if (eventCache.contains(ce)) { + int index; + if (ce instanceof ContainerCalendarEvent) { + index = ((ContainerCalendarEvent) ce).getContainerIndex(); + } else { + index = container.indexOfId(ce); + } + + long eventLength = ce.getEnd().getTime() - ce.getStart().getTime(); + Date newEnd = new Date(event.getNewStart().getTime() + eventLength); + + ignoreContainerEvents(); + Item item = container.getItem(container.getIdByIndex(index)); + item.getItemProperty(startDateProperty) + .setValue(event.getNewStart()); + item.getItemProperty(endDateProperty).setValue(newEnd); + listenToContainerEvents(); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler + * #eventResize + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize) + */ + @Override + public void eventResize(EventResize event) { + CalendarEvent ce = event.getCalendarEvent(); + if (eventCache.contains(ce)) { + int index; + if (ce instanceof ContainerCalendarEvent) { + index = ((ContainerCalendarEvent) ce).getContainerIndex(); + } else { + index = container.indexOfId(ce); + } + ignoreContainerEvents(); + Item item = container.getItem(container.getIdByIndex(index)); + item.getItemProperty(startDateProperty) + .setValue(event.getNewStart()); + item.getItemProperty(endDateProperty).setValue(event.getNewEnd()); + listenToContainerEvents(); + } + } + + /** + * If you are reusing the container which previously have been attached to + * this ContainerEventProvider call this method to remove this event + * providers container listeners before attaching it to an other + * ContainerEventProvider + */ + public void detachContainerDataSource() { + ignoreContainerEvents(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void addEvent(CalendarEvent event) { + Item item; + try { + item = container.addItem(event); + } catch (UnsupportedOperationException uop) { + // Thrown if container does not support adding items with custom + // ids. JPAContainer for example. + item = container.getItem(container.addItem()); + } + if (item != null) { + item.getItemProperty(getCaptionProperty()) + .setValue(event.getCaption()); + item.getItemProperty(getStartDateProperty()) + .setValue(event.getStart()); + item.getItemProperty(getEndDateProperty()).setValue(event.getEnd()); + item.getItemProperty(getStyleNameProperty()) + .setValue(event.getStyleName()); + item.getItemProperty(getDescriptionProperty()) + .setValue(event.getDescription()); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void removeEvent(CalendarEvent event) { + container.removeItem(event); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEvent.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEvent.java new file mode 100644 index 0000000000..f1b6524d2c --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEvent.java @@ -0,0 +1,265 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.event; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent.EventChangeNotifier; + +/** + * Simple implementation of {@link com.vaadin.addon.calendar.event.CalendarEvent + * CalendarEvent}. Has setters for all required fields and fires events when + * this event is changed. + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEvent implements EditableCalendarEvent, EventChangeNotifier { + + private String caption; + private String description; + private Date end; + private Date start; + private String styleName; + private transient List listeners = new ArrayList(); + + private boolean isAllDay; + + /** + * Default constructor + */ + public BasicEvent() { + + } + + /** + * Constructor for creating an event with the same start and end date + * + * @param caption + * The caption for the event + * @param description + * The description for the event + * @param date + * The date the event occurred + */ + public BasicEvent(String caption, String description, Date date) { + this.caption = caption; + this.description = description; + start = date; + end = date; + } + + /** + * Constructor for creating an event with a start date and an end date. + * Start date should be before the end date + * + * @param caption + * The caption for the event + * @param description + * The description for the event + * @param startDate + * The start date of the event + * @param endDate + * The end date of the event + */ + public BasicEvent(String caption, String description, Date startDate, + Date endDate) { + this.caption = caption; + this.description = description; + start = startDate; + end = endDate; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getCaption() + */ + @Override + public String getCaption() { + return caption; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getDescription() + */ + @Override + public String getDescription() { + return description; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd() + */ + @Override + public Date getEnd() { + return end; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart() + */ + @Override + public Date getStart() { + return start; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName() + */ + @Override + public String getStyleName() { + return styleName; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#isAllDay() + */ + @Override + public boolean isAllDay() { + return isAllDay; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setCaption(java.lang + * .String) + */ + @Override + public void setCaption(String caption) { + this.caption = caption; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setDescription(java + * .lang.String) + */ + @Override + public void setDescription(String description) { + this.description = description; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setEnd(java.util. + * Date) + */ + @Override + public void setEnd(Date end) { + this.end = end; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setStart(java.util + * .Date) + */ + @Override + public void setStart(Date start) { + this.start = start; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setStyleName(java + * .lang.String) + */ + @Override + public void setStyleName(String styleName) { + this.styleName = styleName; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setAllDay(boolean) + */ + @Override + public void setAllDay(boolean isAllDay) { + this.isAllDay = isAllDay; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener + * ) + */ + @Override + public void addEventChangeListener(EventChangeListener listener) { + listeners.add(listener); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier + * #removeListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener + * ) + */ + @Override + public void removeEventChangeListener(EventChangeListener listener) { + listeners.remove(listener); + } + + /** + * Fires an event change event to the listeners. Should be triggered when + * some property of the event changes. + */ + protected void fireEventChange() { + EventChangeEvent event = new EventChangeEvent(this); + + for (EventChangeListener listener : listeners) { + listener.eventChange(event); + } + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEventProvider.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEventProvider.java new file mode 100644 index 0000000000..59c8baca9c --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/BasicEventProvider.java @@ -0,0 +1,177 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.event; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent.EventChangeEvent; +import com.vaadin.v7.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier; + +/** + *

    + * Simple implementation of + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider}. Use {@link #addEvent(CalendarEvent)} and + * {@link #removeEvent(CalendarEvent)} to add / remove events. + *

    + * + *

    + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier + * EventSetChangeNotifier} and + * {@link com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener + * EventChangeListener} are also implemented, so the Calendar is notified when + * an event is added, changed or removed. + *

    + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEventProvider implements CalendarEditableEventProvider, + EventSetChangeNotifier, CalendarEvent.EventChangeListener { + + protected List eventList = new ArrayList(); + + private List listeners = new ArrayList(); + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. + * util.Date, java.util.Date) + */ + @Override + public List getEvents(Date startDate, Date endDate) { + ArrayList activeEvents = new ArrayList(); + + for (CalendarEvent ev : eventList) { + long from = startDate.getTime(); + long to = endDate.getTime(); + + if (ev.getStart() != null && ev.getEnd() != null) { + long f = ev.getStart().getTime(); + long t = ev.getEnd().getTime(); + // Select only events that overlaps with startDate and + // endDate. + if ((f <= to && f >= from) || (t >= from && t <= to) + || (f <= from && t >= to)) { + activeEvents.add(ev); + } + } + } + + return activeEvents; + } + + /** + * Does this event provider container this event + * + * @param event + * The event to check for + * @return If this provider has the event then true is returned, else false + */ + public boolean containsEvent(BasicEvent event) { + return eventList.contains(event); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents. + * EventSetChangeNotifier #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents. + * EventSetChangeListener ) + */ + @Override + public void addEventSetChangeListener(EventSetChangeListener listener) { + listeners.add(listener); + + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents. + * EventSetChangeNotifier #removeListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents. + * EventSetChangeListener ) + */ + @Override + public void removeEventSetChangeListener(EventSetChangeListener listener) { + listeners.remove(listener); + } + + /** + * Fires a eventsetchange event. The event is fired when either an event is + * added or removed to the event provider + */ + protected void fireEventSetChange() { + EventSetChangeEvent event = new EventSetChangeEvent(this); + + for (EventSetChangeListener listener : listeners) { + listener.eventSetChange(event); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener + * #eventChange + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChange) + */ + @Override + public void eventChange(EventChangeEvent changeEvent) { + // naive implementation + fireEventSetChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void addEvent(CalendarEvent event) { + eventList.add(event); + if (event instanceof BasicEvent) { + ((BasicEvent) event).addEventChangeListener(this); + } + fireEventSetChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void removeEvent(CalendarEvent event) { + eventList.remove(event); + if (event instanceof BasicEvent) { + ((BasicEvent) event).removeEventChangeListener(this); + } + fireEventSetChange(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEditableEventProvider.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEditableEventProvider.java new file mode 100644 index 0000000000..90f8992720 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEditableEventProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.v7.ui.components.calendar.event; + +/** + * An event provider which allows adding and removing events + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +public interface CalendarEditableEventProvider extends CalendarEventProvider { + + /** + * Adds an event to the event provider + * + * @param event + * The event to add + */ + void addEvent(CalendarEvent event); + + /** + * Removes an event from the event provider + * + * @param event + * The event + */ + void removeEvent(CalendarEvent event); +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEvent.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEvent.java new file mode 100644 index 0000000000..9aaa1cd5ba --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEvent.java @@ -0,0 +1,147 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.event; + +import java.io.Serializable; +import java.util.Date; + +/** + *

    + * Event in the calendar. Customize your own event by implementing this + * interface. + *

    + * + *
  • Start and end fields are mandatory.
  • + * + *
  • In "allDay" events longer than one day, starting and ending clock times + * are omitted in UI and only dates are shown.
  • + * + * @since 7.1.0 + * @author Vaadin Ltd. + * + */ +public interface CalendarEvent extends Serializable { + + /** + * Gets start date of event. + * + * @return Start date. + */ + public Date getStart(); + + /** + * Get end date of event. + * + * @return End date; + */ + public Date getEnd(); + + /** + * Gets caption of event. + * + * @return Caption text + */ + public String getCaption(); + + /** + * Gets description of event. Shown as a tooltip over the event. + * + * @return Description text. + */ + public String getDescription(); + + /** + *

    + * Gets style name of event. In the client, style name will be set to the + * event's element class name and can be styled by CSS + *

    + * Styling example:
    + * Java code:
    + * event.setStyleName("color1"); + *

    + * CSS:
    + * .v-calendar-event-color1 {
    + *    background-color: #9effae;
    }
    + * + * @return Style name. + */ + public String getStyleName(); + + /** + * An all-day event typically does not occur at a specific time but targets + * a whole day or days. The rendering of all-day events differs from normal + * events. + * + * @return true if this event is an all-day event, false otherwise + */ + public boolean isAllDay(); + + /** + * Event to signal that an event has changed. + */ + @SuppressWarnings("serial") + public class EventChangeEvent implements Serializable { + + private CalendarEvent source; + + public EventChangeEvent(CalendarEvent source) { + this.source = source; + } + + /** + * @return the {@link com.vaadin.addon.calendar.event.CalendarEvent + * CalendarEvent} that has changed + */ + public CalendarEvent getCalendarEvent() { + return source; + } + } + + /** + * Listener for EventSetChange events. + */ + public interface EventChangeListener extends Serializable { + + /** + * Called when an Event has changed. + */ + public void eventChange(EventChangeEvent eventChangeEvent); + } + + /** + * Notifier interface for EventChange events. + */ + public interface EventChangeNotifier extends Serializable { + + /** + * Add a listener to listen for EventChangeEvents. These events are + * fired when a events properties are changed. + * + * @param listener + * The listener to add + */ + void addEventChangeListener(EventChangeListener listener); + + /** + * Remove a listener from the event provider. + * + * @param listener + * The listener to remove + */ + void removeEventChangeListener(EventChangeListener listener); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEventProvider.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEventProvider.java new file mode 100644 index 0000000000..bef6aaea18 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/CalendarEventProvider.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.event; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * Interface for querying events. The Vaadin Calendar always has a + * CalendarEventProvider set. + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +public interface CalendarEventProvider extends Serializable { + /** + *

    + * Gets all available events in the target date range between startDate and + * endDate. The Vaadin Calendar queries the events from the range that is + * shown, which is not guaranteed to be the same as the date range that is + * set. + *

    + * + *

    + * For example, if you set the date range to be monday 22.2.2010 - wednesday + * 24.2.2010, the used Event Provider will be queried for events between + * monday 22.2.2010 00:00 and sunday 28.2.2010 23:59. Generally you can + * expect the date range to be expanded to whole days and whole weeks. + *

    + * + * @param startDate + * Start date + * @param endDate + * End date + * @return List of events + */ + public List getEvents(Date startDate, Date endDate); + + /** + * Event to signal that the set of events has changed and the calendar + * should refresh its view from the + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} . + * + */ + @SuppressWarnings("serial") + public class EventSetChangeEvent implements Serializable { + + private CalendarEventProvider source; + + public EventSetChangeEvent(CalendarEventProvider source) { + this.source = source; + } + + /** + * @return the + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} that has changed + */ + public CalendarEventProvider getProvider() { + return source; + } + } + + /** + * Listener for EventSetChange events. + */ + public interface EventSetChangeListener extends Serializable { + + /** + * Called when the set of Events has changed. + */ + public void eventSetChange(EventSetChangeEvent changeEvent); + } + + /** + * Notifier interface for EventSetChange events. + */ + public interface EventSetChangeNotifier extends Serializable { + + /** + * Add a listener for listening to when new events are adding or removed + * from the event provider. + * + * @param listener + * The listener to add + */ + void addEventSetChangeListener(EventSetChangeListener listener); + + /** + * Remove a listener which listens to {@link EventSetChangeEvent}-events + * + * @param listener + * The listener to remove + */ + void removeEventSetChangeListener(EventSetChangeListener listener); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/EditableCalendarEvent.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/EditableCalendarEvent.java new file mode 100644 index 0000000000..cb3553ec87 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/event/EditableCalendarEvent.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.event; + +import java.util.Date; + +/** + *

    + * Extension to the basic {@link com.vaadin.addon.calendar.event.CalendarEvent + * CalendarEvent}. This interface provides setters (and thus editing + * capabilities) for all {@link com.vaadin.addon.calendar.event.CalendarEvent + * CalendarEvent} fields. For descriptions on the fields, refer to the extended + * interface. + *

    + * + *

    + * This interface is used by some of the basic Calendar event handlers in the + * com.vaadin.addon.calendar.ui.handler package to determine + * whether an event can be edited. + *

    + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public interface EditableCalendarEvent extends CalendarEvent { + + /** + * Set the visible text in the calendar for the event. + * + * @param caption + * The text to show in the calendar + */ + void setCaption(String caption); + + /** + * Set the description of the event. This is shown in the calendar when + * hoovering over the event. + * + * @param description + * The text which describes the event + */ + void setDescription(String description); + + /** + * Set the end date of the event. Must be after the start date. + * + * @param end + * The end date to set + */ + void setEnd(Date end); + + /** + * Set the start date for the event. Must be before the end date + * + * @param start + * The start date of the event + */ + void setStart(Date start); + + /** + * Set the style name for the event used for styling the event cells + * + * @param styleName + * The stylename to use + * + */ + void setStyleName(String styleName); + + /** + * Does the event span the whole day. If so then set this to true + * + * @param isAllDay + * True if the event spans the whole day. In this case the start + * and end times are ignored. + */ + void setAllDay(boolean isAllDay); + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicBackwardHandler.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicBackwardHandler.java new file mode 100644 index 0000000000..956db6b179 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicBackwardHandler.java @@ -0,0 +1,96 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; + +import com.vaadin.shared.ui.calendar.DateConstants; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.BackwardEvent; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.BackwardHandler; + +/** + * Implements basic functionality needed to enable backwards navigation. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicBackwardHandler implements BackwardHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler# + * backward + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardEvent) + */ + @Override + public void backward(BackwardEvent event) { + Date start = event.getComponent().getStartDate(); + Date end = event.getComponent().getEndDate(); + + // calculate amount to move back + int durationInDays = (int) (((end.getTime()) - start.getTime()) + / DateConstants.DAYINMILLIS); + durationInDays++; + // for week view durationInDays = -7, for day view durationInDays = -1 + durationInDays = -durationInDays; + + // set new start and end times + Calendar javaCalendar = event.getComponent().getInternalCalendar(); + javaCalendar.setTime(start); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newStart = javaCalendar.getTime(); + + javaCalendar.setTime(end); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newEnd = javaCalendar.getTime(); + + if (start.equals(end)) { // day view + int firstDay = event.getComponent().getFirstVisibleDayOfWeek(); + int lastDay = event.getComponent().getLastVisibleDayOfWeek(); + int dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); + + // we suppose that 7 >= lastDay >= firstDay >= 1 + while (!(firstDay <= dayOfWeek && dayOfWeek <= lastDay)) { + javaCalendar.add(java.util.Calendar.DATE, -1); + dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); + } + + newStart = javaCalendar.getTime(); + newEnd = javaCalendar.getTime(); + } + + setDates(event, newStart, newEnd); + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(BackwardEvent event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicDateClickHandler.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicDateClickHandler.java new file mode 100644 index 0000000000..381acb6b1d --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicDateClickHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; + +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.DateClickEvent; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.DateClickHandler; + +/** + * Implements basic functionality needed to switch to day view when a single day + * is clicked. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicDateClickHandler implements DateClickHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler + * #dateClick + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickEvent) + */ + @Override + public void dateClick(DateClickEvent event) { + Date clickedDate = event.getDate(); + + Calendar javaCalendar = event.getComponent().getInternalCalendar(); + javaCalendar.setTime(clickedDate); + + // as times are expanded, this is all that is needed to show one day + Date start = javaCalendar.getTime(); + Date end = javaCalendar.getTime(); + + setDates(event, start, end); + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(DateClickEvent event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventMoveHandler.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventMoveHandler.java new file mode 100644 index 0000000000..be27b606fe --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventMoveHandler.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.handler; + +import java.util.Date; + +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.MoveEvent; +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent; +import com.vaadin.v7.ui.components.calendar.event.EditableCalendarEvent; + +/** + * Implements basic functionality needed to enable moving events. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEventMoveHandler implements EventMoveHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler + * #eventMove + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent) + */ + @Override + public void eventMove(MoveEvent event) { + CalendarEvent calendarEvent = event.getCalendarEvent(); + + if (calendarEvent instanceof EditableCalendarEvent) { + + EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent; + + Date newFromTime = event.getNewStart(); + + // Update event dates + long length = editableEvent.getEnd().getTime() + - editableEvent.getStart().getTime(); + setDates(editableEvent, newFromTime, + new Date(newFromTime.getTime() + length)); + } + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(EditableCalendarEvent event, Date start, Date end) { + event.setStart(start); + event.setEnd(end); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventResizeHandler.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventResizeHandler.java new file mode 100644 index 0000000000..9b6f08ff09 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicEventResizeHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.handler; + +import java.util.Date; + +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventResize; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; +import com.vaadin.v7.ui.components.calendar.event.CalendarEvent; +import com.vaadin.v7.ui.components.calendar.event.EditableCalendarEvent; + +/** + * Implements basic functionality needed to enable event resizing. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEventResizeHandler implements EventResizeHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler + * #eventResize + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize) + */ + @Override + public void eventResize(EventResize event) { + CalendarEvent calendarEvent = event.getCalendarEvent(); + + if (calendarEvent instanceof EditableCalendarEvent) { + Date newStartTime = event.getNewStart(); + Date newEndTime = event.getNewEnd(); + + EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent; + + setDates(editableEvent, newStartTime, newEndTime); + } + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(EditableCalendarEvent event, Date start, Date end) { + event.setStart(start); + event.setEnd(end); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicForwardHandler.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicForwardHandler.java new file mode 100644 index 0000000000..d71958536e --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicForwardHandler.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; + +import com.vaadin.shared.ui.calendar.DateConstants; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.ForwardEvent; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.ForwardHandler; + +/** + * Implements basic functionality needed to enable forward navigation. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicForwardHandler implements ForwardHandler { + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler# + * forward + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardEvent) + */ + @Override + public void forward(ForwardEvent event) { + Date start = event.getComponent().getStartDate(); + Date end = event.getComponent().getEndDate(); + + // calculate amount to move forward + int durationInDays = (int) (((end.getTime()) - start.getTime()) + / DateConstants.DAYINMILLIS); + // for week view durationInDays = 7, for day view durationInDays = 1 + durationInDays++; + + // set new start and end times + Calendar javaCalendar = Calendar.getInstance(); + javaCalendar.setTime(start); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newStart = javaCalendar.getTime(); + + javaCalendar.setTime(end); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newEnd = javaCalendar.getTime(); + + if (start.equals(end)) { // day view + int firstDay = event.getComponent().getFirstVisibleDayOfWeek(); + int lastDay = event.getComponent().getLastVisibleDayOfWeek(); + int dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); + + // we suppose that 7 >= lastDay >= firstDay >= 1 + while (!(firstDay <= dayOfWeek && dayOfWeek <= lastDay)) { + javaCalendar.add(java.util.Calendar.DATE, 1); + dayOfWeek = javaCalendar.get(Calendar.DAY_OF_WEEK); + } + + newStart = javaCalendar.getTime(); + newEnd = javaCalendar.getTime(); + } + + setDates(event, newStart, newEnd); + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(ForwardEvent event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicWeekClickHandler.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicWeekClickHandler.java new file mode 100644 index 0000000000..420d0a76f6 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/calendar/handler/BasicWeekClickHandler.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.v7.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.WeekClick; +import com.vaadin.v7.ui.components.calendar.CalendarComponentEvents.WeekClickHandler; + +/** + * Implements basic functionality needed to change to week view when a week + * number is clicked. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicWeekClickHandler implements WeekClickHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler + * #weekClick + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClick) + */ + @Override + public void weekClick(WeekClick event) { + int week = event.getWeek(); + int year = event.getYear(); + + // set correct year and month + Calendar javaCalendar = event.getComponent().getInternalCalendar(); + javaCalendar.set(GregorianCalendar.YEAR, year); + javaCalendar.set(GregorianCalendar.WEEK_OF_YEAR, week); + + // starting at the beginning of the week + javaCalendar.set(GregorianCalendar.DAY_OF_WEEK, + javaCalendar.getFirstDayOfWeek()); + Date start = javaCalendar.getTime(); + + // ending at the end of the week + javaCalendar.add(GregorianCalendar.DATE, 6); + Date end = javaCalendar.getTime(); + + setDates(event, start, end); + + // times are automatically expanded, no need to worry about them + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(WeekClick event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeEvent.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeEvent.java new file mode 100644 index 0000000000..da0b435ddc --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeEvent.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.v7.ui.components.colorpicker; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.Component; +import com.vaadin.ui.Component.Event; + +/** + * The color changed event which is passed to the listeners when a color change + * occurs. + * + * @since 7.0.0 + */ +public class ColorChangeEvent extends Event { + private final Color color; + + public ColorChangeEvent(Component source, Color color) { + super(source); + + this.color = color; + } + + /** + * Returns the new color. + */ + public Color getColor() { + return color; + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeListener.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeListener.java new file mode 100644 index 0000000000..87f0046242 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorChangeListener.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import java.io.Serializable; + +/** + * The listener interface for receiving colorChange events. The class that is + * interested in processing a {@link ColorChangeEvent} implements this + * interface, and the object created with that class is registered with a + * component using the component's addColorChangeListener method. + * When the colorChange event occurs, that object's appropriate method is + * invoked. + * + * @since 7.0.0 + * + * @see ColorChangeEvent + */ +public interface ColorChangeListener extends Serializable { + + /** + * Called when a new color has been selected. + * + * @param event + * An event containing information about the color change. + */ + void colorChanged(ColorChangeEvent event); + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGradient.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGradient.java new file mode 100644 index 0000000000..cb9dd698d9 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGradient.java @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import java.lang.reflect.Method; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.shared.ui.colorpicker.ColorPickerGradientServerRpc; +import com.vaadin.shared.ui.colorpicker.ColorPickerGradientState; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.v7.ui.AbstractColorPicker.Coordinates2Color; + +/** + * A component that represents a color gradient within a color picker. + * + * @since 7.0.0 + */ +public class ColorPickerGradient extends AbstractComponent + implements ColorSelector { + + private static final Method COLOR_CHANGE_METHOD; + static { + try { + COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( + "colorChanged", new Class[] { ColorChangeEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException( + "Internal error finding methods in ColorPicker"); + } + } + + private ColorPickerGradientServerRpc rpc = new ColorPickerGradientServerRpc() { + + @Override + public void select(int cursorX, int cursorY) { + x = cursorX; + y = cursorY; + color = converter.calculate(x, y); + + fireColorChanged(color); + } + }; + + /** The converter. */ + private Coordinates2Color converter; + + /** The foreground color. */ + private Color color; + + /** The x-coordinate. */ + private int x = 0; + + /** The y-coordinate. */ + private int y = 0; + + private ColorPickerGradient() { + registerRpc(rpc); + // width and height must be set here instead of in theme, otherwise + // coordinate calculations fail + getState().width = "220px"; + getState().height = "220px"; + } + + /** + * Instantiates a new color picker gradient. + * + * @param id + * the id + * @param converter + * the converter + */ + public ColorPickerGradient(String id, Coordinates2Color converter) { + this(); + addStyleName(id); + this.converter = converter; + } + + @Override + public void setColor(Color c) { + color = c; + + int[] coords = converter.calculate(c); + x = coords[0]; + y = coords[1]; + + getState().cursorX = x; + getState().cursorY = y; + + } + + @Override + public void addColorChangeListener(ColorChangeListener listener) { + addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); + } + + @Override + public void removeColorChangeListener(ColorChangeListener listener) { + removeListener(ColorChangeEvent.class, listener); + } + + /** + * Sets the background color. + * + * @param color + * the new background color + */ + public void setBackgroundColor(Color color) { + getState().bgColor = color.getCSS(); + } + + @Override + public Color getColor() { + return color; + } + + /** + * Notifies the listeners that the color has changed + * + * @param color + * The color which it changed to + */ + public void fireColorChanged(Color color) { + fireEvent(new ColorChangeEvent(this, color)); + } + + @Override + protected ColorPickerGradientState getState() { + return (ColorPickerGradientState) super.getState(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGrid.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGrid.java new file mode 100644 index 0000000000..a1286dbc58 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerGrid.java @@ -0,0 +1,258 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import java.awt.Point; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.shared.ui.colorpicker.ColorPickerGridServerRpc; +import com.vaadin.shared.ui.colorpicker.ColorPickerGridState; +import com.vaadin.ui.AbstractComponent; + +/** + * A component that represents a color selection grid within a color picker. + * + * @since 7.0.0 + */ +public class ColorPickerGrid extends AbstractComponent + implements ColorSelector { + + private static final String STYLENAME = "v-colorpicker-grid"; + + private static final Method COLOR_CHANGE_METHOD; + static { + try { + COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( + "colorChanged", new Class[] { ColorChangeEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException( + "Internal error finding methods in ColorPicker"); + } + } + + private ColorPickerGridServerRpc rpc = new ColorPickerGridServerRpc() { + + @Override + public void select(int x, int y) { + ColorPickerGrid.this.x = x; + ColorPickerGrid.this.y = y; + + fireColorChanged(colorGrid[y][x]); + } + + @Override + public void refresh() { + for (int row = 0; row < rows; row++) { + for (int col = 0; col < columns; col++) { + changedColors.put(new Point(row, col), colorGrid[row][col]); + } + } + sendChangedColors(); + markAsDirty(); + } + }; + + /** The x-coordinate. */ + private int x = 0; + + /** The y-coordinate. */ + private int y = 0; + + /** The rows. */ + private int rows; + + /** The columns. */ + private int columns; + + /** The color grid. */ + private Color[][] colorGrid = new Color[1][1]; + + /** The changed colors. */ + private final Map changedColors = new HashMap(); + + /** + * Instantiates a new color picker grid. + */ + public ColorPickerGrid() { + registerRpc(rpc); + setPrimaryStyleName(STYLENAME); + setColorGrid(new Color[1][1]); + setColor(Color.WHITE); + } + + /** + * Instantiates a new color picker grid. + * + * @param rows + * the rows + * @param columns + * the columns + */ + public ColorPickerGrid(int rows, int columns) { + registerRpc(rpc); + setPrimaryStyleName(STYLENAME); + setColorGrid(new Color[rows][columns]); + setColor(Color.WHITE); + } + + /** + * Instantiates a new color picker grid. + * + * @param colors + * the colors + */ + public ColorPickerGrid(Color[][] colors) { + registerRpc(rpc); + setPrimaryStyleName(STYLENAME); + setColorGrid(colors); + } + + private void setColumnCount(int columns) { + this.columns = columns; + getState().columnCount = columns; + } + + private void setRowCount(int rows) { + this.rows = rows; + getState().rowCount = rows; + } + + private void sendChangedColors() { + if (!changedColors.isEmpty()) { + String[] colors = new String[changedColors.size()]; + String[] XCoords = new String[changedColors.size()]; + String[] YCoords = new String[changedColors.size()]; + int counter = 0; + for (Point p : changedColors.keySet()) { + Color c = changedColors.get(p); + if (c == null) { + continue; + } + + String color = c.getCSS(); + + colors[counter] = color; + XCoords[counter] = String.valueOf((int) p.getX()); + YCoords[counter] = String.valueOf((int) p.getY()); + counter++; + } + getState().changedColor = colors; + getState().changedX = XCoords; + getState().changedY = YCoords; + + changedColors.clear(); + } + } + + /** + * Sets the color grid. + * + * @param colors + * the new color grid + */ + public void setColorGrid(Color[][] colors) { + setRowCount(colors.length); + setColumnCount(colors[0].length); + colorGrid = colors; + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < columns; col++) { + changedColors.put(new Point(row, col), colorGrid[row][col]); + } + } + sendChangedColors(); + + markAsDirty(); + } + + /** + * Adds a color change listener + * + * @param listener + * The color change listener + */ + @Override + public void addColorChangeListener(ColorChangeListener listener) { + addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); + } + + @Override + public Color getColor() { + return colorGrid[x][y]; + } + + /** + * Removes a color change listener + * + * @param listener + * The listener + */ + @Override + public void removeColorChangeListener(ColorChangeListener listener) { + removeListener(ColorChangeEvent.class, listener); + } + + @Override + public void setColor(Color color) { + colorGrid[x][y] = color; + changedColors.put(new Point(x, y), color); + sendChangedColors(); + markAsDirty(); + } + + /** + * Sets the position. + * + * @param x + * the x + * @param y + * the y + */ + public void setPosition(int x, int y) { + if (x >= 0 && x < columns && y >= 0 && y < rows) { + this.x = x; + this.y = y; + } + } + + /** + * Gets the position. + * + * @return the position + */ + public int[] getPosition() { + return new int[] { x, y }; + } + + /** + * Notifies the listeners that a color change has occurred + * + * @param color + * The color which it changed to + */ + public void fireColorChanged(Color color) { + fireEvent(new ColorChangeEvent(this, color)); + } + + @Override + protected ColorPickerGridState getState() { + return (ColorPickerGridState) super.getState(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerHistory.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerHistory.java new file mode 100644 index 0000000000..e26e802a32 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerHistory.java @@ -0,0 +1,217 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.CustomComponent; + +/** + * A component that represents color selection history within a color picker. + * + * @since 7.0.0 + */ +public class ColorPickerHistory extends CustomComponent + implements ColorSelector, ColorChangeListener { + + private static final String STYLENAME = "v-colorpicker-history"; + + private static final Method COLOR_CHANGE_METHOD; + static { + try { + COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( + "colorChanged", new Class[] { ColorChangeEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException( + "Internal error finding methods in ColorPicker"); + } + } + + /** The rows. */ + private static final int rows = 4; + + /** The columns. */ + private static final int columns = 15; + + /** Temporary color history for when the component is detached. */ + private ArrayBlockingQueue tempHistory = new ArrayBlockingQueue( + rows * columns); + + /** The grid. */ + private final ColorPickerGrid grid; + + /** + * Instantiates a new color picker history. + */ + public ColorPickerHistory() { + setPrimaryStyleName(STYLENAME); + + grid = new ColorPickerGrid(rows, columns); + grid.setWidth("100%"); + grid.setPosition(0, 0); + grid.addColorChangeListener(this); + + setCompositionRoot(grid); + } + + @Override + public void attach() { + super.attach(); + createColorHistoryIfNecessary(); + } + + private void createColorHistoryIfNecessary() { + List tempColors = new ArrayList(tempHistory); + if (getSession().getAttribute("colorPickerHistory") == null) { + getSession().setAttribute("colorPickerHistory", + new ArrayBlockingQueue(rows * columns)); + } + for (Color color : tempColors) { + setColor(color); + } + tempHistory.clear(); + } + + @SuppressWarnings("unchecked") + private ArrayBlockingQueue getColorHistory() { + if (isAttached()) { + Object colorHistory = getSession() + .getAttribute("colorPickerHistory"); + if (colorHistory instanceof ArrayBlockingQueue) { + return (ArrayBlockingQueue) colorHistory; + } + } + return tempHistory; + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + grid.setHeight(height); + } + + @Override + public void setColor(Color color) { + + ArrayBlockingQueue colorHistory = getColorHistory(); + + // Check that the color does not already exist + boolean exists = false; + Iterator iter = colorHistory.iterator(); + while (iter.hasNext()) { + if (color.equals(iter.next())) { + exists = true; + break; + } + } + + // If the color does not exist then add it + if (!exists) { + if (!colorHistory.offer(color)) { + colorHistory.poll(); + colorHistory.offer(color); + } + } + + List colorList = new ArrayList(colorHistory); + + // Invert order of colors + Collections.reverse(colorList); + + // Move the selected color to the front of the list + Collections.swap(colorList, colorList.indexOf(color), 0); + + // Create 2d color map + Color[][] colors = new Color[rows][columns]; + iter = colorList.iterator(); + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < columns; col++) { + if (iter.hasNext()) { + colors[row][col] = iter.next(); + } else { + colors[row][col] = Color.WHITE; + } + } + } + + grid.setColorGrid(colors); + grid.markAsDirty(); + } + + @Override + public Color getColor() { + return getColorHistory().peek(); + } + + /** + * Gets the history. + * + * @return the history + */ + public List getHistory() { + ArrayBlockingQueue colorHistory = getColorHistory(); + Color[] array = colorHistory.toArray(new Color[colorHistory.size()]); + return Collections.unmodifiableList(Arrays.asList(array)); + } + + /** + * Checks if the history contains given color. + * + * @param c + * the color + * + * @return true, if successful + */ + public boolean hasColor(Color c) { + return getColorHistory().contains(c); + } + + /** + * Adds a color change listener + * + * @param listener + * The listener + */ + @Override + public void addColorChangeListener(ColorChangeListener listener) { + addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); + } + + /** + * Removes a color change listener + * + * @param listener + * The listener + */ + @Override + public void removeColorChangeListener(ColorChangeListener listener) { + removeListener(ColorChangeEvent.class, listener); + } + + @Override + public void colorChanged(ColorChangeEvent event) { + fireEvent(new ColorChangeEvent(this, event.getColor())); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPopup.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPopup.java new file mode 100644 index 0000000000..39931ce570 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPopup.java @@ -0,0 +1,759 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Component; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Layout; +import com.vaadin.ui.Slider; +import com.vaadin.ui.Slider.ValueOutOfBoundsException; +import com.vaadin.ui.TabSheet; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.Window; +import com.vaadin.v7.ui.AbstractColorPicker.Coordinates2Color; + +/** + * A component that represents color selection popup within a color picker. + * + * @since 7.0.0 + */ +public class ColorPickerPopup extends Window + implements ClickListener, ColorChangeListener, ColorSelector { + + private static final String STYLENAME = "v-colorpicker-popup"; + + private static final Method COLOR_CHANGE_METHOD; + static { + try { + COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( + "colorChanged", new Class[] { ColorChangeEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException( + "Internal error finding methods in ColorPicker"); + } + } + + /** The tabs. */ + private final TabSheet tabs = new TabSheet(); + + private Component rgbTab; + + private Component hsvTab; + + private Component swatchesTab; + + /** The layout. */ + private final VerticalLayout layout; + + /** The ok button. */ + private final Button ok = new Button("OK"); + + /** The cancel button. */ + private final Button cancel = new Button("Cancel"); + + /** The resize button. */ + private final Button resize = new Button("show/hide history"); + + /** The selected color. */ + private Color selectedColor = Color.WHITE; + + /** The history. */ + private ColorPickerHistory history; + + /** The history container. */ + private Layout historyContainer; + + /** The rgb gradient. */ + private ColorPickerGradient rgbGradient; + + /** The hsv gradient. */ + private ColorPickerGradient hsvGradient; + + /** The red slider. */ + private Slider redSlider; + + /** The green slider. */ + private Slider greenSlider; + + /** The blue slider. */ + private Slider blueSlider; + + /** The hue slider. */ + private Slider hueSlider; + + /** The saturation slider. */ + private Slider saturationSlider; + + /** The value slider. */ + private Slider valueSlider; + + /** The preview on the rgb tab. */ + private ColorPickerPreview rgbPreview; + + /** The preview on the hsv tab. */ + private ColorPickerPreview hsvPreview; + + /** The preview on the swatches tab. */ + private ColorPickerPreview selPreview; + + /** The color select. */ + private ColorPickerSelect colorSelect; + + /** The selectors. */ + private final Set selectors = new HashSet(); + + /** + * Set true while the slider values are updated after colorChange. When + * true, valueChange reactions from the sliders are disabled, because + * otherwise the set color may become corrupted as it is repeatedly re-set + * in valueChangeListeners using values from sliders that may not have been + * updated yet. + */ + private boolean updatingColors = false; + + private ColorPickerPopup() { + // Set the layout + layout = new VerticalLayout(); + layout.setSpacing(false); + layout.setMargin(false); + layout.setWidth("100%"); + layout.setHeight(null); + + setContent(layout); + setStyleName(STYLENAME); + setResizable(false); + setImmediate(true); + // Create the history + history = new ColorPickerHistory(); + history.addColorChangeListener(this); + } + + /** + * Instantiates a new color picker popup. + */ + public ColorPickerPopup(Color initialColor) { + this(); + selectedColor = initialColor; + initContents(); + } + + private void initContents() { + // Create the preview on the rgb tab + rgbPreview = new ColorPickerPreview(selectedColor); + rgbPreview.setWidth("240px"); + rgbPreview.setHeight("20px"); + rgbPreview.addColorChangeListener(this); + selectors.add(rgbPreview); + + // Create the preview on the hsv tab + hsvPreview = new ColorPickerPreview(selectedColor); + hsvPreview.setWidth("240px"); + hsvPreview.setHeight("20px"); + hsvPreview.addColorChangeListener(this); + selectors.add(hsvPreview); + + // Create the preview on the swatches tab + selPreview = new ColorPickerPreview(selectedColor); + selPreview.setWidth("100%"); + selPreview.setHeight("20px"); + selPreview.addColorChangeListener(this); + selectors.add(selPreview); + + // Create the tabs + rgbTab = createRGBTab(selectedColor); + tabs.addTab(rgbTab, "RGB", null); + + hsvTab = createHSVTab(selectedColor); + tabs.addTab(hsvTab, "HSV", null); + + swatchesTab = createSelectTab(); + tabs.addTab(swatchesTab, "Swatches", null); + + // Add the tabs + tabs.setWidth("100%"); + + layout.addComponent(tabs); + + // Add the history + history.setWidth("97%"); + history.setHeight("22px"); + + // Create the default colors + List defaultColors = new ArrayList(); + defaultColors.add(Color.BLACK); + defaultColors.add(Color.WHITE); + + // Create the history + VerticalLayout innerContainer = new VerticalLayout(); + innerContainer.setWidth("100%"); + innerContainer.setHeight(null); + innerContainer.addComponent(history); + + VerticalLayout outerContainer = new VerticalLayout(); + outerContainer.setWidth("99%"); + outerContainer.setHeight("27px"); + outerContainer.addComponent(innerContainer); + historyContainer = outerContainer; + + layout.addComponent(historyContainer); + + // Add the resize button for the history + resize.addClickListener(this); + resize.setData(new Boolean(false)); + resize.setWidth("100%"); + resize.setHeight("10px"); + resize.setPrimaryStyleName("resize-button"); + layout.addComponent(resize); + + // Add the buttons + ok.setWidth("70px"); + ok.addClickListener(this); + + cancel.setWidth("70px"); + cancel.addClickListener(this); + + HorizontalLayout buttons = new HorizontalLayout(); + buttons.addComponent(ok); + buttons.addComponent(cancel); + buttons.setWidth("100%"); + buttons.setHeight("30px"); + buttons.setComponentAlignment(ok, Alignment.MIDDLE_CENTER); + buttons.setComponentAlignment(cancel, Alignment.MIDDLE_CENTER); + layout.addComponent(buttons); + } + + /** + * Creates the RGB tab. + * + * @return the component + */ + private Component createRGBTab(Color color) { + VerticalLayout rgbLayout = new VerticalLayout(); + rgbLayout.setMargin(new MarginInfo(false, false, true, false)); + rgbLayout.addComponent(rgbPreview); + rgbLayout.setStyleName("rgbtab"); + + // Add the RGB color gradient + rgbGradient = new ColorPickerGradient("rgb-gradient", RGBConverter); + rgbGradient.setColor(color); + rgbGradient.addColorChangeListener(this); + rgbLayout.addComponent(rgbGradient); + selectors.add(rgbGradient); + + // Add the RGB sliders + VerticalLayout sliders = new VerticalLayout(); + sliders.setStyleName("rgb-sliders"); + + redSlider = createRGBSlider("Red", "red"); + greenSlider = createRGBSlider("Green", "green"); + blueSlider = createRGBSlider("Blue", "blue"); + setRgbSliderValues(color); + + redSlider.addValueChangeListener(e -> { + double red = e.getValue(); + if (!updatingColors) { + Color newColor = new Color((int) red, selectedColor.getGreen(), + selectedColor.getBlue()); + setColor(newColor); + } + }); + + sliders.addComponent(redSlider); + + greenSlider.addValueChangeListener(e -> { + double green = e.getValue(); + if (!updatingColors) { + Color newColor = new Color(selectedColor.getRed(), (int) green, + selectedColor.getBlue()); + setColor(newColor); + } + }); + sliders.addComponent(greenSlider); + + blueSlider.addValueChangeListener(e -> { + double blue = e.getValue(); + if (!updatingColors) { + Color newColor = new Color(selectedColor.getRed(), + selectedColor.getGreen(), (int) blue); + setColor(newColor); + } + }); + sliders.addComponent(blueSlider); + + rgbLayout.addComponent(sliders); + + return rgbLayout; + } + + private Slider createRGBSlider(String caption, String styleName) { + Slider redSlider = new Slider(caption, 0, 255); + redSlider.setImmediate(true); + redSlider.setStyleName("rgb-slider"); + redSlider.setWidth("220px"); + redSlider.addStyleName(styleName); + return redSlider; + } + + /** + * Creates the hsv tab. + * + * @return the component + */ + private Component createHSVTab(Color color) { + VerticalLayout hsvLayout = new VerticalLayout(); + hsvLayout.setMargin(new MarginInfo(false, false, true, false)); + hsvLayout.addComponent(hsvPreview); + hsvLayout.setStyleName("hsvtab"); + + // Add the hsv gradient + hsvGradient = new ColorPickerGradient("hsv-gradient", HSVConverter); + hsvGradient.setColor(color); + hsvGradient.addColorChangeListener(this); + hsvLayout.addComponent(hsvGradient); + selectors.add(hsvGradient); + + VerticalLayout sliders = new VerticalLayout(); + sliders.setStyleName("hsv-sliders"); + + hueSlider = new Slider("Hue", 0, 360); + saturationSlider = new Slider("Saturation", 0, 100); + valueSlider = new Slider("Value", 0, 100); + + float[] hsv = color.getHSV(); + setHsvSliderValues(hsv); + + hueSlider.setStyleName("hsv-slider"); + hueSlider.addStyleName("hue-slider"); + hueSlider.setWidth("220px"); + hueSlider.setImmediate(true); + hueSlider.addValueChangeListener(event -> { + if (!updatingColors) { + float hue = (Float.parseFloat(event.getValue().toString())) + / 360f; + float saturation = (Float + .parseFloat(saturationSlider.getValue().toString())) + / 100f; + float value = (Float + .parseFloat(valueSlider.getValue().toString())) / 100f; + + // Set the color + Color newColor = new Color( + Color.HSVtoRGB(hue, saturation, value)); + setColor(newColor); + + /* + * Set the background color of the hue gradient. This has to be + * done here since in the conversion the base color information + * is lost when color is black/white + */ + Color bgColor = new Color(Color.HSVtoRGB(hue, 1f, 1f)); + hsvGradient.setBackgroundColor(bgColor); + } + }); + sliders.addComponent(hueSlider); + + saturationSlider.setStyleName("hsv-slider"); + saturationSlider.setWidth("220px"); + saturationSlider.setImmediate(true); + saturationSlider.addValueChangeListener(event -> { + if (!updatingColors) { + float hue = (Float.parseFloat(hueSlider.getValue().toString())) + / 360f; + float saturation = (Float + .parseFloat(event.getValue().toString())) / 100f; + float value = (Float + .parseFloat(valueSlider.getValue().toString())) / 100f; + Color newColor = new Color( + Color.HSVtoRGB(hue, saturation, value)); + setColor(newColor); + } + }); + sliders.addComponent(saturationSlider); + + valueSlider.setStyleName("hsv-slider"); + valueSlider.setWidth("220px"); + valueSlider.setImmediate(true); + valueSlider.addValueChangeListener(event -> { + if (!updatingColors) { + float hue = (Float.parseFloat(hueSlider.getValue().toString())) + / 360f; + float saturation = (Float + .parseFloat(saturationSlider.getValue().toString())) + / 100f; + float value = (Float.parseFloat(event.getValue().toString())) + / 100f; + + Color newColor = new Color( + Color.HSVtoRGB(hue, saturation, value)); + setColor(newColor); + } + }); + + sliders.addComponent(valueSlider); + hsvLayout.addComponent(sliders); + + return hsvLayout; + } + + /** + * Creates the select tab. + * + * @return the component + */ + private Component createSelectTab() { + VerticalLayout selLayout = new VerticalLayout(); + selLayout.setMargin(new MarginInfo(false, false, true, false)); + selLayout.addComponent(selPreview); + selLayout.addStyleName("seltab"); + + colorSelect = new ColorPickerSelect(); + colorSelect.addColorChangeListener(this); + selLayout.addComponent(colorSelect); + + return selLayout; + } + + @Override + public void buttonClick(ClickEvent event) { + // History resize was clicked + if (event.getButton() == resize) { + boolean state = (Boolean) resize.getData(); + + // minimize + if (state) { + historyContainer.setHeight("27px"); + history.setHeight("22px"); + + // maximize + } else { + historyContainer.setHeight("90px"); + history.setHeight("85px"); + } + + resize.setData(new Boolean(!state)); + } + + // Ok button was clicked + else if (event.getButton() == ok) { + history.setColor(getColor()); + fireColorChanged(); + close(); + } + + // Cancel button was clicked + else if (event.getButton() == cancel) { + close(); + } + + } + + /** + * Notifies the listeners that the color changed + */ + public void fireColorChanged() { + fireEvent(new ColorChangeEvent(this, getColor())); + } + + /** + * Gets the history. + * + * @return the history + */ + public ColorPickerHistory getHistory() { + return history; + } + + @Override + public void setColor(Color color) { + if (color == null) { + return; + } + + selectedColor = color; + + hsvGradient.setColor(selectedColor); + hsvPreview.setColor(selectedColor); + + rgbGradient.setColor(selectedColor); + rgbPreview.setColor(selectedColor); + + selPreview.setColor(selectedColor); + } + + @Override + public Color getColor() { + return selectedColor; + } + + /** + * Gets the color history. + * + * @return the color history + */ + public List getColorHistory() { + return Collections.unmodifiableList(history.getHistory()); + } + + @Override + public void colorChanged(ColorChangeEvent event) { + setColor(event.getColor()); + + updatingColors = true; + + setRgbSliderValues(selectedColor); + float[] hsv = selectedColor.getHSV(); + setHsvSliderValues(hsv); + + updatingColors = false; + + for (ColorSelector s : selectors) { + if (event.getSource() != s && s != this + && s.getColor() != selectedColor) { + s.setColor(selectedColor); + } + } + } + + private void setRgbSliderValues(Color color) { + try { + redSlider.setValue(((Integer) color.getRed()).doubleValue()); + blueSlider.setValue(((Integer) color.getBlue()).doubleValue()); + greenSlider.setValue(((Integer) color.getGreen()).doubleValue()); + } catch (ValueOutOfBoundsException e) { + getLogger().log(Level.WARNING, + "Unable to set RGB color value to " + color.getRed() + "," + + color.getGreen() + "," + color.getBlue(), + e); + } + } + + private void setHsvSliderValues(float[] hsv) { + try { + hueSlider.setValue(((Float) (hsv[0] * 360f)).doubleValue()); + saturationSlider.setValue(((Float) (hsv[1] * 100f)).doubleValue()); + valueSlider.setValue(((Float) (hsv[2] * 100f)).doubleValue()); + } catch (ValueOutOfBoundsException e) { + getLogger().log(Level.WARNING, "Unable to set HSV color value to " + + hsv[0] + "," + hsv[1] + "," + hsv[2], e); + } + } + + @Override + public void addColorChangeListener(ColorChangeListener listener) { + addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); + } + + @Override + public void removeColorChangeListener(ColorChangeListener listener) { + removeListener(ColorChangeEvent.class, listener); + } + + /** + * Checks the visibility of the given tab + * + * @param tab + * The tab to check + * @return true if tab is visible, false otherwise + */ + private boolean tabIsVisible(Component tab) { + Iterator tabIterator = tabs.getComponentIterator(); + while (tabIterator.hasNext()) { + if (tabIterator.next() == tab) { + return true; + } + } + return false; + } + + /** + * How many tabs are visible + * + * @return The number of tabs visible + */ + private int tabsNumVisible() { + Iterator tabIterator = tabs.getComponentIterator(); + int tabCounter = 0; + while (tabIterator.hasNext()) { + tabIterator.next(); + tabCounter++; + } + return tabCounter; + } + + /** + * Checks if tabs are needed and hides them if not + */ + private void checkIfTabsNeeded() { + tabs.hideTabs(tabsNumVisible() == 1); + } + + /** + * Set RGB tab visibility + * + * @param visible + * The visibility of the RGB tab + */ + public void setRGBTabVisible(boolean visible) { + if (visible && !tabIsVisible(rgbTab)) { + tabs.addTab(rgbTab, "RGB", null); + checkIfTabsNeeded(); + } else if (!visible && tabIsVisible(rgbTab)) { + tabs.removeComponent(rgbTab); + checkIfTabsNeeded(); + } + } + + /** + * Set HSV tab visibility + * + * @param visible + * The visibility of the HSV tab + */ + public void setHSVTabVisible(boolean visible) { + if (visible && !tabIsVisible(hsvTab)) { + tabs.addTab(hsvTab, "HSV", null); + checkIfTabsNeeded(); + } else if (!visible && tabIsVisible(hsvTab)) { + tabs.removeComponent(hsvTab); + checkIfTabsNeeded(); + } + } + + /** + * Set Swatches tab visibility + * + * @param visible + * The visibility of the Swatches tab + */ + public void setSwatchesTabVisible(boolean visible) { + if (visible && !tabIsVisible(swatchesTab)) { + tabs.addTab(swatchesTab, "Swatches", null); + checkIfTabsNeeded(); + } else if (!visible && tabIsVisible(swatchesTab)) { + tabs.removeComponent(swatchesTab); + checkIfTabsNeeded(); + } + } + + /** + * Set the History visibility + * + * @param visible + */ + public void setHistoryVisible(boolean visible) { + historyContainer.setVisible(visible); + resize.setVisible(visible); + } + + /** + * Set the preview visibility + * + * @param visible + */ + public void setPreviewVisible(boolean visible) { + hsvPreview.setVisible(visible); + rgbPreview.setVisible(visible); + selPreview.setVisible(visible); + } + + /** RGB color converter */ + private Coordinates2Color RGBConverter = new Coordinates2Color() { + + @Override + public Color calculate(int x, int y) { + float h = (x / 220f); + float s = 1f; + float v = 1f; + + if (y < 110) { + s = y / 110f; + } else if (y > 110) { + v = 1f - (y - 110f) / 110f; + } + + return new Color(Color.HSVtoRGB(h, s, v)); + } + + @Override + public int[] calculate(Color color) { + + float[] hsv = color.getHSV(); + + int x = Math.round(hsv[0] * 220f); + int y = 0; + + // lower half + if (hsv[1] == 1f) { + y = Math.round(110f - (hsv[1] + hsv[2]) * 110f); + } else { + y = Math.round(hsv[1] * 110f); + } + + return new int[] { x, y }; + } + }; + + /** HSV color converter */ + Coordinates2Color HSVConverter = new Coordinates2Color() { + @Override + public int[] calculate(Color color) { + + float[] hsv = color.getHSV(); + + // Calculate coordinates + int x = Math.round(hsv[2] * 220.0f); + int y = Math.round(220 - hsv[1] * 220.0f); + + // Create background color of clean color + Color bgColor = new Color(Color.HSVtoRGB(hsv[0], 1f, 1f)); + hsvGradient.setBackgroundColor(bgColor); + + return new int[] { x, y }; + } + + @Override + public Color calculate(int x, int y) { + float saturation = 1f - (y / 220.0f); + float value = (x / 220.0f); + float hue = Float.parseFloat(hueSlider.getValue().toString()) + / 360f; + + Color color = new Color(Color.HSVtoRGB(hue, saturation, value)); + return color; + } + }; + + private static Logger getLogger() { + return Logger.getLogger(ColorPickerPopup.class.getName()); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPreview.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPreview.java new file mode 100644 index 0000000000..2a5b7c456f --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerPreview.java @@ -0,0 +1,198 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import java.lang.reflect.Method; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.v7.data.Property.ValueChangeEvent; +import com.vaadin.v7.data.Property.ValueChangeListener; +import com.vaadin.v7.ui.TextField; + +/** + * A component that represents color selection preview within a color picker. + * + * @since 7.0.0 + */ +public class ColorPickerPreview extends CssLayout + implements ColorSelector, ValueChangeListener { + + private static final String STYLE_DARK_COLOR = "v-textfield-dark"; + private static final String STYLE_LIGHT_COLOR = "v-textfield-light"; + + private static final Method COLOR_CHANGE_METHOD; + static { + try { + COLOR_CHANGE_METHOD = ColorChangeListener.class.getDeclaredMethod( + "colorChanged", new Class[] { ColorChangeEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException( + "Internal error finding methods in ColorPicker"); + } + } + + /** The color. */ + private Color color; + + /** The field. */ + private final TextField field; + + /** The old value. */ + private String oldValue; + + private ColorPickerPreview() { + setStyleName("v-colorpicker-preview"); + setImmediate(true); + field = new TextField(); + field.setImmediate(true); + field.setSizeFull(); + field.setStyleName("v-colorpicker-preview-textfield"); + field.setData(this); + field.addValueChangeListener(this); + addComponent(field); + } + + /** + * Instantiates a new color picker preview. + */ + public ColorPickerPreview(Color color) { + this(); + setColor(color); + } + + @Override + public void setColor(Color color) { + this.color = color; + + // Unregister listener + field.removeValueChangeListener(this); + + String colorCSS = color.getCSS(); + field.setValue(colorCSS); + + if (field.isValid()) { + oldValue = colorCSS; + } else { + field.setValue(oldValue); + } + + // Re-register listener + field.addValueChangeListener(this); + + // Set the text color + field.removeStyleName(STYLE_DARK_COLOR); + field.removeStyleName(STYLE_LIGHT_COLOR); + if (this.color.getRed() + this.color.getGreen() + + this.color.getBlue() < 3 * 128) { + field.addStyleName(STYLE_DARK_COLOR); + } else { + field.addStyleName(STYLE_LIGHT_COLOR); + } + + markAsDirty(); + } + + @Override + public Color getColor() { + return color; + } + + @Override + public void addColorChangeListener(ColorChangeListener listener) { + addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); + } + + @Override + public void removeColorChangeListener(ColorChangeListener listener) { + removeListener(ColorChangeEvent.class, listener); + } + + @Override + public void valueChange(ValueChangeEvent event) { + String value = (String) event.getProperty().getValue(); + try { + if (value != null) { + /* + * Description of supported formats see + * http://www.w3schools.com/cssref/css_colors_legal.asp + */ + if (value.length() == 7 && value.startsWith("#")) { + // CSS color format (e.g. #000000) + int red = Integer.parseInt(value.substring(1, 3), 16); + int green = Integer.parseInt(value.substring(3, 5), 16); + int blue = Integer.parseInt(value.substring(5, 7), 16); + color = new Color(red, green, blue); + + } else if (value.startsWith("rgb")) { + // RGB color format rgb/rgba(255,255,255,0.1) + String[] colors = value.substring(value.indexOf("(") + 1, + value.length() - 1).split(","); + + int red = Integer.parseInt(colors[0]); + int green = Integer.parseInt(colors[1]); + int blue = Integer.parseInt(colors[2]); + if (colors.length > 3) { + int alpha = (int) (Double.parseDouble(colors[3]) + * 255d); + color = new Color(red, green, blue, alpha); + } else { + color = new Color(red, green, blue); + } + + } else if (value.startsWith("hsl")) { + // HSL color format hsl/hsla(100,50%,50%,1.0) + String[] colors = value.substring(value.indexOf("(") + 1, + value.length() - 1).split(","); + + int hue = Integer.parseInt(colors[0]); + int saturation = Integer + .parseInt(colors[1].replace("%", "")); + int lightness = Integer + .parseInt(colors[2].replace("%", "")); + int rgb = Color.HSLtoRGB(hue, saturation, lightness); + + if (colors.length > 3) { + int alpha = (int) (Double.parseDouble(colors[3]) + * 255d); + color = new Color(rgb); + color.setAlpha(alpha); + } else { + color = new Color(rgb); + } + } + + oldValue = value; + fireEvent(new ColorChangeEvent((Component) field.getData(), + color)); + } + + } catch (NumberFormatException nfe) { + // Revert value + field.setValue(oldValue); + } + } + + /** + * Called when the component is refreshing + */ + @Override + protected String getCss(Component c) { + return "background: " + color.getCSS(); + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerSelect.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerSelect.java new file mode 100644 index 0000000000..87156b34db --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorPickerSelect.java @@ -0,0 +1,235 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.ui.CustomComponent; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.v7.data.Property.ValueChangeEvent; +import com.vaadin.v7.data.Property.ValueChangeListener; +import com.vaadin.v7.ui.ComboBox; + +/** + * A component that represents color selection swatches within a color picker. + * + * @since 7.0.0 + */ +public class ColorPickerSelect extends CustomComponent + implements ColorSelector, ValueChangeListener { + + /** The range. */ + private final ComboBox range; + + /** The grid. */ + private final ColorPickerGrid grid; + + /** + * The Enum ColorRangePropertyId. + */ + private enum ColorRangePropertyId { + ALL("All colors"), RED("Red colors"), GREEN("Green colors"), BLUE( + "Blue colors"); + + /** The caption. */ + private String caption; + + /** + * Instantiates a new color range property id. + * + * @param caption + * the caption + */ + ColorRangePropertyId(String caption) { + this.caption = caption; + } + + @Override + public String toString() { + return caption; + } + } + + /** + * Instantiates a new color picker select. + * + * @param rows + * the rows + * @param columns + * the columns + */ + public ColorPickerSelect() { + + VerticalLayout layout = new VerticalLayout(); + setCompositionRoot(layout); + + setStyleName("colorselect"); + setWidth("100%"); + + range = new ComboBox(); + range.setImmediate(true); + range.setImmediate(true); + range.setNullSelectionAllowed(false); + range.setNewItemsAllowed(false); + range.setWidth("100%"); + range.addValueChangeListener(this); + + for (ColorRangePropertyId id : ColorRangePropertyId.values()) { + range.addItem(id); + } + range.select(ColorRangePropertyId.ALL); + + layout.addComponent(range); + + grid = new ColorPickerGrid(createAllColors(14, 10)); + grid.setWidth("100%"); + + layout.addComponent(grid); + } + + /** + * Creates the all colors. + * + * @param rows + * the rows + * @param columns + * the columns + * + * @return the color[][] + */ + private Color[][] createAllColors(int rows, int columns) { + Color[][] colors = new Color[rows][columns]; + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < columns; col++) { + + // Create the color grid by varying the saturation and value + if (row < (rows - 1)) { + // Calculate new hue value + float hue = ((float) col / (float) columns); + float saturation = 1f; + float value = 1f; + + // For the upper half use value=1 and variable + // saturation + if (row < (rows / 2)) { + saturation = ((row + 1f) / (rows / 2f)); + } else { + value = 1f - ((row - (rows / 2f)) / (rows / 2f)); + } + + colors[row][col] = new Color( + Color.HSVtoRGB(hue, saturation, value)); + } + + // The last row should have the black&white gradient + else { + float hue = 0f; + float saturation = 0f; + float value = 1f - ((float) col / (float) columns); + + colors[row][col] = new Color( + Color.HSVtoRGB(hue, saturation, value)); + } + } + } + + return colors; + } + + /** + * Creates the color. + * + * @param color + * the color + * @param rows + * the rows + * @param columns + * the columns + * + * @return the color[][] + */ + private Color[][] createColors(Color color, int rows, int columns) { + Color[][] colors = new Color[rows][columns]; + + float[] hsv = color.getHSV(); + + float hue = hsv[0]; + float saturation = 1f; + float value = 1f; + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < columns; col++) { + + int index = row * columns + col; + saturation = 1f; + value = 1f; + + if (index <= ((rows * columns) / 2)) { + saturation = index + / (((float) rows * (float) columns) / 2f); + } else { + index -= ((rows * columns) / 2); + value = 1f + - index / (((float) rows * (float) columns) / 2f); + } + + colors[row][col] = new Color( + Color.HSVtoRGB(hue, saturation, value)); + } + } + + return colors; + } + + @Override + public Color getColor() { + return grid.getColor(); + } + + @Override + public void setColor(Color color) { + grid.getColor(); + } + + @Override + public void addColorChangeListener(ColorChangeListener listener) { + grid.addColorChangeListener(listener); + } + + @Override + public void removeColorChangeListener(ColorChangeListener listener) { + grid.removeColorChangeListener(listener); + } + + @Override + public void valueChange(ValueChangeEvent event) { + if (grid == null) { + return; + } + + if (event.getProperty().getValue() == ColorRangePropertyId.ALL) { + grid.setColorGrid(createAllColors(14, 10)); + } else if (event.getProperty().getValue() == ColorRangePropertyId.RED) { + grid.setColorGrid(createColors(new Color(0xFF, 0, 0), 14, 10)); + } else if (event.getProperty() + .getValue() == ColorRangePropertyId.GREEN) { + grid.setColorGrid(createColors(new Color(0, 0xFF, 0), 14, 10)); + } else if (event.getProperty() + .getValue() == ColorRangePropertyId.BLUE) { + grid.setColorGrid(createColors(new Color(0, 0, 0xFF), 14, 10)); + } + } +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorSelector.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorSelector.java new file mode 100644 index 0000000000..a4da97c46b --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/ColorSelector.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.v7.ui.components.colorpicker; + +import java.io.Serializable; + +import com.vaadin.shared.ui.colorpicker.Color; + +/** + * An interface for a color selector. + * + * @since 7.0.0 + */ +public interface ColorSelector extends Serializable, HasColorChangeListener { + + /** + * Sets the color. + * + * @param color + * the new color + */ + public void setColor(Color color); + + /** + * Gets the color. + * + * @return the color + */ + public Color getColor(); +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/HasColorChangeListener.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/HasColorChangeListener.java new file mode 100644 index 0000000000..ed416c1ebe --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/components/colorpicker/HasColorChangeListener.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.components.colorpicker; + +import java.io.Serializable; + +public interface HasColorChangeListener extends Serializable { + + /** + * Adds a {@link ColorChangeListener} to the component. + * + * @param listener + */ + void addColorChangeListener(ColorChangeListener listener); + + /** + * Removes a {@link ColorChangeListener} from the component. + * + * @param listener + */ + void removeColorChangeListener(ColorChangeListener listener); + +} diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/AbstractJavaScriptRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/AbstractJavaScriptRenderer.java new file mode 100644 index 0000000000..2d0ac7f62e --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.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.JavaScriptFunction; +import com.vaadin.v7.ui.Grid.AbstractRenderer; + +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/v7/ui/renderers/ButtonRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ButtonRenderer.java new file mode 100644 index 0000000000..906fc025bb --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ButtonRenderer.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.renderers; + +/** + * 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/v7/ui/renderers/ClickableRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ClickableRenderer.java new file mode 100644 index 0000000000..a1f48a99c4 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.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.util.ReflectTools; +import com.vaadin.v7.ui.Grid; +import com.vaadin.v7.ui.Grid.AbstractRenderer; +import com.vaadin.v7.ui.Grid.Column; + +/** + * 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(Grid 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/v7/ui/renderers/DateRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/DateRenderer.java new file mode 100644 index 0000000000..ac3b831acc --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.ui.renderers; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; + +import com.vaadin.v7.ui.Grid.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/v7/ui/renderers/HtmlRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/HtmlRenderer.java new file mode 100644 index 0000000000..5264f19e0b --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/HtmlRenderer.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.v7.ui.renderers; + +import com.vaadin.v7.ui.Grid.AbstractRenderer; + +/** + * 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/v7/ui/renderers/ImageRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ImageRenderer.java new file mode 100644 index 0000000000..19c7a77b01 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.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/v7/ui/renderers/NumberRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/NumberRenderer.java new file mode 100644 index 0000000000..061f6d5790 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.ui.renderers; + +import java.text.NumberFormat; +import java.util.Locale; + +import com.vaadin.v7.ui.Grid.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/v7/ui/renderers/ProgressBarRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/ProgressBarRenderer.java new file mode 100644 index 0000000000..d19c09ec59 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.ui.renderers; + +import com.vaadin.v7.ui.Grid.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/v7/ui/renderers/Renderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/Renderer.java new file mode 100644 index 0000000000..ce221760cc --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/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.v7.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/v7/ui/renderers/TextRenderer.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/TextRenderer.java new file mode 100644 index 0000000000..23306baf75 --- /dev/null +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/renderers/TextRenderer.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.v7.ui.renderers; + +import com.vaadin.v7.ui.Grid.AbstractRenderer; + +/** + * 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 deleted file mode 100644 index 3333cd7744..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.java deleted file mode 100644 index e5aade1217..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactoryTest.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.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.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 deleted file mode 100644 index e270211abf..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java deleted file mode 100644 index 4622f0960e..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java b/compatibility-server/src/test/java/com/vaadin/data/fieldgroup/FieldGroupTest.java deleted file mode 100644 index c0039fc4fb..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractBeanContainerTestBase.java deleted file mode 100644 index 4a9e4f9891..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractContainerTestBase.java deleted file mode 100644 index 955b609735..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractHierarchicalContainerTestBase.java deleted file mode 100644 index f3eda74100..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/AbstractInMemoryContainerTestBase.java deleted file mode 100644 index 3858504bc7..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/BeanContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanContainerTest.java deleted file mode 100644 index bdf6ba1958..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerGenerator.java deleted file mode 100644 index a5bdcc7cf9..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerSortTest.java deleted file mode 100644 index 4f4e35258f..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemContainerTest.java deleted file mode 100644 index 19b0835fd6..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/BeanItemTest.java deleted file mode 100644 index 1bbe74818c..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerHierarchicalWrapperTest.java deleted file mode 100644 index e41f751bfd..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerOrderedWrapperTest.java deleted file mode 100644 index c3a8b2c4f9..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSizeAssertTest.java deleted file mode 100644 index 58cb2b1d27..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ContainerSortingTest.java deleted file mode 100644 index 1c4dc1de5e..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/FileSystemContainerTest.java deleted file mode 100644 index 992b265702..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerBasicTest.java deleted file mode 100644 index 11335314f0..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/GeneratedPropertyContainerTest.java deleted file mode 100644 index 1e34567439..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerOrderedWrapperTest.java deleted file mode 100644 index 54984f07ad..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/HierarchicalContainerTest.java deleted file mode 100644 index 7ab20ca3dd..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/IndexedContainerTest.java deleted file mode 100644 index b19a425518..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/MethodPropertyMemoryConsumptionTest.java deleted file mode 100644 index 6776021c24..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/NestedMethodPropertyTest.java deleted file mode 100644 index 4a1e2a1784..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ObjectPropertyTest.java deleted file mode 100644 index e0307034cf..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/PerformanceTestIndexedContainerTest.java deleted file mode 100644 index 5f64c0e8d8..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/PropertyDescriptorTest.java deleted file mode 100644 index af9db229c5..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/PropertySetItemTest.java deleted file mode 100644 index fc91a20dd0..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/ReflectToolsGetSuperFieldTest.java deleted file mode 100644 index df4258f316..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/TransactionalPropertyWrapperTest.java deleted file mode 100644 index ef3e416f96..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/data/util/filter/AbstractFilterTestBase.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/AbstractFilterTestBase.java deleted file mode 100644 index 0c6932c65e..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/AbstractFilterTestBase.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.vaadin.data.util.filter; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; - -import junit.framework.TestCase; - -public abstract class AbstractFilterTestBase - extends TestCase { - - protected static final String PROPERTY1 = "property1"; - protected static final String PROPERTY2 = "property2"; - - protected static class TestItem extends PropertysetItem { - - public TestItem(T1 value1, T2 value2) { - addItemProperty(PROPERTY1, new ObjectProperty(value1)); - addItemProperty(PROPERTY2, new ObjectProperty(value2)); - } - } - - protected static class NullProperty implements Property { - - @Override - public String getValue() { - return null; - } - - @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) { - // do nothing - } - - } - - public static class SameItemFilter implements Filter { - - private final Item item; - private final Object propertyId; - - public SameItemFilter(Item item) { - this(item, ""); - } - - public SameItemFilter(Item item, Object propertyId) { - this.item = item; - this.propertyId = propertyId; - } - - @Override - public boolean passesFilter(Object itemId, Item item) - throws UnsupportedOperationException { - return this.item == item; - } - - @Override - public boolean appliesToProperty(Object propertyId) { - return this.propertyId != null ? this.propertyId.equals(propertyId) - : true; - } - - @Override - public boolean equals(Object obj) { - if (obj == null || !getClass().equals(obj.getClass())) { - return false; - } - SameItemFilter other = (SameItemFilter) obj; - return item == other.item - && (propertyId == null ? other.propertyId == null - : propertyId.equals(other.propertyId)); - } - - @Override - public int hashCode() { - return item.hashCode(); - } - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/filter/AndOrFilterTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/AndOrFilterTest.java deleted file mode 100644 index f825ef64c6..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/AndOrFilterTest.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.vaadin.data.util.filter; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; -import com.vaadin.data.util.BeanItem; - -public class AndOrFilterTest - extends AbstractFilterTestBase { - - protected Item item1 = new BeanItem(1); - protected Item item2 = new BeanItem(2); - - @Test - public void testNoFilterAnd() { - Filter filter = new And(); - - Assert.assertTrue(filter.passesFilter(null, item1)); - } - - @Test - public void testSingleFilterAnd() { - Filter filter = new And(new SameItemFilter(item1)); - - Assert.assertTrue(filter.passesFilter(null, item1)); - Assert.assertFalse(filter.passesFilter(null, item2)); - } - - @Test - public void testTwoFilterAnd() { - Filter filter1 = new And(new SameItemFilter(item1), - new SameItemFilter(item1)); - Filter filter2 = new And(new SameItemFilter(item1), - new SameItemFilter(item2)); - - Assert.assertTrue(filter1.passesFilter(null, item1)); - Assert.assertFalse(filter1.passesFilter(null, item2)); - - Assert.assertFalse(filter2.passesFilter(null, item1)); - Assert.assertFalse(filter2.passesFilter(null, item2)); - } - - @Test - public void testThreeFilterAnd() { - Filter filter1 = new And(new SameItemFilter(item1), - new SameItemFilter(item1), new SameItemFilter(item1)); - Filter filter2 = new And(new SameItemFilter(item1), - new SameItemFilter(item1), new SameItemFilter(item2)); - - Assert.assertTrue(filter1.passesFilter(null, item1)); - Assert.assertFalse(filter1.passesFilter(null, item2)); - - Assert.assertFalse(filter2.passesFilter(null, item1)); - Assert.assertFalse(filter2.passesFilter(null, item2)); - } - - @Test - public void testNoFilterOr() { - Filter filter = new Or(); - - Assert.assertFalse(filter.passesFilter(null, item1)); - } - - @Test - public void testSingleFilterOr() { - Filter filter = new Or(new SameItemFilter(item1)); - - Assert.assertTrue(filter.passesFilter(null, item1)); - Assert.assertFalse(filter.passesFilter(null, item2)); - } - - @Test - public void testTwoFilterOr() { - Filter filter1 = new Or(new SameItemFilter(item1), - new SameItemFilter(item1)); - Filter filter2 = new Or(new SameItemFilter(item1), - new SameItemFilter(item2)); - - Assert.assertTrue(filter1.passesFilter(null, item1)); - Assert.assertFalse(filter1.passesFilter(null, item2)); - - Assert.assertTrue(filter2.passesFilter(null, item1)); - Assert.assertTrue(filter2.passesFilter(null, item2)); - } - - @Test - public void testThreeFilterOr() { - Filter filter1 = new Or(new SameItemFilter(item1), - new SameItemFilter(item1), new SameItemFilter(item1)); - Filter filter2 = new Or(new SameItemFilter(item1), - new SameItemFilter(item1), new SameItemFilter(item2)); - - Assert.assertTrue(filter1.passesFilter(null, item1)); - Assert.assertFalse(filter1.passesFilter(null, item2)); - - Assert.assertTrue(filter2.passesFilter(null, item1)); - Assert.assertTrue(filter2.passesFilter(null, item2)); - } - - @Test - public void testAndEqualsHashCode() { - Filter filter0 = new And(); - Filter filter0b = new And(); - Filter filter1a = new And(new SameItemFilter(item1)); - Filter filter1a2 = new And(new SameItemFilter(item1)); - Filter filter1b = new And(new SameItemFilter(item2)); - Filter filter2a = new And(new SameItemFilter(item1), - new SameItemFilter(item1)); - Filter filter2b = new And(new SameItemFilter(item1), - new SameItemFilter(item2)); - Filter filter2b2 = new And(new SameItemFilter(item1), - new SameItemFilter(item2)); - Filter other0 = new Or(); - Filter other1 = new Or(new SameItemFilter(item1)); - - Assert.assertEquals(filter0, filter0); - Assert.assertEquals(filter0, filter0b); - Assert.assertFalse(filter0.equals(filter1a)); - Assert.assertFalse(filter0.equals(other0)); - Assert.assertFalse(filter0.equals(other1)); - - Assert.assertFalse(filter1a.equals(filter1b)); - Assert.assertFalse(filter1a.equals(other1)); - - Assert.assertFalse(filter1a.equals(filter2a)); - Assert.assertFalse(filter2a.equals(filter1a)); - - Assert.assertFalse(filter2a.equals(filter2b)); - Assert.assertEquals(filter2b, filter2b2); - - // hashCode() - Assert.assertEquals(filter0.hashCode(), filter0.hashCode()); - Assert.assertEquals(filter0.hashCode(), filter0b.hashCode()); - Assert.assertEquals(filter1a.hashCode(), filter1a.hashCode()); - Assert.assertEquals(filter1a.hashCode(), filter1a2.hashCode()); - Assert.assertEquals(filter2a.hashCode(), filter2a.hashCode()); - Assert.assertEquals(filter2b.hashCode(), filter2b2.hashCode()); - } - - @Test - public void testOrEqualsHashCode() { - Filter filter0 = new Or(); - Filter filter0b = new Or(); - Filter filter1a = new Or(new SameItemFilter(item1)); - Filter filter1a2 = new Or(new SameItemFilter(item1)); - Filter filter1b = new Or(new SameItemFilter(item2)); - Filter filter2a = new Or(new SameItemFilter(item1), - new SameItemFilter(item1)); - Filter filter2b = new Or(new SameItemFilter(item1), - new SameItemFilter(item2)); - Filter filter2b2 = new Or(new SameItemFilter(item1), - new SameItemFilter(item2)); - Filter other0 = new And(); - Filter other1 = new And(new SameItemFilter(item1)); - - Assert.assertEquals(filter0, filter0); - Assert.assertEquals(filter0, filter0b); - Assert.assertFalse(filter0.equals(filter1a)); - Assert.assertFalse(filter0.equals(other0)); - Assert.assertFalse(filter0.equals(other1)); - - Assert.assertFalse(filter1a.equals(filter1b)); - Assert.assertFalse(filter1a.equals(other1)); - - Assert.assertFalse(filter1a.equals(filter2a)); - Assert.assertFalse(filter2a.equals(filter1a)); - - Assert.assertFalse(filter2a.equals(filter2b)); - Assert.assertEquals(filter2b, filter2b2); - - // hashCode() - Assert.assertEquals(filter0.hashCode(), filter0.hashCode()); - Assert.assertEquals(filter0.hashCode(), filter0b.hashCode()); - Assert.assertEquals(filter1a.hashCode(), filter1a.hashCode()); - Assert.assertEquals(filter1a.hashCode(), filter1a2.hashCode()); - Assert.assertEquals(filter2a.hashCode(), filter2a.hashCode()); - Assert.assertEquals(filter2b.hashCode(), filter2b2.hashCode()); - } - - @Test - public void testAndAppliesToProperty() { - Filter filter0 = new And(); - Filter filter1a = new And(new SameItemFilter(item1, "a")); - Filter filter1b = new And(new SameItemFilter(item1, "b")); - Filter filter2aa = new And(new SameItemFilter(item1, "a"), - new SameItemFilter(item1, "a")); - Filter filter2ab = new And(new SameItemFilter(item1, "a"), - new SameItemFilter(item1, "b")); - Filter filter3abc = new And(new SameItemFilter(item1, "a"), - new SameItemFilter(item1, "b"), new SameItemFilter(item1, "c")); - - // empty And does not filter out anything - Assert.assertFalse(filter0.appliesToProperty("a")); - Assert.assertFalse(filter0.appliesToProperty("d")); - - Assert.assertTrue(filter1a.appliesToProperty("a")); - Assert.assertFalse(filter1a.appliesToProperty("b")); - Assert.assertFalse(filter1b.appliesToProperty("a")); - Assert.assertTrue(filter1b.appliesToProperty("b")); - - Assert.assertTrue(filter2aa.appliesToProperty("a")); - Assert.assertFalse(filter2aa.appliesToProperty("b")); - Assert.assertTrue(filter2ab.appliesToProperty("a")); - Assert.assertTrue(filter2ab.appliesToProperty("b")); - - Assert.assertTrue(filter3abc.appliesToProperty("a")); - Assert.assertTrue(filter3abc.appliesToProperty("b")); - Assert.assertTrue(filter3abc.appliesToProperty("c")); - Assert.assertFalse(filter3abc.appliesToProperty("d")); - } - - @Test - public void testOrAppliesToProperty() { - Filter filter0 = new Or(); - Filter filter1a = new Or(new SameItemFilter(item1, "a")); - Filter filter1b = new Or(new SameItemFilter(item1, "b")); - Filter filter2aa = new Or(new SameItemFilter(item1, "a"), - new SameItemFilter(item1, "a")); - Filter filter2ab = new Or(new SameItemFilter(item1, "a"), - new SameItemFilter(item1, "b")); - Filter filter3abc = new Or(new SameItemFilter(item1, "a"), - new SameItemFilter(item1, "b"), new SameItemFilter(item1, "c")); - - // empty Or filters out everything - Assert.assertTrue(filter0.appliesToProperty("a")); - Assert.assertTrue(filter0.appliesToProperty("d")); - - Assert.assertTrue(filter1a.appliesToProperty("a")); - Assert.assertFalse(filter1a.appliesToProperty("b")); - Assert.assertFalse(filter1b.appliesToProperty("a")); - Assert.assertTrue(filter1b.appliesToProperty("b")); - - Assert.assertTrue(filter2aa.appliesToProperty("a")); - Assert.assertFalse(filter2aa.appliesToProperty("b")); - Assert.assertTrue(filter2ab.appliesToProperty("a")); - Assert.assertTrue(filter2ab.appliesToProperty("b")); - - Assert.assertTrue(filter3abc.appliesToProperty("a")); - Assert.assertTrue(filter3abc.appliesToProperty("b")); - Assert.assertTrue(filter3abc.appliesToProperty("c")); - Assert.assertFalse(filter3abc.appliesToProperty("d")); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterDateTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterDateTest.java deleted file mode 100644 index 7c3dba9db3..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterDateTest.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.vaadin.data.util.filter; - -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.filter.Compare.Greater; -import com.vaadin.data.util.filter.Compare.GreaterOrEqual; -import com.vaadin.data.util.filter.Compare.Less; -import com.vaadin.data.util.filter.Compare.LessOrEqual; - -public class CompareFilterDateTest extends AbstractFilterTestBase { - - protected Item itemNullUtilDate; - protected Item itemNullSqlDate; - protected Item itemUtilDate; - protected Item itemSqlDate; - - protected SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyy"); - - protected Filter equalCompUtilDate; - protected Filter greaterCompUtilDate; - protected Filter lessCompUtilDate; - protected Filter greaterEqualCompUtilDate; - protected Filter lessEqualCompUtilDate; - - protected Filter equalCompSqlDate; - protected Filter greaterCompSqlDate; - protected Filter lessCompSqlDate; - protected Filter greaterEqualCompSqlDate; - protected Filter lessEqualCompSqlDate; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - equalCompUtilDate = new Equal(PROPERTY1, formatter.parse("26072016")); - greaterCompUtilDate = new Greater(PROPERTY1, - formatter.parse("26072016")); - lessCompUtilDate = new Less(PROPERTY1, formatter.parse("26072016")); - greaterEqualCompUtilDate = new GreaterOrEqual(PROPERTY1, - formatter.parse("26072016")); - lessEqualCompUtilDate = new LessOrEqual(PROPERTY1, - formatter.parse("26072016")); - - equalCompSqlDate = new Equal(PROPERTY1, - new java.sql.Date(formatter.parse("26072016").getTime())); - greaterCompSqlDate = new Greater(PROPERTY1, - new java.sql.Date(formatter.parse("26072016").getTime())); - lessCompSqlDate = new Less(PROPERTY1, - new java.sql.Date(formatter.parse("26072016").getTime())); - greaterEqualCompSqlDate = new GreaterOrEqual(PROPERTY1, - new java.sql.Date(formatter.parse("26072016").getTime())); - lessEqualCompSqlDate = new LessOrEqual(PROPERTY1, - new java.sql.Date(formatter.parse("26072016").getTime())); - - itemNullUtilDate = new PropertysetItem(); - itemNullUtilDate.addItemProperty(PROPERTY1, - new ObjectProperty(null, Date.class)); - itemNullSqlDate = new PropertysetItem(); - itemNullSqlDate.addItemProperty(PROPERTY1, - new ObjectProperty(null, java.sql.Date.class)); - itemUtilDate = new PropertysetItem(); - itemUtilDate.addItemProperty(PROPERTY1, new ObjectProperty( - formatter.parse("25072016"), Date.class)); - itemSqlDate = new PropertysetItem(); - itemSqlDate.addItemProperty(PROPERTY1, - new ObjectProperty( - new java.sql.Date( - formatter.parse("25072016").getTime()), - java.sql.Date.class)); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - itemNullUtilDate = null; - itemNullSqlDate = null; - itemUtilDate = null; - itemSqlDate = null; - } - - @Test - public void testCompareUtilDatesAndUtilDates() { - Assert.assertFalse( - equalCompUtilDate.passesFilter(null, itemNullUtilDate)); - Assert.assertFalse(equalCompUtilDate.passesFilter(null, itemUtilDate)); - Assert.assertFalse( - greaterCompUtilDate.passesFilter(null, itemUtilDate)); - Assert.assertTrue(lessCompUtilDate.passesFilter(null, itemUtilDate)); - Assert.assertFalse( - greaterEqualCompUtilDate.passesFilter(null, itemUtilDate)); - Assert.assertTrue( - lessEqualCompUtilDate.passesFilter(null, itemUtilDate)); - } - - @Test - public void testCompareUtilDatesAndSqlDates() { - Assert.assertFalse( - equalCompUtilDate.passesFilter(null, itemNullSqlDate)); - Assert.assertFalse(equalCompUtilDate.passesFilter(null, itemSqlDate)); - Assert.assertFalse(greaterCompUtilDate.passesFilter(null, itemSqlDate)); - Assert.assertTrue(lessCompUtilDate.passesFilter(null, itemSqlDate)); - Assert.assertFalse( - greaterEqualCompUtilDate.passesFilter(null, itemSqlDate)); - Assert.assertTrue( - lessEqualCompUtilDate.passesFilter(null, itemSqlDate)); - } - - @Test - public void testCompareSqlDatesAndSqlDates() { - Assert.assertFalse( - equalCompSqlDate.passesFilter(null, itemNullSqlDate)); - Assert.assertFalse(equalCompSqlDate.passesFilter(null, itemSqlDate)); - Assert.assertFalse(greaterCompSqlDate.passesFilter(null, itemSqlDate)); - Assert.assertTrue(lessCompSqlDate.passesFilter(null, itemSqlDate)); - Assert.assertFalse( - greaterEqualCompSqlDate.passesFilter(null, itemSqlDate)); - Assert.assertTrue(lessEqualCompSqlDate.passesFilter(null, itemSqlDate)); - } - - @Test - public void testCompareSqlDatesAndUtilDates() { - Assert.assertFalse( - equalCompSqlDate.passesFilter(null, itemNullUtilDate)); - Assert.assertFalse(equalCompSqlDate.passesFilter(null, itemUtilDate)); - Assert.assertFalse(greaterCompSqlDate.passesFilter(null, itemUtilDate)); - Assert.assertTrue(lessCompSqlDate.passesFilter(null, itemUtilDate)); - Assert.assertFalse( - greaterEqualCompSqlDate.passesFilter(null, itemUtilDate)); - Assert.assertTrue( - lessEqualCompSqlDate.passesFilter(null, itemUtilDate)); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterTest.java deleted file mode 100644 index 4bdad3c54d..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/CompareFilterTest.java +++ /dev/null @@ -1,322 +0,0 @@ -package com.vaadin.data.util.filter; - -import java.math.BigDecimal; -import java.util.Date; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.filter.Compare.Greater; -import com.vaadin.data.util.filter.Compare.GreaterOrEqual; -import com.vaadin.data.util.filter.Compare.Less; -import com.vaadin.data.util.filter.Compare.LessOrEqual; - -public class CompareFilterTest extends AbstractFilterTestBase { - - protected Item itemNull; - protected Item itemEmpty; - protected Item itemA; - protected Item itemB; - protected Item itemC; - - protected final Filter equalB = new Equal(PROPERTY1, "b"); - protected final Filter greaterB = new Greater(PROPERTY1, "b"); - protected final Filter lessB = new Less(PROPERTY1, "b"); - protected final Filter greaterEqualB = new GreaterOrEqual(PROPERTY1, "b"); - protected final Filter lessEqualB = new LessOrEqual(PROPERTY1, "b"); - - protected final Filter equalNull = new Equal(PROPERTY1, null); - protected final Filter greaterNull = new Greater(PROPERTY1, null); - protected final Filter lessNull = new Less(PROPERTY1, null); - protected final Filter greaterEqualNull = new GreaterOrEqual(PROPERTY1, - null); - protected final Filter lessEqualNull = new LessOrEqual(PROPERTY1, null); - - @Override - protected void setUp() throws Exception { - super.setUp(); - itemNull = new PropertysetItem(); - itemNull.addItemProperty(PROPERTY1, - new ObjectProperty(null, String.class)); - itemEmpty = new PropertysetItem(); - itemEmpty.addItemProperty(PROPERTY1, - new ObjectProperty("", String.class)); - itemA = new PropertysetItem(); - itemA.addItemProperty(PROPERTY1, - new ObjectProperty("a", String.class)); - itemB = new PropertysetItem(); - itemB.addItemProperty(PROPERTY1, - new ObjectProperty("b", String.class)); - itemC = new PropertysetItem(); - itemC.addItemProperty(PROPERTY1, - new ObjectProperty("c", String.class)); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - itemNull = null; - itemEmpty = null; - itemA = null; - itemB = null; - } - - @Test - public void testCompareString() { - Assert.assertFalse(equalB.passesFilter(null, itemEmpty)); - Assert.assertFalse(equalB.passesFilter(null, itemA)); - Assert.assertTrue(equalB.passesFilter(null, itemB)); - Assert.assertFalse(equalB.passesFilter(null, itemC)); - - Assert.assertFalse(greaterB.passesFilter(null, itemEmpty)); - Assert.assertFalse(greaterB.passesFilter(null, itemA)); - Assert.assertFalse(greaterB.passesFilter(null, itemB)); - Assert.assertTrue(greaterB.passesFilter(null, itemC)); - - Assert.assertTrue(lessB.passesFilter(null, itemEmpty)); - Assert.assertTrue(lessB.passesFilter(null, itemA)); - Assert.assertFalse(lessB.passesFilter(null, itemB)); - Assert.assertFalse(lessB.passesFilter(null, itemC)); - - Assert.assertFalse(greaterEqualB.passesFilter(null, itemEmpty)); - Assert.assertFalse(greaterEqualB.passesFilter(null, itemA)); - Assert.assertTrue(greaterEqualB.passesFilter(null, itemB)); - Assert.assertTrue(greaterEqualB.passesFilter(null, itemC)); - - Assert.assertTrue(lessEqualB.passesFilter(null, itemEmpty)); - Assert.assertTrue(lessEqualB.passesFilter(null, itemA)); - Assert.assertTrue(lessEqualB.passesFilter(null, itemB)); - Assert.assertFalse(lessEqualB.passesFilter(null, itemC)); - } - - @Test - public void testCompareWithNull() { - // null comparisons: null is less than any other value - Assert.assertFalse(equalB.passesFilter(null, itemNull)); - Assert.assertTrue(greaterB.passesFilter(null, itemNull)); - Assert.assertFalse(lessB.passesFilter(null, itemNull)); - Assert.assertTrue(greaterEqualB.passesFilter(null, itemNull)); - Assert.assertFalse(lessEqualB.passesFilter(null, itemNull)); - - Assert.assertTrue(equalNull.passesFilter(null, itemNull)); - Assert.assertFalse(greaterNull.passesFilter(null, itemNull)); - Assert.assertFalse(lessNull.passesFilter(null, itemNull)); - Assert.assertTrue(greaterEqualNull.passesFilter(null, itemNull)); - Assert.assertTrue(lessEqualNull.passesFilter(null, itemNull)); - - Assert.assertFalse(equalNull.passesFilter(null, itemA)); - Assert.assertFalse(greaterNull.passesFilter(null, itemA)); - Assert.assertTrue(lessNull.passesFilter(null, itemA)); - Assert.assertFalse(greaterEqualNull.passesFilter(null, itemA)); - Assert.assertTrue(lessEqualNull.passesFilter(null, itemA)); - } - - @Test - public void testCompareInteger() { - int negative = -1; - int zero = 0; - int positive = 1; - - Item itemNegative = new PropertysetItem(); - itemNegative.addItemProperty(PROPERTY1, - new ObjectProperty(negative, Integer.class)); - Item itemZero = new PropertysetItem(); - itemZero.addItemProperty(PROPERTY1, - new ObjectProperty(zero, Integer.class)); - Item itemPositive = new PropertysetItem(); - itemPositive.addItemProperty(PROPERTY1, - new ObjectProperty(positive, Integer.class)); - - Filter equalZero = new Equal(PROPERTY1, zero); - Assert.assertFalse(equalZero.passesFilter(null, itemNegative)); - Assert.assertTrue(equalZero.passesFilter(null, itemZero)); - Assert.assertFalse(equalZero.passesFilter(null, itemPositive)); - - Filter isPositive = new Greater(PROPERTY1, zero); - Assert.assertFalse(isPositive.passesFilter(null, itemNegative)); - Assert.assertFalse(isPositive.passesFilter(null, itemZero)); - Assert.assertTrue(isPositive.passesFilter(null, itemPositive)); - - Filter isNegative = new Less(PROPERTY1, zero); - Assert.assertTrue(isNegative.passesFilter(null, itemNegative)); - Assert.assertFalse(isNegative.passesFilter(null, itemZero)); - Assert.assertFalse(isNegative.passesFilter(null, itemPositive)); - - Filter isNonNegative = new GreaterOrEqual(PROPERTY1, zero); - Assert.assertFalse(isNonNegative.passesFilter(null, itemNegative)); - Assert.assertTrue(isNonNegative.passesFilter(null, itemZero)); - Assert.assertTrue(isNonNegative.passesFilter(null, itemPositive)); - - Filter isNonPositive = new LessOrEqual(PROPERTY1, zero); - Assert.assertTrue(isNonPositive.passesFilter(null, itemNegative)); - Assert.assertTrue(isNonPositive.passesFilter(null, itemZero)); - Assert.assertFalse(isNonPositive.passesFilter(null, itemPositive)); - } - - @Test - public void testCompareBigDecimal() { - BigDecimal negative = new BigDecimal(-1); - BigDecimal zero = new BigDecimal(0); - BigDecimal positive = new BigDecimal(1); - positive.setScale(1); - BigDecimal positiveScaleTwo = new BigDecimal(1).setScale(2); - - Item itemNegative = new PropertysetItem(); - itemNegative.addItemProperty(PROPERTY1, - new ObjectProperty(negative, BigDecimal.class)); - Item itemZero = new PropertysetItem(); - itemZero.addItemProperty(PROPERTY1, - new ObjectProperty(zero, BigDecimal.class)); - Item itemPositive = new PropertysetItem(); - itemPositive.addItemProperty(PROPERTY1, - new ObjectProperty(positive, BigDecimal.class)); - Item itemPositiveScaleTwo = new PropertysetItem(); - itemPositiveScaleTwo.addItemProperty(PROPERTY1, - new ObjectProperty(positiveScaleTwo, - BigDecimal.class)); - - Filter equalZero = new Equal(PROPERTY1, zero); - Assert.assertFalse(equalZero.passesFilter(null, itemNegative)); - Assert.assertTrue(equalZero.passesFilter(null, itemZero)); - Assert.assertFalse(equalZero.passesFilter(null, itemPositive)); - - Filter isPositive = new Greater(PROPERTY1, zero); - Assert.assertFalse(isPositive.passesFilter(null, itemNegative)); - Assert.assertFalse(isPositive.passesFilter(null, itemZero)); - Assert.assertTrue(isPositive.passesFilter(null, itemPositive)); - - Filter isNegative = new Less(PROPERTY1, zero); - Assert.assertTrue(isNegative.passesFilter(null, itemNegative)); - Assert.assertFalse(isNegative.passesFilter(null, itemZero)); - Assert.assertFalse(isNegative.passesFilter(null, itemPositive)); - - Filter isNonNegative = new GreaterOrEqual(PROPERTY1, zero); - Assert.assertFalse(isNonNegative.passesFilter(null, itemNegative)); - Assert.assertTrue(isNonNegative.passesFilter(null, itemZero)); - Assert.assertTrue(isNonNegative.passesFilter(null, itemPositive)); - - Filter isNonPositive = new LessOrEqual(PROPERTY1, zero); - Assert.assertTrue(isNonPositive.passesFilter(null, itemNegative)); - Assert.assertTrue(isNonPositive.passesFilter(null, itemZero)); - Assert.assertFalse(isNonPositive.passesFilter(null, itemPositive)); - - Filter isPositiveScaleTwo = new Equal(PROPERTY1, positiveScaleTwo); - Assert.assertTrue( - isPositiveScaleTwo.passesFilter(null, itemPositiveScaleTwo)); - Assert.assertTrue(isPositiveScaleTwo.passesFilter(null, itemPositive)); - - } - - @Test - public void testCompareDate() { - Date now = new Date(); - // new Date() is only accurate to the millisecond, so repeating it gives - // the same date - Date earlier = new Date(now.getTime() - 1); - Date later = new Date(now.getTime() + 1); - - Item itemEarlier = new PropertysetItem(); - itemEarlier.addItemProperty(PROPERTY1, - new ObjectProperty(earlier, Date.class)); - Item itemNow = new PropertysetItem(); - itemNow.addItemProperty(PROPERTY1, - new ObjectProperty(now, Date.class)); - Item itemLater = new PropertysetItem(); - itemLater.addItemProperty(PROPERTY1, - new ObjectProperty(later, Date.class)); - - Filter equalNow = new Equal(PROPERTY1, now); - Assert.assertFalse(equalNow.passesFilter(null, itemEarlier)); - Assert.assertTrue(equalNow.passesFilter(null, itemNow)); - Assert.assertFalse(equalNow.passesFilter(null, itemLater)); - - Filter after = new Greater(PROPERTY1, now); - Assert.assertFalse(after.passesFilter(null, itemEarlier)); - Assert.assertFalse(after.passesFilter(null, itemNow)); - Assert.assertTrue(after.passesFilter(null, itemLater)); - - Filter before = new Less(PROPERTY1, now); - Assert.assertTrue(before.passesFilter(null, itemEarlier)); - Assert.assertFalse(before.passesFilter(null, itemNow)); - Assert.assertFalse(before.passesFilter(null, itemLater)); - - Filter afterOrNow = new GreaterOrEqual(PROPERTY1, now); - Assert.assertFalse(afterOrNow.passesFilter(null, itemEarlier)); - Assert.assertTrue(afterOrNow.passesFilter(null, itemNow)); - Assert.assertTrue(afterOrNow.passesFilter(null, itemLater)); - - Filter beforeOrNow = new LessOrEqual(PROPERTY1, now); - Assert.assertTrue(beforeOrNow.passesFilter(null, itemEarlier)); - Assert.assertTrue(beforeOrNow.passesFilter(null, itemNow)); - Assert.assertFalse(beforeOrNow.passesFilter(null, itemLater)); - } - - @Test - public void testCompareAppliesToProperty() { - Filter filterA = new Equal("a", 1); - Filter filterB = new Equal("b", 1); - - Assert.assertTrue(filterA.appliesToProperty("a")); - Assert.assertFalse(filterA.appliesToProperty("b")); - Assert.assertFalse(filterB.appliesToProperty("a")); - Assert.assertTrue(filterB.appliesToProperty("b")); - } - - @Test - public void testCompareEqualsHashCode() { - // most checks with Equal filter, then only some with others - Filter equalNull2 = new Equal(PROPERTY1, null); - Filter equalNullProperty2 = new Equal(PROPERTY2, null); - Filter equalEmpty = new Equal(PROPERTY1, ""); - Filter equalEmpty2 = new Equal(PROPERTY1, ""); - Filter equalEmptyProperty2 = new Equal(PROPERTY2, ""); - Filter equalA = new Equal(PROPERTY1, "a"); - Filter equalB2 = new Equal(PROPERTY1, "b"); - Filter equalBProperty2 = new Equal(PROPERTY2, "b"); - - Filter greaterEmpty = new Greater(PROPERTY1, ""); - - // equals() - Assert.assertEquals(equalNull, equalNull); - Assert.assertEquals(equalNull, equalNull2); - Assert.assertFalse(equalNull.equals(equalNullProperty2)); - Assert.assertFalse(equalNull.equals(equalEmpty)); - Assert.assertFalse(equalNull.equals(equalB)); - - Assert.assertEquals(equalEmpty, equalEmpty); - Assert.assertFalse(equalEmpty.equals(equalNull)); - Assert.assertEquals(equalEmpty, equalEmpty2); - Assert.assertFalse(equalEmpty.equals(equalEmptyProperty2)); - Assert.assertFalse(equalEmpty.equals(equalB)); - - Assert.assertEquals(equalB, equalB); - Assert.assertFalse(equalB.equals(equalNull)); - Assert.assertFalse(equalB.equals(equalEmpty)); - Assert.assertEquals(equalB, equalB2); - Assert.assertFalse(equalB.equals(equalBProperty2)); - Assert.assertFalse(equalB.equals(equalA)); - - Assert.assertEquals(greaterB, greaterB); - Assert.assertFalse(greaterB.equals(lessB)); - Assert.assertFalse(greaterB.equals(greaterEqualB)); - Assert.assertFalse(greaterB.equals(lessEqualB)); - - Assert.assertFalse(greaterNull.equals(greaterEmpty)); - Assert.assertFalse(greaterNull.equals(greaterB)); - Assert.assertFalse(greaterEmpty.equals(greaterNull)); - Assert.assertFalse(greaterEmpty.equals(greaterB)); - Assert.assertFalse(greaterB.equals(greaterNull)); - Assert.assertFalse(greaterB.equals(greaterEmpty)); - - // hashCode() - Assert.assertEquals(equalNull.hashCode(), equalNull2.hashCode()); - Assert.assertEquals(equalEmpty.hashCode(), equalEmpty2.hashCode()); - Assert.assertEquals(equalB.hashCode(), equalB2.hashCode()); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/filter/IsNullFilterTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/IsNullFilterTest.java deleted file mode 100644 index 18013cd41c..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/IsNullFilterTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.vaadin.data.util.filter; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; - -public class IsNullFilterTest extends AbstractFilterTestBase { - - @Test - public void testIsNull() { - Item item1 = new PropertysetItem(); - item1.addItemProperty("a", - new ObjectProperty(null, String.class)); - item1.addItemProperty("b", - new ObjectProperty("b", String.class)); - Item item2 = new PropertysetItem(); - item2.addItemProperty("a", - new ObjectProperty("a", String.class)); - item2.addItemProperty("b", - new ObjectProperty(null, String.class)); - - Filter filter1 = new IsNull("a"); - Filter filter2 = new IsNull("b"); - - Assert.assertTrue(filter1.passesFilter(null, item1)); - Assert.assertFalse(filter1.passesFilter(null, item2)); - Assert.assertFalse(filter2.passesFilter(null, item1)); - Assert.assertTrue(filter2.passesFilter(null, item2)); - } - - @Test - public void testIsNullAppliesToProperty() { - Filter filterA = new IsNull("a"); - Filter filterB = new IsNull("b"); - - Assert.assertTrue(filterA.appliesToProperty("a")); - Assert.assertFalse(filterA.appliesToProperty("b")); - Assert.assertFalse(filterB.appliesToProperty("a")); - Assert.assertTrue(filterB.appliesToProperty("b")); - } - - @Test - public void testIsNullEqualsHashCode() { - Filter filter1 = new IsNull("a"); - Filter filter1b = new IsNull("a"); - Filter filter2 = new IsNull("b"); - - // equals() - Assert.assertEquals(filter1, filter1b); - Assert.assertFalse(filter1.equals(filter2)); - Assert.assertFalse(filter1.equals(new And())); - - // hashCode() - Assert.assertEquals(filter1.hashCode(), filter1b.hashCode()); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/filter/LikeFilterTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/LikeFilterTest.java deleted file mode 100644 index 39054008cd..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/LikeFilterTest.java +++ /dev/null @@ -1,48 +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.filter; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; - -public class LikeFilterTest extends AbstractFilterTestBase { - - protected Item item1 = new PropertysetItem(); - protected Item item2 = new PropertysetItem(); - protected Item item3 = new PropertysetItem(); - - @Test - public void testLikeWithNulls() { - - Like filter = new Like("value", "a"); - - item1.addItemProperty("value", new ObjectProperty("a")); - item2.addItemProperty("value", new ObjectProperty("b")); - item3.addItemProperty("value", - new ObjectProperty(null, String.class)); - - Assert.assertTrue(filter.passesFilter(null, item1)); - Assert.assertFalse(filter.passesFilter(null, item2)); - Assert.assertFalse(filter.passesFilter(null, item3)); - - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/filter/NotFilterTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/NotFilterTest.java deleted file mode 100644 index e797b484f8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/NotFilterTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.vaadin.data.util.filter; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; -import com.vaadin.data.util.BeanItem; - -public class NotFilterTest extends AbstractFilterTestBase { - - protected Item item1 = new BeanItem(1); - protected Item item2 = new BeanItem(2); - - @Test - public void testNot() { - Filter origFilter = new SameItemFilter(item1); - Filter filter = new Not(origFilter); - - Assert.assertTrue(origFilter.passesFilter(null, item1)); - Assert.assertFalse(origFilter.passesFilter(null, item2)); - Assert.assertFalse(filter.passesFilter(null, item1)); - Assert.assertTrue(filter.passesFilter(null, item2)); - } - - @Test - public void testANotAppliesToProperty() { - Filter filterA = new Not(new SameItemFilter(item1, "a")); - Filter filterB = new Not(new SameItemFilter(item1, "b")); - - Assert.assertTrue(filterA.appliesToProperty("a")); - Assert.assertFalse(filterA.appliesToProperty("b")); - Assert.assertFalse(filterB.appliesToProperty("a")); - Assert.assertTrue(filterB.appliesToProperty("b")); - } - - @Test - public void testNotEqualsHashCode() { - Filter origFilter = new SameItemFilter(item1); - Filter filter1 = new Not(origFilter); - Filter filter1b = new Not(new SameItemFilter(item1)); - Filter filter2 = new Not(new SameItemFilter(item2)); - - // equals() - Assert.assertEquals(filter1, filter1b); - Assert.assertFalse(filter1.equals(filter2)); - Assert.assertFalse(filter1.equals(origFilter)); - Assert.assertFalse(filter1.equals(new And())); - - // hashCode() - Assert.assertEquals(filter1.hashCode(), filter1b.hashCode()); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/filter/SimpleStringFilterTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/filter/SimpleStringFilterTest.java deleted file mode 100644 index dd8267107a..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/filter/SimpleStringFilterTest.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.vaadin.data.util.filter; - -import org.junit.Assert; -import org.junit.Test; - -public class SimpleStringFilterTest - extends AbstractFilterTestBase { - - protected static TestItem createTestItem() { - return new TestItem("abcde", "TeSt"); - } - - protected TestItem getTestItem() { - return createTestItem(); - } - - protected SimpleStringFilter f(Object propertyId, String filterString, - boolean ignoreCase, boolean onlyMatchPrefix) { - return new SimpleStringFilter(propertyId, filterString, ignoreCase, - onlyMatchPrefix); - } - - protected boolean passes(Object propertyId, String filterString, - boolean ignoreCase, boolean onlyMatchPrefix) { - return f(propertyId, filterString, ignoreCase, onlyMatchPrefix) - .passesFilter(null, getTestItem()); - } - - @Test - public void testStartsWithCaseSensitive() { - Assert.assertTrue(passes(PROPERTY1, "ab", false, true)); - Assert.assertTrue(passes(PROPERTY1, "", false, true)); - - Assert.assertFalse(passes(PROPERTY2, "ab", false, true)); - Assert.assertFalse(passes(PROPERTY1, "AB", false, true)); - } - - @Test - public void testStartsWithCaseInsensitive() { - Assert.assertTrue(passes(PROPERTY1, "AB", true, true)); - Assert.assertTrue(passes(PROPERTY2, "te", true, true)); - Assert.assertFalse(passes(PROPERTY2, "AB", true, true)); - } - - @Test - public void testContainsCaseSensitive() { - Assert.assertTrue(passes(PROPERTY1, "ab", false, false)); - Assert.assertTrue(passes(PROPERTY1, "abcde", false, false)); - Assert.assertTrue(passes(PROPERTY1, "cd", false, false)); - Assert.assertTrue(passes(PROPERTY1, "e", false, false)); - Assert.assertTrue(passes(PROPERTY1, "", false, false)); - - Assert.assertFalse(passes(PROPERTY2, "ab", false, false)); - Assert.assertFalse(passes(PROPERTY1, "es", false, false)); - } - - @Test - public void testContainsCaseInsensitive() { - Assert.assertTrue(passes(PROPERTY1, "AB", true, false)); - Assert.assertTrue(passes(PROPERTY1, "aBcDe", true, false)); - Assert.assertTrue(passes(PROPERTY1, "CD", true, false)); - Assert.assertTrue(passes(PROPERTY1, "", true, false)); - - Assert.assertTrue(passes(PROPERTY2, "es", true, false)); - - Assert.assertFalse(passes(PROPERTY2, "ab", true, false)); - } - - @Test - public void testAppliesToProperty() { - SimpleStringFilter filter = f(PROPERTY1, "ab", false, true); - Assert.assertTrue(filter.appliesToProperty(PROPERTY1)); - Assert.assertFalse(filter.appliesToProperty(PROPERTY2)); - Assert.assertFalse(filter.appliesToProperty("other")); - } - - @Test - public void testEqualsHashCode() { - SimpleStringFilter filter = f(PROPERTY1, "ab", false, true); - - SimpleStringFilter f1 = f(PROPERTY2, "ab", false, true); - SimpleStringFilter f1b = f(PROPERTY2, "ab", false, true); - SimpleStringFilter f2 = f(PROPERTY1, "cd", false, true); - SimpleStringFilter f2b = f(PROPERTY1, "cd", false, true); - SimpleStringFilter f3 = f(PROPERTY1, "ab", true, true); - SimpleStringFilter f3b = f(PROPERTY1, "ab", true, true); - SimpleStringFilter f4 = f(PROPERTY1, "ab", false, false); - SimpleStringFilter f4b = f(PROPERTY1, "ab", false, false); - - // equal but not same instance - Assert.assertEquals(f1, f1b); - Assert.assertEquals(f2, f2b); - Assert.assertEquals(f3, f3b); - Assert.assertEquals(f4, f4b); - - // more than one property differ - Assert.assertFalse(f1.equals(f2)); - Assert.assertFalse(f1.equals(f3)); - Assert.assertFalse(f1.equals(f4)); - Assert.assertFalse(f2.equals(f1)); - Assert.assertFalse(f2.equals(f3)); - Assert.assertFalse(f2.equals(f4)); - Assert.assertFalse(f3.equals(f1)); - Assert.assertFalse(f3.equals(f2)); - Assert.assertFalse(f3.equals(f4)); - Assert.assertFalse(f4.equals(f1)); - Assert.assertFalse(f4.equals(f2)); - Assert.assertFalse(f4.equals(f3)); - - // only one property differs - Assert.assertFalse(filter.equals(f1)); - Assert.assertFalse(filter.equals(f2)); - Assert.assertFalse(filter.equals(f3)); - Assert.assertFalse(filter.equals(f4)); - - Assert.assertFalse(f1.equals(null)); - Assert.assertFalse(f1.equals(new Object())); - - Assert.assertEquals(f1.hashCode(), f1b.hashCode()); - Assert.assertEquals(f2.hashCode(), f2b.hashCode()); - Assert.assertEquals(f3.hashCode(), f3b.hashCode()); - Assert.assertEquals(f4.hashCode(), f4b.hashCode()); - } - - @Test - public void testNonExistentProperty() { - Assert.assertFalse(passes("other1", "ab", false, true)); - } - - @Test - public void testNullValueForProperty() { - TestItem item = createTestItem(); - item.addItemProperty("other1", new NullProperty()); - - Assert.assertFalse( - f("other1", "ab", false, true).passesFilter(null, item)); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/AllTests.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/AllTests.java deleted file mode 100644 index 9c3f6c0eaa..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/AllTests.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -import com.vaadin.data.util.sqlcontainer.connection.J2EEConnectionPoolTest; -import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPoolTest; -import com.vaadin.data.util.sqlcontainer.filters.BetweenTest; -import com.vaadin.data.util.sqlcontainer.filters.LikeTest; -import com.vaadin.data.util.sqlcontainer.generator.SQLGeneratorsTest; -import com.vaadin.data.util.sqlcontainer.query.FreeformQueryTest; -import com.vaadin.data.util.sqlcontainer.query.QueryBuilderTest; -import com.vaadin.data.util.sqlcontainer.query.TableQueryTest; - -@RunWith(Suite.class) -@SuiteClasses({ SimpleJDBCConnectionPoolTest.class, - J2EEConnectionPoolTest.class, LikeTest.class, QueryBuilderTest.class, - FreeformQueryTest.class, RowIdTest.class, SQLContainerTest.class, - SQLContainerTableQueryTest.class, ColumnPropertyTest.class, - TableQueryTest.class, SQLGeneratorsTest.class, UtilTest.class, - TicketTest.class, BetweenTest.class, ReadOnlyRowIdTest.class }) -public class AllTests { -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ColumnPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ColumnPropertyTest.java deleted file mode 100644 index edc56035e1..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ColumnPropertyTest.java +++ /dev/null @@ -1,318 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Property.ReadOnlyException; -import com.vaadin.data.util.sqlcontainer.ColumnProperty.NotNullableException; -import com.vaadin.data.util.sqlcontainer.query.QueryDelegate; - -public class ColumnPropertyTest { - - @Test - public void constructor_legalParameters_shouldSucceed() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - Assert.assertNotNull(cp); - } - - @Test(expected = IllegalArgumentException.class) - public void constructor_missingPropertyId_shouldFail() { - new ColumnProperty(null, false, true, true, false, "Ville", - String.class); - } - - @Test(expected = IllegalArgumentException.class) - public void constructor_missingType_shouldFail() { - new ColumnProperty("NAME", false, true, true, false, "Ville", null); - } - - @Test - public void getValue_defaultValue_returnsVille() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - Assert.assertEquals("Ville", cp.getValue()); - } - - @Test - public void setValue_readWriteNullable_returnsKalle() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - SQLContainer container = EasyMock.createMock(SQLContainer.class); - RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(cp)); - container.itemChangeNotification(owner); - EasyMock.replay(container); - cp.setValue("Kalle"); - Assert.assertEquals("Kalle", cp.getValue()); - EasyMock.verify(container); - } - - @Test(expected = ReadOnlyException.class) - public void setValue_readOnlyNullable_shouldFail() { - ColumnProperty cp = new ColumnProperty("NAME", true, true, true, false, - "Ville", String.class); - SQLContainer container = EasyMock.createMock(SQLContainer.class); - new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(cp)); - EasyMock.replay(container); - cp.setValue("Kalle"); - EasyMock.verify(container); - } - - @Test - public void setValue_readWriteNullable_nullShouldWork() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - SQLContainer container = EasyMock.createMock(SQLContainer.class); - RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(cp)); - container.itemChangeNotification(owner); - EasyMock.replay(container); - cp.setValue(null); - Assert.assertNull(cp.getValue()); - EasyMock.verify(container); - } - - @Test(expected = NotNullableException.class) - public void setValue_readWriteNotNullable_nullShouldFail() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, false, - false, "Ville", String.class); - SQLContainer container = EasyMock.createMock(SQLContainer.class); - RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(cp)); - container.itemChangeNotification(owner); - EasyMock.replay(container); - cp.setValue(null); - Assert.assertNotNull(cp.getValue()); - EasyMock.verify(container); - } - - @Test - public void getType_normal_returnsStringClass() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - Assert.assertSame(String.class, cp.getType()); - } - - @Test - public void isReadOnly_readWriteNullable_returnsTrue() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - Assert.assertFalse(cp.isReadOnly()); - } - - @Test - public void isReadOnly_readOnlyNullable_returnsTrue() { - ColumnProperty cp = new ColumnProperty("NAME", true, true, true, false, - "Ville", String.class); - Assert.assertTrue(cp.isReadOnly()); - } - - @Test - public void setReadOnly_readOnlyChangeAllowed_shouldSucceed() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - cp.setReadOnly(true); - Assert.assertTrue(cp.isReadOnly()); - } - - @Test - public void setReadOnly_readOnlyChangeDisallowed_shouldFail() { - ColumnProperty cp = new ColumnProperty("NAME", false, false, true, - false, "Ville", String.class); - cp.setReadOnly(true); - Assert.assertFalse(cp.isReadOnly()); - } - - @Test - public void getPropertyId_normal_returnsNAME() { - ColumnProperty cp = new ColumnProperty("NAME", false, false, true, - false, "Ville", String.class); - Assert.assertEquals("NAME", cp.getPropertyId()); - } - - @Test - public void isModified_valueModified_returnsTrue() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "Ville", String.class); - SQLContainer container = EasyMock.createMock(SQLContainer.class); - RowItem owner = new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(cp)); - container.itemChangeNotification(owner); - EasyMock.replay(container); - cp.setValue("Kalle"); - Assert.assertEquals("Kalle", cp.getValue()); - Assert.assertTrue(cp.isModified()); - EasyMock.verify(container); - } - - @Test - public void isModified_valueNotModified_returnsFalse() { - ColumnProperty cp = new ColumnProperty("NAME", false, false, true, - false, "Ville", String.class); - Assert.assertFalse(cp.isModified()); - } - - @Test - public void setValue_nullOnNullable_shouldWork() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - "asdf", String.class); - SQLContainer container = EasyMock.createMock(SQLContainer.class); - new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(cp)); - cp.setValue(null); - Assert.assertNull(cp.getValue()); - } - - @Test - public void setValue_resetTonullOnNullable_shouldWork() { - ColumnProperty cp = new ColumnProperty("NAME", false, true, true, false, - null, String.class); - SQLContainer container = EasyMock.createMock(SQLContainer.class); - new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(cp)); - cp.setValue("asdf"); - Assert.assertEquals("asdf", cp.getValue()); - cp.setValue(null); - Assert.assertNull(cp.getValue()); - } - - @Test - public void setValue_sendsItemChangeNotification() throws SQLException { - - class TestContainer extends SQLContainer { - Object value = null; - boolean modified = false; - - public TestContainer(QueryDelegate delegate) throws SQLException { - super(delegate); - } - - @Override - public void itemChangeNotification(RowItem changedItem) { - ColumnProperty cp = (ColumnProperty) changedItem - .getItemProperty("NAME"); - value = cp.getValue(); - modified = cp.isModified(); - } - } - - ColumnProperty property = new ColumnProperty("NAME", false, true, true, - false, "Ville", String.class); - - Statement statement = EasyMock.createNiceMock(Statement.class); - EasyMock.replay(statement); - - ResultSetMetaData metadata = EasyMock - .createNiceMock(ResultSetMetaData.class); - EasyMock.replay(metadata); - - ResultSet resultSet = EasyMock.createNiceMock(ResultSet.class); - EasyMock.expect(resultSet.getStatement()).andReturn(statement); - EasyMock.expect(resultSet.getMetaData()).andReturn(metadata); - EasyMock.replay(resultSet); - - QueryDelegate delegate = EasyMock.createNiceMock(QueryDelegate.class); - EasyMock.expect(delegate.getResults(0, 1)).andReturn(resultSet); - EasyMock.replay(delegate); - - TestContainer container = new TestContainer(delegate); - - new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(property)); - - property.setValue("Kalle"); - Assert.assertEquals("Kalle", container.value); - Assert.assertTrue(container.modified); - } - - @Test - public void versionColumnsShouldNotBeInValueMap_shouldReturnFalse() { - ColumnProperty property = new ColumnProperty("NAME", false, true, true, - false, "Ville", String.class); - property.setVersionColumn(true); - - Assert.assertFalse(property.isPersistent()); - } - - @Test - public void neverWritableColumnsShouldNotBeInValueMap_shouldReturnFalse() { - ColumnProperty property = new ColumnProperty("NAME", true, false, true, - false, "Ville", String.class); - - Assert.assertFalse(property.isPersistent()); - } - - @Test - public void writableColumnsShouldBeInValueMap_shouldReturnTrue() { - ColumnProperty property = new ColumnProperty("NAME", false, true, true, - false, "Ville", String.class); - - Assert.assertTrue(property.isPersistent()); - } - - @Test - public void writableButReadOnlyColumnsShouldNotBeInValueMap_shouldReturnFalse() { - ColumnProperty property = new ColumnProperty("NAME", true, true, true, - false, "Ville", String.class); - - Assert.assertFalse(property.isPersistent()); - } - - @Test - public void primKeysShouldBeRowIdentifiers_shouldReturnTrue() { - ColumnProperty property = new ColumnProperty("NAME", false, true, true, - true, "Ville", String.class); - - Assert.assertTrue(property.isRowIdentifier()); - } - - @Test - public void versionColumnsShouldBeRowIdentifiers_shouldReturnTrue() { - ColumnProperty property = new ColumnProperty("NAME", false, true, true, - false, "Ville", String.class); - property.setVersionColumn(true); - - Assert.assertTrue(property.isRowIdentifier()); - } - - @Test - public void nonPrimKeyOrVersionColumnsShouldBeNotRowIdentifiers_shouldReturnFalse() { - ColumnProperty property = new ColumnProperty("NAME", false, true, true, - false, "Ville", String.class); - - Assert.assertFalse(property.isRowIdentifier()); - } - - @Test - public void getOldValueShouldReturnPreviousValue_shouldReturnVille() { - ColumnProperty property = new ColumnProperty("NAME", false, true, true, - false, "Ville", String.class); - - // Here we really don't care about the container management, but in - // order to set the value for a column the owner (RowItem) must be set - // and to create the owner we must have a container... - ArrayList properties = new ArrayList(); - properties.add(property); - - SQLContainer container = EasyMock.createNiceMock(SQLContainer.class); - RowItem rowItem = new RowItem(container, new RowId(new Object[] { 1 }), - Arrays.asList(property)); - - property.setValue("Kalle"); - // Just check that the new value was actually set... - Assert.assertEquals("Kalle", property.getValue()); - // Assert that old value is the original value... - Assert.assertEquals("Ville", property.getOldValue()); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/DataGenerator.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/DataGenerator.java deleted file mode 100644 index 3d12fc78ba..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/DataGenerator.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import org.junit.Assert; - -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants.DB; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; - -public class DataGenerator { - - public static void addPeopleToDatabase(JDBCConnectionPool connectionPool) - throws SQLException { - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - try { - statement.execute("drop table PEOPLE"); - if (SQLTestsConstants.db == DB.ORACLE) { - statement.execute("drop sequence people_seq"); - } - } catch (SQLException e) { - // Will fail if table doesn't exist, which is OK. - conn.rollback(); - } - statement.execute(SQLTestsConstants.peopleFirst); - if (SQLTestsConstants.peopleSecond != null) { - statement.execute(SQLTestsConstants.peopleSecond); - } - if (SQLTestsConstants.db == DB.ORACLE) { - statement.execute(SQLTestsConstants.peopleThird); - } - if (SQLTestsConstants.db == DB.MSSQL) { - statement.executeUpdate("insert into people values('Ville', '23')"); - statement.executeUpdate("insert into people values('Kalle', '7')"); - statement.executeUpdate("insert into people values('Pelle', '18')"); - statement.executeUpdate("insert into people values('Börje', '64')"); - } else { - statement.executeUpdate( - "insert into people values(default, 'Ville', '23')"); - statement.executeUpdate( - "insert into people values(default, 'Kalle', '7')"); - statement.executeUpdate( - "insert into people values(default, 'Pelle', '18')"); - statement.executeUpdate( - "insert into people values(default, 'Börje', '64')"); - } - statement.close(); - statement = conn.createStatement(); - ResultSet rs = statement.executeQuery("select * from PEOPLE"); - Assert.assertTrue(rs.next()); - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - } - - public static void addFiveThousandPeople(JDBCConnectionPool connectionPool) - throws SQLException { - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - for (int i = 4; i < 5000; i++) { - if (SQLTestsConstants.db == DB.MSSQL) { - statement.executeUpdate("insert into people values('Person " + i - + "', '" + i % 99 + "')"); - } else { - statement.executeUpdate( - "insert into people values(default, 'Person " + i - + "', '" + i % 99 + "')"); - } - } - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - } - - public static void addVersionedData(JDBCConnectionPool connectionPool) - throws SQLException { - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - try { - statement.execute("DROP TABLE VERSIONED"); - if (SQLTestsConstants.db == DB.ORACLE) { - statement.execute("drop sequence versioned_seq"); - statement.execute("drop sequence versioned_version"); - } - } catch (SQLException e) { - // Will fail if table doesn't exist, which is OK. - conn.rollback(); - } - for (String stmtString : SQLTestsConstants.versionStatements) { - statement.execute(stmtString); - } - if (SQLTestsConstants.db == DB.MSSQL) { - statement.executeUpdate( - "insert into VERSIONED values('Junk', default)"); - } else { - statement.executeUpdate( - "insert into VERSIONED values(default, 'Junk', default)"); - } - statement.close(); - statement = conn.createStatement(); - ResultSet rs = statement.executeQuery("select * from VERSIONED"); - Assert.assertTrue(rs.next()); - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - } - - public static void createGarbage(JDBCConnectionPool connectionPool) - throws SQLException { - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - try { - statement.execute("drop table GARBAGE"); - if (SQLTestsConstants.db == DB.ORACLE) { - statement.execute("drop sequence garbage_seq"); - } - } catch (SQLException e) { - // Will fail if table doesn't exist, which is OK. - conn.rollback(); - } - statement.execute(SQLTestsConstants.createGarbage); - if (SQLTestsConstants.db == DB.ORACLE) { - statement.execute(SQLTestsConstants.createGarbageSecond); - statement.execute(SQLTestsConstants.createGarbageThird); - } - conn.commit(); - connectionPool.releaseConnection(conn); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/FreeformQueryUtil.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/FreeformQueryUtil.java deleted file mode 100644 index c8b5b5a2b3..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/FreeformQueryUtil.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import java.util.List; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants.DB; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; - -public class FreeformQueryUtil { - - public static StatementHelper getQueryWithFilters(List filters, - int offset, int limit) { - StatementHelper sh = new StatementHelper(); - if (SQLTestsConstants.db == DB.MSSQL) { - if (limit > 1) { - offset++; - limit--; - } - StringBuilder query = new StringBuilder(); - query.append("SELECT * FROM (SELECT row_number() OVER ("); - query.append("ORDER BY \"ID\" ASC"); - query.append(") AS rownum, * FROM \"PEOPLE\""); - - if (!filters.isEmpty()) { - query.append( - QueryBuilder.getWhereStringForFilters(filters, sh)); - } - query.append(") AS a WHERE a.rownum BETWEEN ").append(offset) - .append(" AND ").append(Integer.toString(offset + limit)); - sh.setQueryString(query.toString()); - return sh; - } else if (SQLTestsConstants.db == DB.ORACLE) { - if (limit > 1) { - offset++; - limit--; - } - StringBuilder query = new StringBuilder(); - query.append("SELECT * FROM (SELECT x.*, ROWNUM AS " - + "\"rownum\" FROM (SELECT * FROM \"PEOPLE\""); - if (!filters.isEmpty()) { - query.append( - QueryBuilder.getWhereStringForFilters(filters, sh)); - } - query.append(") x) WHERE \"rownum\" BETWEEN ? AND ?"); - sh.addParameterValue(offset); - sh.addParameterValue(offset + limit); - sh.setQueryString(query.toString()); - return sh; - } else { - StringBuilder query = new StringBuilder("SELECT * FROM people"); - if (!filters.isEmpty()) { - query.append( - QueryBuilder.getWhereStringForFilters(filters, sh)); - } - if (limit != 0 || offset != 0) { - query.append(" LIMIT ? OFFSET ?"); - sh.addParameterValue(limit); - sh.addParameterValue(offset); - } - sh.setQueryString(query.toString()); - return sh; - } - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowIdTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowIdTest.java deleted file mode 100644 index 29968ecf94..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/ReadOnlyRowIdTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import org.junit.Assert; -import org.junit.Test; - -public class ReadOnlyRowIdTest { - - @Test - public void getRowNum_shouldReturnRowNumGivenInConstructor() { - int rowNum = 1337; - ReadOnlyRowId rid = new ReadOnlyRowId(rowNum); - Assert.assertEquals(rowNum, rid.getRowNum()); - } - - @Test - public void hashCode_shouldBeEqualToHashCodeOfRowNum() { - int rowNum = 1337; - ReadOnlyRowId rid = new ReadOnlyRowId(rowNum); - Assert.assertEquals(Integer.valueOf(rowNum).hashCode(), rid.hashCode()); - } - - @Test - public void equals_compareWithNull_shouldBeFalse() { - ReadOnlyRowId rid = new ReadOnlyRowId(1337); - Assert.assertFalse(rid.equals(null)); - } - - @Test - public void equals_compareWithSameInstance_shouldBeTrue() { - ReadOnlyRowId rid = new ReadOnlyRowId(1337); - ReadOnlyRowId rid2 = rid; - Assert.assertTrue(rid.equals(rid2)); - } - - @Test - public void equals_compareWithOtherType_shouldBeFalse() { - ReadOnlyRowId rid = new ReadOnlyRowId(1337); - Assert.assertFalse(rid.equals(new Object())); - } - - @Test - public void equals_compareWithOtherRowId_shouldBeFalse() { - ReadOnlyRowId rid = new ReadOnlyRowId(1337); - ReadOnlyRowId rid2 = new ReadOnlyRowId(42); - Assert.assertFalse(rid.equals(rid2)); - } - - @Test - public void toString_rowNumberIsReturned() { - int i = 1; - ReadOnlyRowId rowId = new ReadOnlyRowId(i); - Assert.assertEquals("Unexpected toString value", String.valueOf(i), - rowId.toString()); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/RowIdTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/RowIdTest.java deleted file mode 100644 index e93048157b..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/RowIdTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import org.junit.Assert; -import org.junit.Test; - -public class RowIdTest { - - @Test - public void constructor_withArrayOfPrimaryKeyColumns_shouldSucceed() { - RowId id = new RowId(new Object[] { "id", "name" }); - Assert.assertArrayEquals(new Object[] { "id", "name" }, id.getId()); - } - - @Test(expected = IllegalArgumentException.class) - public void constructor_withNullParameter_shouldFail() { - new RowId(null); - } - - @Test - public void hashCode_samePrimaryKeys_sameResult() { - RowId id = new RowId(new Object[] { "id", "name" }); - RowId id2 = new RowId(new Object[] { "id", "name" }); - Assert.assertEquals(id.hashCode(), id2.hashCode()); - } - - @Test - public void hashCode_differentPrimaryKeys_differentResult() { - RowId id = new RowId(new Object[] { "id", "name" }); - RowId id2 = new RowId(new Object[] { "id" }); - Assert.assertFalse(id.hashCode() == id2.hashCode()); - } - - @Test - public void equals_samePrimaryKeys_returnsTrue() { - RowId id = new RowId(new Object[] { "id", "name" }); - RowId id2 = new RowId(new Object[] { "id", "name" }); - Assert.assertEquals(id, id2); - } - - @Test - public void equals_differentPrimaryKeys_returnsFalse() { - RowId id = new RowId(new Object[] { "id", "name" }); - RowId id2 = new RowId(new Object[] { "id" }); - Assert.assertFalse(id.equals(id2.hashCode())); - } - - @Test - public void equals_differentDataType_returnsFalse() { - RowId id = new RowId(new Object[] { "id", "name" }); - Assert.assertFalse(id.equals("Tudiluu")); - Assert.assertFalse(id.equals(new Integer(1337))); - } - - @Test - public void toString_defaultCtor_noException() { - RowId rowId = new RowId(); - Assert.assertTrue("Unexpected to string for empty Row Id", - rowId.toString().isEmpty()); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java deleted file mode 100644 index 0bdcc9a3c3..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java +++ /dev/null @@ -1,1353 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.math.BigDecimal; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.easymock.EasyMock; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Item; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants.DB; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; -import com.vaadin.data.util.sqlcontainer.query.TableQuery; -import com.vaadin.data.util.sqlcontainer.query.ValidatingSimpleJDBCConnectionPool; - -public class SQLContainerTableQueryTest { - - private static final int offset = SQLTestsConstants.offset; - private final int numberOfRowsInContainer = 4; - private final int numberOfPropertiesInContainer = 3; - private final String NAME = "NAME"; - private final String ID = "ID"; - private final String AGE = "AGE"; - private JDBCConnectionPool connectionPool; - private TableQuery query; - private SQLContainer container; - private final RowId existingItemId = getRowId(1); - private final RowId nonExistingItemId = getRowId(1337); - - @Before - public void setUp() throws SQLException { - - try { - connectionPool = new ValidatingSimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 2, 2); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - - DataGenerator.addPeopleToDatabase(connectionPool); - - query = getTableQuery("people"); - container = new SQLContainer(query); - } - - private TableQuery getTableQuery(String tableName) { - return new TableQuery(tableName, connectionPool, - SQLTestsConstants.sqlGen); - } - - private SQLContainer getGarbageContainer() throws SQLException { - DataGenerator.createGarbage(connectionPool); - - return new SQLContainer(getTableQuery("garbage")); - } - - private Item getItem(Object id) { - return container.getItem(id); - } - - private RowId getRowId(int id) { - return new RowId(new Object[] { id + offset }); - } - - @After - public void tearDown() { - if (connectionPool != null) { - connectionPool.destroy(); - } - } - - @Test - public void itemWithExistingVersionColumnIsRemoved() throws SQLException { - container.setAutoCommit(true); - query.setVersionColumn(ID); - - assertTrue(container.removeItem(container.lastItemId())); - } - - @Test(expected = SQLException.class) - public void itemWithNonExistingVersionColumnCannotBeRemoved() - throws SQLException { - query.setVersionColumn("version"); - - container.removeItem(container.lastItemId()); - - container.commit(); - } - - @Test - public void containerContainsId() { - assertTrue(container.containsId(existingItemId)); - } - - @Test - public void containerDoesNotContainId() { - assertFalse(container.containsId(nonExistingItemId)); - } - - @Test - public void idPropertyHasCorrectType() { - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(container.getType(ID), BigDecimal.class); - } else { - assertEquals(container.getType(ID), Integer.class); - } - } - - @Test - public void namePropertyHasCorrectType() { - assertEquals(container.getType(NAME), String.class); - } - - @Test - public void nonExistingPropertyDoesNotHaveType() { - assertThat(container.getType("adsf"), is(nullValue())); - } - - @Test - public void sizeIsReturnedCorrectly() { - assertEquals(numberOfRowsInContainer, container.size()); - } - - @Test - public void propertyIsFetchedForExistingItem() { - assertThat(container.getContainerProperty(existingItemId, NAME) - .getValue().toString(), is("Kalle")); - } - - @Test - public void containerDoesNotContainPropertyForExistingItem() { - assertThat(container.getContainerProperty(existingItemId, "asdf"), - is(nullValue())); - } - - @Test - public void containerDoesNotContainExistingPropertyForNonExistingItem() { - assertThat(container.getContainerProperty(nonExistingItemId, NAME), - is(nullValue())); - } - - @Test - public void propertyIdsAreFetched() { - ArrayList propertyIds = new ArrayList( - (Collection) container - .getContainerPropertyIds()); - - assertThat(propertyIds.size(), is(numberOfPropertiesInContainer)); - assertThat(propertyIds, hasItems(ID, NAME, AGE)); - } - - @Test - public void existingItemIsFetched() { - Item item = container.getItem(existingItemId); - - assertThat(item.getItemProperty(NAME).getValue().toString(), - is("Kalle")); - } - - @Test - public void newItemIsAdded() throws SQLException { - Object id = container.addItem(); - getItem(id).getItemProperty(NAME).setValue("foo"); - - container.commit(); - - Item item = getItem(container.lastItemId()); - assertThat(item.getItemProperty(NAME).getValue(), is("foo")); - } - - @Test - public void itemPropertyIsNotRevertedOnRefresh() { - getItem(existingItemId).getItemProperty(NAME).setValue("foo"); - - container.refresh(); - - assertThat(getItem(existingItemId).getItemProperty(NAME).getValue(), - is("foo")); - } - - @Test - public void correctItemIsFetchedFromMultipleRows() throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - Item item = container.getItem(getRowId(1337)); - - assertThat((Integer) item.getItemProperty(ID).getValue(), - is(equalTo(1337 + offset))); - assertThat(item.getItemProperty(NAME).getValue().toString(), - is("Person 1337")); - } - - @Test - public void getItemIds_table_returnsItemIdsWithKeys0through3() - throws SQLException { - Collection itemIds = container.getItemIds(); - assertEquals(4, itemIds.size()); - RowId zero = new RowId(new Object[] { 0 + offset }); - RowId one = new RowId(new Object[] { 1 + offset }); - RowId two = new RowId(new Object[] { 2 + offset }); - RowId three = new RowId(new Object[] { 3 + offset }); - if (SQLTestsConstants.db == DB.ORACLE) { - String[] correct = new String[] { "1", "2", "3", "4" }; - List oracle = new ArrayList(); - for (Object o : itemIds) { - oracle.add(o.toString()); - } - Assert.assertArrayEquals(correct, oracle.toArray()); - } else { - Assert.assertArrayEquals(new Object[] { zero, one, two, three }, - itemIds.toArray()); - } - } - - @Test - public void size_tableOneAddedItem_returnsFive() throws SQLException { - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - if (SQLTestsConstants.db == DB.MSSQL) { - statement.executeUpdate("insert into people values('Bengt', 30)"); - } else { - statement.executeUpdate( - "insert into people values(default, 'Bengt', 30)"); - } - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - - assertEquals(5, container.size()); - } - - @Test - public void indexOfId_tableWithParameterThree_returnsThree() - throws SQLException { - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(3, container.indexOfId( - new RowId(new Object[] { new BigDecimal(3 + offset) }))); - } else { - assertEquals(3, container - .indexOfId(new RowId(new Object[] { 3 + offset }))); - } - } - - @Test - public void indexOfId_table5000RowsWithParameter1337_returns1337() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - if (SQLTestsConstants.db == DB.ORACLE) { - container.getItem( - new RowId(new Object[] { new BigDecimal(1337 + offset) })); - assertEquals(1337, container.indexOfId( - new RowId(new Object[] { new BigDecimal(1337 + offset) }))); - } else { - container.getItem(new RowId(new Object[] { 1337 + offset })); - assertEquals(1337, container - .indexOfId(new RowId(new Object[] { 1337 + offset }))); - } - } - - @Test - public void getIdByIndex_table5000rowsIndex1337_returnsRowId1337() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(new RowId(new Object[] { 1337 + offset }).toString(), - itemId.toString()); - } else { - assertEquals(new RowId(new Object[] { 1337 + offset }), itemId); - } - } - - @Test - public void getIdByIndex_tableWithPaging5000rowsIndex1337_returnsRowId1337() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(new RowId(new Object[] { 1337 + offset }).toString(), - itemId.toString()); - } else { - assertEquals(new RowId(new Object[] { 1337 + offset }), itemId); - } - } - - @Test - public void nextItemId_tableCurrentItem1337_returnsItem1338() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - SQLContainer container = new SQLContainer(new TableQuery("people", - connectionPool, SQLTestsConstants.sqlGen)); - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(new RowId(new Object[] { 1338 + offset }).toString(), - container.nextItemId(itemId).toString()); - } else { - assertEquals(new RowId(new Object[] { 1338 + offset }), - container.nextItemId(itemId)); - } - } - - @Test - public void prevItemId_tableCurrentItem1337_returns1336() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(new RowId(new Object[] { 1336 + offset }).toString(), - container.prevItemId(itemId).toString()); - } else { - assertEquals(new RowId(new Object[] { 1336 + offset }), - container.prevItemId(itemId)); - } - } - - @Test - public void firstItemId_table_returnsItemId0() throws SQLException { - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(new RowId(new Object[] { 0 + offset }).toString(), - container.firstItemId().toString()); - } else { - assertEquals(new RowId(new Object[] { 0 + offset }), - container.firstItemId()); - } - } - - @Test - public void lastItemId_table5000Rows_returnsItemId4999() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - if (SQLTestsConstants.db == DB.ORACLE) { - assertEquals(new RowId(new Object[] { 4999 + offset }).toString(), - container.lastItemId().toString()); - } else { - assertEquals(new RowId(new Object[] { 4999 + offset }), - container.lastItemId()); - } - } - - @Test - public void isFirstId_tableActualFirstId_returnsTrue() throws SQLException { - if (SQLTestsConstants.db == DB.ORACLE) { - assertTrue(container.isFirstId( - new RowId(new Object[] { new BigDecimal(0 + offset) }))); - } else { - assertTrue(container - .isFirstId(new RowId(new Object[] { 0 + offset }))); - } - } - - @Test - public void isFirstId_tableSecondId_returnsFalse() throws SQLException { - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertFalse(container.isFirstId( - new RowId(new Object[] { new BigDecimal(1 + offset) }))); - } else { - Assert.assertFalse(container - .isFirstId(new RowId(new Object[] { 1 + offset }))); - } - } - - @Test - public void isLastId_tableSecondId_returnsFalse() throws SQLException { - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertFalse(container.isLastId( - new RowId(new Object[] { new BigDecimal(1 + offset) }))); - } else { - Assert.assertFalse( - container.isLastId(new RowId(new Object[] { 1 + offset }))); - } - } - - @Test - public void isLastId_tableLastId_returnsTrue() throws SQLException { - if (SQLTestsConstants.db == DB.ORACLE) { - assertTrue(container.isLastId( - new RowId(new Object[] { new BigDecimal(3 + offset) }))); - } else { - assertTrue( - container.isLastId(new RowId(new Object[] { 3 + offset }))); - } - } - - @Test - public void isLastId_table5000RowsLastId_returnsTrue() throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - if (SQLTestsConstants.db == DB.ORACLE) { - assertTrue(container.isLastId( - new RowId(new Object[] { new BigDecimal(4999 + offset) }))); - } else { - assertTrue(container - .isLastId(new RowId(new Object[] { 4999 + offset }))); - } - } - - @Test - public void allIdsFound_table5000RowsLastId_shouldSucceed() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - for (int i = 0; i < 5000; i++) { - assertTrue(container.containsId(container.getIdByIndex(i))); - } - } - - @Test - public void allIdsFound_table5000RowsLastId_autoCommit_shouldSucceed() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - container.setAutoCommit(true); - for (int i = 0; i < 5000; i++) { - assertTrue(container.containsId(container.getIdByIndex(i))); - } - } - - @Test - public void refresh_table_sizeShouldUpdate() throws SQLException { - assertEquals(4, container.size()); - DataGenerator.addFiveThousandPeople(connectionPool); - container.refresh(); - assertEquals(5000, container.size()); - } - - @Test - public void refresh_tableWithoutCallingRefresh_sizeShouldNotUpdate() - throws SQLException { - // Yeah, this is a weird one. We're testing that the size doesn't update - // after adding lots of items unless we call refresh inbetween. This to - // make sure that the refresh method actually refreshes stuff and isn't - // a NOP. - assertEquals(4, container.size()); - DataGenerator.addFiveThousandPeople(connectionPool); - assertEquals(4, container.size()); - } - - @Test - public void setAutoCommit_table_shouldSucceed() throws SQLException { - container.setAutoCommit(true); - assertTrue(container.isAutoCommit()); - container.setAutoCommit(false); - Assert.assertFalse(container.isAutoCommit()); - } - - @Test - public void getPageLength_table_returnsDefault100() throws SQLException { - assertEquals(100, container.getPageLength()); - } - - @Test - public void setPageLength_table_shouldSucceed() throws SQLException { - container.setPageLength(20); - assertEquals(20, container.getPageLength()); - container.setPageLength(200); - assertEquals(200, container.getPageLength()); - } - - @Test(expected = UnsupportedOperationException.class) - public void addContainerProperty_normal_isUnsupported() - throws SQLException { - container.addContainerProperty("asdf", String.class, ""); - } - - @Test(expected = UnsupportedOperationException.class) - public void removeContainerProperty_normal_isUnsupported() - throws SQLException { - container.removeContainerProperty("asdf"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemObject_normal_isUnsupported() throws SQLException { - container.addItem("asdf"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAfterObjectObject_normal_isUnsupported() - throws SQLException { - container.addItemAfter("asdf", "foo"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAtIntObject_normal_isUnsupported() throws SQLException { - container.addItemAt(2, "asdf"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAtInt_normal_isUnsupported() throws SQLException { - container.addItemAt(2); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAfterObject_normal_isUnsupported() throws SQLException { - container.addItemAfter("asdf"); - } - - @Test - public void addItem_tableAddOneNewItem_returnsItemId() throws SQLException { - Object itemId = container.addItem(); - Assert.assertNotNull(itemId); - } - - @Test - public void addItem_tableAddOneNewItem_autoCommit_returnsFinalItemId() - throws SQLException { - container.setAutoCommit(true); - Object itemId = container.addItem(); - Assert.assertNotNull(itemId); - assertTrue(itemId instanceof RowId); - Assert.assertFalse(itemId instanceof TemporaryRowId); - } - - @Test - public void addItem_tableAddOneNewItem_autoCommit_sizeIsIncreased() - throws SQLException { - container.setAutoCommit(true); - int originalSize = container.size(); - container.addItem(); - assertEquals(originalSize + 1, container.size()); - } - - @Test - public void addItem_tableAddOneNewItem_shouldChangeSize() - throws SQLException { - int size = container.size(); - container.addItem(); - assertEquals(size + 1, container.size()); - } - - @Test - public void addItem_tableAddTwoNewItems_shouldChangeSize() - throws SQLException { - int size = container.size(); - Object id1 = container.addItem(); - Object id2 = container.addItem(); - assertEquals(size + 2, container.size()); - Assert.assertNotSame(id1, id2); - Assert.assertFalse(id1.equals(id2)); - } - - @Test - public void nextItemId_tableNewlyAddedItem_returnsNewlyAdded() - throws SQLException { - Object lastId = container.lastItemId(); - Object id = container.addItem(); - assertEquals(id, container.nextItemId(lastId)); - } - - @Test - public void lastItemId_tableNewlyAddedItem_returnsNewlyAdded() - throws SQLException { - Object lastId = container.lastItemId(); - Object id = container.addItem(); - assertEquals(id, container.lastItemId()); - Assert.assertNotSame(lastId, container.lastItemId()); - } - - @Test - public void indexOfId_tableNewlyAddedItem_returnsFour() - throws SQLException { - Object id = container.addItem(); - assertEquals(4, container.indexOfId(id)); - } - - @Test - public void getItem_tableNewlyAddedItem_returnsNewlyAdded() - throws SQLException { - Object id = container.addItem(); - Assert.assertNotNull(container.getItem(id)); - } - - @Test - public void getItemIds_tableNewlyAddedItem_containsNewlyAdded() - throws SQLException { - Object id = container.addItem(); - assertTrue(container.getItemIds().contains(id)); - } - - @Test - public void getContainerProperty_tableNewlyAddedItem_returnsPropertyOfNewlyAddedItem() - throws SQLException { - Object id = container.addItem(); - Item item = container.getItem(id); - item.getItemProperty(NAME).setValue("asdf"); - assertEquals("asdf", - container.getContainerProperty(id, NAME).getValue()); - } - - @Test - public void containsId_tableNewlyAddedItem_returnsTrue() - throws SQLException { - Object id = container.addItem(); - - assertTrue(container.containsId(id)); - } - - @Test - public void prevItemId_tableTwoNewlyAddedItems_returnsFirstAddedItem() - throws SQLException { - Object id1 = container.addItem(); - Object id2 = container.addItem(); - - assertEquals(id1, container.prevItemId(id2)); - } - - @Test - public void firstItemId_tableEmptyResultSet_returnsFirstAddedItem() - throws SQLException { - SQLContainer garbageContainer = getGarbageContainer(); - - Object id = garbageContainer.addItem(); - - Assert.assertSame(id, garbageContainer.firstItemId()); - } - - @Test - public void isFirstId_tableEmptyResultSet_returnsFirstAddedItem() - throws SQLException { - SQLContainer garbageContainer = getGarbageContainer(); - - Object id = garbageContainer.addItem(); - - assertTrue(garbageContainer.isFirstId(id)); - } - - @Test - public void isLastId_tableOneItemAdded_returnsTrueForAddedItem() - throws SQLException { - Object id = container.addItem(); - - assertTrue(container.isLastId(id)); - } - - @Test - public void isLastId_tableTwoItemsAdded_returnsTrueForLastAddedItem() - throws SQLException { - container.addItem(); - - Object id2 = container.addItem(); - - assertTrue(container.isLastId(id2)); - } - - @Test - public void getIdByIndex_tableOneItemAddedLastIndexInContainer_returnsAddedItem() - throws SQLException { - Object id = container.addItem(); - - assertEquals(id, container.getIdByIndex(container.size() - 1)); - } - - @Test - public void removeItem_tableNoAddedItems_removesItemFromContainer() - throws SQLException { - int originalSize = container.size(); - Object id = container.firstItemId(); - - assertTrue(container.removeItem(id)); - - Assert.assertNotSame(id, container.firstItemId()); - assertEquals(originalSize - 1, container.size()); - } - - @Test - public void containsId_tableRemovedItem_returnsFalse() throws SQLException { - Object id = container.firstItemId(); - assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - } - - @Test - public void removeItem_tableOneAddedItem_removesTheAddedItem() - throws SQLException { - Object id = container.addItem(); - int size = container.size(); - - assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - assertEquals(size - 1, container.size()); - } - - @Test - public void getItem_tableItemRemoved_returnsNull() throws SQLException { - Object id = container.firstItemId(); - - assertTrue(container.removeItem(id)); - Assert.assertNull(container.getItem(id)); - } - - @Test - public void getItem_tableAddedItemRemoved_returnsNull() - throws SQLException { - Object id = container.addItem(); - - Assert.assertNotNull(container.getItem(id)); - assertTrue(container.removeItem(id)); - Assert.assertNull(container.getItem(id)); - } - - @Test - public void getItemIds_tableItemRemoved_shouldNotContainRemovedItem() - throws SQLException { - Object id = container.firstItemId(); - - assertTrue(container.getItemIds().contains(id)); - assertTrue(container.removeItem(id)); - Assert.assertFalse(container.getItemIds().contains(id)); - } - - @Test - public void getItemIds_tableAddedItemRemoved_shouldNotContainRemovedItem() - throws SQLException { - Object id = container.addItem(); - - assertTrue(container.getItemIds().contains(id)); - assertTrue(container.removeItem(id)); - Assert.assertFalse(container.getItemIds().contains(id)); - } - - @Test - public void containsId_tableItemRemoved_returnsFalse() throws SQLException { - Object id = container.firstItemId(); - - assertTrue(container.containsId(id)); - assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - } - - @Test - public void containsId_tableAddedItemRemoved_returnsFalse() - throws SQLException { - Object id = container.addItem(); - - assertTrue(container.containsId(id)); - assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - } - - @Test - public void nextItemId_tableItemRemoved_skipsRemovedItem() - throws SQLException { - Object first = container.getIdByIndex(0); - Object second = container.getIdByIndex(1); - Object third = container.getIdByIndex(2); - - assertTrue(container.removeItem(second)); - assertEquals(third, container.nextItemId(first)); - } - - @Test - public void nextItemId_tableAddedItemRemoved_skipsRemovedItem() - throws SQLException { - Object first = container.lastItemId(); - Object second = container.addItem(); - Object third = container.addItem(); - - assertTrue(container.removeItem(second)); - assertEquals(third, container.nextItemId(first)); - } - - @Test - public void prevItemId_tableItemRemoved_skipsRemovedItem() - throws SQLException { - Object first = container.getIdByIndex(0); - Object second = container.getIdByIndex(1); - Object third = container.getIdByIndex(2); - - assertTrue(container.removeItem(second)); - assertEquals(first, container.prevItemId(third)); - } - - @Test - public void prevItemId_tableAddedItemRemoved_skipsRemovedItem() - throws SQLException { - Object first = container.lastItemId(); - Object second = container.addItem(); - Object third = container.addItem(); - - assertTrue(container.removeItem(second)); - assertEquals(first, container.prevItemId(third)); - } - - @Test - public void firstItemId_tableFirstItemRemoved_resultChanges() - throws SQLException { - Object first = container.firstItemId(); - - assertTrue(container.removeItem(first)); - Assert.assertNotSame(first, container.firstItemId()); - } - - @Test - public void firstItemId_tableNewlyAddedFirstItemRemoved_resultChanges() - throws SQLException { - SQLContainer garbageContainer = getGarbageContainer(); - - Object first = garbageContainer.addItem(); - Object second = garbageContainer.addItem(); - - Assert.assertSame(first, garbageContainer.firstItemId()); - assertTrue(garbageContainer.removeItem(first)); - Assert.assertSame(second, garbageContainer.firstItemId()); - } - - @Test - public void lastItemId_tableLastItemRemoved_resultChanges() - throws SQLException { - Object last = container.lastItemId(); - - assertTrue(container.removeItem(last)); - Assert.assertNotSame(last, container.lastItemId()); - } - - @Test - public void lastItemId_tableAddedLastItemRemoved_resultChanges() - throws SQLException { - Object last = container.addItem(); - - Assert.assertSame(last, container.lastItemId()); - assertTrue(container.removeItem(last)); - Assert.assertNotSame(last, container.lastItemId()); - } - - @Test - public void isFirstId_tableFirstItemRemoved_returnsFalse() - throws SQLException { - Object first = container.firstItemId(); - - assertTrue(container.removeItem(first)); - Assert.assertFalse(container.isFirstId(first)); - } - - @Test - public void isFirstId_tableAddedFirstItemRemoved_returnsFalse() - throws SQLException { - SQLContainer garbageContainer = getGarbageContainer(); - - Object first = garbageContainer.addItem(); - garbageContainer.addItem(); - - Assert.assertSame(first, garbageContainer.firstItemId()); - assertTrue(garbageContainer.removeItem(first)); - Assert.assertFalse(garbageContainer.isFirstId(first)); - } - - @Test - public void isLastId_tableLastItemRemoved_returnsFalse() - throws SQLException { - Object last = container.lastItemId(); - - assertTrue(container.removeItem(last)); - Assert.assertFalse(container.isLastId(last)); - } - - @Test - public void isLastId_tableAddedLastItemRemoved_returnsFalse() - throws SQLException { - Object last = container.addItem(); - - Assert.assertSame(last, container.lastItemId()); - assertTrue(container.removeItem(last)); - Assert.assertFalse(container.isLastId(last)); - } - - @Test - public void indexOfId_tableItemRemoved_returnsNegOne() throws SQLException { - Object id = container.getIdByIndex(2); - - assertTrue(container.removeItem(id)); - assertEquals(-1, container.indexOfId(id)); - } - - @Test - public void indexOfId_tableAddedItemRemoved_returnsNegOne() - throws SQLException { - Object id = container.addItem(); - - assertTrue(container.indexOfId(id) != -1); - assertTrue(container.removeItem(id)); - assertEquals(-1, container.indexOfId(id)); - } - - @Test - public void getIdByIndex_tableItemRemoved_resultChanges() - throws SQLException { - Object id = container.getIdByIndex(2); - - assertTrue(container.removeItem(id)); - Assert.assertNotSame(id, container.getIdByIndex(2)); - } - - @Test - public void getIdByIndex_tableAddedItemRemoved_resultChanges() - throws SQLException { - Object id = container.addItem(); - container.addItem(); - int index = container.indexOfId(id); - - assertTrue(container.removeItem(id)); - Assert.assertNotSame(id, container.getIdByIndex(index)); - } - - @Test - public void removeAllItems_table_shouldSucceed() throws SQLException { - assertTrue(container.removeAllItems()); - assertEquals(0, container.size()); - } - - @Test - public void removeAllItems_tableAddedItems_shouldSucceed() - throws SQLException { - container.addItem(); - container.addItem(); - - assertTrue(container.removeAllItems()); - assertEquals(0, container.size()); - } - - // Set timeout to ensure there is no infinite looping (#12882) - @Test(timeout = 1000) - public void removeAllItems_manyItems_commit_shouldSucceed() - throws SQLException { - final int itemNumber = (SQLContainer.CACHE_RATIO + 1) - * SQLContainer.DEFAULT_PAGE_LENGTH + 1; - - container.removeAllItems(); - - assertEquals(container.size(), 0); - for (int i = 0; i < itemNumber; ++i) { - container.addItem(); - } - container.commit(); - assertEquals(container.size(), itemNumber); - assertTrue(container.removeAllItems()); - container.commit(); - assertEquals(container.size(), 0); - } - - @Test - public void commit_tableAddedItem_shouldBeWrittenToDB() - throws SQLException { - Object id = container.addItem(); - container.getContainerProperty(id, NAME).setValue("New Name"); - - assertTrue(id instanceof TemporaryRowId); - Assert.assertSame(id, container.lastItemId()); - container.commit(); - Assert.assertFalse(container.lastItemId() instanceof TemporaryRowId); - assertEquals("New Name", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void commit_tableTwoAddedItems_shouldBeWrittenToDB() - throws SQLException { - Object id = container.addItem(); - Object id2 = container.addItem(); - container.getContainerProperty(id, NAME).setValue("Herbert"); - container.getContainerProperty(id2, NAME).setValue("Larry"); - assertTrue(id2 instanceof TemporaryRowId); - Assert.assertSame(id2, container.lastItemId()); - container.commit(); - Object nextToLast = container.getIdByIndex(container.size() - 2); - - Assert.assertFalse(nextToLast instanceof TemporaryRowId); - assertEquals("Herbert", - container.getContainerProperty(nextToLast, NAME).getValue()); - Assert.assertFalse(container.lastItemId() instanceof TemporaryRowId); - assertEquals("Larry", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void commit_tableRemovedItem_shouldBeRemovedFromDB() - throws SQLException { - Object last = container.lastItemId(); - container.removeItem(last); - container.commit(); - - Assert.assertFalse(last.equals(container.lastItemId())); - } - - @Test - public void commit_tableLastItemUpdated_shouldUpdateRowInDB() - throws SQLException { - Object last = container.lastItemId(); - container.getContainerProperty(last, NAME).setValue("Donald"); - container.commit(); - - assertEquals("Donald", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void commit_removeModifiedItem_shouldSucceed() throws SQLException { - int size = container.size(); - Object key = container.firstItemId(); - Item row = container.getItem(key); - row.getItemProperty(NAME).setValue("Pekka"); - - assertTrue(container.removeItem(key)); - container.commit(); - assertEquals(size - 1, container.size()); - } - - @Test - public void rollback_tableItemAdded_discardsAddedItem() - throws SQLException { - int size = container.size(); - Object id = container.addItem(); - container.getContainerProperty(id, NAME).setValue("foo"); - assertEquals(size + 1, container.size()); - container.rollback(); - assertEquals(size, container.size()); - Assert.assertFalse("foo".equals( - container.getContainerProperty(container.lastItemId(), NAME) - .getValue())); - } - - @Test - public void rollback_tableItemRemoved_restoresRemovedItem() - throws SQLException { - int size = container.size(); - Object last = container.lastItemId(); - container.removeItem(last); - assertEquals(size - 1, container.size()); - container.rollback(); - assertEquals(size, container.size()); - assertEquals(last, container.lastItemId()); - } - - @Test - public void rollback_tableItemChanged_discardsChanges() - throws SQLException { - Object last = container.lastItemId(); - container.getContainerProperty(last, NAME).setValue("foo"); - container.rollback(); - Assert.assertFalse("foo".equals( - container.getContainerProperty(container.lastItemId(), NAME) - .getValue())); - } - - @Test - public void itemChangeNotification_table_isModifiedReturnsTrue() - throws SQLException { - Assert.assertFalse(container.isModified()); - RowItem last = (RowItem) container.getItem(container.lastItemId()); - container.itemChangeNotification(last); - assertTrue(container.isModified()); - } - - @Test - public void itemSetChangeListeners_table_shouldFire() throws SQLException { - ItemSetChangeListener listener = EasyMock - .createMock(ItemSetChangeListener.class); - listener.containerItemSetChange(EasyMock.isA(ItemSetChangeEvent.class)); - EasyMock.replay(listener); - - container.addListener(listener); - container.addItem(); - - EasyMock.verify(listener); - } - - @Test - public void itemSetChangeListeners_tableItemRemoved_shouldFire() - throws SQLException { - ItemSetChangeListener listener = EasyMock - .createMock(ItemSetChangeListener.class); - listener.containerItemSetChange(EasyMock.isA(ItemSetChangeEvent.class)); - EasyMock.expectLastCall().anyTimes(); - EasyMock.replay(listener); - - container.addListener(listener); - container.removeItem(container.lastItemId()); - - EasyMock.verify(listener); - } - - @Test - public void removeListener_table_shouldNotFire() throws SQLException { - ItemSetChangeListener listener = EasyMock - .createMock(ItemSetChangeListener.class); - EasyMock.replay(listener); - - container.addListener(listener); - container.removeListener(listener); - container.addItem(); - - EasyMock.verify(listener); - } - - @Test - public void isModified_tableRemovedItem_returnsTrue() throws SQLException { - Assert.assertFalse(container.isModified()); - container.removeItem(container.lastItemId()); - assertTrue(container.isModified()); - } - - @Test - public void isModified_tableAddedItem_returnsTrue() throws SQLException { - Assert.assertFalse(container.isModified()); - container.addItem(); - assertTrue(container.isModified()); - } - - @Test - public void isModified_tableChangedItem_returnsTrue() throws SQLException { - Assert.assertFalse(container.isModified()); - container.getContainerProperty(container.lastItemId(), NAME) - .setValue("foo"); - assertTrue(container.isModified()); - } - - @Test - public void getSortableContainerPropertyIds_table_returnsAllPropertyIds() - throws SQLException { - Collection sortableIds = container.getSortableContainerPropertyIds(); - assertTrue(sortableIds.contains(ID)); - assertTrue(sortableIds.contains(NAME)); - assertTrue(sortableIds.contains("AGE")); - assertEquals(3, sortableIds.size()); - if (SQLTestsConstants.db == DB.MSSQL - || SQLTestsConstants.db == DB.ORACLE) { - Assert.assertFalse(sortableIds.contains("rownum")); - } - } - - @Test - public void addOrderBy_table_shouldReorderResults() throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals("Ville", - container.getContainerProperty(container.firstItemId(), NAME) - .getValue()); - assertEquals("Börje", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - - container.addOrderBy(new OrderBy(NAME, true)); - // Börje, Kalle, Pelle, Ville - assertEquals("Börje", - container.getContainerProperty(container.firstItemId(), NAME) - .getValue()); - assertEquals("Ville", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test(expected = IllegalArgumentException.class) - public void addOrderBy_tableIllegalColumn_shouldFail() throws SQLException { - container.addOrderBy(new OrderBy("asdf", true)); - } - - @Test - public void sort_table_sortsByName() throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals("Ville", - container.getContainerProperty(container.firstItemId(), NAME) - .getValue()); - assertEquals("Börje", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - - container.sort(new Object[] { NAME }, new boolean[] { true }); - - // Börje, Kalle, Pelle, Ville - assertEquals("Börje", - container.getContainerProperty(container.firstItemId(), NAME) - .getValue()); - assertEquals("Ville", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void addFilter_table_filtersResults() throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals(4, container.size()); - assertEquals("Börje", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - - container.addContainerFilter(new Like(NAME, "%lle")); - // Ville, Kalle, Pelle - assertEquals(3, container.size()); - assertEquals("Pelle", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void addContainerFilter_filtersResults() throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals(4, container.size()); - - container.addContainerFilter(NAME, "Vi", false, false); - - // Ville - assertEquals(1, container.size()); - assertEquals("Ville", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void addContainerFilter_ignoreCase_filtersResults() - throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals(4, container.size()); - - container.addContainerFilter(NAME, "vi", true, false); - - // Ville - assertEquals(1, container.size()); - assertEquals("Ville", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void removeAllContainerFilters_table_noFiltering() - throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals(4, container.size()); - - container.addContainerFilter(NAME, "Vi", false, false); - - // Ville - assertEquals(1, container.size()); - assertEquals("Ville", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - - container.removeAllContainerFilters(); - - assertEquals(4, container.size()); - assertEquals("Börje", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void removeContainerFilters_table_noFiltering() throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals(4, container.size()); - - container.addContainerFilter(NAME, "Vi", false, false); - - // Ville - assertEquals(1, container.size()); - assertEquals("Ville", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - - container.removeContainerFilters(NAME); - - assertEquals(4, container.size()); - assertEquals("Börje", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - - @Test - public void addFilter_tableBufferedItems_alsoFiltersBufferedItems() - throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals(4, container.size()); - assertEquals("Börje", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - - Object id1 = container.addItem(); - container.getContainerProperty(id1, NAME).setValue("Palle"); - Object id2 = container.addItem(); - container.getContainerProperty(id2, NAME).setValue("Bengt"); - - container.addContainerFilter(new Like(NAME, "%lle")); - - // Ville, Kalle, Pelle, Palle - assertEquals(4, container.size()); - assertEquals("Ville", - container.getContainerProperty(container.getIdByIndex(0), NAME) - .getValue()); - assertEquals("Kalle", - container.getContainerProperty(container.getIdByIndex(1), NAME) - .getValue()); - assertEquals("Pelle", - container.getContainerProperty(container.getIdByIndex(2), NAME) - .getValue()); - assertEquals("Palle", - container.getContainerProperty(container.getIdByIndex(3), NAME) - .getValue()); - - try { - container.getIdByIndex(4); - Assert.fail( - "SQLContainer.getIdByIndex() returned a value for an index beyond the end of the container"); - } catch (IndexOutOfBoundsException e) { - // should throw exception - item is filtered out - } - Assert.assertNull(container.nextItemId(container.getIdByIndex(3))); - - Assert.assertFalse(container.containsId(id2)); - Assert.assertFalse(container.getItemIds().contains(id2)); - - Assert.assertNull(container.getItem(id2)); - assertEquals(-1, container.indexOfId(id2)); - - Assert.assertNotSame(id2, container.lastItemId()); - Assert.assertSame(id1, container.lastItemId()); - } - - @Test - public void sort_tableBufferedItems_sortsBufferedItemsLastInOrderAdded() - throws SQLException { - // Ville, Kalle, Pelle, Börje - assertEquals("Ville", - container.getContainerProperty(container.firstItemId(), NAME) - .getValue()); - assertEquals("Börje", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - - Object id1 = container.addItem(); - container.getContainerProperty(id1, NAME).setValue("Wilbert"); - Object id2 = container.addItem(); - container.getContainerProperty(id2, NAME).setValue("Albert"); - - container.sort(new Object[] { NAME }, new boolean[] { true }); - - // Börje, Kalle, Pelle, Ville, Wilbert, Albert - assertEquals("Börje", - container.getContainerProperty(container.firstItemId(), NAME) - .getValue()); - assertEquals("Wilbert", - container.getContainerProperty( - container.getIdByIndex(container.size() - 2), NAME) - .getValue()); - assertEquals("Albert", container - .getContainerProperty(container.lastItemId(), NAME).getValue()); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTest.java deleted file mode 100644 index d499ceed92..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLContainerTest.java +++ /dev/null @@ -1,2469 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import java.math.BigDecimal; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.logging.Handler; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -import org.easymock.EasyMock; -import org.easymock.IAnswer; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Item; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants.DB; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.FreeformQuery; -import com.vaadin.data.util.sqlcontainer.query.FreeformQueryDelegate; -import com.vaadin.data.util.sqlcontainer.query.FreeformStatementDelegate; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; -import com.vaadin.data.util.sqlcontainer.query.ValidatingSimpleJDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; - -public class SQLContainerTest { - private static final int offset = SQLTestsConstants.offset; - private JDBCConnectionPool connectionPool; - - @Before - public void setUp() throws SQLException { - - try { - connectionPool = new ValidatingSimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 2, 2); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - - DataGenerator.addPeopleToDatabase(connectionPool); - } - - @After - public void tearDown() { - if (connectionPool != null) { - connectionPool.destroy(); - } - } - - @Test - public void constructor_withFreeformQuery_shouldSucceed() - throws SQLException { - new SQLContainer(new FreeformQuery("SELECT * FROM people", - connectionPool, "ID")); - } - - @Test(expected = SQLException.class) - public void constructor_withIllegalFreeformQuery_shouldFail() - throws SQLException { - SQLContainer c = new SQLContainer( - new FreeformQuery("SELECT * FROM asdf", connectionPool, "ID")); - c.getItem(c.firstItemId()); - } - - @Test - public void containsId_withFreeformQueryAndExistingId_returnsTrue() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertTrue(container.containsId(new RowId(new Object[] { 1 }))); - } - - @Test - public void containsId_withFreeformQueryAndNonexistingId_returnsFalse() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertFalse( - container.containsId(new RowId(new Object[] { 1337 }))); - } - - @Test - public void getContainerProperty_freeformExistingItemIdAndPropertyId_returnsProperty() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals("Ville", - container.getContainerProperty(new RowId( - new Object[] { new BigDecimal(0 + offset) }), - "NAME").getValue()); - } else { - Assert.assertEquals("Ville", - container.getContainerProperty( - new RowId(new Object[] { 0 + offset }), "NAME") - .getValue()); - } - } - - @Test - public void getContainerProperty_freeformExistingItemIdAndNonexistingPropertyId_returnsNull() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertNull(container.getContainerProperty( - new RowId(new Object[] { 1 + offset }), "asdf")); - } - - @Test - public void getContainerProperty_freeformNonexistingItemId_returnsNull() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertNull(container.getContainerProperty( - new RowId(new Object[] { 1337 + offset }), "NAME")); - } - - @Test - public void getContainerPropertyIds_freeform_returnsIDAndNAME() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Collection propertyIds = container.getContainerPropertyIds(); - Assert.assertEquals(3, propertyIds.size()); - Assert.assertArrayEquals(new String[] { "ID", "NAME", "AGE" }, - propertyIds.toArray()); - } - - @Test - public void getItem_freeformExistingItemId_returnsItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Item item; - if (SQLTestsConstants.db == DB.ORACLE) { - item = container.getItem( - new RowId(new Object[] { new BigDecimal(0 + offset) })); - } else { - item = container.getItem(new RowId(new Object[] { 0 + offset })); - } - Assert.assertNotNull(item); - Assert.assertEquals("Ville", item.getItemProperty("NAME").getValue()); - } - - @Test - public void nextItemNullAtEnd_freeformExistingItem() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object lastItemId = container.lastItemId(); - Object afterLast = container.nextItemId(lastItemId); - Assert.assertNull(afterLast); - } - - @Test - public void prevItemNullAtStart_freeformExistingItem() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object firstItemId = container.firstItemId(); - Object beforeFirst = container.prevItemId(firstItemId); - Assert.assertNull(beforeFirst); - } - - @Test - public void getItem_freeform5000RowsWithParameter1337_returnsItemWithId1337() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Item item; - if (SQLTestsConstants.db == DB.ORACLE) { - item = container.getItem( - new RowId(new Object[] { new BigDecimal(1337 + offset) })); - Assert.assertNotNull(item); - Assert.assertEquals(new BigDecimal(1337 + offset), - item.getItemProperty("ID").getValue()); - } else { - item = container.getItem(new RowId(new Object[] { 1337 + offset })); - Assert.assertNotNull(item); - Assert.assertEquals(1337 + offset, - item.getItemProperty("ID").getValue()); - } - Assert.assertEquals("Person 1337", - item.getItemProperty("NAME").getValue()); - } - - @Test - public void getItemIds_freeform_returnsItemIdsWithKeys0through3() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Collection itemIds = container.getItemIds(); - Assert.assertEquals(4, itemIds.size()); - RowId zero = new RowId(new Object[] { 0 + offset }); - RowId one = new RowId(new Object[] { 1 + offset }); - RowId two = new RowId(new Object[] { 2 + offset }); - RowId three = new RowId(new Object[] { 3 + offset }); - if (SQLTestsConstants.db == DB.ORACLE) { - String[] correct = new String[] { "1", "2", "3", "4" }; - List oracle = new ArrayList(); - for (Object o : itemIds) { - oracle.add(o.toString()); - } - Assert.assertArrayEquals(correct, oracle.toArray()); - } else { - Assert.assertArrayEquals(new Object[] { zero, one, two, three }, - itemIds.toArray()); - } - } - - @Test - public void getType_freeformNAMEPropertyId_returnsString() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertEquals(String.class, container.getType("NAME")); - } - - @Test - public void getType_freeformIDPropertyId_returnsInteger() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals(BigDecimal.class, container.getType("ID")); - } else { - Assert.assertEquals(Integer.class, container.getType("ID")); - } - } - - @Test - public void getType_freeformNonexistingPropertyId_returnsNull() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertNull(container.getType("asdf")); - } - - @Test - public void size_freeform_returnsFour() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertEquals(4, container.size()); - } - - @Test - public void size_freeformOneAddedItem_returnsFive() throws SQLException { - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - if (SQLTestsConstants.db == DB.MSSQL) { - statement.executeUpdate("insert into people values('Bengt', '42')"); - } else { - statement.executeUpdate( - "insert into people values(default, 'Bengt', '42')"); - } - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertEquals(5, container.size()); - } - - @Test - public void indexOfId_freeformWithParameterThree_returnsThree() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals(3, container.indexOfId( - new RowId(new Object[] { new BigDecimal(3 + offset) }))); - } else { - Assert.assertEquals(3, container - .indexOfId(new RowId(new Object[] { 3 + offset }))); - } - } - - @Test - public void indexOfId_freeform5000RowsWithParameter1337_returns1337() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - SQLContainer container = new SQLContainer( - new FreeformQuery("SELECT * FROM people ORDER BY \"ID\" ASC", - connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - container.getItem( - new RowId(new Object[] { new BigDecimal(1337 + offset) })); - Assert.assertEquals(1337, container.indexOfId( - new RowId(new Object[] { new BigDecimal(1337 + offset) }))); - } else { - container.getItem(new RowId(new Object[] { 1337 + offset })); - Assert.assertEquals(1337, container - .indexOfId(new RowId(new Object[] { 1337 + offset }))); - } - } - - @Test - public void getIdByIndex_freeform5000rowsIndex1337_returnsRowId1337() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - SQLContainer container = new SQLContainer( - new FreeformQuery("SELECT * FROM people ORDER BY \"ID\" ASC", - connectionPool, "ID")); - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals( - new RowId(new Object[] { new BigDecimal(1337 + offset) }), - itemId); - } else { - Assert.assertEquals(new RowId(new Object[] { 1337 + offset }), - itemId); - } - } - - @SuppressWarnings("unchecked") - @Test - public void getIdByIndex_freeformWithPaging5000rowsIndex1337_returnsRowId1337() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT row_number() OVER" - + " ( ORDER BY \"ID\" ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN " + start - + " AND " + end; - return q; - } else if (SQLTestsConstants.db == DB.ORACLE) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people ORDER BY \"ID\" ASC) x) " - + " WHERE r BETWEEN " + start + " AND " - + end; - return q; - } else { - return "SELECT * FROM people LIMIT " + limit - + " OFFSET " + offset; - } - } - }).anyTimes(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals( - new RowId(new Object[] { 1337 + offset }).toString(), - itemId.toString()); - } else { - Assert.assertEquals(new RowId(new Object[] { 1337 + offset }), - itemId); - } - } - - @Test - public void nextItemId_freeformCurrentItem1337_returnsItem1338() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - SQLContainer container = new SQLContainer( - new FreeformQuery("SELECT * FROM people ORDER BY \"ID\" ASC", - connectionPool, "ID")); - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals( - new RowId(new Object[] { 1338 + offset }).toString(), - container.nextItemId(itemId).toString()); - } else { - Assert.assertEquals(new RowId(new Object[] { 1338 + offset }), - container.nextItemId(itemId)); - } - } - - @Test - public void prevItemId_freeformCurrentItem1337_returns1336() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - SQLContainer container = new SQLContainer( - new FreeformQuery("SELECT * FROM people ORDER BY \"ID\" ASC", - connectionPool, "ID")); - Object itemId = container.getIdByIndex(1337); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals( - new RowId(new Object[] { 1336 + offset }).toString(), - container.prevItemId(itemId).toString()); - } else { - Assert.assertEquals(new RowId(new Object[] { 1336 + offset }), - container.prevItemId(itemId)); - } - } - - @Test - public void firstItemId_freeform_returnsItemId0() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals( - new RowId(new Object[] { 0 + offset }).toString(), - container.firstItemId().toString()); - } else { - Assert.assertEquals(new RowId(new Object[] { 0 + offset }), - container.firstItemId()); - } - } - - @Test - public void lastItemId_freeform5000Rows_returnsItemId4999() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - SQLContainer container = new SQLContainer( - new FreeformQuery("SELECT * FROM people ORDER BY \"ID\" ASC", - connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals( - new RowId(new Object[] { 4999 + offset }).toString(), - container.lastItemId().toString()); - } else { - Assert.assertEquals(new RowId(new Object[] { 4999 + offset }), - container.lastItemId()); - } - } - - @Test - public void isFirstId_freeformActualFirstId_returnsTrue() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertTrue(container.isFirstId( - new RowId(new Object[] { new BigDecimal(0 + offset) }))); - } else { - Assert.assertTrue(container - .isFirstId(new RowId(new Object[] { 0 + offset }))); - } - } - - @Test - public void isFirstId_freeformSecondId_returnsFalse() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertFalse(container.isFirstId( - new RowId(new Object[] { new BigDecimal(1 + offset) }))); - } else { - Assert.assertFalse(container - .isFirstId(new RowId(new Object[] { 1 + offset }))); - } - } - - @Test - public void isLastId_freeformSecondId_returnsFalse() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertFalse(container.isLastId( - new RowId(new Object[] { new BigDecimal(1 + offset) }))); - } else { - Assert.assertFalse( - container.isLastId(new RowId(new Object[] { 1 + offset }))); - } - } - - @Test - public void isLastId_freeformLastId_returnsTrue() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertTrue(container.isLastId( - new RowId(new Object[] { new BigDecimal(3 + offset) }))); - } else { - Assert.assertTrue( - container.isLastId(new RowId(new Object[] { 3 + offset }))); - } - } - - @Test - public void isLastId_freeform5000RowsLastId_returnsTrue() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - SQLContainer container = new SQLContainer( - new FreeformQuery("SELECT * FROM people ORDER BY \"ID\" ASC", - connectionPool, "ID")); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertTrue(container.isLastId( - new RowId(new Object[] { new BigDecimal(4999 + offset) }))); - } else { - Assert.assertTrue(container - .isLastId(new RowId(new Object[] { 4999 + offset }))); - } - } - - @Test - public void refresh_freeform_sizeShouldUpdate() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertEquals(4, container.size()); - DataGenerator.addFiveThousandPeople(connectionPool); - container.refresh(); - Assert.assertEquals(5000, container.size()); - } - - @Test - public void refresh_freeformWithoutCallingRefresh_sizeShouldNotUpdate() - throws SQLException { - // Yeah, this is a weird one. We're testing that the size doesn't update - // after adding lots of items unless we call refresh inbetween. This to - // make sure that the refresh method actually refreshes stuff and isn't - // a NOP. - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertEquals(4, container.size()); - DataGenerator.addFiveThousandPeople(connectionPool); - Assert.assertEquals(4, container.size()); - } - - @Test - public void setAutoCommit_freeform_shouldSucceed() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.setAutoCommit(true); - Assert.assertTrue(container.isAutoCommit()); - container.setAutoCommit(false); - Assert.assertFalse(container.isAutoCommit()); - } - - @Test - public void getPageLength_freeform_returnsDefault100() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertEquals(100, container.getPageLength()); - } - - @Test - public void setPageLength_freeform_shouldSucceed() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.setPageLength(20); - Assert.assertEquals(20, container.getPageLength()); - container.setPageLength(200); - Assert.assertEquals(200, container.getPageLength()); - } - - @Test(expected = UnsupportedOperationException.class) - public void addContainerProperty_normal_isUnsupported() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addContainerProperty("asdf", String.class, ""); - } - - @Test(expected = UnsupportedOperationException.class) - public void removeContainerProperty_normal_isUnsupported() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.removeContainerProperty("asdf"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemObject_normal_isUnsupported() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addItem("asdf"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAfterObjectObject_normal_isUnsupported() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addItemAfter("asdf", "foo"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAtIntObject_normal_isUnsupported() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addItemAt(2, "asdf"); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAtInt_normal_isUnsupported() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addItemAt(2); - } - - @Test(expected = UnsupportedOperationException.class) - public void addItemAfterObject_normal_isUnsupported() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addItemAfter("asdf"); - } - - @Test - public void addItem_freeformAddOneNewItem_returnsItemId() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object itemId = container.addItem(); - Assert.assertNotNull(itemId); - } - - @Test - public void addItem_freeformAddOneNewItem_shouldChangeSize() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - int size = container.size(); - container.addItem(); - Assert.assertEquals(size + 1, container.size()); - } - - @Test - public void addItem_freeformAddTwoNewItems_shouldChangeSize() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - int size = container.size(); - Object id1 = container.addItem(); - Object id2 = container.addItem(); - Assert.assertEquals(size + 2, container.size()); - Assert.assertNotSame(id1, id2); - Assert.assertFalse(id1.equals(id2)); - } - - @Test - public void nextItemId_freeformNewlyAddedItem_returnsNewlyAdded() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object lastId = container.lastItemId(); - Object id = container.addItem(); - Assert.assertEquals(id, container.nextItemId(lastId)); - } - - @Test - public void lastItemId_freeformNewlyAddedItem_returnsNewlyAdded() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object lastId = container.lastItemId(); - Object id = container.addItem(); - Assert.assertEquals(id, container.lastItemId()); - Assert.assertNotSame(lastId, container.lastItemId()); - } - - @Test - public void indexOfId_freeformNewlyAddedItem_returnsFour() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertEquals(4, container.indexOfId(id)); - } - - @Test - public void getItem_freeformNewlyAddedItem_returnsNewlyAdded() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertNotNull(container.getItem(id)); - } - - @Test - public void getItem_freeformNewlyAddedItemAndFiltered_returnsNull() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addContainerFilter(new Equal("NAME", "asdf")); - Object id = container.addItem(); - Assert.assertNull(container.getItem(id)); - } - - @Test - public void getItemUnfiltered_freeformNewlyAddedItemAndFiltered_returnsNewlyAdded() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addContainerFilter(new Equal("NAME", "asdf")); - Object id = container.addItem(); - Assert.assertNotNull(container.getItemUnfiltered(id)); - } - - @Test - public void getItemIds_freeformNewlyAddedItem_containsNewlyAdded() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertTrue(container.getItemIds().contains(id)); - } - - @Test - public void getContainerProperty_freeformNewlyAddedItem_returnsPropertyOfNewlyAddedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Item item = container.getItem(id); - item.getItemProperty("NAME").setValue("asdf"); - Assert.assertEquals("asdf", - container.getContainerProperty(id, "NAME").getValue()); - } - - @Test - public void containsId_freeformNewlyAddedItem_returnsTrue() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertTrue(container.containsId(id)); - } - - @Test - public void prevItemId_freeformTwoNewlyAddedItems_returnsFirstAddedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id1 = container.addItem(); - Object id2 = container.addItem(); - Assert.assertEquals(id1, container.prevItemId(id2)); - } - - @Test - public void firstItemId_freeformEmptyResultSet_returnsFirstAddedItem() - throws SQLException { - DataGenerator.createGarbage(connectionPool); - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM GARBAGE", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertSame(id, container.firstItemId()); - } - - @Test - public void isFirstId_freeformEmptyResultSet_returnsFirstAddedItem() - throws SQLException { - DataGenerator.createGarbage(connectionPool); - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM GARBAGE", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertTrue(container.isFirstId(id)); - } - - @Test - public void isLastId_freeformOneItemAdded_returnsTrueForAddedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertTrue(container.isLastId(id)); - } - - @Test - public void isLastId_freeformTwoItemsAdded_returnsTrueForLastAddedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addItem(); - Object id2 = container.addItem(); - Assert.assertTrue(container.isLastId(id2)); - } - - @Test - public void getIdByIndex_freeformOneItemAddedLastIndexInContainer_returnsAddedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertEquals(id, container.getIdByIndex(container.size() - 1)); - } - - @Test - public void removeItem_freeformNoAddedItems_removesItemFromContainer() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - int size = container.size(); - Object id = container.firstItemId(); - Assert.assertTrue(container.removeItem(id)); - Assert.assertNotSame(id, container.firstItemId()); - Assert.assertEquals(size - 1, container.size()); - } - - @Test - public void containsId_freeformRemovedItem_returnsFalse() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.firstItemId(); - Assert.assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - } - - @Test - public void containsId_unknownObject() throws SQLException { - - Handler ensureNoLogging = new Handler() { - - @Override - public void publish(LogRecord record) { - Assert.fail("No messages should be logged"); - - } - - @Override - public void flush() { - } - - @Override - public void close() throws SecurityException { - } - }; - - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Logger logger = Logger.getLogger(SQLContainer.class.getName()); - - logger.addHandler(ensureNoLogging); - try { - Assert.assertFalse(container.containsId(new Object())); - } finally { - logger.removeHandler(ensureNoLogging); - } - } - - @Test - public void removeItem_freeformOneAddedItem_removesTheAddedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - int size = container.size(); - Assert.assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - Assert.assertEquals(size - 1, container.size()); - } - - @Test - public void getItem_freeformItemRemoved_returnsNull() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.firstItemId(); - Assert.assertTrue(container.removeItem(id)); - Assert.assertNull(container.getItem(id)); - } - - @Test - public void getItem_freeformAddedItemRemoved_returnsNull() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertNotNull(container.getItem(id)); - Assert.assertTrue(container.removeItem(id)); - Assert.assertNull(container.getItem(id)); - } - - @Test - public void getItemIds_freeformItemRemoved_shouldNotContainRemovedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.firstItemId(); - Assert.assertTrue(container.getItemIds().contains(id)); - Assert.assertTrue(container.removeItem(id)); - Assert.assertFalse(container.getItemIds().contains(id)); - } - - @Test - public void getItemIds_freeformAddedItemRemoved_shouldNotContainRemovedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertTrue(container.getItemIds().contains(id)); - Assert.assertTrue(container.removeItem(id)); - Assert.assertFalse(container.getItemIds().contains(id)); - } - - @Test - public void containsId_freeformItemRemoved_returnsFalse() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.firstItemId(); - Assert.assertTrue(container.containsId(id)); - Assert.assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - } - - @Test - public void containsId_freeformAddedItemRemoved_returnsFalse() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertTrue(container.containsId(id)); - Assert.assertTrue(container.removeItem(id)); - Assert.assertFalse(container.containsId(id)); - } - - @Test - public void nextItemId_freeformItemRemoved_skipsRemovedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object first = container.getIdByIndex(0); - Object second = container.getIdByIndex(1); - Object third = container.getIdByIndex(2); - Assert.assertTrue(container.removeItem(second)); - Assert.assertEquals(third, container.nextItemId(first)); - } - - @Test - public void nextItemId_freeformAddedItemRemoved_skipsRemovedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object first = container.lastItemId(); - Object second = container.addItem(); - Object third = container.addItem(); - Assert.assertTrue(container.removeItem(second)); - Assert.assertEquals(third, container.nextItemId(first)); - } - - @Test - public void prevItemId_freeformItemRemoved_skipsRemovedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object first = container.getIdByIndex(0); - Object second = container.getIdByIndex(1); - Object third = container.getIdByIndex(2); - Assert.assertTrue(container.removeItem(second)); - Assert.assertEquals(first, container.prevItemId(third)); - } - - @Test - public void prevItemId_freeformAddedItemRemoved_skipsRemovedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object first = container.lastItemId(); - Object second = container.addItem(); - Object third = container.addItem(); - Assert.assertTrue(container.removeItem(second)); - Assert.assertEquals(first, container.prevItemId(third)); - } - - @Test - public void firstItemId_freeformFirstItemRemoved_resultChanges() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object first = container.firstItemId(); - Assert.assertTrue(container.removeItem(first)); - Assert.assertNotSame(first, container.firstItemId()); - } - - @Test - public void firstItemId_freeformNewlyAddedFirstItemRemoved_resultChanges() - throws SQLException { - DataGenerator.createGarbage(connectionPool); - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM GARBAGE", connectionPool, "ID")); - Object first = container.addItem(); - Object second = container.addItem(); - Assert.assertSame(first, container.firstItemId()); - Assert.assertTrue(container.removeItem(first)); - Assert.assertSame(second, container.firstItemId()); - } - - @Test - public void lastItemId_freeformLastItemRemoved_resultChanges() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object last = container.lastItemId(); - Assert.assertTrue(container.removeItem(last)); - Assert.assertNotSame(last, container.lastItemId()); - } - - @Test - public void lastItemId_freeformAddedLastItemRemoved_resultChanges() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object last = container.addItem(); - Assert.assertSame(last, container.lastItemId()); - Assert.assertTrue(container.removeItem(last)); - Assert.assertNotSame(last, container.lastItemId()); - } - - @Test - public void isFirstId_freeformFirstItemRemoved_returnsFalse() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object first = container.firstItemId(); - Assert.assertTrue(container.removeItem(first)); - Assert.assertFalse(container.isFirstId(first)); - } - - @Test - public void isFirstId_freeformAddedFirstItemRemoved_returnsFalse() - throws SQLException { - DataGenerator.createGarbage(connectionPool); - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM GARBAGE", connectionPool, "ID")); - Object first = container.addItem(); - container.addItem(); - Assert.assertSame(first, container.firstItemId()); - Assert.assertTrue(container.removeItem(first)); - Assert.assertFalse(container.isFirstId(first)); - } - - @Test - public void isLastId_freeformLastItemRemoved_returnsFalse() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object last = container.lastItemId(); - Assert.assertTrue(container.removeItem(last)); - Assert.assertFalse(container.isLastId(last)); - } - - @Test - public void isLastId_freeformAddedLastItemRemoved_returnsFalse() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object last = container.addItem(); - Assert.assertSame(last, container.lastItemId()); - Assert.assertTrue(container.removeItem(last)); - Assert.assertFalse(container.isLastId(last)); - } - - @Test - public void indexOfId_freeformItemRemoved_returnsNegOne() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.getIdByIndex(2); - Assert.assertTrue(container.removeItem(id)); - Assert.assertEquals(-1, container.indexOfId(id)); - } - - @Test - public void indexOfId_freeformAddedItemRemoved_returnsNegOne() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - Assert.assertTrue(container.indexOfId(id) != -1); - Assert.assertTrue(container.removeItem(id)); - Assert.assertEquals(-1, container.indexOfId(id)); - } - - @Test - public void getIdByIndex_freeformItemRemoved_resultChanges() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.getIdByIndex(2); - Assert.assertTrue(container.removeItem(id)); - Assert.assertNotSame(id, container.getIdByIndex(2)); - } - - @Test - public void getIdByIndex_freeformAddedItemRemoved_resultChanges() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object id = container.addItem(); - container.addItem(); - int index = container.indexOfId(id); - Assert.assertTrue(container.removeItem(id)); - Assert.assertNotSame(id, container.getIdByIndex(index)); - } - - @Test - public void removeAllItems_freeform_shouldSucceed() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertTrue(container.removeAllItems()); - Assert.assertEquals(0, container.size()); - } - - @Test - public void removeAllItems_freeformAddedItems_shouldSucceed() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addItem(); - container.addItem(); - Assert.assertTrue(container.removeAllItems()); - Assert.assertEquals(0, container.size()); - } - - @SuppressWarnings("unchecked") - @Test - public void commit_freeformAddedItem_shouldBeWrittenToDB() - throws SQLException { - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.storeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))).andAnswer(new IAnswer() { - @Override - public Integer answer() throws Throwable { - Connection conn = (Connection) EasyMock - .getCurrentArguments()[0]; - RowItem item = (RowItem) EasyMock - .getCurrentArguments()[1]; - Statement statement = conn.createStatement(); - if (SQLTestsConstants.db == DB.MSSQL) { - statement - .executeUpdate("insert into people values('" - + item.getItemProperty("NAME") - .getValue() - + "', '" - + item.getItemProperty("AGE") - .getValue() - + "')"); - } else { - statement.executeUpdate( - "insert into people values(default, '" - + item.getItemProperty("NAME") - .getValue() - + "', '" - + item.getItemProperty("AGE") - .getValue() - + "')"); - } - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - return 1; - } - }).anyTimes(); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT row_number() OVER" - + " ( ORDER BY \"ID\" ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN " + start - + " AND " + end; - return q; - } else if (SQLTestsConstants.db == DB.ORACLE) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people ORDER BY \"ID\" ASC) x) " - + " WHERE r BETWEEN " + start + " AND " - + end; - return q; - } else { - return "SELECT * FROM people LIMIT " + limit - + " OFFSET " + offset; - } - } - }).anyTimes(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - query.setDelegate(delegate); - EasyMock.replay(delegate); - SQLContainer container = new SQLContainer(query); - Object id = container.addItem(); - container.getContainerProperty(id, "NAME").setValue("New Name"); - container.getContainerProperty(id, "AGE").setValue(30); - Assert.assertTrue(id instanceof TemporaryRowId); - Assert.assertSame(id, container.lastItemId()); - container.commit(); - Assert.assertFalse(container.lastItemId() instanceof TemporaryRowId); - Assert.assertEquals("New Name", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void commit_freeformTwoAddedItems_shouldBeWrittenToDB() - throws SQLException { - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.storeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))).andAnswer(new IAnswer() { - @Override - public Integer answer() throws Throwable { - Connection conn = (Connection) EasyMock - .getCurrentArguments()[0]; - RowItem item = (RowItem) EasyMock - .getCurrentArguments()[1]; - Statement statement = conn.createStatement(); - if (SQLTestsConstants.db == DB.MSSQL) { - statement - .executeUpdate("insert into people values('" - + item.getItemProperty("NAME") - .getValue() - + "', '" - + item.getItemProperty("AGE") - .getValue() - + "')"); - } else { - statement.executeUpdate( - "insert into people values(default, '" - + item.getItemProperty("NAME") - .getValue() - + "', '" - + item.getItemProperty("AGE") - .getValue() - + "')"); - } - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - return 1; - } - }).anyTimes(); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT row_number() OVER" - + " ( ORDER BY \"ID\" ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN " + start - + " AND " + end; - return q; - } else if (SQLTestsConstants.db == DB.ORACLE) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people ORDER BY \"ID\" ASC) x) " - + " WHERE r BETWEEN " + start + " AND " - + end; - return q; - } else { - return "SELECT * FROM people LIMIT " + limit - + " OFFSET " + offset; - } - } - }).anyTimes(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - query.setDelegate(delegate); - EasyMock.replay(delegate); - SQLContainer container = new SQLContainer(query); - Object id = container.addItem(); - Object id2 = container.addItem(); - container.getContainerProperty(id, "NAME").setValue("Herbert"); - container.getContainerProperty(id, "AGE").setValue(30); - container.getContainerProperty(id2, "NAME").setValue("Larry"); - container.getContainerProperty(id2, "AGE").setValue(50); - Assert.assertTrue(id2 instanceof TemporaryRowId); - Assert.assertSame(id2, container.lastItemId()); - container.commit(); - Object nextToLast = container.getIdByIndex(container.size() - 2); - Assert.assertFalse(nextToLast instanceof TemporaryRowId); - Assert.assertEquals("Herbert", - container.getContainerProperty(nextToLast, "NAME").getValue()); - Assert.assertFalse(container.lastItemId() instanceof TemporaryRowId); - Assert.assertEquals("Larry", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void commit_freeformRemovedItem_shouldBeRemovedFromDB() - throws SQLException { - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.removeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))).andAnswer(new IAnswer() { - @Override - public Boolean answer() throws Throwable { - Connection conn = (Connection) EasyMock - .getCurrentArguments()[0]; - RowItem item = (RowItem) EasyMock - .getCurrentArguments()[1]; - Statement statement = conn.createStatement(); - statement.executeUpdate( - "DELETE FROM people WHERE \"ID\"=" + item - .getItemProperty("ID").getValue()); - statement.close(); - return true; - } - }).anyTimes(); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT row_number() OVER" - + " ( ORDER BY \"ID\" ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN " + start - + " AND " + end; - return q; - } else if (SQLTestsConstants.db == DB.ORACLE) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people ORDER BY \"ID\" ASC) x) " - + " WHERE r BETWEEN " + start + " AND " - + end; - return q; - } else { - return "SELECT * FROM people LIMIT " + limit - + " OFFSET " + offset; - } - } - }).anyTimes(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - query.setDelegate(delegate); - EasyMock.replay(delegate); - SQLContainer container = new SQLContainer(query); - Object last = container.lastItemId(); - container.removeItem(last); - container.commit(); - Assert.assertFalse(last.equals(container.lastItemId())); - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void commit_freeformLastItemUpdated_shouldUpdateRowInDB() - throws SQLException { - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.storeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))).andAnswer(new IAnswer() { - @Override - public Integer answer() throws Throwable { - Connection conn = (Connection) EasyMock - .getCurrentArguments()[0]; - RowItem item = (RowItem) EasyMock - .getCurrentArguments()[1]; - Statement statement = conn.createStatement(); - statement.executeUpdate("UPDATE people SET \"NAME\"='" - + item.getItemProperty("NAME").getValue() - + "' WHERE \"ID\"=" - + item.getItemProperty("ID").getValue()); - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - return 1; - } - }).anyTimes(); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT row_number() OVER" - + " ( ORDER BY \"ID\" ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN " + start - + " AND " + end; - return q; - } else if (SQLTestsConstants.db == DB.ORACLE) { - int start = offset + 1; - int end = offset + limit + 1; - String q = "SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people ORDER BY \"ID\" ASC) x) " - + " WHERE r BETWEEN " + start + " AND " - + end; - return q; - } else { - return "SELECT * FROM people LIMIT " + limit - + " OFFSET " + offset; - } - } - }).anyTimes(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - query.setDelegate(delegate); - EasyMock.replay(delegate); - SQLContainer container = new SQLContainer(query); - Object last = container.lastItemId(); - container.getContainerProperty(last, "NAME").setValue("Donald"); - container.commit(); - Assert.assertEquals("Donald", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - EasyMock.verify(delegate); - } - - @Test - public void rollback_freeformItemAdded_discardsAddedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - int size = container.size(); - Object id = container.addItem(); - container.getContainerProperty(id, "NAME").setValue("foo"); - Assert.assertEquals(size + 1, container.size()); - container.rollback(); - Assert.assertEquals(size, container.size()); - Assert.assertFalse("foo".equals( - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue())); - } - - @Test - public void rollback_freeformItemRemoved_restoresRemovedItem() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - int size = container.size(); - Object last = container.lastItemId(); - container.removeItem(last); - Assert.assertEquals(size - 1, container.size()); - container.rollback(); - Assert.assertEquals(size, container.size()); - Assert.assertEquals(last, container.lastItemId()); - } - - @Test - public void rollback_freeformItemChanged_discardsChanges() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Object last = container.lastItemId(); - container.getContainerProperty(last, "NAME").setValue("foo"); - container.rollback(); - Assert.assertFalse("foo".equals( - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue())); - } - - @Test - public void itemChangeNotification_freeform_isModifiedReturnsTrue() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertFalse(container.isModified()); - RowItem last = (RowItem) container.getItem(container.lastItemId()); - container.itemChangeNotification(last); - Assert.assertTrue(container.isModified()); - } - - @Test - public void itemSetChangeListeners_freeform_shouldFire() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - ItemSetChangeListener listener = EasyMock - .createMock(ItemSetChangeListener.class); - listener.containerItemSetChange(EasyMock.isA(ItemSetChangeEvent.class)); - EasyMock.replay(listener); - - container.addListener(listener); - container.addItem(); - - EasyMock.verify(listener); - } - - @Test - public void itemSetChangeListeners_freeformItemRemoved_shouldFire() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - ItemSetChangeListener listener = EasyMock - .createMock(ItemSetChangeListener.class); - listener.containerItemSetChange(EasyMock.isA(ItemSetChangeEvent.class)); - EasyMock.expectLastCall().anyTimes(); - EasyMock.replay(listener); - - container.addListener(listener); - container.removeItem(container.lastItemId()); - - EasyMock.verify(listener); - } - - @Test - public void removeListener_freeform_shouldNotFire() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - ItemSetChangeListener listener = EasyMock - .createMock(ItemSetChangeListener.class); - EasyMock.replay(listener); - - container.addListener(listener); - container.removeListener(listener); - container.addItem(); - - EasyMock.verify(listener); - } - - @Test - public void isModified_freeformRemovedItem_returnsTrue() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertFalse(container.isModified()); - container.removeItem(container.lastItemId()); - Assert.assertTrue(container.isModified()); - } - - @Test - public void isModified_freeformAddedItem_returnsTrue() throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertFalse(container.isModified()); - container.addItem(); - Assert.assertTrue(container.isModified()); - } - - @Test - public void isModified_freeformChangedItem_returnsTrue() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Assert.assertFalse(container.isModified()); - container.getContainerProperty(container.lastItemId(), "NAME") - .setValue("foo"); - Assert.assertTrue(container.isModified()); - } - - @Test - public void getSortableContainerPropertyIds_freeform_returnsAllPropertyIds() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - Collection sortableIds = container.getSortableContainerPropertyIds(); - Assert.assertTrue(sortableIds.contains("ID")); - Assert.assertTrue(sortableIds.contains("NAME")); - Assert.assertTrue(sortableIds.contains("AGE")); - Assert.assertEquals(3, sortableIds.size()); - } - - @SuppressWarnings("unchecked") - @Test - public void addOrderBy_freeform_shouldReorderResults() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - final ArrayList orderBys = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - orderBys.clear(); - orderBys.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - SQLGenerator gen = new MSSQLGenerator(); - if (orderBys == null || orderBys.isEmpty()) { - List ob = new ArrayList(); - ob.add(new OrderBy("ID", true)); - return gen - .generateSelectQuery("people", null, ob, - offset, limit, null) - .getQueryString(); - } else { - return gen - .generateSelectQuery("people", null, - orderBys, offset, limit, null) - .getQueryString(); - } - } else if (SQLTestsConstants.db == DB.ORACLE) { - SQLGenerator gen = new OracleGenerator(); - if (orderBys == null || orderBys.isEmpty()) { - List ob = new ArrayList(); - ob.add(new OrderBy("ID", true)); - return gen - .generateSelectQuery("people", null, ob, - offset, limit, null) - .getQueryString(); - } else { - return gen - .generateSelectQuery("people", null, - orderBys, offset, limit, null) - .getQueryString(); - } - } else { - StringBuffer query = new StringBuffer( - "SELECT * FROM people"); - if (!orderBys.isEmpty()) { - query.append(" ORDER BY "); - for (OrderBy orderBy : orderBys) { - query.append( - "\"" + orderBy.getColumn() + "\""); - if (orderBy.isAscending()) { - query.append(" ASC"); - } else { - query.append(" DESC"); - } - } - } - query.append(" LIMIT ").append(limit) - .append(" OFFSET ").append(offset); - return query.toString(); - } - } - }).anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals("Ville", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - container.addOrderBy(new OrderBy("NAME", true)); - // Börje, Kalle, Pelle, Ville - Assert.assertEquals("Börje", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - Assert.assertEquals("Ville", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - - @Test(expected = IllegalArgumentException.class) - public void addOrderBy_freeformIllegalColumn_shouldFail() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", connectionPool, "ID")); - container.addOrderBy(new OrderBy("asdf", true)); - } - - @SuppressWarnings("unchecked") - @Test - public void sort_freeform_sortsByName() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - final ArrayList orderBys = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - orderBys.clear(); - orderBys.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - SQLGenerator gen = new MSSQLGenerator(); - if (orderBys == null || orderBys.isEmpty()) { - List ob = new ArrayList(); - ob.add(new OrderBy("ID", true)); - return gen - .generateSelectQuery("people", null, ob, - offset, limit, null) - .getQueryString(); - } else { - return gen - .generateSelectQuery("people", null, - orderBys, offset, limit, null) - .getQueryString(); - } - } else if (SQLTestsConstants.db == DB.ORACLE) { - SQLGenerator gen = new OracleGenerator(); - if (orderBys == null || orderBys.isEmpty()) { - List ob = new ArrayList(); - ob.add(new OrderBy("ID", true)); - return gen - .generateSelectQuery("people", null, ob, - offset, limit, null) - .getQueryString(); - } else { - return gen - .generateSelectQuery("people", null, - orderBys, offset, limit, null) - .getQueryString(); - } - } else { - StringBuffer query = new StringBuffer( - "SELECT * FROM people"); - if (!orderBys.isEmpty()) { - query.append(" ORDER BY "); - for (OrderBy orderBy : orderBys) { - query.append( - "\"" + orderBy.getColumn() + "\""); - if (orderBy.isAscending()) { - query.append(" ASC"); - } else { - query.append(" DESC"); - } - } - } - query.append(" LIMIT ").append(limit) - .append(" OFFSET ").append(offset); - return query.toString(); - } - } - }).anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - EasyMock.replay(delegate); - - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals("Ville", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - container.sort(new Object[] { "NAME" }, new boolean[] { true }); - - // Börje, Kalle, Pelle, Ville - Assert.assertEquals("Börje", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - Assert.assertEquals("Ville", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void addFilter_freeform_filtersResults() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformStatementDelegate delegate = EasyMock - .createMock(FreeformStatementDelegate.class); - final ArrayList filters = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - filters.clear(); - filters.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect(delegate.getQueryStatement(EasyMock.anyInt(), - EasyMock.anyInt())).andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - return FreeformQueryUtil.getQueryWithFilters(filters, - offset, limit); - } - }).anyTimes(); - EasyMock.expect(delegate.getCountStatement()) - .andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - StatementHelper sh = new StatementHelper(); - StringBuffer query = new StringBuffer( - "SELECT COUNT(*) FROM people"); - if (!filters.isEmpty()) { - query.append(QueryBuilder - .getWhereStringForFilters(filters, sh)); - } - sh.setQueryString(query.toString()); - return sh; - } - }).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - container.addContainerFilter(new Like("NAME", "%lle")); - // Ville, Kalle, Pelle - Assert.assertEquals(3, container.size()); - Assert.assertEquals("Pelle", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void addContainerFilter_filtersResults() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformStatementDelegate delegate = EasyMock - .createMock(FreeformStatementDelegate.class); - final ArrayList filters = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - filters.clear(); - filters.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect(delegate.getQueryStatement(EasyMock.anyInt(), - EasyMock.anyInt())).andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - return FreeformQueryUtil.getQueryWithFilters(filters, - offset, limit); - } - }).anyTimes(); - EasyMock.expect(delegate.getCountStatement()) - .andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - StatementHelper sh = new StatementHelper(); - StringBuffer query = new StringBuffer( - "SELECT COUNT(*) FROM people"); - if (!filters.isEmpty()) { - query.append(QueryBuilder - .getWhereStringForFilters(filters, sh)); - } - sh.setQueryString(query.toString()); - return sh; - } - }).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - - container.addContainerFilter("NAME", "Vi", false, false); - - // Ville - Assert.assertEquals(1, container.size()); - Assert.assertEquals("Ville", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void addContainerFilter_ignoreCase_filtersResults() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformStatementDelegate delegate = EasyMock - .createMock(FreeformStatementDelegate.class); - final ArrayList filters = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - filters.clear(); - filters.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect(delegate.getQueryStatement(EasyMock.anyInt(), - EasyMock.anyInt())).andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - return FreeformQueryUtil.getQueryWithFilters(filters, - offset, limit); - } - }).anyTimes(); - EasyMock.expect(delegate.getCountStatement()) - .andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - StatementHelper sh = new StatementHelper(); - StringBuffer query = new StringBuffer( - "SELECT COUNT(*) FROM people"); - if (!filters.isEmpty()) { - query.append(QueryBuilder - .getWhereStringForFilters(filters, sh)); - } - sh.setQueryString(query.toString()); - return sh; - } - }).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - - // FIXME LIKE %asdf% doesn't match a string that begins with asdf - container.addContainerFilter("NAME", "vi", true, true); - - // Ville - Assert.assertEquals(1, container.size()); - Assert.assertEquals("Ville", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void removeAllContainerFilters_freeform_noFiltering() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformStatementDelegate delegate = EasyMock - .createMock(FreeformStatementDelegate.class); - final ArrayList filters = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - filters.clear(); - filters.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect(delegate.getQueryStatement(EasyMock.anyInt(), - EasyMock.anyInt())).andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - return FreeformQueryUtil.getQueryWithFilters(filters, - offset, limit); - } - }).anyTimes(); - EasyMock.expect(delegate.getCountStatement()) - .andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - StatementHelper sh = new StatementHelper(); - StringBuffer query = new StringBuffer( - "SELECT COUNT(*) FROM people"); - if (!filters.isEmpty()) { - query.append(QueryBuilder - .getWhereStringForFilters(filters, sh)); - } - sh.setQueryString(query.toString()); - return sh; - } - }).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - - container.addContainerFilter("NAME", "Vi", false, false); - - // Ville - Assert.assertEquals(1, container.size()); - Assert.assertEquals("Ville", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - container.removeAllContainerFilters(); - - Assert.assertEquals(4, container.size()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void removeContainerFilters_freeform_noFiltering() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformStatementDelegate delegate = EasyMock - .createMock(FreeformStatementDelegate.class); - final ArrayList filters = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - filters.clear(); - filters.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect(delegate.getQueryStatement(EasyMock.anyInt(), - EasyMock.anyInt())).andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - return FreeformQueryUtil.getQueryWithFilters(filters, - offset, limit); - } - }).anyTimes(); - EasyMock.expect(delegate.getCountStatement()) - .andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - StatementHelper sh = new StatementHelper(); - StringBuffer query = new StringBuffer( - "SELECT COUNT(*) FROM people"); - if (!filters.isEmpty()) { - query.append(QueryBuilder - .getWhereStringForFilters(filters, sh)); - } - sh.setQueryString(query.toString()); - return sh; - } - }).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - - container.addContainerFilter("NAME", "Vi", false, true); - - // Ville - Assert.assertEquals(1, container.size()); - Assert.assertEquals("Ville", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - container.removeContainerFilters("NAME"); - - Assert.assertEquals(4, container.size()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void addFilter_freeformBufferedItems_alsoFiltersBufferedItems() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformStatementDelegate delegate = EasyMock - .createMock(FreeformStatementDelegate.class); - final ArrayList filters = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - filters.clear(); - filters.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect(delegate.getQueryStatement(EasyMock.anyInt(), - EasyMock.anyInt())).andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - return FreeformQueryUtil.getQueryWithFilters(filters, - offset, limit); - } - }).anyTimes(); - EasyMock.expect(delegate.getCountStatement()) - .andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - StatementHelper sh = new StatementHelper(); - StringBuffer query = new StringBuffer( - "SELECT COUNT(*) FROM people"); - if (!filters.isEmpty()) { - query.append(QueryBuilder - .getWhereStringForFilters(filters, sh)); - } - sh.setQueryString(query.toString()); - return sh; - } - }).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - Object id1 = container.addItem(); - container.getContainerProperty(id1, "NAME").setValue("Palle"); - Object id2 = container.addItem(); - container.getContainerProperty(id2, "NAME").setValue("Bengt"); - - container.addContainerFilter(new Like("NAME", "%lle")); - - // Ville, Kalle, Pelle, Palle - Assert.assertEquals(4, container.size()); - Assert.assertEquals("Ville", - container - .getContainerProperty(container.getIdByIndex(0), "NAME") - .getValue()); - Assert.assertEquals("Kalle", - container - .getContainerProperty(container.getIdByIndex(1), "NAME") - .getValue()); - Assert.assertEquals("Pelle", - container - .getContainerProperty(container.getIdByIndex(2), "NAME") - .getValue()); - Assert.assertEquals("Palle", - container - .getContainerProperty(container.getIdByIndex(3), "NAME") - .getValue()); - - try { - container.getIdByIndex(4); - Assert.fail( - "SQLContainer.getIdByIndex() returned a value for an index beyond the end of the container"); - } catch (IndexOutOfBoundsException e) { - // should throw exception - item is filtered out - } - container.nextItemId(container.getIdByIndex(3)); - - Assert.assertFalse(container.containsId(id2)); - Assert.assertFalse(container.getItemIds().contains(id2)); - - Assert.assertNull(container.getItem(id2)); - Assert.assertEquals(-1, container.indexOfId(id2)); - - Assert.assertNotSame(id2, container.lastItemId()); - Assert.assertSame(id1, container.lastItemId()); - - EasyMock.verify(delegate); - } - - @SuppressWarnings("unchecked") - @Test - public void sort_freeformBufferedItems_sortsBufferedItemsLastInOrderAdded() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - connectionPool, "ID"); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - final ArrayList orderBys = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - orderBys.clear(); - orderBys.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect( - delegate.getQueryString(EasyMock.anyInt(), EasyMock.anyInt())) - .andAnswer(new IAnswer() { - @Override - public String answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - if (SQLTestsConstants.db == DB.MSSQL) { - SQLGenerator gen = new MSSQLGenerator(); - if (orderBys == null || orderBys.isEmpty()) { - List ob = new ArrayList(); - ob.add(new OrderBy("ID", true)); - return gen - .generateSelectQuery("people", null, ob, - offset, limit, null) - .getQueryString(); - } else { - return gen - .generateSelectQuery("people", null, - orderBys, offset, limit, null) - .getQueryString(); - } - } else if (SQLTestsConstants.db == DB.ORACLE) { - SQLGenerator gen = new OracleGenerator(); - if (orderBys == null || orderBys.isEmpty()) { - List ob = new ArrayList(); - ob.add(new OrderBy("ID", true)); - return gen - .generateSelectQuery("people", null, ob, - offset, limit, null) - .getQueryString(); - } else { - return gen - .generateSelectQuery("people", null, - orderBys, offset, limit, null) - .getQueryString(); - } - } else { - StringBuffer query = new StringBuffer( - "SELECT * FROM people"); - if (!orderBys.isEmpty()) { - query.append(" ORDER BY "); - for (OrderBy orderBy : orderBys) { - query.append( - "\"" + orderBy.getColumn() + "\""); - if (orderBy.isAscending()) { - query.append(" ASC"); - } else { - query.append(" DESC"); - } - } - } - query.append(" LIMIT ").append(limit) - .append(" OFFSET ").append(offset); - return query.toString(); - } - } - }).anyTimes(); - EasyMock.expect(delegate.getCountQuery()) - .andThrow(new UnsupportedOperationException()).anyTimes(); - EasyMock.replay(delegate); - - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals("Ville", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - Object id1 = container.addItem(); - container.getContainerProperty(id1, "NAME").setValue("Wilbert"); - Object id2 = container.addItem(); - container.getContainerProperty(id2, "NAME").setValue("Albert"); - - container.sort(new Object[] { "NAME" }, new boolean[] { true }); - - // Börje, Kalle, Pelle, Ville, Wilbert, Albert - Assert.assertEquals("Börje", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - Assert.assertEquals("Wilbert", - container.getContainerProperty( - container.getIdByIndex(container.size() - 2), "NAME") - .getValue()); - Assert.assertEquals("Albert", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - EasyMock.verify(delegate); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java deleted file mode 100755 index 3718cf756d..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java +++ /dev/null @@ -1,154 +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.sqlcontainer; - -import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator; - -public class SQLTestsConstants { - - /* Set the DB used for testing here! */ - public enum DB { - HSQLDB, MYSQL, POSTGRESQL, MSSQL, ORACLE; - } - - /* 0 = HSQLDB, 1 = MYSQL, 2 = POSTGRESQL, 3 = MSSQL, 4 = ORACLE */ - public static final DB db = DB.HSQLDB; - - /* Auto-increment column offset (HSQLDB = 0, MYSQL = 1, POSTGRES = 1) */ - public static int offset; - /* Garbage table creation query (=three queries for oracle) */ - public static String createGarbage; - public static String createGarbageSecond; - public static String createGarbageThird; - /* DB Drivers, urls, usernames and passwords */ - public static String dbDriver; - public static String dbURL; - public static String dbUser; - public static String dbPwd; - /* People -test table creation statement(s) */ - public static String peopleFirst; - public static String peopleSecond; - public static String peopleThird; - /* Schema test creation statement(s) */ - public static String createSchema; - public static String createProductTable; - public static String dropSchema; - /* Versioned -test table createion statement(s) */ - public static String[] versionStatements; - /* SQL Generator used during the testing */ - public static SQLGenerator sqlGen; - - /* Set DB-specific settings based on selected DB */ - static { - sqlGen = new DefaultSQLGenerator(); - switch (db) { - case HSQLDB: - offset = 0; - createGarbage = "create table garbage (id integer generated always as identity, type varchar(32), PRIMARY KEY(id))"; - dbDriver = "org.hsqldb.jdbc.JDBCDriver"; - dbURL = "jdbc:hsqldb:mem:sqlcontainer"; - dbUser = "SA"; - dbPwd = ""; - peopleFirst = "create table people (id integer generated always as identity, name varchar(32), AGE INTEGER)"; - peopleSecond = "alter table people add primary key (id)"; - versionStatements = new String[] { - "create table versioned (id integer generated always as identity, text varchar(255), version tinyint default 0)", - "alter table versioned add primary key (id)" }; - // TODO these should ideally exist for all databases - createSchema = "create schema oaas authorization DBA"; - createProductTable = "create table oaas.product (\"ID\" integer generated always as identity primary key, \"NAME\" VARCHAR(32))"; - dropSchema = "drop schema if exists oaas cascade"; - break; - case MYSQL: - offset = 1; - createGarbage = "create table GARBAGE (ID integer auto_increment, type varchar(32), PRIMARY KEY(ID))"; - dbDriver = "com.mysql.jdbc.Driver"; - dbURL = "jdbc:mysql:///sqlcontainer"; - dbUser = "sqlcontainer"; - dbPwd = "sqlcontainer"; - peopleFirst = "create table PEOPLE (ID integer auto_increment not null, NAME varchar(32), AGE INTEGER, primary key(ID))"; - peopleSecond = null; - versionStatements = new String[] { - "create table VERSIONED (ID integer auto_increment not null, TEXT varchar(255), VERSION tinyint default 0, primary key(ID))", - "CREATE TRIGGER upd_version BEFORE UPDATE ON VERSIONED" - + " FOR EACH ROW SET NEW.VERSION = OLD.VERSION+1" }; - break; - case POSTGRESQL: - offset = 1; - createGarbage = "create table GARBAGE (\"ID\" serial PRIMARY KEY, \"TYPE\" varchar(32))"; - dbDriver = "org.postgresql.Driver"; - dbURL = "jdbc:postgresql://localhost:5432/test"; - dbUser = "postgres"; - dbPwd = "postgres"; - peopleFirst = "create table PEOPLE (\"ID\" serial primary key, \"NAME\" VARCHAR(32), \"AGE\" INTEGER)"; - peopleSecond = null; - versionStatements = new String[] { - "create table VERSIONED (\"ID\" serial primary key, \"TEXT\" VARCHAR(255), \"VERSION\" INTEGER DEFAULT 0)", - "CREATE OR REPLACE FUNCTION zz_row_version() RETURNS TRIGGER AS $$" - + "BEGIN" + " IF TG_OP = 'UPDATE'" - + " AND NEW.\"VERSION\" = old.\"VERSION\"" - + " AND ROW(NEW.*) IS DISTINCT FROM ROW (old.*)" - + " THEN" - + " NEW.\"VERSION\" := NEW.\"VERSION\" + 1;" - + " END IF;" + " RETURN NEW;" + "END;" - + "$$ LANGUAGE plpgsql;", - "CREATE TRIGGER \"mytable_modify_dt_tr\" BEFORE UPDATE" - + " ON VERSIONED FOR EACH ROW" - + " EXECUTE PROCEDURE \"public\".\"zz_row_version\"();" }; - createSchema = "create schema oaas"; - createProductTable = "create table oaas.product (\"ID\" serial primary key, \"NAME\" VARCHAR(32))"; - dropSchema = "drop schema oaas cascade"; - break; - case MSSQL: - offset = 1; - createGarbage = "create table GARBAGE (\"ID\" int identity(1,1) primary key, \"TYPE\" varchar(32))"; - dbDriver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; - dbURL = "jdbc:sqlserver://localhost:1433;databaseName=tempdb;"; - dbUser = "sa"; - dbPwd = "sa"; - peopleFirst = "create table PEOPLE (\"ID\" int identity(1,1) primary key, \"NAME\" VARCHAR(32), \"AGE\" INTEGER)"; - peopleSecond = null; - versionStatements = new String[] { - "create table VERSIONED (\"ID\" int identity(1,1) primary key, \"TEXT\" VARCHAR(255), \"VERSION\" rowversion not null)" }; - sqlGen = new MSSQLGenerator(); - break; - case ORACLE: - offset = 1; - createGarbage = "create table GARBAGE (\"ID\" integer primary key, \"TYPE\" varchar2(32))"; - createGarbageSecond = "create sequence garbage_seq start with 1 increment by 1 nomaxvalue"; - createGarbageThird = "create trigger garbage_trigger before insert on GARBAGE for each row begin select garbage_seq.nextval into :new.ID from dual; end;"; - dbDriver = "oracle.jdbc.OracleDriver"; - dbURL = "jdbc:oracle:thin:test/test@localhost:1521:XE"; - dbUser = "test"; - dbPwd = "test"; - peopleFirst = "create table PEOPLE (\"ID\" integer primary key, \"NAME\" VARCHAR2(32), \"AGE\" INTEGER)"; - peopleSecond = "create sequence people_seq start with 1 increment by 1 nomaxvalue"; - peopleThird = "create trigger people_trigger before insert on PEOPLE for each row begin select people_seq.nextval into :new.ID from dual; end;"; - versionStatements = new String[] { - "create table VERSIONED (\"ID\" integer primary key, \"TEXT\" VARCHAR(255), \"VERSION\" INTEGER DEFAULT 0)", - "create sequence versioned_seq start with 1 increment by 1 nomaxvalue", - "create trigger versioned_trigger before insert on VERSIONED for each row begin select versioned_seq.nextval into :new.ID from dual; end;", - "create sequence versioned_version start with 1 increment by 1 nomaxvalue", - "create trigger versioned_version_trigger before insert or update on VERSIONED for each row begin select versioned_version.nextval into :new.VERSION from dual; end;" }; - sqlGen = new OracleGenerator(); - break; - } - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/TicketTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/TicketTest.java deleted file mode 100644 index 1a19eda542..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/TicketTest.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import java.math.BigDecimal; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.easymock.EasyMock; -import org.easymock.IAnswer; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.Item; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants.DB; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.FreeformQuery; -import com.vaadin.data.util.sqlcontainer.query.FreeformStatementDelegate; -import com.vaadin.data.util.sqlcontainer.query.TableQuery; -import com.vaadin.data.util.sqlcontainer.query.ValidatingSimpleJDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; -import com.vaadin.ui.Table; -import com.vaadin.ui.Window; - -public class TicketTest { - - private JDBCConnectionPool connectionPool; - - @Before - public void setUp() throws SQLException { - connectionPool = new ValidatingSimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 2, 2); - DataGenerator.addPeopleToDatabase(connectionPool); - } - - @Test - public void ticket5867_throwsIllegalState_transactionAlreadyActive() - throws SQLException { - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people", Arrays.asList("ID"), connectionPool)); - Table table = new Table(); - Window w = new Window(); - w.setContent(table); - table.setContainerDataSource(container); - } - - @SuppressWarnings("unchecked") - @Test - public void ticket6136_freeform_ageIs18() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformStatementDelegate delegate = EasyMock - .createMock(FreeformStatementDelegate.class); - final ArrayList filters = new ArrayList(); - delegate.setFilters(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(EasyMock.isA(List.class)); - EasyMock.expectLastCall().anyTimes(); - delegate.setOrderBy(null); - EasyMock.expectLastCall().anyTimes(); - delegate.setFilters(EasyMock.isA(List.class)); - EasyMock.expectLastCall().andAnswer(new IAnswer() { - @Override - public Object answer() throws Throwable { - List orders = (List) EasyMock - .getCurrentArguments()[0]; - filters.clear(); - filters.addAll(orders); - return null; - } - }).anyTimes(); - EasyMock.expect(delegate.getQueryStatement(EasyMock.anyInt(), - EasyMock.anyInt())).andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - Object[] args = EasyMock.getCurrentArguments(); - int offset = (Integer) (args[0]); - int limit = (Integer) (args[1]); - return FreeformQueryUtil.getQueryWithFilters(filters, - offset, limit); - } - }).anyTimes(); - EasyMock.expect(delegate.getCountStatement()) - .andAnswer(new IAnswer() { - @Override - public StatementHelper answer() throws Throwable { - StatementHelper sh = new StatementHelper(); - StringBuffer query = new StringBuffer( - "SELECT COUNT(*) FROM people"); - if (!filters.isEmpty()) { - query.append(QueryBuilder - .getWhereStringForFilters(filters, sh)); - } - sh.setQueryString(query.toString()); - return sh; - } - }).anyTimes(); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - Assert.assertEquals("Börje", - container.getContainerProperty(container.lastItemId(), "NAME") - .getValue()); - - container.addContainerFilter(new Equal("AGE", 18)); - // Pelle - Assert.assertEquals(1, container.size()); - Assert.assertEquals("Pelle", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals(new BigDecimal(18), container - .getContainerProperty(container.firstItemId(), "AGE") - .getValue()); - } else { - Assert.assertEquals(18, container - .getContainerProperty(container.firstItemId(), "AGE") - .getValue()); - } - - EasyMock.verify(delegate); - } - - @Test - public void ticket6136_table_ageIs18() throws SQLException { - TableQuery query = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - SQLContainer container = new SQLContainer(query); - // Ville, Kalle, Pelle, Börje - Assert.assertEquals(4, container.size()); - - container.addContainerFilter(new Equal("AGE", 18)); - - // Pelle - Assert.assertEquals(1, container.size()); - Assert.assertEquals("Pelle", - container.getContainerProperty(container.firstItemId(), "NAME") - .getValue()); - if (SQLTestsConstants.db == DB.ORACLE) { - Assert.assertEquals(new BigDecimal(18), container - .getContainerProperty(container.firstItemId(), "AGE") - .getValue()); - } else { - Assert.assertEquals(18, container - .getContainerProperty(container.firstItemId(), "AGE") - .getValue()); - } - } - - @Test - public void ticket7434_getItem_Modified_Changed_Unchanged() - throws SQLException { - SQLContainer container = new SQLContainer(new TableQuery("people", - connectionPool, SQLTestsConstants.sqlGen)); - - Object id = container.firstItemId(); - Item item = container.getItem(id); - String name = (String) item.getItemProperty("NAME").getValue(); - - // set a different name - item.getItemProperty("NAME").setValue("otherName"); - Assert.assertEquals("otherName", - item.getItemProperty("NAME").getValue()); - - // access the item and reset the name to its old value - Item item2 = container.getItem(id); - item2.getItemProperty("NAME").setValue(name); - Assert.assertEquals(name, item2.getItemProperty("NAME").getValue()); - - Item item3 = container.getItem(id); - String name3 = (String) item3.getItemProperty("NAME").getValue(); - - Assert.assertEquals(name, name3); - } - - @Test - public void ticket10032_empty_set_metadata_correctly_handled() - throws SQLException { - // If problem exists will break when method getPropertyIds() - // is called in constructor SQLContainer(QueryDelegate delegate). - SQLContainer container = new SQLContainer(new FreeformQuery( - "SELECT * FROM people WHERE name='does_not_exist'", - Arrays.asList("ID"), connectionPool)); - Assert.assertTrue("Got items while expected empty set", - container.size() == 0); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/UtilTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/UtilTest.java deleted file mode 100644 index a575d649f1..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/UtilTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.vaadin.data.util.sqlcontainer; - -import org.junit.Assert; -import org.junit.Test; - -public class UtilTest { - - @Test - public void escapeSQL_noQuotes_returnsSameString() { - Assert.assertEquals("asdf", SQLUtil.escapeSQL("asdf")); - } - - @Test - public void escapeSQL_singleQuotes_returnsEscapedString() { - Assert.assertEquals("O''Brien", SQLUtil.escapeSQL("O'Brien")); - } - - @Test - public void escapeSQL_severalQuotes_returnsEscapedString() { - Assert.assertEquals("asdf''ghjk''qwerty", - SQLUtil.escapeSQL("asdf'ghjk'qwerty")); - } - - @Test - public void escapeSQL_doubleQuotes_returnsEscapedString() { - Assert.assertEquals("asdf\\\"foo", SQLUtil.escapeSQL("asdf\"foo")); - } - - @Test - public void escapeSQL_multipleDoubleQuotes_returnsEscapedString() { - Assert.assertEquals("asdf\\\"foo\\\"bar", - SQLUtil.escapeSQL("asdf\"foo\"bar")); - } - - @Test - public void escapeSQL_backslashes_returnsEscapedString() { - Assert.assertEquals("foo\\\\nbar\\\\r", - SQLUtil.escapeSQL("foo\\nbar\\r")); - } - - @Test - public void escapeSQL_x00_removesX00() { - Assert.assertEquals("foobar", SQLUtil.escapeSQL("foo\\x00bar")); - } - - @Test - public void escapeSQL_x1a_removesX1a() { - Assert.assertEquals("foobar", SQLUtil.escapeSQL("foo\\x1abar")); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPoolTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPoolTest.java deleted file mode 100644 index 1463a27217..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/J2EEConnectionPoolTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.connection; - -import java.sql.Connection; -import java.sql.SQLException; - -import javax.naming.Context; -import javax.naming.NamingException; -import javax.sql.DataSource; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -public class J2EEConnectionPoolTest { - - @Test - public void reserveConnection_dataSourceSpecified_shouldReturnValidConnection() - throws SQLException { - Connection connection = EasyMock.createMock(Connection.class); - connection.setAutoCommit(false); - EasyMock.expectLastCall(); - DataSource ds = EasyMock.createMock(DataSource.class); - ds.getConnection(); - EasyMock.expectLastCall().andReturn(connection); - EasyMock.replay(connection, ds); - - J2EEConnectionPool pool = new J2EEConnectionPool(ds); - Connection c = pool.reserveConnection(); - Assert.assertEquals(connection, c); - EasyMock.verify(connection, ds); - } - - @Test - public void releaseConnection_shouldCloseConnection() throws SQLException { - Connection connection = EasyMock.createMock(Connection.class); - connection.setAutoCommit(false); - EasyMock.expectLastCall(); - connection.close(); - EasyMock.expectLastCall(); - DataSource ds = EasyMock.createMock(DataSource.class); - ds.getConnection(); - EasyMock.expectLastCall().andReturn(connection); - EasyMock.replay(connection, ds); - - J2EEConnectionPool pool = new J2EEConnectionPool(ds); - Connection c = pool.reserveConnection(); - Assert.assertEquals(connection, c); - pool.releaseConnection(c); - EasyMock.verify(connection, ds); - } - - @Test - public void reserveConnection_dataSourceLookedUp_shouldReturnValidConnection() - throws SQLException, NamingException { - Connection connection = EasyMock.createMock(Connection.class); - connection.setAutoCommit(false); - EasyMock.expectLastCall(); - connection.close(); - EasyMock.expectLastCall(); - - DataSource ds = EasyMock.createMock(DataSource.class); - ds.getConnection(); - EasyMock.expectLastCall().andReturn(connection); - - System.setProperty("java.naming.factory.initial", - "com.vaadin.data.util.sqlcontainer.connection.MockInitialContextFactory"); - Context context = EasyMock.createMock(Context.class); - context.lookup("testDataSource"); - EasyMock.expectLastCall().andReturn(ds); - MockInitialContextFactory.setMockContext(context); - - EasyMock.replay(context, connection, ds); - - J2EEConnectionPool pool = new J2EEConnectionPool("testDataSource"); - Connection c = pool.reserveConnection(); - Assert.assertEquals(connection, c); - pool.releaseConnection(c); - EasyMock.verify(context, connection, ds); - } - - @Test(expected = SQLException.class) - public void reserveConnection_nonExistantDataSourceLookedUp_shouldFail() - throws SQLException, NamingException { - System.setProperty("java.naming.factory.initial", - "com.vaadin.addon.sqlcontainer.connection.MockInitialContextFactory"); - Context context = EasyMock.createMock(Context.class); - context.lookup("foo"); - EasyMock.expectLastCall().andThrow(new NamingException("fail")); - MockInitialContextFactory.setMockContext(context); - - EasyMock.replay(context); - - J2EEConnectionPool pool = new J2EEConnectionPool("foo"); - pool.reserveConnection(); - EasyMock.verify(context); - } - - @Test - public void releaseConnection_null_shouldSucceed() throws SQLException { - DataSource ds = EasyMock.createMock(DataSource.class); - EasyMock.replay(ds); - - J2EEConnectionPool pool = new J2EEConnectionPool(ds); - pool.releaseConnection(null); - EasyMock.verify(ds); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/MockInitialContextFactory.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/MockInitialContextFactory.java deleted file mode 100644 index 42b02cf874..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/MockInitialContextFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.connection; - -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.spi.InitialContextFactory; - -/** - * Provides a JNDI initial context factory for the MockContext. - */ -public class MockInitialContextFactory implements InitialContextFactory { - private static Context mockCtx = null; - - public static void setMockContext(Context ctx) { - mockCtx = ctx; - } - - @Override - public Context getInitialContext(java.util.Hashtable environment) - throws NamingException { - if (mockCtx == null) { - throw new IllegalStateException("mock context was not set."); - } - return mockCtx; - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java deleted file mode 100644 index 5a6ae78aeb..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/connection/SimpleJDBCConnectionPoolTest.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.connection; - -import java.sql.Connection; -import java.sql.SQLException; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants; -import com.vaadin.data.util.sqlcontainer.query.ValidatingSimpleJDBCConnectionPool; - -public class SimpleJDBCConnectionPoolTest { - private JDBCConnectionPool connectionPool; - - @Before - public void setUp() throws SQLException { - connectionPool = new ValidatingSimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 2, 2); - } - - @Test - public void reserveConnection_reserveNewConnection_returnsConnection() - throws SQLException { - Connection conn = connectionPool.reserveConnection(); - Assert.assertNotNull(conn); - } - - @Test - public void releaseConnection_releaseUnused_shouldNotThrowException() - throws SQLException { - Connection conn = connectionPool.reserveConnection(); - connectionPool.releaseConnection(conn); - Assert.assertFalse(conn.isClosed()); - } - - @Test(expected = SQLException.class) - public void reserveConnection_noConnectionsLeft_shouldFail() - throws SQLException { - try { - connectionPool.reserveConnection(); - connectionPool.reserveConnection(); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail( - "Exception before all connections used! " + e.getMessage()); - } - - connectionPool.reserveConnection(); - Assert.fail( - "Reserving connection didn't fail even though no connections are available!"); - } - - @Test - public void reserveConnection_oneConnectionLeft_returnsConnection() - throws SQLException { - try { - connectionPool.reserveConnection(); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail( - "Exception before all connections used! " + e.getMessage()); - } - - Connection conn = connectionPool.reserveConnection(); - Assert.assertNotNull(conn); - } - - @Test - public void reserveConnection_oneConnectionJustReleased_returnsConnection() - throws SQLException { - Connection conn2 = null; - try { - connectionPool.reserveConnection(); - conn2 = connectionPool.reserveConnection(); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail( - "Exception before all connections used! " + e.getMessage()); - } - - connectionPool.releaseConnection(conn2); - - connectionPool.reserveConnection(); - } - - @Test(expected = IllegalArgumentException.class) - public void construct_allParametersNull_shouldFail() throws SQLException { - SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool(null, null, - null, null); - } - - @Test(expected = IllegalArgumentException.class) - public void construct_onlyDriverNameGiven_shouldFail() throws SQLException { - SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, null, null, null); - } - - @Test(expected = IllegalArgumentException.class) - public void construct_onlyDriverNameAndUrlGiven_shouldFail() - throws SQLException { - SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, null, - null); - } - - @Test(expected = IllegalArgumentException.class) - public void construct_onlyDriverNameAndUrlAndUserGiven_shouldFail() - throws SQLException { - SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, null); - } - - @Test(expected = RuntimeException.class) - public void construct_nonExistingDriver_shouldFail() throws SQLException { - SimpleJDBCConnectionPool cp = new SimpleJDBCConnectionPool("foo", - SQLTestsConstants.dbURL, SQLTestsConstants.dbUser, - SQLTestsConstants.dbPwd); - } - - @Test - public void reserveConnection_newConnectionOpened_shouldSucceed() - throws SQLException { - connectionPool = new SimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 0, 2); - Connection c = connectionPool.reserveConnection(); - Assert.assertNotNull(c); - } - - @Test - public void releaseConnection_nullConnection_shouldDoNothing() { - connectionPool.releaseConnection(null); - } - - @Test - public void releaseConnection_failingRollback_shouldCallClose() - throws SQLException { - Connection c = EasyMock.createMock(Connection.class); - c.getAutoCommit(); - EasyMock.expectLastCall().andReturn(false); - c.rollback(); - EasyMock.expectLastCall().andThrow(new SQLException("Rollback failed")); - c.close(); - EasyMock.expectLastCall().atLeastOnce(); - EasyMock.replay(c); - // make sure the connection pool is initialized - // Bypass validation - JDBCConnectionPool realPool = ((ValidatingSimpleJDBCConnectionPool) connectionPool) - .getRealPool(); - realPool.reserveConnection(); - realPool.releaseConnection(c); - EasyMock.verify(c); - } - - @Test - public void destroy_shouldCloseAllConnections() throws SQLException { - Connection c1 = connectionPool.reserveConnection(); - Connection c2 = connectionPool.reserveConnection(); - try { - connectionPool.destroy(); - } catch (RuntimeException e) { - // The test connection pool throws an exception when the pool was - // not empty but only after cleanup of the real pool has been done - } - - Assert.assertTrue(c1.isClosed()); - Assert.assertTrue(c2.isClosed()); - } - - @Test - public void destroy_shouldCloseAllConnections2() throws SQLException { - Connection c1 = connectionPool.reserveConnection(); - Connection c2 = connectionPool.reserveConnection(); - connectionPool.releaseConnection(c1); - connectionPool.releaseConnection(c2); - connectionPool.destroy(); - Assert.assertTrue(c1.isClosed()); - Assert.assertTrue(c2.isClosed()); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/BetweenTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/BetweenTest.java deleted file mode 100644 index 41db88b881..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/BetweenTest.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.filters; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.filter.Between; - -public class BetweenTest { - - private Item itemWithPropertyValue(Object propertyId, Object value) { - Property property = EasyMock.createMock(Property.class); - property.getValue(); - EasyMock.expectLastCall().andReturn(value).anyTimes(); - EasyMock.replay(property); - - Item item = EasyMock.createMock(Item.class); - item.getItemProperty(propertyId); - EasyMock.expectLastCall().andReturn(property).anyTimes(); - EasyMock.replay(item); - return item; - } - - @Test - public void passesFilter_valueIsInRange_shouldBeTrue() { - Item item = itemWithPropertyValue("foo", 15); - Between between = new Between("foo", 1, 30); - Assert.assertTrue(between.passesFilter("foo", item)); - } - - @Test - public void passesFilter_valueIsOutOfRange_shouldBeFalse() { - Item item = itemWithPropertyValue("foo", 15); - Between between = new Between("foo", 0, 2); - Assert.assertFalse(between.passesFilter("foo", item)); - } - - @Test - public void passesFilter_valueNotComparable_shouldBeFalse() { - Item item = itemWithPropertyValue("foo", new Object()); - Between between = new Between("foo", 0, 2); - Assert.assertFalse(between.passesFilter("foo", item)); - } - - @Test - public void appliesToProperty_differentProperties_shoudlBeFalse() { - Between between = new Between("foo", 0, 2); - Assert.assertFalse(between.appliesToProperty("bar")); - } - - @Test - public void appliesToProperty_sameProperties_shouldBeTrue() { - Between between = new Between("foo", 0, 2); - Assert.assertTrue(between.appliesToProperty("foo")); - } - - @Test - public void hashCode_equalInstances_shouldBeEqual() { - Between b1 = new Between("foo", 0, 2); - Between b2 = new Between("foo", 0, 2); - Assert.assertEquals(b1.hashCode(), b2.hashCode()); - } - - @Test - public void equals_differentObjects_shouldBeFalse() { - Between b1 = new Between("foo", 0, 2); - Object obj = new Object(); - Assert.assertFalse(b1.equals(obj)); - } - - @Test - public void equals_sameInstance_shouldBeTrue() { - Between b1 = new Between("foo", 0, 2); - Between b2 = b1; - Assert.assertTrue(b1.equals(b2)); - } - - @Test - public void equals_equalInstances_shouldBeTrue() { - Between b1 = new Between("foo", 0, 2); - Between b2 = new Between("foo", 0, 2); - Assert.assertTrue(b1.equals(b2)); - } - - @Test - public void equals_equalInstances2_shouldBeTrue() { - Between b1 = new Between(null, null, null); - Between b2 = new Between(null, null, null); - Assert.assertTrue(b1.equals(b2)); - } - - @Test - public void equals_secondValueDiffers_shouldBeFalse() { - Between b1 = new Between("foo", 0, 1); - Between b2 = new Between("foo", 0, 2); - Assert.assertFalse(b1.equals(b2)); - } - - @Test - public void equals_firstAndSecondValueDiffers_shouldBeFalse() { - Between b1 = new Between("foo", 0, null); - Between b2 = new Between("foo", 1, 2); - Assert.assertFalse(b1.equals(b2)); - } - - @Test - public void equals_propertyAndFirstAndSecondValueDiffers_shouldBeFalse() { - Between b1 = new Between("foo", null, 1); - Between b2 = new Between("bar", 1, 2); - Assert.assertFalse(b1.equals(b2)); - } - - @Test - public void equals_propertiesDiffer_shouldBeFalse() { - Between b1 = new Between(null, 0, 1); - Between b2 = new Between("bar", 0, 1); - Assert.assertFalse(b1.equals(b2)); - } - - @Test - public void hashCode_nullStartValue_shouldBeEqual() { - Between b1 = new Between("foo", null, 2); - Between b2 = new Between("foo", null, 2); - Assert.assertEquals(b1.hashCode(), b2.hashCode()); - } - - @Test - public void hashCode_nullEndValue_shouldBeEqual() { - Between b1 = new Between("foo", 0, null); - Between b2 = new Between("foo", 0, null); - Assert.assertEquals(b1.hashCode(), b2.hashCode()); - } - - @Test - public void hashCode_nullPropertyId_shouldBeEqual() { - Between b1 = new Between(null, 0, 2); - Between b2 = new Between(null, 0, 2); - Assert.assertEquals(b1.hashCode(), b2.hashCode()); - } - - @Test - public void passesFilter_nullValue_filterIsPassed() { - String id = "id"; - Between between = new Between(id, null, null); - Assert.assertTrue( - between.passesFilter(id, itemWithPropertyValue(id, null))); - } - - @Test - public void passesFilter_nullStartValue_filterIsPassed() { - String id = "id"; - Between between = new Between(id, null, 2); - Assert.assertTrue( - between.passesFilter(id, itemWithPropertyValue(id, 1))); - } - - @Test - public void passesFilter_nullEndValue_filterIsPassed() { - String id = "id"; - Between between = new Between(id, 0, null); - Assert.assertTrue( - between.passesFilter(id, itemWithPropertyValue(id, 1))); - } - - @Test - public void passesFilter_nullStartValueAndEndValue_filterIsPassed() { - String id = "id"; - Between between = new Between(id, null, null); - Assert.assertTrue( - between.passesFilter(id, itemWithPropertyValue(id, 1))); - } - - @Test - public void passesFilter_nullStartValueAndEndValueAndValueIsNotComparable_filterIsNotPassed() { - String id = "id"; - Between between = new Between(id, null, null); - Assert.assertFalse(between.passesFilter(id, - itemWithPropertyValue(id, new Object()))); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/CompareTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/CompareTest.java deleted file mode 100644 index a6b6f4b55c..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/CompareTest.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.data.util.sqlcontainer.filters; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.util.filter.Compare; - -public class CompareTest { - - @Test - public void testEquals() { - Compare c1 = new Compare.Equal("prop1", "val1"); - Compare c2 = new Compare.Equal("prop1", "val1"); - Assert.assertTrue(c1.equals(c2)); - } - - @Test - public void testDifferentTypeEquals() { - Compare c1 = new Compare.Equal("prop1", "val1"); - Compare c2 = new Compare.Greater("prop1", "val1"); - Assert.assertFalse(c1.equals(c2)); - } - - @Test - public void testEqualsNull() { - Compare c1 = new Compare.Equal("prop1", "val1"); - Assert.assertFalse(c1.equals(null)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/LikeTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/LikeTest.java deleted file mode 100644 index f1130aad80..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/filters/LikeTest.java +++ /dev/null @@ -1,229 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.filters; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.data.util.PropertysetItem; -import com.vaadin.data.util.filter.Like; - -public class LikeTest { - - @Test - public void passesFilter_valueIsNotStringType_shouldFail() { - Like like = new Like("test", "%foo%"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty(5)); - - Assert.assertFalse(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_containsLikeQueryOnStringContainingValue_shouldSucceed() { - Like like = new Like("test", "%foo%"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("asdfooghij")); - - Assert.assertTrue(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_containsLikeQueryOnStringContainingValueCaseInsensitive_shouldSucceed() { - Like like = new Like("test", "%foo%"); - like.setCaseSensitive(false); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("asdfOOghij")); - - Assert.assertTrue(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_containsLikeQueryOnStringContainingValueConstructedCaseInsensitive_shouldSucceed() { - Like like = new Like("test", "%foo%", false); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("asdfOOghij")); - - Assert.assertTrue(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_containsLikeQueryOnStringNotContainingValue_shouldFail() { - Like like = new Like("test", "%foo%"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("asdbarghij")); - - Assert.assertFalse(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_containsLikeQueryOnStringExactlyEqualToValue_shouldSucceed() { - Like like = new Like("test", "%foo%"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("foo")); - - Assert.assertTrue(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_containsLikeQueryOnStringEqualToValueMinusOneCharAtTheEnd_shouldFail() { - Like like = new Like("test", "%foo%"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("fo")); - - Assert.assertFalse(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_beginsWithLikeQueryOnStringBeginningWithValue_shouldSucceed() { - Like like = new Like("test", "foo%"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("foobar")); - - Assert.assertTrue(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_beginsWithLikeQueryOnStringNotBeginningWithValue_shouldFail() { - Like like = new Like("test", "foo%"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("barfoo")); - - Assert.assertFalse(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_endsWithLikeQueryOnStringEndingWithValue_shouldSucceed() { - Like like = new Like("test", "%foo"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("barfoo")); - - Assert.assertTrue(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_endsWithLikeQueryOnStringNotEndingWithValue_shouldFail() { - Like like = new Like("test", "%foo"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("foobar")); - - Assert.assertFalse(like.passesFilter("id", item)); - } - - @Test - public void passesFilter_startsWithAndEndsWithOnMatchingValue_shouldSucceed() { - Like like = new Like("test", "foo%bar"); - - Item item = new PropertysetItem(); - item.addItemProperty("test", new ObjectProperty("fooASDFbar")); - - Assert.assertTrue(like.passesFilter("id", item)); - } - - @Test - public void appliesToProperty_valueIsProperty_shouldBeTrue() { - Like like = new Like("test", "%foo"); - Assert.assertTrue(like.appliesToProperty("test")); - } - - @Test - public void appliesToProperty_valueIsNotProperty_shouldBeFalse() { - Like like = new Like("test", "%foo"); - Assert.assertFalse(like.appliesToProperty("bar")); - } - - @Test - public void equals_sameInstances_shouldBeTrue() { - Like like1 = new Like("test", "%foo"); - Like like2 = like1; - Assert.assertTrue(like1.equals(like2)); - } - - @Test - public void equals_twoEqualInstances_shouldBeTrue() { - Like like1 = new Like("test", "foo"); - Like like2 = new Like("test", "foo"); - Assert.assertTrue(like1.equals(like2)); - } - - @Test - public void equals_differentValues_shouldBeFalse() { - Like like1 = new Like("test", "foo"); - Like like2 = new Like("test", "bar"); - Assert.assertFalse(like1.equals(like2)); - } - - @Test - public void equals_differentProperties_shouldBeFalse() { - Like like1 = new Like("foo", "test"); - Like like2 = new Like("bar", "test"); - Assert.assertFalse(like1.equals(like2)); - } - - @Test - public void equals_differentPropertiesAndValues_shouldBeFalse() { - Like like1 = new Like("foo", "bar"); - Like like2 = new Like("baz", "zomg"); - Assert.assertFalse(like1.equals(like2)); - } - - @Test - public void equals_differentClasses_shouldBeFalse() { - Like like1 = new Like("foo", "bar"); - Object obj = new Object(); - Assert.assertFalse(like1.equals(obj)); - } - - @Test - public void equals_bothHaveNullProperties_shouldBeTrue() { - Like like1 = new Like(null, "foo"); - Like like2 = new Like(null, "foo"); - Assert.assertTrue(like1.equals(like2)); - } - - @Test - public void equals_bothHaveNullValues_shouldBeTrue() { - Like like1 = new Like("foo", null); - Like like2 = new Like("foo", null); - Assert.assertTrue(like1.equals(like2)); - } - - @Test - public void equals_onePropertyIsNull_shouldBeFalse() { - Like like1 = new Like(null, "bar"); - Like like2 = new Like("foo", "baz"); - Assert.assertFalse(like1.equals(like2)); - } - - @Test - public void equals_oneValueIsNull_shouldBeFalse() { - Like like1 = new Like("foo", null); - Like like2 = new Like("baz", "bar"); - Assert.assertFalse(like1.equals(like2)); - } - - @Test - public void hashCode_equalInstances_shouldBeEqual() { - Like like1 = new Like("test", "foo"); - Like like2 = new Like("test", "foo"); - Assert.assertEquals(like1.hashCode(), like2.hashCode()); - } - - @Test - public void hashCode_differentPropertiesAndValues_shouldNotEqual() { - Like like1 = new Like("foo", "bar"); - Like like2 = new Like("baz", "zomg"); - Assert.assertTrue(like1.hashCode() != like2.hashCode()); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/SQLGeneratorsTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/SQLGeneratorsTest.java deleted file mode 100644 index dd1db30b72..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/SQLGeneratorsTest.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.generator; - -import java.sql.SQLException; -import java.util.ArrayList; -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.Container.Filter; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.filter.Or; -import com.vaadin.data.util.sqlcontainer.DataGenerator; -import com.vaadin.data.util.sqlcontainer.RowItem; -import com.vaadin.data.util.sqlcontainer.SQLContainer; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.OrderBy; -import com.vaadin.data.util.sqlcontainer.query.TableQuery; -import com.vaadin.data.util.sqlcontainer.query.ValidatingSimpleJDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.MSSQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.OracleGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.SQLGenerator; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class SQLGeneratorsTest { - private JDBCConnectionPool connectionPool; - - @Before - public void setUp() throws SQLException { - - try { - connectionPool = new ValidatingSimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 2, 2); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - - DataGenerator.addPeopleToDatabase(connectionPool); - } - - @After - public void tearDown() { - if (connectionPool != null) { - connectionPool.destroy(); - } - } - - @Test - public void generateSelectQuery_basicQuery_shouldSucceed() { - SQLGenerator sg = new DefaultSQLGenerator(); - StatementHelper sh = sg.generateSelectQuery("TABLE", null, null, 0, 0, - null); - Assert.assertEquals(sh.getQueryString(), "SELECT * FROM TABLE"); - } - - @Test - public void generateSelectQuery_pagingAndColumnsSet_shouldSucceed() { - SQLGenerator sg = new DefaultSQLGenerator(); - StatementHelper sh = sg.generateSelectQuery("TABLE", null, null, 4, 8, - "COL1, COL2, COL3"); - Assert.assertEquals(sh.getQueryString(), - "SELECT COL1, COL2, COL3 FROM TABLE LIMIT 8 OFFSET 4"); - } - - /** - * Note: Only tests one kind of filter and ordering. - */ - @Test - public void generateSelectQuery_filtersAndOrderingSet_shouldSucceed() { - SQLGenerator sg = new DefaultSQLGenerator(); - List f = new ArrayList(); - f.add(new Like("name", "%lle")); - List ob = Arrays.asList(new OrderBy("name", true)); - StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 0, 0, null); - Assert.assertEquals(sh.getQueryString(), - "SELECT * FROM TABLE WHERE \"name\" LIKE ? ORDER BY \"name\" ASC"); - } - - @Test - public void generateSelectQuery_filtersAndOrderingSet_exclusiveFilteringMode_shouldSucceed() { - SQLGenerator sg = new DefaultSQLGenerator(); - List f = new ArrayList(); - f.add(new Or(new Like("name", "%lle"), new Like("name", "vi%"))); - List ob = Arrays.asList(new OrderBy("name", true)); - StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 0, 0, null); - // TODO - Assert.assertEquals(sh.getQueryString(), - "SELECT * FROM TABLE WHERE (\"name\" LIKE ? " - + "OR \"name\" LIKE ?) ORDER BY \"name\" ASC"); - } - - @Test - public void generateDeleteQuery_basicQuery_shouldSucceed() - throws SQLException { - /* - * No need to run this for Oracle/MSSQL generators since the - * DefaultSQLGenerator method would be called anyway. - */ - if (SQLTestsConstants.sqlGen instanceof MSSQLGenerator - || SQLTestsConstants.sqlGen instanceof OracleGenerator) { - return; - } - SQLGenerator sg = SQLTestsConstants.sqlGen; - TableQuery query = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - SQLContainer container = new SQLContainer(query); - - StatementHelper sh = sg.generateDeleteQuery("people", - query.getPrimaryKeyColumns(), null, (RowItem) container - .getItem(container.getItemIds().iterator().next())); - Assert.assertEquals("DELETE FROM people WHERE \"ID\" = ?", - sh.getQueryString()); - } - - @Test - public void generateUpdateQuery_basicQuery_shouldSucceed() - throws SQLException { - /* - * No need to run this for Oracle/MSSQL generators since the - * DefaultSQLGenerator method would be called anyway. - */ - if (SQLTestsConstants.sqlGen instanceof MSSQLGenerator - || SQLTestsConstants.sqlGen instanceof OracleGenerator) { - return; - } - SQLGenerator sg = new DefaultSQLGenerator(); - TableQuery query = new TableQuery("people", connectionPool); - SQLContainer container = new SQLContainer(query); - - RowItem ri = (RowItem) container - .getItem(container.getItemIds().iterator().next()); - ri.getItemProperty("NAME").setValue("Viljami"); - - StatementHelper sh = sg.generateUpdateQuery("people", ri); - Assert.assertTrue( - "UPDATE people SET \"NAME\" = ?, \"AGE\" = ? WHERE \"ID\" = ?" - .equals(sh.getQueryString()) - || "UPDATE people SET \"AGE\" = ?, \"NAME\" = ? WHERE \"ID\" = ?" - .equals(sh.getQueryString())); - } - - @Test - public void generateInsertQuery_basicQuery_shouldSucceed() - throws SQLException { - /* - * No need to run this for Oracle/MSSQL generators since the - * DefaultSQLGenerator method would be called anyway. - */ - if (SQLTestsConstants.sqlGen instanceof MSSQLGenerator - || SQLTestsConstants.sqlGen instanceof OracleGenerator) { - return; - } - SQLGenerator sg = new DefaultSQLGenerator(); - TableQuery query = new TableQuery("people", connectionPool); - SQLContainer container = new SQLContainer(query); - - RowItem ri = (RowItem) container.getItem(container.addItem()); - ri.getItemProperty("NAME").setValue("Viljami"); - - StatementHelper sh = sg.generateInsertQuery("people", ri); - - Assert.assertTrue("INSERT INTO people (\"NAME\", \"AGE\") VALUES (?, ?)" - .equals(sh.getQueryString()) - || "INSERT INTO people (\"AGE\", \"NAME\") VALUES (?, ?)" - .equals(sh.getQueryString())); - } - - @Test - public void generateComplexSelectQuery_forOracle_shouldSucceed() - throws SQLException { - SQLGenerator sg = new OracleGenerator(); - List f = new ArrayList(); - f.add(new Like("name", "%lle")); - List ob = Arrays.asList(new OrderBy("name", true)); - StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8, - "NAME, ID"); - Assert.assertEquals( - "SELECT * FROM (SELECT x.*, ROWNUM AS \"rownum\" FROM" - + " (SELECT NAME, ID FROM TABLE WHERE \"name\" LIKE ?" - + " ORDER BY \"name\" ASC) x) WHERE \"rownum\" BETWEEN 5 AND 12", - sh.getQueryString()); - } - - @Test - public void generateComplexSelectQuery_forMSSQL_shouldSucceed() - throws SQLException { - SQLGenerator sg = new MSSQLGenerator(); - List f = new ArrayList(); - f.add(new Like("name", "%lle")); - List ob = Arrays.asList(new OrderBy("name", true)); - StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8, - "NAME, ID"); - Assert.assertEquals(sh.getQueryString(), - "SELECT * FROM (SELECT row_number() OVER " - + "( ORDER BY \"name\" ASC) AS rownum, NAME, ID " - + "FROM TABLE WHERE \"name\" LIKE ?) " - + "AS a WHERE a.rownum BETWEEN 5 AND 12"); - } - - @Test - public void generateComplexSelectQuery_forOracle_exclusiveFilteringMode_shouldSucceed() - throws SQLException { - SQLGenerator sg = new OracleGenerator(); - List f = new ArrayList(); - f.add(new Or(new Like("name", "%lle"), new Like("name", "vi%"))); - List ob = Arrays.asList(new OrderBy("name", true)); - StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8, - "NAME, ID"); - Assert.assertEquals(sh.getQueryString(), - "SELECT * FROM (SELECT x.*, ROWNUM AS \"rownum\" FROM" - + " (SELECT NAME, ID FROM TABLE WHERE (\"name\" LIKE ?" - + " OR \"name\" LIKE ?) " - + "ORDER BY \"name\" ASC) x) WHERE \"rownum\" BETWEEN 5 AND 12"); - } - - @Test - public void generateComplexSelectQuery_forMSSQL_exclusiveFilteringMode_shouldSucceed() - throws SQLException { - SQLGenerator sg = new MSSQLGenerator(); - List f = new ArrayList(); - f.add(new Or(new Like("name", "%lle"), new Like("name", "vi%"))); - List ob = Arrays.asList(new OrderBy("name", true)); - StatementHelper sh = sg.generateSelectQuery("TABLE", f, ob, 4, 8, - "NAME, ID"); - Assert.assertEquals(sh.getQueryString(), - "SELECT * FROM (SELECT row_number() OVER " - + "( ORDER BY \"name\" ASC) AS rownum, NAME, ID " - + "FROM TABLE WHERE (\"name\" LIKE ? " - + "OR \"name\" LIKE ?)) " - + "AS a WHERE a.rownum BETWEEN 5 AND 12"); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/StatementHelperTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/StatementHelperTest.java deleted file mode 100644 index 7201ec7ad8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/generator/StatementHelperTest.java +++ /dev/null @@ -1,64 +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.sqlcontainer.generator; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -/** - * - * @author Vaadin Ltd - */ -public class StatementHelperTest { - - @Test - public void testSetValueNullParameter() throws SQLException { - StatementHelper helper = new StatementHelper(); - helper.addParameterValue(null, StatementHelper.class); - PreparedStatement statement = EasyMock - .createMock(PreparedStatement.class); - // should throw SQLException, not NPE - try { - helper.setParameterValuesToStatement(statement); - Assert.fail("Expected SQLExecption for unsupported type"); - } catch (SQLException e) { - // Exception should contain info about which parameter and the type - // which was unsupported - Assert.assertTrue(e.getMessage().contains("parameter 0")); - Assert.assertTrue( - e.getMessage().contains(StatementHelper.class.getName())); - } - } - - @Test - public void testSetByteArrayValue() throws SQLException { - StatementHelper helper = new StatementHelper(); - helper.addParameterValue(null, byte[].class); - PreparedStatement statement = EasyMock - .createMock(PreparedStatement.class); - // should not throw SQLException - helper.setParameterValuesToStatement(statement); - - EasyMock.replay(statement); - statement.setBytes(1, null); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryTest.java deleted file mode 100644 index 1b9e14b0d8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/FreeformQueryTest.java +++ /dev/null @@ -1,1005 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.query; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.easymock.EasyMock; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.sqlcontainer.DataGenerator; -import com.vaadin.data.util.sqlcontainer.RowId; -import com.vaadin.data.util.sqlcontainer.RowItem; -import com.vaadin.data.util.sqlcontainer.SQLContainer; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants.DB; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; - -public class FreeformQueryTest { - - private static final int offset = SQLTestsConstants.offset; - private JDBCConnectionPool connectionPool; - - @Before - public void setUp() throws SQLException { - - try { - connectionPool = new ValidatingSimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 2, 2); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - - DataGenerator.addPeopleToDatabase(connectionPool); - } - - @After - public void tearDown() { - if (connectionPool != null) { - connectionPool.destroy(); - } - } - - @Test - public void construction_legalParameters_shouldSucceed() { - FreeformQuery ffQuery = new FreeformQuery("SELECT * FROM foo", - Arrays.asList("ID"), connectionPool); - Assert.assertArrayEquals(new Object[] { "ID" }, - ffQuery.getPrimaryKeyColumns().toArray()); - - Assert.assertEquals("SELECT * FROM foo", ffQuery.getQueryString()); - } - - @Test(expected = IllegalArgumentException.class) - public void construction_emptyQueryString_shouldFail() { - new FreeformQuery("", Arrays.asList("ID"), connectionPool); - } - - @Test - public void construction_nullPrimaryKeys_shouldSucceed() { - new FreeformQuery("SELECT * FROM foo", null, connectionPool); - } - - @Test - public void construction_nullPrimaryKeys2_shouldSucceed() { - new FreeformQuery("SELECT * FROM foo", connectionPool); - } - - @Test - public void construction_emptyPrimaryKeys_shouldSucceed() { - new FreeformQuery("SELECT * FROM foo", connectionPool); - } - - @Test(expected = IllegalArgumentException.class) - public void construction_emptyStringsInPrimaryKeys_shouldFail() { - new FreeformQuery("SELECT * FROM foo", Arrays.asList(""), - connectionPool); - } - - @Test(expected = IllegalArgumentException.class) - public void construction_nullConnectionPool_shouldFail() { - new FreeformQuery("SELECT * FROM foo", Arrays.asList("ID"), null); - } - - @Test - public void getCount_simpleQuery_returnsFour() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - Assert.assertEquals(4, query.getCount()); - } - - @Test(expected = SQLException.class) - public void getCount_illegalQuery_shouldThrowSQLException() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM asdf", - Arrays.asList("ID"), connectionPool); - query.getResults(0, 50); - } - - @Test - public void getCount_simpleQueryTwoMorePeopleAdded_returnsSix() - throws SQLException { - // Add some people - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - if (SQLTestsConstants.db == DB.MSSQL) { - statement.executeUpdate("insert into people values('Bengt', 30)"); - statement.executeUpdate("insert into people values('Ingvar', 50)"); - } else { - statement.executeUpdate( - "insert into people values(default, 'Bengt', 30)"); - statement.executeUpdate( - "insert into people values(default, 'Ingvar', 50)"); - } - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - - Assert.assertEquals(6, query.getCount()); - } - - @Test - public void getCount_moreComplexQuery_returnsThree() throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT * FROM people WHERE \"NAME\" LIKE '%lle'", - connectionPool, new String[] { "ID" }); - Assert.assertEquals(3, query.getCount()); - } - - @Test - public void getCount_normalState_releasesConnection() throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT * FROM people WHERE \"NAME\" LIKE '%lle'", - connectionPool, "ID"); - query.getCount(); - query.getCount(); - Connection c = connectionPool.reserveConnection(); - Assert.assertNotNull(c); - // Cleanup to make test connection pool happy - connectionPool.releaseConnection(c); - } - - @Test - public void getCount_delegateRegistered_shouldUseDelegate() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.getCountQuery()).andReturn( - "SELECT COUNT(*) FROM people WHERE \"NAME\" LIKE '%lle'"); - EasyMock.replay(delegate); - query.setDelegate(delegate); - Assert.assertEquals(3, query.getCount()); - EasyMock.verify(delegate); - } - - @Test - public void getCount_delegateRegisteredZeroRows_returnsZero() - throws SQLException { - DataGenerator.createGarbage(connectionPool); - FreeformQuery query = new FreeformQuery("SELECT * FROM GARBAGE", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.getCountQuery()) - .andReturn("SELECT COUNT(*) FROM GARBAGE"); - EasyMock.replay(delegate); - query.setDelegate(delegate); - Assert.assertEquals(0, query.getCount()); - EasyMock.verify(delegate); - } - - @Test - public void getResults_simpleQuery_returnsFourRecords() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT \"ID\",\"NAME\" FROM people", Arrays.asList("ID"), - connectionPool); - query.beginTransaction(); - ResultSet rs = query.getResults(0, 0); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(0 + offset, rs.getInt(1)); - Assert.assertEquals("Ville", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(1 + offset, rs.getInt(1)); - Assert.assertEquals("Kalle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(2 + offset, rs.getInt(1)); - Assert.assertEquals("Pelle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(3 + offset, rs.getInt(1)); - Assert.assertEquals("Börje", rs.getString(2)); - - Assert.assertFalse(rs.next()); - query.commit(); - } - - @Test - public void getResults_moreComplexQuery_returnsThreeRecords() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT * FROM people WHERE \"NAME\" LIKE '%lle'", - Arrays.asList("ID"), connectionPool); - query.beginTransaction(); - ResultSet rs = query.getResults(0, 0); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(0 + offset, rs.getInt(1)); - Assert.assertEquals("Ville", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(1 + offset, rs.getInt(1)); - Assert.assertEquals("Kalle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(2 + offset, rs.getInt(1)); - Assert.assertEquals("Pelle", rs.getString(2)); - - Assert.assertFalse(rs.next()); - query.commit(); - } - - @Test - public void getResults_noDelegate5000Rows_returns5000rows() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.beginTransaction(); - ResultSet rs = query.getResults(0, 0); - for (int i = 0; i < 5000; i++) { - Assert.assertTrue(rs.next()); - } - Assert.assertFalse(rs.next()); - query.commit(); - } - - @Test(expected = UnsupportedOperationException.class) - public void setFilters_noDelegate_shouldFail() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - ArrayList filters = new ArrayList(); - filters.add(new Like("name", "%lle")); - query.setFilters(filters); - } - - @Test(expected = UnsupportedOperationException.class) - public void setOrderBy_noDelegate_shouldFail() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.setOrderBy(Arrays.asList(new OrderBy("name", true))); - } - - @Test(expected = IllegalStateException.class) - public void storeRow_noDelegateNoTransactionActive_shouldFail() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.storeRow(new RowItem(new SQLContainer(query), - new RowId(new Object[] { 1 }), null)); - } - - @Test - public void storeRow_noDelegate_shouldFail() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - SQLContainer container = EasyMock.createNiceMock(SQLContainer.class); - EasyMock.replay(container); - query.beginTransaction(); - try { - query.storeRow(new RowItem(container, new RowId(new Object[] { 1 }), - null)); - Assert.fail("storeRow should fail when there is no delegate"); - } catch (UnsupportedOperationException e) { - // Cleanup to make test connection pool happy - query.rollback(); - } - } - - @Test - public void removeRow_noDelegate_shouldFail() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - SQLContainer container = EasyMock.createNiceMock(SQLContainer.class); - EasyMock.replay(container); - query.beginTransaction(); - try { - query.removeRow(new RowItem(container, - new RowId(new Object[] { 1 }), null)); - Assert.fail("removeRow should fail when there is no delgate"); - } catch (UnsupportedOperationException e) { - // Cleanup to make test connection pool happy - query.rollback(); - } - } - - @Test - public void commit_readOnly_shouldSucceed() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.beginTransaction(); - query.commit(); - } - - @Test - public void rollback_readOnly_shouldSucceed() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.beginTransaction(); - query.rollback(); - } - - @Test(expected = SQLException.class) - public void commit_noActiveTransaction_shouldFail() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.commit(); - } - - @Test(expected = SQLException.class) - public void rollback_noActiveTransaction_shouldFail() throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.rollback(); - } - - @Test - public void containsRowWithKeys_simpleQueryWithExistingKeys_returnsTrue() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - Assert.assertTrue(query.containsRowWithKey(1)); - } - - @Test - public void containsRowWithKeys_simpleQueryWithNonexistingKeys_returnsTrue() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - Assert.assertFalse(query.containsRowWithKey(1337)); - } - - // (expected = SQLException.class) - @Test - public void containsRowWithKeys_simpleQueryWithInvalidKeys_shouldFail() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - Assert.assertFalse(query.containsRowWithKey(38796)); - } - - @Test - public void containsRowWithKeys_queryContainingWhereClauseAndExistingKeys_returnsTrue() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT * FROM people WHERE \"NAME\" LIKE '%lle'", - Arrays.asList("ID"), connectionPool); - Assert.assertTrue(query.containsRowWithKey(1)); - } - - @Test - public void containsRowWithKeys_queryContainingLowercaseWhereClauseAndExistingKeys_returnsTrue() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "select * from people where \"NAME\" like '%lle'", - Arrays.asList("ID"), connectionPool); - Assert.assertTrue(query.containsRowWithKey(1)); - } - - @Test - public void containsRowWithKeys_nullKeys_shouldFailAndReleaseConnections() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "select * from people where \"NAME\" like '%lle'", - Arrays.asList("ID"), connectionPool); - try { - query.containsRowWithKey(new Object[] { null }); - } catch (SQLException e) { - // We should now be able to reserve two connections - connectionPool.reserveConnection(); - connectionPool.reserveConnection(); - } - } - - /* - * -------- Tests with a delegate --------- - */ - - @Test - public void setDelegate_noExistingDelegate_shouldRegisterNewDelegate() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - query.setDelegate(delegate); - Assert.assertEquals(delegate, query.getDelegate()); - } - - @Test - public void getResults_hasDelegate_shouldCallDelegate() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - if (SQLTestsConstants.db == DB.MSSQL) { - EasyMock.expect(delegate.getQueryString(0, 2)) - .andReturn("SELECT * FROM (SELECT row_number()" - + "OVER (ORDER BY id ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN 0 AND 2"); - } else if (SQLTestsConstants.db == DB.ORACLE) { - EasyMock.expect(delegate.getQueryString(0, 2)) - .andReturn("SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people) x) WHERE r BETWEEN 1 AND 2"); - } else { - EasyMock.expect(delegate.getQueryString(0, 2)) - .andReturn("SELECT * FROM people LIMIT 2 OFFSET 0"); - } - EasyMock.replay(delegate); - - query.setDelegate(delegate); - query.beginTransaction(); - query.getResults(0, 2); - EasyMock.verify(delegate); - query.commit(); - } - - @Test - public void getResults_delegateImplementsGetQueryString_shouldHonorOffsetAndPagelength() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - if (SQLTestsConstants.db == DB.MSSQL) { - EasyMock.expect(delegate.getQueryString(0, 2)) - .andReturn("SELECT * FROM (SELECT row_number()" - + "OVER (ORDER BY id ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN 0 AND 2"); - } else if (SQLTestsConstants.db == DB.ORACLE) { - EasyMock.expect(delegate.getQueryString(0, 2)) - .andReturn("SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people) x) WHERE r BETWEEN 1 AND 2"); - } else { - EasyMock.expect(delegate.getQueryString(0, 2)) - .andReturn("SELECT * FROM people LIMIT 2 OFFSET 0"); - } - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - ResultSet rs = query.getResults(0, 2); - int rsoffset = 0; - if (SQLTestsConstants.db == DB.MSSQL) { - rsoffset++; - } - Assert.assertTrue(rs.next()); - Assert.assertEquals(0 + offset, rs.getInt(1 + rsoffset)); - Assert.assertEquals("Ville", rs.getString(2 + rsoffset)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(1 + offset, rs.getInt(1 + rsoffset)); - Assert.assertEquals("Kalle", rs.getString(2 + rsoffset)); - - Assert.assertFalse(rs.next()); - - EasyMock.verify(delegate); - query.commit(); - } - - @Test - public void getResults_delegateRegistered5000Rows_returns100rows() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - if (SQLTestsConstants.db == DB.MSSQL) { - EasyMock.expect(delegate.getQueryString(200, 100)) - .andReturn("SELECT * FROM (SELECT row_number()" - + "OVER (ORDER BY id ASC) AS rownum, * FROM people)" - + " AS a WHERE a.rownum BETWEEN 201 AND 300"); - } else if (SQLTestsConstants.db == DB.ORACLE) { - EasyMock.expect(delegate.getQueryString(200, 100)) - .andReturn("SELECT * FROM (SELECT x.*, ROWNUM AS r FROM" - + " (SELECT * FROM people ORDER BY ID ASC) x) WHERE r BETWEEN 201 AND 300"); - } else { - EasyMock.expect(delegate.getQueryString(200, 100)) - .andReturn("SELECT * FROM people LIMIT 100 OFFSET 200"); - } - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - ResultSet rs = query.getResults(200, 100); - for (int i = 0; i < 100; i++) { - Assert.assertTrue(rs.next()); - Assert.assertEquals(200 + i + offset, rs.getInt("ID")); - } - Assert.assertFalse(rs.next()); - query.commit(); - } - - @Test - public void setFilters_delegateImplementsSetFilters_shouldPassFiltersToDelegate() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - List filters = new ArrayList(); - filters.add(new Like("name", "%lle")); - delegate.setFilters(filters); - - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.setFilters(filters); - - EasyMock.verify(delegate); - } - - @Test(expected = UnsupportedOperationException.class) - public void setFilters_delegateDoesNotImplementSetFilters_shouldFail() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - List filters = new ArrayList(); - filters.add(new Like("name", "%lle")); - delegate.setFilters(filters); - EasyMock.expectLastCall().andThrow(new UnsupportedOperationException()); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.setFilters(filters); - - EasyMock.verify(delegate); - } - - @Test - public void setOrderBy_delegateImplementsSetOrderBy_shouldPassArgumentsToDelegate() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - List orderBys = Arrays.asList(new OrderBy("name", false)); - delegate.setOrderBy(orderBys); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.setOrderBy(orderBys); - - EasyMock.verify(delegate); - } - - @Test(expected = UnsupportedOperationException.class) - public void setOrderBy_delegateDoesNotImplementSetOrderBy_shouldFail() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - List orderBys = Arrays.asList(new OrderBy("name", false)); - delegate.setOrderBy(orderBys); - EasyMock.expectLastCall().andThrow(new UnsupportedOperationException()); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.setOrderBy(orderBys); - - EasyMock.verify(delegate); - } - - @Test - public void setFilters_noDelegateAndNullParameter_shouldSucceed() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.setFilters(null); - } - - @Test - public void setOrderBy_noDelegateAndNullParameter_shouldSucceed() { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - query.setOrderBy(null); - } - - @Test - public void storeRow_delegateImplementsStoreRow_shouldPassToDelegate() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.storeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))).andReturn(1); - SQLContainer container = EasyMock.createNiceMock(SQLContainer.class); - EasyMock.replay(delegate, container); - query.setDelegate(delegate); - - query.beginTransaction(); - RowItem row = new RowItem(container, new RowId(new Object[] { 1 }), - null); - query.storeRow(row); - query.commit(); - - EasyMock.verify(delegate, container); - } - - @Test - public void storeRow_delegateDoesNotImplementStoreRow_shouldFail() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.storeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))) - .andThrow(new UnsupportedOperationException()); - SQLContainer container = EasyMock.createNiceMock(SQLContainer.class); - EasyMock.replay(delegate, container); - query.setDelegate(delegate); - - query.beginTransaction(); - RowItem row = new RowItem(container, new RowId(new Object[] { 1 }), - null); - try { - query.storeRow(row); - Assert.fail( - "storeRow should fail when delgate does not implement storeRow"); - } catch (UnsupportedOperationException e) { - // Cleanup to make test connection pool happy - query.rollback(); - } - } - - @Test - public void removeRow_delegateImplementsRemoveRow_shouldPassToDelegate() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.removeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))).andReturn(true); - SQLContainer container = EasyMock.createNiceMock(SQLContainer.class); - EasyMock.replay(delegate, container); - query.setDelegate(delegate); - - query.beginTransaction(); - RowItem row = new RowItem(container, new RowId(new Object[] { 1 }), - null); - query.removeRow(row); - query.commit(); - - EasyMock.verify(delegate, container); - } - - @Test - public void removeRow_delegateDoesNotImplementRemoveRow_shouldFail() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.removeRow(EasyMock.isA(Connection.class), - EasyMock.isA(RowItem.class))) - .andThrow(new UnsupportedOperationException()); - SQLContainer container = EasyMock.createNiceMock(SQLContainer.class); - EasyMock.replay(delegate, container); - query.setDelegate(delegate); - - query.beginTransaction(); - RowItem row = new RowItem(container, new RowId(new Object[] { 1 }), - null); - try { - query.removeRow(row); - Assert.fail( - "removeRow should fail when delegate does not implement removeRow"); - } catch (UnsupportedOperationException e) { - // Cleanup to make test connection pool happy - query.rollback(); - } - } - - @Test - public void beginTransaction_delegateRegistered_shouldSucceed() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - // Cleanup to make test connection pool happy - query.rollback(); - } - - @Test - public void beginTransaction_transactionAlreadyActive_shouldFail() - throws SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - - query.beginTransaction(); - try { - query.beginTransaction(); - Assert.fail( - "Should throw exception when starting a transaction while already in a transaction"); - } catch (IllegalStateException e) { - // Cleanup to make test connection pool happy - query.rollback(); - } - } - - @Test(expected = SQLException.class) - public void commit_delegateRegisteredNoActiveTransaction_shouldFail() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.commit(); - } - - @Test - public void commit_delegateRegisteredActiveTransaction_shouldSucceed() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - query.commit(); - } - - @Test(expected = SQLException.class) - public void commit_delegateRegisteredActiveTransactionDoubleCommit_shouldFail() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - query.commit(); - query.commit(); - } - - @Test(expected = SQLException.class) - public void rollback_delegateRegisteredNoActiveTransaction_shouldFail() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.rollback(); - } - - @Test - public void rollback_delegateRegisteredActiveTransaction_shouldSucceed() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - query.rollback(); - } - - @Test(expected = SQLException.class) - public void rollback_delegateRegisteredActiveTransactionDoubleRollback_shouldFail() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - query.rollback(); - query.rollback(); - } - - @Test(expected = SQLException.class) - public void rollback_delegateRegisteredCommittedTransaction_shouldFail() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - query.commit(); - query.rollback(); - } - - @Test(expected = SQLException.class) - public void commit_delegateRegisteredRollbackedTransaction_shouldFail() - throws UnsupportedOperationException, SQLException { - FreeformQuery query = new FreeformQuery("SELECT * FROM people", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.beginTransaction(); - query.rollback(); - query.commit(); - } - - @Test(expected = SQLException.class) - public void containsRowWithKeys_delegateRegistered_shouldCallGetContainsRowQueryString() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT * FROM people WHERE name LIKE '%lle'", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.getContainsRowQueryString(1)).andReturn(""); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - query.containsRowWithKey(1); - - EasyMock.verify(delegate); - } - - @Test - public void containsRowWithKeys_delegateRegistered_shouldUseResultFromGetContainsRowQueryString() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT * FROM people WHERE \"NAME\" LIKE '%lle'", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - // In order to test that this is the query that is actually used, we use - // a non-existing id in place of the existing one. - EasyMock.expect(delegate.getContainsRowQueryString(1)).andReturn( - "SELECT * FROM people WHERE \"NAME\" LIKE '%lle' AND \"ID\" = 1337"); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - // The id (key) used should be 1337 as above, for the call with key = 1 - Assert.assertFalse(query.containsRowWithKey(1)); - - EasyMock.verify(delegate); - } - - public static class NonMatchingDelegateWithGroupBy - implements FreeformQueryDelegate { - - private String fromWhere = "FROM people p1 LEFT JOIN people p2 ON p2.id = p1.id WHERE p1.\"NAME\" LIKE 'notfound' GROUP BY p1.ID"; - - @Override - public int storeRow(Connection conn, RowItem row) - throws UnsupportedOperationException, SQLException { - // Not used in this test - return 0; - } - - @Override - public void setOrderBy(List orderBys) - throws UnsupportedOperationException { - // Not used in this test - } - - @Override - public void setFilters(List filters) - throws UnsupportedOperationException { - // Not used in this test - } - - @Override - public boolean removeRow(Connection conn, RowItem row) - throws UnsupportedOperationException, SQLException { - // Not used in this test - return false; - } - - @Override - public String getQueryString(int offset, int limit) - throws UnsupportedOperationException { - return "SELECT * " + fromWhere; - } - - @Override - public String getCountQuery() throws UnsupportedOperationException { - return "SELECT COUNT(*) " + fromWhere; - } - - @Override - public String getContainsRowQueryString(Object... keys) - throws UnsupportedOperationException { - // Not used in this test - return null; - } - } - - public static class NonMatchingStatementDelegateWithGroupBy - extends NonMatchingDelegateWithGroupBy - implements FreeformStatementDelegate { - - @Override - public StatementHelper getQueryStatement(int offset, int limit) - throws UnsupportedOperationException { - StatementHelper sh = new StatementHelper(); - sh.setQueryString(getQueryString(offset, limit)); - return sh; - } - - @Override - public StatementHelper getCountStatement() - throws UnsupportedOperationException { - StatementHelper sh = new StatementHelper(); - sh.setQueryString(getCountQuery()); - return sh; - } - - @Override - public StatementHelper getContainsRowQueryStatement(Object... keys) - throws UnsupportedOperationException { - // Not used in this test - return null; - } - } - - @Test - public void containsRowWithKeys_delegateRegisteredGetContainsRowQueryStringNotImplemented_shouldBuildQueryString() - throws SQLException { - FreeformQuery query = new FreeformQuery( - "SELECT * FROM people WHERE \"NAME\" LIKE '%lle'", - Arrays.asList("ID"), connectionPool); - FreeformQueryDelegate delegate = EasyMock - .createMock(FreeformQueryDelegate.class); - EasyMock.expect(delegate.getContainsRowQueryString(1)) - .andThrow(new UnsupportedOperationException()); - EasyMock.replay(delegate); - query.setDelegate(delegate); - - Assert.assertTrue(query.containsRowWithKey(1)); - - EasyMock.verify(delegate); - } - - @Test - public void delegateStatementCountWithGroupBy() throws SQLException { - String dummyNotUsed = "foo"; - FreeformQuery query = new FreeformQuery(dummyNotUsed, connectionPool, - "p1.ID"); - query.setDelegate(new NonMatchingStatementDelegateWithGroupBy()); - - Assert.assertEquals(0, query.getCount()); - } - - @Test - public void delegateCountWithGroupBy() throws SQLException { - String dummyNotUsed = "foo"; - FreeformQuery query = new FreeformQuery(dummyNotUsed, connectionPool, - "p1.ID"); - query.setDelegate(new NonMatchingDelegateWithGroupBy()); - - Assert.assertEquals(0, query.getCount()); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/QueryBuilderTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/QueryBuilderTest.java deleted file mode 100644 index 7974582147..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/QueryBuilderTest.java +++ /dev/null @@ -1,315 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.query; - -import java.util.ArrayList; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.And; -import com.vaadin.data.util.filter.Between; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.filter.Compare.Greater; -import com.vaadin.data.util.filter.Compare.GreaterOrEqual; -import com.vaadin.data.util.filter.Compare.Less; -import com.vaadin.data.util.filter.Compare.LessOrEqual; -import com.vaadin.data.util.filter.IsNull; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.filter.Not; -import com.vaadin.data.util.filter.Or; -import com.vaadin.data.util.filter.SimpleStringFilter; -import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.QueryBuilder; -import com.vaadin.data.util.sqlcontainer.query.generator.filter.StringDecorator; - -public class QueryBuilderTest { - - private StatementHelper mockedStatementHelper(Object... values) { - StatementHelper sh = EasyMock.createMock(StatementHelper.class); - for (Object val : values) { - sh.addParameterValue(val); - EasyMock.expectLastCall(); - } - EasyMock.replay(sh); - return sh; - } - - // escape bad characters and wildcards - - @Test - public void getWhereStringForFilter_equals() { - StatementHelper sh = mockedStatementHelper("Fido"); - Equal f = new Equal("NAME", "Fido"); - Assert.assertEquals("\"NAME\" = ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_greater() { - StatementHelper sh = mockedStatementHelper(18); - Greater f = new Greater("AGE", 18); - Assert.assertEquals("\"AGE\" > ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_less() { - StatementHelper sh = mockedStatementHelper(65); - Less f = new Less("AGE", 65); - Assert.assertEquals("\"AGE\" < ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_greaterOrEqual() { - StatementHelper sh = mockedStatementHelper(18); - GreaterOrEqual f = new GreaterOrEqual("AGE", 18); - Assert.assertEquals("\"AGE\" >= ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_lessOrEqual() { - StatementHelper sh = mockedStatementHelper(65); - LessOrEqual f = new LessOrEqual("AGE", 65); - Assert.assertEquals("\"AGE\" <= ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_simpleStringFilter() { - StatementHelper sh = mockedStatementHelper("Vi%"); - SimpleStringFilter f = new SimpleStringFilter("NAME", "Vi", false, - true); - Assert.assertEquals("\"NAME\" LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_simpleStringFilterMatchAnywhere() { - StatementHelper sh = mockedStatementHelper("%Vi%"); - SimpleStringFilter f = new SimpleStringFilter("NAME", "Vi", false, - false); - Assert.assertEquals("\"NAME\" LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_simpleStringFilterMatchAnywhereIgnoreCase() { - StatementHelper sh = mockedStatementHelper("%VI%"); - SimpleStringFilter f = new SimpleStringFilter("NAME", "Vi", true, - false); - Assert.assertEquals("UPPER(\"NAME\") LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_startsWith() { - StatementHelper sh = mockedStatementHelper("Vi%"); - Like f = new Like("NAME", "Vi%"); - Assert.assertEquals("\"NAME\" LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_startsWithNumber() { - StatementHelper sh = mockedStatementHelper("1%"); - Like f = new Like("AGE", "1%"); - Assert.assertEquals("\"AGE\" LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_endsWith() { - StatementHelper sh = mockedStatementHelper("%lle"); - Like f = new Like("NAME", "%lle"); - Assert.assertEquals("\"NAME\" LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_contains() { - StatementHelper sh = mockedStatementHelper("%ill%"); - Like f = new Like("NAME", "%ill%"); - Assert.assertEquals("\"NAME\" LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_between() { - StatementHelper sh = mockedStatementHelper(18, 65); - Between f = new Between("AGE", 18, 65); - Assert.assertEquals("\"AGE\" BETWEEN ? AND ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_caseInsensitive_equals() { - StatementHelper sh = mockedStatementHelper("FIDO"); - Like f = new Like("NAME", "Fido"); - f.setCaseSensitive(false); - Assert.assertEquals("UPPER(\"NAME\") LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_caseInsensitive_startsWith() { - StatementHelper sh = mockedStatementHelper("VI%"); - Like f = new Like("NAME", "Vi%"); - f.setCaseSensitive(false); - Assert.assertEquals("UPPER(\"NAME\") LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_caseInsensitive_endsWith() { - StatementHelper sh = mockedStatementHelper("%LLE"); - Like f = new Like("NAME", "%lle"); - f.setCaseSensitive(false); - Assert.assertEquals("UPPER(\"NAME\") LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilter_caseInsensitive_contains() { - StatementHelper sh = mockedStatementHelper("%ILL%"); - Like f = new Like("NAME", "%ill%"); - f.setCaseSensitive(false); - Assert.assertEquals("UPPER(\"NAME\") LIKE ?", - QueryBuilder.getWhereStringForFilter(f, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_listOfFilters() { - StatementHelper sh = mockedStatementHelper("%lle", 18); - ArrayList filters = new ArrayList(); - filters.add(new Like("NAME", "%lle")); - filters.add(new Greater("AGE", 18)); - Assert.assertEquals(" WHERE \"NAME\" LIKE ? AND \"AGE\" > ?", - QueryBuilder.getWhereStringForFilters(filters, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_oneAndFilter() { - StatementHelper sh = mockedStatementHelper("%lle", 18); - ArrayList filters = new ArrayList(); - filters.add(new And(new Like("NAME", "%lle"), new Greater("AGE", 18))); - Assert.assertEquals(" WHERE (\"NAME\" LIKE ? AND \"AGE\" > ?)", - QueryBuilder.getWhereStringForFilters(filters, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_oneOrFilter() { - StatementHelper sh = mockedStatementHelper("%lle", 18); - ArrayList filters = new ArrayList(); - filters.add(new Or(new Like("NAME", "%lle"), new Greater("AGE", 18))); - Assert.assertEquals(" WHERE (\"NAME\" LIKE ? OR \"AGE\" > ?)", - QueryBuilder.getWhereStringForFilters(filters, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_complexCompoundFilters() { - StatementHelper sh = mockedStatementHelper("%lle", 18, 65, "Pelle"); - ArrayList filters = new ArrayList(); - filters.add(new Or( - new And(new Like("NAME", "%lle"), - new Or(new Less("AGE", 18), new Greater("AGE", 65))), - new Equal("NAME", "Pelle"))); - Assert.assertEquals( - " WHERE ((\"NAME\" LIKE ? AND (\"AGE\" < ? OR \"AGE\" > ?)) OR \"NAME\" = ?)", - QueryBuilder.getWhereStringForFilters(filters, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_complexCompoundFiltersAndSingleFilter() { - StatementHelper sh = mockedStatementHelper("%lle", 18, 65, "Pelle", - "Virtanen"); - ArrayList filters = new ArrayList(); - filters.add(new Or( - new And(new Like("NAME", "%lle"), - new Or(new Less("AGE", 18), new Greater("AGE", 65))), - new Equal("NAME", "Pelle"))); - filters.add(new Equal("LASTNAME", "Virtanen")); - Assert.assertEquals( - " WHERE ((\"NAME\" LIKE ? AND (\"AGE\" < ? OR \"AGE\" > ?)) OR \"NAME\" = ?) AND \"LASTNAME\" = ?", - QueryBuilder.getWhereStringForFilters(filters, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_emptyList_shouldReturnEmptyString() { - ArrayList filters = new ArrayList(); - Assert.assertEquals("", QueryBuilder.getWhereStringForFilters(filters, - new StatementHelper())); - } - - @Test - public void getWhereStringForFilters_NotFilter() { - StatementHelper sh = mockedStatementHelper(18); - ArrayList filters = new ArrayList(); - filters.add(new Not(new Equal("AGE", 18))); - Assert.assertEquals(" WHERE NOT \"AGE\" = ?", - QueryBuilder.getWhereStringForFilters(filters, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_complexNegatedFilter() { - StatementHelper sh = mockedStatementHelper(65, 18); - ArrayList filters = new ArrayList(); - filters.add( - new Not(new Or(new Equal("AGE", 65), new Equal("AGE", 18)))); - Assert.assertEquals(" WHERE NOT (\"AGE\" = ? OR \"AGE\" = ?)", - QueryBuilder.getWhereStringForFilters(filters, sh)); - EasyMock.verify(sh); - } - - @Test - public void getWhereStringForFilters_isNull() { - ArrayList filters = new ArrayList(); - filters.add(new IsNull("NAME")); - Assert.assertEquals(" WHERE \"NAME\" IS NULL", QueryBuilder - .getWhereStringForFilters(filters, new StatementHelper())); - } - - @Test - public void getWhereStringForFilters_isNotNull() { - ArrayList filters = new ArrayList(); - filters.add(new Not(new IsNull("NAME"))); - Assert.assertEquals(" WHERE \"NAME\" IS NOT NULL", QueryBuilder - .getWhereStringForFilters(filters, new StatementHelper())); - } - - @Test - public void getWhereStringForFilters_customStringDecorator() { - QueryBuilder.setStringDecorator(new StringDecorator("[", "]")); - ArrayList filters = new ArrayList(); - filters.add(new Not(new IsNull("NAME"))); - Assert.assertEquals(" WHERE [NAME] IS NOT NULL", QueryBuilder - .getWhereStringForFilters(filters, new StatementHelper())); - // Reset the default string decorator - QueryBuilder.setStringDecorator(new StringDecorator("\"", "\"")); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java deleted file mode 100644 index d2067a85b4..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java +++ /dev/null @@ -1,746 +0,0 @@ -package com.vaadin.data.util.sqlcontainer.query; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Filter; -import com.vaadin.data.util.filter.Compare.Equal; -import com.vaadin.data.util.filter.Like; -import com.vaadin.data.util.sqlcontainer.DataGenerator; -import com.vaadin.data.util.sqlcontainer.OptimisticLockException; -import com.vaadin.data.util.sqlcontainer.RowItem; -import com.vaadin.data.util.sqlcontainer.SQLContainer; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants; -import com.vaadin.data.util.sqlcontainer.SQLTestsConstants.DB; -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator; - -public class TableQueryTest { - private static final int offset = SQLTestsConstants.offset; - private JDBCConnectionPool connectionPool; - - @Before - public void setUp() throws SQLException { - try { - connectionPool = new ValidatingSimpleJDBCConnectionPool( - SQLTestsConstants.dbDriver, SQLTestsConstants.dbURL, - SQLTestsConstants.dbUser, SQLTestsConstants.dbPwd, 2, 2); - } catch (SQLException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); - } - DataGenerator.addPeopleToDatabase(connectionPool); - } - - @After - public void tearDown() { - if (connectionPool != null) { - connectionPool.destroy(); - } - } - - /********************************************************************** - * TableQuery construction tests - **********************************************************************/ - @Test - public void construction_legalParameters_shouldSucceed() { - TableQuery tQuery = new TableQuery("people", connectionPool, - new DefaultSQLGenerator()); - Assert.assertArrayEquals(new Object[] { "ID" }, - tQuery.getPrimaryKeyColumns().toArray()); - boolean correctTableName = "people" - .equalsIgnoreCase(tQuery.getTableName()); - Assert.assertTrue(correctTableName); - } - - @Test - public void construction_legalParameters_defaultGenerator_shouldSucceed() { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - Assert.assertArrayEquals(new Object[] { "ID" }, - tQuery.getPrimaryKeyColumns().toArray()); - boolean correctTableName = "people" - .equalsIgnoreCase(tQuery.getTableName()); - Assert.assertTrue(correctTableName); - } - - @Test(expected = IllegalArgumentException.class) - public void construction_nonExistingTableName_shouldFail() { - new TableQuery("skgwaguhsd", connectionPool, new DefaultSQLGenerator()); - } - - @Test(expected = IllegalArgumentException.class) - public void construction_emptyTableName_shouldFail() { - new TableQuery("", connectionPool, new DefaultSQLGenerator()); - } - - @Test(expected = IllegalArgumentException.class) - public void construction_nullSqlGenerator_shouldFail() { - new TableQuery("people", connectionPool, null); - } - - @Test(expected = IllegalArgumentException.class) - public void construction_nullConnectionPool_shouldFail() { - new TableQuery("people", null, new DefaultSQLGenerator()); - } - - /********************************************************************** - * TableQuery row count tests - **********************************************************************/ - @Test - public void getCount_simpleQuery_returnsFour() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - Assert.assertEquals(4, tQuery.getCount()); - } - - @Test - public void getCount_simpleQueryTwoMorePeopleAdded_returnsSix() - throws SQLException { - // Add some people - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - if (SQLTestsConstants.db == DB.MSSQL) { - statement.executeUpdate("insert into people values('Bengt', 30)"); - statement.executeUpdate("insert into people values('Ingvar', 50)"); - } else { - statement.executeUpdate( - "insert into people values(default, 'Bengt', 30)"); - statement.executeUpdate( - "insert into people values(default, 'Ingvar', 50)"); - } - statement.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - Assert.assertEquals(6, tQuery.getCount()); - } - - @Test - public void getCount_normalState_releasesConnection() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.getCount(); - tQuery.getCount(); - Connection c = connectionPool.reserveConnection(); - Assert.assertNotNull(c); - connectionPool.releaseConnection(c); - } - - /********************************************************************** - * TableQuery get results tests - **********************************************************************/ - @Test - public void getResults_simpleQuery_returnsFourRecords() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.beginTransaction(); - ResultSet rs = tQuery.getResults(0, 0); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(0 + offset, rs.getInt(1)); - Assert.assertEquals("Ville", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(1 + offset, rs.getInt(1)); - Assert.assertEquals("Kalle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(2 + offset, rs.getInt(1)); - Assert.assertEquals("Pelle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(3 + offset, rs.getInt(1)); - Assert.assertEquals("Börje", rs.getString(2)); - - Assert.assertFalse(rs.next()); - tQuery.commit(); - } - - @Test - public void getResults_noDelegate5000Rows_returns5000rows() - throws SQLException { - DataGenerator.addFiveThousandPeople(connectionPool); - - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - tQuery.beginTransaction(); - ResultSet rs = tQuery.getResults(0, 0); - for (int i = 0; i < 5000; i++) { - Assert.assertTrue(rs.next()); - } - Assert.assertFalse(rs.next()); - tQuery.commit(); - } - - /********************************************************************** - * TableQuery transaction management tests - **********************************************************************/ - @Test - public void beginTransaction_transactionAlreadyActive_shouldFail() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - tQuery.beginTransaction(); - try { - tQuery.beginTransaction(); - Assert.fail( - "Should throw exception when starting a transaction while already in a transaction"); - } catch (IllegalStateException e) { - // Cleanup to make test connection pool happy - tQuery.rollback(); - } - } - - @Test - public void commit_readOnly_shouldSucceed() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.beginTransaction(); - tQuery.commit(); - } - - @Test - public void rollback_readOnly_shouldSucceed() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.beginTransaction(); - tQuery.rollback(); - } - - @Test(expected = SQLException.class) - public void commit_noActiveTransaction_shouldFail() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.commit(); - } - - @Test(expected = SQLException.class) - public void rollback_noActiveTransaction_shouldFail() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.rollback(); - } - - /********************************************************************** - * TableQuery row query with given keys tests - **********************************************************************/ - @Test - public void containsRowWithKeys_existingKeys_returnsTrue() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - Assert.assertTrue(tQuery.containsRowWithKey(1)); - } - - @Test - public void containsRowWithKeys_nonexistingKeys_returnsTrue() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - Assert.assertFalse(tQuery.containsRowWithKey(1337)); - } - - @Test - public void containsRowWithKeys_invalidKeys_shouldFail() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - boolean b = true; - try { - b = tQuery.containsRowWithKey("foo"); - } catch (SQLException se) { - return; - } - Assert.assertFalse(b); - } - - @Test - public void containsRowWithKeys_nullKeys_shouldFailAndReleaseConnections() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - try { - tQuery.containsRowWithKey(new Object[] { null }); - org.junit.Assert.fail( - "null should throw an IllegalArgumentException from StatementHelper"); - } catch (IllegalArgumentException e) { - // We should now be able to reserve two connections - Connection c1 = connectionPool.reserveConnection(); - Connection c2 = connectionPool.reserveConnection(); - - // Cleanup to make test connection pool happy - connectionPool.releaseConnection(c1); - connectionPool.releaseConnection(c2); - - } - } - - /********************************************************************** - * TableQuery filtering and ordering tests - **********************************************************************/ - @Test - public void setFilters_shouldReturnCorrectCount() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - List filters = new ArrayList(); - filters.add(new Like("NAME", "%lle")); - tQuery.setFilters(filters); - Assert.assertEquals(3, tQuery.getCount()); - } - - @Test - public void setOrderByNameAscending_shouldReturnCorrectOrder() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - List orderBys = Arrays.asList(new OrderBy("NAME", true)); - tQuery.setOrderBy(orderBys); - - tQuery.beginTransaction(); - ResultSet rs; - rs = tQuery.getResults(0, 0); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(3 + offset, rs.getInt(1)); - Assert.assertEquals("Börje", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(1 + offset, rs.getInt(1)); - Assert.assertEquals("Kalle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(2 + offset, rs.getInt(1)); - Assert.assertEquals("Pelle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(0 + offset, rs.getInt(1)); - Assert.assertEquals("Ville", rs.getString(2)); - - Assert.assertFalse(rs.next()); - tQuery.commit(); - } - - @Test - public void setOrderByNameDescending_shouldReturnCorrectOrder() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - List orderBys = Arrays.asList(new OrderBy("NAME", false)); - tQuery.setOrderBy(orderBys); - - tQuery.beginTransaction(); - ResultSet rs; - rs = tQuery.getResults(0, 0); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(0 + offset, rs.getInt(1)); - Assert.assertEquals("Ville", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(2 + offset, rs.getInt(1)); - Assert.assertEquals("Pelle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(1 + offset, rs.getInt(1)); - Assert.assertEquals("Kalle", rs.getString(2)); - - Assert.assertTrue(rs.next()); - Assert.assertEquals(3 + offset, rs.getInt(1)); - Assert.assertEquals("Börje", rs.getString(2)); - - Assert.assertFalse(rs.next()); - tQuery.commit(); - } - - @Test - public void setFilters_nullParameter_shouldSucceed() { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setFilters(null); - } - - @Test - public void setOrderBy_nullParameter_shouldSucceed() { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setOrderBy(null); - } - - /********************************************************************** - * TableQuery row removal tests - **********************************************************************/ - @Test - public void removeRowThroughContainer_legalRowItem_shouldSucceed() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - SQLContainer container = new SQLContainer(tQuery); - container.setAutoCommit(false); - Assert.assertTrue( - container.removeItem(container.getItemIds().iterator().next())); - - Assert.assertEquals(4, tQuery.getCount()); - Assert.assertEquals(3, container.size()); - container.commit(); - - Assert.assertEquals(3, tQuery.getCount()); - Assert.assertEquals(3, container.size()); - } - - @Test - public void removeRowThroughContainer_nonexistingRowId_shouldFail() - throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - SQLContainer container = new SQLContainer(tQuery); - container.setAutoCommit(true); - Assert.assertFalse(container.removeItem("foo")); - } - - /********************************************************************** - * TableQuery row adding / modification tests - **********************************************************************/ - @Test - public void insertRowThroughContainer_shouldSucceed() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setVersionColumn("ID"); - - SQLContainer container = new SQLContainer(tQuery); - container.setAutoCommit(false); - - Object item = container.addItem(); - Assert.assertNotNull(item); - - Assert.assertEquals(4, tQuery.getCount()); - Assert.assertEquals(5, container.size()); - container.commit(); - - Assert.assertEquals(5, tQuery.getCount()); - Assert.assertEquals(5, container.size()); - } - - @Test - public void modifyRowThroughContainer_shouldSucceed() throws SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - - // In this test the primary key is used as a version column - tQuery.setVersionColumn("ID"); - SQLContainer container = new SQLContainer(tQuery); - container.setAutoCommit(false); - - /* Check that the container size is correct and there is no 'Viljami' */ - Assert.assertEquals(4, container.size()); - List filters = new ArrayList(); - filters.add(new Equal("NAME", "Viljami")); - tQuery.setFilters(filters); - Assert.assertEquals(0, tQuery.getCount()); - tQuery.setFilters(null); - - /* Fetch first item, modify and commit */ - Object item = container - .getItem(container.getItemIds().iterator().next()); - Assert.assertNotNull(item); - - RowItem ri = (RowItem) item; - Assert.assertNotNull(ri.getItemProperty("NAME")); - ri.getItemProperty("NAME").setValue("Viljami"); - - container.commit(); - - // Check that the size is still correct and only 1 'Viljami' is found - Assert.assertEquals(4, tQuery.getCount()); - Assert.assertEquals(4, container.size()); - tQuery.setFilters(filters); - Assert.assertEquals(1, tQuery.getCount()); - } - - @Test - public void storeRow_noVersionColumn_shouldSucceed() - throws UnsupportedOperationException, SQLException { - TableQuery tQuery = new TableQuery("people", connectionPool, - SQLTestsConstants.sqlGen); - SQLContainer container = new SQLContainer(tQuery); - Object id = container.addItem(); - RowItem row = (RowItem) container.getItem(id); - row.getItemProperty("NAME").setValue("R2D2"); - row.getItemProperty("AGE").setValue(123); - tQuery.beginTransaction(); - tQuery.storeRow(row); - tQuery.commit(); - - Connection conn = connectionPool.reserveConnection(); - PreparedStatement stmt = conn - .prepareStatement("SELECT * FROM PEOPLE WHERE \"NAME\" = ?"); - stmt.setString(1, "R2D2"); - ResultSet rs = stmt.executeQuery(); - Assert.assertTrue(rs.next()); - rs.close(); - stmt.close(); - connectionPool.releaseConnection(conn); - } - - @Test - public void storeRow_versionSetAndEqualToDBValue_shouldSucceed() - throws SQLException { - DataGenerator.addVersionedData(connectionPool); - - TableQuery tQuery = new TableQuery("versioned", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setVersionColumn("VERSION"); - SQLContainer container = new SQLContainer(tQuery); - RowItem row = (RowItem) container.getItem(container.firstItemId()); - Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue()); - - row.getItemProperty("TEXT").setValue("asdf"); - container.commit(); - - Connection conn = connectionPool.reserveConnection(); - PreparedStatement stmt = conn - .prepareStatement("SELECT * FROM VERSIONED WHERE \"TEXT\" = ?"); - stmt.setString(1, "asdf"); - ResultSet rs = stmt.executeQuery(); - Assert.assertTrue(rs.next()); - rs.close(); - stmt.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - } - - @Test(expected = OptimisticLockException.class) - public void storeRow_versionSetAndLessThanDBValue_shouldThrowException() - throws SQLException { - if (SQLTestsConstants.db == DB.HSQLDB) { - throw new OptimisticLockException( - "HSQLDB doesn't support row versioning for optimistic locking - don't run this test.", - null); - } - DataGenerator.addVersionedData(connectionPool); - - TableQuery tQuery = new TableQuery("versioned", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setVersionColumn("VERSION"); - SQLContainer container = new SQLContainer(tQuery); - RowItem row = (RowItem) container.getItem(container.firstItemId()); - Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue()); - - row.getItemProperty("TEXT").setValue("asdf"); - - // Update the version using another connection. - Connection conn = connectionPool.reserveConnection(); - PreparedStatement stmt = conn.prepareStatement( - "UPDATE VERSIONED SET \"TEXT\" = ? WHERE \"ID\" = ?"); - stmt.setString(1, "foo"); - stmt.setObject(2, row.getItemProperty("ID").getValue()); - stmt.executeUpdate(); - stmt.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - - container.commit(); - } - - @Test - public void removeRow_versionSetAndEqualToDBValue_shouldSucceed() - throws SQLException { - DataGenerator.addVersionedData(connectionPool); - - TableQuery tQuery = new TableQuery("versioned", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setVersionColumn("VERSION"); - SQLContainer container = new SQLContainer(tQuery); - RowItem row = (RowItem) container.getItem(container.firstItemId()); - Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue()); - - container.removeItem(container.firstItemId()); - container.commit(); - - Connection conn = connectionPool.reserveConnection(); - PreparedStatement stmt = conn - .prepareStatement("SELECT * FROM VERSIONED WHERE \"TEXT\" = ?"); - stmt.setString(1, "Junk"); - ResultSet rs = stmt.executeQuery(); - Assert.assertFalse(rs.next()); - rs.close(); - stmt.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - } - - @Test(expected = OptimisticLockException.class) - public void removeRow_versionSetAndLessThanDBValue_shouldThrowException() - throws SQLException { - if (SQLTestsConstants.db == SQLTestsConstants.DB.HSQLDB) { - // HSQLDB doesn't support versioning, so this is to make the test - // green. - throw new OptimisticLockException(null); - } - DataGenerator.addVersionedData(connectionPool); - - TableQuery tQuery = new TableQuery("versioned", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setVersionColumn("VERSION"); - SQLContainer container = new SQLContainer(tQuery); - RowItem row = (RowItem) container.getItem(container.firstItemId()); - Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue()); - - // Update the version using another connection. - Connection conn = connectionPool.reserveConnection(); - PreparedStatement stmt = conn.prepareStatement( - "UPDATE VERSIONED SET \"TEXT\" = ? WHERE \"ID\" = ?"); - stmt.setString(1, "asdf"); - stmt.setObject(2, row.getItemProperty("ID").getValue()); - stmt.executeUpdate(); - stmt.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - - container.removeItem(container.firstItemId()); - container.commit(); - } - - @Test - public void removeRow_throwsOptimisticLockException_shouldStillWork() - throws SQLException { - if (SQLTestsConstants.db == SQLTestsConstants.DB.HSQLDB) { - // HSQLDB doesn't support versioning, so this is to make the test - // green. - return; - } - DataGenerator.addVersionedData(connectionPool); - - TableQuery tQuery = new TableQuery("versioned", connectionPool, - SQLTestsConstants.sqlGen); - tQuery.setVersionColumn("VERSION"); - SQLContainer container = new SQLContainer(tQuery); - RowItem row = (RowItem) container.getItem(container.firstItemId()); - Assert.assertEquals("Junk", row.getItemProperty("TEXT").getValue()); - - // Update the version using another connection. - Connection conn = connectionPool.reserveConnection(); - PreparedStatement stmt = conn.prepareStatement( - "UPDATE VERSIONED SET \"TEXT\" = ? WHERE \"ID\" = ?"); - stmt.setString(1, "asdf"); - stmt.setObject(2, row.getItemProperty("ID").getValue()); - stmt.executeUpdate(); - stmt.close(); - conn.commit(); - connectionPool.releaseConnection(conn); - - Object itemToRemove = container.firstItemId(); - try { - container.removeItem(itemToRemove); - container.commit(); - } catch (OptimisticLockException e) { - // This is expected, refresh and try again. - container.rollback(); - container.removeItem(itemToRemove); - container.commit(); - } - Object id = container.addItem(); - RowItem item = (RowItem) container.getItem(id); - item.getItemProperty("TEXT").setValue("foo"); - container.commit(); - } - - @Test - public void construction_explicitSchema_shouldSucceed() - throws SQLException { - if (SQLTestsConstants.createSchema == null - || SQLTestsConstants.createProductTable == null - || SQLTestsConstants.dropSchema == null) { - // only perform the test on the databases for which the setup and - // cleanup statements are available - return; - } - - // create schema "oaas" and table "product" in it - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - try { - statement.execute(SQLTestsConstants.dropSchema); - } catch (SQLException e) { - // May fail if schema doesn't exist, which is OK. - conn.rollback(); - } - statement.execute(SQLTestsConstants.createSchema); - statement.execute(SQLTestsConstants.createProductTable); - conn.commit(); - - try { - // metadata scanning at query creation time should not fail - TableQuery tq1 = new TableQuery(null, "oaas", "product", - connectionPool, SQLTestsConstants.sqlGen); - Assert.assertNotNull(tq1); - } finally { - // cleanup - might not be an in-memory DB - statement.execute(SQLTestsConstants.dropSchema); - } - - // Cleanup to make test connection pool happy - connectionPool.releaseConnection(conn); - } - - @Test - public void construction_explicitCatalogAndSchema_shouldSucceed() - throws SQLException { - // not all databases support explicit catalogs, test with PostgreSQL - // using database name as catalog - if (SQLTestsConstants.db != SQLTestsConstants.DB.POSTGRESQL - || SQLTestsConstants.createSchema == null - || SQLTestsConstants.createProductTable == null - || SQLTestsConstants.dropSchema == null) { - // only perform the test on the databases for which the setup and - // cleanup statements are available - return; - } - - // create schema "oaas" and table "product" in it - Connection conn = connectionPool.reserveConnection(); - Statement statement = conn.createStatement(); - try { - statement.execute(SQLTestsConstants.dropSchema); - } catch (SQLException e) { - // May fail if schema doesn't exist, which is OK. - conn.rollback(); - } - statement.execute(SQLTestsConstants.createSchema); - statement.execute(SQLTestsConstants.createProductTable); - conn.commit(); - - try { - // metadata scanning at query creation time should not fail - // note that for most DBMS, catalog is just an optional database - // name - TableQuery tq1 = new TableQuery("sqlcontainer", "oaas", "product", - connectionPool, SQLTestsConstants.sqlGen); - Assert.assertNotNull(tq1); - } finally { - // cleanup - might not be an in-memory DB - statement.execute(SQLTestsConstants.dropSchema); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/ValidatingSimpleJDBCConnectionPool.java b/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/ValidatingSimpleJDBCConnectionPool.java deleted file mode 100644 index 8a6ee0aa45..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/data/util/sqlcontainer/query/ValidatingSimpleJDBCConnectionPool.java +++ /dev/null @@ -1,88 +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.sqlcontainer.query; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Logger; - -import com.vaadin.data.util.sqlcontainer.connection.JDBCConnectionPool; -import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool; - -/** - * Connection pool for testing SQLContainer. Ensures that only reserved - * connections are released and that all connections are released before the - * pool is destroyed. - * - * @author Vaadin Ltd - */ -public class ValidatingSimpleJDBCConnectionPool implements JDBCConnectionPool { - - private JDBCConnectionPool realPool; - private Set reserved = new HashSet(); - private Set alreadyReleased = new HashSet(); - - public ValidatingSimpleJDBCConnectionPool(String driverName, - String connectionUri, String userName, String password, - int initialConnections, int maxConnections) throws SQLException { - realPool = new SimpleJDBCConnectionPool(driverName, connectionUri, - userName, password, initialConnections, maxConnections); - } - - @Deprecated - public JDBCConnectionPool getRealPool() { - return realPool; - } - - @Override - public Connection reserveConnection() throws SQLException { - Connection c = realPool.reserveConnection(); - reserved.add(c); - return c; - } - - @Override - public void releaseConnection(Connection conn) { - if (conn != null && !reserved.remove(conn)) { - if (alreadyReleased.contains(conn)) { - getLogger().severe("Tried to release connection (" + conn - + ") which has already been released"); - } else { - throw new RuntimeException("Tried to release connection (" - + conn + ") not reserved using reserveConnection"); - } - } - realPool.releaseConnection(conn); - alreadyReleased.add(conn); - - } - - @Override - public void destroy() { - realPool.destroy(); - if (!reserved.isEmpty()) { - throw new RuntimeException( - reserved.size() + " connections never released"); - } - } - - private static Logger getLogger() { - return Logger - .getLogger(ValidatingSimpleJDBCConnectionPool.class.getName()); - } -} \ No newline at end of file diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigDecimalRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigDecimalRangeValidatorTest.java deleted file mode 100644 index 3fe6d1f6ff..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigDecimalRangeValidatorTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.vaadin.tests.data.validator; - -import java.math.BigDecimal; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyBigDecimalRangeValidator; - -public class BigDecimalRangeValidatorTest { - - private LegacyBigDecimalRangeValidator cleanValidator = new LegacyBigDecimalRangeValidator( - "no values", null, null); - private LegacyBigDecimalRangeValidator minValidator = new LegacyBigDecimalRangeValidator( - "no values", new BigDecimal(10.1), null); - private LegacyBigDecimalRangeValidator maxValidator = new LegacyBigDecimalRangeValidator( - "no values", null, new BigDecimal(100.1)); - private LegacyBigDecimalRangeValidator minMaxValidator = new LegacyBigDecimalRangeValidator( - "no values", new BigDecimal(10.5), new BigDecimal(100.5)); - - @Test - public void testNullValue() { - Assert.assertTrue("Didn't accept null", cleanValidator.isValid(null)); - Assert.assertTrue("Didn't accept null", minValidator.isValid(null)); - Assert.assertTrue("Didn't accept null", maxValidator.isValid(null)); - Assert.assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - Assert.assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(new BigDecimal(-15.0))); - Assert.assertTrue("Didn't accept valid value", - minValidator.isValid(new BigDecimal(10.1))); - Assert.assertFalse("Accepted too small value", - minValidator.isValid(new BigDecimal(10.0))); - } - - @Test - public void testMaxValue() { - Assert.assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(new BigDecimal(1120.0))); - Assert.assertTrue("Didn't accept valid value", - maxValidator.isValid(new BigDecimal(15.0))); - Assert.assertFalse("Accepted too large value", - maxValidator.isValid(new BigDecimal(100.6))); - } - - @Test - public void testMinMaxValue() { - Assert.assertTrue("Didn't accept valid value", - minMaxValidator.isValid(new BigDecimal(10.5))); - Assert.assertTrue("Didn't accept valid value", - minMaxValidator.isValid(new BigDecimal(100.5))); - Assert.assertFalse("Accepted too small value", - minMaxValidator.isValid(new BigDecimal(10.4))); - Assert.assertFalse("Accepted too large value", - minMaxValidator.isValid(new BigDecimal(100.6))); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigIntegerRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigIntegerRangeValidatorTest.java deleted file mode 100644 index c54e868e64..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/BigIntegerRangeValidatorTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.vaadin.tests.data.validator; - -import java.math.BigInteger; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyBigIntegerRangeValidator; - -public class BigIntegerRangeValidatorTest { - - private LegacyBigIntegerRangeValidator cleanValidator = new LegacyBigIntegerRangeValidator( - "no values", null, null); - private LegacyBigIntegerRangeValidator minValidator = new LegacyBigIntegerRangeValidator( - "no values", BigInteger.valueOf(10), null); - private LegacyBigIntegerRangeValidator maxValidator = new LegacyBigIntegerRangeValidator( - "no values", null, BigInteger.valueOf(100)); - private LegacyBigIntegerRangeValidator minMaxValidator = new LegacyBigIntegerRangeValidator( - "no values", BigInteger.valueOf(10), BigInteger.valueOf(100)); - - @Test - public void testNullValue() { - Assert.assertTrue("Didn't accept null", cleanValidator.isValid(null)); - Assert.assertTrue("Didn't accept null", minValidator.isValid(null)); - Assert.assertTrue("Didn't accept null", maxValidator.isValid(null)); - Assert.assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - Assert.assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(BigInteger.valueOf(-15))); - Assert.assertTrue("Didn't accept valid value", - minValidator.isValid(BigInteger.valueOf(15))); - Assert.assertFalse("Accepted too small value", - minValidator.isValid(BigInteger.valueOf(9))); - } - - @Test - public void testMaxValue() { - Assert.assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(BigInteger.valueOf(1120))); - Assert.assertTrue("Didn't accept valid value", - maxValidator.isValid(BigInteger.valueOf(15))); - Assert.assertFalse("Accepted too large value", - maxValidator.isValid(BigInteger.valueOf(120))); - } - - @Test - public void testMinMaxValue() { - Assert.assertTrue("Didn't accept valid value", - minMaxValidator.isValid(BigInteger.valueOf(15))); - Assert.assertTrue("Didn't accept valid value", - minMaxValidator.isValid(BigInteger.valueOf(99))); - Assert.assertFalse("Accepted too small value", - minMaxValidator.isValid(BigInteger.valueOf(9))); - Assert.assertFalse("Accepted too large value", - minMaxValidator.isValid(BigInteger.valueOf(110))); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/ByteRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/ByteRangeValidatorTest.java deleted file mode 100644 index fa6dd8f098..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/ByteRangeValidatorTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyByteRangeValidator; - -public class ByteRangeValidatorTest { - - private LegacyByteRangeValidator cleanValidator = new LegacyByteRangeValidator( - "no values", null, null); - private LegacyByteRangeValidator minValidator = new LegacyByteRangeValidator( - "no values", (byte) 10, null); - private LegacyByteRangeValidator maxValidator = new LegacyByteRangeValidator( - "no values", null, (byte) 100); - private LegacyByteRangeValidator minMaxValidator = new LegacyByteRangeValidator( - "no values", (byte) 10, (byte) 100); - - @Test - public void testNullValue() { - assertTrue("Didn't accept null", cleanValidator.isValid(null)); - assertTrue("Didn't accept null", minValidator.isValid(null)); - assertTrue("Didn't accept null", maxValidator.isValid(null)); - assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid((byte) -15)); - assertTrue("Didn't accept valid value", - minValidator.isValid((byte) 15)); - assertFalse("Accepted too small value", minValidator.isValid((byte) 9)); - } - - @Test - public void testMaxValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid((byte) 112)); - assertTrue("Didn't accept valid value", - maxValidator.isValid((byte) 15)); - assertFalse("Accepted too large value", - maxValidator.isValid((byte) 120)); - } - - @Test - public void testMinMaxValue() { - assertTrue("Didn't accept valid value", - minMaxValidator.isValid((byte) 15)); - assertTrue("Didn't accept valid value", - minMaxValidator.isValid((byte) 99)); - assertFalse("Accepted too small value", - minMaxValidator.isValid((byte) 9)); - assertFalse("Accepted too large value", - minMaxValidator.isValid((byte) 110)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/CompositeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/CompositeValidatorTest.java deleted file mode 100644 index 2975331e8a..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/CompositeValidatorTest.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.vaadin.tests.data.validator; - -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.v7.data.Validator; -import com.vaadin.v7.data.validator.LegacyCompositeValidator; -import com.vaadin.v7.data.validator.LegacyCompositeValidator.CombinationMode; -import com.vaadin.v7.data.validator.LegacyEmailValidator; -import com.vaadin.v7.data.validator.LegacyRegexpValidator; - -public class CompositeValidatorTest { - - LegacyCompositeValidator and = new LegacyCompositeValidator( - CombinationMode.AND, "One validator not valid"); - LegacyCompositeValidator or = new LegacyCompositeValidator( - CombinationMode.OR, "No validators are valid"); - LegacyEmailValidator email = new LegacyEmailValidator("Faulty email"); - LegacyRegexpValidator regex = new LegacyRegexpValidator("@mail.com", false, - "Partial match validator error"); - - @Before - public void setUp() { - and.addValidator(email); - and.addValidator(regex); - - or.addValidator(email); - or.addValidator(regex); - } - - @Test - public void testCorrectValue() { - String testString = "user@mail.com"; - assertTrue(email.isValid(testString)); - assertTrue(regex.isValid(testString)); - try { - // notNull.validate(null); - // fail("expected null to fail with an exception"); - and.validate(testString); - } catch (Validator.InvalidValueException ex) { - // assertEquals("Null not accepted", ex.getMessage()); - fail("And validator should be valid"); - } - try { - or.validate(testString); - } catch (Validator.InvalidValueException ex) { - // assertEquals("Null not accepted", ex.getMessage()); - fail("And validator should be valid"); - } - } - - @Test - public void testCorrectRegex() { - - String testString = "@mail.com"; - assertFalse(testString + " should not validate", - email.isValid(testString)); - assertTrue(testString + "should validate", regex.isValid(testString)); - try { - // notNull.validate(null); - and.validate(testString); - fail("expected and to fail with an exception"); - } catch (Validator.InvalidValueException ex) { - assertEquals("Faulty email", ex.getMessage()); - // fail("And validator should be valid"); - } - try { - or.validate(testString); - } catch (Validator.InvalidValueException ex) { - // assertEquals("Null not accepted", ex.getMessage()); - fail("Or validator should be valid"); - } - } - - @Test - public void testCorrectEmail() { - - String testString = "user@gmail.com"; - - assertTrue(testString + " should validate", email.isValid(testString)); - assertFalse(testString + " should not validate", - regex.isValid(testString)); - try { - and.validate(testString); - fail("expected and to fail with an exception"); - } catch (Validator.InvalidValueException ex) { - assertEquals("Partial match validator error", ex.getMessage()); - } - try { - or.validate(testString); - } catch (Validator.InvalidValueException ex) { - fail("Or validator should be valid"); - } - } - - @Test - public void testBothFaulty() { - - String testString = "gmail.com"; - - assertFalse(testString + " should not validate", - email.isValid(testString)); - assertFalse(testString + " should not validate", - regex.isValid(testString)); - try { - and.validate(testString); - fail("expected and to fail with an exception"); - } catch (Validator.InvalidValueException ex) { - assertEquals("Faulty email", ex.getMessage()); - } - try { - or.validate(testString); - fail("expected or to fail with an exception"); - } catch (Validator.InvalidValueException ex) { - assertEquals("No validators are valid", ex.getMessage()); - } - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/DateRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/DateRangeValidatorTest.java deleted file mode 100644 index a0afc9c617..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/DateRangeValidatorTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.shared.ui.datefield.Resolution; -import com.vaadin.v7.data.validator.LegacyDateRangeValidator; - -public class DateRangeValidatorTest { - Calendar startDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"), - Locale.ENGLISH); - Calendar endDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"), - Locale.ENGLISH); - - private LegacyDateRangeValidator cleanValidator; - private LegacyDateRangeValidator minValidator; - private LegacyDateRangeValidator maxValidator; - private LegacyDateRangeValidator minMaxValidator; - - @Before - public void setUp() { - startDate.set(2000, Calendar.JANUARY, 1, 12, 0, 0); - endDate.set(2000, Calendar.FEBRUARY, 20, 12, 0, 0); - - cleanValidator = new LegacyDateRangeValidator( - "Given date outside range", null, null, Resolution.DAY); - minValidator = new LegacyDateRangeValidator( - "Given date before startDate", startDate.getTime(), null, - Resolution.DAY); - maxValidator = new LegacyDateRangeValidator("Given date after endDate", - null, endDate.getTime(), Resolution.DAY); - minMaxValidator = new LegacyDateRangeValidator( - "Given date outside range", startDate.getTime(), - endDate.getTime(), Resolution.DAY); - } - - @Test - public void testNullValue() { - assertTrue("Didn't accept null", cleanValidator.isValid(null)); - assertTrue("Didn't accept null", minValidator.isValid(null)); - assertTrue("Didn't accept null", maxValidator.isValid(null)); - assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"), - Locale.ENGLISH); - cal.setTime(startDate.getTime()); - cal.add(Calendar.SECOND, 1); - - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(cal.getTime())); - assertTrue("Didn't accept valid value", - minValidator.isValid(cal.getTime())); - - cal.add(Calendar.SECOND, -3); - - assertFalse("Accepted too small value", - minValidator.isValid(cal.getTime())); - } - - @Test - public void testMaxValue() { - Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"), - Locale.ENGLISH); - cal.setTime(endDate.getTime()); - cal.add(Calendar.SECOND, -1); - - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(cal.getTime())); - assertTrue("Didn't accept valid value", - maxValidator.isValid(cal.getTime())); - - cal.add(Calendar.SECOND, 2); - assertFalse("Accepted too large value", - maxValidator.isValid(cal.getTime())); - } - - @Test - public void testMinMaxValue() { - Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"), - Locale.ENGLISH); - cal.setTime(endDate.getTime()); - - assertTrue("Didn't accept valid value", - minMaxValidator.isValid(cal.getTime())); - cal.add(Calendar.SECOND, 1); - assertFalse("Accepted too large value", - minMaxValidator.isValid(cal.getTime())); - cal.setTime(startDate.getTime()); - assertTrue("Didn't accept valid value", - minMaxValidator.isValid(cal.getTime())); - cal.add(Calendar.SECOND, -1); - assertFalse("Accepted too small value", - minMaxValidator.isValid(cal.getTime())); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/DoubleRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/DoubleRangeValidatorTest.java deleted file mode 100644 index ec3a34bfa6..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/DoubleRangeValidatorTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyDoubleRangeValidator; - -public class DoubleRangeValidatorTest { - - private LegacyDoubleRangeValidator cleanValidator = new LegacyDoubleRangeValidator( - "no values", null, null); - private LegacyDoubleRangeValidator minValidator = new LegacyDoubleRangeValidator( - "no values", 10.1, null); - private LegacyDoubleRangeValidator maxValidator = new LegacyDoubleRangeValidator( - "no values", null, 100.1); - private LegacyDoubleRangeValidator minMaxValidator = new LegacyDoubleRangeValidator( - "no values", 10.5, 100.5); - - @Test - public void testNullValue() { - assertTrue("Didn't accept null", cleanValidator.isValid(null)); - assertTrue("Didn't accept null", minValidator.isValid(null)); - assertTrue("Didn't accept null", maxValidator.isValid(null)); - assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(-15.0)); - assertTrue("Didn't accept valid value", minValidator.isValid(10.1)); - assertFalse("Accepted too small value", minValidator.isValid(10.0)); - } - - @Test - public void testMaxValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(1120.0)); - assertTrue("Didn't accept valid value", maxValidator.isValid(15.0)); - assertFalse("Accepted too large value", maxValidator.isValid(100.6)); - } - - @Test - public void testMinMaxValue() { - assertTrue("Didn't accept valid value", minMaxValidator.isValid(10.5)); - assertTrue("Didn't accept valid value", minMaxValidator.isValid(100.5)); - assertFalse("Accepted too small value", minMaxValidator.isValid(10.4)); - assertFalse("Accepted too large value", minMaxValidator.isValid(100.6)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/EmailValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/EmailValidatorTest.java deleted file mode 100644 index 92eef3d4b8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/EmailValidatorTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.vaadin.tests.data.validator; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyEmailValidator; - -public class EmailValidatorTest { - - private LegacyEmailValidator validator = new LegacyEmailValidator("Error"); - - @Test - public void testEmailValidatorWithNull() { - Assert.assertTrue(validator.isValid(null)); - } - - @Test - public void testEmailValidatorWithEmptyString() { - Assert.assertTrue(validator.isValid("")); - } - - @Test - public void testEmailValidatorWithFaultyString() { - Assert.assertFalse(validator.isValid("not.an.email")); - } - - @Test - public void testEmailValidatorWithOkEmail() { - Assert.assertTrue(validator.isValid("my.name@email.com")); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/FloatRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/FloatRangeValidatorTest.java deleted file mode 100644 index 36bd41864c..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/FloatRangeValidatorTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyFloatRangeValidator; - -public class FloatRangeValidatorTest { - - private LegacyFloatRangeValidator cleanValidator = new LegacyFloatRangeValidator( - "no values", null, null); - private LegacyFloatRangeValidator minValidator = new LegacyFloatRangeValidator( - "no values", 10.1f, null); - private LegacyFloatRangeValidator maxValidator = new LegacyFloatRangeValidator( - "no values", null, 100.1f); - private LegacyFloatRangeValidator minMaxValidator = new LegacyFloatRangeValidator( - "no values", 10.5f, 100.5f); - - @Test - public void testNullValue() { - assertTrue("Didn't accept null", cleanValidator.isValid(null)); - assertTrue("Didn't accept null", minValidator.isValid(null)); - assertTrue("Didn't accept null", maxValidator.isValid(null)); - assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(-15.0f)); - assertTrue("Didn't accept valid value", minValidator.isValid(10.1f)); - assertFalse("Accepted too small value", minValidator.isValid(10.0f)); - } - - @Test - public void testMaxValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(1120.0f)); - assertTrue("Didn't accept valid value", maxValidator.isValid(15.0f)); - assertFalse("Accepted too large value", maxValidator.isValid(100.6f)); - } - - @Test - public void testMinMaxValue() { - assertTrue("Didn't accept valid value", minMaxValidator.isValid(10.5f)); - assertTrue("Didn't accept valid value", - minMaxValidator.isValid(100.5f)); - assertFalse("Accepted too small value", minMaxValidator.isValid(10.4f)); - assertFalse("Accepted too large value", - minMaxValidator.isValid(100.6f)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/IntegerRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/IntegerRangeValidatorTest.java deleted file mode 100644 index 595d5f4ab8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/IntegerRangeValidatorTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyIntegerRangeValidator; - -public class IntegerRangeValidatorTest { - - private LegacyIntegerRangeValidator cleanValidator = new LegacyIntegerRangeValidator( - "no values", null, null); - private LegacyIntegerRangeValidator minValidator = new LegacyIntegerRangeValidator( - "no values", 10, null); - private LegacyIntegerRangeValidator maxValidator = new LegacyIntegerRangeValidator( - "no values", null, 100); - private LegacyIntegerRangeValidator minMaxValidator = new LegacyIntegerRangeValidator( - "no values", 10, 100); - - @Test - public void testNullValue() { - assertTrue("Didn't accept null", cleanValidator.isValid(null)); - assertTrue("Didn't accept null", minValidator.isValid(null)); - assertTrue("Didn't accept null", maxValidator.isValid(null)); - assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(-15)); - assertTrue("Didn't accept valid value", minValidator.isValid(15)); - assertFalse("Accepted too small value", minValidator.isValid(9)); - } - - @Test - public void testMaxValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(1120)); - assertTrue("Didn't accept valid value", maxValidator.isValid(15)); - assertFalse("Accepted too large value", maxValidator.isValid(120)); - } - - @Test - public void testMinMaxValue() { - assertTrue("Didn't accept valid value", minMaxValidator.isValid(15)); - assertTrue("Didn't accept valid value", minMaxValidator.isValid(99)); - assertFalse("Accepted too small value", minMaxValidator.isValid(9)); - assertFalse("Accepted too large value", minMaxValidator.isValid(110)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/LongRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/LongRangeValidatorTest.java deleted file mode 100644 index 6ecd21984c..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/LongRangeValidatorTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyLongRangeValidator; - -public class LongRangeValidatorTest { - - private LegacyLongRangeValidator cleanValidator = new LegacyLongRangeValidator( - "no values", null, null); - private LegacyLongRangeValidator minValidator = new LegacyLongRangeValidator( - "no values", 10l, null); - private LegacyLongRangeValidator maxValidator = new LegacyLongRangeValidator( - "no values", null, 100l); - private LegacyLongRangeValidator minMaxValidator = new LegacyLongRangeValidator( - "no values", 10l, 100l); - - @Test - public void testNullValue() { - assertTrue("Didn't accept null", cleanValidator.isValid(null)); - assertTrue("Didn't accept null", minValidator.isValid(null)); - assertTrue("Didn't accept null", maxValidator.isValid(null)); - assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(-15l)); - assertTrue("Didn't accept valid value", minValidator.isValid(15l)); - assertFalse("Accepted too small value", minValidator.isValid(9l)); - } - - @Test - public void testMaxValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid(1120l)); - assertTrue("Didn't accept valid value", maxValidator.isValid(15l)); - assertFalse("Accepted too large value", maxValidator.isValid(120l)); - } - - @Test - public void testMinMaxValue() { - assertTrue("Didn't accept valid value", minMaxValidator.isValid(15l)); - assertTrue("Didn't accept valid value", minMaxValidator.isValid(99l)); - assertFalse("Accepted too small value", minMaxValidator.isValid(9l)); - assertFalse("Accepted too large value", minMaxValidator.isValid(110l)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/NullValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/NullValidatorTest.java deleted file mode 100644 index 6e43b30cdf..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/NullValidatorTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import org.junit.Test; - -import com.vaadin.v7.data.Validator; -import com.vaadin.v7.data.validator.LegacyNullValidator; - -public class NullValidatorTest { - - LegacyNullValidator notNull = new LegacyNullValidator("Null not accepted", - false); - LegacyNullValidator onlyNull = new LegacyNullValidator("Only null accepted", - true); - - @Test - public void testNullValue() { - try { - notNull.validate(null); - fail("expected null to fail with an exception"); - } catch (Validator.InvalidValueException ex) { - assertEquals("Null not accepted", ex.getMessage()); - } - try { - onlyNull.validate(null); - } catch (Validator.InvalidValueException ex) { - fail("onlyNull should not throw exception for null"); - } - } - - @Test - public void testNonNullValue() { - try { - onlyNull.validate("Not a null value"); - fail("expected onlyNull validator to fail with an exception"); - } catch (Validator.InvalidValueException ex) { - assertEquals("Only null accepted", ex.getMessage()); - } - try { - notNull.validate("Not a null value"); - } catch (Validator.InvalidValueException ex) { - fail("notNull should not throw exception for \"Not a null value\""); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/RegexpValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/RegexpValidatorTest.java deleted file mode 100644 index 1768999969..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/RegexpValidatorTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyRegexpValidator; - -public class RegexpValidatorTest { - - private LegacyRegexpValidator completeValidator = new LegacyRegexpValidator( - "pattern", true, "Complete match validator error"); - private LegacyRegexpValidator partialValidator = new LegacyRegexpValidator( - "pattern", false, "Partial match validator error"); - - @Test - public void testRegexpValidatorWithNull() { - assertTrue(completeValidator.isValid(null)); - assertTrue(partialValidator.isValid(null)); - } - - @Test - public void testRegexpValidatorWithEmptyString() { - assertTrue(completeValidator.isValid("")); - assertTrue(partialValidator.isValid("")); - } - - @Test - public void testCompleteRegexpValidatorWithFaultyString() { - assertFalse(completeValidator.isValid("mismatch")); - assertFalse(completeValidator.isValid("pattern2")); - assertFalse(completeValidator.isValid("1pattern")); - } - - @Test - public void testCompleteRegexpValidatorWithOkString() { - assertTrue(completeValidator.isValid("pattern")); - } - - @Test - public void testPartialRegexpValidatorWithFaultyString() { - assertFalse(partialValidator.isValid("mismatch")); - } - - @Test - public void testPartialRegexpValidatorWithOkString() { - assertTrue(partialValidator.isValid("pattern")); - assertTrue(partialValidator.isValid("1pattern")); - assertTrue(partialValidator.isValid("pattern2")); - assertTrue(partialValidator.isValid("1pattern2")); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/ShortRangeValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/ShortRangeValidatorTest.java deleted file mode 100644 index 41c3e67942..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/ShortRangeValidatorTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyShortRangeValidator; - -public class ShortRangeValidatorTest { - - private LegacyShortRangeValidator cleanValidator = new LegacyShortRangeValidator( - "no values", null, null); - private LegacyShortRangeValidator minValidator = new LegacyShortRangeValidator( - "no values", (short) 10, null); - private LegacyShortRangeValidator maxValidator = new LegacyShortRangeValidator( - "no values", null, (short) 100); - private LegacyShortRangeValidator minMaxValidator = new LegacyShortRangeValidator( - "no values", (short) 10, (short) 100); - - @Test - public void testNullValue() { - assertTrue("Didn't accept null", cleanValidator.isValid(null)); - assertTrue("Didn't accept null", minValidator.isValid(null)); - assertTrue("Didn't accept null", maxValidator.isValid(null)); - assertTrue("Didn't accept null", minMaxValidator.isValid(null)); - } - - @Test - public void testMinValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid((short) -15)); - assertTrue("Didn't accept valid value", - minValidator.isValid((short) 15)); - assertFalse("Accepted too small value", - minValidator.isValid((short) 9)); - } - - @Test - public void testMaxValue() { - assertTrue("Validator without ranges didn't accept value", - cleanValidator.isValid((short) 1120)); - assertTrue("Didn't accept valid value", - maxValidator.isValid((short) 15)); - assertFalse("Accepted too large value", - maxValidator.isValid((short) 120)); - } - - @Test - public void testMinMaxValue() { - assertTrue("Didn't accept valid value", - minMaxValidator.isValid((short) 15)); - assertTrue("Didn't accept valid value", - minMaxValidator.isValid((short) 99)); - assertFalse("Accepted too small value", - minMaxValidator.isValid((short) 9)); - assertFalse("Accepted too large value", - minMaxValidator.isValid((short) 110)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/StringLengthValidatorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/data/validator/StringLengthValidatorTest.java deleted file mode 100644 index e24e8fcd5d..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/data/validator/StringLengthValidatorTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.vaadin.tests.data.validator; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import com.vaadin.v7.data.validator.LegacyStringLengthValidator; - -public class StringLengthValidatorTest { - - private LegacyStringLengthValidator validator = new LegacyStringLengthValidator( - "Error"); - private LegacyStringLengthValidator validatorNoNull = new LegacyStringLengthValidator( - "Error", 1, 5, false); - private LegacyStringLengthValidator validatorMinValue = new LegacyStringLengthValidator( - "Error", 5, null, true); - private LegacyStringLengthValidator validatorMaxValue = new LegacyStringLengthValidator( - "Error", null, 15, true); - - @Test - public void testValidatorWithNull() { - assertTrue("Didn't accept null", validator.isValid(null)); - assertTrue("Didn't accept null", validatorMinValue.isValid(null)); - } - - @Test - public void testValidatorNotAcceptingNull() { - assertFalse("Accepted null", validatorNoNull.isValid(null)); - } - - @Test - public void testEmptyString() { - assertTrue("Didn't accept empty String", validator.isValid("")); - assertTrue("Didn't accept empty String", validatorMaxValue.isValid("")); - assertFalse("Accepted empty string even though has lower bound of 1", - validatorNoNull.isValid("")); - assertFalse("Accepted empty string even though has lower bound of 5", - validatorMinValue.isValid("")); - } - - @Test - public void testTooLongString() { - assertFalse("Too long string was accepted", - validatorNoNull.isValid("This string is too long")); - assertFalse("Too long string was accepted", - validatorMaxValue.isValid("This string is too long")); - } - - @Test - public void testNoUpperBound() { - assertTrue("String not accepted even though no upper bound", - validatorMinValue.isValid( - "This is a really long string to test that no upper bound exists")); - } - - @Test - public void testNoLowerBound() { - assertTrue("Didn't accept short string", validatorMaxValue.isValid("")); - assertTrue("Didn't accept short string", - validatorMaxValue.isValid("1")); - } - - @Test - public void testStringLengthValidatorWithOkStringLength() { - assertTrue("Didn't accept string of correct length", - validatorNoNull.isValid("OK!")); - assertTrue("Didn't accept string of correct length", - validatorMaxValue.isValid("OK!")); - } - - @Test - public void testTooShortStringLength() { - assertFalse("Accepted a string that was too short.", - validatorMinValue.isValid("shot")); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java b/compatibility-server/src/test/java/com/vaadin/tests/design/ParseAllSupportedComponentsTest.java deleted file mode 100644 index 6694372539..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/design/ParseAllSupportedComponentsTest.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.tests.design; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.io.FileNotFoundException; - -import org.junit.Test; - -import com.vaadin.ui.declarative.Design; -import com.vaadin.ui.declarative.DesignContext; - -/** - * Just top level test case that contains all synchronizable components - * - * @author Vaadin Ltd - */ -public class ParseAllSupportedComponentsTest { - - @Test - public void allComponentsAreParsed() throws FileNotFoundException { - DesignContext ctx = Design.read( - getClass().getResourceAsStream("all-components.html"), null); - - assertThat(ctx, is(not(nullValue()))); - assertThat(ctx.getRootComponent(), is(not(nullValue()))); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/design/ParseLegacyPrefixTest.java b/compatibility-server/src/test/java/com/vaadin/tests/design/ParseLegacyPrefixTest.java deleted file mode 100644 index 5dd21b35f8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/design/ParseLegacyPrefixTest.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.design; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.io.FileNotFoundException; - -import org.junit.Test; - -import com.vaadin.ui.declarative.Design; -import com.vaadin.ui.declarative.DesignContext; - -/** - * Test reading a design with all components using the legacy prefix. - */ -public class ParseLegacyPrefixTest { - - @Test - public void allComponentsAreParsed() throws FileNotFoundException { - DesignContext ctx = Design.read( - getClass().getResourceAsStream("all-components-legacy.html"), - null); - - assertThat(ctx, is(not(nullValue()))); - assertThat(ctx.getRootComponent(), is(not(nullValue()))); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/design/WriteLegacyDesignTest.java b/compatibility-server/src/test/java/com/vaadin/tests/design/WriteLegacyDesignTest.java deleted file mode 100644 index eb3841b094..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/design/WriteLegacyDesignTest.java +++ /dev/null @@ -1,99 +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.design; - -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Properties; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.nodes.Node; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.server.Constants; -import com.vaadin.server.DefaultDeploymentConfiguration; -import com.vaadin.server.DeploymentConfiguration; -import com.vaadin.server.VaadinService; -import com.vaadin.server.VaadinServletService; -import com.vaadin.ui.declarative.Design; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.util.CurrentInstance; - -/** - * Parse and write a legacy design (using the "v-" prefix). - */ -public class WriteLegacyDesignTest { - - // The context is used for accessing the created component hierarchy. - private DesignContext ctx; - - @Before - public void setUp() throws Exception { - Properties properties = new Properties(); - properties.put(Constants.SERVLET_PARAMETER_LEGACY_DESIGN_PREFIX, - "true"); - final DeploymentConfiguration configuration = new DefaultDeploymentConfiguration( - WriteLegacyDesignTest.class, properties); - - VaadinService service = new VaadinServletService(null, configuration); - - CurrentInstance.set(VaadinService.class, service); - - ctx = Design.read( - getClass().getResourceAsStream("testFile-legacy.html"), null); - } - - @After - public void tearDown() { - CurrentInstance.set(VaadinService.class, null); - } - - private ByteArrayOutputStream serializeDesign(DesignContext context) - throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Design.write(context, out); - - return out; - } - - @Test - public void designIsSerializedWithCorrectPrefixesAndPackageNames() - throws IOException { - ByteArrayOutputStream out = serializeDesign(ctx); - - Document doc = Jsoup.parse(out.toString("UTF-8")); - for (Node child : doc.body().childNodes()) { - checkNode(child); - } - } - - private void checkNode(Node node) { - if (node instanceof Element) { - assertTrue("Wrong design element prefix", - node.nodeName().startsWith("v-")); - for (Node child : node.childNodes()) { - checkNode(child); - } - } - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractBeanContainerListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractBeanContainerListenersTest.java deleted file mode 100644 index 98a2513515..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractBeanContainerListenersTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.vaadin.tests.server; - -import com.vaadin.data.Container.PropertySetChangeEvent; -import com.vaadin.data.Container.PropertySetChangeListener; -import com.vaadin.data.util.BeanItemContainer; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; - -public class AbstractBeanContainerListenersTest - extends AbstractListenerMethodsTestBase { - public void testPropertySetChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(BeanItemContainer.class, - PropertySetChangeEvent.class, PropertySetChangeListener.class, - new BeanItemContainer( - PropertySetChangeListener.class)); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractContainerListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractContainerListenersTest.java deleted file mode 100644 index 5e0d464fe8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractContainerListenersTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.vaadin.tests.server; - -import org.junit.Test; - -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.util.IndexedContainer; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; - -public class AbstractContainerListenersTest - extends AbstractListenerMethodsTestBase { - - @Test - public void testItemSetChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(IndexedContainer.class, - ItemSetChangeEvent.class, ItemSetChangeListener.class); - } - - @Test - public void testPropertySetChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(IndexedContainer.class, - PropertySetChangeEvent.class, PropertySetChangeListener.class); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractInMemoryContainerListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractInMemoryContainerListenersTest.java deleted file mode 100644 index d5a7131182..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/AbstractInMemoryContainerListenersTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.vaadin.tests.server; - -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; - -public class AbstractInMemoryContainerListenersTest - extends AbstractListenerMethodsTestBase { - public void testItemSetChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(IndexedContainer.class, - ItemSetChangeEvent.class, ItemSetChangeListener.class); - } -} 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 deleted file mode 100644 index 1b93a3063d..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/tests/server/IndexedContainerListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/IndexedContainerListenersTest.java deleted file mode 100644 index d8a1290b68..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/IndexedContainerListenersTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.vaadin.tests.server; - -import org.junit.Test; - -import com.vaadin.data.Container.PropertySetChangeEvent; -import com.vaadin.data.Container.PropertySetChangeListener; -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; - -public class IndexedContainerListenersTest - extends AbstractListenerMethodsTestBase { - - @Test - public void testValueChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(IndexedContainer.class, ValueChangeEvent.class, - ValueChangeListener.class); - } - - @Test - public void testPropertySetChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(IndexedContainer.class, - PropertySetChangeEvent.class, PropertySetChangeListener.class); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/SerializationTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/SerializationTest.java deleted file mode 100644 index 30b0729ace..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/SerializationTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.vaadin.tests.server; - -import static org.junit.Assert.assertNotNull; - -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.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.data.util.MethodProperty; -import com.vaadin.server.VaadinSession; -import com.vaadin.v7.data.validator.LegacyRegexpValidator; - -public class SerializationTest { - - @Test - public void testValidators() throws Exception { - LegacyRegexpValidator validator = new LegacyRegexpValidator(".*", - "Error"); - validator.validate("aaa"); - LegacyRegexpValidator validator2 = serializeAndDeserialize(validator); - validator2.validate("aaa"); - } - - @Test - public void testIndedexContainerItemIds() throws Exception { - IndexedContainer ic = new IndexedContainer(); - ic.addContainerProperty("prop1", String.class, null); - Object id = ic.addItem(); - ic.getItem(id).getItemProperty("prop1").setValue("1"); - - Item item2 = ic.addItem("item2"); - item2.getItemProperty("prop1").setValue("2"); - - serializeAndDeserialize(ic); - } - - @Test - public void testMethodPropertyGetter() throws Exception { - MethodProperty mp = new MethodProperty(new Data(), - "dummyGetter"); - serializeAndDeserialize(mp); - } - - @Test - public void testMethodPropertyGetterAndSetter() throws Exception { - MethodProperty mp = new MethodProperty(new Data(), - "dummyGetterAndSetter"); - serializeAndDeserialize(mp); - } - - @Test - public void testMethodPropertyInt() throws Exception { - MethodProperty mp = new MethodProperty(new Data(), - "dummyInt"); - serializeAndDeserialize(mp); - } - - @Test - public void testVaadinSession() throws Exception { - VaadinSession session = new VaadinSession(null); - - session = serializeAndDeserialize(session); - - assertNotNull( - "Pending access queue was not recreated after deserialization", - session.getPendingAccessQueue()); - } - - private static S serializeAndDeserialize(S s) - throws IOException, ClassNotFoundException { - // Serialize and deserialize - - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bs); - out.writeObject(s); - byte[] data = bs.toByteArray(); - ObjectInputStream in = new ObjectInputStream( - new ByteArrayInputStream(data)); - @SuppressWarnings("unchecked") - S s2 = (S) in.readObject(); - - // using special toString(Object) method to avoid calling - // Property.toString(), which will be temporarily disabled - // TODO This is hilariously broken (#12723) - if (s.equals(s2)) { - System.out.println(toString(s) + " equals " + toString(s2)); - } else { - System.out.println(toString(s) + " does NOT equal " + toString(s2)); - } - - return s2; - } - - private static String toString(Object o) { - if (o instanceof Property) { - return String.valueOf(((Property) o).getValue()); - } else { - return String.valueOf(o); - } - } - - public static class Data implements Serializable { - private String dummyGetter; - private String dummyGetterAndSetter; - private int dummyInt; - - public String getDummyGetterAndSetter() { - return dummyGetterAndSetter; - } - - public void setDummyGetterAndSetter(String dummyGetterAndSetter) { - this.dummyGetterAndSetter = dummyGetterAndSetter; - } - - public int getDummyInt() { - return dummyInt; - } - - public void setDummyInt(int dummyInt) { - this.dummyInt = dummyInt; - } - - public String getDummyGetter() { - return dummyGetter; - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractfield/LegacyAbstractFieldListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractfield/LegacyAbstractFieldListenersTest.java deleted file mode 100644 index 5843bd901f..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractfield/LegacyAbstractFieldListenersTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.vaadin.tests.server.component.abstractfield; - -import org.junit.Test; - -import com.vaadin.data.Property.ReadOnlyStatusChangeEvent; -import com.vaadin.data.Property.ReadOnlyStatusChangeListener; -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; -import com.vaadin.ui.Table; - -public class LegacyAbstractFieldListenersTest - extends AbstractListenerMethodsTestBase { - - @Test - public void testReadOnlyStatusChangeListenerAddGetRemove() - throws Exception { - testListenerAddGetRemove(Table.class, ReadOnlyStatusChangeEvent.class, - ReadOnlyStatusChangeListener.class); - } - - @Test - public void testValueChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Table.class, ValueChangeEvent.class, - ValueChangeListener.class); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java deleted file mode 100644 index 9a8777909f..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectDeclarativeTest.java +++ /dev/null @@ -1,306 +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.abstractselect; - -import org.jsoup.nodes.Attributes; -import org.jsoup.nodes.Element; -import org.jsoup.parser.Tag; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.ExternalResource; -import com.vaadin.server.Resource; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.tests.design.DeclarativeTestBaseBase; -import com.vaadin.ui.AbstractSelect; -import com.vaadin.ui.AbstractSelect.ItemCaptionMode; -import com.vaadin.ui.ComboBox; -import com.vaadin.ui.ListSelect; -import com.vaadin.ui.declarative.DesignContext; -import com.vaadin.ui.declarative.DesignException; - -/** - * Test cases for reading the properties of selection components. - * - * @author Vaadin Ltd - */ -public class AbstractSelectDeclarativeTest - extends DeclarativeTestBase { - - public String getDesignSingleSelectNewItemsAllowed() { - return ""; - - } - - public AbstractSelect getExpectedSingleSelectNewItemsAllowed() { - ComboBox c = new ComboBox(); - c.setNewItemsAllowed(true); - c.setItemCaptionMode(ItemCaptionMode.ICON_ONLY); - c.setNullSelectionAllowed(true);// Default - c.setNullSelectionItemId("nullIid"); - return c; - } - - public String getDesignMultiSelect() { - return ""; - } - - public AbstractSelect getExpectedMultiSelect() { - ListSelect c = new ListSelect(); - c.setNewItemsAllowed(true); - c.setNullSelectionAllowed(false); - c.setItemCaptionMode(ItemCaptionMode.PROPERTY); - c.setMultiSelect(true); - return c; - } - - @Test - public void testReadSingleSelectNewItemsAllowed() { - testRead(getDesignSingleSelectNewItemsAllowed(), - getExpectedSingleSelectNewItemsAllowed()); - } - - @Test - public void testWriteSingleSelectNewItemsAllowed() { - testWrite(getDesignSingleSelectNewItemsAllowed(), - getExpectedSingleSelectNewItemsAllowed()); - } - - @Test - public void testReadMultiSelect() { - testRead(getDesignMultiSelect(), getExpectedMultiSelect()); - } - - @Test - public void testWriteMultiSelect() { - testWrite(getDesignMultiSelect(), getExpectedMultiSelect()); - } - - @Test - public void testReadInlineData() { - testRead(getDesignForInlineData(), getExpectedComponentForInlineData()); - } - - @Test(expected = DesignException.class) - public void testReadMultipleValuesForSingleSelect() { - testRead("" + "" - + "" + "", - null); - } - - @Test - public void testReadMultipleValuesForMultiSelect() { - ListSelect ls = new ListSelect(); - ls.setMultiSelect(true); - ls.addItem("1"); - ls.addItem("2"); - ls.select("1"); - ls.select("2"); - testRead("" - + "" + "" - + "", ls); - } - - @Test - public void testReadSingleValueForMultiSelect() { - ListSelect ls = new ListSelect(); - ls.setMultiSelect(true); - ls.addItem("1"); - ls.addItem("2"); - ls.select("1"); - testRead("" - + "" + "" - + "", ls); - } - - @Test - public void testReadSingleValueForSingleSelect() { - ListSelect ls = new ListSelect(); - ls.setMultiSelect(false); - ls.addItem("1"); - ls.addItem("2"); - ls.select("1"); - testRead("" + "" - + "" + "", ls); - } - - @Test - public void testWriteInlineDataIgnored() { - // No data is written by default - testWrite(stripOptionTags(getDesignForInlineData()), - getExpectedComponentForInlineData()); - } - - @Test - public void testWriteInlineData() { - testWrite(getDesignForInlineData(), getExpectedComponentForInlineData(), - true); - } - - private String getDesignForInlineData() { - return "\n" - + " \n" // - + " \n"// - + ""; - } - - private AbstractSelect getExpectedComponentForInlineData() { - AbstractSelect as = new ListSelect(); - as.addItem("Value 1"); - as.setItemIcon("Value 1", - new ExternalResource("http://some.url/icon.png")); - as.addItem("Value 2"); - as.setValue("Value 2"); - return as; - } - - @Test - public void testReadAttributesSingleSelect() { - Element design = createDesignWithAttributesSingleSelect(); - ComboBox cb = new ComboBox(); - IndexedContainer container = new IndexedContainer(); - container.addContainerProperty("icon", Resource.class, null); - container.addContainerProperty("name", String.class, null); - cb.setContainerDataSource(container); - cb.readDesign(design, new DesignContext()); - Assert.assertTrue("Adding new items should be allowed.", - cb.isNewItemsAllowed()); - assertEquals("Wrong item caption mode.", - AbstractSelect.ItemCaptionMode.PROPERTY, - cb.getItemCaptionMode()); - assertEquals("Wrong item caption property id.", "name", - cb.getItemCaptionPropertyId()); - assertEquals("Wrong item icon property id.", "icon", - cb.getItemIconPropertyId()); - Assert.assertTrue("Null selection should be allowed.", - cb.isNullSelectionAllowed()); - assertEquals("Wrong null selection item id.", "No items selected", - cb.getNullSelectionItemId()); - } - - @Test - public void testReadAttributesMultiSelect() { - Element design = createDesignWithAttributesMultiSelect(); - ListSelect ls = new ListSelect(); - ls.readDesign(design, new DesignContext()); - Assert.assertTrue("Multi select should be allowed.", - ls.isMultiSelect()); - assertEquals("Wrong caption mode.", - AbstractSelect.ItemCaptionMode.EXPLICIT, - ls.getItemCaptionMode()); - Assert.assertFalse("Null selection should not be allowed.", - ls.isNullSelectionAllowed()); - } - - private Element createDesignWithAttributesSingleSelect() { - Attributes attributes = new Attributes(); - attributes.put("new-items-allowed", true); - attributes.put("multi-select", "false"); - attributes.put("item-caption-mode", "property"); - attributes.put("item-caption-property-id", "name"); - attributes.put("item-icon-property-id", "icon"); - attributes.put("null-selection-allowed", true); - attributes.put("null-selection-item-id", "No items selected"); - return new Element(Tag.valueOf("vaadin-combo-box"), "", attributes); - } - - private Element createDesignWithAttributesMultiSelect() { - Attributes attributes = new Attributes(); - attributes.put("multi-select", true); - attributes.put("item-caption-mode", "EXPLICIT"); - attributes.put("null-selection-allowed", "false"); - return new Element(Tag.valueOf("vaadin-list-select"), "", attributes); - } - - @Test - public void testWriteAttributesSingleSelect() { - ComboBox cb = createSingleSelectWithOnlyAttributes(); - Element e = new Element(Tag.valueOf("vaadin-combo-box"), ""); - cb.writeDesign(e, new DesignContext()); - assertEquals("Wrong caption for the combo box.", "A combo box", - e.attr("caption")); - Assert.assertTrue("Adding new items should be allowed.", - "".equals(e.attr("new-items-allowed"))); - assertEquals("Wrong item caption mode.", "icon_only", - e.attr("item-caption-mode")); - assertEquals("Wrong item icon property id.", "icon", - e.attr("item-icon-property-id")); - Assert.assertTrue("Null selection should be allowed.", - "".equals(e.attr("null-selection-allowed")) - || "true".equals(e.attr("null-selection-allowed"))); - assertEquals("Wrong null selection item id.", "No item selected", - e.attr("null-selection-item-id")); - } - - @Test - public void testWriteMultiListSelect() { - ListSelect ls = createMultiSelect(); - Element e = new Element(Tag.valueOf("vaadin-list-select"), ""); - ls.writeDesign(e, new DesignContext()); - assertEquals("Null selection should not be allowed.", "false", - e.attr("null-selection-allowed")); - Assert.assertTrue("Multi select should be allowed.", - "".equals(e.attr("multi-select")) - || "true".equals(e.attr("multi-select"))); - } - - @Test - public void testHtmlEntities() { - String design = "" - + " " - + " " + ""; - AbstractSelect read = read(design); - - Assert.assertEquals("> One", read.getItemCaption("one")); - - AbstractSelect underTest = new ComboBox(); - underTest.addItem("> One"); - - Element root = new Element(Tag.valueOf("vaadin-combo-box"), ""); - DesignContext dc = new DesignContext(); - dc.setShouldWriteDataDelegate( - DeclarativeTestBaseBase.ALWAYS_WRITE_DATA); - underTest.writeDesign(root, dc); - - Assert.assertEquals("> One", - root.getElementsByTag("option").first().html()); - } - - public ComboBox createSingleSelectWithOnlyAttributes() { - ComboBox cb = new ComboBox(); - Container dataSource = new IndexedContainer(); - dataSource.addContainerProperty("icon", Resource.class, null); - cb.setContainerDataSource(dataSource); - cb.setCaption("A combo box"); - cb.setNewItemsAllowed(true); - cb.setItemCaptionMode(ItemCaptionMode.ICON_ONLY); - cb.setItemIconPropertyId("icon"); - cb.setNullSelectionAllowed(true); - cb.setNullSelectionItemId("No item selected"); - return cb; - } - - public ListSelect createMultiSelect() { - ListSelect ls = new ListSelect(); - ls.setNullSelectionAllowed(false); - ls.setMultiSelect(true); - return ls; - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectListenersTest.java deleted file mode 100644 index 7fc584e3d5..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectListenersTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.vaadin.tests.server.component.abstractselect; - -import org.junit.Test; - -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.tests.server.component.AbstractListenerMethodsTestBase; -import com.vaadin.ui.ComboBox; - -public class AbstractSelectListenersTest - extends AbstractListenerMethodsTestBase { - - @Test - public void testItemSetChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(ComboBox.class, ItemSetChangeEvent.class, - ItemSetChangeListener.class); - } - - @Test - public void testPropertySetChangeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(ComboBox.class, PropertySetChangeEvent.class, - PropertySetChangeListener.class); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectStateTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectStateTest.java deleted file mode 100644 index c5ff297d7b..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/AbstractSelectStateTest.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.tests.server.component.abstractselect; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.select.AbstractSelectState; -import com.vaadin.ui.AbstractSelect; - -/** - * Tests for AbstractSelect state - * - */ -public class AbstractSelectStateTest { - - @Test - public void getState_selectHasCustomState() { - TestSelect select = new TestSelect(); - AbstractSelectState state = select.getState(); - Assert.assertEquals("Unexpected state class", AbstractSelectState.class, - state.getClass()); - } - - @Test - public void getPrimaryStyleName_selectHasCustomPrimaryStyleName() { - TestSelect combobox = new TestSelect(); - AbstractSelectState state = new AbstractSelectState(); - Assert.assertEquals("Unexpected primary style name", - state.primaryStyleName, combobox.getPrimaryStyleName()); - } - - @Test - public void selectStateHasCustomPrimaryStyleName() { - AbstractSelectState state = new AbstractSelectState(); - Assert.assertEquals("Unexpected primary style name", "v-select", - state.primaryStyleName); - } - - private static class TestSelect extends AbstractSelect { - - @Override - public AbstractSelectState getState() { - return super.getState(); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupDeclarativeTest.java deleted file mode 100644 index 8bd253ab64..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupDeclarativeTest.java +++ /dev/null @@ -1,160 +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.abstractselect; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.server.ThemeResource; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.OptionGroup; - -public class OptionGroupDeclarativeTest - extends DeclarativeTestBase { - - private OptionGroup og; - - @Before - public void init() { - og = new OptionGroup(); - } - - @Test - public void testBasicSyntax() { - - String expected = ""; - testReadWrite(expected); - - } - - @Test - public void testOptionSyntax() { - - og.addItems("foo", "bar", "baz", "bang"); - - //@formatter:off - String expected = - "" - + "" - + "" - + "" - + "" - + ""; - //@formatter:on - - testReadWrite(expected); - - } - - @Test - public void testDisabledOptionSyntax() { - - og.addItems("foo", "bar", "baz", "bang"); - og.setItemEnabled("baz", false); - - //@formatter:off - String expected = - "" - + "" - + "" - + "" - + "" - + ""; - //@formatter:on - - testReadWrite(expected); - - } - - @Test - public void testIconSyntax() { - - og.addItems("foo", "bar", "baz", "bang"); - og.setItemIcon("bar", new ThemeResource("foobar.png")); - - //@formatter:off - String expected = - "" - + "" - + "" - + "" - + "" - + ""; - //@formatter:on - - testReadWrite(expected); - - } - - @Test - public void testHTMLCaption() { - - og.addItems("foo", "bar", "baz", "bang"); - - og.setHtmlContentAllowed(true); - - og.setItemCaption("foo", "True"); - og.setItemCaption("bar", "False"); - - //@formatter:off - String expected = - "" - + "" - + "" - + "" - + "" - + ""; - //@formatter:on - - testReadWrite(expected); - } - - @Test - public void testPlaintextCaption() { - - og.addItems("foo", "bar", "baz", "bang"); - - og.setItemCaption("foo", "True"); - og.setItemCaption("bar", "False"); - - //@formatter:off - String expected = - "" - + "" - + "" - + "" - + "" - + ""; - //@formatter:on - - testReadWrite(expected); - } - - private void testReadWrite(String design) { - testWrite(design, og, true); - testRead(design, og); - } - - @Override - public OptionGroup testRead(String design, OptionGroup expected) { - - OptionGroup read = super.testRead(design, expected); - testWrite(design, read, true); - - return read; - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupTest.java deleted file mode 100644 index 9a20d2fd79..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/abstractselect/OptionGroupTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.vaadin.tests.server.component.abstractselect; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Collection; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.ui.OptionGroup; - -public class OptionGroupTest { - - private OptionGroup optionGroup; - - @Before - public void setup() { - optionGroup = new OptionGroup(); - } - - @Test - public void itemsAreAdded() { - optionGroup.addItems("foo", "bar"); - - Collection itemIds = optionGroup.getItemIds(); - - assertEquals(2, itemIds.size()); - assertTrue(itemIds.contains("foo")); - assertTrue(itemIds.contains("bar")); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarBasicsTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarBasicsTest.java deleted file mode 100644 index 7d011afc8b..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarBasicsTest.java +++ /dev/null @@ -1,290 +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.calendar; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.ui.Calendar; -import com.vaadin.ui.Calendar.TimeFormat; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent; -import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick; -import com.vaadin.ui.components.calendar.event.BasicEventProvider; -import com.vaadin.ui.components.calendar.event.CalendarEventProvider; - -/** - * Basic API tests for the calendar - */ -public class CalendarBasicsTest { - - @Test - public void testEmptyConstructorInitialization() { - - Calendar calendar = new Calendar(); - - // The calendar should have a basic event provider with no events - CalendarEventProvider provider = calendar.getEventProvider(); - assertNotNull("Event provider should not be null", provider); - - // Basic event handlers should be registered - assertNotNull(calendar.getHandler(BackwardEvent.EVENT_ID)); - assertNotNull(calendar.getHandler(ForwardEvent.EVENT_ID)); - assertNotNull(calendar.getHandler(WeekClick.EVENT_ID)); - assertNotNull(calendar.getHandler(DateClickEvent.EVENT_ID)); - assertNotNull(calendar.getHandler(MoveEvent.EVENT_ID)); - assertNotNull(calendar.getHandler(EventResize.EVENT_ID)); - - // Calendar should have undefined size - assertTrue(calendar.getWidth() < 0); - assertTrue(calendar.getHeight() < 0); - } - - @Test - public void testConstructorWithCaption() { - final String caption = "My Calendar Caption"; - Calendar calendar = new Calendar(caption); - assertEquals(caption, calendar.getCaption()); - } - - @Test - public void testConstructorWithCustomEventProvider() { - BasicEventProvider myProvider = new BasicEventProvider(); - Calendar calendar = new Calendar(myProvider); - assertEquals(myProvider, calendar.getEventProvider()); - } - - @Test - public void testConstructorWithCustomEventProviderAndCaption() { - BasicEventProvider myProvider = new BasicEventProvider(); - final String caption = "My Calendar Caption"; - Calendar calendar = new Calendar(caption, myProvider); - assertEquals(caption, calendar.getCaption()); - assertEquals(myProvider, calendar.getEventProvider()); - } - - @Test - public void testDefaultStartAndEndDates() { - Calendar calendar = new Calendar(); - - // If no start and end date is set the calendar will display the current - // week - java.util.Calendar c = new GregorianCalendar(); - java.util.Calendar c2 = new GregorianCalendar(); - - c2.setTime(calendar.getStartDate()); - assertEquals(c.getFirstDayOfWeek(), - c2.get(java.util.Calendar.DAY_OF_WEEK)); - c2.setTime(calendar.getEndDate()); - - c.set(java.util.Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6); - assertEquals(c.get(java.util.Calendar.DAY_OF_WEEK), - c2.get(java.util.Calendar.DAY_OF_WEEK)); - } - - @Test - public void testCustomStartAndEndDates() { - Calendar calendar = new Calendar(); - java.util.Calendar c = new GregorianCalendar(); - - Date start = c.getTime(); - c.add(java.util.Calendar.DATE, 3); - Date end = c.getTime(); - - calendar.setStartDate(start); - calendar.setEndDate(end); - - assertEquals(start.getTime(), calendar.getStartDate().getTime()); - assertEquals(end.getTime(), calendar.getEndDate().getTime()); - } - - @Test - public void testCustomLocale() { - Calendar calendar = new Calendar(); - calendar.setLocale(Locale.CANADA_FRENCH); - - // Setting the locale should set the internal calendars locale - assertEquals(Locale.CANADA_FRENCH, calendar.getLocale()); - java.util.Calendar c = new GregorianCalendar(Locale.CANADA_FRENCH); - assertEquals(c.getTimeZone().getRawOffset(), - calendar.getInternalCalendar().getTimeZone().getRawOffset()); - } - - @Test - public void testTimeFormat() { - Calendar calendar = new Calendar(); - - // The default timeformat depends on the current locale - calendar.setLocale(Locale.ENGLISH); - assertEquals(TimeFormat.Format12H, calendar.getTimeFormat()); - - calendar.setLocale(Locale.ITALIAN); - assertEquals(TimeFormat.Format24H, calendar.getTimeFormat()); - - // Setting a specific time format overrides the locale - calendar.setTimeFormat(TimeFormat.Format12H); - assertEquals(TimeFormat.Format12H, calendar.getTimeFormat()); - } - - @Test - public void testTimeZone() { - Calendar calendar = new Calendar(); - calendar.setLocale(Locale.CANADA_FRENCH); - - // By default the calendars timezone is returned - assertEquals(calendar.getInternalCalendar().getTimeZone(), - calendar.getTimeZone()); - - // One can override the default behaviour by specifying a timezone - TimeZone customTimeZone = TimeZone.getTimeZone("Europe/Helsinki"); - calendar.setTimeZone(customTimeZone); - assertEquals(customTimeZone, calendar.getTimeZone()); - } - - @Test - public void testVisibleDaysOfWeek() { - Calendar calendar = new Calendar(); - - // The defaults are the whole week - assertEquals(1, calendar.getFirstVisibleDayOfWeek()); - assertEquals(7, calendar.getLastVisibleDayOfWeek()); - - calendar.setFirstVisibleDayOfWeek(0); // Invalid input - assertEquals(1, calendar.getFirstVisibleDayOfWeek()); - - calendar.setLastVisibleDayOfWeek(0); // Invalid input - assertEquals(7, calendar.getLastVisibleDayOfWeek()); - - calendar.setFirstVisibleDayOfWeek(8); // Invalid input - assertEquals(1, calendar.getFirstVisibleDayOfWeek()); - - calendar.setLastVisibleDayOfWeek(8); // Invalid input - assertEquals(7, calendar.getLastVisibleDayOfWeek()); - - calendar.setFirstVisibleDayOfWeek(4); - assertEquals(4, calendar.getFirstVisibleDayOfWeek()); - - calendar.setLastVisibleDayOfWeek(6); - assertEquals(6, calendar.getLastVisibleDayOfWeek()); - - calendar.setFirstVisibleDayOfWeek(7); // Invalid since last day is 6 - assertEquals(4, calendar.getFirstVisibleDayOfWeek()); - - calendar.setLastVisibleDayOfWeek(2); // Invalid since first day is 4 - assertEquals(6, calendar.getLastVisibleDayOfWeek()); - } - - @Test - public void testVisibleHoursInDay() { - Calendar calendar = new Calendar(); - - // Defaults are the whole day - assertEquals(0, calendar.getFirstVisibleHourOfDay()); - assertEquals(23, calendar.getLastVisibleHourOfDay()); - } - - @Test - public void isClientChangeAllowed_connectorEnabled() { - TestCalendar calendar = new TestCalendar(true); - Assert.assertTrue( - "Calendar with enabled connector doesn't allow client change", - calendar.isClientChangeAllowed()); - } - - // regression test to ensure old functionality is not broken - @Test - public void defaultFirstDayOfWeek() { - Calendar calendar = new Calendar(); - calendar.setLocale(Locale.GERMAN); - // simulating consequences of markAsDirty - calendar.beforeClientResponse(true); - assertEquals(java.util.Calendar.MONDAY, - calendar.getInternalCalendar().getFirstDayOfWeek()); - } - - @Test - public void customFirstDayOfWeek() { - Calendar calendar = new Calendar(); - calendar.setLocale(Locale.GERMAN); - calendar.setFirstDayOfWeek(java.util.Calendar.SUNDAY); - - // simulating consequences of markAsDirty - calendar.beforeClientResponse(true); - assertEquals(java.util.Calendar.SUNDAY, - calendar.getInternalCalendar().getFirstDayOfWeek()); - } - - @Test - public void customFirstDayOfWeekCanSetEvenBeforeLocale() { - Calendar calendar = new Calendar(); - calendar.setFirstDayOfWeek(java.util.Calendar.SUNDAY); - - calendar.setLocale(Locale.GERMAN); - // simulating consequences of markAsDirty - calendar.beforeClientResponse(true); - assertEquals(java.util.Calendar.SUNDAY, - calendar.getInternalCalendar().getFirstDayOfWeek()); - } - - @Test - public void customFirstDayOfWeekSetNullRestoresDefault() { - Calendar calendar = new Calendar(); - calendar.setLocale(Locale.GERMAN); - calendar.setFirstDayOfWeek(java.util.Calendar.SUNDAY); - calendar.setFirstDayOfWeek(null); - // simulating consequences of markAsDirty - calendar.beforeClientResponse(true); - assertEquals(java.util.Calendar.MONDAY, - calendar.getInternalCalendar().getFirstDayOfWeek()); - } - - @Test(expected = IllegalArgumentException.class) - public void customFirstDayOfWeekValidation() { - Calendar calendar = new Calendar(); - int someWrongDayOfWeek = 10; - calendar.setFirstDayOfWeek(someWrongDayOfWeek); - } - - private static class TestCalendar extends Calendar { - TestCalendar(boolean connectorEnabled) { - isConnectorEnabled = connectorEnabled; - } - - @Override - public boolean isConnectorEnabled() { - return isConnectorEnabled; - } - - @Override - public boolean isClientChangeAllowed() { - return super.isClientChangeAllowed(); - } - - private final boolean isConnectorEnabled; - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarDeclarativeTest.java deleted file mode 100644 index f6896ff15d..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/CalendarDeclarativeTest.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.calendar; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.TimeZone; - -import org.junit.Test; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.Calendar; -import com.vaadin.ui.Calendar.TimeFormat; - -public class CalendarDeclarativeTest extends DeclarativeTestBase { - - @Test - public void testEmpty() { - verifyDeclarativeDesign("", - new Calendar()); - } - - @Test - public void testCalendarAllFeatures() throws ParseException { - String design = ""; - - DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); - Calendar calendar = new Calendar(); - calendar.setStartDate(format.parse("2014-11-17")); - calendar.setEndDate(format.parse("2014-11-23")); - calendar.setFirstVisibleDayOfWeek(2); - calendar.setLastVisibleDayOfWeek(5); - calendar.setTimeZone(TimeZone.getTimeZone("EST")); - calendar.setTimeFormat(TimeFormat.Format12H); - calendar.setFirstVisibleHourOfDay(8); - calendar.setLastVisibleHourOfDay(18); - calendar.setWeeklyCaptionFormat("mmm MM/dd"); - verifyDeclarativeDesign(design, calendar); - } - - protected void verifyDeclarativeDesign(String design, Calendar expected) { - testRead(design, expected); - testWrite(design, expected); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerDataSourceTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerDataSourceTest.java deleted file mode 100644 index 9cc78269f8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerDataSourceTest.java +++ /dev/null @@ -1,396 +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.calendar; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Date; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.Container.Sortable; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.BeanItemContainer; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.ui.Calendar; -import com.vaadin.ui.components.calendar.ContainerEventProvider; -import com.vaadin.ui.components.calendar.event.BasicEvent; -import com.vaadin.ui.components.calendar.event.CalendarEvent; - -public class ContainerDataSourceTest { - - private Calendar calendar; - - @Before - public void setUp() { - calendar = new Calendar(); - } - - /** - * Tests adding a bean item container to the Calendar - */ - @Test - public void testWithBeanItemContainer() { - - // Create a container to use as a datasource - Indexed container = createTestBeanItemContainer(); - - // Set datasource - calendar.setContainerDataSource(container); - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart()); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Test the all events are returned - List events = calendar.getEventProvider() - .getEvents(start, end); - assertEquals(container.size(), events.size()); - - // Test that a certain range is returned - cal.setTime(((CalendarEvent) container.getIdByIndex(6)).getStart()); - end = cal.getTime(); - events = calendar.getEventProvider().getEvents(start, end); - assertEquals(6, events.size()); - } - - /** - * This tests tests that if you give the Calendar an unsorted (== not sorted - * by starting date) container then the calendar should gracefully handle - * it. In this case the size of the container will be wrong. The test is - * exactly the same as {@link #testWithBeanItemContainer()} except that the - * beans has been intentionally sorted by caption instead of date. - */ - @Test - public void testWithUnsortedBeanItemContainer() { - // Create a container to use as a datasource - Indexed container = createTestBeanItemContainer(); - - // Make the container sorted by caption - ((Sortable) container).sort(new Object[] { "caption" }, - new boolean[] { true }); - - // Set data source - calendar.setContainerDataSource(container); - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart()); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Test the all events are returned - List events = calendar.getEventProvider() - .getEvents(start, end); - assertEquals(container.size(), events.size()); - - // Test that a certain range is returned - cal.setTime(((CalendarEvent) container.getIdByIndex(6)).getStart()); - end = cal.getTime(); - events = calendar.getEventProvider().getEvents(start, end); - - // The events size is 1 since the getEvents returns the wrong range - assertEquals(1, events.size()); - } - - /** - * Tests adding a Indexed container to the Calendar - */ - @Test - public void testWithIndexedContainer() { - - // Create a container to use as a datasource - Indexed container = createTestIndexedContainer(); - - // Set datasource - calendar.setContainerDataSource(container, "testCaption", - "testDescription", "testStartDate", "testEndDate", null); - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - cal.setTime((Date) container.getItem(container.getIdByIndex(0)) - .getItemProperty("testStartDate").getValue()); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Test the all events are returned - List events = calendar.getEventProvider() - .getEvents(start, end); - assertEquals(container.size(), events.size()); - - // Check that event values are present - CalendarEvent e = events.get(0); - assertEquals("Test 1", e.getCaption()); - assertEquals("Description 1", e.getDescription()); - assertTrue(e.getStart().compareTo(start) == 0); - - // Test that a certain range is returned - cal.setTime((Date) container.getItem(container.getIdByIndex(6)) - .getItemProperty("testStartDate").getValue()); - end = cal.getTime(); - events = calendar.getEventProvider().getEvents(start, end); - assertEquals(6, events.size()); - } - - @Test - public void testNullLimitsBeanItemContainer() { - // Create a container to use as a datasource - Indexed container = createTestBeanItemContainer(); - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart()); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Set datasource - calendar.setContainerDataSource(container); - - // Test null start time - List events = calendar.getEventProvider().getEvents(null, - end); - assertEquals(container.size(), events.size()); - - // Test null end time - events = calendar.getEventProvider().getEvents(start, null); - assertEquals(container.size(), events.size()); - - // Test both null times - events = calendar.getEventProvider().getEvents(null, null); - assertEquals(container.size(), events.size()); - } - - @Test - public void testNullLimitsIndexedContainer() { - // Create a container to use as a datasource - Indexed container = createTestIndexedContainer(); - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - cal.setTime((Date) container.getItem(container.getIdByIndex(0)) - .getItemProperty("testStartDate").getValue()); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Set datasource - calendar.setContainerDataSource(container, "testCaption", - "testDescription", "testStartDate", "testEndDate", null); - - // Test null start time - List events = calendar.getEventProvider().getEvents(null, - end); - assertEquals(container.size(), events.size()); - - // Test null end time - events = calendar.getEventProvider().getEvents(start, null); - assertEquals(container.size(), events.size()); - - // Test both null times - events = calendar.getEventProvider().getEvents(null, null); - assertEquals(container.size(), events.size()); - } - - /** - * Tests the addEvent convenience method with the default event provider - */ - @Test - public void testAddEventConvinienceMethod() { - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Ensure no events - assertEquals(0, calendar.getEvents(start, end).size()); - - // Add an event - BasicEvent event = new BasicEvent("Test", "Test", start); - calendar.addEvent(event); - - // Ensure event exists - List events = calendar.getEvents(start, end); - assertEquals(1, events.size()); - assertEquals(events.get(0).getCaption(), event.getCaption()); - assertEquals(events.get(0).getDescription(), event.getDescription()); - assertEquals(events.get(0).getStart(), event.getStart()); - } - - /** - * Test the removeEvent convenience method with the default event provider - */ - @Test - public void testRemoveEventConvinienceMethod() { - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Ensure no events - assertEquals(0, calendar.getEvents(start, end).size()); - - // Add an event - CalendarEvent event = new BasicEvent("Test", "Test", start); - calendar.addEvent(event); - - // Ensure event exists - assertEquals(1, calendar.getEvents(start, end).size()); - - // Remove event - calendar.removeEvent(event); - - // Ensure no events - assertEquals(0, calendar.getEvents(start, end).size()); - } - - @Test - public void testAddEventConvinienceMethodWithCustomEventProvider() { - - // Use a container data source - calendar.setEventProvider(new ContainerEventProvider( - new BeanItemContainer(BasicEvent.class))); - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Ensure no events - assertEquals(0, calendar.getEvents(start, end).size()); - - // Add an event - BasicEvent event = new BasicEvent("Test", "Test", start); - calendar.addEvent(event); - - // Ensure event exists - List events = calendar.getEvents(start, end); - assertEquals(1, events.size()); - assertEquals(events.get(0).getCaption(), event.getCaption()); - assertEquals(events.get(0).getDescription(), event.getDescription()); - assertEquals(events.get(0).getStart(), event.getStart()); - } - - @Test - public void testRemoveEventConvinienceMethodWithCustomEventProvider() { - - // Use a container data source - calendar.setEventProvider(new ContainerEventProvider( - new BeanItemContainer(BasicEvent.class))); - - // Start and end dates to query for - java.util.Calendar cal = java.util.Calendar.getInstance(); - Date start = cal.getTime(); - cal.add(java.util.Calendar.MONTH, 1); - Date end = cal.getTime(); - - // Ensure no events - assertEquals(0, calendar.getEvents(start, end).size()); - - // Add an event - BasicEvent event = new BasicEvent("Test", "Test", start); - calendar.addEvent(event); - - // Ensure event exists - List events = calendar.getEvents(start, end); - assertEquals(1, events.size()); - - // Remove event - calendar.removeEvent(event); - - // Ensure no events - assertEquals(0, calendar.getEvents(start, end).size()); - } - - @Test - public void testStyleNamePropertyRetrieved() { - IndexedContainer ic = (IndexedContainer) createTestIndexedContainer(); - ic.addContainerProperty("testStyleName", String.class, ""); - for (int i = 0; i < 10; i++) { - Item item = ic.getItem(ic.getIdByIndex(i)); - @SuppressWarnings("unchecked") - Property itemProperty = item - .getItemProperty("testStyleName"); - itemProperty.setValue("testStyle"); - } - - ContainerEventProvider provider = new ContainerEventProvider(ic); - provider.setCaptionProperty("testCaption"); - provider.setDescriptionProperty("testDescription"); - provider.setStartDateProperty("testStartDate"); - provider.setEndDateProperty("testEndDate"); - provider.setStyleNameProperty("testStyleName"); - - calendar.setEventProvider(provider); - java.util.Calendar cal = java.util.Calendar.getInstance(); - Date now = cal.getTime(); - cal.add(java.util.Calendar.DAY_OF_MONTH, 20); - Date then = cal.getTime(); - List events = calendar.getEventProvider().getEvents(now, - then); - for (CalendarEvent ce : events) { - assertEquals("testStyle", ce.getStyleName()); - } - } - - private static Indexed createTestBeanItemContainer() { - BeanItemContainer eventContainer = new BeanItemContainer( - CalendarEvent.class); - java.util.Calendar cal = java.util.Calendar.getInstance(); - for (int i = 1; i <= 10; i++) { - eventContainer.addBean(new BasicEvent("Test " + i, - "Description " + i, cal.getTime())); - cal.add(java.util.Calendar.DAY_OF_MONTH, 2); - } - return eventContainer; - } - - private static Indexed createTestIndexedContainer() { - IndexedContainer container = new IndexedContainer(); - container.addContainerProperty("testCaption", String.class, ""); - container.addContainerProperty("testDescription", String.class, ""); - container.addContainerProperty("testStartDate", Date.class, null); - container.addContainerProperty("testEndDate", Date.class, null); - - java.util.Calendar cal = java.util.Calendar.getInstance(); - for (int i = 1; i <= 10; i++) { - Item item = container.getItem(container.addItem()); - item.getItemProperty("testCaption").setValue("Test " + i); - item.getItemProperty("testDescription") - .setValue("Description " + i); - item.getItemProperty("testStartDate").setValue(cal.getTime()); - item.getItemProperty("testEndDate").setValue(cal.getTime()); - cal.add(java.util.Calendar.DAY_OF_MONTH, 2); - } - return container; - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerEventProviderTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerEventProviderTest.java deleted file mode 100644 index 401b5861ce..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/calendar/ContainerEventProviderTest.java +++ /dev/null @@ -1,88 +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.calendar; - -import java.util.Calendar; -import java.util.Date; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.util.BeanItemContainer; -import com.vaadin.ui.components.calendar.ContainerEventProvider; -import com.vaadin.ui.components.calendar.event.CalendarEvent; - -/** - * - * @author Vaadin Ltd - */ -public class ContainerEventProviderTest { - - @Test - public void testDefaultAllDayProperty() { - ContainerEventProvider provider = new ContainerEventProvider(null); - Assert.assertEquals(ContainerEventProvider.ALL_DAY_PROPERTY, - provider.getAllDayProperty()); - - } - - @Test - public void testSetAllDayProperty() { - ContainerEventProvider provider = new ContainerEventProvider(null); - Object prop = new Object(); - provider.setAllDayProperty(prop); - Assert.assertEquals(prop, provider.getAllDayProperty()); - } - - @Test - public void testGetEvents() { - BeanItemContainer container = new BeanItemContainer( - EventBean.class); - EventBean bean = new EventBean(); - container.addBean(bean); - ContainerEventProvider provider = new ContainerEventProvider(container); - List events = provider.getEvents(bean.getStart(), - bean.getEnd()); - Assert.assertTrue(events.get(0).isAllDay()); - } - - public static class EventBean { - - public boolean isAllDay() { - return true; - } - - public void setAllDay(boolean allDay) { - } - - public Date getStart() { - return Calendar.getInstance().getTime(); - } - - public Date getEnd() { - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.MINUTE, 10); - return calendar.getTime(); - } - - public void setStart(Date date) { - } - - public void setEnd(Date date) { - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java deleted file mode 100644 index 2e3b8e412e..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java +++ /dev/null @@ -1,87 +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.colorpicker; - -import org.junit.Test; - -import com.vaadin.shared.ui.colorpicker.Color; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.AbstractColorPicker; -import com.vaadin.ui.AbstractColorPicker.PopupStyle; -import com.vaadin.ui.ColorPicker; -import com.vaadin.ui.ColorPickerArea; - -public class AbstractColorPickerDeclarativeTest - extends DeclarativeTestBase { - - @Test - public void testAllAbstractColorPickerFeatures() { - String design = ""; - ColorPicker colorPicker = new ColorPicker(); - int colorInt = Integer.parseInt("fafafa", 16); - colorPicker.setColor(new Color(colorInt)); - colorPicker.setDefaultCaptionEnabled(true); - colorPicker.setPosition(100, 100); - colorPicker.setPopupStyle(PopupStyle.POPUP_SIMPLE); - colorPicker.setRGBVisibility(false); - colorPicker.setHSVVisibility(false); - colorPicker.setSwatchesVisibility(true); - colorPicker.setHistoryVisibility(false); - colorPicker.setTextfieldVisibility(false); - - testWrite(design, colorPicker); - testRead(design, colorPicker); - } - - @Test - public void testEmptyColorPicker() { - String design = ""; - ColorPicker colorPicker = new ColorPicker(); - testRead(design, colorPicker); - testWrite(design, colorPicker); - } - - @Test - public void testAllAbstractColorPickerAreaFeatures() { - String design = ""; - AbstractColorPicker colorPicker = new ColorPickerArea(); - int colorInt = Integer.parseInt("fafafa", 16); - colorPicker.setColor(new Color(colorInt)); - colorPicker.setDefaultCaptionEnabled(true); - colorPicker.setPosition(100, 100); - colorPicker.setPopupStyle(PopupStyle.POPUP_SIMPLE); - colorPicker.setRGBVisibility(false); - colorPicker.setHSVVisibility(false); - colorPicker.setSwatchesVisibility(true); - colorPicker.setHistoryVisibility(false); - colorPicker.setTextfieldVisibility(false); - - testWrite(design, colorPicker); - testRead(design, colorPicker); - } - - @Test - public void testEmptyColorPickerArea() { - String design = ""; - AbstractColorPicker colorPicker = new ColorPickerArea(); - testRead(design, colorPicker); - testWrite(design, colorPicker); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/ColorConversionsTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/ColorConversionsTest.java deleted file mode 100644 index a55ed89691..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/colorpicker/ColorConversionsTest.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.tests.server.component.colorpicker; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import com.vaadin.shared.ui.colorpicker.Color; - -public class ColorConversionsTest { - - @Test - public void convertHSL2RGB() { - - int rgb = Color.HSLtoRGB(100, 50, 50); - Color c = new Color(rgb); - assertEquals(106, c.getRed()); - assertEquals(191, c.getGreen()); - assertEquals(64, c.getBlue()); - assertEquals("#6abf40", c.getCSS()); - - rgb = Color.HSLtoRGB(0, 50, 50); - c = new Color(rgb); - assertEquals(191, c.getRed()); - assertEquals(64, c.getGreen()); - assertEquals(64, c.getBlue()); - assertEquals("#bf4040", c.getCSS()); - - rgb = Color.HSLtoRGB(50, 0, 50); - c = new Color(rgb); - assertEquals(128, c.getRed()); - assertEquals(128, c.getGreen()); - assertEquals(128, c.getBlue()); - assertEquals("#808080", c.getCSS()); - - rgb = Color.HSLtoRGB(50, 100, 0); - c = new Color(rgb); - assertEquals(0, c.getRed(), 0); - assertEquals(0, c.getGreen(), 0); - assertEquals(0, c.getBlue(), 0); - assertEquals("#000000", c.getCSS()); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java deleted file mode 100644 index 482267f63d..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxDeclarativeTest.java +++ /dev/null @@ -1,88 +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.combobox; - -import org.junit.Test; - -import com.vaadin.shared.ui.combobox.FilteringMode; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.ComboBox; - -public class ComboBoxDeclarativeTest extends DeclarativeTestBase { - - @Test - public void testReadOnlyWithOptionsRead() { - testRead(getReadOnlyWithOptionsDesign(), - getReadOnlyWithOptionsExpected()); - } - - private ComboBox getReadOnlyWithOptionsExpected() { - ComboBox cb = new ComboBox(); - cb.setTextInputAllowed(false); - cb.addItem("Hello"); - cb.addItem("World"); - return cb; - } - - private String getReadOnlyWithOptionsDesign() { - return ""; - } - - @Test - public void testReadOnlyWithOptionsWrite() { - testWrite(stripOptionTags(getReadOnlyWithOptionsDesign()), - getReadOnlyWithOptionsExpected()); - } - - @Test - public void testBasicRead() { - testRead(getBasicDesign(), getBasicExpected()); - } - - @Test - public void testBasicWrite() { - testWrite(getBasicDesign(), getBasicExpected()); - } - - @Test - public void testReadOnlyValue() { - String design = ""; - - ComboBox comboBox = new ComboBox(); - comboBox.addItems("foo", "bar"); - comboBox.setValue("foo"); - comboBox.setReadOnly(true); - - testRead(design, comboBox); - - // Selects items are not written out by default - String design2 = ""; - testWrite(design2, comboBox); - } - - private String getBasicDesign() { - return ""; - } - - private ComboBox getBasicExpected() { - ComboBox cb = new ComboBox(); - cb.setInputPrompt("Select something"); - cb.setTextInputAllowed(true); - cb.setFilteringMode(FilteringMode.OFF); - cb.setScrollToSelectedItem(false); - return cb; - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxStateTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxStateTest.java deleted file mode 100644 index e8ca64895a..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/combobox/ComboBoxStateTest.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.combobox; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.combobox.ComboBoxState; -import com.vaadin.ui.ComboBox; - -/** - * Tests for ComboBox state. - * - */ -public class ComboBoxStateTest { - @Test - public void getState_comboboxHasCustomState() { - TestComboBox combobox = new TestComboBox(); - ComboBoxState state = combobox.getState(); - Assert.assertEquals("Unexpected state class", ComboBoxState.class, - state.getClass()); - } - - @Test - public void getPrimaryStyleName_comboboxHasCustomPrimaryStyleName() { - ComboBox combobox = new ComboBox(); - ComboBoxState state = new ComboBoxState(); - Assert.assertEquals("Unexpected primary style name", - state.primaryStyleName, combobox.getPrimaryStyleName()); - } - - @Test - public void comboboxStateHasCustomPrimaryStyleName() { - ComboBoxState state = new ComboBoxState(); - Assert.assertEquals("Unexpected primary style name", "v-filterselect", - state.primaryStyleName); - } - - private static class TestComboBox extends ComboBox { - - @Override - public ComboBoxState getState() { - return super.getState(); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldConverterTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldConverterTest.java deleted file mode 100644 index 5b4e8c7352..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldConverterTest.java +++ /dev/null @@ -1,79 +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.datefield; - -import java.util.Date; -import java.util.Locale; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Property; -import com.vaadin.data.util.ObjectProperty; -import com.vaadin.shared.ui.datefield.Resolution; -import com.vaadin.v7.data.util.converter.LegacyConverter; -import com.vaadin.v7.ui.LegacyDateField; - -public class DateFieldConverterTest { - - private Property date; - private LegacyDateField datefield; - - @Before - public void setUp() { - date = new ObjectProperty(0L); - datefield = new LegacyDateField(); - datefield.setBuffered(false); - datefield.setConverter(new LegacyConverter() { - - @Override - public Long convertToModel(Date value, - Class targetType, Locale locale) - throws ConversionException { - return value.getTime(); - } - - @Override - public Date convertToPresentation(Long value, - Class targetType, Locale locale) - throws ConversionException { - return new Date(value); - } - - @Override - public Class getModelType() { - return Long.class; - } - - @Override - public Class getPresentationType() { - return Date.class; - } - }); - datefield.setPropertyDataSource(date); - } - - /* - * See #12193. - */ - @Test - public void testResolution() { - datefield.setValue(new Date(110, 0, 1)); - datefield.setResolution(Resolution.MINUTE); - datefield.validate(); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyDateFieldDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyDateFieldDeclarativeTest.java deleted file mode 100644 index 3ac1b6af32..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyDateFieldDeclarativeTest.java +++ /dev/null @@ -1,113 +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.datefield; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; - -import org.junit.Test; - -import com.vaadin.shared.ui.datefield.Resolution; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.v7.ui.LegacyDateField; - -/** - * Tests the declarative support for implementations of {@link LegacyDateField}. - * - * @author Vaadin Ltd - * @since 7.4 - */ -public class LegacyDateFieldDeclarativeTest - extends DeclarativeTestBase { - - private String getYearResolutionDesign() { - return ""; - } - - private LegacyDateField getYearResolutionExpected() { - LegacyDateField df = new LegacyDateField(); - df.setResolution(Resolution.YEAR); - df.setValue(new Date(2020 - 1900, 1 - 1, 1)); - return df; - } - - private String getTimezoneDesign() { - String timeZone = new SimpleDateFormat("Z").format(new Date()); - return String.format( - "", - timeZone); - } - - private LegacyDateField getTimezoneExpected() { - LegacyDateField df = new LegacyDateField(); - - df.setRangeStart(new Date(2014 - 1900, 5 - 1, 5)); - df.setRangeEnd(new Date(2014 - 1900, 6 - 1, 5)); - df.setDateOutOfRangeMessage("Please select a sensible date"); - df.setResolution(Resolution.DAY); - df.setDateFormat("yyyy-MM-dd"); - df.setLenient(true); - df.setShowISOWeekNumbers(true); - df.setParseErrorMessage("You are doing it wrong"); - df.setTimeZone(TimeZone.getTimeZone("GMT+5")); - df.setValue(new Date(2014 - 1900, 5 - 1, 15)); - - return df; - } - - @Test - public void readTimezone() { - testRead(getTimezoneDesign(), getTimezoneExpected()); - } - - @Test - public void writeTimezone() { - testWrite(getTimezoneDesign(), getTimezoneExpected()); - } - - @Test - public void readYearResolution() { - testRead(getYearResolutionDesign(), getYearResolutionExpected()); - } - - @Test - public void writeYearResolution() { - // Writing is always done in full resolution.. - String timeZone = new SimpleDateFormat("Z") - .format(new Date(2020 - 1900, 1 - 1, 1)); - testWrite( - getYearResolutionDesign().replace("2020", - "2020-01-01 00:00:00" + timeZone), - getYearResolutionExpected()); - } - - @Test - public void testReadOnlyValue() { - Date date = new Date(2020 - 1900, 1 - 1, 1); - String timeZone = new SimpleDateFormat("Z").format(date); - String design = ""; - LegacyDateField df = new LegacyDateField(); - df.setResolution(Resolution.YEAR); - df.setValue(date); - df.setReadOnly(true); - - testRead(design, df); - testWrite(design, df); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyPopupDateFieldDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyPopupDateFieldDeclarativeTest.java deleted file mode 100644 index 0777a0c782..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/datefield/LegacyPopupDateFieldDeclarativeTest.java +++ /dev/null @@ -1,73 +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.datefield; - -import java.util.Date; - -import org.junit.Test; - -import com.vaadin.shared.ui.datefield.Resolution; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.v7.ui.LegacyPopupDateField; - -/** - * Tests the declarative support for implementations of - * {@link LegacyPopupDateField}. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class LegacyPopupDateFieldDeclarativeTest - extends DeclarativeTestBase { - - private static final String TAG_NAME = "com_vaadin_v7_ui-legacy-popup-date-field"; - - private String getBasicDesign() { - return "<" + TAG_NAME - + " assistive-text='at' text-field-enabled='false' show-iso-week-numbers resolution=\"MINUTE\" range-end=\"2019-01-15\" input-prompt=\"Pick a day\" value=\"2003-02-27 07:15\">"; - } - - private LegacyPopupDateField getBasicExpected() { - LegacyPopupDateField pdf = new LegacyPopupDateField(); - pdf.setShowISOWeekNumbers(true); - pdf.setResolution(Resolution.MINUTE); - pdf.setRangeEnd(new Date(2019 - 1900, 1 - 1, 15)); - pdf.setInputPrompt("Pick a day"); - pdf.setValue(new Date(2003 - 1900, 2 - 1, 27, 7, 15)); - pdf.setTextFieldEnabled(false); - pdf.setAssistiveText("at"); - return pdf; - } - - @Test - public void readBasic() throws Exception { - testRead(getBasicDesign(), getBasicExpected()); - } - - @Test - public void writeBasic() throws Exception { - testRead(getBasicDesign(), getBasicExpected()); - } - - @Override - public LegacyPopupDateField testRead(String design, - LegacyPopupDateField expected) { - return super.testRead( - " " - + design + "", - expected); - } -} 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 deleted file mode 100644 index f5c315b440..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index a324c02cfc..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 43d2bf8a6a..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index f14b966a2d..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 0ef30316ac..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 3bff93c042..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 5897a1fc2d..0000000000 --- a/compatibility-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 org.junit.Assert; -import org.junit.Test; - -import com.vaadin.ui.Component; -import com.vaadin.ui.Label; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.FooterCell; -import com.vaadin.ui.LegacyGrid.HeaderCell; - -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 deleted file mode 100644 index e5c46e3ba2..0000000000 --- a/compatibility-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 org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Property; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.ui.LegacyGrid; - -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 deleted file mode 100644 index 769532b03b..0000000000 --- a/compatibility-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 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; -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 deleted file mode 100644 index 6bbac2977c..0000000000 --- a/compatibility-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 org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.AbstractInMemoryContainer; -import com.vaadin.ui.LegacyGrid; -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 deleted file mode 100644 index c865bd8494..0000000000 --- a/compatibility-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 org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.ui.Component; -import com.vaadin.ui.Label; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.DetailsGenerator; -import com.vaadin.ui.LegacyGrid.RowReference; - -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 deleted file mode 100644 index 6fe47b22a0..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/GridEditorTest.java +++ /dev/null @@ -1,295 +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 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; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.v7.ui.LegacyField; -import com.vaadin.v7.ui.LegacyTextField; - -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 deleted file mode 100644 index 3bba51ab17..0000000000 --- a/compatibility-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 org.junit.Test; - -import com.vaadin.ui.LegacyGrid; -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 deleted file mode 100644 index 1a2011de73..0000000000 --- a/compatibility-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 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; -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 deleted file mode 100644 index 9f5f67d8be..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 95daf47e25..0000000000 --- a/compatibility-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 org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container.Indexed; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.ui.LegacyGrid; - -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 deleted file mode 100644 index 54e9b30988..0000000000 --- a/compatibility-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 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.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 deleted file mode 100644 index 3183ad9021..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 9b2dde4d24..0000000000 --- a/compatibility-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/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 deleted file mode 100644 index 5a2afed01c..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridColumnDeclarativeTest.java +++ /dev/null @@ -1,102 +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 org.junit.Test; - -import com.vaadin.ui.LegacyGrid; - -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 deleted file mode 100644 index 6d26b40996..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/grid/declarative/GridDeclarativeAttributeTest.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.component.grid.declarative; - -import static org.junit.Assert.assertSame; - -import org.junit.Test; - -import com.vaadin.shared.ui.grid.HeightMode; -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.MultiSelectionModel; -import com.vaadin.ui.LegacyGrid.NoSelectionModel; -import com.vaadin.ui.LegacyGrid.SingleSelectionModel; - -/** - * Tests declarative support for 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 deleted file mode 100644 index 59ab5b8343..0000000000 --- a/compatibility-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 org.junit.Assert; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.LegacyGrid; -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 deleted file mode 100644 index ef536359be..0000000000 --- a/compatibility-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 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.Label; -import com.vaadin.ui.LegacyGrid; -import com.vaadin.ui.LegacyGrid.Column; -import com.vaadin.ui.LegacyGrid.FooterRow; -import com.vaadin.ui.LegacyGrid.HeaderRow; -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 deleted file mode 100644 index 8bf03e9d5e..0000000000 --- a/compatibility-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 org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.ui.LegacyGrid; - -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 deleted file mode 100644 index 1a0e0a1102..0000000000 --- a/compatibility-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 org.junit.Test; - -import com.vaadin.ui.LegacyGrid; -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 deleted file mode 100644 index beb774528f..0000000000 --- a/compatibility-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/compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java deleted file mode 100644 index f0fa4aad55..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectDeclarativeTest.java +++ /dev/null @@ -1,71 +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.listselect; - -import org.junit.Test; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.ListSelect; - -public class ListSelectDeclarativeTest extends DeclarativeTestBase { - - private ListSelect getWithOptionsExpected() { - ListSelect ls = new ListSelect(); - ls.setRows(10); - ls.addItem("Male"); - ls.addItem("Female"); - return ls; - } - - private String getWithOptionsDesign() { - return "\n" - + " \n" - + " \n" - + "\n" + ""; - } - - @Test - public void testReadWithOptions() { - testRead(getWithOptionsDesign(), getWithOptionsExpected()); - } - - @Test - public void testWriteWithOptions() { - testWrite(stripOptionTags(getWithOptionsDesign()), - getWithOptionsExpected()); - } - - private ListSelect getBasicExpected() { - ListSelect ls = new ListSelect(); - ls.setCaption("Hello"); - return ls; - } - - private String getBasicDesign() { - return ""; - } - - @Test - public void testReadBasic() { - testRead(getBasicDesign(), getBasicExpected()); - } - - @Test - public void testWriteBasic() { - testWrite(getBasicDesign(), getBasicExpected()); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectStateTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectStateTest.java deleted file mode 100644 index 595909aa69..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/listselect/ListSelectStateTest.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.tests.server.component.listselect; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.select.AbstractSelectState; -import com.vaadin.ui.ListSelect; - -/** - * Tests for ListSelect State. - * - */ -public class ListSelectStateTest { - - @Test - public void getState_listSelectHasCustomState() { - TestListSelect select = new TestListSelect(); - AbstractSelectState state = select.getState(); - Assert.assertEquals("Unexpected state class", AbstractSelectState.class, - state.getClass()); - } - - private static class TestListSelect extends ListSelect { - @Override - public AbstractSelectState getState() { - return super.getState(); - } - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.java deleted file mode 100644 index 853f38c7d3..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/nativeselect/NativeSelectDeclarativeTest.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.tests.server.component.nativeselect; - -import org.junit.Test; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.NativeSelect; - -/** - * Test cases for reading the properties of selection components. - * - * @author Vaadin Ltd - */ -public class NativeSelectDeclarativeTest - extends DeclarativeTestBase { - - public String getBasicDesign() { - return ""; - - } - - public NativeSelect getBasicExpected() { - NativeSelect ns = new NativeSelect(); - ns.addItem("foo"); - ns.addItem("bar"); - return ns; - } - - @Test - public void testReadBasic() { - testRead(getBasicDesign(), getBasicExpected()); - } - - @Test - public void testWriteBasic() { - testWrite(stripOptionTags(getBasicDesign()), getBasicExpected()); - } - - @Test - public void testReadOnlyValue() { - String design = ""; - - NativeSelect ns = new NativeSelect(); - ns.addItems("foo", "bar"); - ns.setValue("foo"); - ns.setReadOnly(true); - - testRead(design, ns); - - // Selects items are not written out by default - String design2 = ""; - testWrite(design2, ns); - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupListenersTest.java deleted file mode 100644 index 2c2a8a81fb..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupListenersTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.vaadin.tests.server.component.optiongroup; - -import org.junit.Test; - -import com.vaadin.event.FieldEvents.BlurEvent; -import com.vaadin.event.FieldEvents.BlurListener; -import com.vaadin.event.FieldEvents.FocusEvent; -import com.vaadin.event.FieldEvents.FocusListener; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; -import com.vaadin.ui.OptionGroup; - -public class OptionGroupListenersTest extends AbstractListenerMethodsTestBase { - - @Test - public void testFocusListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(OptionGroup.class, FocusEvent.class, - FocusListener.class); - } - - @Test - public void testBlurListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(OptionGroup.class, BlurEvent.class, - BlurListener.class); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupStateTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupStateTest.java deleted file mode 100644 index a6d04576bc..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/optiongroup/OptionGroupStateTest.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.tests.server.component.optiongroup; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.optiongroup.OptionGroupState; -import com.vaadin.ui.OptionGroup; - -/** - * Tests for OptionGroup state. - * - */ -public class OptionGroupStateTest { - - @Test - public void getState_optionGroupHasCustomState() { - TestOptionGroup group = new TestOptionGroup(); - OptionGroupState state = group.getState(); - Assert.assertEquals("Unexpected state class", OptionGroupState.class, - state.getClass()); - } - - @Test - public void getPrimaryStyleName_optionGroupHasCustomPrimaryStyleName() { - OptionGroup layout = new OptionGroup(); - OptionGroupState state = new OptionGroupState(); - Assert.assertEquals("Unexpected primary style name", - state.primaryStyleName, layout.getPrimaryStyleName()); - } - - @Test - public void optionGroupStateHasCustomPrimaryStyleName() { - OptionGroupState state = new OptionGroupState(); - Assert.assertEquals("Unexpected primary style name", - "v-select-optiongroup", state.primaryStyleName); - } - - private static class TestOptionGroup extends OptionGroup { - - @Override - public OptionGroupState getState() { - return super.getState(); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaDeclarativeTest.java deleted file mode 100644 index eec1781f81..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaDeclarativeTest.java +++ /dev/null @@ -1,70 +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.richtextarea; - -import org.junit.Test; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.RichTextArea; - -public class RichTextAreaDeclarativeTest - extends DeclarativeTestBase { - - private String getBasicDesign() { - return "\n" - + "\n Header
    Some text\n " - + "
    "; - } - - private RichTextArea getBasicExpected() { - RichTextArea rta = new RichTextArea(); - rta.setNullRepresentation(""); - rta.setNullSettingAllowed(true); - rta.setValue("Header \n
    Some text"); - return rta; - } - - @Test - public void testBasicRead() { - testRead(getBasicDesign(), getBasicExpected()); - } - - @Test - public void testBasicWrite() { - testWrite(getBasicDesign(), getBasicExpected()); - } - - @Test - public void testReadEmpty() { - testRead("", new RichTextArea()); - } - - @Test - public void testWriteEmpty() { - testWrite("", new RichTextArea()); - } - - @Test - public void testReadOnlyValue() { - String design = "Hello World!"; - RichTextArea ta = new RichTextArea(); - ta.setValue("Hello World!"); - ta.setReadOnly(true); - - testRead(design, ta); - testWrite(design, ta); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaStateTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaStateTest.java deleted file mode 100644 index feb3b2e7b0..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/richtextarea/RichTextAreaStateTest.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.richtextarea; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.textarea.RichTextAreaState; -import com.vaadin.ui.RichTextArea; - -/** - * Tests for RichTextArea State. - * - */ -public class RichTextAreaStateTest { - @Test - public void getState_areaHasCustomState() { - TestRichTextArea area = new TestRichTextArea(); - RichTextAreaState state = area.getState(); - Assert.assertEquals("Unexpected state class", RichTextAreaState.class, - state.getClass()); - } - - @Test - public void getPrimaryStyleName_areaHasCustomPrimaryStyleName() { - RichTextArea area = new RichTextArea(); - RichTextAreaState state = new RichTextAreaState(); - Assert.assertEquals("Unexpected primary style name", - state.primaryStyleName, area.getPrimaryStyleName()); - } - - @Test - public void areaStateHasCustomPrimaryStyleName() { - RichTextAreaState state = new RichTextAreaState(); - Assert.assertEquals("Unexpected primary style name", "v-richtextarea", - state.primaryStyleName); - } - - private static class TestRichTextArea extends RichTextArea { - - @Override - public RichTextAreaState getState() { - return super.getState(); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/select/SelectListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/select/SelectListenersTest.java deleted file mode 100644 index 1b0ff1caff..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/select/SelectListenersTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.vaadin.tests.server.component.select; - -import org.junit.Test; - -import com.vaadin.event.FieldEvents.BlurEvent; -import com.vaadin.event.FieldEvents.BlurListener; -import com.vaadin.event.FieldEvents.FocusEvent; -import com.vaadin.event.FieldEvents.FocusListener; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; -import com.vaadin.ui.Select; - -public class SelectListenersTest extends AbstractListenerMethodsTestBase { - - @Test - public void testFocusListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Select.class, FocusEvent.class, - FocusListener.class); - } - - @Test - public void testBlurListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Select.class, BlurEvent.class, - BlurListener.class); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/CacheUpdateExceptionCausesTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/CacheUpdateExceptionCausesTest.java deleted file mode 100644 index 86bb34145f..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/CacheUpdateExceptionCausesTest.java +++ /dev/null @@ -1,55 +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.tests.server.component.table; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.ui.Table; -import com.vaadin.ui.Table.CacheUpdateException; - -public class CacheUpdateExceptionCausesTest { - @Test - public void testSingleCauseException() { - Table table = new Table(); - Throwable[] causes = new Throwable[] { - new RuntimeException("Broken in one way.") }; - - CacheUpdateException exception = new CacheUpdateException(table, - "Error during Table cache update.", causes); - - Assert.assertSame(causes[0], exception.getCause()); - Assert.assertEquals("Error during Table cache update.", - exception.getMessage()); - } - - @Test - public void testMultipleCauseException() { - Table table = new Table(); - Throwable[] causes = new Throwable[] { - new RuntimeException("Broken in the first way."), - new RuntimeException("Broken in the second way.") }; - - CacheUpdateException exception = new CacheUpdateException(table, - "Error during Table cache update.", causes); - - Assert.assertSame(causes[0], exception.getCause()); - Assert.assertEquals( - "Error during Table cache update. Additional causes not shown.", - exception.getMessage()); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/FooterTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/FooterTest.java deleted file mode 100644 index 3df2b8fbcb..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/FooterTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.vaadin.tests.server.component.table; - -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 org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.ui.Table; - -/** - * Test case for testing the footer API - * - */ -public class FooterTest { - - /** - * Tests if setting the footer visibility works properly - */ - @Test - public void testFooterVisibility() { - Table table = new Table("Test table", createContainer()); - - // The footer should by default be hidden - assertFalse(table.isFooterVisible()); - - // Set footer visibility to tru should be reflected in the - // isFooterVisible() method - table.setFooterVisible(true); - assertTrue(table.isFooterVisible()); - } - - /** - * Tests adding footers to the columns - */ - @Test - public void testAddingFooters() { - Table table = new Table("Test table", createContainer()); - - // Table should not contain any footers at initialization - assertNull(table.getColumnFooter("col1")); - assertNull(table.getColumnFooter("col2")); - assertNull(table.getColumnFooter("col3")); - - // Adding column footer - table.setColumnFooter("col1", "Footer1"); - assertEquals("Footer1", table.getColumnFooter("col1")); - - // Add another footer - table.setColumnFooter("col2", "Footer2"); - assertEquals("Footer2", table.getColumnFooter("col2")); - - // Add footer for a non-existing column - table.setColumnFooter("fail", "FooterFail"); - } - - /** - * Test removing footers - */ - @Test - public void testRemovingFooters() { - Table table = new Table("Test table", createContainer()); - table.setColumnFooter("col1", "Footer1"); - table.setColumnFooter("col2", "Footer2"); - - // Test removing footer - assertNotNull(table.getColumnFooter("col1")); - table.setColumnFooter("col1", null); - assertNull(table.getColumnFooter("col1")); - - // The other footer should still be there - assertNotNull(table.getColumnFooter("col2")); - - // Remove non-existing footer - table.setColumnFooter("fail", null); - } - - /** - * Creates a container with three properties "col1,col2,col3" with 100 items - * - * @return Returns the created table - */ - private static Container createContainer() { - IndexedContainer container = new IndexedContainer(); - container.addContainerProperty("col1", String.class, ""); - container.addContainerProperty("col2", String.class, ""); - container.addContainerProperty("col3", String.class, ""); - - for (int i = 0; i < 100; i++) { - Item item = container.addItem("item " + i); - item.getItemProperty("col1").setValue("first" + i); - item.getItemProperty("col2").setValue("middle" + i); - item.getItemProperty("col3").setValue("last" + i); - } - - return container; - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/MultipleSelectionTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/MultipleSelectionTest.java deleted file mode 100644 index b29357ea6a..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/MultipleSelectionTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.vaadin.tests.server.component.table; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Set; - -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.shared.ui.MultiSelectMode; -import com.vaadin.ui.Table; - -public class MultipleSelectionTest { - - /** - * Tests weather the multiple select mode is set when using Table.set - */ - @Test - @SuppressWarnings("unchecked") - public void testSetMultipleItems() { - Table table = new Table("", createTestContainer()); - - // Tests if multiple selection is set - table.setMultiSelect(true); - assertTrue(table.isMultiSelect()); - - // Test multiselect by setting several items at once - - table.setValue(Arrays.asList("1", new String[] { "3" })); - assertEquals(2, ((Set) table.getValue()).size()); - } - - /** - * Tests setting the multiselect mode of the Table. The multiselect mode - * affects how mouse selection is made in the table by the user. - */ - @Test - public void testSetMultiSelectMode() { - Table table = new Table("", createTestContainer()); - - // Default multiselect mode should be MultiSelectMode.DEFAULT - assertEquals(MultiSelectMode.DEFAULT, table.getMultiSelectMode()); - - // Tests if multiselectmode is set - table.setMultiSelectMode(MultiSelectMode.SIMPLE); - assertEquals(MultiSelectMode.SIMPLE, table.getMultiSelectMode()); - } - - /** - * Creates a testing container for the tests - * - * @return A new container with test items - */ - private Container createTestContainer() { - IndexedContainer container = new IndexedContainer( - Arrays.asList("1", new String[] { "2", "3", "4" })); - return container; - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableColumnAlignmentsTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableColumnAlignmentsTest.java deleted file mode 100644 index ff4c8336fb..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableColumnAlignmentsTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.vaadin.tests.server.component.table; - -import static org.junit.Assert.assertArrayEquals; - -import org.junit.Test; - -import com.vaadin.ui.Table; -import com.vaadin.ui.Table.Align; - -public class TableColumnAlignmentsTest { - - @Test - public void defaultColumnAlignments() { - for (int properties = 0; properties < 10; properties++) { - Table t = TableGeneratorTest - .createTableWithDefaultContainer(properties, 10); - Object[] expected = new Object[properties]; - for (int i = 0; i < properties; i++) { - expected[i] = Align.LEFT; - } - org.junit.Assert.assertArrayEquals("getColumnAlignments", expected, - t.getColumnAlignments()); - } - } - - @Test - public void explicitColumnAlignments() { - int properties = 5; - Table t = TableGeneratorTest.createTableWithDefaultContainer(properties, - 10); - Align[] explicitAlignments = new Align[] { Align.CENTER, Align.LEFT, - Align.RIGHT, Align.RIGHT, Align.LEFT }; - - t.setColumnAlignments(explicitAlignments); - - assertArrayEquals("Explicit visible columns, 5 properties", - explicitAlignments, t.getColumnAlignments()); - } - - @Test - public void invalidColumnAlignmentStrings() { - Table t = TableGeneratorTest.createTableWithDefaultContainer(3, 7); - Align[] defaultAlignments = new Align[] { Align.LEFT, Align.LEFT, - Align.LEFT }; - try { - t.setColumnAlignments(new Align[] { Align.RIGHT, Align.RIGHT }); - junit.framework.Assert - .fail("No exception thrown for invalid array length"); - } catch (IllegalArgumentException e) { - // Ok, expected - } - - assertArrayEquals("Invalid change affected alignments", - defaultAlignments, t.getColumnAlignments()); - - } - - @Test - public void columnAlignmentForPropertyNotInContainer() { - Table t = TableGeneratorTest.createTableWithDefaultContainer(3, 7); - Align[] defaultAlignments = new Align[] { Align.LEFT, Align.LEFT, - Align.LEFT }; - try { - t.setColumnAlignment("Property 1200", Align.LEFT); - // FIXME: Uncomment as there should be an exception (#6475) - // junit.framework.Assert - // .fail("No exception thrown for property not in container"); - } catch (IllegalArgumentException e) { - // Ok, expected - } - - assertArrayEquals("Invalid change affected alignments", - defaultAlignments, t.getColumnAlignments()); - - // FIXME: Uncomment as null should be returned (#6474) - // junit.framework.Assert.assertEquals( - // "Column alignment for property not in container returned", - // null, t.getColumnAlignment("Property 1200")); - - } - - @Test - public void invalidColumnAlignmentsLength() { - Table t = TableGeneratorTest.createTableWithDefaultContainer(7, 7); - Align[] defaultAlignments = new Align[] { Align.LEFT, Align.LEFT, - Align.LEFT, Align.LEFT, Align.LEFT, Align.LEFT, Align.LEFT }; - - try { - t.setColumnAlignments(new Align[] { Align.LEFT }); - junit.framework.Assert - .fail("No exception thrown for invalid array length"); - } catch (IllegalArgumentException e) { - // Ok, expected - } - assertArrayEquals("Invalid change affected alignments", - defaultAlignments, t.getColumnAlignments()); - - try { - t.setColumnAlignments(new Align[] {}); - junit.framework.Assert - .fail("No exception thrown for invalid array length"); - } catch (IllegalArgumentException e) { - // Ok, expected - } - assertArrayEquals("Invalid change affected alignments", - defaultAlignments, t.getColumnAlignments()); - - try { - t.setColumnAlignments(new Align[] { Align.LEFT, Align.LEFT, - Align.LEFT, Align.LEFT, Align.LEFT, Align.LEFT, Align.LEFT, - Align.LEFT }); - junit.framework.Assert - .fail("No exception thrown for invalid array length"); - } catch (IllegalArgumentException e) { - // Ok, expected - } - assertArrayEquals("Invalid change affected alignments", - defaultAlignments, t.getColumnAlignments()); - - } - - @Test - public void explicitColumnAlignmentOneByOne() { - int properties = 5; - Table t = TableGeneratorTest.createTableWithDefaultContainer(properties, - 10); - Align[] explicitAlignments = new Align[] { Align.CENTER, Align.LEFT, - Align.RIGHT, Align.RIGHT, Align.LEFT }; - - Align[] currentAlignments = new Align[] { Align.LEFT, Align.LEFT, - Align.LEFT, Align.LEFT, Align.LEFT }; - - for (int i = 0; i < properties; i++) { - t.setColumnAlignment("Property " + i, explicitAlignments[i]); - currentAlignments[i] = explicitAlignments[i]; - - assertArrayEquals( - "Explicit visible columns, " + i + " alignments set", - currentAlignments, t.getColumnAlignments()); - } - - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableContextClickTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableContextClickTest.java deleted file mode 100644 index 3ee8b76d76..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableContextClickTest.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.tests.server.component.table; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.event.ContextClickEvent; -import com.vaadin.event.ContextClickEvent.ContextClickListener; -import com.vaadin.shared.ui.table.TableConstants.Section; -import com.vaadin.ui.Table; - -public class TableContextClickTest extends Table { - - private String error = null; - private boolean handled = false; - - @Test - public void testContextClickListenerWithTableEvent() { - addContextClickListener(new ContextClickListener() { - - @Override - public void contextClick(ContextClickEvent event) { - if (!(event instanceof TableContextClickEvent)) { - return; - } - - TableContextClickEvent e = (TableContextClickEvent) event; - if (e.getSection() != Section.BODY) { - error = "Event section was not BODY."; - } - handled = true; - } - }); - fireEvent(new TableContextClickEvent(this, null, null, null, - Section.BODY)); - - if (error != null) { - Assert.fail(error); - } else if (!handled) { - Assert.fail("Event was not handled by the ContextClickListener"); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTest.java deleted file mode 100644 index 53f3d3acb8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTest.java +++ /dev/null @@ -1,179 +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.table; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.server.ExternalResource; -import com.vaadin.shared.ui.MultiSelectMode; -import com.vaadin.ui.Table; -import com.vaadin.ui.Table.Align; -import com.vaadin.ui.Table.ColumnHeaderMode; -import com.vaadin.ui.Table.RowHeaderMode; -import com.vaadin.ui.Table.TableDragMode; - -/** - * Test declarative support for {@link Table}. - * - * @since - * @author Vaadin Ltd - */ -public class TableDeclarativeTest extends TableDeclarativeTestBase { - - @Test - public void testBasicAttributes() { - - String design = "<" + getTag() - + " page-length=30 cache-rate=3 selectable editable " - + "sortable=false sort-ascending=false sort-container-property-id=foo " - + "drag-mode=row multi-select-mode=simple column-header-mode=id row-header-mode=id " - + "column-reordering-allowed column-collapsing-allowed />"; - - Table table = getTable(); - table.setPageLength(30); - table.setCacheRate(3); - table.setSelectable(true); - table.setEditable(true); - - table.setSortEnabled(false); - table.setSortAscending(false); - table.setSortContainerPropertyId("foo"); - - table.setDragMode(TableDragMode.ROW); - table.setMultiSelectMode(MultiSelectMode.SIMPLE); - table.setColumnHeaderMode(ColumnHeaderMode.ID); - table.setRowHeaderMode(RowHeaderMode.ID); - - table.setColumnReorderingAllowed(true); - table.setColumnCollapsingAllowed(true); - - testRead(design, table); - testWrite(design, table); - } - - @Test - public void testColumns() { - String design = "<" + getTag() + " column-collapsing-allowed>" // - + " " // - + " " + " " - + " " - + " " - + " " // - + "
    "; - - Table table = getTable(); - table.setColumnCollapsingAllowed(true); - - table.addContainerProperty("foo", String.class, null); - table.setColumnAlignment("foo", Align.LEFT); - table.setColumnWidth("foo", 300); - - table.addContainerProperty("bar", String.class, null); - table.setColumnAlignment("bar", Align.CENTER); - table.setColumnExpandRatio("bar", 1); - table.setColumnCollapsible("bar", false); - - table.addContainerProperty("baz", String.class, null); - table.setColumnAlignment("baz", Align.RIGHT); - table.setColumnExpandRatio("baz", 2); - table.setColumnCollapsed("baz", true); - - testRead(design, table); - testWrite(design, table); - } - - @Test - public void testHeadersFooters() { - String design = "<" + getTag() + ">" // - + " " // - + " " // - + " " // - + " " // - + " " // - + " " // - + "
    FOOBAR" // - + "
    foobar" // - + "
    "; - - Table table = getTable(); - table.setFooterVisible(true); - - table.addContainerProperty("foo", String.class, null); - table.setColumnHeader("foo", "FOO"); - table.setColumnIcon("foo", - new ExternalResource("http://example.com/icon.png")); - table.setColumnFooter("foo", "foo"); - - table.addContainerProperty("bar", String.class, null); - table.setColumnHeader("bar", "BAR"); - table.setColumnFooter("bar", "bar"); - - testRead(design, table); - testWrite(design, table); - } - - @Test - public void testInlineData() { - String design = "<" + getTag() + ">" // - + " " // - + " " + " " - + " " - + " " // - + " " + " " - + " " - + " " + " " - + " " // - + " " // - + " " // - + " " // - + " " // - + " " // - + "
    DescriptionMilestoneStatus
    r1c1r1c2r1c3
    r2c1r2c2r2c3
    F1F2F3
    "; - - Table table = getTable(); - table.addContainerProperty("foo", String.class, null); - table.addContainerProperty("bar", String.class, null); - table.addContainerProperty("baz", String.class, null); - table.setColumnHeaders("Description", "Milestone", "Status"); - table.setColumnFooter("foo", "F1"); - table.setColumnFooter("bar", "F2"); - table.setColumnFooter("baz", "F3"); - table.addItem(new Object[] { "r1c1", "r1c2", "r1c3" }, "1"); - table.addItem(new Object[] { "r2c1", "r2c2", "r2c3" }, "2"); - table.setFooterVisible(true); - - testRead(design, table); - testWrite(design, table, true); - } - - @Test - public void testHtmlEntities() { - String design = "" + "" + " " - + " " - + " " + " " - + " " + " " - + " " + " " - + " " + " " - + " " + "
    & Test
    & Test
    & Test
    " + "
    "; - Table read = read(design); - - Assert.assertEquals("& Test", - read.getContainerProperty("test", "test").getValue()); - Assert.assertEquals("& Test", read.getColumnHeader("test")); - Assert.assertEquals("& Test", read.getColumnFooter("test")); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTestBase.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTestBase.java deleted file mode 100644 index 7a7b13e933..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableDeclarativeTestBase.java +++ /dev/null @@ -1,74 +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.table; - -import static org.junit.Assert.assertTrue; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.Table; - -public abstract class TableDeclarativeTestBase - extends DeclarativeTestBase { - - @Override - public Table testRead(String design, Table expected) { - Table read = super.testRead(design, expected); - compareColumns(read, expected); - compareBody(read, expected); - return read; - } - - protected Table getTable() { - return new Table(); - } - - protected String getTag() { - return "vaadin-table"; - } - - protected void compareBody(Table read, Table expected) { - assertEquals("number of items", expected.getItemIds().size(), - read.getItemIds().size()); - for (Object rowId : expected.getItemIds()) { - assertTrue(read.containsId(rowId)); - for (Object propertyId : read.getVisibleColumns()) { - Object expectedItem = expected.getContainerProperty(rowId, - propertyId); - Object readItem = read.getContainerProperty(rowId, propertyId); - assertEquals("property '" + propertyId + "'", expectedItem, - readItem); - } - } - } - - protected void compareColumns(Table read, Table expected) { - for (Object pid : expected.getVisibleColumns()) { - String col = "column '" + pid + "'"; - assertEquals(col + " width", expected.getColumnWidth(pid), - read.getColumnWidth(pid)); - assertEquals(col + " expand ratio", - expected.getColumnExpandRatio(pid), - read.getColumnExpandRatio(pid)); - assertEquals(col + " collapsible", - expected.isColumnCollapsible(pid), - read.isColumnCollapsible(pid)); - assertEquals(col + " collapsed", expected.isColumnCollapsed(pid), - read.isColumnCollapsed(pid)); - assertEquals(col + " footer", expected.getColumnFooter(pid), - read.getColumnFooter(pid)); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableGeneratorTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableGeneratorTest.java deleted file mode 100644 index 6fbe5557f8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableGeneratorTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.vaadin.tests.server.component.table; - -import org.junit.Test; - -import com.vaadin.data.Item; -import com.vaadin.ui.Table; - -public class TableGeneratorTest { - public static Table createTableWithDefaultContainer(int properties, - int items) { - Table t = new Table(); - - for (int i = 0; i < properties; i++) { - t.addContainerProperty("Property " + i, String.class, null); - } - - for (int j = 0; j < items; j++) { - Item item = t.addItem("Item " + j); - for (int i = 0; i < properties; i++) { - item.getItemProperty("Property " + i) - .setValue("Item " + j + "/Property " + i); - } - } - - return t; - } - - @Test - public void testTableGenerator() { - Table t = createTableWithDefaultContainer(1, 1); - junit.framework.Assert.assertEquals(t.size(), 1); - junit.framework.Assert.assertEquals(t.getContainerPropertyIds().size(), - 1); - - t = createTableWithDefaultContainer(100, 50); - junit.framework.Assert.assertEquals(t.size(), 50); - junit.framework.Assert.assertEquals(t.getContainerPropertyIds().size(), - 100); - - } - -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableListenersTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableListenersTest.java deleted file mode 100644 index ac6fb171e3..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableListenersTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.vaadin.tests.server.component.table; - -import org.junit.Test; - -import com.vaadin.event.ItemClickEvent; -import com.vaadin.event.ItemClickEvent.ItemClickListener; -import com.vaadin.tests.server.component.AbstractListenerMethodsTestBase; -import com.vaadin.ui.Table; -import com.vaadin.ui.Table.ColumnReorderEvent; -import com.vaadin.ui.Table.ColumnReorderListener; -import com.vaadin.ui.Table.ColumnResizeEvent; -import com.vaadin.ui.Table.ColumnResizeListener; -import com.vaadin.ui.Table.FooterClickEvent; -import com.vaadin.ui.Table.FooterClickListener; -import com.vaadin.ui.Table.HeaderClickEvent; -import com.vaadin.ui.Table.HeaderClickListener; - -public class TableListenersTest extends AbstractListenerMethodsTestBase { - - @Test - public void testColumnResizeListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Table.class, ColumnResizeEvent.class, - ColumnResizeListener.class); - } - - @Test - public void testItemClickListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Table.class, ItemClickEvent.class, - ItemClickListener.class); - } - - @Test - public void testFooterClickListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Table.class, FooterClickEvent.class, - FooterClickListener.class); - } - - @Test - public void testHeaderClickListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Table.class, HeaderClickEvent.class, - HeaderClickListener.class); - } - - @Test - public void testColumnReorderListenerAddGetRemove() throws Exception { - testListenerAddGetRemove(Table.class, ColumnReorderEvent.class, - ColumnReorderListener.class); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TablePropertyValueConverterTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TablePropertyValueConverterTest.java deleted file mode 100644 index 5a9297014c..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TablePropertyValueConverterTest.java +++ /dev/null @@ -1,384 +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.table; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Map.Entry; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.Container; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.ui.Table; -import com.vaadin.v7.data.util.converter.LegacyConverter; - -public class TablePropertyValueConverterTest { - protected TestableTable table; - protected Collection initialProperties; - - @Test - public void testRemovePropertyId() { - Collection converters = table.getCurrentConverters(); - assertTrue("Set of converters was empty at the start.", - converters.size() > 0); - - Object firstId = converters.iterator().next(); - - table.removeContainerProperty(firstId); - - Collection converters2 = table.getCurrentConverters(); - assertTrue("FirstId was not removed", !converters2.contains(firstId)); - - assertTrue("The number of removed converters was not one.", - converters.size() - converters2.size() == 1); - - for (Object originalId : converters) { - if (!originalId.equals(firstId)) { - assertTrue("The wrong converter was removed.", - converters2.contains(originalId)); - } - } - - } - - @Test - public void testSetContainer() { - table.setContainerDataSource(createContainer( - new String[] { "col1", "col3", "col4", "col5" })); - Collection converters = table.getCurrentConverters(); - assertTrue("There should only have been one converter left.", - converters.size() == 1); - Object onlyKey = converters.iterator().next(); - assertTrue("The incorrect key was left.", onlyKey.equals("col1")); - - } - - @Test - public void testSetContainerWithInexactButCompatibleTypes() { - TestableTable customTable = new TestableTable("Test table", - createContainer(new String[] { "col1", "col2", "col3" }, - new Class[] { String.class, BaseClass.class, - DerivedClass.class })); - customTable.setConverter("col1", new LegacyConverter() { - private static final long serialVersionUID = 1L; - - @Override - public String convertToModel(String value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return "model"; - } - - @Override - public String convertToPresentation(String value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return "presentation"; - } - - @Override - public Class getModelType() { - return String.class; - } - - @Override - public Class getPresentationType() { - return String.class; - } - - }); - customTable.setConverter("col2", - new LegacyConverter() { - private static final long serialVersionUID = 1L; - - @Override - public BaseClass convertToModel(String value, - Class targetType, - Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return new BaseClass("model"); - } - - @Override - public Class getModelType() { - return BaseClass.class; - } - - @Override - public Class getPresentationType() { - return String.class; - } - - @Override - public String convertToPresentation(BaseClass value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return null; - } - }); - customTable.setConverter("col3", - new LegacyConverter() { - private static final long serialVersionUID = 1L; - - @Override - public DerivedClass convertToModel(String value, - Class targetType, - Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return new DerivedClass("derived" + 1001); - } - - @Override - public Class getModelType() { - return DerivedClass.class; - } - - @Override - public Class getPresentationType() { - return String.class; - } - - @Override - public String convertToPresentation(DerivedClass value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return null; - } - }); - customTable.setContainerDataSource( - createContainer(new String[] { "col1", "col2", "col3" }, - new Class[] { DerivedClass.class, DerivedClass.class, - BaseClass.class })); - Set converters = customTable.getCurrentConverters(); - // TODO Test temporarily disabled as this feature - // is not yet implemented in Table - /* - * assertTrue("Incompatible types were not removed.", converters.size() - * <= 1); assertTrue("Even compatible types were removed", - * converters.size() == 1); assertTrue("Compatible type was missing.", - * converters.contains("col2")); - */ - } - - @Test - public void testPrimitiveTypeConverters() { - TestableTable customTable = new TestableTable("Test table", - createContainer(new String[] { "col1", "col2", "col3" }, - new Class[] { int.class, BaseClass.class, - DerivedClass.class })); - customTable.setConverter("col1", - new LegacyConverter() { - private static final long serialVersionUID = 1L; - - @Override - public Integer convertToModel(String value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return 11; - } - - @Override - public String convertToPresentation(Integer value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return "presentation"; - } - - @Override - public Class getModelType() { - return Integer.class; - } - - @Override - public Class getPresentationType() { - return String.class; - } - }); - Set converters = customTable.getCurrentConverters(); - assertTrue("Converter was not set.", converters.size() > 0); - } - - @Test - public void testInheritance() { - assertTrue("BaseClass isn't assignable from DerivedClass", - BaseClass.class.isAssignableFrom(DerivedClass.class)); - assertFalse("DerivedClass is assignable from BaseClass", - DerivedClass.class.isAssignableFrom(BaseClass.class)); - } - - @Before - public void setUp() { - table = new TestableTable("Test table", - createContainer(new String[] { "col1", "col2", "col3" })); - table.setConverter("col1", new LegacyConverter() { - private static final long serialVersionUID = 1L; - - @Override - public String convertToModel(String value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return "model"; - } - - @Override - public String convertToPresentation(String value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return "presentation"; - } - - @Override - public Class getModelType() { - return String.class; - } - - @Override - public Class getPresentationType() { - return String.class; - } - - }); - - table.setConverter("col2", new LegacyConverter() { - private static final long serialVersionUID = 1L; - - @Override - public String convertToModel(String value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return "model2"; - } - - @Override - public String convertToPresentation(String value, - Class targetType, Locale locale) - throws com.vaadin.v7.data.util.converter.LegacyConverter.ConversionException { - return "presentation2"; - } - - @Override - public Class getModelType() { - return String.class; - } - - @Override - public Class getPresentationType() { - return String.class; - } - - }); - - initialProperties = table.getContainerPropertyIds(); - } - - private static Container createContainer(Object[] ids) { - Class[] types = new Class[ids.length]; - for (int i = 0; i < types.length; ++i) { - types[i] = String.class; - } - return createContainer(ids, types); - } - - private static Container createContainer(Object[] ids, Class[] types) { - IndexedContainer container = new IndexedContainer(); - if (ids.length > types.length) { - throw new IllegalArgumentException("Too few defined types"); - } - for (int i = 0; i < ids.length; ++i) { - container.addContainerProperty(ids[i], types[i], ""); - } - - for (int i = 0; i < 100; i++) { - Item item = container.addItem("item " + i); - for (int j = 0; j < ids.length; ++j) { - Property itemProperty = item.getItemProperty(ids[j]); - if (types[j] == String.class) { - itemProperty.setValue(ids[j].toString() + i); - } else if (types[j] == BaseClass.class) { - itemProperty.setValue(new BaseClass("base" + i)); - } else if (types[j] == DerivedClass.class) { - itemProperty.setValue(new DerivedClass("derived" + i)); - } else if (types[j] == int.class) { - // FIXME can't set values because the int is autoboxed into - // an Integer and not unboxed prior to set - - // itemProperty.setValue(i); - } else { - throw new IllegalArgumentException( - "Unhandled type in createContainer: " + types[j]); - } - } - } - - return container; - } - - private class TestableTable extends Table { - /** - * @param string - * @param createContainer - */ - public TestableTable(String string, Container container) { - super(string, container); - } - - Set getCurrentConverters() { - try { - Field f = Table.class - .getDeclaredField("propertyValueConverters"); - f.setAccessible(true); - HashMap> pvc = (HashMap>) f - .get(this); - Set currentConverters = new HashSet(); - for (Entry> entry : pvc - .entrySet()) { - currentConverters.add(entry.getKey()); - } - return currentConverters; - - } catch (Exception e) { - fail("Unable to retrieve propertyValueConverters"); - return null; - } - } - } - - private static class BaseClass { - private String title; - - public BaseClass(String title) { - this.title = title; - } - } - - private static class DerivedClass extends BaseClass { - public DerivedClass(String title) { - super(title); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSelectableTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSelectableTest.java deleted file mode 100644 index 906827958f..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSelectableTest.java +++ /dev/null @@ -1,75 +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.table; - -import org.easymock.EasyMock; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.ui.Table; - -/** - * Tests for 'selectable' property of {@link Table} class. - * - * @author Vaadin Ltd - */ -public class TableSelectableTest { - - @Test - public void setSelectable_explicitSelectable_tableIsSelectable() { - Table table = new Table(); - table.setSelectable(true); - - Assert.assertTrue(table.isSelectable()); - } - - @Test - public void addValueChangeListener_explicitSelectable_tableIsSelectable() { - TestTable table = new TestTable(); - table.addValueChangeListener( - EasyMock.createMock(ValueChangeListener.class)); - - Assert.assertTrue(table.isSelectable()); - Assert.assertTrue(table.markAsDirtyCalled); - } - - @Test - public void tableIsNotSelectableByDefult() { - Table table = new Table(); - - Assert.assertFalse(table.isSelectable()); - } - - @Test - public void setSelectable_explicitNotSelectable_tableIsNotSelectable() { - Table table = new Table(); - table.setSelectable(false); - table.addValueChangeListener( - EasyMock.createMock(ValueChangeListener.class)); - - Assert.assertFalse(table.isSelectable()); - } - - private static final class TestTable extends Table { - @Override - public void markAsDirty() { - markAsDirtyCalled = true; - } - - private boolean markAsDirtyCalled; - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSerializationTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSerializationTest.java deleted file mode 100644 index 22f2381980..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableSerializationTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.vaadin.tests.server.component.table; - -import org.apache.commons.lang.SerializationUtils; -import org.junit.Test; - -import com.vaadin.ui.Table; - -public class TableSerializationTest { - - @Test - public void testSerialization() { - Table t = new Table(); - byte[] ser = SerializationUtils.serialize(t); - Table t2 = (Table) SerializationUtils.deserialize(ser); - - } - - @Test - public void testSerializationWithRowHeaders() { - Table t = new Table(); - t.setRowHeaderMode(Table.ROW_HEADER_MODE_EXPLICIT); - t.setColumnWidth(null, 100); - byte[] ser = SerializationUtils.serialize(t); - Table t2 = (Table) SerializationUtils.deserialize(ser); - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableStateTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableStateTest.java deleted file mode 100644 index 43b03c41e8..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableStateTest.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.tests.server.component.table; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.shared.ui.table.TableState; -import com.vaadin.ui.Table; - -/** - * Tests for Table State. - * - */ -public class TableStateTest { - - @Test - public void getState_tableHasCustomState() { - TestTable table = new TestTable(); - TableState state = table.getState(); - Assert.assertEquals("Unexpected state class", TableState.class, - state.getClass()); - } - - @Test - public void getPrimaryStyleName_tableHasCustomPrimaryStyleName() { - Table table = new Table(); - TableState state = new TableState(); - Assert.assertEquals("Unexpected primary style name", - state.primaryStyleName, table.getPrimaryStyleName()); - } - - @Test - public void tableStateHasCustomPrimaryStyleName() { - TableState state = new TableState(); - Assert.assertEquals("Unexpected primary style name", "v-table", - state.primaryStyleName); - } - - private static class TestTable extends Table { - - @Override - public TableState getState() { - return super.getState(); - } - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableVisibleColumnsTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableVisibleColumnsTest.java deleted file mode 100644 index f9f6e7f979..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/table/TableVisibleColumnsTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.vaadin.tests.server.component.table; - -import static org.junit.Assert.assertArrayEquals; - -import org.junit.Test; - -import com.vaadin.ui.Table; - -public class TableVisibleColumnsTest { - - String[] defaultColumns3 = new String[] { "Property 0", "Property 1", - "Property 2" }; - - @Test - public void defaultVisibleColumns() { - for (int properties = 0; properties < 10; properties++) { - Table t = TableGeneratorTest - .createTableWithDefaultContainer(properties, 10); - Object[] expected = new Object[properties]; - for (int i = 0; i < properties; i++) { - expected[i] = "Property " + i; - } - org.junit.Assert.assertArrayEquals("getVisibleColumns", expected, - t.getVisibleColumns()); - } - } - - @Test - public void explicitVisibleColumns() { - Table t = TableGeneratorTest.createTableWithDefaultContainer(5, 10); - Object[] newVisibleColumns = new Object[] { "Property 1", - "Property 2" }; - t.setVisibleColumns(newVisibleColumns); - assertArrayEquals("Explicit visible columns, 5 properties", - newVisibleColumns, t.getVisibleColumns()); - - } - - @Test - public void invalidVisibleColumnIds() { - Table t = TableGeneratorTest.createTableWithDefaultContainer(3, 10); - - try { - t.setVisibleColumns( - new Object[] { "a", "Property 2", "Property 3" }); - junit.framework.Assert.fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException e) { - // OK, expected - } - assertArrayEquals(defaultColumns3, t.getVisibleColumns()); - } - - @Test - public void duplicateVisibleColumnIds() { - Table t = TableGeneratorTest.createTableWithDefaultContainer(3, 10); - try { - t.setVisibleColumns(new Object[] { "Property 0", "Property 1", - "Property 2", "Property 1" }); - } catch (IllegalArgumentException e) { - // OK, expected - } - assertArrayEquals(defaultColumns3, t.getVisibleColumns()); - } - - @Test - public void noVisibleColumns() { - Table t = TableGeneratorTest.createTableWithDefaultContainer(3, 10); - t.setVisibleColumns(new Object[] {}); - assertArrayEquals(new Object[] {}, t.getVisibleColumns()); - - } -} diff --git a/compatibility-server/src/test/java/com/vaadin/tests/server/component/textarea/TextAreaDeclarativeTest.java b/compatibility-server/src/test/java/com/vaadin/tests/server/component/textarea/TextAreaDeclarativeTest.java deleted file mode 100644 index cbfa33320f..0000000000 --- a/compatibility-server/src/test/java/com/vaadin/tests/server/component/textarea/TextAreaDeclarativeTest.java +++ /dev/null @@ -1,74 +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.textarea; - -import java.io.IOException; - -import org.jsoup.nodes.Element; -import org.jsoup.parser.Tag; -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.tests.design.DeclarativeTestBase; -import com.vaadin.ui.TextArea; -import com.vaadin.ui.declarative.DesignContext; - -/** - * Tests declarative support for implementations of {@link TextArea}. - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class TextAreaDeclarativeTest extends DeclarativeTestBase