All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.holonplatform.vaadin.internal.components.AbstractComposableForm Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * 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 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 getComposer() {
		return composer;
	}

	/**
	 * Set the composer
	 * @param composer the composer to set
	 */
	public void setComposer(Composer 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