View Javadoc
1   /*
2    * Copyright (c) 2002-2025, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.formresponsxpage.business;
35  
36  
37  import java.lang.reflect.Method;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  
44  
45  import org.apache.commons.lang3.StringUtils;
46  
47  
48  public abstract class AbstractFilterDao {
49  	
50  	//Maps containing names and types of each databases column associated to a business class attribute 
51  	protected HashMap<String,String> _mapSql;
52  	
53  	//Prefix 
54  	private final static String PREFIX_GET = "get";
55  	private final static String PREFIX_IS = "is";
56  	
57  	//Constants SQL
58  	private final static String SQL_WHERE =" WHERE 1 ";
59  	private final static String SQL_ORDER_BY =" ORDER BY ";
60  	private final static String SQL_EQUAL =" = ? ";
61  	private final static String SQL_LIKE =" LIKE ? ";
62  	private final static String SQL_AND = " AND ";
63  	private final static String SQL_ASC =" ASC ";
64  	private final static String SQL_DESC =" DESC ";
65  	
66  	//types only allowed for research
67  	protected final static String TYPE_DATE = "Date";
68  	protected final static String TYPE_STRING = "String";
69  	protected final static String TYPE_BOOLEAN = "boolean";
70  	protected final static String TYPE_INT = "int";
71  
72  	//List of constraints
73  	private final static List<String> _listPrefixToRemove = Arrays.asList(PREFIX_GET,PREFIX_IS);
74  	protected final static List<String> _listTypeAllowedForSearch = Arrays.asList(TYPE_DATE,TYPE_STRING,TYPE_BOOLEAN,TYPE_INT);
75  	
76  	
77  	 /**
78       *  Preparation of filterStatement
79       * @param mapFilterCriteria contains searchbar names/values inputs 
80       * @param strColumnToOrder contains the column name to use for orderBy statement in case of sorting request (must be null)
81       * @param strSortMode contains the sortMode in case of sorting request : ASC or DESC (must be null)
82  	 * @return a string with the WHERE part and the ORDER BY part of the sql statement
83       */
84  	
85  	protected String prepareSelectStatement(String SQL_QUERY_SELECTALL_ID,Map <String,String> mapFilterCriteria, String strColumnToOrder, String strSortMode) {
86  		
87  		
88  		StringBuilder builder = new StringBuilder();
89  
90          builder.append(SQL_QUERY_SELECTALL_ID);
91          builder.append(addWhereClauses(mapFilterCriteria));
92          builder.append(addOrderByClause(strColumnToOrder,strSortMode));
93  		
94  		
95  		return  builder.toString();	
96  		
97  	}
98  
99  	 /**
100      *  add Where clause to the filterStatement
101      *  @param mapFilterCriteria contains name and value of each where clause
102      *  @return the where part of the filterStatement
103      */
104 	
105 	protected String addWhereClauses(Map<String, String> mapFilterCriteria) {
106 		
107 		StringBuilder WhereClauses = new StringBuilder();
108 		
109 		if(!mapFilterIsEmpty(mapFilterCriteria)) {
110 			
111 			WhereClauses.append(SQL_WHERE);
112 			
113 			for(Map.Entry<String, String> filter : mapFilterCriteria.entrySet()) {
114 			    
115 				//Check if a value was passed for the search 
116 				if(StringUtils.isNotBlank(filter.getValue())) {
117 					
118 					//Check if the criteria name match with a BDD column name and if the type of this column is allowed for a search
119 					if(_mapSql.containsKey(filter.getKey()) && _listTypeAllowedForSearch.contains(_mapSql.get(filter.getKey()))) {
120 								
121 						WhereClauses.append(SQL_AND);
122 						WhereClauses.append(filter.getKey());
123 						WhereClauses.append(addWhereClauseOperator(filter.getKey()));
124 					}
125 				}
126 			}
127 			
128 		}
129 		
130 		return WhereClauses.toString();
131 	}; 
132 	    
133 	 /**
134      *  add OrderBy columns to the filterStatement
135      * @param strColumnToOrder contains the column name to use for orderBy statement in case of sorting request (must be null)
136      * @param strSortMode contains the sortMode in case of sorting request : ASC or DESC (must be null)
137 	 * @return the orderBy part of the filterStatement
138      */
139 	
140 	protected String addOrderByClause(String strColumnToOrder,String strSortMode) {
141 		
142 		
143 		if(StringUtils.isNotBlank(strColumnToOrder) && _mapSql.containsKey(strColumnToOrder)) {
144 
145 			
146 			StringBuilder orderByClauses = new StringBuilder();
147 			
148 			orderByClauses.append(SQL_ORDER_BY);
149 			orderByClauses.append(strColumnToOrder);
150 			orderByClauses.append(strSortMode);				
151 			
152 			return  orderByClauses.toString(); 
153 			
154 		}
155 		
156 		return "";
157 	} 
158 	
159     /**
160      * Check if _mapFilter is empty
161      * @return boolean
162      */
163     
164     private boolean mapFilterIsEmpty(Map<String,String> mapFilterCriteria) {
165 		
166     	for (Map.Entry<String, String> entry : mapFilterCriteria.entrySet()) {
167     		if(StringUtils.isNotBlank(entry.getValue())) {
168     			return false;
169     		}
170         }
171     	
172     	return true;
173 	}
174 	
175 	 /**
176      *  add where clause operator to the filterStatement
177      * @param strWhereClauseColumn contains one of the column names to use for where clause part
178 	 * @return operator to use for the clause passed in argument
179      */
180 	
181     private String addWhereClauseOperator(String strWhereClauseColumn) {
182     	
183     	if(_mapSql.containsKey(strWhereClauseColumn)) {
184     		
185     		switch(_mapSql.get(strWhereClauseColumn)) {
186     			case TYPE_DATE :
187     				return SQL_EQUAL;
188     			case TYPE_STRING :
189     				return SQL_LIKE;  			
190     			case TYPE_BOOLEAN :
191     				return SQL_EQUAL;
192     			case TYPE_INT :
193     				return SQL_EQUAL;
194     			default :
195     				return SQL_LIKE; //Other types will be managed as strings
196     		}
197     	}
198             	
199     	return SQL_LIKE; //Other types will be managed as strings
200     }
201     
202     /**
203     * Return name of column in sql database format from getter name of business class exemples : getImageUrl -> image_url , getCost -> cost
204     * @return the name of column in sql database   
205     */
206     private String getFormatedColumnName(String strAttributeName, String strPrefixToCut){
207     	
208     	//Remove prefix (get or is) and lowercase the first character
209     	String strRemovePrefix = StringUtils.uncapitalize(strAttributeName.substring(strPrefixToCut.length())).toString();
210     	
211     	StringBuilder builder = new StringBuilder();
212     	
213     	//Change uppercase character to lowercase and add an underscore in front of it. exemple : dateStart -> date_start 
214     	for(char c: strRemovePrefix.toCharArray()) {
215     		
216     		if( Character.isUpperCase(c)) {
217     			builder.append('_');
218     			builder.append(Character.toLowerCase(c));
219     		}else {
220     			builder.append(c);
221     		}
222     	}
223     	
224     	return builder.toString();
225     	
226     }
227     
228     
229     /**
230     * Initialization of mapSql. 
231     * mapSql Containing names and types of each databases column associated to a business class attribute.
232     */
233 	protected void initMapSql(Class<?> businessClass) {
234 			
235 			_mapSql = new HashMap<>();
236 			
237 			for (Method method : businessClass.getDeclaredMethods()) {
238 				
239 				for(String prefix : _listPrefixToRemove) {
240 					//Use only getter and is function of business class to infer database name of each attributes
241 					if(method.getName().startsWith(prefix)){
242 						_mapSql.put(getFormatedColumnName(method.getName(),prefix),method.getReturnType().getSimpleName()); 
243 					}
244 				}
245 	        }
246 	}
247 	
248 }
249