/***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
* @author Mustansar@rice.edu
*
* Licensed under the Educational Community License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ecl1.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.calendar.impl;
import java.util.GregorianCalendar;
import java.util.Stack;
import java.util.TimeZone;
import java.util.Vector;
import java.util.List;
import org.sakaiproject.time.api.Time;
import org.sakaiproject.time.api.TimeRange;
import org.sakaiproject.time.cover.TimeService;
import org.sakaiproject.time.api.TimeBreakdown;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* <p>MWFRecurrenceRule is a time range generating rule that is based on a weekly recurrence.</p>
* <p>repaeting Mondays Wendnesday and Fridays</p>
* <p>The recurrences happen on the same day-of-week, at the same time as the prototype.</p>
* <p>TODO: support changing the day-of-week of recurrences -Mustansar</p>
*/
public class MWFRecurrenceRule extends RecurrenceRuleBase
{
/** The unique type / short frequency description. */
protected final static String FREQ = "MWF";
/**
* Construct.
*/
public MWFRecurrenceRule()
{
super();
} // MWFRecurrenceRule
/**
* Construct with no limits.
* @param interval Every this many number of weeks: 1 would be weekly.
*/
public MWFRecurrenceRule(int interval)
{
super(interval);
} //MWFRecurrenceRule
/**
* Construct with count limit.
* @param interval Every this many number of weeks: 1 would be weekly.
* @param count For this many occurrences - if 0, does not limit.
*/
public MWFRecurrenceRule(int interval, int count)
{
super(interval, count);
} // MWFRecurrenceRule
/**
* Construct with time limit.
* @param interval Every this many number of weeks: 1 would be weekly.
* @param until No time ranges past this time are generated - if null, does not limit.
*/
public MWFRecurrenceRule(int interval, Time until)
{
super(interval, until);
} // MWFRecurrenceRule
/**
* Serialize the resource into XML, adding an element to the doc under the top of the stack element.
* @param doc The DOM doc to contain the XML (or null for a string return).
* @param stack The DOM elements, the top of which is the containing element of the new "resource" element.
* @return The newly added element.
*/
public Element toXml(Document doc, Stack stack)
{
// add the "rule" element to the stack'ed element
Element rule = doc.createElement("rule");
((Element)stack.peek()).appendChild(rule);
// set the class name - old style for CHEF 1.2.10 compatibility
rule.setAttribute("class", "org.chefproject.osid.calendar.MWFRecurrenceRule");
// set the rule class name w/o package, for modern usage
rule.setAttribute("name", "MWFRecurrenceRule");
// Do the base class part.
setBaseClassXML(rule);
return rule;
} // toXml
/* (non-Javadoc)
* @see org.chefproject.service.calendar.RecurrenceRuleBase#getRecurrenceType()
*/
protected int getRecurrenceType()
{
return GregorianCalendar.WEEK_OF_MONTH;
}
/**
* {@inheritDoc}
*/
public String getFrequencyDescription()
{
return rb.getString("set.MWF");
}
/**
* {@inheritDoc}
*/
public String getFrequency()
{
return FREQ;
}
/**
* Return a List of all RecurrenceInstance objects generated by this rule within the given time range, based on the
* prototype first range, in time order.
* @param prototype The prototype first TimeRange.
* @param range A time range to limit the generated ranges.
* @param timeZone The time zone to use for displaying times.
* %%% Note: this is currently not implemented, and always uses the "local" zone.
* @return a List of RecurrenceInstance generated by this rule in this range.
*/
public List generateInstances(TimeRange prototype, TimeRange range, TimeZone timeZone)
{
TimeBreakdown startBreakdown = prototype.firstTime().breakdownLocal();
List rv = new Vector();
GregorianCalendar startCalendarDate = TimeService.getCalendar(TimeService.getLocalTimeZone(),0,0,0,0,0,0,0);
startCalendarDate.set(
startBreakdown.getYear(),
startBreakdown.getMonth() - 1,
startBreakdown.getDay(),
startBreakdown.getHour(),
startBreakdown.getMin(),
startBreakdown.getSec()); //may have to move this line ahead
GregorianCalendar nextCalendarDate = (GregorianCalendar) startCalendarDate.clone();
if(startCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)%2!=0 ){
if(startCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)==7){
startCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 2);
}
else if (startCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)==6) {
startCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 3);
}
else {
startCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 1);
}
}
nextCalendarDate = (GregorianCalendar) startCalendarDate.clone();
int currentCount = 1;
int hitCount=1; //counts tth/mwf occurences when getCount()>0
do
{
Time nextTime = TimeService.newTime(nextCalendarDate);
// is this past count?
if ((getCount() > 0) && (hitCount > getCount()))
break;
// is this past until?
if ((getUntil() != null) && isAfter(nextTime, getUntil()) )
break;
TimeRange nextTimeRange = TimeService.newTimeRange(nextTime.getTime(), prototype.duration());
// Is this out of the range?
if (isOverlap(range, nextTimeRange))
{
TimeRange eventTimeRange = null;
// Single time cases require special handling.
if ( prototype.isSingleTime() )
{
eventTimeRange = TimeService.newTimeRange(nextTimeRange.firstTime());
}
else
{
eventTimeRange = TimeService.newTimeRange(nextTimeRange.firstTime(), nextTimeRange.lastTime(), true, false);
}
// use this one
String eventHR=eventTimeRange.toStringHR();
rv.add(new RecurrenceInstance(eventTimeRange, currentCount));
}
// if next starts after the range, stop generating I have added an extra condition here to test TTH/MWF Number of times repetition
else if (isAfter(nextTime, range.lastTime()) ){
break;
}
do{
nextCalendarDate = (GregorianCalendar) startCalendarDate.clone();
nextCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, currentCount);//"1" is the recurrence type but here thing are confusing
if((getInterval()>1&&(nextCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)==7))){
nextCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, ((getInterval()-1)*7)+currentCount);//"1"is the supposed recuurence Type
currentCount+=((getInterval()-1)*7)+1;
}
else {
nextCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 1);
currentCount++;
}
} while((nextCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)%2!=0));
hitCount++;
} while (true);
return rv;
}
} // MWFRecurrenceRule