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

com.ocs.dynamo.ui.composite.grid.PivotGridWrapper Maven / Gradle / Ivy

/*
   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.ocs.dynamo.ui.composite.grid;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import com.ocs.dynamo.dao.FetchJoinInformation;
import com.ocs.dynamo.domain.AbstractEntity;
import com.ocs.dynamo.domain.model.EntityModel;
import com.ocs.dynamo.service.BaseService;
import com.ocs.dynamo.ui.composite.ComponentContext;
import com.ocs.dynamo.ui.composite.export.PivotParameters;
import com.ocs.dynamo.ui.composite.layout.FormOptions;
import com.ocs.dynamo.ui.provider.BaseDataProvider;
import com.ocs.dynamo.ui.provider.IdBasedDataProvider;
import com.ocs.dynamo.ui.provider.PagingDataProvider;
import com.ocs.dynamo.ui.provider.PivotAggregationType;
import com.ocs.dynamo.ui.provider.PivotDataProvider;
import com.ocs.dynamo.ui.provider.PivotedItem;
import com.ocs.dynamo.ui.provider.QueryType;
import com.ocs.dynamo.ui.utils.VaadinUtils;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid.SelectionMode;
import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.grid.contextmenu.GridContextMenu;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.data.provider.SortOrder;
import com.vaadin.flow.function.SerializablePredicate;

import lombok.Getter;
import lombok.Setter;

/**
 * Wrapper around a pivot grid
 * 
 * @author Bas Rutten
 *
 * @param 
 * @param 
 */
