AbstractFilterDao.java
/*
* Copyright (c) 2002-2026, 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.plugins.deviceregistration.business;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class AbstractFilterDao
{
// 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_BETWEEN = " >= ? and ";
private static final String SQL_STRICTLY_UNDER = " < ?";
private final static String SQL_LIKE = " LIKE ? ";
private final static String SQL_AND = " AND ";
private final static String SQL_ASC = " ASC ";
private final static String SQL_DESC = " DESC ";
// 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";
protected final static String TYPE_CHANGETYPE = "ChangeType";
protected final static String TYPE_TIMESTAMP = "Timestamp";
// 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, TYPE_CHANGETYPE,
TYPE_TIMESTAMP );
/**
* 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( final String selectStatement, final Map<String, String> mapFilterCriteria, final String strColumnToOrder, final String strSortMode )
{
return selectStatement + addWhereClauses( mapFilterCriteria ) + addOrderByClause( strColumnToOrder, strSortMode );
}
/**
* 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( final Map<String, String> mapFilterCriteria )
{
final StringBuilder whereClauses = new StringBuilder( );
if ( !mapFilterIsEmpty( mapFilterCriteria ) )
{
whereClauses.append( SQL_WHERE );
for ( final Map.Entry<String, String> filter : mapFilterCriteria.entrySet( ) )
{
/*
* Check if a value was passed for the search and Check if the criteria name match with a BDD column name and if the type of this column is
* allowed for a search
*/
if ( StringUtils.isNotBlank( filter.getValue( ) ) && _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( final String strColumnToOrder, final String strSortMode )
{
final StringBuilder orderByClauses = new StringBuilder( );
if ( StringUtils.isNotBlank( strColumnToOrder ) && _mapSql.containsKey( strColumnToOrder ) )
{
orderByClauses.append( SQL_ORDER_BY );
orderByClauses.append( strColumnToOrder );
orderByClauses.append( strSortMode );
}
return orderByClauses.toString( );
}
/**
* Check if _mapFilter is empty
*
* @return boolean
*/
private boolean mapFilterIsEmpty( final Map<String, String> mapFilterCriteria )
{
for ( final 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( final String strWhereClauseColumn )
{
if ( _mapSql.containsKey( strWhereClauseColumn ) )
{
switch( _mapSql.get( strWhereClauseColumn ) )
{
case TYPE_DATE:
case TYPE_BOOLEAN:
case TYPE_INT:
case TYPE_CHANGETYPE:
return SQL_EQUAL;
case TYPE_TIMESTAMP:
return SQL_BETWEEN + strWhereClauseColumn + SQL_STRICTLY_UNDER;
case TYPE_STRING:
return SQL_LIKE;
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( final String strAttributeName, final String strPrefixToCut )
{
// Remove prefix (get or is) and lowercase the first character
final String strRemovePrefix = StringUtils.uncapitalize( strAttributeName.substring( strPrefixToCut.length( ) ) );
final StringBuilder builder = new StringBuilder( );
// Change uppercase character to lowercase and add an underscore in front of it. exemple : dateStart -> date_start
for ( final 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( final Class<?> businessClass )
{
_mapSql = new HashMap<>( );
for ( final Method method : businessClass.getDeclaredMethods( ) )
{
for ( final 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( this.getFormatedColumnName( method.getName( ), prefix ), method.getReturnType( ).getSimpleName( ) );
}
}
}
}
}