001// license-header java merge-point
002//
003// Attention: Generated code! Do not modify by hand!
004// Generated by: hibernate/search/PropertySearch.java.vsl in andromda-spring-cartridge.
005//
006package fr.ifremer.adagio.core.dao;
007
008/*
009 * #%L
010 * SIH-Adagio :: Core
011 * $Id:$
012 * $HeadURL:$
013 * %%
014 * Copyright (C) 2012 - 2014 Ifremer
015 * %%
016 * This program is free software: you can redistribute it and/or modify
017 * it under the terms of the GNU Affero General Public License as published by
018 * the Free Software Foundation, either version 3 of the License, or
019 * (at your option) any later version.
020 * 
021 * This program is distributed in the hope that it will be useful,
022 * but WITHOUT ANY WARRANTY; without even the implied warranty of
023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
024 * GNU General Public License for more details.
025 * 
026 * You should have received a copy of the GNU Affero General Public License
027 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
028 * #L%
029 */
030
031import java.util.Calendar;
032import java.util.Date;
033import java.util.List;
034import java.util.StringTokenizer;
035import org.apache.commons.beanutils.ConversionException;
036import org.apache.commons.beanutils.ConvertUtils;
037import org.apache.commons.beanutils.Converter;
038import org.apache.commons.beanutils.PropertyUtils;
039import org.apache.commons.beanutils.converters.IntegerConverter;
040import org.apache.commons.beanutils.converters.LongConverter;
041import org.apache.commons.beanutils.converters.ShortConverter;
042import org.hibernate.HibernateException;
043import org.hibernate.Session;
044import org.hibernate.criterion.MatchMode;
045import org.hibernate.criterion.Projections;
046
047/**
048 * Provides the ability to search by properties.
049 */
050@SuppressWarnings({"unchecked"})
051public class PropertySearch
052    extends CriteriaSearch
053{
054    private Search search;
055
056    /**
057     * Constructor for PropertySearch. Creates a <code>PropertySearch</code> instance
058     * from the given arguments.
059     *
060     * @param session The Hibernate session.
061     * @param entityType The <code>Class</code> of the result.
062     * @param searchIn the object that specifies the search criteria.
063     */
064    public PropertySearch(
065        final Session session,
066        final Class entityType,
067        final Search searchIn)
068    {
069        super(session, entityType);
070        this.search = searchIn;
071        try
072        {
073            this.initializeConverters();
074            this.getConfiguration().setForceEagerLoading(searchIn.isEagerFetching());
075            final SearchParameter[] parameters = searchIn.getParameters();
076            if (parameters != null)
077            {
078                for (int ctr = 0; ctr < parameters.length; ctr++)
079                {
080                    final SearchParameter searchParameter = parameters[ctr];
081
082                    Object value;
083                    switch (searchParameter.getComparator())
084                    {
085                        case SearchParameter.IN_COMPARATOR :
086                            value = searchParameter.getValue();
087                            break;
088                        case SearchParameter.NOT_IN_COMPARATOR :
089                            value = searchParameter.getValue();
090                            break;
091                        default :
092                            value = this.getValue(entityType, searchParameter);
093                            break;
094                    }
095
096                    // - now add the parameter.
097                    final CriteriaSearchParameter parameter =
098                        new CriteriaSearchParameter(value,
099                            searchParameter.getName(),
100                            searchParameter.getComparator());
101                    parameter.setOrderDirection(searchParameter.getOrder());
102                    parameter.setSearchIfNull(searchParameter.isSearchIfNull());
103                    switch (searchParameter.getMatch())
104                    {
105                        case SearchParameter.MATCH_ANYWHERE :
106                            parameter.setMatchMode(MatchMode.ANYWHERE);
107                            break;
108                        case SearchParameter.MATCH_START :
109                            parameter.setMatchMode(MatchMode.START);
110                            break;
111                        case SearchParameter.MATCH_END :
112                            parameter.setMatchMode(MatchMode.END);
113                            break;
114                        default:
115                            parameter.setMatchMode(MatchMode.EXACT);
116                            break;
117                    }
118                    this.addParameter(parameter);
119                    if (searchIn.isUseSqlLimiting())
120                    {
121                        if (searchIn.getPageNumber() > 0 && searchIn.getPageSize() > 0)
122                        {
123                            // - set the first result as part of pagination support
124                            this.getConfiguration().setFirstResult(new Integer(this.calculateFirstResult(searchIn.getPageNumber(), searchIn.getPageSize())));
125                            this.getConfiguration().setMaximumResultSize(new Integer(searchIn.getPageSize()));
126                        }
127                    }
128                }
129            }
130        }
131        catch (Exception exception)
132        {
133            throw new RuntimeException(exception);
134        }
135    }
136
137    /**
138     * Stores the total count when sql limiting isn't used.
139     */
140    private int totalCount;
141
142    /**
143     * Gets the total possible count of objects returned in this search.
144     * @return totalCount
145     */
146    public int getTotalCount()
147    {
148        int count;
149        if (this.search.isUseSqlLimiting())
150        {
151            // Remove first result requirement
152            this.getConfiguration().setFirstResult(new Integer(0));
153            this.getRootCriteria().setProjection(
154                Projections.projectionList().add(
155                    Projections.rowCount()));
156            count = ((Long)this.executeAsList().iterator().next()).intValue();
157        }
158        else
159        {
160            count = this.totalCount;
161        }
162        return count;
163    }
164
165    /**
166     * @see CriteriaSearch#executeAsList()
167     */
168    @Override
169    public List executeAsList()
170        throws HibernateException
171    {
172        List results = super.executeAsList();
173        if (!this.search.isUseSqlLimiting() && this.search.getPageNumber() > 0 && this.search.getPageSize() > 0)
174        {
175            int start = this.calculateFirstResult(this.search.getPageNumber(), this.search.getPageSize());
176            int end = this.calculateLastResult(this.search.getPageNumber(), this.search.getPageSize());
177            this.totalCount = results.size();
178            if (this.totalCount < start)
179            {
180                start = this.totalCount;
181            }
182            if (this.totalCount < end)
183            {
184                end = this.totalCount;
185            }
186            results = results.subList(start, end);
187        }
188        return results;
189    }
190
191    /**
192     * Calculates the first result based upon page size and current
193     * desired page.
194     *
195     * @param pageNumber the page number to retrieve.
196     * @param pageSize the page size to retrieve.
197     *
198     * @return the calculated first result.
199     */
200    private int calculateFirstResult(int pageNumber, int pageSize)
201    {
202        int firstResult = 0;
203        if (pageNumber > 0 && pageSize > 0)
204        {
205            firstResult = (pageNumber - 1) * pageSize;
206        }
207        return firstResult;
208    }
209
210    /**
211     * Calculates the last result based upon page size and current
212     * desired page.
213     *
214     * @param pageNumber the page number to retrieve.
215     * @param pageSize the page size to retrieve.
216     *
217     * @return the calculated first result.
218     */
219    private int calculateLastResult(int pageNumber, int pageSize)
220    {
221        int lastResult = 0;
222        if (pageNumber > 0 && pageSize > 0)
223        {
224            lastResult = pageNumber * pageSize;
225        }
226        return lastResult;
227    }
228
229    private static final String PERIOD = ".";
230
231    /**
232     * Initializes the converters to behave the way we want when converting values (we don't
233     * want empty strings converted to zeros, like beanutils does by default)
234     */
235    private void initializeConverters()
236    {
237        ConvertUtils.register(new LongConverter(null), Long.class);
238        ConvertUtils.register(new IntegerConverter(null), Integer.class);
239        ConvertUtils.register(new ShortConverter(null), Short.class);
240        ConvertUtils.register(new CalendarConverter(), Calendar.class);
241        ConvertUtils.register(new DateConverter(), Date.class);
242    }
243
244    /**
245     * <p>Convert the value to an object of the specified class (if
246     * possible).</p>
247     *
248     * @param value Value to be converted (may be null)
249     * @param type Class of the value to be converted to
250     * @return The converted value
251     *
252     * @exception ConversionException if thrown by an underlying Converter
253     */
254    protected Object convert(Object value, Class type)
255    {
256        Converter converter = ConvertUtils.lookup(type);
257        Object result;
258        if (converter != null)
259        {
260            result = converter.convert(type, value);
261        }
262        else
263        {
264            result = value;
265        }
266        return result;
267    }
268
269    /**
270     * Converts the value contained within the parameter to the type which Hibernate expects.
271     *
272     * @param entityType the class of the entity for which the search is being performed.
273     * @param parameter the parameter from which to get the value.
274     * @return the appropriate value.
275     */
276    private Object getValue(Class type, final SearchParameter parameter)
277    {
278        try
279        {
280            Object value = parameter.getValue();
281            // - don't try to convert null values or classes
282            if (value != null && !value.getClass().equals(Class.class))
283            {
284                Class propertyType = type;
285                for (final StringTokenizer tokenizer = new StringTokenizer(parameter.getName(), PERIOD); tokenizer.hasMoreTokens();)
286                {
287                    final String token = tokenizer.nextToken().trim();
288                    Class lastType = type;
289                    type = CriteriaSearchProperties.getPropertyType(type, token);
290                    if (!tokenizer.hasMoreTokens())
291                    {
292                        break;
293                    }
294                    if (type == null)
295                    {
296                        throw new RuntimeException("No accessible property named '" + token + "', exists on: " + lastType.getName());
297                    }
298                    propertyType = type;
299                }
300                final Object object = propertyType.newInstance();
301                final String name = parameter.getName().replaceAll(".*\\" + PERIOD, "");
302                try
303                {
304                    value = convert(value, PropertyUtils.getPropertyType(object, name));
305                }
306                catch (final NoSuchMethodException noSuchMethodException)
307                {
308                    throw new RuntimeException("No accessible property named '" + name + "', exists on: " + propertyType.getName());
309                }
310            }
311            return value;
312        }
313        catch (final Exception exception)
314        {
315            throw new RuntimeException(exception);
316        }
317    }
318
319    /**
320     * A beanutils converter that converts Calendar objects to Date instances.
321     */
322    private static final class DateConverter
323        implements Converter
324    {
325        public DateConverter()
326        {
327            // Empty block comment
328        }
329
330        /**
331         * @see org.apache.commons.beanutils.Converter#convert(Class, Object)
332         */
333        public Object convert(
334            Class type,
335            Object value)
336            throws ConversionException
337        {
338            if (value != null)
339            {
340                try
341                {
342                    if (value instanceof Calendar)
343                    {
344                        value = ((Calendar)value).getTime();
345                    }
346                }
347                catch (Exception ex)
348                {
349                    throw new ConversionException(ex);
350                }
351            }
352            return value;
353        }
354    }
355
356    /**
357     * A beanutils converter that converts Date objects to Calendar instances.
358     */
359    private static final class CalendarConverter
360        implements Converter
361    {
362        public CalendarConverter()
363        {
364            // Empty block comment
365        }
366
367        /**
368         * @see org.apache.commons.beanutils.Converter#convert(Class, Object)
369         */
370        public Object convert(
371            Class type,
372            Object value)
373            throws ConversionException
374        {
375            if (value != null)
376            {
377                try
378                {
379                    if (value instanceof Date)
380                    {
381                        Calendar calendar = Calendar.getInstance();
382                        calendar.setTime((Date)value);
383                        value = calendar;
384                    }
385                }
386                catch (Exception ex)
387                {
388                    throw new ConversionException(ex);
389                }
390            }
391            return value;
392        }
393    }
394}