public class PivotGridWrapper>
		extends GridWrapper {

	private static final long serialVersionUID = -4691108261565306844L;

	@Getter
	@Setter
	private Map> aggregationClassMap = new HashMap<>();

	@Getter
	@Setter
	private Map aggregationMap = new HashMap<>();

	/**
	 * The label that displays the table caption
	 */
	private Span caption = new Span("");

	/**
	 * The name of the property that contains the values that lead to the pivoted
	 * columns
	 */
	@Getter
	@Setter
	private String columnKeyProperty;

	@Getter
	@Setter
	private BiFunction customFormatter = null;

	/**
	 * The data provider
	 */
	private PivotDataProvider dataProvider;

	/**
	 * Bifunction used to map pivot column headers for export only
	 */
	@Getter
	@Setter
	private BiFunction exportHeaderMapper;

	/**
	 * Bifunction used to map pivot column subheaders for export only
	 */
	@Getter
	@Setter
	private BiFunction exportSubHeaderMapper;

	/**
	 * The names of the fixed/frozen columns
	 */
	@Getter
	@Setter
	private List fixedColumnKeys;

	/**
	 * Function for mapping for fixed property name to grid header
	 */
	@Getter
	@Setter
	private Function fixedHeaderMapper = Function.identity();

	/**
	 * The wrapped grid component
	 */
	private PivotGrid grid;

	/**
	 * Bifunction used to map pivot column headers
	 */
	@Getter
	@Setter
	private BiFunction headerMapper = (a, b) -> a.toString();

	/**
	 * The properties to display in the pivoted columns
	 */
	@Getter
	@Setter
	private List hiddenPivotedProperties;

	/**
	 * Whether to include an aggregate row at the bottom
	 */
	@Getter
	@Setter
	private boolean includeAggregateRow;

	/**
	 * The layout that contains the grid
	 */
	private VerticalLayout layout;

	/**
	 * The properties to display in the pivoted columns
	 */
	@Getter
	@Setter
	private List pivotedProperties;

	/**
	 * The possible values of the columnPropertyKey property.
	 */
	@Getter
	@Setter
	private List possibleColumnKeys;

	/**
	 * The property that is checked to determine whether a new row is reached
	 */
	@Getter
	@Setter
	private String rowKeyProperty;

	/**
	 * Supplier that is used to determine the number of rows in the pivot table
	 */
	@Getter
	@Setter
	private Supplier sizeSupplier;

	/**
	 * Mapping function for determining the sub header from the column key value and
	 * pivot property
	 */
	@Getter
	@Setter
	private BiFunction subHeaderMapper = (a, b) -> b.toString();

	/**
	 * The wrapped data provider
	 */
	private BaseDataProvider wrappedProvider;

	/**
	 * @param service     the service that is used for retrieving data
	 * @param entityModel the entity model
	 * @param queryType   the query type to use
	 * @param order       the default sort order
	 * @param joins       options list of fetch joins to include in the query
	 */
	public PivotGridWrapper(BaseService service, EntityModel entityModel, QueryType queryType,
			FormOptions formOptions, ComponentContext componentContext, SerializablePredicate filter,
			List> sortOrders, FetchJoinInformation... joins) {
		super(service, entityModel, queryType, formOptions, componentContext, filter, sortOrders, joins);
	}

	/**
	 * Builds the component.
	 */
	@Override
	public void build() {
		layout = new VerticalLayout();
		layout.add(caption);

		this.dataProvider = constructDataProvider();
		grid = getGrid();
		layout.add(grid);
		initSortingAndFiltering();

		grid.setSelectionMode(SelectionMode.SINGLE);
		add(layout);
	}

	protected PivotDataProvider constructDataProvider() {

		if (QueryType.PAGING.equals(getQueryType())) {
			wrappedProvider = new PagingDataProvider<>(getService(), getEntityModel(),
					getFormOptions().isShowNextButton() || getFormOptions().isShowPrevButton(), getJoins());
		} else {
			wrappedProvider = new IdBasedDataProvider<>(getService(), getEntityModel(), getJoins());
		}

		PivotDataProvider pivotDataProvider = new PivotDataProvider<>(wrappedProvider, rowKeyProperty,
				columnKeyProperty, fixedColumnKeys, pivotedProperties, hiddenPivotedProperties, sizeSupplier);
		pivotDataProvider.setAggregationMap(aggregationMap);
		pivotDataProvider.setAggregationClassMap(aggregationClassMap);
		pivotDataProvider.setAfterCountCompleted(count -> updateCaption(count));
		postProcessDataProvider(pivotDataProvider);
		return pivotDataProvider;
	}

	/**
	 * Constructs the grid - override in subclasses if you need a different grid
	 * implementation
	 * 
	 * @return
	 */
	protected PivotGrid constructGrid() {
		return new PivotGrid<>(dataProvider, possibleColumnKeys, fixedHeaderMapper, headerMapper, subHeaderMapper,
				customFormatter);
	}

	public DataProvider> getDataProvider() {
		return dataProvider;
	}

	@Override
	public int getDataProviderSize() {
		return dataProvider.getSize();
	}

	/**
	 * Lazily construct and return the grid
	 * 
	 * @return
	 */
	public PivotGrid getGrid() {
		if (grid == null) {
			grid = constructGrid();
		}
		return grid;
	}

	/**
	 * Extracts the sort directions from the sort orders
	 */
	protected boolean[] getSortDirections() {
		boolean[] result = new boolean[getSortOrders().size()];
		for (int i = 0; i < result.length; i++) {
			result[i] = SortDirection.ASCENDING == getSortOrders().get(i).getDirection();
		}
		return result;
	}

	/**
	 * Initializes the sorting and filtering for the grid
	 */
	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected List> initSortingAndFiltering() {

		// pass along initial filter
		getGrid().getDataCommunicator().setDataProvider(getDataProvider(), (SerializablePredicate) getFilter());

		List> fallbackOrders = super.initSortingAndFiltering();

		// set fall back sort orders
		if (wrappedProvider instanceof BaseDataProvider) {
			wrappedProvider.setFallbackSortOrders(fallbackOrders);
		}

		if (getFormOptions().isExportAllowed() && getExportDelegate() != null) {

			GridContextMenu contextMenu = getGrid().addContextMenu();
			Button downloadButton = new Button(message("ocs.download"));
			downloadButton.addClickListener(event -> {
				List> orders = new ArrayList<>();
				List> so = getGrid().getSortOrder();
				for (GridSortOrder gso : so) {
					orders.add(new SortOrder(gso.getSorted().getKey(), gso.getDirection()));
				}

				PivotParameters pars = PivotParameters.builder() //
						.columnKeyProperty(columnKeyProperty) //
						.fixedColumnKeys(fixedColumnKeys) //
						.fixedHeaderMapper(fixedHeaderMapper) //
						.pivotedProperties(pivotedProperties) //
						.possibleColumnKeys(possibleColumnKeys) //
						.rowKeyProperty(rowKeyProperty) //
						.hiddenPivotedProperties(hiddenPivotedProperties) //
						.aggregationMap(aggregationMap) //
						.aggregationClassMap(aggregationClassMap) //
						.includeAggregateRow(includeAggregateRow) //
						.subHeaderMapper(subHeaderMapper) //
						.headerMapper(exportHeaderMapper != null ? exportHeaderMapper : headerMapper) //
						.subHeaderMapper(exportSubHeaderMapper != null ? exportSubHeaderMapper : subHeaderMapper)
						.build();

				// use the fallback sort orders here
				getExportDelegate().exportPivoted(
						getExportEntityModel() != null ? getExportEntityModel() : getEntityModel(), getFilter(),
						fallbackOrders, pars, getExportJoins() != null ? getExportJoins() : getJoins());

			});
			contextMenu.add(downloadButton);
		}

		return fallbackOrders;
	}

	/**
	 * Respond to a selection of an item in the grid
	 */
	protected void onSelect(Object selected) {
		// overwrite in subclasses
	}

	/**
	 * Callback method used to modify data provider creation
	 * 
	 * @param container
	 */
	protected void postProcessDataProvider(PivotDataProvider provider) {
		// overwrite in subclasses
	}

	@Override
	public void reloadDataProvider() {
		// not needed
	}

	/**
	 * Updates the caption above the grid that shows the number of items
	 * 
	 * @param size the number of items
	 */
	protected void updateCaption(int size) {
		caption.setText(getEntityModel().getDisplayNamePlural(VaadinUtils.getLocale()) + " "
				+ getMessageService().getMessage("ocs.showing.results", VaadinUtils.getLocale(), size));
	}

}