001    /*
002     * Copyright 2011 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.kfs.module.endow.document.service.impl;
017    
018    import java.sql.Date;
019    import java.util.Calendar;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.kfs.module.endow.EndowConstants;
025    import org.kuali.kfs.module.endow.businessobject.FrequencyCode;
026    import org.kuali.kfs.module.endow.document.service.FrequencyCodeService;
027    import org.kuali.kfs.module.endow.document.service.KEMService;
028    import org.kuali.rice.kns.service.BusinessObjectService;
029    import org.kuali.rice.kns.service.DateTimeService;
030    
031    public class FrequencyCodeServiceImpl implements FrequencyCodeService {
032        
033        protected BusinessObjectService businessObjectService;
034        protected DateTimeService dateTimeService;
035        protected KEMService kemService;
036        
037        /**
038         * @see org.kuali.kfs.module.endow.document.service.FrequencyCodeService#getByPrimaryKey(java.lang.String)
039         */
040        public FrequencyCode getByPrimaryKey(String code) {
041            FrequencyCode frequencyCode = null;
042            if (StringUtils.isNotBlank(code)) {
043                Map criteria = new HashMap();
044                criteria.put("code", code);
045    
046                frequencyCode = (FrequencyCode) businessObjectService.findByPrimaryKey(FrequencyCode.class, criteria);
047            }
048            
049            return frequencyCode;
050        }
051    
052        /**
053         * @see org.kuali.kfs.module.endow.document.service.FrequencyCodeService#calculateProcessDate(String)
054         */
055        public Date calculateProcessDate(String frequencyCode) {
056            Date currentDate = kemService.getCurrentDate();
057            
058            String frequencyType = frequencyCode.substring(0, 1);
059            
060            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.DAILY)) {
061                return currentDate;
062            }
063            
064            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.WEEKLY)) {
065                String dayOfWeek =  frequencyCode.substring(1, 4).toUpperCase();
066                return calculateNextWeeklyDate(dayOfWeek, currentDate);
067            }
068            
069            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_MONTHLY)) {
070                String dayOfSemiMonthly =  frequencyCode.substring(1, 3);
071                return calculateNextSemiMonthlyDate(dayOfSemiMonthly, currentDate);
072            }
073    
074            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.MONTHLY)) {
075                String dayOfMonth =  frequencyCode.substring(1, 3);
076                return calculateNextMonthlyDate(dayOfMonth, currentDate);
077            }
078    
079            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.QUARTERLY) || 
080                frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_ANNUALLY) ||
081                frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.ANNUALLY)) {
082                String month = frequencyCode.substring(1, 2);
083                String dayOfMonth =  frequencyCode.substring(2, 4);
084                return calculateNextQuarterlyOrSemiAnnuallyOrAnnuallyProcessDate(month, dayOfMonth, frequencyType, currentDate);
085            }
086            
087            return currentDate;
088        }
089        
090        /**
091         * Method to calculate the next processing week date based on the frequency type
092         * adds the appropriate number of days to the current date
093         * @param dayOfWeek
094         * @return next processing date
095         */
096        protected Date calculateNextWeeklyDate(String dayOfWeekFromFrequencyCode, Date currentDate) {
097            Calendar calendar = Calendar.getInstance();
098            calendar.setTime(currentDate);
099            
100            int daysToAdd = 0;
101            int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);  // today's day of the week
102            int maximumDaysInWeek = calendar.getActualMaximum(Calendar.DAY_OF_WEEK);
103                    
104            if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.MONDAY)) {
105                if (dayOfWeek < Calendar.MONDAY)
106                    daysToAdd = Calendar.MONDAY - dayOfWeek;
107                else
108                    daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.MONDAY;
109            } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.TUESDAY)) {
110                       if (dayOfWeek < Calendar.TUESDAY)
111                           daysToAdd = Calendar.TUESDAY - dayOfWeek;
112                       else
113                           daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.TUESDAY;
114                  } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.WEDNESDAY)) {
115                             if (dayOfWeek < Calendar.WEDNESDAY)
116                                 daysToAdd = Calendar.WEDNESDAY - dayOfWeek;
117                             else
118                                 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.WEDNESDAY;
119                    } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.THURSDAY)) {
120                               if (dayOfWeek < Calendar.THURSDAY)
121                                   daysToAdd = Calendar.THURSDAY - dayOfWeek;
122                               else   
123                                   daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.THURSDAY;
124                          } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.FRIDAY)) {
125                                     if (dayOfWeek < Calendar.FRIDAY)
126                                         daysToAdd = Calendar.FRIDAY - dayOfWeek;
127                                     else   
128                                         daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.FRIDAY;
129                            }
130    
131            calendar.add(Calendar.DAY_OF_MONTH, daysToAdd);
132            
133            return new java.sql.Date(calendar.getTimeInMillis());
134        }
135    
136        /**
137         * Method to calculate the next processing semi-monthly date based on the frequency type
138         * Sets the day of the month and then returns the processing date
139         * @param dayOfSemiMonthly
140         * @return next processing date
141         */
142        protected Date calculateNextSemiMonthlyDate(String dayOfSemiMonthly, Date currentDate) {
143            Calendar calendar = Calendar.getInstance();
144            calendar.setTime(currentDate);
145            
146            int dayOfMonthToSet = Integer.parseInt(dayOfSemiMonthly);
147            int dayOfMonthNextToSet = dayOfMonthToSet + 15;
148            
149            calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthToSet);
150            
151            if (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
152                calendar.add(Calendar.DAY_OF_MONTH, dayOfMonthNextToSet);
153            }
154            if (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
155                calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthToSet);
156                calendar.add(Calendar.MONTH, 1);
157            }
158            
159            return new java.sql.Date(calendar.getTimeInMillis());
160        }
161        
162        /**
163         * Method to calculate the next processing monthly date based on the frequency type
164         * Sets the day in the calendar based on day part of the frequency code.
165         * @param dayOfMonth
166         * @return next processing date
167         */
168        protected Date calculateNextMonthlyDate(String dayOfMonth, Date currentDate) {
169            int dayInMonthToSet;
170            
171            Calendar calendar = Calendar.getInstance();
172            calendar.setTime(currentDate);
173            setCalendarWithDays(calendar, dayOfMonth);
174            while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
175                calendar.add(Calendar.MONTH, 1);  
176            }
177            
178            return new java.sql.Date(calendar.getTimeInMillis());
179        }
180    
181        /**
182         * Method to calculate the next processing quarterly or semi-annually or annually date based on the frequency type
183         * Sets the day in the calendar based on day part of the frequency code.
184         * @param frequencyType frequency code for quarterly, month, dayOfMonth
185         * @return next processing date
186         */
187        protected Date calculateNextQuarterlyOrSemiAnnuallyOrAnnuallyProcessDate(String month, String dayOfMonth, String frequencyType, Date currentDate) {
188            Calendar calendar = Calendar.getInstance();
189            calendar.setTime(currentDate);
190            calendar = setCaledarWithMonth(month, currentDate);        
191            setCalendarWithDays(calendar, dayOfMonth);
192            
193            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.QUARTERLY)) {
194                while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
195                    calendar.add(Calendar.MONTH, 3);  
196                    setCalendarWithDays(calendar, dayOfMonth);
197                }
198            }
199            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_ANNUALLY)) { 
200                while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
201                    calendar.add(Calendar.MONTH, 6);  
202                    setCalendarWithDays(calendar, dayOfMonth);
203                }
204            }
205            if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.ANNUALLY)) { 
206                while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
207                    calendar.add(Calendar.MONTH, 12); 
208                    setCalendarWithDays(calendar, dayOfMonth);
209                }
210            }
211            
212            return new java.sql.Date(calendar.getTimeInMillis());
213        }
214        
215        /**
216         * This method will check the current month and set the calendar to that month
217         * @param month month to set the calendar
218         * @return calendar calendar is set to the month selected
219         */
220        protected Calendar setCaledarWithMonth(String month, Date currentDate) {
221            Calendar calendar = Calendar.getInstance();
222            calendar.setTime(currentDate);
223            int calendarMonth = 1;
224            
225            if (EndowConstants.FrequencyMonths.JANUARY.equalsIgnoreCase(month)) {
226                calendarMonth = Calendar.JANUARY;
227            } else if (EndowConstants.FrequencyMonths.FEBRUARY.equalsIgnoreCase(month)) {
228                calendarMonth = Calendar.FEBRUARY;
229              } else if (EndowConstants.FrequencyMonths.MARCH.equalsIgnoreCase(month)) {
230                  calendarMonth = Calendar.MARCH;
231                } else if (EndowConstants.FrequencyMonths.APRIL.equalsIgnoreCase(month)) {
232                    calendarMonth = Calendar.APRIL;
233                  } else if (EndowConstants.FrequencyMonths.MAY.equalsIgnoreCase(month)) {
234                      calendarMonth = Calendar.MAY;
235                    } else if (EndowConstants.FrequencyMonths.JUNE.equalsIgnoreCase(month)) {
236                        calendarMonth = Calendar.JUNE;
237                      } else if (EndowConstants.FrequencyMonths.JULY.equalsIgnoreCase(month)) {
238                          calendarMonth = Calendar.JULY;
239                        } else if (EndowConstants.FrequencyMonths.AUGUST.equalsIgnoreCase(month)) {
240                            calendarMonth = Calendar.AUGUST;
241                          } else if (EndowConstants.FrequencyMonths.SEPTEMBER.equalsIgnoreCase(month)) {
242                              calendarMonth = Calendar.SEPTEMBER;
243                            } else if (EndowConstants.FrequencyMonths.OCTOBER.equalsIgnoreCase(month)) {
244                                calendarMonth = Calendar.OCTOBER;
245                              } else if (EndowConstants.FrequencyMonths.NOVEMBER.equalsIgnoreCase(month)) {
246                                  calendarMonth = Calendar.NOVEMBER;
247                                } else if (EndowConstants.FrequencyMonths.DECEMBER.equalsIgnoreCase(month)) {
248                                    calendarMonth = Calendar.DECEMBER;
249                                  }
250            
251            calendar.set(Calendar.MONTH, calendarMonth);
252            
253            return calendar;
254        }
255    
256        /**
257         * This method will check the current month and set the calendar to that month
258         * @param month, dayOfMonth month to set the calendar, dayOfMonth day of the month to set to
259         * @return calendar calendar is set to the month selected
260         */
261        protected void setCalendarWithDays(Calendar calendar, String dayOfMonth) {
262            int dayInMonthToSet;
263            int calendarMonth = calendar.get(Calendar.MONTH);
264            
265            if (StringUtils.equalsIgnoreCase(dayOfMonth, EndowConstants.FrequencyMonthly.MONTH_END)) { // month end for the month so need to get max days...
266                dayInMonthToSet = checkMaximumDaysInMonth(calendar.get(Calendar.MONTH));
267            } else {
268                dayInMonthToSet = Integer.parseInt(dayOfMonth);
269                
270                if (dayInMonthToSet > 29 && calendarMonth == Calendar.FEBRUARY) {
271                    dayInMonthToSet = checkMaximumDaysInFebruary();
272                } else if (dayInMonthToSet > 30 && (calendarMonth == Calendar.APRIL || calendarMonth == Calendar.JUNE ||
273                           calendarMonth == Calendar.SEPTEMBER || calendarMonth == Calendar.NOVEMBER)) {
274                           dayInMonthToSet = 30;
275                           dayInMonthToSet = checkMaximumDaysInMonth(calendarMonth);                       
276                  }
277              }
278                
279            calendar.set(Calendar.DAY_OF_MONTH, dayInMonthToSet);
280        }
281        
282        /**
283         * This method will check and return either maximum days in the month as 28 or 29 for leap year.
284         * It first sets the month to February and then checks the maximum days..
285         * @return maxDays Maximum number of days in the month of February for calendar.
286         */
287        protected int checkMaximumDaysInFebruary() {
288            int maxDays;      
289            Calendar februaryMonthlyDateCalendar = Calendar.getInstance();
290            februaryMonthlyDateCalendar.set(Calendar.MONTH, Calendar.FEBRUARY);
291            maxDays = februaryMonthlyDateCalendar.getActualMaximum(Calendar.DAY_OF_MONTH);
292            
293            return maxDays;
294        }
295        
296        /**
297         * This method will check and return maximum days in a month.
298         * @param monthNumber The number of the month to test for maximum days..
299         * @return maxDays Maximum number of days in the month of February for calendar.
300         */
301        protected int checkMaximumDaysInMonth(int monthNumber) {
302            int maxDays;   
303            
304            Calendar calendar = Calendar.getInstance();
305            calendar.set(Calendar.MONTH, monthNumber);
306            maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
307            
308            return maxDays;
309        }
310        
311        /**
312         * This method gets the businessObjectService.
313         * 
314         * @return businessObjectService
315         */
316        protected BusinessObjectService getBusinessObjectService() {
317            return businessObjectService;
318        }
319    
320        /**
321         * This method sets the businessObjectService
322         * 
323         * @param businessObjectService
324         */
325        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
326            this.businessObjectService = businessObjectService;
327        }
328        
329        public void setDateTimeService(DateTimeService dateTimeService) {
330            this.dateTimeService = dateTimeService;
331        }
332    
333        protected DateTimeService getDateTimeService() {
334            return dateTimeService;
335        }
336        
337        protected KEMService getKemService() {
338            return kemService;
339        }
340        
341        public void setKemService(KEMService kemService) {
342            this.kemService = kemService;
343        }
344        
345    }