/*
* Copyright (c) 2009, University of Bristol
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3) Neither the name of the University of Bristol nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.ilrt.mca.harvester.events;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.apache.log4j.Logger;
import org.ilrt.mca.Common;
import org.ilrt.mca.domain.BaseItem;
import org.ilrt.mca.domain.Item;
/**
* @author Chris Bailey (c.bailey@bristol.ac.uk)
*/
public class EventItemImpl extends BaseItem implements EventItem, Comparable<Item> {
Logger log = Logger.getLogger(EventItemImpl.class);
public EventItemImpl(){}
Date startDate;
Date endDate;
String location;
String organiser;
String provenance;
// Repreating events specificiation,
// See http://tools.ietf.org/html/rfc2445
enum FREQ {DAILY,WEEKLY,MONTHLY,YEARLY } // currently don't support SECONDLY,MINUTELY,HOURLY
enum DAYS { MO, TU, WE, TH, FR, SA, SU }
ArrayList <DAYS> _byDay = new ArrayList(); /* If present, this indicates the nth occurrence of the specific day within the MONTHLY or YEARLY RRULE. */
ArrayList <Integer> _byMonth = new ArrayList(); /* The BYMONTH rule part specifies a COMMA character (US-ASCII decimal 44) separated list of months of the year. Valid values are 1 to 12. */
int _count = -1; /* The COUNT rule part defines the number of occurrences at which to range-bound the recurrence */
int _interval = 1;/* The INTERVAL rule part contains a positive integer representing how often the recurrence rule repeats */
Date _until = null; /* The UNTIL rule part defines a date-time value which bounds the recurrence rule in an inclusive manner */
DAYS _wkst; /* The WKST rule part specifies the day on which the workweek starts. */
FREQ _freq = null; /* The FREQ rule part identifies the type of recurrence rule. This rule part MUST be specified in the recurrence rule. */
public void setStartDate(Date date) {
this.startDate = date;
}
@Override
public Date getStartDate()
{
return this.startDate;
}
public long getStartDateAsTimeStamp()
{
return this.startDate.getTime();
}
public void setEndDate(Date date) {
this.endDate = date;
}
@Override
public Date getEndDate()
{
return this.endDate;
}
@Override
/* Removing any newline characters shown in the description field */
public void setDescription(String description)
{
if (description == null) return;
description = description.replaceAll("\\\\n", "");
description = Common.closeAllTags(description);
super.setDescription(description);
}
public void setLocation(String s)
{
this.location = s;
}
@Override
public String getLocation()
{
return this.location;
}
public void setOrganiser(String s)
{
this.organiser = s;
}
@Override
public String getOrganiser()
{
return this.organiser;
}
@Override
public String getProvenance() {
return provenance;
}
public void setProvenance(String provenance) {
this.provenance = provenance;
}
@Override
public int compareTo(Item n) {
if (n instanceof EventItem)
{
EventItem event = (EventItem)n;
return this.getStartDate().compareTo(event.getStartDate());
}
return 0;
}
public void setCount(int i)
{
this._count = i;
}
public void setUntil(Date date)
{
this._until = date;
}
public void setStartOfWeek(String wkst)
{
try
{
this._wkst = DAYS.valueOf(wkst);
}
catch (IllegalArgumentException iae)
{
iae.printStackTrace();
}
}
public void setFrequency(String freq)
{
try
{
this._freq = FREQ.valueOf(freq);
}
catch (IllegalArgumentException iae)
{
log.info("Unable to convert " + freq + " into a valid frequency");
}
}
public void setByDays(String days)
{
this._byDay = new ArrayList();
String [] selectedDays = days.split(",");
for(String s : selectedDays)
{
try
{
DAYS d = DAYS.valueOf(s);
this._byDay.add(d);
}
catch (IllegalArgumentException iae)
{
log.info("Unable to convert " + days + " into a valid day of the week");
}
}
Collections.sort(this._byDay);
}
public void setByMonth(String month)
{
this._byMonth = new ArrayList();
if (month != null && month.length() > 0)
{
String [] selectedMonths = month.split(",");
for(String s : selectedMonths)
{
try
{
Integer i = Integer.parseInt(s);
this._byMonth.add(i);
}
catch (NumberFormatException nfe)
{
log.info("Unable to convert " + s + " into a valid integer");
}
}
Collections.sort(this._byMonth);
}
}
public boolean repeatsForever()
{
if (_until == null && _count < 0)
{
return true;
}
return false;
}
public boolean isRecurring()
{
if (this._freq != null && (_until != null || _count > -1 || this._byDay.size() > 0 || this._byMonth.size() > 0))
{
return true;
}
return false;
}
public List<Date> getRecurringDatesUntil(Date endDate)
{
ArrayList dates = new ArrayList();
Calendar cal = Calendar.getInstance();
cal.setTime(startDate);
int datesToGenerate = this._count;
Date nextDate = this.startDate;
int pointer = 0;
//System.out.println("Generating ("+datesToGenerate+") dates"+" freq:"+this._freq);
//System.out.println("until "+this._until);
//System.out.println("start:"+this.startDate);
//System.out.println("end "+endDate);
//System.out.print("_byMonth:");
//for (int i : this._byMonth)
// {
//System.out.print(i+",");
//}
//System.out.print("\n_byDay:");
//for (DAYS i : this._byDay)
// {
//System.out.print(i+",");
//}
//System.out.print("\n");
if (!this._freq.equals(FREQ.DAILY) && !this._freq.equals(FREQ.WEEKLY) && !this._freq.equals(FREQ.MONTHLY))
{
log.warn("Unable to handle this frequency type - not implemented");
return dates;
}
if (this._byDay.size() > 0)
{
log.warn("Unable to handle specific BYDAY restrictions - assuming all days of the week");
}
// setup default pointer position
if ((this._freq.equals(FREQ.DAILY) || this._freq.equals(FREQ.MONTHLY)) && this._byMonth.size() > 0)
{
int currMonth = cal.get(Calendar.MONTH) + 1;
if (this._byMonth.contains(currMonth)) pointer = this._byMonth.indexOf(currMonth);
else
{
for (int i : this._byMonth)
{
if (i <= currMonth)
{
pointer = this._byMonth.indexOf(i);
}
}
}
if (this._byMonth.size() == 1) pointer = -1; // as this will automatically get incremented
}
// loop through calculating the next date
while ((datesToGenerate == -1 || datesToGenerate > 0) && (nextDate.compareTo(endDate) <= 0) && (this._until != null && nextDate.compareTo(this._until) <= 0))
{
cal.setTime(nextDate);
switch (this._freq)
{
case DAILY:
cal.add(Calendar.DAY_OF_MONTH, 1);
if (this._byMonth.size() > 0)
{
int currentMonth = cal.get(Calendar.MONTH)+1;// starting from 0
if (!this._byMonth.contains(currentMonth))
{
// only allow days withing specified months, so in this case, reset
cal.add(Calendar.DAY_OF_MONTH, -1);
// increament pointer
pointer = pointer + 1;
if (pointer >= this._byMonth.size())
{
pointer = 0;
cal.add(Calendar.YEAR, 1);
}
int nextMonth = this._byMonth.get(pointer);
// calculate next month
cal.set(Calendar.MONTH, nextMonth-1);
cal.set(Calendar.DAY_OF_MONTH, 1);
}
}
break;
case WEEKLY:
cal.add(Calendar.DAY_OF_MONTH, 7);
if (this._byMonth.size() > 0)
{
pointer = pointer + 1;
while (!isAllowed(cal.get(Calendar.MONTH)+1))
{
cal.add(Calendar.DAY_OF_MONTH, 7);
}
}
break;
case MONTHLY:
if (this._byMonth.size() > 0)
{
// increament pointer
pointer = pointer + 1;
if (pointer >= this._byMonth.size())
{
pointer = 0;
cal.add(Calendar.YEAR, 1);
}
int nextMonth = this._byMonth.get(pointer);
// calculate next month
cal.set(Calendar.MONTH, nextMonth-1);
// what happends if month over max allowed for this month? - bug to fix
}
else
{
cal.add(Calendar.MONTH, 1);
}
break;
}
nextDate = cal.getTime();
if ((datesToGenerate == -1 || datesToGenerate > 0) && (nextDate.compareTo(endDate) <= 0) && (this._until != null && nextDate.compareTo(this._until) <= 0))
{
if (datesToGenerate > 0) datesToGenerate--;
log.debug("Adding " + nextDate + " datesToGenerate " + datesToGenerate);
dates.add(nextDate);
}
}
log.info("Generated " + dates.size() + " new dates");
return dates;
}
@Override
public EventItemImpl clone()
{
EventItemImpl item = new EventItemImpl();
item.setId(this.getId());
item.setLabel(this.getLabel());
item.setDescription(this.getDescription());
item.setLocation(this.getLocation());
item.setOrganiser(this.getOrganiser());
item.setProvenance(this.getProvenance());
item.setOrder(this.getOrder());
item.setType(this.getType());
item.setTemplate(this.getTemplate());
item.setStartDate(this.getStartDate());
item.setEndDate(this.getEndDate());
return item;
}
/** Simply check if supplied integer is present in the _byMonth array **/
private boolean isAllowed(int q)
{
for (int i : this._byMonth)
{
if (i == q) return true;
}
return false;
}
}