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     */
022    package org.granite.tide.spring.data;
023    
024    import java.beans.BeanInfo;
025    import java.beans.Introspector;
026    import java.beans.PropertyDescriptor;
027    import java.util.ArrayList;
028    import java.util.List;
029    
030    import javax.persistence.criteria.CriteriaBuilder;
031    import javax.persistence.criteria.CriteriaQuery;
032    import javax.persistence.criteria.Predicate;
033    import javax.persistence.criteria.Root;
034    
035    import org.springframework.data.jpa.domain.Specification;
036    
037    public 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    }