001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.service.impl;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.core.api.datetime.DateTimeService;
020    import org.kuali.rice.krad.bo.BusinessObject;
021    import org.kuali.rice.krad.bo.InactivatableFromTo;
022    import org.kuali.rice.krad.service.DataDictionaryService;
023    import org.kuali.rice.krad.service.InactivateableFromToService;
024    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
025    import org.kuali.rice.krad.service.LookupService;
026    import org.kuali.rice.krad.util.BeanPropertyComparator;
027    import org.kuali.rice.krad.util.KRADPropertyConstants;
028    import org.kuali.rice.krad.util.ObjectUtils;
029    
030    import java.util.ArrayList;
031    import java.util.Collections;
032    import java.util.Date;
033    import java.util.List;
034    import java.util.Map;
035    
036    /**
037     * Implementation of InactivateableFromToService that uses the lookup service for query implementation
038     * 
039     * @see org.kuali.rice.krad.service.InactivateableFromToService
040     */
041    public class InactivateableFromToServiceImpl implements InactivateableFromToService {
042    
043            protected DateTimeService dateTimeService;
044            protected LookupService lookupService;
045        protected DataDictionaryService dataDictionaryService;
046    
047            /**
048             * Uses lookup service which will convert the active criteria to active begin/to field criteria
049             * 
050             * @see org.kuali.rice.krad.service.InactivateableFromToService#findMatchingActive(java.lang.Class, java.util.Map)
051             */
052            public List<InactivatableFromTo> findMatchingActive(Class<? extends InactivatableFromTo> clazz, Map fieldValues) {
053                    fieldValues.put(KRADPropertyConstants.ACTIVE, "true");
054    
055                    return (List<InactivatableFromTo>) lookupService.findCollectionBySearchUnbounded(clazz, fieldValues);
056            }
057    
058            /**
059             * Uses lookup service which will convert the active criteria to active begin/to field criteria
060             * 
061             * @see org.kuali.rice.krad.service.InactivateableFromToService#findMatchingActiveAsOfDate(java.lang.Class, java.util.Map,
062             *      java.util.Date)
063             */
064            public List<InactivatableFromTo> findMatchingActiveAsOfDate(Class<? extends InactivatableFromTo> clazz,
065                            Map fieldValues, Date activeAsOfDate) {
066                    fieldValues.put(KRADPropertyConstants.ACTIVE, "true");
067                    fieldValues.put(KRADPropertyConstants.ACTIVE_AS_OF_DATE, dateTimeService.toDateString(activeAsOfDate));
068    
069                    return (List<InactivatableFromTo>) lookupService.findCollectionBySearchUnbounded(clazz, fieldValues);
070            }
071    
072            /**
073             * @see org.kuali.rice.krad.service.InactivateableFromToService#filterOutNonActive(java.util.List)
074             */
075            public List<InactivatableFromTo> filterOutNonActive(List<InactivatableFromTo> filterList) {
076                    return filterOutNonActive(filterList, dateTimeService.getCurrentDate());
077            }
078    
079            /**
080             * @see org.kuali.rice.krad.service.InactivateableFromToService#filterOutNonActive(java.util.List, java.util.Date)
081             */
082            public List<InactivatableFromTo> filterOutNonActive(List<InactivatableFromTo> filterList, Date activeAsOfDate) {
083                    List<InactivatableFromTo> filteredList = new ArrayList<InactivatableFromTo>();
084    
085                    for (InactivatableFromTo inactivateable : filterList) {
086                            inactivateable.setActiveAsOfDate(new java.sql.Timestamp(activeAsOfDate.getTime()));
087                            if (inactivateable.isActive()) {
088                                    filteredList.add(inactivateable);
089                            }
090                    }
091    
092                    return filteredList;
093            }
094    
095            /**
096             * Uses lookup service which will convert the active and current criteria to active begin/to field criteria
097             * 
098             * @see org.kuali.rice.krad.service.InactivateableFromToService#findMatchingCurrent(java.lang.Class, java.util.Map)
099             */
100            public List<InactivatableFromTo> findMatchingCurrent(Class<? extends InactivatableFromTo> clazz,
101                            Map fieldValues) {
102                    fieldValues.put(KRADPropertyConstants.ACTIVE, "true");
103                    fieldValues.put(KRADPropertyConstants.CURRENT, "true");
104    
105                    return (List<InactivatableFromTo>) lookupService.findCollectionBySearchUnbounded(clazz, fieldValues);
106            }
107    
108            /**
109             * Uses lookup service which will convert the active and current criteria to active begin/to field criteria
110             * 
111             * @see org.kuali.rice.krad.service.InactivateableFromToService#findMatchingCurrent(java.lang.Class, java.util.Map, java.util.Date)
112             */
113            public List<InactivatableFromTo> findMatchingCurrent(Class<? extends InactivatableFromTo> clazz,
114                            Map fieldValues, Date currentAsOfDate) {
115                    fieldValues.put(KRADPropertyConstants.ACTIVE, "true");
116                    fieldValues.put(KRADPropertyConstants.CURRENT, "true");
117                    fieldValues.put(KRADPropertyConstants.ACTIVE_AS_OF_DATE, dateTimeService.toDateString(currentAsOfDate));
118    
119                    return (List<InactivatableFromTo>) lookupService.findCollectionBySearchUnbounded(clazz, fieldValues);
120            }
121    
122            /**
123             * @see org.kuali.rice.krad.service.InactivateableFromToService#filterOutNonCurrent(java.util.List)
124             */
125            public List<InactivatableFromTo> filterOutNonCurrent(List<InactivatableFromTo> filterList) {
126                    return filterOutNonCurrent(filterList, dateTimeService.getCurrentDate());
127            }
128    
129            /**
130             * @see org.kuali.rice.krad.service.InactivateableFromToService#filterOutNonCurrent(java.util.List, java.util.Date)
131             */
132            public List<InactivatableFromTo> filterOutNonCurrent(List<InactivatableFromTo> filterList, Date currentAsOfDate) {
133                    List<InactivatableFromTo> activeList = filterOutNonActive(filterList, currentAsOfDate);
134    
135                    if (activeList.isEmpty()) {
136                            return activeList;
137                    }
138    
139                    List<InactivatableFromTo> currentList = new ArrayList<InactivatableFromTo>();
140    
141                    List<String> groupByList = getDataDictionaryService().getGroupByAttributesForEffectiveDating(
142                    activeList.get(0).getClass());
143                    if (groupByList != null) {
144                            List<String> sortByList = new ArrayList<String>(groupByList);
145                            sortByList.add(KRADPropertyConstants.ACTIVE_FROM_DATE);
146    
147                            // sort list so we get records together with same group by
148                            Collections.sort(activeList, new BeanPropertyComparator(sortByList, true));
149    
150                            // reverse so we get max active begin date first within the group by
151                            Collections.reverse(activeList);
152    
153                            String previousGroupByString = "";
154                            Date previousActiveFromDate = null;
155                            for (InactivatableFromTo inactivateable : activeList) {
156                                    String groupByString = buildGroupByValueString((BusinessObject) inactivateable, groupByList);
157                                    if (!StringUtils.equals(groupByString, previousGroupByString)) {
158                                            // always add first record of new group by since list is sorted by active begin date descending
159                                            currentList.add(inactivateable);
160                                    }
161                                    // active from date should not be null here since we are dealing with only active records
162                                    else if (inactivateable.getActiveFromDate().equals(previousActiveFromDate)) {
163                                            // have more than one record for the group by key with same active begin date, so they both are current
164                                            currentList.add(inactivateable);
165                                    }
166    
167                                    previousGroupByString = groupByString;
168                                    previousActiveFromDate = inactivateable.getActiveFromDate();
169                            }
170                    } else {
171                            currentList = activeList;
172                    }
173    
174                    return currentList;
175            }
176    
177            /**
178             * Builds a string containing the values from the given business object for the fields in the given list, concatenated together using a
179             * bar. Null values are treated as an empty string
180             * 
181             * @param businessObject
182             *            - business object instance to get values from
183             * @param groupByList
184             *            - list of fields to get values for
185             * @return String
186             */
187            protected String buildGroupByValueString(BusinessObject businessObject, List<String> groupByList) {
188                    String groupByValueString = "";
189    
190                    for (String groupByField : groupByList) {
191                            Object fieldValue = ObjectUtils.getPropertyValue(businessObject, groupByField);
192                            groupByValueString += "|";
193                            if (fieldValue != null) {
194                                    groupByValueString += fieldValue;
195                            }
196                    }
197    
198                    return groupByValueString;
199            }
200    
201            public void setDateTimeService(DateTimeService dateTimeService) {
202                    this.dateTimeService = dateTimeService;
203            }
204    
205            public void setLookupService(LookupService lookupService) {
206                    this.lookupService = lookupService;
207            }
208    
209        protected DataDictionaryService getDataDictionaryService() {
210            if (dataDictionaryService == null) {
211                this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
212            }
213            return dataDictionaryService;
214        }
215    
216        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
217            this.dataDictionaryService = dataDictionaryService;
218        }
219    }