View Javadoc
1   /*
2    * Copyright (c) 2002-2018, Mairie de 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.directory.modules.multiview.business.record;
35  
36  import java.util.ArrayList;
37  import java.util.Iterator;
38  import java.util.List;
39  
40  import org.apache.commons.collections.CollectionUtils;
41  import org.apache.commons.lang3.StringUtils;
42  
43  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.column.querypart.IRecordColumnQueryPart;
44  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.column.querypart.RecordColumnQueryBuilder;
45  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.filter.RecordFilterQueryConstants;
46  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.filter.querypart.IRecordFilterQueryPart;
47  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.panel.initializer.querypart.IRecordPanelInitializerQueryPart;
48  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.panel.initializer.querypart.RecordPanelInitializerQueryBuilder;
49  
50  /**
51   * Class use to build a SQL query from part of query inside RecordFilter and RecordColumn
52   */
53  public final class QueryBuilder
54  {
55      /**
56       * Constructor
57       */
58      private QueryBuilder( )
59      {
60  
61      }
62  
63      /**
64       * Build a SQL query from different parts from a list of RecordColumn and a list of RecordFilter
65       * 
66       * @param listRecordPanelInitializerQueryPart
67       *            The list of all RecordPanelInitializerQueryPart to use for built the query
68       * @param listRecordColumnQueryPart
69       *            The of RecordColumnQueryPart to retrieve the select and from parts of the query
70       * @param listRecordFilterQueryPart
71       *            The list of RecordFilterQueryPart to retrieve the where parts of the query
72       * @return the global query build from the RecordColmuns and RecordFilters
73       */
74      public static String buildQuery( List<IRecordPanelInitializerQueryPart> listRecordPanelInitializerQueryPart,
75              List<IRecordColumnQueryPart> listRecordColumnQueryPart, List<IRecordFilterQueryPart> listRecordFilterQueryPart )
76      {
77          StringBuilder stringBuilderGlobalQuery = new StringBuilder( );
78  
79          if ( !CollectionUtils.isEmpty( listRecordPanelInitializerQueryPart ) && !CollectionUtils.isEmpty( listRecordColumnQueryPart ) )
80          {
81              // Build the select query part
82              buildSelectQueryPart( stringBuilderGlobalQuery, listRecordPanelInitializerQueryPart, listRecordColumnQueryPart );
83              stringBuilderGlobalQuery.append( RecordFilterQueryConstants.SPACE_SEPARATOR );
84  
85              // Build the from query part
86              buildFromQueryPart( stringBuilderGlobalQuery, listRecordPanelInitializerQueryPart, listRecordColumnQueryPart );
87              stringBuilderGlobalQuery.append( RecordFilterQueryConstants.SPACE_SEPARATOR );
88  
89              // Build the join query part
90              buildJoinQueryPart( stringBuilderGlobalQuery, listRecordPanelInitializerQueryPart, listRecordColumnQueryPart );
91              stringBuilderGlobalQuery.append( RecordFilterQueryConstants.SPACE_SEPARATOR );
92  
93              // Build the where query part
94              buildWhereQueryPart( stringBuilderGlobalQuery, listRecordFilterQueryPart );
95  
96              // Build the group by query part
97              buildGroupByQueryPart( stringBuilderGlobalQuery );
98          }
99  
100         return stringBuilderGlobalQuery.toString( );
101     }
102 
103     /**
104      * Populate the StringBuilder of the global query with the select query part
105      * 
106      * @param stringBuilderGlobalQuery
107      *            The StringBuilder of the global query to populate
108      * @param listRecordPanelInitializerQueryPart
109      *            The list of all IRecordPanelInitializerQueryPart to use
110      * @param listRecordColumnQueryPart
111      *            The list of RecordColumnQueryPart to retrieve the select query parts from
112      */
113     private static void buildSelectQueryPart( StringBuilder stringBuilderGlobalQuery,
114             List<IRecordPanelInitializerQueryPart> listRecordPanelInitializerQueryPart, List<IRecordColumnQueryPart> listRecordColumnQueryPart )
115     {
116         List<String> listSelectQueryParts = new ArrayList<>( );
117 
118         // Use the query part of the RecordPanelInitializer
119         List<String> listRecordPanelInitializerSelectQueryParts = RecordPanelInitializerQueryBuilder
120                 .buildPanelInitializerSelectQueryParts( listRecordPanelInitializerQueryPart );
121         if ( !CollectionUtils.isEmpty( listRecordPanelInitializerSelectQueryParts ) )
122         {
123             listSelectQueryParts.addAll( listRecordPanelInitializerSelectQueryParts );
124         }
125 
126         // Use the query part of the column
127         List<String> listRecordColumnSelectQueryParts = RecordColumnQueryBuilder.buildRecordColumnSelectQueryPart( listRecordColumnQueryPart );
128         if ( !CollectionUtils.isEmpty( listRecordColumnSelectQueryParts ) )
129         {
130             listSelectQueryParts.addAll( listRecordColumnSelectQueryParts );
131         }
132 
133         stringBuilderGlobalQuery.append( buildQueryPart( listSelectQueryParts, RecordFilterQueryConstants.SELECT_KEYWORD ) );
134     }
135 
136     /**
137      * Populate the StringBuilder of the global query with the from query part
138      * 
139      * @param stringBuilderGlobalQuery
140      *            The StringBuilder of the global query to populate
141      * @param listRecordPanelInitializerQueryPart
142      *            The list of all IRecordPanelInitializerQueryPart to use
143      * @param listRecordColumnQueryPart
144      *            The list of RecordColumnQueryPart to retrieve the select query parts from
145      */
146     private static void buildFromQueryPart( StringBuilder stringBuilderGlobalQuery, List<IRecordPanelInitializerQueryPart> listRecordPanelInitializerQueryPart,
147             List<IRecordColumnQueryPart> listRecordColumnQueryPart )
148     {
149         List<String> listFromQueryParts = new ArrayList<>( );
150 
151         // Use the query part of the RecordPanelInitializer
152         List<String> listRecordPanelInitializerFromQueryPart = RecordPanelInitializerQueryBuilder
153                 .buildPanelInitializerFromQueryParts( listRecordPanelInitializerQueryPart );
154         if ( !CollectionUtils.isEmpty( listRecordPanelInitializerFromQueryPart ) )
155         {
156             listFromQueryParts.addAll( listRecordPanelInitializerFromQueryPart );
157         }
158 
159         // Use the query parts of the columns
160         List<String> listRecordColumnFromQueryParts = RecordColumnQueryBuilder.buildRecordColumnFromQueryParts( listRecordColumnQueryPart );
161         if ( !CollectionUtils.isEmpty( listRecordColumnFromQueryParts ) )
162         {
163             listFromQueryParts.addAll( listRecordColumnFromQueryParts );
164         }
165 
166         stringBuilderGlobalQuery.append( buildQueryPart( listFromQueryParts, RecordFilterQueryConstants.FROM_KEYWORD ) );
167     }
168 
169     /**
170      * Populate the StringBuilder of the global query with the join query part
171      * 
172      * @param stringBuilderGlobalQuery
173      *            The StringBuilder of the global query to populate
174      * @param listRecordPanelInitializerQueryPart
175      *            The list of all IRecordPanelInitializerQueryPart to use
176      * @param listRecordColumnQueryPart
177      *            The list of RecordColumnQueryPart to retrieve the select query parts from
178      */
179     private static void buildJoinQueryPart( StringBuilder stringBuilderGlobalQuery, List<IRecordPanelInitializerQueryPart> listRecordPanelInitializerQueryPart,
180             List<IRecordColumnQueryPart> listRecordColumnQueryPart )
181     {
182         StringBuilder stringBuilderJoinQueryPart = new StringBuilder( );
183 
184         // Use the query parts of the panel filter
185         if ( !CollectionUtils.isEmpty( listRecordPanelInitializerQueryPart ) )
186         {
187             RecordPanelInitializerQueryBuilder.buildRecordPanelInitializerJoinQueryParts( stringBuilderJoinQueryPart, listRecordPanelInitializerQueryPart );
188         }
189 
190         // Use the query parts of the columns
191         if ( listRecordColumnQueryPart != null && !listRecordColumnQueryPart.isEmpty( ) )
192         {
193             RecordColumnQueryBuilder.buildRecordColumnJoinQueryParts( stringBuilderJoinQueryPart, listRecordColumnQueryPart );
194         }
195 
196         stringBuilderGlobalQuery.append( stringBuilderJoinQueryPart.toString( ) );
197     }
198 
199     /**
200      * Populate the StringBuilder of the global query with the where query part
201      * 
202      * @param stringBuilderGlobalQuery
203      *            The StringBuilder of the global query to populate
204      * @param listRecordFilterQueryPart
205      *            The list of IRecordFilterQueryPart to retrieve the where query parts from
206      */
207     private static void buildWhereQueryPart( StringBuilder stringBuilderGlobalQuery, List<IRecordFilterQueryPart> listRecordFilterQueryPart )
208     {
209         if ( listRecordFilterQueryPart != null && !listRecordFilterQueryPart.isEmpty( ) )
210         {
211             stringBuilderGlobalQuery.append( RecordFilterQueryConstants.WHERE_BASE_KEYWORD ).append( RecordFilterQueryConstants.SPACE_SEPARATOR );
212             manageFilterWhereQueryParts( stringBuilderGlobalQuery, listRecordFilterQueryPart );
213         }
214     }
215 
216     /**
217      * Populate the Group by Query part of the global query
218      * 
219      * @param stringBuilderGlobalQuery
220      *            The StringBuilder of the global query
221      * @param listRecordFilterQueryPart
222      */
223     private static void buildGroupByQueryPart( StringBuilder stringBuilderGlobalQuery )
224     {
225         stringBuilderGlobalQuery.append( RecordFilterQueryConstants.GROUP_BY_RECORD_ID_CLAUSE );
226     }
227 
228     /**
229      * Build a part of the query
230      * 
231      * @param listQueryPart
232      *            The list of query part to use
233      * @param strKeyWord
234      *            The key word to use for the query for the current list of parts
235      * @return the part of the query
236      */
237     private static String buildQueryPart( List<String> listQueryPart, String strKeyWord )
238     {
239         StringBuilder stringBuilderFromQuery = new StringBuilder( strKeyWord );
240         stringBuilderFromQuery.append( RecordFilterQueryConstants.SPACE_SEPARATOR );
241 
242         Iterator<String> iteratorQueryPart = listQueryPart.iterator( );
243         while ( iteratorQueryPart.hasNext( ) )
244         {
245             stringBuilderFromQuery.append( iteratorQueryPart.next( ) );
246 
247             if ( iteratorQueryPart.hasNext( ) )
248             {
249                 stringBuilderFromQuery.append( RecordFilterQueryConstants.COMMA_SEPARATOR );
250                 stringBuilderFromQuery.append( RecordFilterQueryConstants.SPACE_SEPARATOR );
251             }
252         }
253 
254         return stringBuilderFromQuery.toString( );
255     }
256 
257     /**
258      * Populate the StringBuilder of the query with all the where query parts of the list of RecordFilter.
259      * 
260      * @param stringBuilderWhereQueryPart
261      *            The stringBuilder of the request to populate with the where query parts of the given filters
262      * @param listRecordFilterQueryPart
263      *            The list of RecordFilterQueryPart to retrieve the where query parts
264      */
265     private static void manageFilterWhereQueryParts( StringBuilder stringBuilderWhereQueryPart, List<IRecordFilterQueryPart> listRecordFilterQueryPart )
266     {
267         Iterator<IRecordFilterQueryPart> iteratorRecordFilterQueryPart = listRecordFilterQueryPart.iterator( );
268         while ( iteratorRecordFilterQueryPart.hasNext( ) )
269         {
270             IRecordFilterQueryPart recordFilterQueryPart = iteratorRecordFilterQueryPart.next( );
271             addAndQueryClause( stringBuilderWhereQueryPart, recordFilterQueryPart.getRecordFilterQuery( ) );
272 
273             if ( iteratorRecordFilterQueryPart.hasNext( ) )
274             {
275                 stringBuilderWhereQueryPart.append( RecordFilterQueryConstants.SPACE_SEPARATOR );
276             }
277         }
278     }
279 
280     /**
281      * Add and AND query part to the given StringBuilder for the specified query part. If the query is null or empty nothing will be added.
282      * 
283      * @param stringBuilderQuery
284      *            The StringBuilder of the query to complete
285      * @param strQueryPart
286      *            The part of the query to added to the StringBuilder
287      */
288     private static void addAndQueryClause( StringBuilder stringBuilderQuery, String strQueryPart )
289     {
290         if ( stringBuilderQuery != null && StringUtils.isNotBlank( strQueryPart ) )
291         {
292             stringBuilderQuery.append( RecordFilterQueryConstants.AND_KEYWORD );
293             stringBuilderQuery.append( RecordFilterQueryConstants.AND_OPEN_CLAUSE );
294             stringBuilderQuery.append( strQueryPart );
295             stringBuilderQuery.append( RecordFilterQueryConstants.AND_CLOSE_CLAUSE );
296         }
297     }
298 }