src/net/fortuna/ical4j/model/Recur.java

branch
ICAL4J_EMBED_1
changeset 15
cc93757aeca3
parent 14
5ae3e5665a0b
child 18
6dcaece8ec41
     1.1 --- a/src/net/fortuna/ical4j/model/Recur.java	Thu Feb 12 18:02:00 2015 +0100
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,1266 +0,0 @@
     1.4 -/**
     1.5 - * Copyright (c) 2012, Ben Fortuna
     1.6 - * All rights reserved.
     1.7 - *
     1.8 - * Redistribution and use in source and binary forms, with or without
     1.9 - * modification, are permitted provided that the following conditions
    1.10 - * are met:
    1.11 - *
    1.12 - *  o Redistributions of source code must retain the above copyright
    1.13 - * notice, this list of conditions and the following disclaimer.
    1.14 - *
    1.15 - *  o Redistributions in binary form must reproduce the above copyright
    1.16 - * notice, this list of conditions and the following disclaimer in the
    1.17 - * documentation and/or other materials provided with the distribution.
    1.18 - *
    1.19 - *  o Neither the name of Ben Fortuna nor the names of any other contributors
    1.20 - * may be used to endorse or promote products derived from this software
    1.21 - * without specific prior written permission.
    1.22 - *
    1.23 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.24 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.25 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.26 - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    1.27 - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    1.28 - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    1.29 - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    1.30 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    1.31 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    1.32 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    1.33 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.34 - */
    1.35 -package net.fortuna.ical4j.model;
    1.36 -
    1.37 -import java.io.IOException;
    1.38 -import java.io.Serializable;
    1.39 -import java.text.ParseException;
    1.40 -import java.util.Calendar;
    1.41 -import java.util.Collections;
    1.42 -import java.util.HashMap;
    1.43 -import java.util.Iterator;
    1.44 -import java.util.List;
    1.45 -import java.util.Map;
    1.46 -import java.util.NoSuchElementException;
    1.47 -import java.util.StringTokenizer;
    1.48 -
    1.49 -import net.fortuna.ical4j.model.parameter.Value;
    1.50 -import net.fortuna.ical4j.util.CompatibilityHints;
    1.51 -import net.fortuna.ical4j.util.Configurator;
    1.52 -import net.fortuna.ical4j.util.Dates;
    1.53 -
    1.54 -import org.apache.commons.logging.Log;
    1.55 -import org.apache.commons.logging.LogFactory;
    1.56 -
    1.57 -/**
    1.58 - * $Id$ [18-Apr-2004]
    1.59 - *
    1.60 - * Defines a recurrence.
    1.61 - * @version 2.0
    1.62 - * @author Ben Fortuna
    1.63 - */
    1.64 -public class Recur implements Serializable {
    1.65 -
    1.66 -    private static final long serialVersionUID = -7333226591784095142L;
    1.67 -
    1.68 -    private static final String FREQ = "FREQ";
    1.69 -
    1.70 -    private static final String UNTIL = "UNTIL";
    1.71 -
    1.72 -    private static final String COUNT = "COUNT";
    1.73 -
    1.74 -    private static final String INTERVAL = "INTERVAL";
    1.75 -
    1.76 -    private static final String BYSECOND = "BYSECOND";
    1.77 -
    1.78 -    private static final String BYMINUTE = "BYMINUTE";
    1.79 -
    1.80 -    private static final String BYHOUR = "BYHOUR";
    1.81 -
    1.82 -    private static final String BYDAY = "BYDAY";
    1.83 -
    1.84 -    private static final String BYMONTHDAY = "BYMONTHDAY";
    1.85 -
    1.86 -    private static final String BYYEARDAY = "BYYEARDAY";
    1.87 -
    1.88 -    private static final String BYWEEKNO = "BYWEEKNO";
    1.89 -
    1.90 -    private static final String BYMONTH = "BYMONTH";
    1.91 -
    1.92 -    private static final String BYSETPOS = "BYSETPOS";
    1.93 -
    1.94 -    private static final String WKST = "WKST";
    1.95 -
    1.96 -    /**
    1.97 -     * Second frequency resolution.
    1.98 -     */
    1.99 -    public static final String SECONDLY = "SECONDLY";
   1.100 -
   1.101 -    /**
   1.102 -     * Minute frequency resolution.
   1.103 -     */
   1.104 -    public static final String MINUTELY = "MINUTELY";
   1.105 -
   1.106 -    /**
   1.107 -     * Hour frequency resolution.
   1.108 -     */
   1.109 -    public static final String HOURLY = "HOURLY";
   1.110 -
   1.111 -    /**
   1.112 -     * Day frequency resolution.
   1.113 -     */
   1.114 -    public static final String DAILY = "DAILY";
   1.115 -
   1.116 -    /**
   1.117 -     * Week frequency resolution.
   1.118 -     */
   1.119 -    public static final String WEEKLY = "WEEKLY";
   1.120 -
   1.121 -    /**
   1.122 -     * Month frequency resolution.
   1.123 -     */
   1.124 -    public static final String MONTHLY = "MONTHLY";
   1.125 -
   1.126 -    /**
   1.127 -     * Year frequency resolution.
   1.128 -     */
   1.129 -    public static final String YEARLY = "YEARLY";
   1.130 -
   1.131 -    /**
   1.132 -     * When calculating dates matching this recur ({@code getDates()} or {@code getNextDate}),
   1.133 -     *  this property defines the maximum number of attempt to find a matching date by
   1.134 -     * incrementing the seed.
   1.135 -     * <p>The default value is 1000. A value of -1 corresponds to no maximum.</p>
   1.136 -     */
   1.137 -    public static final String KEY_MAX_INCREMENT_COUNT = "net.fortuna.ical4j.recur.maxincrementcount";
   1.138 -
   1.139 -    private static int maxIncrementCount;
   1.140 -    static {
   1.141 -        final String value = Configurator.getProperty(KEY_MAX_INCREMENT_COUNT);
   1.142 -        if (value != null && value.length() > 0) {
   1.143 -            maxIncrementCount = Integer.parseInt(value);
   1.144 -        } else {
   1.145 -            maxIncrementCount = 1000;
   1.146 -        }
   1.147 -    }
   1.148 -
   1.149 -    private transient Log log = LogFactory.getLog(Recur.class);
   1.150 -
   1.151 -    private String frequency;
   1.152 -
   1.153 -    private Date until;
   1.154 -
   1.155 -    private int count = -1;
   1.156 -
   1.157 -    private int interval = -1;
   1.158 -
   1.159 -    private NumberList secondList;
   1.160 -
   1.161 -    private NumberList minuteList;
   1.162 -
   1.163 -    private NumberList hourList;
   1.164 -
   1.165 -    private WeekDayList dayList;
   1.166 -
   1.167 -    private NumberList monthDayList;
   1.168 -
   1.169 -    private NumberList yearDayList;
   1.170 -
   1.171 -    private NumberList weekNoList;
   1.172 -
   1.173 -    private NumberList monthList;
   1.174 -
   1.175 -    private NumberList setPosList;
   1.176 -
   1.177 -    private String weekStartDay;
   1.178 -    
   1.179 -    private int calendarWeekStartDay;
   1.180 -
   1.181 -    private Map experimentalValues = new HashMap();
   1.182 -
   1.183 -    // Calendar field we increment based on frequency.
   1.184 -    private int calIncField;
   1.185 -
   1.186 -    /**
   1.187 -     * Default constructor.
   1.188 -     */
   1.189 -    public Recur() {
   1.190 -        // default week start is Monday per RFC5545
   1.191 -        calendarWeekStartDay = Calendar.MONDAY;
   1.192 -    }
   1.193 -    
   1.194 -    /**
   1.195 -     * Constructs a new instance from the specified string value.
   1.196 -     * @param aValue a string representation of a recurrence.
   1.197 -     * @throws ParseException thrown when the specified string contains an invalid representation of an UNTIL date value
   1.198 -     */
   1.199 -    public Recur(final String aValue) throws ParseException {
   1.200 -        // default week start is Monday per RFC5545
   1.201 -        calendarWeekStartDay = Calendar.MONDAY;
   1.202 -        final StringTokenizer t = new StringTokenizer(aValue, ";=");
   1.203 -        while (t.hasMoreTokens()) {
   1.204 -            final String token = t.nextToken();
   1.205 -            if (FREQ.equals(token)) {
   1.206 -                frequency = nextToken(t, token);
   1.207 -            }
   1.208 -            else if (UNTIL.equals(token)) {
   1.209 -                final String untilString = nextToken(t, token);
   1.210 -                if (untilString != null && untilString.indexOf("T") >= 0) {
   1.211 -                    until = new DateTime(untilString);
   1.212 -                    // UNTIL must be specified in UTC time..
   1.213 -                    ((DateTime) until).setUtc(true);
   1.214 -                }
   1.215 -                else {
   1.216 -                    until = new Date(untilString);
   1.217 -                }
   1.218 -            }
   1.219 -            else if (COUNT.equals(token)) {
   1.220 -                count = Integer.parseInt(nextToken(t, token));
   1.221 -            }
   1.222 -            else if (INTERVAL.equals(token)) {
   1.223 -                interval = Integer.parseInt(nextToken(t, token));
   1.224 -            }
   1.225 -            else if (BYSECOND.equals(token)) {
   1.226 -                secondList = new NumberList(nextToken(t, token), 0, 59, false);
   1.227 -            }
   1.228 -            else if (BYMINUTE.equals(token)) {
   1.229 -                minuteList = new NumberList(nextToken(t, token), 0, 59, false);
   1.230 -            }
   1.231 -            else if (BYHOUR.equals(token)) {
   1.232 -                hourList = new NumberList(nextToken(t, token), 0, 23, false);
   1.233 -            }
   1.234 -            else if (BYDAY.equals(token)) {
   1.235 -                dayList = new WeekDayList(nextToken(t, token));
   1.236 -            }
   1.237 -            else if (BYMONTHDAY.equals(token)) {
   1.238 -                monthDayList = new NumberList(nextToken(t, token), 1, 31, true);
   1.239 -            }
   1.240 -            else if (BYYEARDAY.equals(token)) {
   1.241 -                yearDayList = new NumberList(nextToken(t, token), 1, 366, true);
   1.242 -            }
   1.243 -            else if (BYWEEKNO.equals(token)) {
   1.244 -                weekNoList = new NumberList(nextToken(t, token), 1, 53, true);
   1.245 -            }
   1.246 -            else if (BYMONTH.equals(token)) {
   1.247 -                monthList = new NumberList(nextToken(t, token), 1, 12, false);
   1.248 -            }
   1.249 -            else if (BYSETPOS.equals(token)) {
   1.250 -                setPosList = new NumberList(nextToken(t, token), 1, 366, true);
   1.251 -            }
   1.252 -            else if (WKST.equals(token)) {
   1.253 -                weekStartDay = nextToken(t, token);
   1.254 -                calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay));
   1.255 -            }
   1.256 -            else {
   1.257 -            	if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) {
   1.258 -	            	// assume experimental value..
   1.259 -	                experimentalValues.put(token, nextToken(t, token));
   1.260 -            	}
   1.261 -            	else {
   1.262 -            		throw new IllegalArgumentException("Invalid recurrence rule part: " + 
   1.263 -            				token + "=" + nextToken(t, token));
   1.264 -            	}
   1.265 -            }
   1.266 -        }
   1.267 -        validateFrequency();
   1.268 -    }
   1.269 -
   1.270 -    private String nextToken(StringTokenizer t, String lastToken) {
   1.271 -        try {
   1.272 -            return t.nextToken();
   1.273 -        }
   1.274 -        catch (NoSuchElementException e) {
   1.275 -            throw new IllegalArgumentException("Missing expected token, last token: " + lastToken);
   1.276 -        }
   1.277 -    }
   1.278 -    
   1.279 -    /**
   1.280 -     * @param frequency a recurrence frequency string
   1.281 -     * @param until maximum recurrence date
   1.282 -     */
   1.283 -    public Recur(final String frequency, final Date until) {
   1.284 -        // default week start is Monday per RFC5545
   1.285 -        calendarWeekStartDay = Calendar.MONDAY;
   1.286 -        this.frequency = frequency;
   1.287 -        this.until = until;
   1.288 -        validateFrequency();
   1.289 -    }
   1.290 -
   1.291 -    /**
   1.292 -     * @param frequency a recurrence frequency string
   1.293 -     * @param count maximum recurrence count
   1.294 -     */
   1.295 -    public Recur(final String frequency, final int count) {
   1.296 -        // default week start is Monday per RFC5545
   1.297 -        calendarWeekStartDay = Calendar.MONDAY;
   1.298 -        this.frequency = frequency;
   1.299 -        this.count = count;
   1.300 -        validateFrequency();
   1.301 -    }
   1.302 -
   1.303 -    /**
   1.304 -     * @return Returns the dayList.
   1.305 -     */
   1.306 -    public final WeekDayList getDayList() {
   1.307 -        if (dayList == null) {
   1.308 -            dayList = new WeekDayList();
   1.309 -        }
   1.310 -        return dayList;
   1.311 -    }
   1.312 -
   1.313 -    /**
   1.314 -     * @return Returns the hourList.
   1.315 -     */
   1.316 -    public final NumberList getHourList() {
   1.317 -        if (hourList == null) {
   1.318 -            hourList = new NumberList(0, 23, false);
   1.319 -        }
   1.320 -        return hourList;
   1.321 -    }
   1.322 -
   1.323 -    /**
   1.324 -     * @return Returns the minuteList.
   1.325 -     */
   1.326 -    public final NumberList getMinuteList() {
   1.327 -        if (minuteList == null) {
   1.328 -            minuteList = new NumberList(0, 59, false);
   1.329 -        }
   1.330 -        return minuteList;
   1.331 -    }
   1.332 -
   1.333 -    /**
   1.334 -     * @return Returns the monthDayList.
   1.335 -     */
   1.336 -    public final NumberList getMonthDayList() {
   1.337 -        if (monthDayList == null) {
   1.338 -            monthDayList = new NumberList(1, 31, true);
   1.339 -        }
   1.340 -        return monthDayList;
   1.341 -    }
   1.342 -
   1.343 -    /**
   1.344 -     * @return Returns the monthList.
   1.345 -     */
   1.346 -    public final NumberList getMonthList() {
   1.347 -        if (monthList == null) {
   1.348 -            monthList = new NumberList(1, 12, false);
   1.349 -        }
   1.350 -        return monthList;
   1.351 -    }
   1.352 -
   1.353 -    /**
   1.354 -     * @return Returns the secondList.
   1.355 -     */
   1.356 -    public final NumberList getSecondList() {
   1.357 -        if (secondList == null) {
   1.358 -            secondList = new NumberList(0, 59, false);
   1.359 -        }
   1.360 -        return secondList;
   1.361 -    }
   1.362 -
   1.363 -    /**
   1.364 -     * @return Returns the setPosList.
   1.365 -     */
   1.366 -    public final NumberList getSetPosList() {
   1.367 -        if (setPosList == null) {
   1.368 -            setPosList = new NumberList(1, 366, true);
   1.369 -        }
   1.370 -        return setPosList;
   1.371 -    }
   1.372 -
   1.373 -    /**
   1.374 -     * @return Returns the weekNoList.
   1.375 -     */
   1.376 -    public final NumberList getWeekNoList() {
   1.377 -        if (weekNoList == null) {
   1.378 -            weekNoList = new NumberList(1, 53, true);
   1.379 -        }
   1.380 -        return weekNoList;
   1.381 -    }
   1.382 -
   1.383 -    /**
   1.384 -     * @return Returns the yearDayList.
   1.385 -     */
   1.386 -    public final NumberList getYearDayList() {
   1.387 -        if (yearDayList == null) {
   1.388 -            yearDayList = new NumberList(1, 366, true);
   1.389 -        }
   1.390 -        return yearDayList;
   1.391 -    }
   1.392 -
   1.393 -    /**
   1.394 -     * @return Returns the count or -1 if the rule does not have a count.
   1.395 -     */
   1.396 -    public final int getCount() {
   1.397 -        return count;
   1.398 -    }
   1.399 -
   1.400 -    /**
   1.401 -     * @return Returns the experimentalValues.
   1.402 -     */
   1.403 -    public final Map getExperimentalValues() {
   1.404 -        return experimentalValues;
   1.405 -    }
   1.406 -
   1.407 -    /**
   1.408 -     * @return Returns the frequency.
   1.409 -     */
   1.410 -    public final String getFrequency() {
   1.411 -        return frequency;
   1.412 -    }
   1.413 -
   1.414 -    /**
   1.415 -     * @return Returns the interval or -1 if the rule does not have an interval defined.
   1.416 -     */
   1.417 -    public final int getInterval() {
   1.418 -        return interval;
   1.419 -    }
   1.420 -
   1.421 -    /**
   1.422 -     * @return Returns the until or null if there is none.
   1.423 -     */
   1.424 -    public final Date getUntil() {
   1.425 -        return until;
   1.426 -    }
   1.427 -
   1.428 -    /**
   1.429 -     * @return Returns the weekStartDay or null if there is none.
   1.430 -     */
   1.431 -    public final String getWeekStartDay() {
   1.432 -        return weekStartDay;
   1.433 -    }
   1.434 -
   1.435 -    /**
   1.436 -     * @param weekStartDay The weekStartDay to set.
   1.437 -     */
   1.438 -    public final void setWeekStartDay(final String weekStartDay) {
   1.439 -        this.weekStartDay = weekStartDay;
   1.440 -        if (weekStartDay != null) {
   1.441 -            calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay));
   1.442 -        }
   1.443 -    }
   1.444 -
   1.445 -    /**
   1.446 -     * {@inheritDoc}
   1.447 -     */
   1.448 -    public final String toString() {
   1.449 -        final StringBuffer b = new StringBuffer();
   1.450 -        b.append(FREQ);
   1.451 -        b.append('=');
   1.452 -        b.append(frequency);
   1.453 -        if (weekStartDay != null) {
   1.454 -            b.append(';');
   1.455 -            b.append(WKST);
   1.456 -            b.append('=');
   1.457 -            b.append(weekStartDay);
   1.458 -        }
   1.459 -        if (until != null) {
   1.460 -            b.append(';');
   1.461 -            b.append(UNTIL);
   1.462 -            b.append('=');
   1.463 -            // Note: date-time representations should always be in UTC time.
   1.464 -            b.append(until);
   1.465 -        }
   1.466 -        if (count >= 1) {
   1.467 -            b.append(';');
   1.468 -            b.append(COUNT);
   1.469 -            b.append('=');
   1.470 -            b.append(count);
   1.471 -        }
   1.472 -        if (interval >= 1) {
   1.473 -            b.append(';');
   1.474 -            b.append(INTERVAL);
   1.475 -            b.append('=');
   1.476 -            b.append(interval);
   1.477 -        }
   1.478 -        if (!getMonthList().isEmpty()) {
   1.479 -            b.append(';');
   1.480 -            b.append(BYMONTH);
   1.481 -            b.append('=');
   1.482 -            b.append(monthList);
   1.483 -        }
   1.484 -        if (!getWeekNoList().isEmpty()) {
   1.485 -            b.append(';');
   1.486 -            b.append(BYWEEKNO);
   1.487 -            b.append('=');
   1.488 -            b.append(weekNoList);
   1.489 -        }
   1.490 -        if (!getYearDayList().isEmpty()) {
   1.491 -            b.append(';');
   1.492 -            b.append(BYYEARDAY);
   1.493 -            b.append('=');
   1.494 -            b.append(yearDayList);
   1.495 -        }
   1.496 -        if (!getMonthDayList().isEmpty()) {
   1.497 -            b.append(';');
   1.498 -            b.append(BYMONTHDAY);
   1.499 -            b.append('=');
   1.500 -            b.append(monthDayList);
   1.501 -        }
   1.502 -        if (!getDayList().isEmpty()) {
   1.503 -            b.append(';');
   1.504 -            b.append(BYDAY);
   1.505 -            b.append('=');
   1.506 -            b.append(dayList);
   1.507 -        }
   1.508 -        if (!getHourList().isEmpty()) {
   1.509 -            b.append(';');
   1.510 -            b.append(BYHOUR);
   1.511 -            b.append('=');
   1.512 -            b.append(hourList);
   1.513 -        }
   1.514 -        if (!getMinuteList().isEmpty()) {
   1.515 -            b.append(';');
   1.516 -            b.append(BYMINUTE);
   1.517 -            b.append('=');
   1.518 -            b.append(minuteList);
   1.519 -        }
   1.520 -        if (!getSecondList().isEmpty()) {
   1.521 -            b.append(';');
   1.522 -            b.append(BYSECOND);
   1.523 -            b.append('=');
   1.524 -            b.append(secondList);
   1.525 -        }
   1.526 -        if (!getSetPosList().isEmpty()) {
   1.527 -            b.append(';');
   1.528 -            b.append(BYSETPOS);
   1.529 -            b.append('=');
   1.530 -            b.append(setPosList);
   1.531 -        }
   1.532 -        return b.toString();
   1.533 -    }
   1.534 -
   1.535 -    /**
   1.536 -     * Returns a list of start dates in the specified period represented by this recur. Any date fields not specified by
   1.537 -     * this recur are retained from the period start, and as such you should ensure the period start is initialised
   1.538 -     * correctly.
   1.539 -     * @param periodStart the start of the period
   1.540 -     * @param periodEnd the end of the period
   1.541 -     * @param value the type of dates to generate (i.e. date/date-time)
   1.542 -     * @return a list of dates
   1.543 -     */
   1.544 -    public final DateList getDates(final Date periodStart,
   1.545 -            final Date periodEnd, final Value value) {
   1.546 -        return getDates(periodStart, periodStart, periodEnd, value, -1);
   1.547 -    }
   1.548 -
   1.549 -    /**
   1.550 -     * Convenience method for retrieving recurrences in a specified period.
   1.551 -     * @param seed a seed date for generating recurrence instances
   1.552 -     * @param period the period of returned recurrence dates
   1.553 -     * @param value type of dates to generate
   1.554 -     * @return a list of dates
   1.555 -     */
   1.556 -    public final DateList getDates(final Date seed, final Period period,
   1.557 -            final Value value) {
   1.558 -        return getDates(seed, period.getStart(), period.getEnd(), value, -1);
   1.559 -    }
   1.560 -
   1.561 -    /**
   1.562 -     * Returns a list of start dates in the specified period represented by this recur. This method includes a base date
   1.563 -     * argument, which indicates the start of the fist occurrence of this recurrence. The base date is used to inject
   1.564 -     * default values to return a set of dates in the correct format. For example, if the search start date (start) is
   1.565 -     * Wed, Mar 23, 12:19PM, but the recurrence is Mon - Fri, 9:00AM - 5:00PM, the start dates returned should all be at
   1.566 -     * 9:00AM, and not 12:19PM.
   1.567 -     * @return a list of dates represented by this recur instance
   1.568 -     * @param seed the start date of this Recurrence's first instance
   1.569 -     * @param periodStart the start of the period
   1.570 -     * @param periodEnd the end of the period
   1.571 -     * @param value the type of dates to generate (i.e. date/date-time)
   1.572 -     */
   1.573 -    public final DateList getDates(final Date seed, final Date periodStart,
   1.574 -            final Date periodEnd, final Value value) {
   1.575 -         return getDates(seed, periodStart, periodEnd, value, -1);
   1.576 -    }
   1.577 -
   1.578 -    /**
   1.579 -     * Returns a list of start dates in the specified period represented by this recur. This method includes a base date
   1.580 -     * argument, which indicates the start of the fist occurrence of this recurrence. The base date is used to inject
   1.581 -     * default values to return a set of dates in the correct format. For example, if the search start date (start) is
   1.582 -     * Wed, Mar 23, 12:19PM, but the recurrence is Mon - Fri, 9:00AM - 5:00PM, the start dates returned should all be at
   1.583 -     * 9:00AM, and not 12:19PM.
   1.584 -     * @return a list of dates represented by this recur instance
   1.585 -     * @param seed the start date of this Recurrence's first instance
   1.586 -     * @param periodStart the start of the period
   1.587 -     * @param periodEnd the end of the period
   1.588 -     * @param value the type of dates to generate (i.e. date/date-time)
   1.589 -     * @param maxCount limits the number of instances returned. Up to one years
   1.590 -     *       worth extra may be returned. Less than 0 means no limit
   1.591 -     */
   1.592 -    public final DateList getDates(final Date seed, final Date periodStart,
   1.593 -                                   final Date periodEnd, final Value value,
   1.594 -                                   final int maxCount) {
   1.595 -
   1.596 -        final DateList dates = new DateList(value);
   1.597 -        if (seed instanceof DateTime) {
   1.598 -            if (((DateTime) seed).isUtc()) {
   1.599 -                dates.setUtc(true);
   1.600 -            }
   1.601 -            else {
   1.602 -                dates.setTimeZone(((DateTime) seed).getTimeZone());
   1.603 -            }
   1.604 -        }
   1.605 -        final Calendar cal = getCalendarInstance(seed, true);
   1.606 -
   1.607 -        // optimize the start time for selecting candidates
   1.608 -        // (only applicable where a COUNT is not specified)
   1.609 -        if (getCount() < 1) {
   1.610 -            final Calendar seededCal = (Calendar) cal.clone();
   1.611 -            while (seededCal.getTime().before(periodStart)) {
   1.612 -                cal.setTime(seededCal.getTime());
   1.613 -                increment(seededCal);
   1.614 -            }
   1.615 -        }
   1.616 -
   1.617 -        int invalidCandidateCount = 0;
   1.618 -        int noCandidateIncrementCount = 0;
   1.619 -        Date candidate = null;
   1.620 -        while ((maxCount < 0) || (dates.size() < maxCount)) {
   1.621 -            final Date candidateSeed = Dates.getInstance(cal.getTime(), value);
   1.622 -
   1.623 -            if (getUntil() != null && candidate != null
   1.624 -                    && candidate.after(getUntil())) {
   1.625 -
   1.626 -                break;
   1.627 -            }
   1.628 -            if (periodEnd != null && candidate != null
   1.629 -                    && candidate.after(periodEnd)) {
   1.630 -
   1.631 -                break;
   1.632 -            }
   1.633 -            if (getCount() >= 1
   1.634 -                    && (dates.size() + invalidCandidateCount) >= getCount()) {
   1.635 -
   1.636 -                break;
   1.637 -            }
   1.638 -
   1.639 -//            if (Value.DATE_TIME.equals(value)) {
   1.640 -            if (candidateSeed instanceof DateTime) {
   1.641 -                if (dates.isUtc()) {
   1.642 -                    ((DateTime) candidateSeed).setUtc(true);
   1.643 -                }
   1.644 -                else {
   1.645 -                    ((DateTime) candidateSeed).setTimeZone(dates.getTimeZone());
   1.646 -                }
   1.647 -            }
   1.648 -
   1.649 -            final DateList candidates = getCandidates(candidateSeed, value);
   1.650 -            if (!candidates.isEmpty()) {
   1.651 -                noCandidateIncrementCount = 0;
   1.652 -                // sort candidates for identifying when UNTIL date is exceeded..
   1.653 -                Collections.sort(candidates);
   1.654 -                for (final Iterator i = candidates.iterator(); i.hasNext();) {
   1.655 -                    candidate = (Date) i.next();
   1.656 -                    // don't count candidates that occur before the seed date..
   1.657 -                    if (!candidate.before(seed)) {
   1.658 -                        // candidates exclusive of periodEnd..
   1.659 -                        if (candidate.before(periodStart)
   1.660 -                                || !candidate.before(periodEnd)) {
   1.661 -                            invalidCandidateCount++;
   1.662 -                        } else if (getCount() >= 1
   1.663 -                                && (dates.size() + invalidCandidateCount) >= getCount()) {
   1.664 -                            break;
   1.665 -                        } else if (!(getUntil() != null
   1.666 -                                && candidate.after(getUntil()))) {
   1.667 -                            dates.add(candidate);
   1.668 -                        }
   1.669 -                    }
   1.670 -                }
   1.671 -            } else {
   1.672 -                noCandidateIncrementCount++;
   1.673 -                if ((maxIncrementCount > 0) && (noCandidateIncrementCount > maxIncrementCount)) {
   1.674 -                    break;
   1.675 -                }
   1.676 -            }
   1.677 -            increment(cal);
   1.678 -        }
   1.679 -        // sort final list..
   1.680 -        Collections.sort(dates);
   1.681 -        return dates;
   1.682 -    }
   1.683 -    
   1.684 -    /**
   1.685 -     * Returns the the next date of this recurrence given a seed date
   1.686 -     * and start date.  The seed date indicates the start of the fist 
   1.687 -     * occurrence of this recurrence. The start date is the
   1.688 -     * starting date to search for the next recurrence.  Return null
   1.689 -     * if there is no occurrence date after start date.
   1.690 -     * @return the next date in the recurrence series after startDate
   1.691 -     * @param seed the start date of this Recurrence's first instance
   1.692 -     * @param startDate the date to start the search
   1.693 -     */
   1.694 -    public final Date getNextDate(final Date seed, final Date startDate) {
   1.695 -
   1.696 -        final Calendar cal = getCalendarInstance(seed, true);
   1.697 -
   1.698 -        // optimize the start time for selecting candidates
   1.699 -        // (only applicable where a COUNT is not specified)
   1.700 -        if (getCount() < 1) {
   1.701 -            final Calendar seededCal = (Calendar) cal.clone();
   1.702 -            while (seededCal.getTime().before(startDate)) {
   1.703 -                cal.setTime(seededCal.getTime());
   1.704 -                increment(seededCal);
   1.705 -            }
   1.706 -        }
   1.707 -
   1.708 -        int invalidCandidateCount = 0;
   1.709 -        int noCandidateIncrementCount = 0;
   1.710 -        Date candidate = null;
   1.711 -        final Value value = seed instanceof DateTime ? Value.DATE_TIME : Value.DATE;
   1.712 -        
   1.713 -        while (true) {
   1.714 -            final Date candidateSeed = Dates.getInstance(cal.getTime(), value);
   1.715 -
   1.716 -            if (getUntil() != null && candidate != null && candidate.after(getUntil())) {
   1.717 -                break;
   1.718 -            }
   1.719 -            
   1.720 -            if (getCount() > 0 && invalidCandidateCount >= getCount()) {
   1.721 -                break;
   1.722 -            }
   1.723 -
   1.724 -            if (Value.DATE_TIME.equals(value)) {
   1.725 -                if (((DateTime) seed).isUtc()) {
   1.726 -                    ((DateTime) candidateSeed).setUtc(true);
   1.727 -                }
   1.728 -                else {
   1.729 -                    ((DateTime) candidateSeed).setTimeZone(((DateTime) seed).getTimeZone());
   1.730 -                }
   1.731 -            }
   1.732 -
   1.733 -            final DateList candidates = getCandidates(candidateSeed, value);
   1.734 -            if (!candidates.isEmpty()) {
   1.735 -                noCandidateIncrementCount = 0;
   1.736 -                // sort candidates for identifying when UNTIL date is exceeded..
   1.737 -                Collections.sort(candidates);
   1.738 -
   1.739 -                for (final Iterator i = candidates.iterator(); i.hasNext();) {
   1.740 -                    candidate = (Date) i.next();
   1.741 -                    // don't count candidates that occur before the seed date..
   1.742 -                    if (!candidate.before(seed)) {
   1.743 -                        // Candidate must be after startDate because
   1.744 -                        // we want the NEXT occurrence
   1.745 -                        if (!candidate.after(startDate)) {
   1.746 -                            invalidCandidateCount++;
   1.747 -                        } else if (getCount() > 0
   1.748 -                                && invalidCandidateCount >= getCount()) {
   1.749 -                            break;
   1.750 -                        } else if (!(getUntil() != null
   1.751 -                                && candidate.after(getUntil()))) {
   1.752 -                            return candidate;
   1.753 -                        }
   1.754 -                    }
   1.755 -                }
   1.756 -            } else {
   1.757 -                noCandidateIncrementCount++;
   1.758 -                if ((maxIncrementCount > 0) && (noCandidateIncrementCount > maxIncrementCount)) {
   1.759 -                    break;
   1.760 -                }
   1.761 -            }
   1.762 -            increment(cal);
   1.763 -        }
   1.764 -        return null;
   1.765 -    }
   1.766 -
   1.767 -    /**
   1.768 -     * Increments the specified calendar according to the frequency and interval specified in this recurrence rule.
   1.769 -     * @param cal a java.util.Calendar to increment
   1.770 -     */
   1.771 -    private void increment(final Calendar cal) {
   1.772 -        // initialise interval..
   1.773 -        final int calInterval = (getInterval() >= 1) ? getInterval() : 1;
   1.774 -        cal.add(calIncField, calInterval);
   1.775 -    }
   1.776 -
   1.777 -    /**
   1.778 -     * Returns a list of possible dates generated from the applicable BY* rules, using the specified date as a seed.
   1.779 -     * @param date the seed date
   1.780 -     * @param value the type of date list to return
   1.781 -     * @return a DateList
   1.782 -     */
   1.783 -    private DateList getCandidates(final Date date, final Value value) {
   1.784 -        DateList dates = new DateList(value);
   1.785 -        if (date instanceof DateTime) {
   1.786 -            if (((DateTime) date).isUtc()) {
   1.787 -                dates.setUtc(true);
   1.788 -            }
   1.789 -            else {
   1.790 -                dates.setTimeZone(((DateTime) date).getTimeZone());
   1.791 -            }
   1.792 -        }
   1.793 -        dates.add(date);
   1.794 -        dates = getMonthVariants(dates);
   1.795 -        // debugging..
   1.796 -        if (log.isDebugEnabled()) {
   1.797 -            log.debug("Dates after BYMONTH processing: " + dates);
   1.798 -        }
   1.799 -        dates = getWeekNoVariants(dates);
   1.800 -        // debugging..
   1.801 -        if (log.isDebugEnabled()) {
   1.802 -            log.debug("Dates after BYWEEKNO processing: " + dates);
   1.803 -        }
   1.804 -        dates = getYearDayVariants(dates);
   1.805 -        // debugging..
   1.806 -        if (log.isDebugEnabled()) {
   1.807 -            log.debug("Dates after BYYEARDAY processing: " + dates);
   1.808 -        }
   1.809 -        dates = getMonthDayVariants(dates);
   1.810 -        // debugging..
   1.811 -        if (log.isDebugEnabled()) {
   1.812 -            log.debug("Dates after BYMONTHDAY processing: " + dates);
   1.813 -        }
   1.814 -        dates = getDayVariants(dates);
   1.815 -        // debugging..
   1.816 -        if (log.isDebugEnabled()) {
   1.817 -            log.debug("Dates after BYDAY processing: " + dates);
   1.818 -        }
   1.819 -        dates = getHourVariants(dates);
   1.820 -        // debugging..
   1.821 -        if (log.isDebugEnabled()) {
   1.822 -            log.debug("Dates after BYHOUR processing: " + dates);
   1.823 -        }
   1.824 -        dates = getMinuteVariants(dates);
   1.825 -        // debugging..
   1.826 -        if (log.isDebugEnabled()) {
   1.827 -            log.debug("Dates after BYMINUTE processing: " + dates);
   1.828 -        }
   1.829 -        dates = getSecondVariants(dates);
   1.830 -        // debugging..
   1.831 -        if (log.isDebugEnabled()) {
   1.832 -            log.debug("Dates after BYSECOND processing: " + dates);
   1.833 -        }
   1.834 -        dates = applySetPosRules(dates);
   1.835 -        // debugging..
   1.836 -        if (log.isDebugEnabled()) {
   1.837 -            log.debug("Dates after SETPOS processing: " + dates);
   1.838 -        }
   1.839 -        return dates;
   1.840 -    }
   1.841 -
   1.842 -    /**
   1.843 -     * Applies BYSETPOS rules to <code>dates</code>. Valid positions are from 1 to the size of the date list. Invalid
   1.844 -     * positions are ignored.
   1.845 -     * @param dates
   1.846 -     */
   1.847 -    private DateList applySetPosRules(final DateList dates) {
   1.848 -        // return if no SETPOS rules specified..
   1.849 -        if (getSetPosList().isEmpty()) {
   1.850 -            return dates;
   1.851 -        }
   1.852 -        // sort the list before processing..
   1.853 -        Collections.sort(dates);
   1.854 -        final DateList setPosDates = getDateListInstance(dates);
   1.855 -        final int size = dates.size();
   1.856 -        for (final Iterator i = getSetPosList().iterator(); i.hasNext();) {
   1.857 -            final Integer setPos = (Integer) i.next();
   1.858 -            final int pos = setPos.intValue();
   1.859 -            if (pos > 0 && pos <= size) {
   1.860 -                setPosDates.add(dates.get(pos - 1));
   1.861 -            }
   1.862 -            else if (pos < 0 && pos >= -size) {
   1.863 -                setPosDates.add(dates.get(size + pos));
   1.864 -            }
   1.865 -        }
   1.866 -        return setPosDates;
   1.867 -    }
   1.868 -
   1.869 -    /**
   1.870 -     * Applies BYMONTH rules specified in this Recur instance to the specified date list. If no BYMONTH rules are
   1.871 -     * specified the date list is returned unmodified.
   1.872 -     * @param dates
   1.873 -     * @return
   1.874 -     */
   1.875 -    private DateList getMonthVariants(final DateList dates) {
   1.876 -        if (getMonthList().isEmpty()) {
   1.877 -            return dates;
   1.878 -        }
   1.879 -        final DateList monthlyDates = getDateListInstance(dates);
   1.880 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
   1.881 -            final Date date = (Date) i.next();
   1.882 -            final Calendar cal = getCalendarInstance(date, true);
   1.883 -
   1.884 -            for (final Iterator j = getMonthList().iterator(); j.hasNext();) {
   1.885 -                final Integer month = (Integer) j.next();
   1.886 -                // Java months are zero-based..
   1.887 -//                cal.set(Calendar.MONTH, month.intValue() - 1);
   1.888 -                cal.roll(Calendar.MONTH, (month.intValue() - 1) - cal.get(Calendar.MONTH));
   1.889 -                monthlyDates.add(Dates.getInstance(cal.getTime(), monthlyDates.getType()));
   1.890 -            }
   1.891 -        }
   1.892 -        return monthlyDates;
   1.893 -    }
   1.894 -
   1.895 -    /**
   1.896 -     * Applies BYWEEKNO rules specified in this Recur instance to the specified date list. If no BYWEEKNO rules are
   1.897 -     * specified the date list is returned unmodified.
   1.898 -     * @param dates
   1.899 -     * @return
   1.900 -     */
   1.901 -    private DateList getWeekNoVariants(final DateList dates) {
   1.902 -        if (getWeekNoList().isEmpty()) {
   1.903 -            return dates;
   1.904 -        }
   1.905 -        final DateList weekNoDates = getDateListInstance(dates);
   1.906 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
   1.907 -            final Date date = (Date) i.next();
   1.908 -            final Calendar cal = getCalendarInstance(date, true);
   1.909 -            for (final Iterator j = getWeekNoList().iterator(); j.hasNext();) {
   1.910 -                final Integer weekNo = (Integer) j.next();
   1.911 -                cal.set(Calendar.WEEK_OF_YEAR, Dates.getAbsWeekNo(cal.getTime(), weekNo.intValue()));
   1.912 -                weekNoDates.add(Dates.getInstance(cal.getTime(), weekNoDates.getType()));
   1.913 -            }
   1.914 -        }
   1.915 -        return weekNoDates;
   1.916 -    }
   1.917 -
   1.918 -    /**
   1.919 -     * Applies BYYEARDAY rules specified in this Recur instance to the specified date list. If no BYYEARDAY rules are
   1.920 -     * specified the date list is returned unmodified.
   1.921 -     * @param dates
   1.922 -     * @return
   1.923 -     */
   1.924 -    private DateList getYearDayVariants(final DateList dates) {
   1.925 -        if (getYearDayList().isEmpty()) {
   1.926 -            return dates;
   1.927 -        }
   1.928 -        final DateList yearDayDates = getDateListInstance(dates);
   1.929 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
   1.930 -            final Date date = (Date) i.next();
   1.931 -            final Calendar cal = getCalendarInstance(date, true);
   1.932 -            for (final Iterator j = getYearDayList().iterator(); j.hasNext();) {
   1.933 -                final Integer yearDay = (Integer) j.next();
   1.934 -                cal.set(Calendar.DAY_OF_YEAR, Dates.getAbsYearDay(cal.getTime(), yearDay.intValue()));
   1.935 -                yearDayDates.add(Dates.getInstance(cal.getTime(), yearDayDates.getType()));
   1.936 -            }
   1.937 -        }
   1.938 -        return yearDayDates;
   1.939 -    }
   1.940 -
   1.941 -    /**
   1.942 -     * Applies BYMONTHDAY rules specified in this Recur instance to the specified date list. If no BYMONTHDAY rules are
   1.943 -     * specified the date list is returned unmodified.
   1.944 -     * @param dates
   1.945 -     * @return
   1.946 -     */
   1.947 -    private DateList getMonthDayVariants(final DateList dates) {
   1.948 -        if (getMonthDayList().isEmpty()) {
   1.949 -            return dates;
   1.950 -        }
   1.951 -        final DateList monthDayDates = getDateListInstance(dates);
   1.952 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
   1.953 -            final Date date = (Date) i.next();
   1.954 -            final Calendar cal = getCalendarInstance(date, false);
   1.955 -            for (final Iterator j = getMonthDayList().iterator(); j.hasNext();) {
   1.956 -                final Integer monthDay = (Integer) j.next();
   1.957 -                try {
   1.958 -                    cal.set(Calendar.DAY_OF_MONTH, Dates.getAbsMonthDay(cal.getTime(), monthDay.intValue()));
   1.959 -                    monthDayDates.add(Dates.getInstance(cal.getTime(), monthDayDates.getType()));
   1.960 -                }
   1.961 -                catch (IllegalArgumentException iae) {
   1.962 -                    if (log.isTraceEnabled()) {
   1.963 -                        log.trace("Invalid day of month: " + Dates.getAbsMonthDay(cal
   1.964 -                                .getTime(), monthDay.intValue()));
   1.965 -                    }
   1.966 -                }
   1.967 -            }
   1.968 -        }
   1.969 -        return monthDayDates;
   1.970 -    }
   1.971 -
   1.972 -    /**
   1.973 -     * Applies BYDAY rules specified in this Recur instance to the specified date list. If no BYDAY rules are specified
   1.974 -     * the date list is returned unmodified.
   1.975 -     * @param dates
   1.976 -     * @return
   1.977 -     */
   1.978 -    private DateList getDayVariants(final DateList dates) {
   1.979 -        if (getDayList().isEmpty()) {
   1.980 -            return dates;
   1.981 -        }
   1.982 -        final DateList weekDayDates = getDateListInstance(dates);
   1.983 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
   1.984 -            final Date date = (Date) i.next();
   1.985 -            for (final Iterator j = getDayList().iterator(); j.hasNext();) {
   1.986 -                final WeekDay weekDay = (WeekDay) j.next();
   1.987 -                // if BYYEARDAY or BYMONTHDAY is specified filter existing
   1.988 -                // list..
   1.989 -                if (!getYearDayList().isEmpty() || !getMonthDayList().isEmpty()) {
   1.990 -                    final Calendar cal = getCalendarInstance(date, true);
   1.991 -                    if (weekDay.equals(WeekDay.getWeekDay(cal))) {
   1.992 -                        weekDayDates.add(date);
   1.993 -                    }
   1.994 -                }
   1.995 -                else {
   1.996 -                    weekDayDates.addAll(getAbsWeekDays(date, dates.getType(), weekDay));
   1.997 -                }
   1.998 -            }
   1.999 -        }
  1.1000 -        return weekDayDates;
  1.1001 -    }
  1.1002 -
  1.1003 -    /**
  1.1004 -     * Returns a list of applicable dates corresponding to the specified week day in accordance with the frequency
  1.1005 -     * specified by this recurrence rule.
  1.1006 -     * @param date
  1.1007 -     * @param weekDay
  1.1008 -     * @return
  1.1009 -     */
  1.1010 -    private List getAbsWeekDays(final Date date, final Value type, final WeekDay weekDay) {
  1.1011 -        final Calendar cal = getCalendarInstance(date, true);
  1.1012 -        final DateList days = new DateList(type);
  1.1013 -        if (date instanceof DateTime) {
  1.1014 -            if (((DateTime) date).isUtc()) {
  1.1015 -                days.setUtc(true);
  1.1016 -            }
  1.1017 -            else {
  1.1018 -                days.setTimeZone(((DateTime) date).getTimeZone());
  1.1019 -            }
  1.1020 -        }
  1.1021 -        final int calDay = WeekDay.getCalendarDay(weekDay);
  1.1022 -        if (calDay == -1) {
  1.1023 -            // a matching weekday cannot be identified..
  1.1024 -            return days;
  1.1025 -        }
  1.1026 -        if (DAILY.equals(getFrequency())) {
  1.1027 -            if (cal.get(Calendar.DAY_OF_WEEK) == calDay) {
  1.1028 -                days.add(Dates.getInstance(cal.getTime(), type));
  1.1029 -            }
  1.1030 -        }
  1.1031 -        else if (WEEKLY.equals(getFrequency()) || !getWeekNoList().isEmpty()) {
  1.1032 -            final int weekNo = cal.get(Calendar.WEEK_OF_YEAR);
  1.1033 -            // construct a list of possible week days..
  1.1034 -            cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
  1.1035 -            while (cal.get(Calendar.DAY_OF_WEEK) != calDay) {
  1.1036 -                cal.add(Calendar.DAY_OF_WEEK, 1);
  1.1037 -            }
  1.1038 -//            final int weekNo = cal.get(Calendar.WEEK_OF_YEAR);
  1.1039 -            if (cal.get(Calendar.WEEK_OF_YEAR) == weekNo) {
  1.1040 -                days.add(Dates.getInstance(cal.getTime(), type));
  1.1041 -//                cal.add(Calendar.DAY_OF_WEEK, Dates.DAYS_PER_WEEK);
  1.1042 -            }
  1.1043 -        }
  1.1044 -        else if (MONTHLY.equals(getFrequency()) || !getMonthList().isEmpty()) {
  1.1045 -            final int month = cal.get(Calendar.MONTH);
  1.1046 -            // construct a list of possible month days..
  1.1047 -            cal.set(Calendar.DAY_OF_MONTH, 1);
  1.1048 -            while (cal.get(Calendar.DAY_OF_WEEK) != calDay) {
  1.1049 -                cal.add(Calendar.DAY_OF_MONTH, 1);
  1.1050 -            }
  1.1051 -            while (cal.get(Calendar.MONTH) == month) {
  1.1052 -                days.add(Dates.getInstance(cal.getTime(), type));
  1.1053 -                cal.add(Calendar.DAY_OF_MONTH, Dates.DAYS_PER_WEEK);
  1.1054 -            }
  1.1055 -        }
  1.1056 -        else if (YEARLY.equals(getFrequency())) {
  1.1057 -            final int year = cal.get(Calendar.YEAR);
  1.1058 -            // construct a list of possible year days..
  1.1059 -            cal.set(Calendar.DAY_OF_YEAR, 1);
  1.1060 -            while (cal.get(Calendar.DAY_OF_WEEK) != calDay) {
  1.1061 -                cal.add(Calendar.DAY_OF_YEAR, 1);
  1.1062 -            }
  1.1063 -            while (cal.get(Calendar.YEAR) == year) {
  1.1064 -                days.add(Dates.getInstance(cal.getTime(), type));
  1.1065 -                cal.add(Calendar.DAY_OF_YEAR, Dates.DAYS_PER_WEEK);
  1.1066 -            }
  1.1067 -        }
  1.1068 -        return getOffsetDates(days, weekDay.getOffset());
  1.1069 -    }
  1.1070 -
  1.1071 -    /**
  1.1072 -     * Returns a single-element sublist containing the element of <code>list</code> at <code>offset</code>. Valid
  1.1073 -     * offsets are from 1 to the size of the list. If an invalid offset is supplied, all elements from <code>list</code>
  1.1074 -     * are added to <code>sublist</code>.
  1.1075 -     * @param list
  1.1076 -     * @param offset
  1.1077 -     * @param sublist
  1.1078 -     */
  1.1079 -    private List getOffsetDates(final DateList dates, final int offset) {
  1.1080 -        if (offset == 0) {
  1.1081 -            return dates;
  1.1082 -        }
  1.1083 -        final List offsetDates = getDateListInstance(dates);
  1.1084 -        final int size = dates.size();
  1.1085 -        if (offset < 0 && offset >= -size) {
  1.1086 -            offsetDates.add(dates.get(size + offset));
  1.1087 -        }
  1.1088 -        else if (offset > 0 && offset <= size) {
  1.1089 -            offsetDates.add(dates.get(offset - 1));
  1.1090 -        }
  1.1091 -        return offsetDates;
  1.1092 -    }
  1.1093 -
  1.1094 -    /**
  1.1095 -     * Applies BYHOUR rules specified in this Recur instance to the specified date list. If no BYHOUR rules are
  1.1096 -     * specified the date list is returned unmodified.
  1.1097 -     * @param dates
  1.1098 -     * @return
  1.1099 -     */
  1.1100 -    private DateList getHourVariants(final DateList dates) {
  1.1101 -        if (getHourList().isEmpty()) {
  1.1102 -            return dates;
  1.1103 -        }
  1.1104 -        final DateList hourlyDates = getDateListInstance(dates);
  1.1105 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
  1.1106 -            final Date date = (Date) i.next();
  1.1107 -            final Calendar cal = getCalendarInstance(date, true);
  1.1108 -            for (final Iterator j = getHourList().iterator(); j.hasNext();) {
  1.1109 -                final Integer hour = (Integer) j.next();
  1.1110 -                cal.set(Calendar.HOUR_OF_DAY, hour.intValue());
  1.1111 -                hourlyDates.add(Dates.getInstance(cal.getTime(), hourlyDates.getType()));
  1.1112 -            }
  1.1113 -        }
  1.1114 -        return hourlyDates;
  1.1115 -    }
  1.1116 -
  1.1117 -    /**
  1.1118 -     * Applies BYMINUTE rules specified in this Recur instance to the specified date list. If no BYMINUTE rules are
  1.1119 -     * specified the date list is returned unmodified.
  1.1120 -     * @param dates
  1.1121 -     * @return
  1.1122 -     */
  1.1123 -    private DateList getMinuteVariants(final DateList dates) {
  1.1124 -        if (getMinuteList().isEmpty()) {
  1.1125 -            return dates;
  1.1126 -        }
  1.1127 -        final DateList minutelyDates = getDateListInstance(dates);
  1.1128 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
  1.1129 -            final Date date = (Date) i.next();
  1.1130 -            final Calendar cal = getCalendarInstance(date, true);
  1.1131 -            for (final Iterator j = getMinuteList().iterator(); j.hasNext();) {
  1.1132 -                final Integer minute = (Integer) j.next();
  1.1133 -                cal.set(Calendar.MINUTE, minute.intValue());
  1.1134 -                minutelyDates.add(Dates.getInstance(cal.getTime(), minutelyDates.getType()));
  1.1135 -            }
  1.1136 -        }
  1.1137 -        return minutelyDates;
  1.1138 -    }
  1.1139 -
  1.1140 -    /**
  1.1141 -     * Applies BYSECOND rules specified in this Recur instance to the specified date list. If no BYSECOND rules are
  1.1142 -     * specified the date list is returned unmodified.
  1.1143 -     * @param dates
  1.1144 -     * @return
  1.1145 -     */
  1.1146 -    private DateList getSecondVariants(final DateList dates) {
  1.1147 -        if (getSecondList().isEmpty()) {
  1.1148 -            return dates;
  1.1149 -        }
  1.1150 -        final DateList secondlyDates = getDateListInstance(dates);
  1.1151 -        for (final Iterator i = dates.iterator(); i.hasNext();) {
  1.1152 -            final Date date = (Date) i.next();
  1.1153 -            final Calendar cal = getCalendarInstance(date, true);
  1.1154 -            for (final Iterator j = getSecondList().iterator(); j.hasNext();) {
  1.1155 -                final Integer second = (Integer) j.next();
  1.1156 -                cal.set(Calendar.SECOND, second.intValue());
  1.1157 -                secondlyDates.add(Dates.getInstance(cal.getTime(), secondlyDates.getType()));
  1.1158 -            }
  1.1159 -        }
  1.1160 -        return secondlyDates;
  1.1161 -    }
  1.1162 -
  1.1163 -    private void validateFrequency() {
  1.1164 -        if (frequency == null) {
  1.1165 -            throw new IllegalArgumentException(
  1.1166 -                    "A recurrence rule MUST contain a FREQ rule part.");
  1.1167 -        }
  1.1168 -        if (SECONDLY.equals(getFrequency())) {
  1.1169 -            calIncField = Calendar.SECOND;
  1.1170 -        }
  1.1171 -        else if (MINUTELY.equals(getFrequency())) {
  1.1172 -            calIncField = Calendar.MINUTE;
  1.1173 -        }
  1.1174 -        else if (HOURLY.equals(getFrequency())) {
  1.1175 -            calIncField = Calendar.HOUR_OF_DAY;
  1.1176 -        }
  1.1177 -        else if (DAILY.equals(getFrequency())) {
  1.1178 -            calIncField = Calendar.DAY_OF_YEAR;
  1.1179 -        }
  1.1180 -        else if (WEEKLY.equals(getFrequency())) {
  1.1181 -            calIncField = Calendar.WEEK_OF_YEAR;
  1.1182 -        }
  1.1183 -        else if (MONTHLY.equals(getFrequency())) {
  1.1184 -            calIncField = Calendar.MONTH;
  1.1185 -        }
  1.1186 -        else if (YEARLY.equals(getFrequency())) {
  1.1187 -            calIncField = Calendar.YEAR;
  1.1188 -        }
  1.1189 -        else {
  1.1190 -            throw new IllegalArgumentException("Invalid FREQ rule part '"
  1.1191 -                    + frequency + "' in recurrence rule");
  1.1192 -        }
  1.1193 -    }
  1.1194 -
  1.1195 -    /**
  1.1196 -     * @param count The count to set.
  1.1197 -     */
  1.1198 -    public final void setCount(final int count) {
  1.1199 -        this.count = count;
  1.1200 -        this.until = null;
  1.1201 -    }
  1.1202 -
  1.1203 -    /**
  1.1204 -     * @param frequency The frequency to set.
  1.1205 -     */
  1.1206 -    public final void setFrequency(final String frequency) {
  1.1207 -        this.frequency = frequency;
  1.1208 -        validateFrequency();
  1.1209 -    }
  1.1210 -
  1.1211 -    /**
  1.1212 -     * @param interval The interval to set.
  1.1213 -     */
  1.1214 -    public final void setInterval(final int interval) {
  1.1215 -        this.interval = interval;
  1.1216 -    }
  1.1217 -
  1.1218 -    /**
  1.1219 -     * @param until The until to set.
  1.1220 -     */
  1.1221 -    public final void setUntil(final Date until) {
  1.1222 -        this.until = until;
  1.1223 -        this.count = -1;
  1.1224 -    }
  1.1225 -    
  1.1226 -    /**
  1.1227 -     * Construct a Calendar object and sets the time.
  1.1228 -     * @param date
  1.1229 -     * @param lenient 
  1.1230 -     * @return 
  1.1231 -     */
  1.1232 -    private Calendar getCalendarInstance(final Date date, final boolean lenient) {
  1.1233 -        Calendar cal = Dates.getCalendarInstance(date);
  1.1234 -        // A week should have at least 4 days to be considered as such per RFC5545
  1.1235 -        cal.setMinimalDaysInFirstWeek(4);
  1.1236 -        cal.setFirstDayOfWeek(calendarWeekStartDay);
  1.1237 -        cal.setLenient(lenient);     
  1.1238 -        cal.setTime(date);
  1.1239 -        
  1.1240 -        return cal;
  1.1241 -    }
  1.1242 -    
  1.1243 -    /**
  1.1244 -     * @param stream
  1.1245 -     * @throws IOException
  1.1246 -     * @throws ClassNotFoundException
  1.1247 -     */
  1.1248 -    private void readObject(final java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
  1.1249 -        stream.defaultReadObject();
  1.1250 -        log = LogFactory.getLog(Recur.class);
  1.1251 -    }
  1.1252 -    
  1.1253 -    /**
  1.1254 -     * Instantiate a new datelist with the same type, timezone and utc settings
  1.1255 -     *  as the origList.
  1.1256 -     * @param origList
  1.1257 -     * @return a new empty list.
  1.1258 -     */
  1.1259 -    private static DateList getDateListInstance(final DateList origList) {
  1.1260 -        final DateList list = new DateList(origList.getType());
  1.1261 -        if (origList.isUtc()) {
  1.1262 -            list.setUtc(true);
  1.1263 -        } else {
  1.1264 -            list.setTimeZone(origList.getTimeZone());
  1.1265 -        }
  1.1266 -        return list;
  1.1267 -    }
  1.1268 -
  1.1269 -}

mercurial