AbstractFilterDao.java
package fr.paris.lutece.plugins.notificationstore.business;
/*
* Copyright (c) 2002-2025, 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
*/
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.util.sql.DAOUtil;
public abstract class AbstractFilterDao<T>
{
// Maps containing names and types of each databases column associated to a business class attribute
protected HashMap<String, String> _mapSql;
// Prefix
private final static String PREFIX_GET = "get";
private final static String PREFIX_IS = "is";
// Constants SQL
private final static String SQL_WHERE = " WHERE 1 ";
private final static String SQL_ORDER_BY = " ORDER BY ";
private final static String SQL_EQUAL = " = ? ";
private final static String SQL_LIKE = " LIKE ? ";
private final static String SQL_AND = " AND ";
// types only allowed for research
protected final static String TYPE_DATE = "Date";
protected final static String TYPE_STRING = "String";
protected final static String TYPE_BOOLEAN = "boolean";
protected final static String TYPE_INT = "int";
// List of constraints
private final static List<String> _listPrefixToRemove = Arrays.asList( PREFIX_GET, PREFIX_IS );
protected final static List<String> _listTypeAllowedForSearch = Arrays.asList( TYPE_DATE, TYPE_STRING, TYPE_BOOLEAN, TYPE_INT );
private final Class<T> _clazz;
protected Plugin _plugin;
@SuppressWarnings( "unchecked" )
protected AbstractFilterDao( )
{
Type superClass = getClass( ).getGenericSuperclass( );
if ( superClass instanceof ParameterizedType )
{
Type actualType = ( (ParameterizedType) superClass ).getActualTypeArguments( ) [0];
this._clazz = (Class<T>) actualType;
}
else
{
throw new IllegalArgumentException( "Pas de type générique trouvé !" );
}
initMapSql( _clazz ); // Maps with name and type of each databases column associated to the business class attributes
}
/**
* Preparation of filterStatement
*
* @param mapFilterCriteria
* contains searchbar names/values inputs
* @param strColumnToOrder
* contains the column name to use for orderBy statement in case of sorting request (must be null)
* @param strSortMode
* contains the sortMode in case of sorting request : ASC or DESC (must be null)
* @return a string with the WHERE part and the ORDER BY part of the sql statement
*/
protected String prepareSelectStatement( String SQL_QUERY_SELECTALL_ID, Map<String, String> mapFilterCriteria, String strColumnToOrder, String strSortMode )
{
StringBuilder builder = new StringBuilder( );
builder.append( SQL_QUERY_SELECTALL_ID );
builder.append( addWhereClauses( mapFilterCriteria ) );
builder.append( addOrderByClause( strColumnToOrder, strSortMode ) );
return builder.toString( );
}
/**
* add Where clause to the filterStatement
*
* @param mapFilterCriteria
* contains name and value of each where clause
* @return the where part of the filterStatement
*/
protected String addWhereClauses( Map<String, String> mapFilterCriteria )
{
StringBuilder WhereClauses = new StringBuilder( );
if ( !mapFilterIsEmpty( mapFilterCriteria ) )
{
WhereClauses.append( SQL_WHERE );
for ( Map.Entry<String, String> filter : mapFilterCriteria.entrySet( ) )
{
// Check if a value was passed for the search
if ( StringUtils.isNotBlank( filter.getValue( ) ) )
{
// Check if the criteria name match with a BDD column name and if the type of this column is allowed for a search
if ( _mapSql.containsKey( filter.getKey( ) ) && _listTypeAllowedForSearch.contains( _mapSql.get( filter.getKey( ) ) ) )
{
WhereClauses.append( SQL_AND );
WhereClauses.append( filter.getKey( ) );
WhereClauses.append( addWhereClauseOperator( filter.getKey( ) ) );
}
}
}
}
return WhereClauses.toString( );
};
/**
* add OrderBy columns to the filterStatement
*
* @param strColumnToOrder
* contains the column name to use for orderBy statement in case of sorting request (must be null)
* @param strSortMode
* contains the sortMode in case of sorting request : ASC or DESC (must be null)
* @return the orderBy part of the filterStatement
*/
protected String addOrderByClause( String strColumnToOrder, String strSortMode )
{
if ( StringUtils.isNotBlank( strColumnToOrder ) && _mapSql.containsKey( strColumnToOrder ) )
{
StringBuilder orderByClauses = new StringBuilder( );
orderByClauses.append( SQL_ORDER_BY );
orderByClauses.append( strColumnToOrder );
orderByClauses.append( strSortMode );
return orderByClauses.toString( );
}
return "";
}
/**
* Check if _mapFilter is empty
*
* @return boolean
*/
private boolean mapFilterIsEmpty( Map<String, String> mapFilterCriteria )
{
for ( Map.Entry<String, String> entry : mapFilterCriteria.entrySet( ) )
{
if ( StringUtils.isNotBlank( entry.getValue( ) ) )
{
return false;
}
}
return true;
}
/**
* add where clause operator to the filterStatement
*
* @param strWhereClauseColumn
* contains one of the column names to use for where clause part
* @return operator to use for the clause passed in argument
*/
private String addWhereClauseOperator( String strWhereClauseColumn )
{
if ( _mapSql.containsKey( strWhereClauseColumn ) )
{
switch( _mapSql.get( strWhereClauseColumn ) )
{
case TYPE_DATE:
return SQL_EQUAL;
case TYPE_STRING:
return SQL_LIKE;
case TYPE_BOOLEAN:
return SQL_EQUAL;
case TYPE_INT:
return SQL_EQUAL;
default:
return SQL_LIKE; // Other types will be managed as strings
}
}
return SQL_LIKE; // Other types will be managed as strings
}
/**
* Return name of column in sql database format from getter name of business class exemples : getImageUrl -> image_url , getCost -> cost
*
* @return the name of column in sql database
*/
private String getFormatedColumnName( String strAttributeName, String strPrefixToCut )
{
// Remove prefix (get or is) and lowercase the first character
String strRemovePrefix = StringUtils.uncapitalize( strAttributeName.substring( strPrefixToCut.length( ) ) ).toString( );
StringBuilder builder = new StringBuilder( );
// Change uppercase character to lowercase and add an underscore in front of it. exemple : dateStart -> date_start
for ( char c : strRemovePrefix.toCharArray( ) )
{
if ( Character.isUpperCase( c ) )
{
builder.append( '_' );
builder.append( Character.toLowerCase( c ) );
}
else
{
builder.append( c );
}
}
return builder.toString( );
}
/**
* Initialization of mapSql. mapSql Containing names and types of each databases column associated to a business class attribute.
*/
protected void initMapSql( Class<?> businessClass )
{
_mapSql = new HashMap<>( );
for ( Method method : businessClass.getDeclaredMethods( ) )
{
for ( String prefix : _listPrefixToRemove )
{
// Use only getter and is function of business class to infer database name of each attributes
if ( method.getName( ).startsWith( prefix ) )
{
_mapSql.put( getFormatedColumnName( method.getName( ), prefix ), method.getReturnType( ).getSimpleName( ) );
}
}
}
}
/**
* searchItemsIdList
*
* @param mapFilterCriteria
* @param strColumnToOrder
* @param strSortMode
* @param plugin
* @return
*/
public List<Integer> searchItemsIdList( String strSqlSelectAllIds, Map<String, String> mapFilterCriteria, String strColumnToOrder, String strSortMode )
{
List<Integer> itemList = new ArrayList<>( );
String strSelectStatement = prepareSelectStatement( strSqlSelectAllIds, mapFilterCriteria, strColumnToOrder, strSortMode );
try ( DAOUtil daoUtil = new DAOUtil( strSelectStatement, _plugin ) )
{
int nIndex = 1;
for ( Map.Entry<String, String> filter : mapFilterCriteria.entrySet( ) )
{
if ( StringUtils.isNotBlank( filter.getValue( ) ) && _mapSql.containsKey( filter.getKey( ) ) )
{
daoUtil.setString( nIndex++, filter.getValue( ) );
}
}
daoUtil.executeQuery( );
while ( daoUtil.next( ) )
{
itemList.add( daoUtil.getInt( 1 ) );
}
return itemList;
}
}
}