package er.extensions.components;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webobjects.appserver.WODisplayGroup;
import com.webobjects.eocontrol.EODataSource;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSKeyValueCodingAdditions;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSTimeZone;
import com.webobjects.foundation.NSTimestamp;
import er.extensions.eof.ERXConstant;
import er.extensions.foundation.ERXTimestampUtilities;
/**
* Works much the same as a {@link WODisplayGroup}.
* See {@link ERXMonthView} for an example on how to use it.
*
* @author ak on Mon Nov 04 2002
*/
public class ERXDateGrouper extends WODisplayGroup {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(ERXDateGrouper.class);
public static final int DAY = Calendar.DAY_OF_YEAR;
public static final int MONTH = Calendar.MONTH;
public static final int WEEK = Calendar.WEEK_OF_YEAR;
public static final int YEAR = Calendar.YEAR;
static NSMutableDictionary cachedDays = new NSMutableDictionary();
static int currentYear = ERXTimestampUtilities.yearOfCommonEra(new NSTimestamp());
protected NSArray _objects;
protected String _dateKeyPath;
protected NSTimestamp _selectedDate = new NSTimestamp();
protected NSTimestamp _currentDate;
protected NSTimestamp _firstDateInSameMonth;
protected NSTimestamp _firstDateInNextMonth;
protected NSTimestamp _today = new NSTimestamp();
protected int _currentMonth;
protected int _groupingMode = DAY;
protected int _currentWeek;
protected int _currentDayOfMonth;
protected int _currentDayOfWeek;
protected NSArray _datesForCurrentWeek;
protected NSMutableDictionary _groupedObjects;
protected NSMutableArray _datesForWeeksForCurrentMonth;
protected boolean _weekStartsMonday = false;
public boolean weekStartsMonday() {
return _weekStartsMonday;
}
public void setWeekStartsMonday(boolean value) {
_reset();
_weekStartsMonday = value;
}
public boolean hasNoObjectsForCurrentDate() {
return displayedObjects().count() == 0;
}
public boolean isToday() {
return ERXTimestampUtilities.differenceByDay(today(), currentDate()) == 0;
}
public boolean isSelectedDate() {
return ERXTimestampUtilities.differenceByDay(selectedDate(), currentDate()) == 0;
}
public boolean isInMonth() {
return ERXTimestampUtilities.differenceByDay(firstDateInSameMonth(), currentDate()) >= 0 && ERXTimestampUtilities.differenceByDay(firstDateInNextMonth(), currentDate()) < 0;
}
public String dateKeyPath() { return _dateKeyPath; }
public void setDateKeyPath(String value) {
_groupedObjects = null;
_dateKeyPath = value;
}
public int groupingMode() { return _groupingMode; }
public void setGroupingMode(int value) {
_groupingMode = value;
_reset();
}
@Override
public NSArray allObjects() {
return super.allObjects();
}
@Override
public void setObjectArray(NSArray value) {
_groupedObjects = null;
_reset();
super.setObjectArray(value);
}
@Override
public void setDataSource(EODataSource value) {
_groupedObjects = null;
_reset();
super.setDataSource(value);
}
public NSTimestamp today() {
return _today;
}
protected void _reset() {
_firstDateInSameMonth = null;
_firstDateInNextMonth = null;
_datesForWeeksForCurrentMonth = null;
}
public NSTimestamp selectedDate() { return _selectedDate == null ? today() : _selectedDate; }
public void setSelectedDate(Date date) {
NSTimestamp value = (date instanceof NSTimestamp)?(NSTimestamp)date:new NSTimestamp(date);
_reset();
_selectedDate = value;
}
public NSTimestamp currentDate() { return _currentDate == null ? selectedDate() : _currentDate; }
public void setCurrentDate(NSTimestamp value) {
_currentDate = value;
}
protected Object _groupingKeyForDate(NSTimestamp date) {
int value = 0;
switch(groupingMode()) {
case DAY:
value = ERXTimestampUtilities.dayOfCommonEra(date) - currentYear * 365;
break;
case MONTH:
value = ERXTimestampUtilities.yearOfCommonEra(date) * 12 + ERXTimestampUtilities.monthOfYear(date) - currentYear * 12;
break;
case WEEK:
value = ERXTimestampUtilities.yearOfCommonEra(date) * 53 + ERXTimestampUtilities.monthOfYear(date) - currentYear * 53;
break;
case YEAR:
value = ERXTimestampUtilities.yearOfCommonEra(date) - currentYear;
break;
}
return ERXConstant.integerForInt(value);
}
protected NSDictionary _groupedObjects() {
if(_groupedObjects == null) {
_groupedObjects = new NSMutableDictionary();
for (Enumeration e = allObjects().objectEnumerator(); e.hasMoreElements();) {
Object eo = e.nextElement();
Object date = NSKeyValueCodingAdditions.Utility.valueForKeyPath(eo, dateKeyPath());
boolean isNullKey = date == null || date instanceof NSKeyValueCoding.Null;
if (!isNullKey) {
Object key = _groupingKeyForDate((NSTimestamp)date);
NSMutableArray existingGroup = (NSMutableArray)_groupedObjects.objectForKey(key);
if (existingGroup == null) {
existingGroup = new NSMutableArray();
_groupedObjects.setObjectForKey(existingGroup,key);
}
existingGroup.addObject(eo);
}
}
}
return _groupedObjects;
}
@Override
public NSArray displayedObjects() {
NSArray _displayedObjects = (NSArray)_groupedObjects().objectForKey(_groupingKeyForDate(currentDate()));
return _displayedObjects == null ? NSArray.EmptyArray : _displayedObjects;
}
protected NSTimestamp _dateForDayInYear(int year, int day) {
synchronized(cachedDays) {
String key = year + "-" + day;
NSTimestamp date = (NSTimestamp)cachedDays.valueForKey(key);
if(date == null) {
date = new NSTimestamp(year, 1, day, 0, 0, 0, NSTimeZone.defaultTimeZone());
cachedDays.setObjectForKey(date, key);
}
return date;
}
}
protected NSArray _datesForYearStartDays(int year, int startOffset, int count) {
NSMutableArray dates = new NSMutableArray();
for(int i = 0; i < count; i++) {
dates.addObject(_dateForDayInYear(year, startOffset+i));
}
return dates;
}
protected NSTimestamp _firstDateInSameWeek(NSTimestamp value) {
int dayOfWeek = ERXTimestampUtilities.dayOfWeek(value);
int dayOfYear = ERXTimestampUtilities.dayOfYear(value);
if(log.isDebugEnabled()) {
log.debug("dayOfYear: {}", dayOfYear);
log.debug("dayOfWeek: {}", dayOfWeek);
log.debug("SUNDAY: {}", Calendar.SUNDAY);
}
int startOfWeek = weekStartsMonday() ? Calendar.MONDAY : Calendar.SUNDAY;
if(dayOfWeek == startOfWeek) {
return _dateForDayInYear(ERXTimestampUtilities.yearOfCommonEra(value), ERXTimestampUtilities.dayOfYear(value));
}
int offset = !weekStartsMonday() ? 1 : (dayOfWeek == Calendar.SUNDAY ? -5 : 2);
return _dateForDayInYear(ERXTimestampUtilities.yearOfCommonEra(value), ERXTimestampUtilities.dayOfYear(value) - dayOfWeek + offset);
}
protected NSTimestamp _firstDateInSameMonth(NSTimestamp value) {
int dayOfMonth = ERXTimestampUtilities.dayOfMonth(value);
int dayOfYear = ERXTimestampUtilities.dayOfYear(value);
if(log.isDebugEnabled()) {
log.debug("dayOfYear: {}", dayOfYear);
log.debug("dayOfMonth: {}", dayOfMonth);
}
return _dateForDayInYear(ERXTimestampUtilities.yearOfCommonEra(value), ERXTimestampUtilities.dayOfYear(value) - dayOfMonth + 1);
}
public NSTimestamp firstDateInSameMonth() {
if(_firstDateInSameMonth == null) {
_firstDateInSameMonth = _firstDateInSameMonth(selectedDate());
}
return _firstDateInSameMonth;
}
public NSTimestamp firstDateInNextMonth() {
if(_firstDateInNextMonth == null) {
_firstDateInNextMonth = firstDateInSameMonth().timestampByAddingGregorianUnits(0, 1, 0, 0, 0, 0);
}
return _firstDateInNextMonth;
}
protected NSArray _weekDatesForDate(NSTimestamp week) {
NSTimestamp startDate = _firstDateInSameWeek(week);
int startOffset = ERXTimestampUtilities.dayOfYear(startDate);
int year = ERXTimestampUtilities.yearOfCommonEra(startDate);
return _datesForYearStartDays(year, startOffset, 7);
}
public NSTimestamp lastDateForMonth() {
return (NSTimestamp)((NSArray)datesForWeeksForCurrentMonth().lastObject()).lastObject();
}
public NSTimestamp firstDateForMonth() {
return (NSTimestamp)((NSArray)datesForWeeksForCurrentMonth().objectAtIndex(0)).objectAtIndex(0);
}
public NSArray datesForWeeksForCurrentMonth() {
if(_datesForWeeksForCurrentMonth == null) {
_datesForWeeksForCurrentMonth = new NSMutableArray();
NSTimestamp startDate = firstDateInSameMonth();
NSTimestamp endDate = firstDateInNextMonth();
startDate = _firstDateInSameWeek(startDate);
int year = ERXTimestampUtilities.yearOfCommonEra(startDate);
int startOffset = ERXTimestampUtilities.dayOfYear(startDate);
for(int i = 0; i < 6; i ++) {
NSMutableArray weekDates = new NSMutableArray();
for(int j = 0; j < 7; j ++) {
NSTimestamp day = _dateForDayInYear(year, startOffset + i * 7 + j);
if(j == 0 && ERXTimestampUtilities.differenceByDay(endDate, day) >= 0) {
return _datesForWeeksForCurrentMonth;
}
weekDates.addObject(day);
}
_datesForWeeksForCurrentMonth.addObject(weekDates);
}
}
return _datesForWeeksForCurrentMonth;
}
public void setDatesForCurrentWeek(NSArray value) {
_datesForCurrentWeek = value;
}
public NSArray datesForCurrentWeek() {
NSArray result = _datesForCurrentWeek;
if(result == null) {
// The weekOfMonth result is one based, not zero based
int weekOfMonth = ERXTimestampUtilities.weekOfMonth(selectedDate()) - 1;
// if the first week of the month has less than Calendar.getMinimalDaysInFirstWeek() (usually 4 days),
// the week belongs to the previous month. The weekOfMonth variable will then contain -1.
if (weekOfMonth == -1) {
log.debug("weekOfMonth is -1, setting to 0");
weekOfMonth = 0;
}
result = (NSArray)datesForWeeksForCurrentMonth().objectAtIndex(weekOfMonth);
}
return result;
}
public NSArray datesForCurrentMonth() {
NSTimestamp startDate = currentDate().timestampByAddingGregorianUnits(0, 0, -ERXTimestampUtilities.dayOfMonth(currentDate()) + 1, 0, 0, 0);
int year = ERXTimestampUtilities.yearOfCommonEra(startDate);
int startOffset = ERXTimestampUtilities.dayOfYear(startDate);
int daysInMonth = 31;
if(ERXTimestampUtilities.monthOfYear(startDate) != 12) {
daysInMonth = ERXTimestampUtilities.dayOfYear(startDate.timestampByAddingGregorianUnits(0, 1, -1, 0, 0, 0)) - startOffset + 1;
}
return _datesForYearStartDays(year, startOffset, daysInMonth);
}
public void goToToday() {
setSelectedDate(today());
}
public void nextMonth() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(0, 1, 0, 0, 0, 0));
}
public void previousMonth() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(0, -1, 0, 0, 0, 0));
}
public void nextDay() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(0, 0, 1, 0, 0, 0));
}
public void previousDay() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(0, 0, -1, 0, 0, 0));
}
public void nextWeek() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(0, 0, 7, 0, 0, 0));
}
public void previousWeek() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(0, 0, -7, 0, 0, 0));
}
public void nextYear() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(1, 0, 0, 0, 0, 0));
}
public void previousYear() {
setSelectedDate(selectedDate().timestampByAddingGregorianUnits(-1, 0, 0, 0, 0, 0));
}
}