DataTableManager.java
- /*
- * Copyright (c) 2002-2022, City of Paris
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice
- * and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice
- * and the following disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * License 1.0
- */
- package fr.paris.lutece.util.datatable;
- import java.io.Serializable;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import javax.servlet.http.HttpServletRequest;
- import org.apache.commons.beanutils.BeanUtilsBean;
- import org.apache.commons.lang3.StringUtils;
- import fr.paris.lutece.portal.service.util.AppLogService;
- import fr.paris.lutece.portal.web.constants.Parameters;
- import fr.paris.lutece.portal.web.l10n.LocaleService;
- import fr.paris.lutece.portal.web.util.LocalizedDelegatePaginator;
- import fr.paris.lutece.portal.web.util.LocalizedPaginator;
- import fr.paris.lutece.util.ReferenceList;
- import fr.paris.lutece.util.UniqueIDGenerator;
- import fr.paris.lutece.util.html.AbstractPaginator;
- import fr.paris.lutece.util.html.IPaginator;
- import fr.paris.lutece.util.sort.AttributeComparator;
- import fr.paris.lutece.util.url.UrlItem;
- /**
- * Class to manage data tables with freemarker macros
- *
- * @param <T>
- * Type of data to display
- */
- public class DataTableManager<T> implements Serializable
- {
- /**
- * Serial version UID
- */
- private static final long serialVersionUID = -3906455886374172029L;
- private static final String CONSTANT_GET = "get";
- private static final String CONSTANT_IS = "is";
- private static final String CONSTANT_DATA_TABLE_MANAGER_ID_PREFIX = "dataTableManager";
- private String _strSortUrl;
- private List<DataTableColumn> _listColumn = new ArrayList<>( );
- private FilterPanel _filterPanel;
- private IPaginator<T> _paginator;
- private String _strCurrentPageIndex = StringUtils.EMPTY;
- private int _nItemsPerPage;
- private int _nDefautlItemsPerPage;
- private boolean _bEnablePaginator;
- private Locale _locale;
- private String _strSortedAttributeName;
- private boolean _bIsAscSort;
- private String _strUid;
- /**
- * Private constructor
- */
- protected DataTableManager( )
- {
- generateDataTableId( );
- }
- /**
- * Constructor of the DataTableManager class
- *
- * @param strSortUrl
- * URL used by the paginator to sort data
- * @param strFilterUrl
- * URL used to filter data
- * @param nDefautlItemsPerPage
- * Default number of items to display per page
- * @param bEnablePaginator
- * True to enable pagination, false to disable it
- */
- public DataTableManager( String strSortUrl, String strFilterUrl, int nDefautlItemsPerPage, boolean bEnablePaginator )
- {
- _filterPanel = new FilterPanel( strFilterUrl );
- _nDefautlItemsPerPage = nDefautlItemsPerPage;
- _nItemsPerPage = _nDefautlItemsPerPage;
- _strCurrentPageIndex = "1";
- _bEnablePaginator = bEnablePaginator;
- generateDataTableId( );
- setSortUrl( strSortUrl );
- }
- /**
- * Add a column to this DataTableManager
- *
- * @param strColumnTitle
- * I18n key of the title of the column
- * @param strObjectName
- * Name of the property of objects that should be displayed in this column.<br>
- * For example, if a class "Data" contains a property named "title", then the value of the parameter <i>strObjectName</i> should be "title".
- * @param bSortable
- * True if the column is sortable, false otherwise
- */
- public void addColumn( String strColumnTitle, String strObjectName, boolean bSortable )
- {
- _listColumn.add( new DataTableColumn( strColumnTitle, strObjectName, bSortable, DataTableColumnType.STRING ) );
- }
- /**
- * Add a label column to this DataTableManager. Values of cells of this column will be interpreted as i18n keys.
- *
- * @param strColumnTitle
- * I18n key of the title of the column
- * @param strObjectName
- * Name of the property of objects that should be displayed in this column. This properties must be i18n keys.<br>
- * For example, if a class "Data" contains a property named "title", then the value of the parameter <i>strObjectName</i> should be "title".
- * @param bSortable
- * True if the column is sortable, false otherwise
- */
- public void addLabelColumn( String strColumnTitle, String strObjectName, boolean bSortable )
- {
- _listColumn.add( new DataTableColumn( strColumnTitle, strObjectName, bSortable, DataTableColumnType.LABEL ) );
- }
- /**
- * Add an column to this DataTableManager that will display actions on items. Actions are usually parameterized links. A DataTableManager can only have 1
- * action column. The content of the action column must be generated by a macro.this macro must have one parameter named "item", and its name must be given
- * to the macro <i>@tableData</i>.
- *
- * @param strColumnTitle
- * I18n key of the title of the column
- */
- public void addActionColumn( String strColumnTitle )
- {
- _listColumn.add( new DataTableColumn( strColumnTitle, null, false, DataTableColumnType.ACTION ) );
- }
- /**
- * Add a column to this DataTableManager
- *
- * @param strColumnTitle
- * I18n key of the title of the column
- * @param strObjectName
- * Name of the property of objects that should be displayed in this column.<br>
- * For example, if a class "Data" contains a property named "title", then the value of the parameter <i>strObjectName</i> should be "title".
- * @param strLabelTrue
- * I18n key of the label to display when the value is true
- * @param strLabelFalse
- * I18n key of the label to display when the value is false
- */
- public void addBooleanColumn( String strColumnTitle, String strObjectName, String strLabelTrue, String strLabelFalse )
- {
- _listColumn.add( new DataTableColumn( strColumnTitle, strObjectName, false, DataTableColumnType.BOOLEAN, strLabelTrue, strLabelFalse ) );
- }
- /**
- * Add a free column to this DataTableManager. The content of this column must be generated by a macro. The macro must have one parameter named "item".
- *
- * @param strColumnTitle
- * I18n key of the title of the column
- * @param strFreemarkerMacroName
- * Name of the freemarker macro that will display the content of the column.<br>
- * The macro must have a single parameter named <i>item</i> of type T that will contain the object associated with a row of the table.
- */
- public void addFreeColumn( String strColumnTitle, String strFreemarkerMacroName )
- {
- _listColumn.add( new DataTableColumn( strColumnTitle, strFreemarkerMacroName, false, DataTableColumnType.ACTION ) );
- }
- /**
- * Add an email column to this DataTableManager. Displayed cell will be a "mailto:" link.
- *
- * @param strColumnTitle
- * I18n key of the title of the column
- * @param strObjectName
- * Name of the property of objects that should be displayed in this column.<br>
- * For example, if a class "Data" contains a property named "title", then the value of the parameter <i>strObjectName</i> should be "title".
- * @param bSortable
- * True if the column is sortable, false otherwise
- */
- public void addEmailColumn( String strColumnTitle, String strObjectName, boolean bSortable )
- {
- _listColumn.add( new DataTableColumn( strColumnTitle, strObjectName, bSortable, DataTableColumnType.EMAIL ) );
- }
- /**
- * Add a filter to the filter panel of this DataTableManager
- *
- * @param filterType
- * data type of the filter. For drop down list, use {@link DataTableManager#addDropDownListFilter(String, String, ReferenceList)
- * addDropDownListFilter} instead
- * @param strParameterName
- * Name of the parameter of the object to filter.<br>
- * For example, if this filter should be applied on the parameter "title" of a class named "Data", then the value of the parameter
- * <i>strParameterName</i> should be "title".
- * @param strFilterLabel
- * Label describing the filter
- */
- public void addFilter( DataTableFilterType filterType, String strParameterName, String strFilterLabel )
- {
- _filterPanel.addFilter( filterType, strParameterName, strFilterLabel );
- }
- /**
- * Add a drop down list filter to the filter panel of this DataTableManager
- *
- * @param strParameterName
- * Name of the parameter of the object to filter.<br>
- * For example, if this filter should be applied on the parameter "title" of a class named "Data", then the value of the parameter
- * <i>strParameterName</i> should be "title".
- * @param strFilterLabel
- * Label describing the filter
- * @param refList
- * Reference list containing data of the drop down list
- */
- public void addDropDownListFilter( String strParameterName, String strFilterLabel, ReferenceList refList )
- {
- _filterPanel.addDropDownListFilter( strParameterName, strFilterLabel, refList );
- }
- /**
- * Apply filters on an objects list, sort it and update pagination values.
- *
- * @param request
- * The request
- * @param items
- * Collection of objects to filter, sort and paginate
- */
- public void filterSortAndPaginate( HttpServletRequest request, List<T> items )
- {
- List<T> filteredSortedPaginatedItems = new ArrayList<>( items );
- boolean bSubmitedDataTable = hasDataTableFormBeenSubmited( request );
- // FILTER
- Collection<DataTableFilter> listFilters = _filterPanel.getListFilter( );
- boolean bUpdateFilter = false;
- boolean bResetFilter = false;
- // We check if filters must be updated or cleared
- if ( bSubmitedDataTable )
- {
- bResetFilter = StringUtils.equals( request.getParameter( FilterPanel.PARAM_FILTER_PANEL_PREFIX + FilterPanel.PARAM_RESET_FILTERS ),
- Boolean.TRUE.toString( ) );
- bUpdateFilter = true;
- if ( !bResetFilter )
- {
- bUpdateFilter = StringUtils.equals( request.getParameter( FilterPanel.PARAM_FILTER_PANEL_PREFIX + FilterPanel.PARAM_UPDATE_FILTERS ),
- Boolean.TRUE.toString( ) );
- }
- }
- for ( DataTableFilter filter : listFilters )
- {
- String strFilterValue;
- if ( bSubmitedDataTable && bUpdateFilter )
- {
- // We update or clear filters
- strFilterValue = request.getParameter( FilterPanel.PARAM_FILTER_PANEL_PREFIX + filter.getParameterName( ) );
- if ( !bResetFilter && ( filter.getFilterType( ) == DataTableFilterType.BOOLEAN ) && ( strFilterValue == null ) )
- {
- strFilterValue = Boolean.FALSE.toString( );
- }
- filter.setValue( strFilterValue );
- }
- else
- {
- strFilterValue = filter.getValue( );
- }
- if ( StringUtils.isNotBlank( strFilterValue ) )
- {
- List<T> bufferList = new ArrayList<>( );
- for ( T item : filteredSortedPaginatedItems )
- {
- Method method = getMethod( item, filter.getParameterName( ), CONSTANT_GET );
- if ( ( method == null ) && ( filter.getFilterType( ) == DataTableFilterType.BOOLEAN ) )
- {
- method = getMethod( item, filter.getParameterName( ), CONSTANT_IS );
- }
- if ( method != null )
- {
- try
- {
- Object value = method.invoke( item );
- if ( ( value != null ) && strFilterValue.equals( value.toString( ) ) )
- {
- bufferList.add( item );
- }
- }
- catch( Exception e )
- {
- AppLogService.error( e.getMessage( ), e );
- }
- }
- }
- filteredSortedPaginatedItems.retainAll( bufferList );
- }
- }
- // SORT
- if ( bSubmitedDataTable )
- {
- // We update the sort parameters
- String strSortedAttributeName = request.getParameter( Parameters.SORTED_ATTRIBUTE_NAME );
- if ( strSortedAttributeName != null )
- {
- // We update sort properties
- _strSortedAttributeName = strSortedAttributeName;
- _bIsAscSort = Boolean.parseBoolean( request.getParameter( Parameters.SORTED_ASC ) );
- }
- }
- // We sort the items
- if ( _strSortedAttributeName != null )
- {
- Collections.sort( filteredSortedPaginatedItems, new AttributeComparator( _strSortedAttributeName, _bIsAscSort ) );
- }
- // PAGINATION
- if ( bSubmitedDataTable )
- {
- // We update the pagination properties
- if ( _bEnablePaginator )
- {
- int nOldItemsPerPage = _nItemsPerPage;
- _nItemsPerPage = AbstractPaginator.getItemsPerPage( request, AbstractPaginator.PARAMETER_ITEMS_PER_PAGE, _nItemsPerPage,
- _nDefautlItemsPerPage );
- // If the number of items per page has changed, we switch to the first page
- if ( _nItemsPerPage != nOldItemsPerPage )
- {
- _strCurrentPageIndex = Integer.toString( 1 );
- }
- else
- {
- _strCurrentPageIndex = AbstractPaginator.getPageIndex( request, AbstractPaginator.PARAMETER_PAGE_INDEX, _strCurrentPageIndex );
- }
- }
- else
- {
- _strCurrentPageIndex = Integer.toString( 1 );
- _nItemsPerPage = filteredSortedPaginatedItems.size( );
- }
- }
- if ( request != null )
- {
- // We paginate create the new paginator
- _paginator = new LocalizedPaginator<>( filteredSortedPaginatedItems, _nItemsPerPage, getSortUrl( ), AbstractPaginator.PARAMETER_PAGE_INDEX,
- _strCurrentPageIndex, request.getLocale( ) );
- }
- }
- /**
- * Get the filter panel of the DataTableManager
- *
- * @return The filter panel of the DataTableManager
- */
- public FilterPanel getFilterPanel( )
- {
- return _filterPanel;
- }
- /**
- * Set the filter panel of the DataTableManager
- *
- * @param filterPanel
- * Filter panel
- */
- public void setFilterPanel( FilterPanel filterPanel )
- {
- _filterPanel = filterPanel;
- }
- /**
- * Get the list of columns of this DataTableManager
- *
- * @return The list of columns of this DataTableManager
- */
- public List<DataTableColumn> getListColumn( )
- {
- return _listColumn;
- }
- /**
- * Set the list of columns of this DataTableManager
- *
- * @param listColumn
- * The list of columns of this DataTableManager
- */
- public void setListColumn( List<DataTableColumn> listColumn )
- {
- _listColumn = listColumn;
- }
- /**
- * Get the sort url of this DataTableManager
- *
- * @return The sort url of this DataTableManager
- */
- public String getSortUrl( )
- {
- return _strSortUrl;
- }
- /**
- * Set the sort url of this DataTableManager
- *
- * @param strSortUrl
- * The sort url of this DataTableManager
- */
- public void setSortUrl( String strSortUrl )
- {
- _strSortUrl = strSortUrl;
- if ( ( _strSortUrl != null ) && StringUtils.isNotEmpty( _strSortUrl ) && !StringUtils.contains( _strSortUrl, getId( ) ) )
- {
- // We add to the sort URL the unique parameter of this data table manager
- UrlItem urlItem = new UrlItem( _strSortUrl );
- urlItem.addParameter( getId( ), getId( ) );
- _strSortUrl = urlItem.getUrl( );
- }
- }
- /**
- * Get the filtered, sorted and paginated items collection of this DataTableManager
- *
- * @return The filtered, sorted and paginated items collection of this DataTableManager
- */
- public List<T> getItems( )
- {
- return _paginator.getPageItems( );
- }
- /**
- * Set the items to display. The list of items must be fintered, sorted and paginated. Methods
- * {@link DataTableManager#getAndUpdatePaginator(HttpServletRequest ) getAndUpdatePaginator}, {@link DataTableManager#getAndUpdateSort(HttpServletRequest )
- * getAndUpdateSort} and {@link DataTableManager#getAndUpdateFilter(HttpServletRequest, Object) getAndUpdateFilter} must have been called before the
- * generation of the list of items.
- *
- * @param items
- * The filtered sorted and paginated list of items to display
- * @param nTotalItemsNumber
- * The total number of items
- */
- public void setItems( List<T> items, int nTotalItemsNumber )
- {
- _paginator = new LocalizedDelegatePaginator<>( items, _nItemsPerPage, getSortUrl( ), AbstractPaginator.PARAMETER_PAGE_INDEX, _strCurrentPageIndex,
- nTotalItemsNumber, _locale );
- }
- /**
- * Clear the items stored by this DataTableManager so that the garbage collector can free the memory they use.
- */
- public void clearItems( )
- {
- _paginator = null;
- _locale = null;
- }
- /**
- * Internal method. Get the paginator.
- *
- * Do not use this method, use {@link DataTableManager#getAndUpdatePaginator(HttpServletRequest ) getAndUpdatePaginator} instead to get up to date values !
- *
- * @return The paginator
- */
- public IPaginator<T> getPaginator( )
- {
- return _paginator;
- }
- /**
- * Get the enable paginator boolean
- *
- * @return True if pagination is active, false otherwise
- */
- public boolean getEnablePaginator( )
- {
- return _bEnablePaginator;
- }
- /**
- * Get the locale
- *
- * @return The locale
- */
- public Locale getLocale( )
- {
- return _locale;
- }
- /**
- * Set the locale
- *
- * @param locale
- * The locale
- */
- public void setLocale( Locale locale )
- {
- _locale = locale;
- }
- /**
- * Get the unique id of this data table manager
- *
- * @return The unique id of this data table manager
- */
- public String getId( )
- {
- return _strUid;
- }
- /**
- * Get the paginator updated with values in the request
- *
- * @param request
- * The request
- * @return The paginator up to date
- */
- public DataTablePaginationProperties getAndUpdatePaginator( HttpServletRequest request )
- {
- DataTablePaginationProperties paginationProperties = null;
- if ( _bEnablePaginator )
- {
- paginationProperties = new DataTablePaginationProperties( );
- if ( hasDataTableFormBeenSubmited( request ) )
- {
- _strCurrentPageIndex = AbstractPaginator.getPageIndex( request, AbstractPaginator.PARAMETER_PAGE_INDEX, _strCurrentPageIndex );
- _nItemsPerPage = AbstractPaginator.getItemsPerPage( request, AbstractPaginator.PARAMETER_ITEMS_PER_PAGE, _nItemsPerPage,
- _nDefautlItemsPerPage );
- }
- paginationProperties.setItemsPerPage( _nItemsPerPage );
- int nCurrentPageIndex = 1;
- if ( !StringUtils.isEmpty( _strCurrentPageIndex ) )
- {
- nCurrentPageIndex = Integer.parseInt( _strCurrentPageIndex );
- }
- paginationProperties.setCurrentPageIndex( nCurrentPageIndex );
- }
- _locale = ( request != null ) ? request.getLocale( ) : LocaleService.getDefault( );
- return paginationProperties;
- }
- /**
- * Get sort properties updated with values in the request
- *
- * @param request
- * The request
- * @return The sort properties up to date
- */
- public DataTableSort getAndUpdateSort( HttpServletRequest request )
- {
- if ( hasDataTableFormBeenSubmited( request ) )
- {
- String strSortedAttributeName = request.getParameter( Parameters.SORTED_ATTRIBUTE_NAME );
- if ( strSortedAttributeName != null )
- {
- // We update sort properties
- _strSortedAttributeName = strSortedAttributeName;
- _bIsAscSort = Boolean.parseBoolean( request.getParameter( Parameters.SORTED_ASC ) );
- }
- }
- return new DataTableSort( _strSortedAttributeName, _bIsAscSort );
- }
- /**
- * Get filter properties updated with values in the request
- *
- * @param request
- * The request
- * @param <K>
- * Type of the filter to use. This type must have accessors for every declared filter.
- * @param filterObject
- * Filter to apply.
- * @return The filter properties up to date
- */
- public <K> K getAndUpdateFilter( HttpServletRequest request, K filterObject )
- {
- List<DataTableFilter> listFilters = _filterPanel.getListFilter( );
- boolean bSubmitedDataTable = hasDataTableFormBeenSubmited( request );
- boolean bUpdateFilter = false;
- Map<String, Object> mapFilter = new HashMap<>( );
- if ( bSubmitedDataTable )
- {
- StringUtils.equals( request.getParameter( FilterPanel.PARAM_FILTER_PANEL_PREFIX + FilterPanel.PARAM_RESET_FILTERS ), Boolean.TRUE.toString( ) );
- bUpdateFilter = StringUtils.equals( request.getParameter( FilterPanel.PARAM_FILTER_PANEL_PREFIX + FilterPanel.PARAM_UPDATE_FILTERS ),
- Boolean.TRUE.toString( ) );
- }
- for ( DataTableFilter filter : listFilters )
- {
- if ( bSubmitedDataTable )
- {
- String strFilterValue = request.getParameter( FilterPanel.PARAM_FILTER_PANEL_PREFIX + filter.getParameterName( ) );
- if ( bUpdateFilter )
- {
- filter.setValue( strFilterValue );
- }
- }
- if ( StringUtils.isNotBlank( filter.getValue( ) ) )
- {
- mapFilter.put( filter.getParameterName( ), filter.getValue( ) );
- }
- }
- try
- {
- BeanUtilsBean.getInstance( ).populate( filterObject, mapFilter );
- }
- catch( InvocationTargetException | IllegalAccessException e )
- {
- AppLogService.error( e.getMessage( ), e );
- return null;
- }
- return filterObject;
- }
- /**
- * Internal method. Get the prefix of html attributes used by filters
- *
- * @return The prefix of html attributes used by filters
- */
- public String getFilterPanelPrefix( )
- {
- return FilterPanel.PARAM_FILTER_PANEL_PREFIX;
- }
- /**
- * Return the getter method of the object obj for the attribute <i>strAttributName</i>
- *
- * @param obj
- * the object
- * @param strAttributName
- * The name of the attribute to get the getter
- * @param strMethodPrefix
- * Prefix of the name of the method
- * @return method Method of the object obj for the attribute <i>strAttributName</i>
- */
- private Method getMethod( Object obj, String strAttributName, String strMethodPrefix )
- {
- Method method = null;
- String strFirstLetter = strAttributName.substring( 0, 1 ).toUpperCase( );
- String strMethodName = strMethodPrefix + strFirstLetter + strAttributName.substring( 1, strAttributName.length( ) );
- try
- {
- method = obj.getClass( ).getMethod( strMethodName );
- }
- catch( Exception e )
- {
- AppLogService.debug( e.getMessage( ), e );
- }
- return method;
- }
- /**
- * Generates the unique identifier of this data table manager
- */
- private void generateDataTableId( )
- {
- this._strUid = CONSTANT_DATA_TABLE_MANAGER_ID_PREFIX + UniqueIDGenerator.getNewId( );
- }
- /**
- * Check if a request contain data of this data table manager
- *
- * @param request
- * The request
- * @return True if a form of this data table manager has been submited, false otherwise
- */
- private boolean hasDataTableFormBeenSubmited( HttpServletRequest request )
- {
- return request != null && StringUtils.equals( request.getParameter( getId( ) ), getId( ) );
- }
- }