com.holonplatform.vaadin.internal.components.AbstractComposableForm Maven / Gradle / Ivy
/*
* Copyright 2000-2017 Holon TDCN.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR 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.vaadin.internal.components;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import com.holonplatform.core.Path;
import com.holonplatform.core.exceptions.TypeMismatchException;
import com.holonplatform.core.i18n.Localizable;
import com.holonplatform.core.i18n.LocalizationContext;
import com.holonplatform.core.internal.utils.ObjectUtils;
import com.holonplatform.core.property.Property;
import com.holonplatform.vaadin.components.Components;
import com.holonplatform.vaadin.components.ComposableComponent;
import com.holonplatform.vaadin.components.PropertyComponentSource;
import com.holonplatform.vaadin.components.builders.ComponentConfigurator;
import com.holonplatform.vaadin.components.builders.ComponentConfigurator.BaseComponentConfigurator;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomComponent;
/**
* Base {@link ComposableComponent} form.
*
* @param Content component type
* @param Components source type
*
* @since 5.0.0
*/
public abstract class AbstractComposableForm
extends CustomComponent implements ComposableComponent {
private static final long serialVersionUID = 6196476129131362753L;
/**
* Form content initializer
*/
private Consumer initializer;
/**
* Composer
*/
private Composer super C, S> composer;
/**
* Compose on attach behaviour
*/
private boolean composeOnAttach = true;
/**
* Components width mode
*/
private ComponentsWidthMode componentsWidthMode = ComponentsWidthMode.AUTO;
/**
* Composition state
*/
private boolean composed = false;
/**
* Custom property captions
*/
private Map, Localizable> propertyCaptions = new HashMap<>(8);
/**
* Hidden property captions
*/
private Collection> hiddenPropertyCaptions = new HashSet<>(8);
/**
* Component configurators
*/
private Map, Consumer> propertyComponentConfigurators = new HashMap<>(8);
/**
* Constructor
*/
public AbstractComposableForm() {
this(null);
}
/**
* Constructor with form content
* @param content Form composition content
*/
public AbstractComposableForm(C content) {
super();
if (content != null) {
setCompositionRoot(content);
}
// undefined width by default
setWidthUndefined();
// default style name
addStyleName("h-form");
// scrollable by default
addStyleName(Components.SCROLLABLE_STYLENAME);
}
protected abstract S getComponentSource();
/*
* (non-Javadoc)
* @see com.holonplatform.vaadin.components.ComposableComponent#getContent()
*/
@Override
public Component getContent() {
return getCompositionRoot();
}
/**
* Get the form content initializer
* @return the form content initializer
*/
public Optional> getInitializer() {
return Optional.ofNullable(initializer);
}
/**
* Set the form content initializer
* @param initializer the initializer to set
*/
public void setInitializer(Consumer initializer) {
this.initializer = initializer;
}
/**
* Get the composer
* @return the composer
*/
public Composer super C, S> getComposer() {
return composer;
}
/**
* Set the composer
* @param composer the composer to set
*/
public void setComposer(Composer super C, S> composer) {
this.composer = composer;
}
/**
* Gets whether the form must be composed on {@link #attach()}, if not already composed invoking {@link #compose()}.
* @return true
if the form must be composed on {@link #attach()}
*/
public boolean isComposeOnAttach() {
return composeOnAttach;
}
/**
* Sets whether the form must be composed on {@link #attach()}, if not already composed invoking {@link #compose()}.
* @param composeOnAttach true
to compose the form on {@link #attach()}
*/
public void setComposeOnAttach(boolean composeOnAttach) {
this.composeOnAttach = composeOnAttach;
}
/*
* (non-Javadoc)
* @see com.holonplatform.vaadin.components.ComposableComponent#getComponentsWidthMode()
*/
@Override
public ComponentsWidthMode getComponentsWidthMode() {
return componentsWidthMode;
}
/**
* Set the composed Components width setup mode
* @param componentsWidthMode the ComponentsWidthMode to set (not null)
*/
public void setComponentsWidthMode(ComponentsWidthMode componentsWidthMode) {
ObjectUtils.argumentNotNull(componentsWidthMode, "ComponentsWidthMode must be not null");
this.componentsWidthMode = componentsWidthMode;
}
/**
* Set the caption for the component bound to given property
* @param property Property
* @param caption Localizable caption
*/
protected void setPropertyCaption(Property> property, Localizable caption) {
if (property != null && caption != null) {
propertyCaptions.put(property, caption);
}
}
/**
* Set the caption for the component bound to given property as hidden
* @param property Property
*/
protected void hidePropertyCaption(Property> property) {
if (property != null) {
hiddenPropertyCaptions.add(property);
}
}
/**
* Set the {@link ComponentConfigurator} consumer associated to given {@link Property}.
* @param property Property (not null)
* @param configurator Component configurator (not null)
*/
public void setPropertyComponentConfigurator(Property> property,
Consumer configurator) {
ObjectUtils.argumentNotNull(property, "Property must be not null");
ObjectUtils.argumentNotNull(configurator, "ComponentConfigurator Consumer must be not null");
propertyComponentConfigurators.put(property, configurator);
}
/**
* Get the {@link ComponentConfigurator} consumer associated to given {@link Property}, if available.
* @param property Property
* @return Optional component configurator
*/
protected Optional> getPropertyComponentConfigurator(Property> property) {
return Optional.ofNullable(propertyComponentConfigurators.get(property));
}
/*
* (non-Javadoc)
* @see com.holonplatform.vaadin.components.ComposableComponent#compose()
*/
@SuppressWarnings("unchecked")
@Override
public void compose() {
if (getContent() == null) {
throw new IllegalStateException("Missing form content");
}
if (getComposer() == null) {
throw new IllegalStateException("Missing form composer");
}
final C content;
try {
content = (C) getContent();
} catch (Exception e) {
throw new IllegalStateException("Form content is not of expected type", e);
}
// setup content
setupContent(content);
// init form content
getInitializer().ifPresent(i -> i.accept(content));
// setup components
final boolean fullWidth = (getComponentsWidthMode() == ComponentsWidthMode.FULL)
|| ((getComponentsWidthMode() == ComponentsWidthMode.AUTO) && getWidth() > -1);
getComponentSource().streamOfComponents().forEach(binding -> {
setupPropertyComponent(binding.getProperty(), binding.getComponent(), fullWidth);
});
// compose
getComposer().compose(content, getComponentSource());
this.composed = true;
}
/**
* Setup the {@link Component} associated with given {@link Property}.
* @param property Property
* @param component Component
* @param fullWidth whether to set the Component to 100% width according to {@link #getComponentsWidthMode()}
*/
protected void setupPropertyComponent(Property> property, Component component, boolean fullWidth) {
if (fullWidth) {
component.setWidth(100, Unit.PERCENTAGE);
}
// check configurator
getPropertyComponentConfigurator(property).ifPresent(consumer -> {
if (!(component instanceof AbstractComponent)) {
throw new TypeMismatchException("Cannot configure Component of type [" + component.getClass().getName()
+ "] using the ComponentConfigurator associated with property [" + property
+ "]: the Component must extend AbstractComponent");
}
consumer.accept(BaseComponentConfigurator.create((AbstractComponent) component));
});
}
/**
* Setup content component, adjusting width and height according to parent component
* @param content Content component
*/
private void setupContent(C content) {
if (content != null) {
if (getWidth() > -1) {
content.setWidth(100, Unit.PERCENTAGE);
}
}
}
/*
* (non-Javadoc)
* @see com.vaadin.ui.AbstractComponent#attach()
*/
@Override
public void attach() {
super.attach();
// check compose on attach
if (!composed) {
compose();
}
}
/**
* Configure given component using given property.
* @param property Property to which the component refers
* @param component Component to configure
*/
protected void configureComponent(Property> property, Component component) {
if (hiddenPropertyCaptions.contains(property)) {
component.setCaption(null);
} else {
if (propertyCaptions.containsKey(property)) {
component.setCaption(LocalizationContext.translate(propertyCaptions.get(property), true));
} else {
if (component.getCaption() == null) {
if (Path.class.isAssignableFrom(property.getClass())) {
component.setCaption(((Path>) property).getName());
} else {
component.setCaption(property.toString());
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy