/** * Copyright (C) 2010 BonitaSoft S.A. * BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2.0 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.bonitasoft.simulation.model.calendar; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; import org.bonitasoft.simulation.model.Period; /** * @author Romain Bioteau * */ public class SimCalendar { protected Map<Integer , SimCalendarDay> daysOfWeek ; protected Calendar calendar ; public SimCalendar(){ daysOfWeek = new HashMap<Integer , SimCalendarDay>(); calendar = GregorianCalendar.getInstance(); } public void addSimCalendarDay(int dayOfWeek , Set<SimCalendarPeriod> workingDay) throws Exception { if(daysOfWeek.get(dayOfWeek) != null){ throw new Exception("Day : "+dayOfWeek+" already defined"); //$NON-NLS-1$ //$NON-NLS-2$ } daysOfWeek.put(dayOfWeek , new SimCalendarDay(dayOfWeek, workingDay)) ; } public Map<Integer, SimCalendarDay> getDaysOfWeek(){ return Collections.unmodifiableMap(daysOfWeek) ; } public long getWorkingPlanningDuration(long from, long to){ long duration = to-from ; long current = from ; while(current < to){ long next = getNextPlanningAvailable(current) ; if(next < to){ duration = duration - (next-current) ; current = next ; }else{ duration = duration - (to-current) ; current = to ; } current = getNextPlanningUnavailable(current); if(current == -1){ current = to ; } } if(duration < 0){ duration = 0 ; } return duration ; } public long getNextPlanningAvailable(long instant){ calendar.setTimeInMillis(instant); Calendar newCal = GregorianCalendar.getInstance() ; newCal.setTimeInMillis(instant) ; int initialDay = calendar.get(Calendar.DAY_OF_WEEK) ; int i = initialDay ; int nbLoop = 0 ; while ( nbLoop < 8){ SimCalendarDay day = daysOfWeek.get(i); SortedMap<Long,Long> potentialStart = new TreeMap<Long,Long>() ; for(SimCalendarPeriod p : day.getWorkingPeriods()){ if(initialDay == day.getDayOfWeek() && p.contains(instant)){ return instant ; }else if(initialDay == day.getDayOfWeek() && p.getStart().after(calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE))){ newCal.set(Calendar.HOUR_OF_DAY, p.getStart().getHourOfDay()) ; newCal.set(Calendar.MINUTE, p.getStart().getMinuteOfHour()) ; newCal.set(Calendar.SECOND,0) ; newCal.set(Calendar.MILLISECOND,0) ; potentialStart.put(newCal.getTimeInMillis() - instant, newCal.getTimeInMillis()) ; }else if(initialDay != day.getDayOfWeek()){ newCal.set(Calendar.HOUR_OF_DAY, p.getStart().getHourOfDay()) ; newCal.set(Calendar.MINUTE, p.getStart().getMinuteOfHour()) ; newCal.set(Calendar.SECOND,0) ; newCal.set(Calendar.MILLISECOND,0) ; potentialStart.put(newCal.getTimeInMillis(), newCal.getTimeInMillis()) ; } } if(!potentialStart.isEmpty()){ return potentialStart.get(potentialStart.keySet().iterator().next()) ; } newCal.add(Calendar.DAY_OF_WEEK, 1) ; i++; i = i % (calendar.getActualMaximum(Calendar.DAY_OF_WEEK)+1 ); if(i == 0){ i = 1 ; } nbLoop ++ ; } System.err.println("next Available Planning not found..."); //$NON-NLS-1$ return -1; } public long getNextPlanningUnavailable(long instant){ if(!isPlanningAvailable(instant)){ return instant ; } calendar.setTimeInMillis(instant); Calendar newCal = GregorianCalendar.getInstance() ; newCal.setTimeInMillis(instant) ; int i = calendar.get(Calendar.DAY_OF_WEEK) ; int nbLoop = 0 ; boolean nextAvailable = false ; while ( nbLoop < 8){ SimCalendarDay day = daysOfWeek.get(i); if(day.getWorkingPeriods().isEmpty()){ newCal.set(Calendar.HOUR_OF_DAY, 0) ; newCal.set(Calendar.MINUTE, 0) ; newCal.set(Calendar.SECOND,0) ; newCal.set(Calendar.MILLISECOND,0) ; return newCal.getTimeInMillis() ; } for(SimCalendarPeriod p : day.getWorkingPeriods()){ if(nextAvailable && p.getEnd().getHourOfDay() != 0){ newCal.set(Calendar.HOUR_OF_DAY, p.getEnd().getHourOfDay()) ; newCal.set(Calendar.MINUTE, p.getEnd().getMinuteOfHour()) ; newCal.set(Calendar.SECOND,0) ; newCal.set(Calendar.MILLISECOND,0) ; return newCal.getTimeInMillis() ; }else if(p.contains(calendar.getTimeInMillis())){ if( p.getEnd().getHourOfDay() == 0){ nextAvailable = true ; }else{ newCal.set(Calendar.HOUR_OF_DAY, p.getEnd().getHourOfDay()) ; newCal.set(Calendar.MINUTE, p.getEnd().getMinuteOfHour()) ; newCal.set(Calendar.SECOND,0) ; newCal.set(Calendar.MILLISECOND,0) ; return newCal.getTimeInMillis() ; } } } newCal.add(Calendar.DAY_OF_WEEK, 1) ; i++; i = i % (calendar.getActualMaximum(Calendar.DAY_OF_WEEK)+1 ); if(i == 0){ i = 1 ; } nbLoop ++ ; } if(getNextPlanningAvailable(instant) == instant){ instant = -1 ; } return instant ; } public boolean isPlanningAvailable(long date){ calendar.setTimeInMillis(date); SimCalendarDay day = daysOfWeek.get(calendar.get(Calendar.DAY_OF_WEEK)) ; for(SimCalendarPeriod p : day.getWorkingPeriods()){ if(p.contains(date)){ return true ; } } return false ; } public Set<Long> getAvailablePlanningDurations() { Set<Long> result = new TreeSet<Long>(); calendar.set(2010, 1, 1, 0, 0, 0) ; long current = calendar.getTimeInMillis() ; calendar.set(2010, 1, 8, 0, 0, 0) ; long to = calendar.getTimeInMillis() ; while(current < to){ if(!isPlanningAvailable(current)){ long next = getNextPlanningAvailable(current) ; if(next < to){ current = next ; }else{ current = to ; } } long nextUnavailable = getNextPlanningUnavailable(current) ; if(nextUnavailable != -1){ result.add(nextUnavailable-current) ; current = nextUnavailable; }else{ result.add(to-current) ; current = to; } } return result; } public boolean isPlanningAvailable(Period period) { long periodDuration = period.getDuration() ; long start = period.getBegin() ; long end = period.getEnd() ; long workingDuration = getWorkingPlanningDuration(start, end) ; return workingDuration == periodDuration ; } public List<Period> split(Period period) throws Exception { List<Period> results = new ArrayList<Period>(); long currentDuration = 0 ; long totalDuration = period.getDuration() ; long start = period.getBegin() ; long currentDate = start ; if(period.getDuration() == 0 || getNextPlanningUnavailable(new Date().getTime()) == -1){ return Collections.singletonList(period) ; } while (currentDuration < totalDuration){ Period p = new Period(0,0); if (!isPlanningAvailable(currentDate) && currentDuration != totalDuration){ currentDate = getNextPlanningAvailable(currentDate) ; } if(isPlanningAvailable(currentDate)){ p.setBegin(currentDate); if (currentDuration < totalDuration) { long tmp = currentDate ; tmp = getNextPlanningUnavailable(tmp) ; if((currentDuration+(tmp-currentDate) > totalDuration)){ currentDate = currentDate+(totalDuration-currentDuration) ; currentDuration = totalDuration ; }else{ currentDuration = currentDuration + tmp-currentDate ; currentDate = tmp ; } } } else { throw new Exception("Split is inconsistent for Period : "+period+" because "+new Date(currentDate)+" is not available in the planning") ; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } p.setEnd(currentDate); results.add(p); } return results; } public long getWorkingDurationEndDate(long start, long workedDuration) { long currentDuration = 0 ; long newStart = getNextPlanningAvailable(start) ; long end = newStart+workedDuration ; currentDuration = getWorkingPlanningDuration(newStart, end) ; while(currentDuration != workedDuration){ long missingDuration = workedDuration - currentDuration ; end = end + missingDuration ; currentDuration = getWorkingPlanningDuration(newStart, end) ; } return getNextPlanningAvailable(end); } }