diff options
41 files changed, 1116 insertions, 95 deletions
diff --git a/client-compiler/pom.xml b/client-compiler/pom.xml index 1ccfccc666..b9aa8188ef 100644 --- a/client-compiler/pom.xml +++ b/client-compiler/pom.xml @@ -58,7 +58,7 @@ <execution> <id>copy-sources</id> <!-- here the phase you need --> - <phase>prepare-package</phase> + <phase>process-resources</phase> <goals> <goal>copy-resources</goal> </goals> diff --git a/client/pom.xml b/client/pom.xml index 53a497b183..e687aee36e 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -57,7 +57,7 @@ <execution> <id>copy-sources</id> <!-- here the phase you need --> - <phase>prepare-package</phase> + <phase>process-resources</phase> <goals> <goal>copy-resources</goal> </goals> diff --git a/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java b/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java index cc8e3872ad..79df33e9a7 100644 --- a/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/menubar/MenuBarConnector.java @@ -25,9 +25,8 @@ import com.vaadin.client.ApplicationConnection; import com.vaadin.client.Paintable; import com.vaadin.client.TooltipInfo; import com.vaadin.client.UIDL; -import com.vaadin.client.WidgetUtil; import com.vaadin.client.ui.AbstractComponentConnector; -import com.vaadin.client.ui.ImageIcon; +import com.vaadin.client.ui.Icon; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.VMenuBar; import com.vaadin.shared.ui.ComponentStateUtil; @@ -77,11 +76,11 @@ public class MenuBarConnector extends AbstractComponentConnector StringBuffer itemHTML = new StringBuffer(); if (moreItemUIDL.hasAttribute("icon")) { - itemHTML.append("<img src=\"" - + WidgetUtil.escapeAttribute(client.translateVaadinUri( - moreItemUIDL.getStringAttribute("icon"))) - + "\" class=\"" + ImageIcon.CLASSNAME - + "\" alt=\"\" />"); + Icon icon = client + .getIcon(moreItemUIDL.getStringAttribute("icon")); + if (icon != null) { + itemHTML.append(icon.getElement().getString()); + } } String moreItemText = moreItemUIDL.getStringAttribute("text"); diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java index 14c2210696..8ccd4373c2 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java @@ -26,6 +26,7 @@ import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.TableElement; +import com.google.gwt.dom.client.TableRowElement; import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -45,6 +46,7 @@ import com.vaadin.client.widget.grid.RendererCellReference; import com.vaadin.client.widget.grid.events.GridEnabledHandler; import com.vaadin.client.widget.grid.events.GridSelectionAllowedEvent; import com.vaadin.client.widget.grid.events.GridSelectionAllowedHandler; +import com.vaadin.client.widgets.Escalator.AbstractRowContainer; import com.vaadin.client.widgets.Grid; /** @@ -330,7 +332,7 @@ public class MultiSelectionRenderer<T> int constrainedPageY = Math.max(bodyAbsoluteTop, Math.min(bodyAbsoluteBottom, pageY)); - int logicalRow = getLogicalRowIndex(WidgetUtil + int logicalRow = getLogicalRowIndex(grid, WidgetUtil .getElementFromPoint(initialPageX, constrainedPageY)); int incrementOrDecrement = (logicalRow > lastModifiedLogicalRow) ? 1 @@ -584,8 +586,6 @@ public class MultiSelectionRenderer<T> } } - private static final String LOGICAL_ROW_PROPERTY_INT = "vEscalatorLogicalRow"; - private final Grid<T> grid; private HandlerRegistration nativePreviewHandlerRegistration; @@ -629,10 +629,7 @@ public class MultiSelectionRenderer<T> public void render(final RendererCellReference cell, final Boolean data, CheckBox checkBox) { checkBox.setValue(data, false); - checkBox.setEnabled(grid.isEnabled() && !grid.isEditorActive() - && grid.getSelectionModel().isSelectionAllowed()); - checkBox.getElement().setPropertyInt(LOGICAL_ROW_PROPERTY_INT, - cell.getRowIndex()); + checkBox.setEnabled(grid.isEnabled() && !grid.isEditorActive()); } @Override @@ -666,7 +663,7 @@ public class MultiSelectionRenderer<T> private void startDragSelect(NativeEvent event, final Element target) { injectNativeHandler(); - int logicalRowIndex = getLogicalRowIndex(target); + int logicalRowIndex = getLogicalRowIndex(grid, target); autoScrollHandler.start(logicalRowIndex); event.preventDefault(); event.stopPropagation(); @@ -685,7 +682,7 @@ public class MultiSelectionRenderer<T> } } - private int getLogicalRowIndex(final Element target) { + private int getLogicalRowIndex(Grid<T> grid, final Element target) { if (target == null) { return -1; } @@ -705,7 +702,8 @@ public class MultiSelectionRenderer<T> final Element checkbox = td.getFirstChildElement(); assert checkbox != null : "Checkbox has disappeared"; - return checkbox.getPropertyInt(LOGICAL_ROW_PROPERTY_INT); + return ((AbstractRowContainer) grid.getEscalator().getBody()) + .getLogicalRowIndex((TableRowElement) tr); } tr = tr.getNextSiblingElement(); } diff --git a/client/src/main/java/com/vaadin/client/widgets/Escalator.java b/client/src/main/java/com/vaadin/client/widgets/Escalator.java index 3c7b7c0159..239ed9b90b 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Escalator.java +++ b/client/src/main/java/com/vaadin/client/widgets/Escalator.java @@ -53,7 +53,6 @@ import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.dom.client.Touch; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.logging.client.LogConfiguration; -import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.RequiresResize; @@ -702,13 +701,13 @@ public class Escalator extends Widget /*-{ var vScroll = esc.@com.vaadin.client.widgets.Escalator::verticalScrollbar; var vScrollElem = vScroll.@com.vaadin.client.widget.escalator.ScrollbarBundle::getElement()(); - + var hScroll = esc.@com.vaadin.client.widgets.Escalator::horizontalScrollbar; var hScrollElem = hScroll.@com.vaadin.client.widget.escalator.ScrollbarBundle::getElement()(); - + return $entry(function(e) { var target = e.target; - + // in case the scroll event was native (i.e. scrollbars were dragged, or // the scrollTop/Left was manually modified), the bundles have old cache // values. We need to make sure that the caches are kept up to date. @@ -729,29 +728,29 @@ public class Escalator extends Widget return $entry(function(e) { var deltaX = e.deltaX ? e.deltaX : -0.5*e.wheelDeltaX; var deltaY = e.deltaY ? e.deltaY : -0.5*e.wheelDeltaY; - + // Delta mode 0 is in pixels; we don't need to do anything... - + // A delta mode of 1 means we're scrolling by lines instead of pixels // We need to scale the number of lines by the default line height if(e.deltaMode === 1) { var brc = esc.@com.vaadin.client.widgets.Escalator::body; deltaY *= brc.@com.vaadin.client.widgets.Escalator.AbstractRowContainer::getDefaultRowHeight()(); } - + // Other delta modes aren't supported if((e.deltaMode !== undefined) && (e.deltaMode >= 2 || e.deltaMode < 0)) { var msg = "Unsupported wheel delta mode \"" + e.deltaMode + "\""; - + // Print warning message esc.@com.vaadin.client.widgets.Escalator::logWarning(*)(msg); } - + // IE8 has only delta y if (isNaN(deltaY)) { deltaY = -0.5*e.wheelDelta; } - + @com.vaadin.client.widgets.Escalator.JsniUtil::moveScrollFromEvent(*)(esc, deltaX, deltaY, e); }); }-*/; @@ -1098,7 +1097,7 @@ public class Escalator extends Widget } } - protected abstract class AbstractRowContainer implements RowContainer { + public abstract class AbstractRowContainer implements RowContainer { private EscalatorUpdater updater = EscalatorUpdater.NULL; private int rows; @@ -2124,7 +2123,14 @@ public class Escalator extends Widget */ protected abstract double getHeightOfSection(); - protected int getLogicalRowIndex(final TableRowElement tr) { + /** + * Gets the logical row index for the given table row element. + * + * @param tr + * the table row element inside this container. + * @return the logical index of the given element + */ + public int getLogicalRowIndex(final TableRowElement tr) { return tr.getSectionRowIndex(); }; @@ -3433,7 +3439,7 @@ public class Escalator extends Widget } @Override - protected int getLogicalRowIndex(final TableRowElement tr) { + public int getLogicalRowIndex(final TableRowElement tr) { assert tr .getParentNode() == root : "The given element isn't a row element in the body"; int internalIndex = visualRowOrder.indexOf(tr); diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index 9974505bd4..a135827a5a 100644..100755 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -4159,6 +4159,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private GridSpacerUpdater gridSpacerUpdater = new GridSpacerUpdater(); /** A set keeping track of the indices of all currently open details */ private Set<Integer> visibleDetails = new HashSet<>(); + /** A set of indices of details to reopen after detach and on attach */ + private final Set<Integer> reattachVisibleDetails = new HashSet<>(); private boolean columnReorderingAllowed; @@ -6450,8 +6452,14 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, int columnIndex = columns.indexOf(column); // Remove from column configuration - escalator.getColumnConfiguration() - .removeColumns(getVisibleColumns().indexOf(column), 1); + int visibleColumnIndex = getVisibleColumns().indexOf(column); + if (visibleColumnIndex < 0) { + assert column.isHidden(); + // Hidden columns are not included in Escalator + } else { + getEscalator().getColumnConfiguration() + .removeColumns(visibleColumnIndex, 1); + } updateFrozenColumns(); @@ -6830,7 +6838,12 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, return editor; } - protected Escalator getEscalator() { + /** + * Gets the {@link Escalator} used by this Grid instnace. + * + * @return the escalator instance, never <code>null</code> + */ + public Escalator getEscalator() { return escalator; } @@ -8712,11 +8725,17 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, // Grid was just attached to DOM. Column widths should be calculated. recalculateColumnWidths(); + for (int row : reattachVisibleDetails) { + setDetailsVisible(row, true); + } + reattachVisibleDetails.clear(); } @Override protected void onDetach() { Set<Integer> details = new HashSet<>(visibleDetails); + reattachVisibleDetails.clear(); + reattachVisibleDetails.addAll(details); for (int row : details) { setDetailsVisible(row, false); } diff --git a/compatibility-client/pom.xml b/compatibility-client/pom.xml index 898326526c..76c06a3755 100644 --- a/compatibility-client/pom.xml +++ b/compatibility-client/pom.xml @@ -75,7 +75,7 @@ <execution> <id>copy-sources</id> <!-- here the phase you need --> - <phase>prepare-package</phase> + <phase>process-resources</phase> <goals> <goal>copy-resources</goal> </goals> diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/widget/grid/selection/MultiSelectionRenderer.java b/compatibility-client/src/main/java/com/vaadin/v7/client/widget/grid/selection/MultiSelectionRenderer.java index 678d37369f..a7c974aead 100644 --- a/compatibility-client/src/main/java/com/vaadin/v7/client/widget/grid/selection/MultiSelectionRenderer.java +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/widget/grid/selection/MultiSelectionRenderer.java @@ -26,6 +26,7 @@ import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.TableElement; +import com.google.gwt.dom.client.TableRowElement; import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -45,6 +46,7 @@ import com.vaadin.v7.client.widget.grid.RendererCellReference; import com.vaadin.v7.client.widget.grid.events.GridEnabledEvent; import com.vaadin.v7.client.widget.grid.events.GridEnabledHandler; import com.vaadin.v7.client.widget.grid.selection.SelectionModel.Multi.Batched; +import com.vaadin.v7.client.widgets.Escalator.AbstractRowContainer; import com.vaadin.v7.client.widgets.Grid; /** @@ -319,7 +321,7 @@ public class MultiSelectionRenderer<T> int constrainedPageY = Math.max(bodyAbsoluteTop, Math.min(bodyAbsoluteBottom, pageY)); - int logicalRow = getLogicalRowIndex(WidgetUtil + int logicalRow = getLogicalRowIndex(grid, WidgetUtil .getElementFromPoint(initialPageX, constrainedPageY)); int incrementOrDecrement = (logicalRow > lastModifiedLogicalRow) ? 1 @@ -586,8 +588,6 @@ public class MultiSelectionRenderer<T> } } - private static final String LOGICAL_ROW_PROPERTY_INT = "vEscalatorLogicalRow"; - private final Grid<T> grid; private HandlerRegistration nativePreviewHandlerRegistration; @@ -633,8 +633,6 @@ public class MultiSelectionRenderer<T> CheckBox checkBox) { checkBox.setValue(data, false); checkBox.setEnabled(grid.isEnabled() && !grid.isEditorActive()); - checkBox.getElement().setPropertyInt(LOGICAL_ROW_PROPERTY_INT, - cell.getRowIndex()); } @Override @@ -668,7 +666,7 @@ public class MultiSelectionRenderer<T> private void startDragSelect(NativeEvent event, final Element target) { injectNativeHandler(); - int logicalRowIndex = getLogicalRowIndex(target); + int logicalRowIndex = getLogicalRowIndex(grid, target); autoScrollHandler.start(logicalRowIndex); event.preventDefault(); event.stopPropagation(); @@ -687,7 +685,7 @@ public class MultiSelectionRenderer<T> } } - private int getLogicalRowIndex(final Element target) { + private int getLogicalRowIndex(Grid<T> grid, final Element target) { if (target == null) { return -1; } @@ -707,7 +705,8 @@ public class MultiSelectionRenderer<T> final Element checkbox = td.getFirstChildElement(); assert checkbox != null : "Checkbox has disappeared"; - return checkbox.getPropertyInt(LOGICAL_ROW_PROPERTY_INT); + return ((AbstractRowContainer) grid.getEscalator().getBody()) + .getLogicalRowIndex((TableRowElement) tr); } tr = tr.getNextSiblingElement(); } diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java b/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java index f343d45f6f..cb2786a4cf 100644 --- a/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java @@ -673,13 +673,13 @@ public class Escalator extends Widget /*-{ var vScroll = esc.@com.vaadin.v7.client.widgets.Escalator::verticalScrollbar; var vScrollElem = vScroll.@com.vaadin.v7.client.widget.escalator.ScrollbarBundle::getElement()(); - + var hScroll = esc.@com.vaadin.v7.client.widgets.Escalator::horizontalScrollbar; var hScrollElem = hScroll.@com.vaadin.v7.client.widget.escalator.ScrollbarBundle::getElement()(); - + return $entry(function(e) { var target = e.target; - + // in case the scroll event was native (i.e. scrollbars were dragged, or // the scrollTop/Left was manually modified), the bundles have old cache // values. We need to make sure that the caches are kept up to date. @@ -700,29 +700,29 @@ public class Escalator extends Widget return $entry(function(e) { var deltaX = e.deltaX ? e.deltaX : -0.5*e.wheelDeltaX; var deltaY = e.deltaY ? e.deltaY : -0.5*e.wheelDeltaY; - + // Delta mode 0 is in pixels; we don't need to do anything... - + // A delta mode of 1 means we're scrolling by lines instead of pixels // We need to scale the number of lines by the default line height if(e.deltaMode === 1) { var brc = esc.@com.vaadin.v7.client.widgets.Escalator::body; deltaY *= brc.@com.vaadin.v7.client.widgets.Escalator.AbstractRowContainer::getDefaultRowHeight()(); } - + // Other delta modes aren't supported if((e.deltaMode !== undefined) && (e.deltaMode >= 2 || e.deltaMode < 0)) { var msg = "Unsupported wheel delta mode \"" + e.deltaMode + "\""; - + // Print warning message esc.@com.vaadin.v7.client.widgets.Escalator::logWarning(*)(msg); } - + // IE8 has only delta y if (isNaN(deltaY)) { deltaY = -0.5*e.wheelDelta; } - + @com.vaadin.v7.client.widgets.Escalator.JsniUtil::moveScrollFromEvent(*)(esc, deltaX, deltaY, e); }); }-*/; @@ -1044,7 +1044,7 @@ public class Escalator extends Widget } } - protected abstract class AbstractRowContainer implements RowContainer { + public abstract class AbstractRowContainer implements RowContainer { private EscalatorUpdater updater = EscalatorUpdater.NULL; private int rows; @@ -2070,7 +2070,14 @@ public class Escalator extends Widget */ protected abstract double getHeightOfSection(); - protected int getLogicalRowIndex(final TableRowElement tr) { + /** + * Gets the logical row index for the given table row element. + * + * @param tr + * the table row element inside this container + * @return the logical index of the given element + */ + public int getLogicalRowIndex(final TableRowElement tr) { return tr.getSectionRowIndex(); }; @@ -3379,7 +3386,7 @@ public class Escalator extends Widget } @Override - protected int getLogicalRowIndex(final TableRowElement tr) { + public int getLogicalRowIndex(final TableRowElement tr) { assert tr .getParentNode() == root : "The given element isn't a row element in the body"; int internalIndex = visualRowOrder.indexOf(tr); diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java b/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java index e59fc2213f..5e48188128 100644 --- a/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java @@ -4128,6 +4128,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private GridSpacerUpdater gridSpacerUpdater = new GridSpacerUpdater(); /** A set keeping track of the indices of all currently open details */ private Set<Integer> visibleDetails = new HashSet<>(); + /** A set of indices of details to reopen after detach and on attach */ + private final Set<Integer> reattachVisibleDetails = new HashSet<>(); private boolean columnReorderingAllowed; @@ -6451,8 +6453,14 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, int columnIndex = columns.indexOf(column); // Remove from column configuration - escalator.getColumnConfiguration() - .removeColumns(getVisibleColumns().indexOf(column), 1); + int visibleColumnIndex = getVisibleColumns().indexOf(column); + if (visibleColumnIndex < 0) { + assert column.isHidden(); + // Hidden columns are not included in Escalator + } else { + getEscalator().getColumnConfiguration() + .removeColumns(visibleColumnIndex, 1); + } updateFrozenColumns(); @@ -6831,7 +6839,12 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, return editor; } - protected Escalator getEscalator() { + /** + * Gets the {@link Escalator} used by this Grid instnace. + * + * @return the escalator instance, never <code>null</code> + */ + public Escalator getEscalator() { return escalator; } @@ -8766,6 +8779,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, @Override protected void onDetach() { Set<Integer> details = new HashSet<>(visibleDetails); + reattachVisibleDetails.clear(); + reattachVisibleDetails.addAll(details); for (int row : details) { setDetailsVisible(row, false); } 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 index 8bba865ffc..a286cdc3b2 100644 --- 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 @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import com.vaadin.data.util.BeanUtil; +import com.vaadin.v7.data.Property; /** * A wrapper class for adding the Item interface to any Java Bean. @@ -41,7 +42,7 @@ public class BeanItem<BT> extends PropertysetItem { /** * The bean which this Item is based on. */ - private final BT bean; + private BT bean; /** * <p> @@ -264,4 +265,46 @@ public class BeanItem<BT> extends PropertysetItem { return bean; } + /** + * Changes the Java Bean this item is based on. + * <p> + * This will cause any existing properties to be re-mapped to the new bean. + * Any added custom properties which are not of type {@link MethodProperty} + * or {@link NestedMethodProperty} will not be updated to reflect the change + * of bean. + * <p> + * Changing the bean will fire value change events for all properties of + * type {@link MethodProperty} or {@link NestedMethodProperty}. + * + * @param bean + * The new bean to use for this item, not <code>null</code> + */ + public void setBean(BT bean) { + if (bean == null) { + throw new IllegalArgumentException("Bean cannot be null"); + } + + if (getBean().getClass() != bean.getClass()) { + throw new IllegalArgumentException( + "The new bean class " + bean.getClass().getName() + + " does not match the old bean class " + + getBean().getClass()); + } + + // Remap properties + for (Object propertyId : getItemPropertyIds()) { + Property p = getItemProperty(propertyId); + if (p instanceof MethodProperty) { + MethodProperty mp = (MethodProperty) p; + assert (mp.getInstance() == getBean()); + mp.setInstance(bean); + } else if (p instanceof NestedMethodProperty) { + NestedMethodProperty nmp = (NestedMethodProperty) p; + assert (nmp.getInstance() == getBean()); + nmp.setInstance(bean); + } + } + + this.bean = bean; + } } diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/MethodProperty.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/MethodProperty.java index ba5edbb146..e3651d0272 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/util/MethodProperty.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/MethodProperty.java @@ -769,6 +769,37 @@ public class MethodProperty<T> extends AbstractProperty<T> { super.fireValueChange(); } + /** + * The instance used by this property + * + * @return the instance used for fetching the property value + */ + public Object getInstance() { + return instance; + } + + /** + * Sets the instance used by this property. + * <p> + * The new instance must be of the same type as the old instance + * <p> + * To be consistent with {@link #setValue(Object)}, this method will fire a + * value change event even if the value stays the same + * + * @param instance + * the instance to use + */ + public void setInstance(Object instance) { + if (this.instance.getClass() != instance.getClass()) { + throw new IllegalArgumentException("The new instance is of type " + + instance.getClass().getName() + + " which does not match the old instance type " + + this.instance.getClass().getName()); + } + this.instance = instance; + fireValueChange(); + } + private static final Logger getLogger() { return Logger.getLogger(MethodProperty.class.getName()); } diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/util/NestedMethodProperty.java b/compatibility-server/src/main/java/com/vaadin/v7/data/util/NestedMethodProperty.java index 3f9236c4ac..ab23d07551 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/util/NestedMethodProperty.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/util/NestedMethodProperty.java @@ -193,9 +193,10 @@ public class NestedMethodProperty<T> extends AbstractProperty<T> { /** * Gets the value stored in the Property. The value is resolved by calling - * the specified getter method with the argument specified at instantiation. + * the specified getter methods on the current instance: * * @return the value of the Property + * @see #getInstance() */ @Override public T getValue() { @@ -267,4 +268,35 @@ public class NestedMethodProperty<T> extends AbstractProperty<T> { return Collections.unmodifiableList(getMethods); } + /** + * The instance used by this property + * + * @return the instance used for fetching the property value + */ + public Object getInstance() { + return instance; + } + + /** + * Sets the instance used by this property. + * <p> + * The new instance must be of the same type as the old instance + * <p> + * To be consistent with {@link #setValue(Object)}, this method will fire a + * value change event even if the value stays the same + * + * @param instance + * the instance to use + */ + public void setInstance(Object instance) { + if (this.instance.getClass() != instance.getClass()) { + throw new IllegalArgumentException("The new instance is of type " + + instance.getClass().getName() + + " which does not match the old instance type " + + this.instance.getClass().getName()); + } + this.instance = instance; + fireValueChange(); + } + } diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java index cabba91d31..ee9b968c00 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java @@ -2556,6 +2556,9 @@ public class Grid extends AbstractComponent for (CELLTYPE cell : cells.values()) { cell.detach(); } + for (CELLTYPE cell : cellGroups.values()) { + cell.detach(); + } } } diff --git a/compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemTest.java b/compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemTest.java index cc5d72f3df..a9eb3d740b 100644 --- a/compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemTest.java +++ b/compatibility-server/src/test/java/com/vaadin/v7/data/util/BeanItemTest.java @@ -386,4 +386,41 @@ public class BeanItemTest { // Should not be exception property.setValue(null); } + + @Test + public void testChangeBean() { + BeanItem<MyClass> beanItem = new BeanItem<BeanItemTest.MyClass>( + new MyClass("Foo")); + beanItem.setBean(new MyClass("Bar")); + Assert.assertEquals("Bar", beanItem.getItemProperty("name").getValue()); + } + + @Test + public void testChangeBeanNestedProperty() { + BeanItem<MyClass> beanItem = new BeanItem<BeanItemTest.MyClass>( + new MyClass("Foo")); + beanItem.setBean(new MyClass("Bar")); + Assert.assertEquals("Bar", beanItem.getItemProperty("name").getValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testChangeBeanToIncompatibleOne() { + BeanItem<Object> beanItem = new BeanItem<Object>(new MyClass("Foo")); + beanItem.setBean(new Generic<String>()); + } + + @Test(expected = IllegalArgumentException.class) + public void testChangeBeanToSubclass() { + BeanItem<MyClass> beanItem = new BeanItem<BeanItemTest.MyClass>( + new MyClass("Foo")); + beanItem.setBean(new MyClass("Bar")); + beanItem.setBean(new MyClass2("foo")); + } + + @Test(expected = IllegalArgumentException.class) + public void testChangeBeanToNull() { + BeanItem<Object> beanItem = new BeanItem<Object>(new MyClass("Foo")); + beanItem.setBean(null); + } + } diff --git a/compatibility-server/src/test/java/com/vaadin/v7/data/util/MethodPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/v7/data/util/MethodPropertyTest.java new file mode 100644 index 0000000000..61c66b9167 --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/v7/data/util/MethodPropertyTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS 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 org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.v7.data.util.NestedMethodPropertyTest.Address; + +public class MethodPropertyTest { + + private Address testObject; + + @Before + public void setup() { + testObject = new Address("some street", 123); + } + + @Test + public void getValue() { + MethodProperty<String> mp = new MethodProperty<String>(testObject, + "street"); + Assert.assertEquals("some street", mp.getValue()); + } + + @Test + public void getValueAfterBeanUpdate() { + MethodProperty<String> mp = new MethodProperty<String>(testObject, + "street"); + testObject.setStreet("Foo street"); + Assert.assertEquals("Foo street", mp.getValue()); + } + + @Test + public void setValue() { + MethodProperty<String> mp = new MethodProperty<String>(testObject, + "street"); + mp.setValue("Foo street"); + Assert.assertEquals("Foo street", testObject.getStreet()); + } + + @Test + public void changeInstance() { + MethodProperty<String> mp = new MethodProperty<String>(testObject, + "street"); + Address newStreet = new Address("new street", 999); + mp.setInstance(newStreet); + Assert.assertEquals("new street", mp.getValue()); + Assert.assertEquals("some street", testObject.getStreet()); + + } + + @Test(expected = IllegalArgumentException.class) + public void changeInstanceToIncompatible() { + MethodProperty<String> mp = new MethodProperty<String>(testObject, + "street"); + mp.setInstance("foobar"); + + } + +} diff --git a/compatibility-server/src/test/java/com/vaadin/v7/data/util/NestedMethodPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/v7/data/util/NestedMethodPropertyTest.java index a5103e0e0a..9a47e395e2 100644 --- a/compatibility-server/src/test/java/com/vaadin/v7/data/util/NestedMethodPropertyTest.java +++ b/compatibility-server/src/test/java/com/vaadin/v7/data/util/NestedMethodPropertyTest.java @@ -348,4 +348,27 @@ public class NestedMethodPropertyTest { Assert.assertTrue(booleanProperty.isReadOnly()); } + @Test + public void testChangeInstance() { + NestedMethodProperty<String> streetProperty = new NestedMethodProperty<String>( + vaadin, "manager.address.street"); + + Address somewhere = new Address("The street", 1234); + Person someone = new Person("Someone", somewhere); + Team someteam = new Team("The team", someone); + streetProperty.setInstance(someteam); + + Assert.assertEquals("The street", streetProperty.getValue()); + Assert.assertEquals("Ruukinkatu 2-4", vaadin.getManager().getAddress() + .getStreet()); + } + + @Test(expected = IllegalArgumentException.class) + public void testChangeInstanceToIncompatible() { + NestedMethodProperty<String> streetProperty = new NestedMethodProperty<String>( + vaadin, "manager.address.street"); + + streetProperty.setInstance("bar"); + } + } diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridChildrenTest.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridChildrenTest.java index 7a599f2745..2d8edddac1 100644 --- a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridChildrenTest.java +++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridChildrenTest.java @@ -18,6 +18,7 @@ package com.vaadin.v7.tests.server.component.grid; import java.util.Iterator; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import com.vaadin.ui.Component; @@ -28,12 +29,19 @@ import com.vaadin.v7.ui.Grid.HeaderCell; public class GridChildrenTest { - @Test - public void componentsInMergedHeader() { - Grid grid = new Grid(); + private Grid grid; + + @Before + public void createGrid() { + grid = new Grid(); grid.addColumn("foo"); grid.addColumn("bar"); grid.addColumn("baz"); + + } + + @Test + public void iteratorFindsComponentsInMergedHeader() { HeaderCell merged = grid.getDefaultHeaderRow().join("foo", "bar", "baz"); Label label = new Label(); @@ -44,16 +52,55 @@ public class GridChildrenTest { } @Test + public void removeComponentInMergedHeaderCell() { + HeaderCell merged = grid.getDefaultHeaderRow().join("foo", "bar", + "baz"); + Label label = new Label(); + merged.setComponent(label); + Assert.assertEquals(grid, label.getParent()); + merged.setText("foo"); + Assert.assertNull(label.getParent()); + } + + @Test + public void removeHeaderWithComponentInMergedHeaderCell() { + HeaderCell merged = grid.getDefaultHeaderRow().join("foo", "bar", + "baz"); + Label label = new Label(); + merged.setComponent(label); + Assert.assertEquals(grid, label.getParent()); + grid.removeHeaderRow(0); + Assert.assertNull(label.getParent()); + } + + @Test + public void removeComponentInMergedFooterCell() { + FooterCell merged = grid.addFooterRowAt(0).join("foo", "bar", "baz"); + Label label = new Label(); + merged.setComponent(label); + Assert.assertEquals(grid, label.getParent()); + merged.setText("foo"); + Assert.assertNull(label.getParent()); + } + + @Test + public void removeFooterWithComponentInMergedFooterCell() { + FooterCell merged = grid.addFooterRowAt(0).join("foo", "bar", "baz"); + Label label = new Label(); + merged.setComponent(label); + Assert.assertEquals(grid, label.getParent()); + grid.removeFooterRow(0); + Assert.assertNull(label.getParent()); + } + + @Test public void componentsInMergedFooter() { - Grid grid = new Grid(); - 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<Component> i = grid.iterator(); Assert.assertEquals(label, i.next()); Assert.assertFalse(i.hasNext()); + Assert.assertEquals(grid, label.getParent()); } } diff --git a/compatibility-shared/pom.xml b/compatibility-shared/pom.xml index d702702748..abdc1ca849 100644 --- a/compatibility-shared/pom.xml +++ b/compatibility-shared/pom.xml @@ -75,7 +75,7 @@ <execution> <id>copy-sources</id> <!-- here the phase you need --> - <phase>prepare-package</phase> + <phase>process-resources</phase> <goals> <goal>copy-resources</goal> </goals> diff --git a/server/pom.xml b/server/pom.xml index 479935666c..4a31cd47eb 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -165,7 +165,7 @@ <execution> <id>copy-sources</id> <!-- here the phase you need --> - <phase>prepare-package</phase> + <phase>process-resources</phase> <goals> <goal>copy-resources</goal> </goals> diff --git a/server/src/main/java/com/vaadin/server/DownloadStream.java b/server/src/main/java/com/vaadin/server/DownloadStream.java index a0a1334135..b52b2e83a4 100644 --- a/server/src/main/java/com/vaadin/server/DownloadStream.java +++ b/server/src/main/java/com/vaadin/server/DownloadStream.java @@ -20,14 +20,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.http.HttpServletResponse; +import com.vaadin.util.EncodeUtil; + /** * Downloadable stream. * <p> @@ -329,13 +329,10 @@ public class DownloadStream implements Serializable { * @return A value for inclusion in a Content-Disposition header */ public static String getContentDispositionFilename(String filename) { - try { - String encodedFilename = URLEncoder.encode(filename, "UTF-8"); - return String.format("filename=\"%s\"; filename*=utf-8''%s", - encodedFilename, encodedFilename); - } catch (UnsupportedEncodingException e) { - return null; - } + String encodedFilename = EncodeUtil.rfc5987Encode(filename); + + return String.format("filename=\"%s\"; filename*=utf-8''%s", + encodedFilename, encodedFilename); } /** diff --git a/server/src/main/java/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/main/java/com/vaadin/server/communication/AtmospherePushConnection.java index 92d675001b..b5a6f55e4c 100644 --- a/server/src/main/java/com/vaadin/server/communication/AtmospherePushConnection.java +++ b/server/src/main/java/com/vaadin/server/communication/AtmospherePushConnection.java @@ -258,17 +258,21 @@ public class AtmospherePushConnection implements PushConnection { } /** - * @return the UI associated with this connection. + * Gets the UI this push connection is associated with. + * + * @return the UI associated with this connection */ - protected UI getUI() { + public UI getUI() { return ui; } /** - * @return The AtmosphereResource associated with this connection or null if - * connection not open. + * Gets the atmosphere resource associated with this connection. + * + * @return The AtmosphereResource associated with this connection or + * <code>null</code> if the connection is not open. */ - protected AtmosphereResource getResource() { + public AtmosphereResource getResource() { return resource; } diff --git a/server/src/main/java/com/vaadin/server/communication/UIInitHandler.java b/server/src/main/java/com/vaadin/server/communication/UIInitHandler.java index 09d2b0aa79..c0c6850d32 100644 --- a/server/src/main/java/com/vaadin/server/communication/UIInitHandler.java +++ b/server/src/main/java/com/vaadin/server/communication/UIInitHandler.java @@ -211,10 +211,17 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler { // Set thread local here so it is available in init UI.setCurrent(ui); - ui.doInit(request, uiId.intValue(), embedId); - + Exception initException = null; + try { + ui.doInit(request, uiId.intValue(), embedId); + } catch (Exception e) { + initException = e; + } session.addUI(ui); - + if (initException != null) { + ui.getSession().getCommunicationManager() + .handleConnectorRelatedException(ui, initException); + } // Warn if the window can't be preserved if (embedId == null && vaadinService.preserveUIOnRefresh(provider, event)) { diff --git a/server/src/main/java/com/vaadin/util/EncodeUtil.java b/server/src/main/java/com/vaadin/util/EncodeUtil.java new file mode 100644 index 0000000000..eae0890542 --- /dev/null +++ b/server/src/main/java/com/vaadin/util/EncodeUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.util; + +import java.nio.charset.Charset; + +/** + * Utilities related to various encoding schemes. + * + * @author Vaadin Ltd + * @since + */ +public final class EncodeUtil { + private static final Charset UTF8 = Charset.forName("UTF-8"); + + private EncodeUtil() { + // Static utils only + } + + /** + * Encodes the given string to UTF-8 <code>value-chars</code> as defined in + * RFC5987 for use in e.g. the <code>Content-Disposition</code> HTTP header. + * + * @param value + * the string to encode, not <code>null</code> + * @return the encoded string + */ + public static String rfc5987Encode(String value) { + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < value.length();) { + int cp = value.codePointAt(i); + if (cp < 127 && (Character.isLetterOrDigit(cp) || cp == '.')) { + builder.append((char) cp); + } else { + // Create string from a single code point + String cpAsString = new String(new int[] { cp }, 0, 1); + + appendHexBytes(builder, cpAsString.getBytes(UTF8)); + } + + // Advance to the next code point + i += Character.charCount(cp); + } + + return builder.toString(); + } + + private static void appendHexBytes(StringBuilder builder, byte[] bytes) { + for (byte byteValue : bytes) { + // mask with 0xFF to compensate for "negative" values + int intValue = byteValue & 0xFF; + String hexCode = Integer.toString(intValue, 16); + builder.append('%').append(hexCode); + } + } + +} diff --git a/server/src/test/java/com/vaadin/server/DownloadStreamTest.java b/server/src/test/java/com/vaadin/server/DownloadStreamTest.java index 81f45f6173..4bb97de79f 100644 --- a/server/src/test/java/com/vaadin/server/DownloadStreamTest.java +++ b/server/src/test/java/com/vaadin/server/DownloadStreamTest.java @@ -7,13 +7,16 @@ import static org.mockito.Mockito.verify; import java.io.IOException; import java.io.InputStream; -import java.net.URLEncoder; import org.junit.Before; import org.junit.Test; public class DownloadStreamTest { - private final String filename = "日本語.png"; + private String filename = "A å日.png"; + private String encodedFileName = "A" + "%20" // space + + "%c3%a5" // å + + "%e6%97%a5" // 日 + + ".png"; private DownloadStream stream; @Before @@ -27,7 +30,6 @@ public class DownloadStreamTest { stream.writeResponse(mock(VaadinRequest.class), response); - String encodedFileName = URLEncoder.encode(filename, "utf-8"); verify(response).setHeader(eq(DownloadStream.CONTENT_DISPOSITION), contains(String.format("filename=\"%s\";", encodedFileName))); verify(response).setHeader(eq(DownloadStream.CONTENT_DISPOSITION), diff --git a/server/src/test/java/com/vaadin/server/communication/ServletUIInitHandlerTest.java b/server/src/test/java/com/vaadin/server/communication/ServletUIInitHandlerTest.java new file mode 100644 index 0000000000..6c61922307 --- /dev/null +++ b/server/src/test/java/com/vaadin/server/communication/ServletUIInitHandlerTest.java @@ -0,0 +1,188 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS 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; + +import java.io.IOException; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import com.vaadin.server.DefaultDeploymentConfiguration; +import com.vaadin.server.ErrorEvent; +import com.vaadin.server.ErrorHandler; +import com.vaadin.server.LegacyCommunicationManager; +import com.vaadin.server.MockServletConfig; +import com.vaadin.server.UIClassSelectionEvent; +import com.vaadin.server.UICreateEvent; +import com.vaadin.server.UIProvider; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinServletRequest; +import com.vaadin.server.VaadinServletResponse; +import com.vaadin.server.VaadinServletService; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; +import com.vaadin.ui.UI; + +public class ServletUIInitHandlerTest { + + public static class CommunicationMock { + + public final UI ui; + public final ServletConfig servletConfig; + public final VaadinServlet servlet; + public final DefaultDeploymentConfiguration deploymentConfiguration; + public final VaadinServletService service; + public final AlwaysLockedVaadinSession session; + + public CommunicationMock(final UI ui) throws Exception { + servletConfig = new MockServletConfig(); + servlet = new VaadinServlet(); + servlet.init(servletConfig); + + deploymentConfiguration = new DefaultDeploymentConfiguration( + UI.class, new Properties()); + + service = new VaadinServletService(servlet, + deploymentConfiguration); + session = new AlwaysLockedVaadinSession(service); + LegacyCommunicationManager communicationManager = new LegacyCommunicationManager( + session); + session.setCommunicationManager(communicationManager); + session.setConfiguration(deploymentConfiguration); + session.addUIProvider(new UIProvider() { + + @Override + public Class<? extends UI> getUIClass( + UIClassSelectionEvent event) { + return ui.getClass(); + } + + @Override + public UI createInstance(UICreateEvent event) { + return ui; + } + }); + this.ui = ui; + } + + public VaadinRequest createInitRequest() { + return new VaadinServletRequest( + Mockito.mock(HttpServletRequest.class), service) { + @Override + public String getMethod() { + return "POST"; + } + + @Override + public String getParameter(String name) { + if (UIInitHandler.BROWSER_DETAILS_PARAMETER.equals(name)) { + return "1"; + } + return super.getParameter(name); + } + + }; + } + + } + + @Test + public void errorHandlerForInitException() throws Exception { + final AtomicInteger pre = new AtomicInteger(0); + final AtomicInteger errorHandlerCalls = new AtomicInteger(0); + + UI ui = new UI() { + @Override + protected void init(VaadinRequest request) { + pre.incrementAndGet(); + throw new RuntimeException("Exception produced in init()"); + } + }; + + ui.setErrorHandler(new ErrorHandler() { + @Override + public void error(ErrorEvent event) { + errorHandlerCalls.incrementAndGet(); + } + }); + + CommunicationMock mock = new CommunicationMock(ui); + VaadinRequest initRequest = mock.createInitRequest(); + + ServletUIInitHandler servletUIInitHandler = new ServletUIInitHandler(); + servletUIInitHandler.handleRequest(mock.session, initRequest, + new VaadinServletResponse( + Mockito.mock(HttpServletResponse.class), mock.service) { + @Override + public ServletOutputStream getOutputStream() + throws IOException { + return new ServletOutputStream() { + @Override + public void write(int b) throws IOException { + } + }; + } + }); + + Assert.assertEquals(1, pre.getAndIncrement()); + Assert.assertEquals(1, errorHandlerCalls.getAndIncrement()); + Assert.assertEquals(mock.session, ui.getSession()); + } + + @Test + public void initExceptionNoErrorHandler() throws Exception { + final AtomicInteger pre = new AtomicInteger(0); + + UI ui = new UI() { + @Override + protected void init(VaadinRequest request) { + pre.incrementAndGet(); + throw new RuntimeException("Exception produced in init()"); + } + }; + + CommunicationMock mock = new CommunicationMock(ui); + VaadinRequest initRequest = mock.createInitRequest(); + + ServletUIInitHandler servletUIInitHandler = new ServletUIInitHandler(); + servletUIInitHandler.handleRequest(mock.session, initRequest, + new VaadinServletResponse( + Mockito.mock(HttpServletResponse.class), mock.service) { + @Override + public ServletOutputStream getOutputStream() + throws IOException { + return new ServletOutputStream() { + @Override + public void write(int b) throws IOException { + } + }; + } + }); + + Assert.assertEquals(1, pre.getAndIncrement()); + // Default error handler only logs the exception + Assert.assertEquals(mock.session, ui.getSession()); + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java b/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java index fca87ccbdf..a3375585c7 100644 --- a/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java +++ b/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java @@ -74,6 +74,7 @@ public class ClassesSerializableTest { // interfaces "com\\.vaadin\\.server\\.LegacyCommunicationManager.*", // "com\\.vaadin\\.buildhelpers.*", // + "com\\.vaadin\\.util\\.EncodeUtil.*", // "com\\.vaadin\\.util\\.ReflectTools.*", // "com\\.vaadin\\.data\\.util\\.ReflectTools.*", // "com\\.vaadin\\.data\\.util\\.JsonUtil.*", // @@ -235,6 +236,7 @@ public class ClassesSerializableTest { Assert.fail( "Serializable not implemented by the following classes and interfaces: " + nonSerializableString); + } private static boolean isFunctionalType(Type type) { diff --git a/server/src/test/java/com/vaadin/util/EncodeUtilTest.java b/server/src/test/java/com/vaadin/util/EncodeUtilTest.java new file mode 100644 index 0000000000..7bac539968 --- /dev/null +++ b/server/src/test/java/com/vaadin/util/EncodeUtilTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.util; + +import org.junit.Assert; +import org.junit.Test; + +public class EncodeUtilTest { + @Test + public void rfc5987Encode() { + Assert.assertEquals("A", EncodeUtil.rfc5987Encode("A")); + Assert.assertEquals("%20", EncodeUtil.rfc5987Encode(" ")); + Assert.assertEquals("%c3%a5", EncodeUtil.rfc5987Encode("å")); + Assert.assertEquals("%e6%97%a5", EncodeUtil.rfc5987Encode("日")); + + Assert.assertEquals("A" + "%20" + "%c3%a5" + "%e6%97%a5", + EncodeUtil.rfc5987Encode("A å日")); + } +} diff --git a/shared/pom.xml b/shared/pom.xml index c896f6f744..6956e6c490 100644 --- a/shared/pom.xml +++ b/shared/pom.xml @@ -79,7 +79,7 @@ <execution> <id>copy-sources</id> <!-- here the phase you need --> - <phase>prepare-package</phase> + <phase>process-resources</phase> <goals> <goal>copy-resources</goal> </goals> diff --git a/uitest/src/main/java/com/vaadin/tests/components/FileDownloaderUI.java b/uitest/src/main/java/com/vaadin/tests/components/FileDownloaderUI.java index 141ef664b4..e74a7128d5 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/FileDownloaderUI.java +++ b/uitest/src/main/java/com/vaadin/tests/components/FileDownloaderUI.java @@ -99,8 +99,8 @@ public class FileDownloaderUI extends AbstractTestUIWithLog { addComponents("Class resource pdf", resource, components); Button downloadUtf8File = new Button("Download UTF-8 named file"); - FileDownloader fd = new FileDownloader( - new ClassResource(new EmbeddedPdf().getClass(), "åäö-日本語.pdf")); + FileDownloader fd = new FileDownloader(new ClassResource( + new EmbeddedPdf().getClass(), "File åäö-日本語.pdf")); fd.setOverrideContentType(false); fd.extend(downloadUtf8File); addComponent(downloadUtf8File); diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridDetailsReattach.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDetailsReattach.java new file mode 100755 index 0000000000..0328d87d07 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridDetailsReattach.java @@ -0,0 +1,37 @@ +package com.vaadin.tests.components.grid;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.VerticalLayout;
+
+public class GridDetailsReattach extends AbstractTestUI {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ final VerticalLayout verticalMain = new VerticalLayout();
+
+ final VerticalLayout layoutWithGrid = new VerticalLayout();
+
+ Grid<String> grid = new Grid<>("Grid");
+ grid.addColumn(String::toString).setCaption("Foo");
+ grid.setHeight("150px");
+ grid.setItems("Foo");
+ grid.setDetailsGenerator(str -> new Label("AnyDetails"));
+ grid.setDetailsVisible("Foo", true);
+ layoutWithGrid.addComponent(grid);
+
+ Button addCaptionToLayoutWithGridButton = new Button(
+ "Add caption to 'layoutWithGrid' layout");
+ addCaptionToLayoutWithGridButton.addClickListener(e -> layoutWithGrid
+ .setCaption("Caption added to 'layoutWithGrid' layout"));
+ layoutWithGrid.addComponent(addCaptionToLayoutWithGridButton);
+
+ verticalMain.addComponent(layoutWithGrid);
+
+ addComponent(verticalMain);
+
+ }
+}
diff --git a/uitest/src/main/java/com/vaadin/tests/components/menubar/MenuBarIcons.java b/uitest/src/main/java/com/vaadin/tests/components/menubar/MenuBarIcons.java new file mode 100644 index 0000000000..9352daed83 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/menubar/MenuBarIcons.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.menubar; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.FontAwesome; +import com.vaadin.server.Resource; +import com.vaadin.server.ThemeResource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.MenuBar; +import com.vaadin.ui.MenuBar.MenuItem; + +@Theme("tests-valo") +public class MenuBarIcons extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + MenuBar fontIconMenu = new MenuBar(); + fontIconMenu.setId("fontIcon"); + fontIconMenu.setWidth("400px"); + fontIconMenu.addItem("Main", FontAwesome.MAIL_REPLY_ALL, null); + MenuItem hasSub = fontIconMenu.addItem("Has sub", FontAwesome.SUBWAY, + null); + hasSub.addItem("Sub item", FontAwesome.AMBULANCE, null); + for (int i = 0; i < 10; i++) { + fontIconMenu.addItem("Filler " + i, FontAwesome.ANGELLIST, null); + } + MenuItem more = fontIconMenu.getMoreMenuItem(); + more.setText("More"); + more.setIcon(FontAwesome.MOTORCYCLE); + addComponent(fontIconMenu); + + MenuBar menu = new MenuBar(); + menu.setId("image"); + menu.setWidth("400px"); + Resource imageIcon = new ThemeResource("img/email-reply.png"); + menu.addItem("Main", imageIcon, null); + hasSub = menu.addItem("Has sub", imageIcon, null); + hasSub.addItem("Sub item", imageIcon, null); + for (int i = 0; i < 10; i++) { + menu.addItem("Filler " + i, imageIcon, null); + } + more = menu.getMoreMenuItem(); + more.setText("More"); + more.setIcon(imageIcon); + addComponent(menu); + + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/ui/UIInitException.java b/uitest/src/main/java/com/vaadin/tests/components/ui/UIInitException.java index 22b6a8d031..76573d1f81 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/ui/UIInitException.java +++ b/uitest/src/main/java/com/vaadin/tests/components/ui/UIInitException.java @@ -1,12 +1,22 @@ package com.vaadin.tests.components.ui; +import com.vaadin.server.ErrorHandler; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.Label; public class UIInitException extends AbstractReindeerTestUI { @Override protected void setup(VaadinRequest request) { + setErrorHandler(new ErrorHandler() { + @Override + public void error(com.vaadin.server.ErrorEvent event) { + addComponent(new Label("An exception occurred: " + + event.getThrowable().getMessage())); + + } + }); throw new RuntimeException("Catch me if you can"); } diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/RemoveHiddenColumn.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/RemoveHiddenColumn.java new file mode 100644 index 0000000000..1c053ca8d7 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/RemoveHiddenColumn.java @@ -0,0 +1,60 @@ +package com.vaadin.v7.tests.components.grid; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.tests.data.bean.Person; +import com.vaadin.tests.data.bean.Sex; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.v7.data.util.BeanItemContainer; +import com.vaadin.v7.ui.Grid; +import com.vaadin.v7.ui.Grid.Column; + +public class RemoveHiddenColumn extends AbstractTestUIWithLog { + + private final Grid testGrid = new Grid(); + private final Button testBtn = new Button("updateGrid"); + private final Button testBtn2 = new Button("show/hide Grid"); + private final HorizontalLayout buttonBar = new HorizontalLayout(testBtn, + testBtn2); + private final VerticalLayout mainLayout = new VerticalLayout(testGrid, + buttonBar); + + @Override + protected void setup(final VaadinRequest request) { + final Grid grid = new Grid(); + final BeanItemContainer<Person> bic = new BeanItemContainer<Person>( + Person.class); + grid.setContainerDataSource(bic); + grid.setColumns("firstName", "lastName", "email", "age"); + + grid.getColumn("firstName").setHidden(true); + grid.getColumn("email").setHidden(true); + + Button addRow = new Button("Add data row", new ClickListener() { + @Override + public void buttonClick(ClickEvent e) { + bic.addBean(new Person("first", "last", "email", 42, Sex.FEMALE, + null)); + + } + }); + addRow.setId("add"); + Button removeColumn = new Button("Remove first column", + new ClickListener() { + @Override + public void buttonClick(ClickEvent e) { + Column column = grid.getColumns().get(0); + log("Removed column '" + column.getHeaderCaption() + "'" + + (column.isHidden() ? " (hidden)" : "")); + grid.removeColumn(column.getPropertyId()); + } + }); + removeColumn.setId("remove"); + + addComponents(grid, addRow, removeColumn); + } +} diff --git a/uitest/src/main/resources/com/vaadin/tests/components/embedded/åäö-日本語.pdf b/uitest/src/main/resources/com/vaadin/tests/components/embedded/File åäö-日本語.pdf Binary files differindex e44a87e9ad..e44a87e9ad 100644 --- a/uitest/src/main/resources/com/vaadin/tests/components/embedded/åäö-日本語.pdf +++ b/uitest/src/main/resources/com/vaadin/tests/components/embedded/File åäö-日本語.pdf diff --git a/uitest/src/main/themes/VAADIN/themes/tests-valo/img/email-reply.png b/uitest/src/main/themes/VAADIN/themes/tests-valo/img/email-reply.png Binary files differnew file mode 100644 index 0000000000..be7fd2244d --- /dev/null +++ b/uitest/src/main/themes/VAADIN/themes/tests-valo/img/email-reply.png diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridDetailsReattachTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDetailsReattachTest.java new file mode 100755 index 0000000000..a6fa1dae94 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridDetailsReattachTest.java @@ -0,0 +1,34 @@ +package com.vaadin.tests.components.grid;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openqa.selenium.By;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.parallel.TestCategory;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+@TestCategory("grid")
+public class GridDetailsReattachTest extends MultiBrowserTest {
+
+ @Before
+ public void setUp() {
+ setDebug(true);
+ }
+
+ @Test
+ public void clickToAddCaption() {
+ openTestURL();
+ Assert.assertTrue("Grid details don't exist", hasDetailsElement());
+ $(ButtonElement.class).first().click();
+ Assert.assertTrue("Grid details don't exist after deattach and reattach",hasDetailsElement() );
+ }
+
+ private final By locator = By.className("v-grid-spacer");
+
+ private boolean hasDetailsElement() {
+ return !findElements(locator).isEmpty();
+ }
+
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java index 912a1eb996..25a81984ea 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java @@ -3,6 +3,11 @@ package com.vaadin.tests.components.grid; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; @@ -403,6 +408,53 @@ public class GridSelectionTest extends GridBasicsTest { assertTrue(getGridElement().getRow(0).isSelected()); } + @Test + @Ignore("Removing rows is not implemented in the UI") + public void testRemoveSelectedRowMulti() { + openTestURL(); + + setSelectionModelMulti(); + GridElement grid = getGridElement(); + grid.getCell(5, 0).click(); + + selectMenuPath("Component", "Body rows", "Remove selected rows"); + assertSelected(); + grid.getCell(5, 0).click(); + assertSelected(5); + grid.getCell(6, 0).click(); + assertSelected(5, 6); + grid.getCell(5, 0).click(); + assertSelected(6); + grid.getCell(5, 0).click(); + grid.getCell(4, 0).click(); + selectMenuPath("Component", "Body rows", "Remove selected rows"); + assertSelected(); + grid.getCell(0, 0).click(); + assertSelected(0); + grid.getCell(5, 0).click(); + assertSelected(0, 5); + grid.getCell(6, 0).click(); + assertSelected(0, 5, 6); + + } + + private void assertSelected(Integer... selected) { + GridElement grid = getGridElement(); + HashSet<Integer> expected = new HashSet<Integer>( + Arrays.asList(selected)); + for (int i = 0; i < 10; i++) { + boolean rowSelected = grid.getRow(i).isSelected(); + if (expected.contains(i)) { + Assert.assertTrue("Expected row " + i + " to be selected", + rowSelected); + } else { + Assert.assertFalse("Expected row " + i + " not to be selected", + rowSelected); + } + } + + } + private void toggleUserSelectionAllowed() { selectMenuPath("Component", "State", "Disallow user selection"); } diff --git a/uitest/src/test/java/com/vaadin/tests/components/menubar/MenuBarIconsTest.java b/uitest/src/test/java/com/vaadin/tests/components/menubar/MenuBarIconsTest.java new file mode 100644 index 0000000000..7213d2d466 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/menubar/MenuBarIconsTest.java @@ -0,0 +1,82 @@ +package com.vaadin.tests.components.menubar; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.WebElement; + +import com.vaadin.server.FontAwesome; +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.MenuBarElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class MenuBarIconsTest extends SingleBrowserTest { + + @Test + public void fontIconsRendered() { + openTestURL(); + MenuBarElement menu = $(MenuBarElement.class).id("fontIcon"); + WebElement moreItem = menu + .findElements(By.className("v-menubar-menuitem")).get(3); + + assertFontIcon(FontAwesome.MAIL_REPLY_ALL, + menu.findElement(By.vaadin("#Main"))); + WebElement hasSubElement = menu.findElement(By.vaadin("#Has sub")); + assertFontIcon(FontAwesome.SUBWAY, hasSubElement); + assertFontIcon(FontAwesome.ANGELLIST, + menu.findElement(By.vaadin("#Filler 0"))); + + hasSubElement.click(); + + assertFontIcon(FontAwesome.AMBULANCE, + hasSubElement.findElement(By.vaadin("#Sub item"))); + // Close sub menu + hasSubElement.click(); + + assertFontIcon(FontAwesome.MOTORCYCLE, moreItem); + + moreItem.click(); + WebElement filler5 = moreItem.findElement(By.vaadin("#Filler 5")); + assertFontIcon(FontAwesome.ANGELLIST, filler5); + + } + + @Test + public void imageIconsRendered() { + openTestURL(); + MenuBarElement menu = $(MenuBarElement.class).id("image"); + WebElement moreItem = menu + .findElements(By.className("v-menubar-menuitem")).get(3); + + String image = "/tests-valo/img/email-reply.png"; + assertImage(image, menu.findElement(By.vaadin("#Main"))); + WebElement hasSubElement = menu.findElement(By.vaadin("#Has sub")); + assertImage(image, hasSubElement); + assertImage(image, menu.findElement(By.vaadin("#Filler 0"))); + + hasSubElement.click(); + + assertImage(image, hasSubElement.findElement(By.vaadin("#Sub item"))); + // Close sub menu + hasSubElement.click(); + + assertImage(image, moreItem); + + moreItem.click(); + WebElement filler5 = moreItem.findElement(By.vaadin("#Filler 5")); + assertImage(image, filler5); + + } + + private void assertImage(String image, WebElement menuItem) { + WebElement imageElement = menuItem.findElement(By.className("v-icon")); + Assert.assertTrue(imageElement.getAttribute("src").endsWith(image)); + } + + private void assertFontIcon(FontAwesome expected, WebElement menuItem) { + WebElement mainIcon = menuItem.findElement(By.className("v-icon")); + + Assert.assertEquals(expected.getCodepoint(), + mainIcon.getText().codePointAt(0)); + + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/ui/UIInitExceptionTest.java b/uitest/src/test/java/com/vaadin/tests/components/ui/UIInitExceptionTest.java index c1d6aaa55b..064132d4c7 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/ui/UIInitExceptionTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/ui/UIInitExceptionTest.java @@ -3,9 +3,9 @@ package com.vaadin.tests.components.ui; import org.junit.Assert; import org.junit.Test; -import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.tb3.SingleBrowserTest; -public class UIInitExceptionTest extends MultiBrowserTest { +public class UIInitExceptionTest extends SingleBrowserTest { @Test public void testExceptionOnUIInit() throws Exception { openTestURL(); diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/RemoveHiddenColumnTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/RemoveHiddenColumnTest.java new file mode 100644 index 0000000000..ecb5285401 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/RemoveHiddenColumnTest.java @@ -0,0 +1,45 @@ +package com.vaadin.v7.tests.components.grid; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class RemoveHiddenColumnTest extends SingleBrowserTest { + + @Test + public void removeHiddenColumnInEmptyGrid() { + openTestURL("debug"); + removeColumns(); + } + + @Test + public void removeHiddenColumnInPopulatedGrid() { + openTestURL("debug"); + ButtonElement add = $(ButtonElement.class).id("add"); + add.click(); + removeColumns(); + + } + + private void removeColumns() { + ButtonElement remove = $(ButtonElement.class).id("remove"); + remove.click(); + Assert.assertEquals("1. Removed column 'First Name' (hidden)", + getLogRow(0)); + assertNoErrorNotifications(); + + remove.click(); + Assert.assertEquals("2. Removed column 'Last Name'", getLogRow(0)); + assertNoErrorNotifications(); + remove.click(); + Assert.assertEquals("3. Removed column 'Email' (hidden)", getLogRow(0)); + assertNoErrorNotifications(); + remove.click(); + Assert.assertEquals("4. Removed column 'Age'", getLogRow(0)); + assertNoErrorNotifications(); + + } + +} |