com.holonplatform.vaadin7.components.PropertyInputGroup Maven / Gradle / Ivy
/*
* Copyright 2016-2017 Axioma srl.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.holonplatform.vaadin7.components;
import java.util.Optional;
import java.util.stream.Stream;
import com.holonplatform.core.Validator;
import com.holonplatform.core.Validator.ValidationException;
import com.holonplatform.core.i18n.Localizable;
import com.holonplatform.core.internal.utils.ObjectUtils;
import com.holonplatform.core.property.Property;
import com.holonplatform.core.property.PropertyBox;
import com.holonplatform.core.property.PropertyRenderer;
import com.holonplatform.core.property.PropertyRendererRegistry;
import com.holonplatform.vaadin7.components.Input.InputPropertyRenderer;
import com.holonplatform.vaadin7.components.PropertyBinding.PostProcessor;
import com.holonplatform.vaadin7.internal.components.DefaultPropertyInputGroup;
import com.holonplatform.vaadin7.internal.components.ValidationUtils;
import com.vaadin.ui.Label;
/**
* A class to manage a group of {@link Input}s bound to a {@link Property} set, loading and obtaining property values in
* and from {@link Input}s using the {@link PropertyBox} data container type.
*
* Supports overall {@link Validator}s registration to validate all the {@link Input} values, allowing cross input
* validation, using a {@link PropertyBox} to represent the inputs value set.
*
*
* By default, property {@link Input} components are obtained from the {@link PropertyRenderer}s registered in the
* context {@link PropertyRendererRegistry}, if available. Custom {@link PropertyRenderer} registration is supported to
* provide a custom behaviour for specific properties.
*
*
* Default property values are supported using a {@link DefaultValueProvider}. The property default values are loaded
* when {@link #clear()} or {@link #setValue(PropertyBox)} methods are invoked.
*
*
* Convenience methods {@link #setEnabled(boolean)} and {@link #setReadOnly(boolean)} can be used to change the enabled
* / read-only state for all the property bound {@link Input}s.
*
*
* @since 5.0.0
*/
public interface PropertyInputGroup extends PropertySetBound, ValueHolder, Validatable {
/**
* Gets all the {@link Input}s that have been bound to a property.
* @return An {@link Iterable} on all bound {@link Input}s
*/
Iterable> getInputs();
/**
* Get the {@link Input} bound to given property
, if any.
* @param Property type
* @param property Property for which to get the associated {@link Input} (not null)
* @return Optional {@link Input} bound to given property
*/
Optional> getInput(Property property);
/**
* Return a {@link Stream} of the properties and their bound {@link Input}s of this input group.
* @param Property type
* @return Property-Input {@link PropertyBinding} stream
*/
Stream>> stream();
/**
* Get the current property values collected into a {@link PropertyBox}, using the group configured properties as
* property set.
*
* For each property with a bound {@link Input} component, the property value is obtained from the {@link Input}
* component through the {@link Input#getValue()} method.
*
* @param validate true
to check the validity of the property bound {@link Input}s and of this
* {@link PropertyInputGroup} before returing the value, throwing a {@link ValidationException} if the
* validation is not successful.
* @return A {@link PropertyBox} containing the property values (never null)
* @throws ValidationException If validate
is true
and an {@link Input} value is not valid
* @throws OverallValidationException If the overall validation failed
*/
PropertyBox getValue(boolean validate);
/**
* Get the current property values collected into a {@link PropertyBox}, using the group configured properties as
* property set.
*
* For each property with a bound {@link Input} component, the property value is obtained from the {@link Input}
* component through the {@link Input#getValue()} method.
*
*
* The available {@link Input}s and the overall group validation is performed before returning the value, throwing a
* {@link ValidationException} if the validation is not successful.
*
* @return A {@link PropertyBox} containing the property values (never null)
* @throws ValidationException If one or more input value is not valid, providing the validation error messages
* @throws OverallValidationException If the overall validation failed
* @see #getValue(boolean)
* @see #getValueIfValid()
*/
@Override
default PropertyBox getValue() {
return getValue(true);
}
/**
* Get the current property values collected into a {@link PropertyBox}, using the group configured properties as
* property set, only if the property bound {@link Input}s and this {@link PropertyInputGroup} are valid
*
* For each property with a bound {@link Input} component, the property value is obtained from the {@link Input}
* component through the {@link Input#getValue()} method.
*
* @return A {@link PropertyBox} containing the property values, or an empty Optional if validation failed
*/
Optional getValueIfValid();
/**
* Set the current property values using a {@link PropertyBox}, loading the values to the available property bound
* {@link Input}s through the {@link Input#setValue(Object)} method.
*
* Only the properties which belong to the group's property set are taken into account.
*
* @param propertyBox the {@link PropertyBox} which contains the property values to load. If null
, all
* the {@link Input} components are cleared.
* @param validate true
to check the validity of the property bound {@link Input}s and of this
* {@link PropertyInputGroup}, throwing a {@link ValidationException} if the validation is not successful.
* @throws ValidationException If validate
is true
and an {@link Input} value is not valid
* @throws OverallValidationException If overall validation failed
*/
void setValue(PropertyBox propertyBox, boolean validate);
/**
* Set the current property values using a {@link PropertyBox}, loading the values to the available property bound
* {@link Input}s through the {@link Input#setValue(Object)} method.
*
* Only the properties which belong to the group's property set are taken into account.
*
*
* By default, no value validation is performed using this method.
*
* @param propertyBox the {@link PropertyBox} which contains the property values to load. If null
, all
* the {@link Input} components are cleared.
* @see #setValue(PropertyBox, boolean)
*/
@Override
default void setValue(PropertyBox propertyBox) {
setValue(propertyBox, false);
}
/**
* Set the read-only mode for all the group inputs.
* @param readOnly true
to set all inputs as read-only, false
to unset
*/
void setReadOnly(boolean readOnly);
/**
* Updates the enabled state of all the group inputs.
* @param enabled true
to enable all group inputs, false
to disable them
*/
void setEnabled(boolean enabled);
/**
* Get a {@link Builder} to create and setup a {@link PropertyInputGroup}.
* @return {@link PropertyInputGroup} builder
*/
static PropertyInputGroupBuilder builder() {
return new DefaultPropertyInputGroup.DefaultBuilder();
}
// -------
/**
* Interface to provide the default value for a {@link Property}.
* @param Property type
*/
@FunctionalInterface
public interface DefaultValueProvider {
/**
* Get the property default value
* @param property Property (never null)
* @return Default value
*/
T getDefaultValue(Property property);
}
// Builder
/**
* {@link PropertyInputGroup} builder.
*/
public interface PropertyInputGroupBuilder extends Builder {
}
/**
* Base {@link PropertyInputGroup} builder.
* @param Actual {@link PropertyInputGroup} type
* @param Concrete builder type
*/
public interface Builder> {
/**
* Add given properties to the {@link PropertyInputGroup} property set.
* @param Property type
* @param properties Properties to add
* @return this
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
B properties(P... properties);
/**
* Add given properties to the {@link PropertyInputGroup} property set.
* @param
Property type
* @param properties Properties to add (not null)
* @return this
*/
@SuppressWarnings("rawtypes")
B properties(Iterable
properties);
/**
* Set the given property as read-only. If a property is read-only, the {@link Input} bound to the property will
* be setted as read-only too, and its value will never be written to a {@link PropertyBox} nor validated.
* @param Property type
* @param property Property to set as read-only (not null)
* @return this
*/
B readOnly(Property property);
/**
* Set the given property as required. If a property is required, the {@link Input} bound to the property will
* be setted as required, and its validation will fail when empty.
* @param Property type
* @param property Property to set as required (not null)
* @return this
*/
B required(Property property);
/**
* Set the given property as required, using given {@link Validator} to check the property value. If a property
* is required, the {@link Input} bound to the property will be setted as required, and its validation will fail
* when empty.
* @param Property type
* @param property Property to set as required (not null)
* @param validator The {@link Validator} to use to check the required property value (not null)
* @return this
*/
B required(Property property, Validator validator);
/**
* Set the given property as required. If a property is required, the {@link Input} bound to the property will
* be setted as required, and its validation will fail when empty.
* @param Property type
* @param property Property to set as required (not null)
* @param message The message to use to notify the required validation failure
* @return this
*/
B required(Property property, Localizable message);
/**
* Set the given property as required. If a property is required, the {@link Input} bound to the property will
* be setted as required, and its validation will fail when empty.
* @param Property type
* @param property Property to set as required (not null)
* @param message The default message to use to notify the required validation failure
* @param messageCode The message localization code
* @param arguments Optional message translation arguments
* @return this
*/
default B required(Property property, String message, String messageCode, Object... arguments) {
return required(property, Localizable.builder().message(message).messageCode(messageCode)
.messageArguments(arguments).build());
}
/**
* Set the given property as required. If a property is required, the {@link Input} bound to the property will
* be setted as required, and its validation will fail when empty.
* @param Property type
* @param property Property to set as required (not null)
* @param message The default message to use to notify the required validation failure
* @return this
*/
default B required(Property property, String message) {
return required(property, Localizable.builder().message(message).build());
}
/**
* Set the given property as hidden. If a property is hidden, the {@link Input} bound to the property will never
* be generated, but its value will be written to a {@link PropertyBox} using
* {@link PropertyInputGroup#getValue()}.
* @param Property type
* @param property Property to set as hidden (not null)
* @return this
*/
B hidden(Property property);
/**
* Set the default value provider for given property
.
* @param Property type
* @param property Property (not null)
* @param defaultValueProvider DefaultValueProvider (not null)
* @return this
*/
B defaultValue(Property property, DefaultValueProvider defaultValueProvider);
/**
* Adds a {@link Validator} to the {@link Input} bound to given property
.
* @param Property type
* @param property Property (not null)
* @param validator Validator to add (not null)
* @return this
*/
B withValidator(Property property, Validator validator);
/**
* Adds a {@link com.vaadin.data.Validator} to the {@link Input} bound to given property
.
* @param Property type
* @param property Property (not null)
* @param validator Validator to add (not null)
* @return this
*/
default B withValidator(Property property, com.vaadin.data.Validator validator) {
return withValidator(property, ValidationUtils.asValidator(validator));
}
/**
* Adds a {@link Validator} to the {@link PropertyInputGroup}, using a {@link PropertyBox} to provide the
* property values to validate.
* @param validator Validator to add (not null)
* @return this
*/
B withValidator(Validator validator);
/**
* Adds a {@link com.vaadin.data.Validator} to the {@link PropertyInputGroup}.
* @param validator Validator to add (not null)
* @return this
*/
default B withValidator(com.vaadin.data.Validator validator) {
return withValidator(ValidationUtils.asValidator(validator));
}
/**
* Set the {@link ValidationStatusHandler} to use to track given property
validation status
* changes.
* @param Property type
* @param property Property for which to set the validation status handler
* @param validationStatusHandler the {@link ValidationStatusHandler} to associate to given
* property
(not null)
* @return this
*/
B validationStatusHandler(Property property, ValidationStatusHandler validationStatusHandler);
/**
* Set the {@link Label} to use to track given property
validation status changes.
* @param Property type
* @param property Property for which to set the validation status label
* @param statusLabel the status {@link Label} to use to track given property
validation status
* (not null)
* @return this
*/
default B validationStatusHandler(Property property, Label statusLabel) {
return validationStatusHandler(property, ValidationStatusHandler.label(statusLabel));
}
/**
* Set the {@link ValidationStatusHandler} to use to track all the properties validation status changes.
*
* A specific {@link ValidationStatusHandler} for each property can be configured using
* {@link #validationStatusHandler(Property, ValidationStatusHandler)}.
*
* @param validationStatusHandler the {@link ValidationStatusHandler} to set
* @return this
*/
B propertiesValidationStatusHandler(ValidationStatusHandler validationStatusHandler);
/**
* Set the {@link ValidationStatusHandler} to use to track overall validation status changes.
* @param validationStatusHandler the {@link ValidationStatusHandler} to set (not null)
* @return this
*/
B validationStatusHandler(ValidationStatusHandler validationStatusHandler);
/**
* Set the {@link Label} to use as status label to track overall validation status changes.
* @param statusLabel the status {@link Label} to set (not null)
* @return this
*/
default B validationStatusHandler(Label statusLabel) {
return validationStatusHandler(ValidationStatusHandler.label(statusLabel));
}
/**
* Sets whether to validate the available {@link Input}s value every time the {@link Input} value changes.
*
* Default is true
.
*
* @param validateOnValueChange true
to perform value validation every time a {@link Input} value
* changes, false
if not
* @return this
*/
B validateOnValueChange(boolean validateOnValueChange);
/**
* Set whether to stop validation at first validation failure. If true
, only the first
* {@link ValidationException} is thrown at validation, otherwise a {@link ValidationException} containing all
* the occurred validation exception is thrown.
* @param stopValidationAtFirstFailure true
to stop validation at first validation failure
* @return this
*/
B stopValidationAtFirstFailure(boolean stopValidationAtFirstFailure);
/**
* Set whether to stop overall validation at first validation failure. If true
, only the first
* {@link OverallValidationException} is thrown at validation, otherwise a {@link OverallValidationException}
* containing all the occurred validation exception is thrown.
*
* The overall validation is the one which is performed using validators added with
* {@link #withValidator(Validator)} method.
*
* @param stopOverallValidationAtFirstFailure true
to stop overall validation at first validation
* failure
* @return this
*/
B stopOverallValidationAtFirstFailure(boolean stopOverallValidationAtFirstFailure);
/**
* Set to ignore any {@link Property} registered {@link Validator} when binding the property to an {@link Input}
* component, i.e. to not inherit property {@link Validator}s when the property-input binding is performed.
* @return this
*/
B ignorePropertyValidation();
/**
* Set the specific {@link PropertyRenderer} to use to render the {@link Input} to bind to given
* property
.
* @param Property type
* @param property Property (not null)
* @param renderer Property renderer (not null)
* @return this
*/
B bind(Property property, PropertyRenderer, T> renderer);
/**
* Convenience method to set a specific {@link PropertyRenderer} to use to render the {@link Input} to bind to
* given property
using the {@link InputPropertyRenderer} functional interface.
* @param Property type
* @param property Property (not null)
* @param renderer Property renderer (not null)
* @return this
*/
default B bind(Property property, InputPropertyRenderer renderer) {
ObjectUtils.argumentNotNull(property, "Property must be not null");
ObjectUtils.argumentNotNull(renderer, "Renderer must be not null");
return bind(property, (PropertyRenderer, T>) renderer);
}
/**
* Bind the given property
to given input
instance. If the property was already bound
* to a {@link Input}, the old input will be replaced by the new input.
*
* This method also adds property validators to given {@link Input} when applicable.
*
* @param Property type
* @param property Property (not null)
* @param input Input to bind (not null)
* @return this
*/
default B bind(Property property, Input input) {
ObjectUtils.argumentNotNull(property, "Property must be not null");
ObjectUtils.argumentNotNull(input, "Input must be not null");
return bind(property, p -> input);
}
/**
* Set whether to ignore properties without a bound {@link Input}. Default is false
, and an
* exception is thrown if a property of the {@link PropertyInputGroup} cannot be bound to any input component.
* @param ignoreMissingInputs Whether to ignore when the {@link Input} for a property is missing
* @return this
*/
B ignoreMissingInputs(boolean ignoreMissingInputs);
/**
* Add a {@link PostProcessor} to allow further {@link Input} configuration before the input is actually bound
* to a property.
* @param postProcessor the {@link PostProcessor} to add (not null)
* @return this
*/
B withPostProcessor(PostProcessor> postProcessor);
/**
* Build the {@link PropertyInputGroup}.
* @return a new {@link PropertyInputGroup} instance
*/
G build();
}
/**
* {@link ValidationException} extension to discern inputs validation and overall container validation exceptions.
*/
@SuppressWarnings("serial")
public class OverallValidationException extends ValidationException {
/**
* {@inheritDoc}
*/
public OverallValidationException(String message) {
super(message);
}
/**
* {@inheritDoc}
*/
public OverallValidationException(String message, String messageCode, Object... messageArguments) {
super(message, messageCode, messageArguments);
}
/**
* {@inheritDoc}
*/
public OverallValidationException(ValidationException... causes) {
super(causes);
}
}
}