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.DataFilter;
39  import info.magnolia.ui.contentapp.FilterableDataProvider;
40  import info.magnolia.ui.contentapp.configuration.GridViewDefinition;
41  import info.magnolia.ui.contentapp.configuration.column.ColumnDefinition;
42  import info.magnolia.ui.contentapp.configuration.column.ColumnEditorDefinition;
43  import info.magnolia.ui.field.FieldDefinition;
44  import info.magnolia.ui.field.factory.FieldBinder;
45  import info.magnolia.ui.field.factory.FieldFactory;
46  import info.magnolia.ui.framework.FilterContext;
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.shared.ui.ContentMode;
58  import com.vaadin.ui.Component;
59  import com.vaadin.ui.DescriptionGenerator;
60  import com.vaadin.ui.Grid;
61  import com.vaadin.ui.components.grid.HeaderCell;
62  import com.vaadin.ui.components.grid.HeaderRow;
63  import com.vaadin.ui.renderers.AbstractRenderer;
64  
65  /**
66   * Grid configuration utility (column configuration0.
67   */
68  public class GridColumnConfigurer<T> {
69      private final GridViewDefinition<T> definition;
70      private final UiContextBoundComponentProvider componentProvider;
71      private final PropertySet<T> propertySet;
72      private final FilterContext filterContext;
73  
74      public GridColumnConfigurer(GridViewDefinition<T> definition,
75                                  UiContextBoundComponentProvider componentProvider,
76                                  PropertySet<T> propertySet,
77                                  FilterContext filterContext
78      ) {
79          this.definition = definition;
80          this.componentProvider = componentProvider;
81          this.propertySet = propertySet;
82          this.filterContext = filterContext;
83      }
84  
85      public void configureColumns(Grid<T> grid) {
86          grid.removeAllColumns();
87          definition.getColumns().forEach((columnDefinition) -> createTitleColumn(grid, columnDefinition));
88          if (definition.getColumns().stream()
89                  .map(ColumnDefinition::getFilterComponent)
90                  .anyMatch(Objects::nonNull)) {
91              if (!(grid.getDataProvider() instanceof FilterableDataProvider)) {
92                  throw new IllegalArgumentException(
93                          String.format("Filter component is defined for at least one column but grid's %s is not an instance of %s.", grid.getDataProvider(), FilterableDataProvider.class));
94              }
95              HeaderRow filterRow = grid.addHeaderRowAt(1);
96              definition.getColumns().stream()
97                      .filter(columnDefinition -> columnDefinition.getFilterComponent() != null)
98                      .forEach(columnDefinition -> createFilterColumn(filterRow, columnDefinition));
99              grid.setHeaderRowHeight(23);
100             grid.getDefaultHeaderRow().setStyleName("v-grid-first-row");
101             filterRow.setStyleName("v-grid-filter-row");
102 
103             filterContext.getFilter().observe(dataFilter -> {
104                 ((FilterableDataProvider) grid.getDataProvider()).setDataFilter(dataFilter.orElse(null));
105                 grid.getDataProvider().refreshAll();
106                 definition.getColumns().stream()
107                         .filter(columnDefinition -> columnDefinition.getFilterComponent() != null)
108                         .map(filterableColumnDefinition -> grid.getColumn(filterableColumnDefinition.getName()))
109                         .forEach(filterableColumn -> setColumnValue(filterRow, filterableColumn, dataFilter.orElse(null)));
110             });
111         }
112     }
113 
114     private void setColumnValue(HeaderRow filterRow, Grid.Column filterableColumn, DataFilter dataFilter) {
115         HeaderCell filterCell = filterRow.getCell(filterableColumn);
116         if (filterCell.getComponent() instanceof HasValue && filterCell.getCellType() == WIDGET) {
117             HasValue hasValue = (HasValue) filterCell.getComponent();
118             hasValue.setValue(dataFilter == null || dataFilter.getPropertyFilters().get(filterableColumn.getId()) == null ?
119                     hasValue.getEmptyValue() : dataFilter.getPropertyFilters().get(filterableColumn.getId()));
120         }
121     }
122 
123     @SuppressWarnings("unchecked")
124     private void createTitleColumn(Grid<T> grid, ColumnDefinition columnDefinition) {
125         AbstractRenderer renderer = componentProvider.newInstance((Class<AbstractRenderer>) columnDefinition.getRenderer());
126         ValueProvider valueProvider = propertySet.getProperty(columnDefinition.getName()).map(PropertyDefinition::getGetter).orElse(null);
127         if (columnDefinition.getValueProvider() != null) {
128             valueProvider = componentProvider.newInstance(
129                     (Class<ValueProvider>) columnDefinition.getValueProvider(),
130                     columnDefinition,
131                     valueProvider);
132         }
133 
134         final Grid.Column column = grid.addColumn(valueProvider, renderer);
135         column.setId(columnDefinition.getName());
136         if (columnDefinition.getLargeContentColumnWidth() != null) {
137             column.setMinimumWidthFromContent(false);
138             column.setMinimumWidth(columnDefinition.getLargeContentColumnWidth());
139         }
140         column.setCaption(columnDefinition.getCaption());
141 
142         if (!definition.isReadOnly() && columnDefinition.isEditable()) {
143             final ColumnEditorDefinition<T> editor = columnDefinition.getEditor();
144             Optional.ofNullable(editor).map(ColumnEditorDefinition::getField).ifPresent(field -> {
145                 UiContextBoundComponentProvider childComponentProvider = this.componentProvider.inChildContext(columnDefinition.getName() + "editor", field);
146                 FieldFactory fieldFactory = childComponentProvider.newInstance(field.getFactoryClass());
147                 Binder.BindingBuilder bindingBuilder = grid.getEditor().getBinder().forField(fieldFactory.createField());
148 
149                 FieldBinder<T> fieldBinder = childComponentProvider.newInstance(field.getFieldBinderClass());
150                 bindingBuilder = fieldBinder.<T>configureBinding(field, bindingBuilder);
151 
152                 column.setEditorBinding(bindingBuilder.bind(columnDefinition.getName()));
153             });
154         }
155 
156         Optional.ofNullable(columnDefinition.getDescriptionGenerator())
157                 .map(componentProvider::newInstance)
158                 .ifPresent(descriptionGenerator -> column.setDescriptionGenerator((DescriptionGenerator) descriptionGenerator, ContentMode.HTML));
159     }
160 
161     private void createFilterColumn(HeaderRow filterRow, ColumnDefinition columnDefinition) {
162         FieldDefinition<T> filterComponent = columnDefinition.getFilterComponent();
163         UiContextBoundComponentProvider childComponentProvider = this.componentProvider.inChildContext(columnDefinition.getName() + "filter", filterComponent);
164 
165         FieldFactory factory = childComponentProvider.newInstance(filterComponent.getFactoryClass());
166         HasValue<T> field = (HasValue<T>) factory.createField();
167 
168         field.addValueChangeListener(e -> {
169             DataFiltDataFilteraFilter = filterContext.getFilter().value().orElse(new DataFilter());
170             dataFilter.filter(columnDefinition.getName(), field.getValue());
171             filterContext.getFilter().set(dataFilter, true);
172         });
173         filterRow.getCell(columnDefinition.getName()).setComponent((Component) field);
174     }
175 }