001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.tide.spring.data;
023
024import java.beans.BeanInfo;
025import java.beans.Introspector;
026import java.beans.PropertyDescriptor;
027import java.util.ArrayList;
028import java.util.List;
029
030import javax.persistence.criteria.CriteriaBuilder;
031import javax.persistence.criteria.CriteriaQuery;
032import javax.persistence.criteria.Predicate;
033import javax.persistence.criteria.Root;
034
035import org.springframework.data.jpa.domain.Specification;
036
037public class FilterBeanSpecification<T> implements Specification<T> {
038        
039        private Object filter;
040        
041        private FilterBeanSpecification(Object filter) {
042                this.filter = filter;
043        }
044        
045        public static <T> FilterBeanSpecification<T> byBean(Object filter) {
046                return new FilterBeanSpecification<T>(filter);
047        }
048        
049        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
050                List<Predicate> predicates = new ArrayList<Predicate>();
051                
052                // Query by bean filter
053                PropertyDescriptor[] pds = null;
054                try {
055                        BeanInfo info = Introspector.getBeanInfo(filter.getClass());
056                        pds = info.getPropertyDescriptors();
057                }
058                catch (Exception e) {
059                        throw new RuntimeException("Could not introspect filter bean", e);
060                }
061                
062                for (PropertyDescriptor pd : pds) {
063                        if (pd.getWriteMethod() == null || !pd.getReadMethod().isAnnotationPresent(FilterMapping.class) || pd.getReadMethod().getAnnotation(FilterMapping.class).mode() == FilterMode.EXCLUDE)
064                                continue;
065                        
066                        FilterMapping mapping = pd.getReadMethod().getAnnotation(FilterMapping.class);
067                        if (root.get(mapping.value()) == null)
068                                throw new RuntimeException("Invalid filter mapping, path: " + mapping.value());
069                                        
070                        Object value = null;
071                        try {
072                                value = pd.getReadMethod().invoke(filter);
073                        }
074                        catch (Exception e) {
075                                throw new RuntimeException("Could not get filter property " + pd.getName(), e);
076                        }
077                        
078                        Predicate predicate = FilterSpecUtil.buildPredicate(root, builder, pd.getReadMethod().getReturnType(), mapping.value(), value);
079                        if (predicate != null)
080                                predicates.add(predicate);
081                }
082                
083                if (predicates.size() > 0)
084                        return builder.and(predicates.toArray(new Predicate[predicates.size()]));
085                
086                return null;
087        }
088}