package net.databinder.models.hib;
/*---
Copyright 2008 The Scripps Research Institute
http://www.scripps.edu
* Databinder: a simple bridge from Wicket to Hibernate
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
---*/
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.wicket.Application;
import org.apache.wicket.Session;
import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.IFilterStateLocator;
import org.apache.wicket.util.convert.ConversionException;
import org.apache.wicket.util.lang.PropertyResolver;
import org.apache.wicket.util.lang.PropertyResolverConverter;
import org.hibernate.Criteria;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
/**
* An OrderingCriteriaBuilder implementation that can be wired to a FilterToolbar
* String properties are searched via an iLike. Number properties can specify >, >=, < or <=
*
* Example usage (from baseball player example);
*
* CriteriaFilterAndSort builder = new CriteriaFilterAndSort(new Player(), "nameLast", true, false);
* FilterForm form = new FilterForm("form", builder);
* HibernateProvider provider = new HibernateProvider(Player.class, builder);
* provider.setWrapWithPropertyModel(false);
* DataTable table = new DataTable("players", columns, provider, 25) {
* @Override protected Item newRowItem(String id, int index, IModel model) {
* return new OddEvenItem(id, index, model);
* }
* };
* table.addTopToolbar(new AjaxNavigationToolbar(table));
* table.addTopToolbar(new FilterToolbar(table, form, builder));
* table.addTopToolbar(new AjaxFallbackHeadersToolbar(table, builder));
*
* @author Mark Southern
*/
public class CriteriaFilterAndSort extends CriteriaBuildAndSort implements IFilterStateLocator {
// whitespace, a qualifier, a number surrounded by whitespace
private Pattern pattern = Pattern.compile("^(\\s+)?([><]=?)(\\s+)?(.*)(\\s+)?");
private Map<String, String> filterMap = new HashMap<String, String>();
private Object bean;
public CriteriaFilterAndSort(Object bean, String defaultSortProperty, boolean sortAscending, boolean sortCased) {
super(defaultSortProperty, sortAscending, sortCased);
this.bean = bean;
}
public void buildUnordered(Criteria criteria) {
super.buildUnordered(criteria);
Conjunction conj = Restrictions.conjunction();
for (Map.Entry<String, String> entry : (Set<Map.Entry<String, String>>) filterMap.entrySet()) {
// System.out.println(String.format("%s\t%s", entry.getKey(), entry.getValue()));
String property = entry.getKey();
String value = entry.getValue();
if (value == null)
continue;
String prop = processProperty(criteria, property);
Class clazz = PropertyResolver.getPropertyClass(property, bean);
if (String.class.isAssignableFrom(clazz)) {
String[] items = value.split("\\s+");
for (String item : items) {
Disjunction dist = Restrictions.disjunction();
dist.add(Restrictions.ilike(prop, item, MatchMode.ANYWHERE));
conj.add(dist);
}
}
else if (Number.class.isAssignableFrom(clazz)) {
try {
Matcher matcher = pattern.matcher(value);
if (matcher.matches()) {
String qualifier = matcher.group(2);
value = matcher.group(4);
Number num = convertToNumber(value, clazz);
if (">".equals(qualifier))
conj.add(Restrictions.gt(prop, num));
else if ("<".equals(qualifier))
conj.add(Restrictions.lt(prop, num));
else if (">=".equals(qualifier))
conj.add(Restrictions.ge(prop, num));
else if ("<=".equals(qualifier))
conj.add(Restrictions.le(prop, num));
}
else
conj.add(Restrictions.eq(prop, convertToNumber(value, clazz)));
}
catch(ConversionException ex) {
// ignore filter in this case
}
}
else if (Boolean.class.isAssignableFrom(clazz)) {
conj.add(Restrictions.eq(prop, Boolean.parseBoolean(value)));
}
}
criteria.add(conj);
}
protected Number convertToNumber(String value, Class clazz) {
return (Number)
new PropertyResolverConverter(Application.get().getConverterLocator(), Session.get().getLocale())
.convert(value, clazz);
}
public Object getFilterState() {
return filterMap;
}
@SuppressWarnings("unchecked")
public void setFilterState(Object filterMap) {
this.filterMap = (Map) filterMap;
}
}