diff options
author | Olli Tietäväinen <ollit@vaadin.com> | 2018-12-28 09:25:11 +0200 |
---|---|---|
committer | Sun Zhe <31067185+ZheSun88@users.noreply.github.com> | 2018-12-28 09:25:11 +0200 |
commit | 613f36ff23fe06c676f9a71e92c04ccabcd19c63 (patch) | |
tree | 8a7ad51680bd9889bdd7897e69a41a00f724d962 /server | |
parent | d9de8a9470d0e89590e8fd77e1471c5154e110cd (diff) | |
download | vaadin-framework-613f36ff23fe06c676f9a71e92c04ccabcd19c63.tar.gz vaadin-framework-613f36ff23fe06c676f9a71e92c04ccabcd19c63.zip |
Grid column npe behavior (#11390)
* add possibility to configure nested null behavior to Grid.Column
* added addColumn method that allows configuring the nested null handling behavior of the column
* added uitest for nested null value handling, implements #11137
Diffstat (limited to 'server')
-rw-r--r-- | server/src/main/java/com/vaadin/ui/Grid.java | 145 |
1 files changed, 142 insertions, 3 deletions
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index 7c3467eaa3..81f46f8d59 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -845,6 +845,23 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, */ public static class Column<T, V> extends AbstractExtension { + /** + * behavior when parsing nested properties which may contain + * <code>null</code> values in the property chain + */ + public enum NestedNullBehavior { + /** + * throw a NullPointerException if there is a nested + * <code>null</code> value + */ + THROW, + /** + * silently ignore any exceptions caused by nested <code>null</code> + * values + */ + ALLOW_NULLS + } + private final ValueProvider<T, V> valueProvider; private ValueProvider<V, ?> presentationProvider; @@ -856,6 +873,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, return Stream.of(new QuerySortOrder(id, direction)); }; + private NestedNullBehavior nestedNullBehavior = NestedNullBehavior.THROW; private boolean sortable = true; private SerializableComparator<T> comparator; private StyleGenerator<T> styleGenerator = item -> null; @@ -990,6 +1008,38 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, } } + /** + * Constructs a new Column configuration with given renderer and value + * provider. + * <p> + * For a more complete explanation on presentation provider, see + * {@link #setRenderer(ValueProvider, Renderer)}. + * + * @param valueProvider + * the function to get values from items, not + * <code>null</code> + * @param presentationProvider + * the function to get presentations from the value of this + * column, not <code>null</code>. For more details, see + * {@link #setRenderer(ValueProvider, Renderer)} + * @param nestedNullBehavior + * behavior on encountering nested <code>null</code> values + * when reading the value from the bean + * @param renderer + * the presentation renderer, not <code>null</code> + * @param <P> + * the presentation type + * + * @since + */ + protected <P> Column(ValueProvider<T, V> valueProvider, + ValueProvider<V, P> presentationProvider, + Renderer<? super P> renderer, + NestedNullBehavior nestedNullBehavior) { + this(valueProvider, presentationProvider, renderer); + this.nestedNullBehavior = nestedNullBehavior; + } + private static int compareMaybeComparables(Object a, Object b) { if (hasCommonComparableBaseType(a, b)) { return compareComparables(a, b); @@ -1053,8 +1103,16 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, @SuppressWarnings("unchecked") private <P> JsonValue generateRendererValue(T item, ValueProvider<V, P> presentationProvider, Connector renderer) { - P presentationValue = presentationProvider - .apply(valueProvider.apply(item)); + V value; + try { + value = valueProvider.apply(item); + } catch (NullPointerException npe) { + value = null; + if (NestedNullBehavior.THROW == nestedNullBehavior) { + throw npe; + } + } + P presentationValue = presentationProvider.apply(value); // Make Grid track components. if (renderer instanceof ComponentRenderer @@ -2723,6 +2781,59 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, } /** + * Adds a new column with the given property name and renderer. The property + * name will be used as the {@link Column#getId() column id} and the + * {@link Column#getCaption() column caption} will be set based on the + * property definition. + * <p> + * This method can only be used for a <code>Grid</code> created using + * {@link Grid#Grid(Class)} or {@link #withPropertySet(PropertySet)}. + * <p> + * You can add columns for nested properties with dot notation, eg. + * <code>"property.nestedProperty"</code> + * + * @param propertyName + * the property name of the new column, not <code>null</code> + * @param renderer + * the renderer to use, not <code>null</code> + * @param nestedNullBehavior + * the behavior when + * @return the newly added column, not <code>null</code> + */ + public Column<T, ?> addColumn(String propertyName, + AbstractRenderer<? super T, ?> renderer, + Column.NestedNullBehavior nestedNullBehavior) { + Objects.requireNonNull(propertyName, "Property name cannot be null"); + Objects.requireNonNull(renderer, "Renderer cannot be null"); + + if (getColumn(propertyName) != null) { + throw new IllegalStateException( + "There is already a column for " + propertyName); + } + + PropertyDefinition<T, ?> definition = propertySet + .getProperty(propertyName) + .orElseThrow(() -> new IllegalArgumentException( + "Could not resolve property name " + propertyName + + " from " + propertySet)); + + if (!renderer.getPresentationType() + .isAssignableFrom(definition.getType())) { + throw new IllegalArgumentException( + renderer + " cannot be used with a property of type " + + definition.getType().getName()); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) + Column<T, ?> column = createColumn(definition.getGetter(), + ValueProvider.identity(), (AbstractRenderer) renderer, + nestedNullBehavior); + String generatedIdentifier = getGeneratedIdentifier(); + addColumn(generatedIdentifier, column); + column.setId(definition.getName()).setCaption(definition.getCaption()); + return column; + } + + /** * Adds a new text column to this {@link Grid} with a value provider. The * column will use a {@link TextRenderer}. The value is converted to a * String using {@link Object#toString()}. In-memory sorting will use the @@ -2824,7 +2935,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, /** * Adds a column that shows components. * <p> - * This is a shorthand for {@link #addColum()} with a + * This is a shorthand for {@link #addColumn()} with a * {@link ComponentRenderer}. * * @param componentProvider @@ -2865,6 +2976,34 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, return new Column<>(valueProvider, presentationProvider, renderer); } + /** + * Creates a column instance from a value provider, presentation provider + * and a renderer. + * + * @param valueProvider + * the value provider + * @param presentationProvider + * the presentation provider + * @param renderer + * the renderer + * @param nestedNullBehavior + * the behavior when facing nested <code>null</code> values + * @return a new column instance + * @param <V> + * the column value type + * @param <P> + * the column presentation type + * + * @since + */ + private <V, P> Column<T, V> createColumn(ValueProvider<T, V> valueProvider, + ValueProvider<V, P> presentationProvider, + AbstractRenderer<? super T, ? super P> renderer, + Column.NestedNullBehavior nestedNullBehavior) { + return new Column<>(valueProvider, presentationProvider, renderer, + nestedNullBehavior); + } + private void addColumn(String identifier, Column<T, ?> column) { if (getColumns().contains(column)) { return; |