001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.krad.service.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.datetime.DateTimeService;
020import org.kuali.rice.krad.bo.BusinessObject;
021import org.kuali.rice.krad.bo.InactivatableFromTo;
022import org.kuali.rice.krad.data.KradDataServiceLocator;
023import org.kuali.rice.krad.service.DataDictionaryService;
024import org.kuali.rice.krad.service.InactivateableFromToService;
025import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
026import org.kuali.rice.krad.service.LookupService;
027import org.kuali.rice.krad.util.BeanPropertyComparator;
028import org.kuali.rice.krad.util.KRADPropertyConstants;
029
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.Date;
033import java.util.List;
034import 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 */
041public 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 = KradDataServiceLocator.getDataObjectService().wrap(businessObject).getPropertyValueNullSafe(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}