001    /**
002     * Copyright 2004-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.hr.time.util;
017    
018    import java.sql.Time;
019    import java.util.ArrayList;
020    import java.util.Collections;
021    import java.util.Comparator;
022    import java.util.List;
023    
024    import org.joda.time.DateTime;
025    import org.joda.time.DateTimeZone;
026    import org.joda.time.Interval;
027    import org.joda.time.LocalDateTime;
028    import org.joda.time.LocalTime;
029    import org.kuali.hr.time.calendar.Calendar;
030    import org.kuali.hr.time.calendar.CalendarEntries;
031    import org.kuali.hr.time.flsa.FlsaDay;
032    import org.kuali.hr.time.flsa.FlsaWeek;
033    import org.kuali.hr.time.service.base.TkServiceLocator;
034    import org.kuali.hr.time.timeblock.TimeBlock;
035    
036    public class TkTimeBlockAggregate {
037            public List<List<TimeBlock>> dayTimeBlockList = new ArrayList<List<TimeBlock>>();
038            private CalendarEntries payCalendarEntry;
039            private Calendar payCalendar;
040    
041        /**
042         * Defaults to using SYSTEM time zone.
043         *
044         * @param timeBlocks
045         * @param payCalendarEntry
046         */
047            public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry){
048                    this(timeBlocks, payCalendarEntry, TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId()));
049            }
050    
051        /**
052         * Defaults to using SYSTEM time zone.
053         *
054         * @param timeBlocks
055         * @param payCalendarEntry
056         * @param payCalendar
057         */
058            public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry, Calendar payCalendar) {
059            this(timeBlocks, payCalendarEntry, payCalendar, false);
060        }
061    
062        /**
063         * Provides the option to refer to the time zone adjusted time for the current
064         * user.
065         * @param timeBlocks
066         * @param payCalendarEntry
067         * @param payCalendar
068         * @param useUserTimeZone
069         */
070        public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone) {
071                    this.payCalendarEntry = payCalendarEntry;
072                    this.payCalendar = payCalendar;
073    
074                    List<Interval> dayIntervals = TKUtils.getDaySpanForCalendarEntry(payCalendarEntry);
075                    for(Interval dayInt : dayIntervals){
076                            List<TimeBlock> dayTimeBlocks = new ArrayList<TimeBlock>();
077                            for(TimeBlock timeBlock : timeBlocks){
078    
079                    // Assumption: Timezones can only be switched at pay period end boundaries.
080                    // If the above assumption becomes false, the logic below will need to
081                    // accommodate virtual chopping of time blocks to have them fit nicely
082                    // in the "days" that are displayed to users.
083    
084                                    DateTime beginTime = useUserTimeZone ? timeBlock.getBeginTimeDisplay() : new DateTime(timeBlock.getBeginTimestamp(), TKUtils.getSystemDateTimeZone());
085                                    DateTime endTime = useUserTimeZone ? timeBlock.getEndTimeDisplay() :  new DateTime(timeBlock.getEndTimestamp(), TKUtils.getSystemDateTimeZone());
086                                    if(dayInt.contains(beginTime)){
087                                            if(dayInt.contains(endTime) || endTime.compareTo(dayInt.getEnd()) == 0){
088                                                    // determine if the time block needs to be pushed forward / backward
089                                                    if(beginTime.getHourOfDay() < dayInt.getStart().getHourOfDay()) {
090                                                            timeBlock.setPushBackward(true);
091                                                    }
092    
093                                                    dayTimeBlocks.add(timeBlock);
094                                            }
095                                    }
096                            }
097                            dayTimeBlockList.add(dayTimeBlocks);
098                    }
099            }
100    
101        public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone, List<Interval> dayIntervals) {
102            this.payCalendarEntry = payCalendarEntry;
103                    this.payCalendar = payCalendar;
104                    
105                    for(Interval dayInt : dayIntervals){
106                            List<TimeBlock> dayTimeBlocks = new ArrayList<TimeBlock>();
107                            for(TimeBlock timeBlock : timeBlocks){
108    
109                    // Assumption: Timezones can only be switched at pay period end boundaries.
110                    // If the above assumption becomes false, the logic below will need to
111                    // accommodate virtual chopping of time blocks to have them fit nicely
112                    // in the "days" that are displayed to users.
113    
114                                    DateTime beginTime = useUserTimeZone ? timeBlock.getBeginTimeDisplay() : new DateTime(timeBlock.getBeginTimestamp(), TKUtils.getSystemDateTimeZone());
115                                    DateTime endTime = useUserTimeZone ? timeBlock.getEndTimeDisplay() :  new DateTime(timeBlock.getEndTimestamp(), TKUtils.getSystemDateTimeZone());
116                                    if(dayInt.contains(beginTime)){
117                                            if(dayInt.contains(endTime) || endTime.compareTo(dayInt.getEnd()) == 0){
118                                                    // determine if the time block needs to be pushed forward / backward
119                                                    if(beginTime.getHourOfDay() < dayInt.getStart().getHourOfDay()) {
120                                                            timeBlock.setPushBackward(true);
121                                                    }
122    
123                                                    dayTimeBlocks.add(timeBlock);
124                                            }
125                                    }
126                            }
127                            dayTimeBlockList.add(dayTimeBlocks);
128                    }
129                    
130        }
131        
132        
133            public List<TimeBlock> getFlattenedTimeBlockList(){
134                    List<TimeBlock> lstTimeBlocks = new ArrayList<TimeBlock>();
135                    for(List<TimeBlock> timeBlocks : dayTimeBlockList){
136                            lstTimeBlocks.addAll(timeBlocks);
137                    }
138    
139                    Collections.sort(lstTimeBlocks, new Comparator<TimeBlock>() { // Sort the Time Blocks
140                            public int compare(TimeBlock tb1, TimeBlock tb2) {
141                                    if (tb1 != null && tb2 != null)
142                                            return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
143                                    return 0;
144                            }
145                    });
146    
147                    return lstTimeBlocks;
148            }
149    
150            /**
151             * Provides a way to access all of the time blocks for a given week.
152             *
153             * Outer list is 0 indexed list representing days in a week.
154             * Inner List are all of the time blocks for that day.
155             *
156             * Ex.
157             *
158             * List<List<TimeBlock>> week0 = getWeekTimeBlocks(0);
159             * List<TimeBlock> day0 = week0.get(0);
160             *
161             * @param week
162             * @return
163             */
164            public List<List<TimeBlock>> getWeekTimeBlocks(int week){
165                    int startIndex = week*7;
166                    int endIndex = (week*7)+7;
167                    endIndex = endIndex > dayTimeBlockList.size() ? dayTimeBlockList.size() : endIndex;
168    
169            // Need to sort each day by clock time.
170            List<List<TimeBlock>> wList = dayTimeBlockList.subList(startIndex, endIndex);
171            for (List<TimeBlock> dList : wList) {
172                Collections.sort(dList, new Comparator<TimeBlock>() { // Sort the Time Blocks
173                    public int compare(TimeBlock tb1, TimeBlock tb2) {
174                        if (tb1 != null && tb2 != null)
175                            return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp());
176                        return 0;
177                    }
178                });
179            }
180    
181                    return wList;
182            }
183    
184            /**
185             * When consuming these weeks, you must be aware that you could be on a
186             * virtual day, ie noon to noon schedule and have your FLSA time start
187             * before the virtual day start,
188             * but still have a full 7 days for your week.
189         *
190         * @param zone The TimeZone to use when constructing this relative sorting.
191             */
192            public List<FlsaWeek> getFlsaWeeks(DateTimeZone zone){
193                    int flsaDayConstant = payCalendar.getFlsaBeginDayConstant();
194                    Time flsaBeginTime  = payCalendar.getFlsaBeginTime();
195    
196                    // We can use these to build our interval, we have to make sure we
197                    // place them on the proper day when we construct it.
198                    LocalTime flsaBeginLocalTime = LocalTime.fromDateFields(flsaBeginTime);
199    
200                    // Defines both the start date and the start virtual time.
201                    // We will add 1 day to this to move over all days.
202                    //
203                    // FLSA time is set.  This is an FLSA start date.
204            LocalDateTime startLDT = payCalendarEntry.getBeginLocalDateTime();
205    //              DateTime startDate = new DateTime(payCalendarEntry.getBeginPeriodDateTime());
206    //              startDate = startDate.toLocalDate().toDateTime(flsaBeginLocalTime,TKUtils.getSystemDateTimeZone());
207    
208                    List<FlsaWeek> flsaWeeks = new ArrayList<FlsaWeek>();
209                    List<TimeBlock> flatSortedBlockList = getFlattenedTimeBlockList();
210                    FlsaWeek currentWeek = new FlsaWeek(flsaDayConstant, flsaBeginLocalTime, LocalTime.fromDateFields(payCalendarEntry.getBeginPeriodDateTime()));
211    //              commented additional week addition, as causing an extra day on UI
212                    flsaWeeks.add(currentWeek);
213    
214                    for (int i = 0; i<dayTimeBlockList.size(); i++) {
215                            LocalDateTime currentDate = startLDT.plusDays(i);
216                            FlsaDay flsaDay = new FlsaDay(currentDate, flatSortedBlockList, zone);
217    
218                            if (currentDate.getDayOfWeek() == flsaDayConstant) {
219                                    currentWeek = new FlsaWeek(flsaDayConstant, flsaBeginLocalTime, flsaBeginLocalTime);
220                                    flsaWeeks.add(currentWeek);
221                                    
222                                    currentWeek.addFlsaDay(flsaDay);
223                            } else {
224                                    // add to existing week.
225                                    currentWeek.addFlsaDay(flsaDay);
226                            }
227                    }
228    
229                    return flsaWeeks;
230            }
231    
232            /**
233             * @return the total number of weeks that this object represents.
234             */
235            public int numberOfAggregatedWeeks() {
236                    int weeks = 0;
237    
238                    if (this.dayTimeBlockList.size() > 0) {
239                            weeks = this.dayTimeBlockList.size() / 7;
240                            if (this.dayTimeBlockList.size() % 7 > 0)
241                                    weeks++;
242                    }
243    
244                    return weeks;
245            }
246    
247            public List<List<TimeBlock>> getDayTimeBlockList() {
248                    return dayTimeBlockList;
249            }
250    
251            public CalendarEntries getPayCalendarEntry() {
252                    return payCalendarEntry;
253            }
254    
255            public void setPayCalendarEntry(CalendarEntries payCalendarEntry) {
256                    this.payCalendarEntry = payCalendarEntry;
257            }
258    
259            public Calendar getPayCalendar() {
260                    return payCalendar;
261            }
262    
263            public void setPayCalendar(Calendar payCalendar) {
264                    this.payCalendar = payCalendar;
265            }
266    
267    }