001/* 
002 * JKNIV, whinstone one contract to access your database.
003 * 
004 * Copyright (C) 2017, the original author or authors.
005 *
006 * This library is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 2.1 of the License.
010 * 
011 * This library is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with this library; if not, write to the Free Software Foundation, Inc., 
018 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
019 */
020package net.sf.jkniv.whinstone;
021
022import java.util.Comparator;
023import java.util.HashMap;
024import java.util.Map;
025
026import net.sf.jkniv.asserts.Assertable;
027import net.sf.jkniv.asserts.AssertsFactory;
028import net.sf.jkniv.whinstone.types.RegisterType;
029
030public class QueryFactory
031{
032    private static final Assertable NOT_NULL = AssertsFactory.getNotNull();
033    
034    QueryFactory() { /* no instance for this */ }
035        
036    /**
037     * Creates a Query object parameterized starting at first row and retrieve all rows, isolation default, no timeout and online (no batch).
038     * 
039     * @param name query name
040     * @param params dynamically arguments to create {@code Queryable}.
041     * <p>
042     * 1o first param it's key name and 2o your value
043     * <p>
044     * 3o it's key 4o your value and so on.
045     * @return Queryable object with unlimited result
046     */
047    public static Queryable of(String name, Object... params)
048    {
049        NOT_NULL.verify(name);
050        return buildQueryable(name, 0, Integer.MAX_VALUE, null, params);
051    }
052
053    public static Queryable of(String name, RegisterType registerType, Object... args)
054    {
055        NOT_NULL.verify(name, registerType);
056        return buildQueryable(name, 0, Integer.MAX_VALUE, registerType, args);
057    }
058
059    /**
060     * Creates a {@link Queryable} object parameterized starting at first row and retrieve all rows, 
061     * isolation default, no timeout and online (no batch).
062     * 
063     * @param name query name
064     * @param params parameters of query
065     * @return Queryable object with parameters and unlimited result
066     */
067    public static Queryable of(String name, Object params)
068    {
069        NOT_NULL.verify(name, params);
070        return new QueryName(name, params, 0, Integer.MAX_VALUE);
071    }
072
073    /**
074     * Creates a {@link Queryable} object parameterized starting at first row and retrieve all rows, 
075     * isolation default, no timeout and online (no batch).
076     * 
077     * @param name query name
078     * @param params array of parameters
079     * @return Queryable object with parameters and unlimited result
080     */
081    public static Queryable ofArray(String name, Object... params)
082    {
083        NOT_NULL.verify(name, params);
084        return new QueryName(name, params, 0, Integer.MAX_VALUE);
085    }
086
087    public static Queryable ofArray(String name, RegisterType registerType, Object... params)
088    {
089        NOT_NULL.verify(name, registerType, params);
090        return new QueryName(name, params, 0, Integer.MAX_VALUE, registerType);
091    }
092
093    /**
094     * Build a new {@code Queryable} object
095     * 
096     * @param name a name for query
097     * @param params parameters from query
098     * @param offset the first row
099     * @param max row numbers
100     * @return Queryable object with parameters and limited result starting at {@code offset} and {@code max} rows.
101     */
102    public static Queryable of(String name, Object params, int offset, int max)
103    {
104        NOT_NULL.verify(name, name, params);
105        return new QueryName(name, params, offset, max);
106    }
107
108    /**
109     * Build a new {@code Queryable} object without parameters
110     * 
111     * @param name a name for query
112     * @param offset the first row
113     * @param max row numbers
114     * @return Queryable object with parameters and limited result starting at {@code offset} and {@code max} rows.
115     */
116    public static Queryable of(String name, int offset, int max)
117    {
118        NOT_NULL.verify(name);
119        return new QueryName(name, null, offset, max);
120    }
121
122    /**
123     * Build a new {@code Queryable} object
124     * @param name query name
125     * @param returnType return type that overload return type from XML
126     * @param args dynamically arguments to create {@code Queryable}.
127     * <p>
128     * 1o first param it's key name and 2o your value
129     * <p>
130     * 3o it's key 4o your value and so on.
131     * @param <T> type of return type 
132     * @return {@link Queryable} object with parameters and unlimited result and specific return type
133     */
134    public static <T> Queryable of(String name, Class<T> returnType, Object... args)
135    {
136        NOT_NULL.verify(name, returnType, args);
137        QueryName q = (QueryName) buildQueryable(name, 0, Integer.MAX_VALUE, null, args);
138        q.setReturnType(returnType);
139        return q;
140    }
141
142    /**
143     * Creates a {@link Queryable} object parameterized starting at first row and retrieve all rows, 
144     * isolation default, no timeout and online (no batch).
145     * 
146     * @param name query name
147     * @param returnType return type that overload return type from XML
148     * @param params parameters of query
149     * @param <T> class type of overload return
150     * @return {@link Queryable} object with parameters and unlimited result
151     */
152    public static <T> Queryable of(String name, Class<T> returnType, Object params)
153    {
154        NOT_NULL.verify(name, returnType, params);
155        QueryName q = new QueryName(name, params);
156        q.setReturnType(returnType);
157        return q;
158    }
159    
160    /**
161     * Build a new {@link Queryable} object
162     * @param name query name
163     * @param returnType return type that overload return type from XML
164     * @param <T> type of return type 
165     * @return {@link Queryable} object with parameters and unlimited result and specific return type
166     */
167    public static <T> Queryable of(String name, Class<T> returnType)
168    {
169        NOT_NULL.verify(name, returnType);
170        QueryName q = new QueryName(name);
171        q.setReturnType(returnType);
172        return q;
173    }
174 
175    public static <T> Queryable clone(Queryable queryable)
176    {
177        NOT_NULL.verify(queryable);
178        return clone(queryable.getName(), queryable, queryable.getParams(), queryable.getRegisterType(), null);
179    }
180
181    /**
182     * Clone {@code queryable} object with a return type if no {@code null}
183     * @param queryable query name
184     * @param returnType type of return that overload return type from XML
185     * @param <T> type of return type 
186     * @return clone of Queryable instance
187     */
188    public static <T> Queryable clone(Queryable queryable, Class<T> returnType)
189    {
190        NOT_NULL.verify(queryable, returnType);
191        return clone(queryable.getName(), queryable, queryable.getParams(), queryable.getRegisterType(), returnType);
192    }
193
194    /**
195     * Clone {@code queryable} object with a return type if no {@code null}
196     * @param queryable query name
197     * @param registerType registry of type data
198     * @param <T> type of return type 
199     * @return clone of Queryable instance
200     */
201    public static <T> Queryable clone(Queryable queryable, RegisterType registerType)
202    {
203        NOT_NULL.verify(queryable, registerType);
204        return clone(queryable.getName(), queryable, queryable.getParams(), registerType, null);
205    }
206
207    public static <T> Queryable clone(Queryable queryable, RegisterType registerType, Class<T> returnType)
208    {
209        NOT_NULL.verify(queryable, registerType);
210        return clone(queryable.getName(), queryable, queryable.getParams(), registerType, returnType);
211    }
212    
213    /**
214     * Clone {@code queryable} object with a return type if no {@code null}
215     * @param queryName of new query
216     * @param queryable query instance
217     * @param params parameter of new query
218     * @param registerType registry of type data
219     * @param returnType type of return that overload return type from XML
220     * @param <T> type of return type 
221     * @return new instance of Queryable instance
222     */
223    public static <T> Queryable clone(String queryName, Queryable queryable, Object params, RegisterType registerType, Class<T> returnType)
224    {
225        QueryName q = new QueryName(queryName, params, queryable.getOffset(), queryable.getMax(), registerType);
226        
227        if (returnType != null)
228            q.setReturnType(returnType);
229        else if (queryable.hasReturnType())
230            q.setReturnType(queryable.getReturnType());
231        
232        if(queryable.isCacheIgnore())
233            q.cacheIgnore();
234        
235        if(queryable.isScalar())
236            q.scalar();
237        
238        q.setSorter(queryable.getSorter());
239        q.setFilter(queryable.getFilter());
240        q.setBookmark(queryable.getBookmark());
241        return q;
242    }
243
244//  public static Queryable of(String name, List params)
245//  {
246//      return new QueryName(name, params, 0, Integer.MAX_VALUE);
247//  }
248//  
249//  /**
250//   * Build a new {@code Queryable} object
251//   * @param name query name
252//   * @param offset the first row
253//   * @param max row numbers
254//   * @param args dynamically arguments to create {@code Queryable}.
255//   * <p>
256//   * 1o first param it's key name and 2o your value
257//   * <p>
258//   * 3o it's key 4o your value and so on.
259//   * @return Queryable object with parameters with limited result by {@code max}
260//   */
261//  public static Queryable of(String name, int offset, int max, Object... args)
262//  {
263//      QueryName q = (QueryName) buildQueryable(name, args);
264//      q.setOffset(offset);
265//      q.setMax(max);
266//      return q;
267//  }
268    /*
269     * @param name query name
270     * @param returnType type of return that overload return type from XML
271     * @param offset the first row
272     * @param max row numbers
273     * @param args dynamically arguments to create {@code Queryable}.
274     * @param <T> type of return type 
275     * <p>
276     * 1o first param it's key name and 2o your value
277     * <p>
278     * 3o it's key 4o your value and so on.
279     * @return Queryable object with parameters and limited result starting at {@code offset} and {@code max} rows. 
280     *
281    public static <T> Queryable of(String name, Class<T> returnType, int offset, int max, Object... args)
282    {
283        QueryName q = (QueryName) buildQueryable(name, args);
284        q.setOffset(offset);
285        q.setMax(max);
286        q.setReturnType(returnType);
287        return q;
288    }
289   */
290    
291    /**
292     * Create new instance of {@code Queryable} without out pagination.
293     * @param name query name
294     * @param args dynamically arguments to create {@code queryable}.
295     * <p>
296     * 1º first param it's name of {@code queryable} object
297     * <p>
298     * 2º and 3º are pair of name and value of first parameter and so on 4º/5º are second, etc.
299     * @return new instance of Queryable with your parameters
300     */
301    private static Queryable buildQueryable(String name, int offset, int max, RegisterType registerType, Object... args)
302    {
303        NOT_NULL.verify(args);
304        Queryable queryable = null;
305        if (args.length == 1)
306        {
307            queryable = new QueryName(name, args[0], offset, max, registerType);
308        }
309        else
310        {
311            Map<String, Object> params = buildParams(args);
312            if (params.size() > 0 )
313                queryable = new QueryName(name, params, offset, max, registerType);
314            else
315                queryable = new QueryName(name, null, offset, max, registerType);
316        }
317        return queryable;
318    }
319    
320    static Map<String, Object> buildParams(Object... args)
321    {
322        Map<String, Object> params = new HashMap<String, Object>();
323        int i = 0;
324        Object value = null;
325        String key = null;
326        for (Object o : args)
327        {
328            if (i % 2 == 0)
329            {
330                key = o.toString();
331            }
332            else
333            {
334                value = o;
335                params.put(key, value);
336                key = null;
337                value = null;
338            }
339            i++;
340        }
341        return params;
342    }
343    
344    /* *********************************************************************************************************************
345     *                   ==============>    B    U    I    L    D    E    R     <============                              *
346     * *********************************************************************************************************************/
347
348    public static Builder builder()
349    {
350        return new Builder();
351    }
352    
353    public static class Builder
354    {
355        private Map<String, Object> mapParams = new HashMap<String, Object>();
356        private Object[] arrayOfParams;
357        private Object params;
358        private int offset;
359        private int max = Integer.MAX_VALUE;
360        private boolean scalar = false;
361        private RegisterType registerType;
362        private Class<?>     returnType;
363        private Comparator<?> sorter;
364        private Filter<?> filter;
365        
366        public Queryable build(String name) {
367            QueryName q = null;
368            if (arrayOfParams != null)
369                q = new QueryName(name, arrayOfParams, offset, max, registerType);
370            else if (params != null)
371                q = new QueryName(name, params, offset, max, registerType);
372            else if (!mapParams.isEmpty())
373                q = new QueryName(name, mapParams, offset, max, registerType);
374            else
375                q = new QueryName(name, null, offset, max, registerType);
376                
377            q.setReturnType(returnType);
378            q.setSorter(sorter);
379            q.setFilter(filter);
380            if(this.scalar)
381                q.scalar();
382            return q;
383        }
384        /**
385         * Build the query parameters as Map
386         * <p> <b>note:</b> don't mix the set {@code param} values and {@code ofArray} </p>
387         * @param params dynamically created
388         * <p>
389         * 1o first param it's key name and 2o your value
390         * <p>
391         * 3o it's key 4o your value and so on.
392         * @return this builder instance
393         */
394        public Builder params(Object... params) {
395            this.mapParams = QueryFactory.buildParams(params);
396            return this;
397        }
398        /**
399         * Build the query parameters as Map.
400         * <p> <b>note:</b> don't mix the set {@code param} values and {@code ofArray} </p>
401         * @param name parameter
402         * @param value parameter
403         * @return this builder instance
404         */
405        public Builder params(String name, Object value) {
406            this.mapParams.put(name, value);
407            return this;
408        }
409        /**
410         * Build the query parameters as POJO
411         * <p> <b>note:</b> don't mix the set {@code param} values and {@code ofArray} </p>
412         * @param <T> Type of class that represents the parameters
413         * @param param instance of class the represents the parameters
414         * @return this builder instance
415         */
416        public <T> Builder params(T param) {
417            this.params = param;
418            return this;
419        }
420        /**
421         * Build the query parameters as array of values.
422         * <p> <b>note:</b> don't mix the set {@code param} values and {@code ofArray} </p>
423         * @param params array of parameters
424         * @return this builder instance
425         */
426        public Builder ofArray(Object... params) {
427            this.arrayOfParams = params;
428            return this;
429        }
430        public Builder offset(int offset) {
431            this.offset = offset;
432            return this;
433        }
434        public Builder max(int max) {
435            this.max = max;
436            return this;
437        }
438        public Builder registerType(RegisterType registerType) {
439            this.registerType = registerType;
440            return this;
441        }
442        public Builder returnType(Class<?> returnType) {
443            this.returnType = returnType;
444            return this;
445        }
446        public Builder sorter(Comparator<?> sorter) {
447            this.sorter = sorter;
448            return this;
449        }
450        public Builder filter(Filter<?> filter) {
451            this.filter = filter;
452            return this;
453        }
454        public Builder scalar() {
455            this.scalar = true;
456            return this;
457        }
458    }
459}