FormFactoryImpl.java

  1. /*
  2.  * Copyright 2010-2025 James Pether Sörling
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *   http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  *
  16.  *  $Id$
  17.  *  $HeadURL$
  18. */
  19. package com.hack23.cia.web.impl.ui.application.views.common.formfactory.impl;

  20. import java.beans.BeanInfo;
  21. import java.beans.IntrospectionException;
  22. import java.beans.Introspector;
  23. import java.beans.PropertyDescriptor;
  24. import java.io.Serializable;
  25. import java.math.BigDecimal;
  26. import java.math.BigInteger;
  27. import java.text.MessageFormat;
  28. import java.util.Date;
  29. import java.util.HashMap;
  30. import java.util.List;
  31. import java.util.Map;

  32. import org.apache.commons.lang3.StringUtils;
  33. import org.slf4j.Logger;
  34. import org.slf4j.LoggerFactory;
  35. import org.springframework.stereotype.Service;

  36. import com.hack23.cia.web.impl.ui.application.views.common.formfactory.api.FormFactory;
  37. import com.hack23.cia.web.impl.ui.application.views.common.sizing.ContentRatio;
  38. import com.hack23.cia.web.impl.ui.application.views.common.sizing.ContentSize;
  39. import com.hack23.cia.web.impl.ui.application.views.pageclicklistener.CommitFormWrapperClickListener;
  40. import com.vaadin.data.BeanValidationBinder;
  41. import com.vaadin.data.Binder;
  42. import com.vaadin.data.Converter;
  43. import com.vaadin.data.converter.StringToBigDecimalConverter;
  44. import com.vaadin.data.converter.StringToBigIntegerConverter;
  45. import com.vaadin.data.converter.StringToBooleanConverter;
  46. import com.vaadin.data.converter.StringToDateConverter;
  47. import com.vaadin.data.converter.StringToIntegerConverter;
  48. import com.vaadin.data.converter.StringToLongConverter;
  49. import com.vaadin.icons.VaadinIcons;
  50. import com.vaadin.ui.AbstractField;
  51. import com.vaadin.ui.AbstractOrderedLayout;
  52. import com.vaadin.ui.Alignment;
  53. import com.vaadin.ui.Button;
  54. import com.vaadin.ui.Button.ClickListener;
  55. import com.vaadin.ui.ComponentContainer;
  56. import com.vaadin.ui.FormLayout;
  57. import com.vaadin.ui.Panel;
  58. import com.vaadin.ui.PasswordField;
  59. import com.vaadin.ui.TextField;
  60. import com.vaadin.ui.VerticalLayout;

  61. /**
  62.  * The Class FormFactoryImpl.
  63.  */
  64. @Service
  65. public final class FormFactoryImpl implements FormFactory {

  66.     /** The Constant HIDDEN_FIELD_NAME. */
  67.     private static final String HIDDEN_FIELD_NAME = "password";

  68.     /** The Constant LOGGER. */
  69.     private static final Logger LOGGER = LoggerFactory.getLogger(FormFactoryImpl.class);

  70.     /** The Constant SIZE_FOR_GRID. */
  71.     private static final int SIZE_FOR_GRID = 8;

  72.     /** The Constant TYPE_CONVERTERS. */
  73.     private static final Map<Class<?>, Converter<String, ?>> TYPE_CONVERTERS = new HashMap<>();

  74.     static {
  75.         TYPE_CONVERTERS.put(Date.class, new StringToDateConverter());
  76.         TYPE_CONVERTERS.put(Integer.class, new StringToIntegerConverter("Input value should be an integer"));
  77.         TYPE_CONVERTERS.put(Long.class, new StringToLongConverter("Input value should be a long"));
  78.         TYPE_CONVERTERS.put(BigInteger.class, new StringToBigIntegerConverter("Input value should be a biginteger"));
  79.         TYPE_CONVERTERS.put(BigDecimal.class, new StringToBigDecimalConverter("Input value should be a bigdecimal"));
  80.         TYPE_CONVERTERS.put(Boolean.class, new StringToBooleanConverter("Input value should be a boolean"));

  81.         // Map primitive types to their wrapper classes
  82.         TYPE_CONVERTERS.put(int.class, TYPE_CONVERTERS.get(Integer.class));
  83.         TYPE_CONVERTERS.put(long.class, TYPE_CONVERTERS.get(Long.class));
  84.         TYPE_CONVERTERS.put(boolean.class, TYPE_CONVERTERS.get(Boolean.class));
  85.     }

  86.     /**
  87.      * Default constructor for FormFactoryImpl.
  88.      */
  89.     public FormFactoryImpl() {
  90.         // Default constructor
  91.     }

  92.     /**
  93.      * Creates the display property converters.
  94.      *
  95.      * @param                     <T> the generic type
  96.      * @param displayProperties   the display properties
  97.      * @param formContent         the form content
  98.      * @param binder              the binder
  99.      * @param propertyDescriptors the property descriptors
  100.      */
  101.     @SuppressWarnings({ "unchecked", "rawtypes" })
  102.     private static <T extends Serializable> void createDisplayPropertyConverters(final List<String> displayProperties,
  103.             final ComponentContainer formContent, final Binder<T> binder, final PropertyDescriptor[] propertyDescriptors) {
  104.         for (final String property : displayProperties) {
  105.             final Class<?> typeOfProperty = getTypeOfProperty(propertyDescriptors, property);

  106.             if (typeOfProperty != null) {
  107.                 final AbstractField<?> field = new TextField();
  108.                 field.setReadOnly(true);
  109.                 field.setCaption(property);
  110.                 field.setWidth(ContentSize.FULL_SIZE);
  111.                 formContent.addComponent(field);
  112.                 final Converter converter = getConverterForType(typeOfProperty);

  113.                 if (converter != null) {
  114.                     binder.forField(field).withConverter(converter).bind(property);
  115.                 } else {
  116.                     binder.forField(field).bind(property);
  117.                 }
  118.             }
  119.         }
  120.     }


  121.     /**
  122.      * Creates the field.
  123.      *
  124.      * @param property the property
  125.      * @return the abstract field
  126.      */
  127.     private static AbstractField<?> createField(final String property) {
  128.         if (StringUtils.containsIgnoreCase(property,HIDDEN_FIELD_NAME)) {
  129.             return new PasswordField();
  130.         } else {
  131.             return new TextField();
  132.         }
  133.     }


  134.     /**
  135.      * Gets the converter for type.
  136.      *
  137.      * @param typeOfProperty
  138.      *            the type of property
  139.      * @return the converter for type
  140.      */
  141.     @SuppressWarnings({ "unchecked", "rawtypes" })
  142.     private static Converter<String, ?> getConverterForType(final Class<?> typeOfProperty) {
  143.         if (typeOfProperty == null) {
  144.             return null;
  145.         }

  146.         // Handle enums separately since they're dynamic
  147.         if (typeOfProperty.isEnum()) {
  148.             return new StringToEnumConverter();
  149.         }

  150.         // Look up converter from map
  151.         return TYPE_CONVERTERS.get(typeOfProperty);
  152.     }

  153.     /**
  154.      * Gets the type of property.
  155.      *
  156.      * @param propertyDescriptors
  157.      *            the property descriptors
  158.      * @param property
  159.      *            the property
  160.      * @return the type of property
  161.      */
  162.     private static Class<?> getTypeOfProperty(final PropertyDescriptor[] propertyDescriptors, final String property) {

  163.         for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  164.             if (propertyDescriptor.getName().equalsIgnoreCase(property)) {
  165.                 return propertyDescriptor.getPropertyType();
  166.             }
  167.         }
  168.         return null;
  169.     }

  170.     @Override
  171.     public <T extends Serializable> void addFormPanelTextFields(final AbstractOrderedLayout panelContent, final T item,
  172.             final Class<T> beanType, final List<String> displayProperties) {


  173.         final Panel formPanel = new Panel();
  174.         formPanel.setSizeFull();


  175.         panelContent.addComponent(formPanel);
  176.         if (displayProperties.size() > SIZE_FOR_GRID) {
  177.             panelContent.setExpandRatio(formPanel, ContentRatio.GRID);
  178.         }
  179.         else {
  180.             panelContent.setExpandRatio(formPanel, ContentRatio.SMALL_GRID);
  181.         }


  182.         final FormLayout formContent = new FormLayout();
  183.         formPanel.setContent(formContent);
  184.         formContent.setWidth(ContentSize.FULL_SIZE);

  185.         final Binder<T> binder = new Binder<>(beanType);
  186.         binder.setBean(item);
  187.         binder.setReadOnly(true);

  188.         PropertyDescriptor[] propertyDescriptors=null;
  189.         try {
  190.             final BeanInfo info = Introspector.getBeanInfo(item.getClass());
  191.             propertyDescriptors = info.getPropertyDescriptors();
  192.         } catch (final IntrospectionException exception) {
  193.             LOGGER.error("No able to getfieldtypes for type:+ item.getClass()", exception);
  194.         }

  195.         createDisplayPropertyConverters(displayProperties, formContent, binder, propertyDescriptors);
  196.     }

  197.     @SuppressWarnings({ "unchecked", "rawtypes" })
  198.     @Override
  199.     public <T extends Serializable> void addRequestInputFormFields(final FormLayout panelContent, final T item,
  200.             final Class<T> beanType, final List<String> displayProperties,final String buttonLabel,final ClickListener buttonListener) {
  201.         final BeanValidationBinder<T> binder = new BeanValidationBinder<>(beanType);
  202.         binder.setBean(item);
  203.         binder.setReadOnly(true);

  204.         for (final String property : displayProperties) {

  205.             final AbstractField buildAndBind = createField(property);
  206.             binder.bind(buildAndBind,property);
  207.             buildAndBind.setCaption(property);
  208.             buildAndBind.setId(MessageFormat.format("{0}.{1}", buttonLabel, property));
  209.             buildAndBind.setReadOnly(false);
  210.             buildAndBind.setWidth(ContentSize.HALF_SIZE);

  211.             panelContent.addComponent(buildAndBind);
  212.         }

  213.         final VerticalLayout verticalLayout = new VerticalLayout();
  214.         verticalLayout.setWidth("50%");

  215.         final Button button = new Button(buttonLabel,new CommitFormWrapperClickListener(binder,buttonListener));
  216.         button.setId(buttonLabel);
  217.         button.setWidth("100%");
  218.         button.setIcon(VaadinIcons.BULLSEYE);
  219.         button.setEnabled(false);
  220.         binder.addStatusChangeListener(event -> button.setEnabled(event.getBinder().isValid()));


  221.         verticalLayout.addComponent(button);
  222.         verticalLayout.setComponentAlignment(button, Alignment.MIDDLE_RIGHT);

  223.         panelContent.addComponent(verticalLayout);
  224.     }

  225. }