diff options
author | Leif Åstrand <legioth@gmail.com> | 2017-02-01 15:30:57 +0200 |
---|---|---|
committer | Pekka Hyvönen <pekka@vaadin.com> | 2017-02-01 15:30:57 +0200 |
commit | 953e7212d84619332cba22888aa653462f9c1706 (patch) | |
tree | 08ff65e0d812dc507dcf816c5c49743256eeff23 /server/src/main/java/com | |
parent | 38b475330868d2d7b0d0b2da0a14be4040ca89ae (diff) | |
download | vaadin-framework-953e7212d84619332cba22888aa653462f9c1706.tar.gz vaadin-framework-953e7212d84619332cba22888aa653462f9c1706.zip |
Make Grid add columns based on bean properties (#8392)
* Make Grid add columns based on bean properties
The property set concept used for Binder is slightly generalized and
used by Grid as well to support similar functionality.
Fixes vaadin/framework8-issues#250
Diffstat (limited to 'server/src/main/java/com')
-rw-r--r-- | server/src/main/java/com/vaadin/data/BeanPropertySet.java (renamed from server/src/main/java/com/vaadin/data/BeanBinderPropertySet.java) | 51 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/BeanValidationBinder.java | 2 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/Binder.java | 77 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/PropertyDefinition.java (renamed from server/src/main/java/com/vaadin/data/BinderPropertyDefinition.java) | 19 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/PropertySet.java (renamed from server/src/main/java/com/vaadin/data/BinderPropertySet.java) | 9 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/ui/Grid.java | 165 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java | 8 |
7 files changed, 260 insertions, 71 deletions
diff --git a/server/src/main/java/com/vaadin/data/BeanBinderPropertySet.java b/server/src/main/java/com/vaadin/data/BeanPropertySet.java index d5fb2b4f5d..d6ab364aff 100644 --- a/server/src/main/java/com/vaadin/data/BeanBinderPropertySet.java +++ b/server/src/main/java/com/vaadin/data/BeanPropertySet.java @@ -32,10 +32,11 @@ import java.util.stream.Stream; import com.vaadin.data.util.BeanUtil; import com.vaadin.server.Setter; +import com.vaadin.shared.util.SharedUtil; import com.vaadin.util.ReflectTools; /** - * A {@link BinderPropertySet} that uses reflection to find bean properties. + * A {@link PropertySet} that uses reflection to find bean properties. * * @author Vaadin Ltd * @@ -44,7 +45,7 @@ import com.vaadin.util.ReflectTools; * @param <T> * the type of the bean */ -public class BeanBinderPropertySet<T> implements BinderPropertySet<T> { +public class BeanPropertySet<T> implements PropertySet<T> { /** * Serialized form of a property set. When deserialized, the property set @@ -52,7 +53,7 @@ public class BeanBinderPropertySet<T> implements BinderPropertySet<T> { * existing cached instance or creates a new one. * * @see #readResolve() - * @see BeanBinderPropertyDefinition#writeReplace() + * @see BeanPropertyDefinition#writeReplace() */ private static class SerializedPropertySet implements Serializable { private final Class<?> beanType; @@ -77,7 +78,7 @@ public class BeanBinderPropertySet<T> implements BinderPropertySet<T> { * definition is then fetched from the property set. * * @see #readResolve() - * @see BeanBinderPropertySet#writeReplace() + * @see BeanPropertySet#writeReplace() */ private static class SerializedPropertyDefinition implements Serializable { private final Class<?> beanType; @@ -102,14 +103,13 @@ public class BeanBinderPropertySet<T> implements BinderPropertySet<T> { } } - private static class BeanBinderPropertyDefinition<T, V> - implements BinderPropertyDefinition<T, V> { + private static class BeanPropertyDefinition<T, V> + implements PropertyDefinition<T, V> { private final PropertyDescriptor descriptor; - private final BeanBinderPropertySet<T> propertySet; + private final BeanPropertySet<T> propertySet; - public BeanBinderPropertyDefinition( - BeanBinderPropertySet<T> propertySet, + public BeanPropertyDefinition(BeanPropertySet<T> propertySet, PropertyDescriptor descriptor) { this.propertySet = propertySet; this.descriptor = descriptor; @@ -156,7 +156,12 @@ public class BeanBinderPropertySet<T> implements BinderPropertySet<T> { } @Override - public BeanBinderPropertySet<T> getPropertySet() { + public String getCaption() { + return SharedUtil.propertyIdToHumanFriendly(getName()); + } + + @Override + public BeanPropertySet<T> getPropertySet() { return propertySet; } @@ -171,21 +176,21 @@ public class BeanBinderPropertySet<T> implements BinderPropertySet<T> { } } - private static final ConcurrentMap<Class<?>, BeanBinderPropertySet<?>> instances = new ConcurrentHashMap<>(); + private static final ConcurrentMap<Class<?>, BeanPropertySet<?>> instances = new ConcurrentHashMap<>(); private final Class<T> beanType; - private final Map<String, BinderPropertyDefinition<T, ?>> definitions; + private final Map<String, PropertyDefinition<T, ?>> definitions; - private BeanBinderPropertySet(Class<T> beanType) { + private BeanPropertySet(Class<T> beanType) { this.beanType = beanType; try { definitions = BeanUtil.getBeanPropertyDescriptors(beanType).stream() - .filter(BeanBinderPropertySet::hasNonObjectReadMethod) - .map(descriptor -> new BeanBinderPropertyDefinition<>(this, + .filter(BeanPropertySet::hasNonObjectReadMethod) + .map(descriptor -> new BeanPropertyDefinition<>(this, descriptor)) - .collect(Collectors.toMap(BinderPropertyDefinition::getName, + .collect(Collectors.toMap(PropertyDefinition::getName, Function.identity())); } catch (IntrospectionException e) { throw new IllegalArgumentException( @@ -196,28 +201,28 @@ public class BeanBinderPropertySet<T> implements BinderPropertySet<T> { } /** - * Gets a {@link BeanBinderPropertySet} for the given bean type. + * Gets a {@link BeanPropertySet} for the given bean type. * * @param beanType * the bean type to get a property set for, not <code>null</code> - * @return the bean binder property set, not <code>null</code> + * @return the bean property set, not <code>null</code> */ @SuppressWarnings("unchecked") - public static <T> BinderPropertySet<T> get(Class<? extends T> beanType) { + public static <T> PropertySet<T> get(Class<? extends T> beanType) { Objects.requireNonNull(beanType, "Bean type cannot be null"); // Cache the reflection results - return (BinderPropertySet<T>) instances.computeIfAbsent(beanType, - BeanBinderPropertySet::new); + return (PropertySet<T>) instances.computeIfAbsent(beanType, + BeanPropertySet::new); } @Override - public Stream<BinderPropertyDefinition<T, ?>> getProperties() { + public Stream<PropertyDefinition<T, ?>> getProperties() { return definitions.values().stream(); } @Override - public Optional<BinderPropertyDefinition<T, ?>> getProperty(String name) { + public Optional<PropertyDefinition<T, ?>> getProperty(String name) { return Optional.ofNullable(definitions.get(name)); } diff --git a/server/src/main/java/com/vaadin/data/BeanValidationBinder.java b/server/src/main/java/com/vaadin/data/BeanValidationBinder.java index 5e3b220ffd..34af4156a4 100644 --- a/server/src/main/java/com/vaadin/data/BeanValidationBinder.java +++ b/server/src/main/java/com/vaadin/data/BeanValidationBinder.java @@ -56,7 +56,7 @@ public class BeanValidationBinder<BEAN> extends Binder<BEAN> { @Override protected BindingBuilder<BEAN, ?> configureBinding( BindingBuilder<BEAN, ?> binding, - BinderPropertyDefinition<BEAN, ?> definition) { + PropertyDefinition<BEAN, ?> definition) { return binding.withValidator( new BeanValidator(beanType, definition.getName())); } diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java index f291c791b7..d36c9996fd 100644 --- a/server/src/main/java/com/vaadin/data/Binder.java +++ b/server/src/main/java/com/vaadin/data/Binder.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -195,7 +194,7 @@ public class Binder<BEAN> implements Serializable { /** * Completes this binding by connecting the field to the property with * the given name. The getter and setter of the property are looked up - * using a {@link BinderPropertySet}. + * using a {@link PropertySet}. * <p> * For a <code>Binder</code> created using the * {@link Binder#Binder(Class)} constructor, introspection will be used @@ -217,7 +216,7 @@ public class Binder<BEAN> implements Serializable { * if the property has no accessible getter * @throws IllegalStateException * if the binder is not configured with an appropriate - * {@link BinderPropertySet} + * {@link PropertySet} * * @see Binder.BindingBuilder#bind(ValueProvider, Setter) */ @@ -608,7 +607,7 @@ public class Binder<BEAN> implements Serializable { "Property name cannot be null"); checkUnbound(); - BinderPropertyDefinition<BEAN, ?> definition = getBinder().propertySet + PropertyDefinition<BEAN, ?> definition = getBinder().propertySet .getProperty(propertyName) .orElseThrow(() -> new IllegalArgumentException( "Could not resolve property name " + propertyName @@ -627,9 +626,11 @@ public class Binder<BEAN> implements Serializable { definition); try { - return ((BindingBuilder) finalBinding).bind(getter, setter); + Binding binding = ((BindingBuilder) finalBinding).bind(getter, + setter); + getBinder().boundProperties.put(propertyName, binding); + return binding; } finally { - getBinder().boundProperties.add(propertyName); getBinder().incompleteMemberFieldBindings.remove(getField()); } } @@ -1044,12 +1045,12 @@ public class Binder<BEAN> implements Serializable { } } - private final BinderPropertySet<BEAN> propertySet; + private final PropertySet<BEAN> propertySet; /** * Property names that have been used for creating a binding. */ - private final Set<String> boundProperties = new HashSet<>(); + private final Map<String, Binding<BEAN, ?>> boundProperties = new HashMap<>(); private final Map<HasValue<?>, BindingBuilder<BEAN, ?>> incompleteMemberFieldBindings = new IdentityHashMap<>(); @@ -1072,16 +1073,15 @@ public class Binder<BEAN> implements Serializable { private boolean hasChanges = false; /** - * Creates a binder using a custom {@link BinderPropertySet} implementation - * for finding and resolving property names for + * Creates a binder using a custom {@link PropertySet} implementation for + * finding and resolving property names for * {@link #bindInstanceFields(Object)}, {@link #bind(HasValue, String)} and * {@link BindingBuilder#bind(String)}. * * @param propertySet - * the binder property set implementation to use, not - * <code>null</code>. + * the property set implementation to use, not <code>null</code>. */ - protected Binder(BinderPropertySet<BEAN> propertySet) { + protected Binder(PropertySet<BEAN> propertySet) { Objects.requireNonNull(propertySet, "propertySet cannot be null"); this.propertySet = propertySet; } @@ -1094,7 +1094,7 @@ public class Binder<BEAN> implements Serializable { * the bean type to use, not <code>null</code> */ public Binder(Class<BEAN> beanType) { - this(BeanBinderPropertySet.get(beanType)); + this(BeanPropertySet.get(beanType)); } /** @@ -1106,15 +1106,15 @@ public class Binder<BEAN> implements Serializable { * {@link #bind(HasValue, String)} or {@link BindingBuilder#bind(String)}. */ public Binder() { - this(new BinderPropertySet<BEAN>() { + this(new PropertySet<BEAN>() { @Override - public Stream<BinderPropertyDefinition<BEAN, ?>> getProperties() { + public Stream<PropertyDefinition<BEAN, ?>> getProperties() { throw new IllegalStateException( "A Binder created with the default constructor doesn't support listing properties."); } @Override - public Optional<BinderPropertyDefinition<BEAN, ?>> getProperty( + public Optional<PropertyDefinition<BEAN, ?>> getProperty( String name) { throw new IllegalStateException( "A Binder created with the default constructor doesn't support finding properties by name."); @@ -1123,8 +1123,8 @@ public class Binder<BEAN> implements Serializable { } /** - * Creates a binder using a custom {@link BinderPropertySet} implementation - * for finding and resolving property names for + * Creates a binder using a custom {@link PropertySet} implementation for + * finding and resolving property names for * {@link #bindInstanceFields(Object)}, {@link #bind(HasValue, String)} and * {@link BindingBuilder#bind(String)}. * <p> @@ -1137,13 +1137,12 @@ public class Binder<BEAN> implements Serializable { * @see Binder#Binder(Class) * * @param propertySet - * the binder property set implementation to use, not - * <code>null</code>. + * the property set implementation to use, not <code>null</code>. * @return a new binder using the provided property set, not * <code>null</code> */ public static <BEAN> Binder<BEAN> withPropertySet( - BinderPropertySet<BEAN> propertySet) { + PropertySet<BEAN> propertySet) { return new Binder<>(propertySet); } @@ -1272,7 +1271,7 @@ public class Binder<BEAN> implements Serializable { /** * Binds the given field to the property with the given name. The getter and - * setter of the property are looked up using a {@link BinderPropertySet}. + * setter of the property are looked up using a {@link PropertySet}. * <p> * For a <code>Binder</code> created using the {@link Binder#Binder(Class)} * constructor, introspection will be used to find a Java Bean property. If @@ -1297,7 +1296,7 @@ public class Binder<BEAN> implements Serializable { * if the property has no accessible getter * @throws IllegalStateException * if the binder is not configured with an appropriate - * {@link BinderPropertySet} + * {@link PropertySet} * * @see #bind(HasValue, ValueProvider, Setter) */ @@ -1964,7 +1963,7 @@ public class Binder<BEAN> implements Serializable { /** * Configures the {@code binding} with the property definition * {@code definition} before it's being bound. - * + * * @param binding * a binding to configure * @param definition @@ -1973,7 +1972,7 @@ public class Binder<BEAN> implements Serializable { */ protected BindingBuilder<BEAN, ?> configureBinding( BindingBuilder<BEAN, ?> binding, - BinderPropertyDefinition<BEAN, ?> definition) { + PropertyDefinition<BEAN, ?> definition) { return binding; } @@ -2217,7 +2216,7 @@ public class Binder<BEAN> implements Serializable { private void handleProperty(Field field, Object objectWithMemberFields, BiConsumer<String, Class<?>> propertyHandler) { - Optional<BinderPropertyDefinition<BEAN, ?>> descriptor = getPropertyDescriptor( + Optional<PropertyDefinition<BEAN, ?>> descriptor = getPropertyDescriptor( field); if (!descriptor.isPresent()) { @@ -2225,7 +2224,7 @@ public class Binder<BEAN> implements Serializable { } String propertyName = descriptor.get().getName(); - if (boundProperties.contains(propertyName)) { + if (boundProperties.containsKey(propertyName)) { return; } @@ -2237,10 +2236,25 @@ public class Binder<BEAN> implements Serializable { } propertyHandler.accept(propertyName, descriptor.get().getType()); - boundProperties.add(propertyName); + assert boundProperties.containsKey(propertyName); + } + + /** + * Gets the binding for a property name. Bindings are available by property + * name if bound using {@link #bind(HasValue, String)}, + * {@link BindingBuilder#bind(String)} or indirectly using + * {@link #bindInstanceFields(Object)}. + * + * @param propertyName + * the property name of the binding to get + * @return the binding corresponding to the property name, or an empty + * optional if there is no binding with that property name + */ + public Optional<Binding<BEAN, ?>> getBinding(String propertyName) { + return Optional.ofNullable(boundProperties.get(propertyName)); } - private Optional<BinderPropertyDefinition<BEAN, ?>> getPropertyDescriptor( + private Optional<PropertyDefinition<BEAN, ?>> getPropertyDescriptor( Field field) { PropertyId propertyIdAnnotation = field.getAnnotation(PropertyId.class); @@ -2254,8 +2268,7 @@ public class Binder<BEAN> implements Serializable { String minifiedFieldName = minifyFieldName(propertyId); - return propertySet.getProperties() - .map(BinderPropertyDefinition::getName) + return propertySet.getProperties().map(PropertyDefinition::getName) .filter(name -> minifyFieldName(name).equals(minifiedFieldName)) .findFirst().flatMap(propertySet::getProperty); } diff --git a/server/src/main/java/com/vaadin/data/BinderPropertyDefinition.java b/server/src/main/java/com/vaadin/data/PropertyDefinition.java index b4145a8c4f..79bb2159b4 100644 --- a/server/src/main/java/com/vaadin/data/BinderPropertyDefinition.java +++ b/server/src/main/java/com/vaadin/data/PropertyDefinition.java @@ -21,17 +21,17 @@ import java.util.Optional; import com.vaadin.server.Setter; /** - * A property from a {@link BinderPropertySet}. + * A property from a {@link PropertySet}. * * @author Vaadin Ltd * @since * * @param <T> - * the type of the binder property set + * the type of the property set * @param <V> * the property type */ -public interface BinderPropertyDefinition<T, V> extends Serializable { +public interface PropertyDefinition<T, V> extends Serializable { /** * Gets the value provider that is used for finding the value of this * property for a bean. @@ -62,9 +62,16 @@ public interface BinderPropertyDefinition<T, V> extends Serializable { public String getName(); /** - * Gets the {@link BinderPropertySet} that this property belongs to. + * Gets the human readable caption to show for this property. * - * @return the binder property set, not <code>null</code> + * @return the caption to show, not <code>null</code> */ - public BinderPropertySet<T> getPropertySet(); + public String getCaption(); + + /** + * Gets the {@link PropertySet} that this property belongs to. + * + * @return the property set, not <code>null</code> + */ + public PropertySet<T> getPropertySet(); } diff --git a/server/src/main/java/com/vaadin/data/BinderPropertySet.java b/server/src/main/java/com/vaadin/data/PropertySet.java index 6252a228aa..7b557dc293 100644 --- a/server/src/main/java/com/vaadin/data/BinderPropertySet.java +++ b/server/src/main/java/com/vaadin/data/PropertySet.java @@ -20,7 +20,8 @@ import java.util.Optional; import java.util.stream.Stream; /** - * Describes a set of properties that can be used with a {@link Binder}. + * Describes a set of properties that can be used for configuration based on + * property names instead of setter and getter callbacks. * * @author Vaadin Ltd * @@ -29,13 +30,13 @@ import java.util.stream.Stream; * @param <T> * the type for which the properties are defined */ -public interface BinderPropertySet<T> extends Serializable { +public interface PropertySet<T> extends Serializable { /** * Gets all known properties as a stream. * * @return a stream of property names, not <code>null</code> */ - public Stream<BinderPropertyDefinition<T, ?>> getProperties(); + public Stream<PropertyDefinition<T, ?>> getProperties(); /** * Gets the definition for the named property, or an empty optional if there @@ -46,5 +47,5 @@ public interface BinderPropertySet<T> extends Serializable { * @return the property definition, or empty optional if property doesn't * exist */ - public Optional<BinderPropertyDefinition<T, ?>> getProperty(String name); + public Optional<PropertyDefinition<T, ?>> getProperty(String name); } diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index 4052282fdc..daf1cd1c69 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -41,10 +41,13 @@ import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import com.vaadin.data.BeanPropertySet; import com.vaadin.data.Binder; import com.vaadin.data.Binder.Binding; import com.vaadin.data.HasDataProvider; import com.vaadin.data.HasValue; +import com.vaadin.data.PropertyDefinition; +import com.vaadin.data.PropertySet; import com.vaadin.data.ValueProvider; import com.vaadin.data.provider.DataCommunicator; import com.vaadin.data.provider.DataProvider; @@ -1609,7 +1612,9 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, * a setter that stores the component value in the row item * @return this column * + * @see #setEditorBinding(Binding) * @see Grid#getEditor() + * @see Binder#bind(HasValue, ValueProvider, Setter) */ public <C extends HasValue<V> & Component> Column<T, V> setEditorComponent( C editorComponent, Setter<T, V> setter) { @@ -1624,6 +1629,47 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, } /** + * Sets a component to use for editing values of this columns in the + * editor row. This method can only be used if the column has an + * {@link #setId(String) id} and the {@link Grid} has been created using + * {@link Grid#Grid(Class)} or some other way that allows finding + * properties based on property names. + * <p> + * This is a shorthand for use in simple cases where no validator or + * converter is needed. Use {@link #setEditorBinding(Binding)} to + * support more complex cases. + * <p> + * <strong>Note:</strong> The same component cannot be used for multiple + * columns. + * + * @param editorComponent + * the editor component + * @return this column + * + * @see #setEditorBinding(Binding) + * @see Grid#getEditor() + * @see Binder#bind(HasValue, String) + * @see Grid#Grid(Class) + */ + public <F, C extends HasValue<F> & Component> Column<T, V> setEditorComponent( + C editorComponent) { + Objects.requireNonNull(editorComponent, + "Editor component cannot be null"); + + String propertyName = getId(); + if (propertyName == null) { + throw new IllegalStateException( + "setEditorComponent without a setter can only be used if the column has an id. " + + "Use another setEditorComponent(Component, Setter) or setEditorBinding(Binding) instead."); + } + + Binding<T, F> binding = getGrid().getEditor().getBinder() + .bind(editorComponent, propertyName); + + return setEditorBinding(binding); + } + + /** * Gets the grid that this column belongs to. * * @return the grid that this column belongs to, or <code>null</code> if @@ -1851,10 +1897,64 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, private Editor<T> editor; + private final PropertySet<T> propertySet; + /** - * Constructor for the {@link Grid} component. + * Creates a new grid without support for creating columns based on property + * names. Use an alternative constructor, such as {@link Grid#Grid(Class)}, + * to create a grid that automatically sets up columns based on the type of + * presented data. + * + * @see #Grid(Class) + * @see #withPropertySet(PropertySet) */ public Grid() { + this(new PropertySet<T>() { + @Override + public Stream<PropertyDefinition<T, ?>> getProperties() { + // No columns configured by default + return Stream.empty(); + } + + @Override + public Optional<PropertyDefinition<T, ?>> getProperty(String name) { + throw new IllegalStateException( + "A Grid created without a bean type class literal or a custom property set" + + " doesn't support finding properties by name."); + } + }); + } + + /** + * Creates a new grid that uses reflection based on the provided bean type + * to automatically set up an initial set of columns. All columns will be + * configured using the same {@link Object#toString()} renderer that is used + * by {@link #addColumn(ValueProvider)}. + * + * @param beanType + * the bean type to use, not <code>null</code> + * @see #Grid() + * @see #withPropertySet(PropertySet) + */ + public Grid(Class<T> beanType) { + this(BeanPropertySet.get(beanType)); + } + + /** + * Creates a grid using a custom {@link PropertySet} implementation for + * configuring the initial columns and resolving property names for + * {@link #addColumn(String)} and + * {@link Column#setEditorComponent(HasValue)}. + * + * @see #withPropertySet(PropertySet) + * + * @param propertySet + * the property set implementation to use, not <code>null</code>. + */ + protected Grid(PropertySet<T> propertySet) { + Objects.requireNonNull(propertySet, "propertySet cannot be null"); + this.propertySet = propertySet; + registerRpc(new GridServerRpcImpl()); setDefaultHeaderRow(appendHeaderRow()); @@ -1882,6 +1982,33 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, } } }); + + // Automatically add columns for all available properties + propertySet.getProperties().map(PropertyDefinition::getName) + .forEach(this::addColumn); + } + + /** + * Creates a grid using a custom {@link PropertySet} implementation for + * creating a default set of columns and for resolving property names with + * {@link #addColumn(String)} and + * {@link Column#setEditorComponent(HasValue)}. + * <p> + * This functionality is provided as static method instead of as a public + * constructor in order to make it possible to use a custom property set + * without creating a subclass while still leaving the public constructors + * focused on the common use cases. + * + * @see Grid#Grid() + * @see Grid#Grid(Class) + * + * @param propertySet + * the property set implementation to use, not <code>null</code>. + * @return a new grid using the provided property set, not <code>null</code> + */ + public static <BEAN> Grid<BEAN> withPropertySet( + PropertySet<BEAN> propertySet) { + return new Grid<>(propertySet); } /** @@ -1940,6 +2067,37 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, } /** + * Adds a new column with the given property name. 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)}. + * + * @param propertyName + * the property name of the new column, not <code>null</code> + * @return the newly added column, not <code>null</code> + */ + public Column<T, ?> addColumn(String propertyName) { + Objects.requireNonNull(propertyName, "Property name 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)); + + return addColumn(definition.getGetter()).setId(definition.getName()) + .setCaption(definition.getCaption()); + } + + /** * 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()}. Sorting in memory is executed by @@ -2070,7 +2228,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, * * @param columnId * the identifier of the column to get - * @return the column corresponding to the given column identifier + * @return the column corresponding to the given column identifier, or + * <code>null</code> if there is no such column */ public Column<T, ?> getColumn(String columnId) { return columnIds.get(columnId); @@ -2983,7 +3142,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents, * @return editor */ protected Editor<T> createEditor() { - return new EditorImpl<>(); + return new EditorImpl<>(propertySet); } private void addExtensionComponent(Component c) { diff --git a/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java b/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java index a001a5026a..dae7c61ee7 100644 --- a/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java +++ b/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java @@ -27,6 +27,7 @@ import com.vaadin.data.Binder; import com.vaadin.data.Binder.Binding; import com.vaadin.data.BinderValidationStatus; import com.vaadin.data.BinderValidationStatusHandler; +import com.vaadin.data.PropertySet; import com.vaadin.shared.ui.grid.editor.EditorClientRpc; import com.vaadin.shared.ui.grid.editor.EditorServerRpc; import com.vaadin.shared.ui.grid.editor.EditorState; @@ -112,8 +113,11 @@ public class EditorImpl<T> extends AbstractGridExtension<T> /** * Constructor for internal implementation of the Editor. + * + * @param propertySet + * the property set to use for configuring the default binder */ - public EditorImpl() { + public EditorImpl(PropertySet<T> propertySet) { rpc = getRpcProxy(EditorClientRpc.class); registerRpc(new EditorServerRpc() { @@ -142,7 +146,7 @@ public class EditorImpl<T> extends AbstractGridExtension<T> } }); - setBinder(new Binder<>()); + setBinder(Binder.withPropertySet(propertySet)); } @Override |