View Javadoc
1   /**
2    * This file Copyright (c) 2019 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.contentapp.browser;
35  
36  import static com.vaadin.shared.ui.grid.GridStaticCellType.WIDGET;
37  
38  import info.magnolia.ui.contentapp.configuration.GridViewDefinition;
39  import info.magnolia.ui.contentapp.configuration.column.ColumnDefinition;
40  import info.magnolia.ui.contentapp.configuration.column.ColumnEditorDefinition;
41  import info.magnolia.ui.field.FieldBinder;
42  import info.magnolia.ui.field.FieldDefinition;
43  import info.magnolia.ui.field.FieldFactory;
44  import info.magnolia.ui.filter.DataFilter;
45  import info.magnolia.ui.filter.FilterContext;
46  import info.magnolia.ui.framework.ioc.UiComponentProvider;
47  import info.magnolia.ui.framework.ioc.UiContextBoundComponentProvider;
48  
49  import java.util.Objects;
50  import java.util.Optional;
51  
52  import com.vaadin.data.Binder;
53  import com.vaadin.data.HasValue;
54  import com.vaadin.data.PropertyDefinition;
55  import com.vaadin.data.PropertySet;
56  import com.vaadin.data.ValueProvider;
57  import com.vaadin.data.provider.ConfigurableFilterDataProvider;
58  import com.vaadin.shared.ui.ContentMode;
59  import com.vaadin.ui.Component;
60  import com.vaadin.ui.DescriptionGenerator;
61  import com.vaadin.ui.Grid;
62  import com.vaadin.ui.components.grid.HeaderCell;
63  import com.vaadin.ui.components.grid.HeaderRow;
64  import com.vaadin.ui.renderers.AbstractRenderer;
65  
66  /**
67   * Grid configuration utility (column configuration0.
68   */
69  public class GridColumnConfigurer<T> {
70      private final GridViewDefinition<T> definition;
71      private final UiContextBoundComponentProvider componentProvider;
72      private final PropertySet<T> propertySet;
73      private final FilterContext filterContext;
74  
75      public GridColumnConfigurer(GridViewDefinition<T> definition,
76                                  UiContextBoundComponentProvider componentProvider,
77                                  PropertySet<T> propertySet,
78                                  FilterContext filterContext
79      ) {
80          this.definition = definition;
81          this.componentProvider = componentProvider;
82          this.propertySet = propertySet;
83          this.filterContext = filterContext;
84      }
85  
86      public void configureColumns(Grid<T> grid) {
87          grid.removeAllColumns();
88          definition.getColumns().forEach((columnDefinition) -> createTitleColumn(grid, columnDefinition));
89          if (definition.getColumns().stream()
90                  .map(ColumnDefinition::getFilterComponent)
91                  .anyMatch(Objects::nonNull)) {
92              if (!(grid.getDataProvider() instanceof ConfigurableFilterDataProvider)) {
93                  throw new IllegalArgumentException(
94                          String.format("Filter component is defined for at least one column but grid's %s is not an instance of %s.", grid.getDataProvider(), ConfigurableFilterDataProvider.class));
95              }
96              HeaderRow filterRow = grid.addHeaderRowAt(1);
97              definition.getColumns().stream()
98                      .filter(columnDefinition -> columnDefinition.getFilterComponent() != null)
99                      .forEach(columnDefinition -> createFilterColumn(filterRow, columnDefinition));
100             grid.setHeaderRowHeight(23);
101             grid.getDefaultHeaderRow().setStyleName("v-grid-first-row");
102             filterRow.setStyleName("v-grid-filter-row");
103 
104             filterContext.getFilter().observe(dataFilter -> {
105                 ((ConfigurableFilterDataProvider) grid.getDataProvider()).setFilter(dataFilter.orElse(null));
106                 grid.getDataProvider().refreshAll();
107                 definition.getColumns().stream()
108                         .filter(columnDefinition -> columnDefinition.getFilterComponent() != null)
109                         .map(filterableColumnDefinition -> grid.getColumn(filterableColumnDefinition.getName()))
110                         .forEach(filterableColumn -> setColumnValue(filterRow, filterableColumn, dataFilter.orElse(null)));
111             });
112         }
113     }
114 
115     private void setColumnValue(HeaderRow filterRow, Grid.Column filterableColumn, DataFilter dataFilter) {
116         HeaderCell filterCell = filterRow.getCell(filterableColumn);
117         if (filterCell.getComponent() instanceof HasValue && filterCell.getCellType() == WIDGET) {
118             HasValue hasValue = (HasValue) filterCell.getComponent();
119             hasValue.setValue(dataFilter == null || dataFilter.getPropertyFilters().get(filterableColumn.getId()) == null ?
120                     hasValue.getEmptyValue() : dataFilter.getPropertyFilters().get(filterableColumn.getId()));
121         }
122     }
123 
124     @SuppressWarnings("unchecked")
125     private void createTitleColumn(Grid<T> grid, ColumnDefinition columnDefinition) {
126         AbstractRenderer renderer = componentProvider.newInstance((Class<AbstractRenderer>) columnDefinition.getRenderer());
127         ValueProvider valueProvider = propertySet.getProperty(columnDefinition.getName()).map(PropertyDefinition::getGetter).orElse(null);
128         if (columnDefinition.getValueProvider() != null) {
129             valueProvider = componentProvider.newInstance(
130                     (Class<ValueProvider>) columnDefinition.getValueProvider(),
131                     columnDefinition,
132                     valueProvider);
133         }
134 
135         final Grid.Column column = grid.addColumn(valueProvider, renderer);
136         column.setId(columnDefinition.getName());
137         column.setMinimumWidthFromContent(columnDefinition.isMinimumWidthFromContent());
138         if (columnDefinition.getWidth() > 0) {
139             column.setWidth(columnDefinition.getWidth());
140         }
141         if (columnDefinition.getExpandRatio() > 0) {
142             column.setExpandRatio(columnDefinition.getExpandRatio());
143         }
144         if (columnDefinition.getMinimumWidth() > 0) {
145             column.setMinimumWidth(columnDefinition.getMinimumWidth());
146         }
147         if (columnDefinition.getMaximumWidth() > 0) {
148             column.setMaximumWidth(columnDefinition.getMaximumWidth());
149         }
150         column.setCaption(columnDefinition.getLabel());
151 
152         if (!definition.isReadOnly() && columnDefinition.isEditable()) {
153             final ColumnEditorDefinition<T> editor = columnDefinition.getEditor();
154             Optional.ofNullable(editor).map(ColumnEditorDefinition::getField).ifPresent(field -> {
155                 UiComponentProvider childComponentProvider = this.componentProvider.inChildContext(columnDefinition.getName() + "editor", field);
156                 FieldFactory fieldFactory = childComponentProvider.newInstance(field.getFactoryClass());
157                 Binder.BindingBuilder bindingBuilder = grid.getEditor().getBinder().forField(fieldFactory.createField());
158 
159                 FieldBinder<T> fieldBinder = childComponentProvider.newInstance(field.getFieldBinderClass());
160                 bindingBuilder = fieldBinder.<T>configureBinding(field, bindingBuilder);
161 
162                 column.setEditorBinding(bindingBuilder.bind(columnDefinition.getName()));
163             });
164         }
165 
166         Optional.ofNullable(columnDefinition.getDescriptionGenerator())
167                 .map(componentProvider::newInstance)
168                 .ifPresent(descriptionGenerator -> column.setDescriptionGenerator((DescriptionGenerator) descriptionGenerator, ContentMode.HTML));
169     }
170 
171     private void createFilterColumn(HeaderRow filterRow, ColumnDefinition columnDefinition) {
172         FieldDefinition<T> filterComponent = columnDefinition.getFilterComponent();
173         UiComponentProvider childComponentProvider = this.componentProvider.inChildContext(columnDefinition.getName() + "filter", filterComponent);
174 
175         FieldFactory factory = childComponentProvider.newInstance(filterComponent.getFactoryClass());
176         HasValue<T> field = (HasValue<T>) factory.createField();
177 
178         field.addValueChangeListener(e -> {
179             DataFilterDataFilterter = filterContext.getFilter().value().orElse(new DataFilter());
180             dataFilter.filter(columnDefinition.getName(), field.getValue());
181             filterContext.getFilter().set(dataFilter, true);
182         });
183         filterRow.getCell(columnDefinition.getName()).setComponent((Component) field);
184     }
185 }