mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-07 18:25:23 +00:00
40347: GERMAN: Translation update based on EN 40202 40348: SPANISH: Translation update based on EN 40202, fixes: ALF-15360 and ALF-15361 40353: RUSSIAN: Adds official support for Russian (locale: ru) to Alfresco. Initial translation based on EN r38926. 40361: ALF-15453: Incorrect manage permissions working for a file/folder 40362: CHINESE: renames an incorrectly named file. 40382: Fix for ALF-15491 SOLR is generating queries for lucene style cross-language support 40389: ALF-14399 : Added method removeTimeZoneIfIsAllDay to AbstractCalendarWebScript. Used to strip time zone information from all day events, was forced to handle date patterns in the same method due to legacy date format requirements. java.util.Date has been replaced with iso8061 date String in the FTL model to ensure local server time zone is not added when we have explicitly removed it. 40401: Part for for ALF-15406 Index Tracker seems not to gracefully stop upon shutdown keeping all other threads in waiting 40406: A slash was missing (the sites in the folder picker couldn't be shown) 40410: ALF-13190 and ALF-13287: both bugs related to the iCal SETPOS attribute. SETPOS (for outlook) specifies the week number with in a month. Possible values are -1 to 4. The positive values count forward while negative count backwards, -1 should represent the last week in a month. The method toDayOfWeekInMonth within CalendarReccurenceHelper was not setup to handle -1, this has been corrected. buildRecurrenceString within CalendarEntryGet was incorrectly using SETPOS to lookup day name. 40412: GERMAN: Translation updates based on EN r40357 40413: SPANISH: Translation updates based on EN r40357 40414: FRENCH: Translation updates based on EN r40357 40415: ITALIAN: Translation updates based on EN r40357 40416: JAPANESE: Translation updates based on EN r40357 40417: DUTCH: Translation updates based on EN r40357 40418: CHINESE: Translation updates based on EN r40357 40420: First half of fix for ALF-12803 - No user feedback: Cannot transformed content with password. (Failure of synchronous rule causes upload to fail with unhelpful message) - reporting error information inline when DnD upload is used - TODO: push JSON error response through the Flash Adaptor uploader component (requires swf uploader modifications) 40428: Merged V4.1 to V4.1-BUG-FIX 40349: CloudSync: fix move out (=> target delete) - add unit tests ( ALF-14655 / ALF-15011 ) 40364: Testcase for ALF-15178, which is not reproduced. 40381: ALF-15295: Cannot access folders beyond first page of document library 40393: BDE-93 cleanup notice file, especially updating outdated URLs 40419: Fix for the MLText parts of ALF-15502. 40427: Merged DEV/COMMUNITY-4.0-BUGFIX to V4.1 40425: ALF-15470: Solr distribution changes - Added scripts for regenerating keystores to repository keystore directory - Included this directory in the solr distribution zip under alf_data/keystore for easier installation to tomcat git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@40429 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
695 lines
23 KiB
Java
695 lines
23 KiB
Java
/*
|
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
|
*
|
|
* This file is part of Alfresco
|
|
*
|
|
* Alfresco is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Alfresco 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.alfresco.service.cmr.calendar;
|
|
|
|
import java.text.DateFormatSymbols;
|
|
import java.util.ArrayList;
|
|
import java.util.Calendar;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.extensions.surf.util.I18NUtil;
|
|
|
|
/**
|
|
* This class provides helper functions for when working
|
|
* with recurring {@link CalendarEntry} instances.
|
|
* It provides support for working with key parts of the
|
|
* Outlook/SharePoint recurrence rules
|
|
*
|
|
* @author Nick Burch
|
|
* @since 4.0
|
|
*/
|
|
public class CalendarRecurrenceHelper
|
|
{
|
|
private static Log logger = LogFactory.getLog(CalendarRecurrenceHelper.class);
|
|
|
|
@SuppressWarnings("serial")
|
|
protected final static Map<String,Integer> d2cd =
|
|
Collections.unmodifiableMap(new HashMap<String, Integer>() {{
|
|
put("SU", Calendar.SUNDAY);
|
|
put("MO", Calendar.MONDAY);
|
|
put("TU", Calendar.TUESDAY);
|
|
put("WE", Calendar.WEDNESDAY);
|
|
put("TH", Calendar.THURSDAY);
|
|
put("FR", Calendar.FRIDAY);
|
|
put("SA", Calendar.SATURDAY);
|
|
}});
|
|
|
|
/**
|
|
* The lookup from the day strings to Calendar Day entries
|
|
*/
|
|
public static final Map<String,Integer> DAY_NAMES_TO_CALENDAR_DAYS =
|
|
Collections.unmodifiableMap(d2cd);
|
|
|
|
/**
|
|
* Returns a lookup from recurrence rule days of the week, to
|
|
* the proper days of the week in the specified locale
|
|
*/
|
|
public static Map<String,String> buildLocalRecurrenceDaysOfTheWeek(Locale locale)
|
|
{
|
|
// Get our days of the week, in the current locale
|
|
DateFormatSymbols dates = new DateFormatSymbols(I18NUtil.getLocale());
|
|
String[] weekdays = dates.getWeekdays();
|
|
|
|
// And map them based on the outlook two letter codes
|
|
Map<String,String> days = new HashMap<String, String>();
|
|
for(Map.Entry<String,Integer> e : DAY_NAMES_TO_CALENDAR_DAYS.entrySet())
|
|
{
|
|
days.put(e.getKey(), weekdays[e.getValue()]);
|
|
}
|
|
return days;
|
|
}
|
|
|
|
|
|
@SuppressWarnings("serial")
|
|
protected final static Map<Integer,String> WEEK_NUMBER_TO_WEEK_NAME =
|
|
Collections.unmodifiableMap(new HashMap<Integer, String>() {{
|
|
put(1, "first");
|
|
put(2, "second");
|
|
put(3, "third");
|
|
put(4, "fourth");
|
|
put(-1, "last");
|
|
}});
|
|
|
|
|
|
/**
|
|
* The lookup from the week in month number to week
|
|
* in month name in the specified locale
|
|
*/
|
|
public static Map<Integer, String> buildLocalRecurrenceWeekNames(Locale locale){
|
|
return WEEK_NUMBER_TO_WEEK_NAME;
|
|
}
|
|
|
|
/**
|
|
* Returns the parsed calendar recurrence rule
|
|
* WARNING - Alfresco use only. Return type will likely shift to
|
|
* a real object in the near future
|
|
*/
|
|
public static Map<String,String> extractRecurrenceRule(CalendarEntry entry)
|
|
{
|
|
return extractRecurrenceRule(entry.getRecurrenceRule());
|
|
}
|
|
|
|
/**
|
|
* Returns the parsed calendar recurrence rule
|
|
* WARNING - Alfresco use only. Return type will likely shift to
|
|
* a real object in the near future
|
|
*/
|
|
private static Map<String,String> extractRecurrenceRule(String recurrenceRule)
|
|
{
|
|
if (recurrenceRule == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// Turn the string into a useful map
|
|
Map<String,String> params = new HashMap<String, String>();
|
|
for (String rule : recurrenceRule.split(";"))
|
|
{
|
|
String[] parts = rule.split("=");
|
|
if (parts.length != 2)
|
|
{
|
|
logger.warn("Invalid rule '" + rule + "' in recurrence: " + recurrenceRule);
|
|
}
|
|
else
|
|
{
|
|
params.put(parts[0], parts[1]);
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
/**
|
|
* Outlook does some crazy stuff, which is only just about permitted by
|
|
* the specification, and is hard to parse, especially for yearly events.
|
|
* Fix these to more normal cases where possible
|
|
*/
|
|
protected static Map<String,String> fixOutlookRecurrenceQuirks(Map<String,String> params)
|
|
{
|
|
if (params.containsKey("FREQ"))
|
|
{
|
|
// Is it really yearly?
|
|
if ("MONTHLY".equals(params.get("FREQ")) &&
|
|
params.get("BYMONTH") != null)
|
|
{
|
|
// Outlook can be "delightfully" different, and likes to generate
|
|
// events that recur yearly on a specific date+month as FREQ=MONTHLY
|
|
// Detect those cases, and treat as YEARLY as per the spec
|
|
params.put("FREQ", "YEARLY");
|
|
|
|
// Outlook will sometimes do nth of the month (eg 17) instead as
|
|
// BYDAY={any}, BYSETPOS=n
|
|
if (params.containsKey("BYDAY") && params.containsKey("BYSETPOS"))
|
|
{
|
|
int days = params.get("BYDAY").split(",").length;
|
|
if (days == 7)
|
|
{
|
|
// Make it normal
|
|
params.put("BYMONTHDAY", params.get("BYSETPOS"));
|
|
params.remove("BYDAY");
|
|
params.remove("BYSETPOS");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return params;
|
|
}
|
|
|
|
|
|
/**
|
|
* For the given Calendar Entry, return its subsequent Recurrence on or after
|
|
* the specified date, until the given limit. If it doesn't have any recurrences
|
|
* on or after the start date (either no recurrence rules, or the last recurrence
|
|
* date is before then), null will be returned. (The onOrAfter and until dates
|
|
* are treat as inclusive)
|
|
* If requested, can stop after the first hit
|
|
* @return The next recurrence on or after the given date, or null if there aren't any
|
|
*/
|
|
public static List<Date> getRecurrencesOnOrAfter(CalendarEntry entry, Date onOrAfter,
|
|
Date until, boolean firstOnly)
|
|
{
|
|
return getRecurrencesOnOrAfter(
|
|
entry.getRecurrenceRule(), entry.getStart(), entry.getEnd(),
|
|
entry.getLastRecurrence(), onOrAfter, until, firstOnly);
|
|
}
|
|
|
|
/**
|
|
* For the given Calendar Entry, return its subsequent Recurrence on or after
|
|
* the specified date, until the given limit. If it doesn't have any recurrences
|
|
* on or after the start date (either no recurrence rules, or the last recurrence
|
|
* date is before then), null will be returned. (The onOrAfter and until dates
|
|
* are treat as inclusive)
|
|
* If requested, can stop after the first hit
|
|
* @return The next recurrence on or after the given date, or null if there aren't any
|
|
*/
|
|
public static List<Date> getRecurrencesOnOrAfter(String recurrenceRule, Date eventStart,
|
|
Date eventEnd, Date lastRecurrence,
|
|
Date onOrAfter, Date until, boolean firstOnly)
|
|
{
|
|
if (recurrenceRule == null)
|
|
{
|
|
// No recurrence
|
|
return null;
|
|
}
|
|
|
|
// See if we're past the last recurrence date
|
|
// Note - we rely on this being set for us, rather than checking the count
|
|
if (lastRecurrence != null && lastRecurrence.before(onOrAfter))
|
|
{
|
|
// Recurrence has stopped by this point
|
|
return null;
|
|
}
|
|
|
|
// Work until the earlier of the last event and the limit
|
|
if (lastRecurrence != null)
|
|
{
|
|
if (until == null)
|
|
{
|
|
until = lastRecurrence;
|
|
}
|
|
else
|
|
{
|
|
if (lastRecurrence.before(until))
|
|
{
|
|
// Last recurrence is earlier, use that
|
|
until = lastRecurrence;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Safety limit - don't recurse for ever!
|
|
if (lastRecurrence == null && !firstOnly && until == null)
|
|
{
|
|
logger.info("No end date set on the recurring event, and no end date " +
|
|
"specified, only fetching first instance");
|
|
firstOnly = true;
|
|
}
|
|
|
|
// To hold our events
|
|
List<Date> dates = new ArrayList<Date>();
|
|
|
|
// Extract out the rule into its parts
|
|
Map<String,String> params = extractRecurrenceRule(recurrenceRule);
|
|
|
|
// Outlook does some crazy stuff, which is only just about
|
|
// permitted by the specification, and is hard to parse
|
|
// Fix these to more normal cases where possible
|
|
params = fixOutlookRecurrenceQuirks(params);
|
|
|
|
// Fetch the frequency and interval
|
|
if (params.containsKey("FREQ"))
|
|
{
|
|
String freq = params.get("FREQ");
|
|
String intervalS = params.get("INTERVAL");
|
|
int interval = 1;
|
|
if (intervalS != null)
|
|
{
|
|
try
|
|
{
|
|
interval = Integer.parseInt(intervalS);
|
|
}
|
|
catch(NumberFormatException e)
|
|
{
|
|
logger.warn("Invalid interval " + intervalS);
|
|
}
|
|
}
|
|
|
|
// Start with today, and roll forward
|
|
Calendar currentDate = Calendar.getInstance();
|
|
currentDate.setTime(eventStart);
|
|
|
|
if ("DAILY".equals(freq))
|
|
{
|
|
buildDailyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval);
|
|
}
|
|
else if ("WEEKLY".equals(freq))
|
|
{
|
|
buildWeeklyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval);
|
|
}
|
|
else if ("MONTHLY".equals(freq))
|
|
{
|
|
buildMonthlyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval);
|
|
}
|
|
else if ("YEARLY".equals(freq))
|
|
{
|
|
buildYearlyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval);
|
|
}
|
|
else
|
|
{
|
|
logger.warn("Unsupported recurrence frequency " + freq);
|
|
}
|
|
|
|
// Return what we've got
|
|
return dates;
|
|
}
|
|
else
|
|
{
|
|
logger.warn("No frequency found, possible invalid rule? " + recurrenceRule);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
protected static void buildDailyRecurrences(Calendar currentDate, List<Date> dates,
|
|
Map<String,String> params, Date onOrAfter, Date until, boolean firstOnly, int interval)
|
|
{
|
|
// Nice and easy
|
|
while (currentDate.getTime().before(onOrAfter))
|
|
{
|
|
currentDate.add(Calendar.DATE, interval);
|
|
}
|
|
|
|
if (firstOnly)
|
|
{
|
|
// Save the first date, if valid
|
|
if (until != null)
|
|
{
|
|
if (currentDate.getTime().before(until))
|
|
{
|
|
dates.add(currentDate.getTime());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dates.add(currentDate.getTime());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Run until the end
|
|
while (! currentDate.getTime().after(until))
|
|
{
|
|
dates.add(currentDate.getTime());
|
|
currentDate.add(Calendar.DATE, interval);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static void buildWeeklyRecurrences(Calendar currentDate, List<Date> dates,
|
|
Map<String,String> params, Date onOrAfter, Date until, boolean firstOnly, int interval)
|
|
{
|
|
// Get a sorted list of the days it applies to
|
|
List<Integer> daysOfWeek = new ArrayList<Integer>();
|
|
for (String dayS : params.get("BYDAY").split(","))
|
|
{
|
|
Integer day = DAY_NAMES_TO_CALENDAR_DAYS.get(dayS);
|
|
if (day == null)
|
|
{
|
|
logger.warn("Invalid day " + dayS);
|
|
}
|
|
else
|
|
{
|
|
daysOfWeek.add(day);
|
|
}
|
|
}
|
|
Collections.sort(daysOfWeek);
|
|
|
|
// Wind forward
|
|
boolean going = true;
|
|
boolean valid = false;
|
|
Date origDate = currentDate.getTime();
|
|
while (going)
|
|
{
|
|
// Check each day
|
|
for (int day : daysOfWeek)
|
|
{
|
|
currentDate.set(Calendar.DAY_OF_WEEK, day);
|
|
if (!valid)
|
|
{
|
|
if (currentDate.getTime().before(onOrAfter))
|
|
{
|
|
// To early
|
|
}
|
|
else if (currentDate.getTime().before(origDate))
|
|
{
|
|
// Too early
|
|
}
|
|
else
|
|
{
|
|
// Now in the right range
|
|
valid = true;
|
|
}
|
|
}
|
|
if (valid)
|
|
{
|
|
if (until != null)
|
|
{
|
|
if (currentDate.getTime().after(until))
|
|
{
|
|
// Too late
|
|
going = false;
|
|
break;
|
|
}
|
|
}
|
|
dates.add(currentDate.getTime());
|
|
if (firstOnly)
|
|
{
|
|
going = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wind on to the next week
|
|
currentDate.set(Calendar.DAY_OF_WEEK, daysOfWeek.get(0));
|
|
currentDate.add(Calendar.DATE, interval*7);
|
|
}
|
|
}
|
|
|
|
protected static void buildMonthlyRecurrences(Calendar currentDate, List<Date> dates,
|
|
Map<String,String> params, Date onOrAfter, Date until, boolean firstOnly, int monthInterval)
|
|
{
|
|
if (params.get("BYMONTHDAY") != null)
|
|
{
|
|
// eg the 15th of each month
|
|
int dayOfMonth = Integer.parseInt(params.get("BYMONTHDAY"));
|
|
if (currentDate.get(Calendar.DAY_OF_MONTH) > dayOfMonth)
|
|
{
|
|
// Move forward to start of the next month
|
|
addMonthToDayOfMonth(currentDate, dayOfMonth, monthInterval);
|
|
}
|
|
else
|
|
{
|
|
// Move to that date in this month
|
|
currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
|
}
|
|
|
|
// Go until in the ok range
|
|
while (currentDate.getTime().before(onOrAfter))
|
|
{
|
|
addMonthToDayOfMonth(currentDate, dayOfMonth, monthInterval);
|
|
}
|
|
while (true)
|
|
{
|
|
if (until != null)
|
|
{
|
|
if (currentDate.getTime().after(until))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
dates.add(currentDate.getTime());
|
|
if(firstOnly)
|
|
{
|
|
break;
|
|
}
|
|
|
|
addMonthToDayOfMonth(currentDate, dayOfMonth, monthInterval);
|
|
}
|
|
}
|
|
else if (params.get("BYSETPOS") != null)
|
|
{
|
|
// eg the first Thursday of the month, or the third Saturday
|
|
int dayOfWeek = -1;
|
|
int instanceInMonth = 1;
|
|
|
|
// There are two forms...
|
|
if (params.containsKey("BYDAY"))
|
|
{
|
|
dayOfWeek = DAY_NAMES_TO_CALENDAR_DAYS.get(params.get("BYDAY"));
|
|
instanceInMonth = Integer.parseInt(params.get("BYSETPOS"));
|
|
}
|
|
else
|
|
{
|
|
// Implies the first one in the month
|
|
dayOfWeek = DAY_NAMES_TO_CALENDAR_DAYS.get(params.get("BYSETPOS"));
|
|
instanceInMonth = 1;
|
|
}
|
|
|
|
// Move to the date in this month
|
|
Date origDate = currentDate.getTime();
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
|
|
// If the instance in this month is in the past, go
|
|
// forward to the point in the next month
|
|
if (currentDate.getTime().before(origDate))
|
|
{
|
|
addMonthToFirstDayOfWeek(currentDate, dayOfWeek, monthInterval);
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
}
|
|
|
|
// Move forward to the required date
|
|
while (currentDate.getTime().before(onOrAfter))
|
|
{
|
|
addMonthToFirstDayOfWeek(currentDate, dayOfWeek, monthInterval);
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
}
|
|
// Roll on until we get valid matches
|
|
while (true)
|
|
{
|
|
if (until != null)
|
|
{
|
|
if (currentDate.getTime().after(until))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
dates.add(currentDate.getTime());
|
|
if (firstOnly)
|
|
{
|
|
break;
|
|
}
|
|
|
|
addMonthToFirstDayOfWeek(currentDate, dayOfWeek, monthInterval);
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static void buildYearlyRecurrences(Calendar currentDate, List<Date> dates,
|
|
Map<String,String> params, Date onOrAfter, Date until, boolean firstOnly, int interval)
|
|
{
|
|
int realMonth = Integer.parseInt(params.get("BYMONTH"));
|
|
int month = realMonth - 1; // Java months count from zero
|
|
|
|
if (params.get("BYMONTHDAY") != null)
|
|
{
|
|
// eg the 2nd of March every year
|
|
int dayOfMonth = Integer.parseInt(params.get("BYMONTHDAY"));
|
|
if (currentDate.get(Calendar.MONTH) == month &&
|
|
currentDate.get(Calendar.DAY_OF_MONTH) == dayOfMonth)
|
|
{
|
|
// Correct start time
|
|
}
|
|
else if (currentDate.get(Calendar.MONTH) < month ||
|
|
(currentDate.get(Calendar.MONTH) == month &&
|
|
currentDate.get(Calendar.DAY_OF_MONTH) < dayOfMonth))
|
|
{
|
|
// The current date is before the requested date this year
|
|
// Move forward to it in this year
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
|
}
|
|
else
|
|
{
|
|
// The current date is after the date this year, move to next year
|
|
currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval);
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
|
}
|
|
|
|
while (currentDate.getTime().before(onOrAfter))
|
|
{
|
|
currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval);
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
|
}
|
|
while (true)
|
|
{
|
|
if (until != null)
|
|
{
|
|
if (currentDate.getTime().after(until))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
dates.add(currentDate.getTime());
|
|
if (firstOnly)
|
|
{
|
|
break;
|
|
}
|
|
|
|
currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval);
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// eg the third Tuesday in February every year
|
|
int dayOfWeek = -1;
|
|
int instanceInMonth = 1;
|
|
|
|
// There are two forms...
|
|
if (params.containsKey("BYDAY"))
|
|
{
|
|
dayOfWeek = DAY_NAMES_TO_CALENDAR_DAYS.get(params.get("BYDAY"));
|
|
instanceInMonth = Integer.parseInt(params.get("BYSETPOS"));
|
|
}
|
|
else
|
|
{
|
|
// Implies the first one in the month
|
|
dayOfWeek = DAY_NAMES_TO_CALENDAR_DAYS.get(params.get("BYSETPOS"));
|
|
instanceInMonth = 1;
|
|
}
|
|
|
|
|
|
// Find when it is this year
|
|
Date origDate = currentDate.getTime();
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, 1);
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
Date thisYear = currentDate.getTime();
|
|
currentDate.setTime(origDate);
|
|
|
|
// Have we missed it for the year? If so, go to next year
|
|
if (currentDate.getTime().after(thisYear))
|
|
{
|
|
currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval);
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, 1);
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
}
|
|
else
|
|
{
|
|
// Otherwise move to it
|
|
currentDate.setTime(thisYear);
|
|
}
|
|
|
|
|
|
// Move forward to the required date
|
|
while (currentDate.getTime().before(onOrAfter))
|
|
{
|
|
currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval);
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, 1);
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
}
|
|
|
|
// Roll on until we get valid matches
|
|
while (true)
|
|
{
|
|
if (until != null)
|
|
{
|
|
if (currentDate.getTime().after(until))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
dates.add(currentDate.getTime());
|
|
if (firstOnly)
|
|
{
|
|
break;
|
|
}
|
|
|
|
currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval);
|
|
currentDate.set(Calendar.MONTH, month);
|
|
currentDate.set(Calendar.DAY_OF_MONTH, 1);
|
|
toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void addMonthToDayOfMonth(Calendar c, int dayOfMonth, int monthInterval)
|
|
{
|
|
for (int i=0; i<monthInterval; i++)
|
|
{
|
|
// Set it to the 1st
|
|
c.set(Calendar.DAY_OF_MONTH, 1);
|
|
// Add 33 days, will be on the 2nd-6th
|
|
c.add(Calendar.DATE, 33);
|
|
// Set to the requred day in the month
|
|
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
|
}
|
|
}
|
|
|
|
private static void addMonthToFirstDayOfWeek(Calendar c, int dayOfWeek, int monthInterval)
|
|
{
|
|
// Go forward to the 1st of next month
|
|
addMonthToDayOfMonth(c, 1, monthInterval);
|
|
|
|
// Set the day of the week
|
|
toDayOfWeekInMonth(c, dayOfWeek, 1);
|
|
}
|
|
|
|
/**
|
|
* Takes you to eg the 2nd Thursday in the month, which may
|
|
* involve going back before the current date
|
|
*/
|
|
private static void toDayOfWeekInMonth(Calendar c, int dayOfWeek, int weekInMonth)
|
|
{
|
|
//set it to the first day
|
|
c.set(Calendar.DATE, 1);
|
|
//move to the day we need
|
|
c.set(Calendar.DAY_OF_WEEK, dayOfWeek);
|
|
//and then to the month
|
|
c.set(Calendar.DAY_OF_WEEK_IN_MONTH, weekInMonth);
|
|
}
|
|
}
|