/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) 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 3 of the License, * or (at your option) any later version. * * OpenNMS(R) 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 OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.config.users; import java.util.BitSet; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import org.opennms.core.utils.ThreadCategory; import org.springframework.util.Assert; /** * This class holds information on the duty schedules that users can have. * Converstion between different formats of the duty schedule information are * possible, as is the comparision between a Calendar passed in and the start * and stop times of each day in a duty schedule. * * @author <A HREF="mailto:jason@opennms.org">Jason Johns </A> * @author <A HREF="http://www.opennms.org/">OpenNMS </A> * @author <A HREF="mailto:jason@opennms.org">Jason Johns </A> * @author <A HREF="http://www.opennms.org/">OpenNMS </A> * @version 1.1.1.1 */ public class DutySchedule { /** * Each boolean in the bit set represents a day of the week. Monday = 0, * Tuesday = 1 ... Sunday = 6 */ private BitSet m_days; /** * The starting time of this DutySchedule */ private int m_startTime; /** * The ending time of this DutySchedule */ private int m_stopTime; /** * A series of constants to identify the days of the week as used by the * DutySchedule class */ public static final int MONDAY = 0; /** Constant <code>TUESDAY=1</code> */ public static final int TUESDAY = 1; /** Constant <code>WEDNESDAY=2</code> */ public static final int WEDNESDAY = 2; /** Constant <code>THURSDAY=3</code> */ public static final int THURSDAY = 3; /** Constant <code>FRIDAY=4</code> */ public static final int FRIDAY = 4; /** Constant <code>SATURDAY=5</code> */ public static final int SATURDAY = 5; /** Constant <code>SUNDAY=6</code> */ public static final int SUNDAY = 6; /** * A list of names to abbreviate the days of the week */ public static final String[] DAY_NAMES = { "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su" }; /** * A mapping between the days of the week as indexed by the DutySchedule * class and those of the Calendar class */ private static final int[] CALENDAR_DAY_MAPPING = { Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY, Calendar.SUNDAY }; /** * Default constructor, builds the BitSet used to identify the days of the * week that are set. */ public DutySchedule() { m_days = new BitSet(7); } /** * This constructor is designed to convert from a Vector filled with 7 * Boolean objects and two String objects into the BitSet and integer start * and stop time. Very useful for the ModifyUser screen when it is * converting from a table display to save the information to a string * format for the users.xml. * * @param aSchedule * filled with 7 Boolean objects and two String objects */ public DutySchedule(Vector<Object> aSchedule) { m_days = new BitSet(7); // set each day that is set to true for (int i = 0; i < 7; i++) { if (((Boolean) aSchedule.get(i)).booleanValue()) { m_days.set(i); } } // initialize the start and stop times, which should be in the last to // indexes of the vector m_startTime = Integer.parseInt((String) aSchedule.get(7)); m_stopTime = Integer.parseInt((String) aSchedule.get(8)); } /** * This constructor is designed to convert from a Vector filled with 7 * Boolean objects and two String objects into the BitSet and integer start * and stop time. Very useful for the ModifyUser screen when it is * converting from a table display to save the information to a string * format for the users.xml. * * @param schedule a {@link java.util.List} object. * @param start a int. * @param end a int. */ public DutySchedule(List<Boolean> schedule, int start, int end) { Assert.notNull(schedule, "argument schedule must not be null"); Assert.isTrue(schedule.size() == 7, "argument schedule must contain exactly seven Boolean objects"); m_days = new BitSet(7); for (int i = 0; i < 7; i++) { m_days.set(i, schedule.get(i).booleanValue()); } m_startTime = start; m_stopTime = end; } /** * This constructor is designed to build a new DutySchedule from a String * representation formatted as such. <day_of_week_abbr><start>- <stop>eg. * MoWeFr800-1700, TuTh900-1500. * * @param aSchedule * the string to convert to a new DutySchedule */ public DutySchedule(String aSchedule) { m_days = new BitSet(7); // parse the endtime and day/begin time out StringTokenizer timeTokens = new StringTokenizer(aSchedule, "-"); String daysAndStartTime = timeTokens.nextToken(); m_stopTime = Integer.parseInt(timeTokens.nextToken()); // loop through the first half of the string and get each two letter // day abbreviation, set the appropriate BitSet values for (int j = 0; j < daysAndStartTime.length(); j++) { // check to see if there is a character or digit at the current // index if (!Character.isDigit(daysAndStartTime.charAt(j))) { // look at the current and next characters, advance the loop // counter // by one and add one to get the propert substring m_days.set(getDayInt(daysAndStartTime.substring(j, ++j + 1))); } else { // if a digit was seen this is the start time, get it and stop // the loop m_startTime = Integer.parseInt(daysAndStartTime.substring(j, daysAndStartTime.length())); break; } } } /** * This method returns the index value of a day abbreviation * * @param aDay * the day abbreviation * @return The index associated with this abbreviation. */ private int getDayInt(String aDay) { int value = -1; for (int i = 0; i < DAY_NAMES.length; i++) { if (aDay.equals(DAY_NAMES[i])) { value = i; break; } } return value; } /** * This method sets the BitSet that tracks what days this DutySchedule * applies to. * * @param aDay * the day index to set in the BitSet */ public void setDay(int aDay) { m_days.set(aDay); } /** * This method return the start time as an integer * * @return The start time of this DutySchedule. */ public int getStartTime() { return m_startTime; } /** * This method return the stop time as an integer * * @return The stop time of this DutySchedule. */ public int getStopTime() { return m_stopTime; } /** * This method formats the DutySchedule as a vector populated with the first * seven objects as Booleans set to indicate what days of the week are * stored, and the last two objects as Strings that reflect the start time * and stop time respectively. This method gives a Vector that can be passed * to the DutySchedule(Vector) constructor to create a new DutySchedule * * @return A Vector properly formatted to reflect this DutySchedule. */ public Vector<Object> getAsVector() { Vector<Object> vector = new Vector<Object>(); for (int i = 0; i < 7; i++) { vector.add(Boolean.valueOf(m_days.get(i))); } vector.add(String.valueOf(m_startTime)); vector.add(String.valueOf(m_stopTime)); return vector; } /** * This method decides if a given time falls within the duty schedule * contained in this object. It creates two partial Calendars from the * Calendar that is passed in and then sets the start time for one and the * end time for the other. Then in a loop it reassigns the day of week * according to the BitSet. It makes a comparision to see if the argument * Calendar is between the start and stop times and returns true immediately * if it is. * * @param aTime * The time to check. * @return True if the Calendar is contained in the duty schedule. false if * it isn't. */ public boolean isInSchedule(Calendar aTime) { boolean response = false; // make two new Calendar objects from the YEAR, MONTH and DATE of the // date we are checking. Calendar startTime = new GregorianCalendar(aTime.get(Calendar.YEAR), aTime.get(Calendar.MONTH), aTime.get(Calendar.DATE)); // the hour will be the integer part of the start time divided by 100 // cause it should be // in military time startTime.set(Calendar.HOUR_OF_DAY, (m_startTime / 100)); // the minute will be the start time mod 100 cause it should be in // military time startTime.set(Calendar.MINUTE, (m_startTime % 100)); startTime.set(Calendar.SECOND, 0); Calendar endTime = new GregorianCalendar(aTime.get(Calendar.YEAR), aTime.get(Calendar.MONTH), aTime.get(Calendar.DATE)); endTime.set(Calendar.HOUR_OF_DAY, (m_stopTime / 100)); endTime.set(Calendar.MINUTE, (m_stopTime % 100)); endTime.set(Calendar.SECOND, 0); // look at the BitSet to see what days are set for this duty schedule, // reassign the // day of weak for the start and stop time, then see if the argument // Calendar is // between these times. for (int i = 0; i < 7; i++) { // see if the now time corresponds to a day when the user is on duty if (m_days.get(i) && CALENDAR_DAY_MAPPING[i] == aTime.get(Calendar.DAY_OF_WEEK)) { // now check to see if the time given is between these two times // inclusive, if it is quit loop // we want the begin and end times for the ranges to be // includsive, so convert to milliseconds so we // can do a greater than/less than equal to comparisons long dateMillis = aTime.getTime().getTime(); long startMillis = startTime.getTime().getTime(); long endMillis = endTime.getTime().getTime(); // return true if the agrument date falls between the start and // stop time if ((startMillis <= dateMillis) && (dateMillis <= endMillis)) { response = true; break; } } } return response; } /** * This method decides if a given time falls within the duty schedule * contained in this object. If so, it returns 0 milliseconds. If not * it returns the number of milliseconds until the next on-duty period * begins. * It creates two partial Calendars from the Calendar that is passed in * and then sets the start time for one and the end time for the other. * Then in a loop it reassigns the day of week according to the BitSet. * If the day is today, it makes a comparision of the argument Calendar * and the start and stop times to determine the return value. If the * day is not today it calculates the time between now and the day and * start time of the duty schedule, saving the smallest of these as the * return value as we iterate through the BitSet.??? * * @param nTime The time to check. * @return long - number of milliseconds */ public long nextInSchedule(Calendar nTime) { long next = -1; long tempnext = -1; //make two new Calendar objects from the YEAR, MONTH and DATE of the //date we are checking. Calendar startTime = new GregorianCalendar(nTime.get(Calendar.YEAR), nTime.get(Calendar.MONTH), nTime.get(Calendar.DATE)); //the hour will be the integer part of the start time divided by 100 //cause it should be in military time startTime.set(Calendar.HOUR_OF_DAY, (m_startTime/100)); //the minute will be the start time mod 100 cause it should be in //military time startTime.set(Calendar.MINUTE, (m_startTime % 100)); startTime.set(Calendar.SECOND, 0); Calendar endTime = new GregorianCalendar(nTime.get(Calendar.YEAR), nTime.get(Calendar.MONTH), nTime.get(Calendar.DATE)); endTime.set(Calendar.HOUR_OF_DAY, (m_stopTime/100)); endTime.set(Calendar.MINUTE, (m_stopTime % 100)); endTime.set(Calendar.SECOND, 0); //we want the begin and end times for the ranges to be includsive, //so convert to milliseconds so we can do a greater than/less than //equal to comparisons long dateMillis = nTime.getTime().getTime(); long startMillis = startTime.getTime().getTime(); long endMillis = endTime.getTime().getTime(); //look at the BitSet to see what days are set for this duty schedule, //reassign the day of week for the start and stop time, then see if //the argument Calendar is between these times. int itoday = -1; for (int i = 0; i < 7; i++) { // does i correspond to today? if (CALENDAR_DAY_MAPPING[i] == nTime.get(Calendar.DAY_OF_WEEK)) { itoday = i; if (log().isDebugEnabled()) { log().debug("nextInSchedule: day of week is " + i); } } //is duty schedule for today? //see if the now time corresponds to a day when the user is on duty if (m_days.get(i) && CALENDAR_DAY_MAPPING[i] == nTime.get(Calendar.DAY_OF_WEEK)) { log().debug("nextInSchedule: Today is in schedule"); //is start time > current time? if (startMillis > dateMillis) { next = startMillis - dateMillis; if (log().isDebugEnabled()) { log().debug("nextInSchedule: duty starts in " + next + " millisec"); } } else { //is end time >= now if (endMillis >= dateMillis) { next = 0; log().debug("nextInSchedule: on duty now"); } } } } if (next >= 0) { return next; } log().debug("nextInSchedule: Remainder of today is not in schedule"); int ndays = -1; for (int i = 0; i < 7; i++) { if (m_days.get(i)) { if (log().isDebugEnabled()) { log().debug("nextInSchedule: day " + i + " is in schedule"); } ndays = i - itoday; if (ndays <= 0) { ndays += 7; } if (log().isDebugEnabled()) { log().debug("nextInSchedule: day " + i + " is " + ndays + " from today"); } tempnext = (86400000 * ndays) - dateMillis + startMillis; if (tempnext < next || next == -1) { next = tempnext; if (log().isDebugEnabled()) { log().debug("nextInSchedule: duty begins in " + next + " millisecs"); } } } } return next; } /** * This method sets the start time of this DutySchedule * * @param anHour * The hour in military time to set the start time for the * DutySchedule. */ public void setStartHour(int anHour) { m_startTime = anHour; } /** * This method sets the stop time of this DutySchedule * * @param anHour * The hour in military time to set the end time for the * DutySchedule. */ public void setEndHour(int anHour) { m_stopTime = anHour; } /** * This method returns the DutySchedule formatted as a string that the * DutySchedule(String) constructor could parse. The string will be * formatted as such: <day_of_week_abbr><start>- <stop>eg. MoWeFr800-1700, * TuTh900-1500. * * @return A string representation of this DutySchedule. */ public String toString() { StringBuffer buffer = new StringBuffer(); // put in abbreviations for the days of the week for (int i = 0; i < DAY_NAMES.length; i++) { if (m_days.get(i)) { buffer.append(DAY_NAMES[i]); } } // add the start and stop times to the end of the string buffer.append(m_startTime + "-" + m_stopTime); return buffer.toString(); } /** * <p>isInSchedule</p> * * @param time a {@link java.util.Date} object. * @return a boolean. */ public boolean isInSchedule(Date time) { Calendar cal = Calendar.getInstance(); cal.setTime(time); return isInSchedule(cal); } /** * <p>hasDay</p> * * @param aDay a int. * @return a boolean. */ public boolean hasDay(int aDay) { return m_days.get(aDay); } private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } }