// Path: src/main/java/org/dmfs/rfc5545/recurrenceset/AbstractRecurrenceAdapter.java
// interface InstanceIterator
// {
// 
//     /**
//      * Check if there is at least one more instance to iterate.
//      *
//      * @return <code>true</code> if the next call to {@link #next()} will return another instance, <code>false</code> otherwise.
//      */
//     abstract boolean hasNext();
// 
//     /**
//      * Get the next instance of this set. Do not call this if {@link #hasNext()} returns <code>false</code>.
//      *
//      * @return The time in milliseconds since the epoch of the next instance.
//      *
//      * @throws ArrayIndexOutOfBoundsException
//      *         if there are no more instances.
//      */
//     abstract long next();
// 
//     /**
//      * Skip all instances till <code>until</code>. If <code>until</code> is an instance itself it will be the next iterated instance. If the rule doesn't
//      * recur till that date the next call to {@link #hasNext()} will return <code>false</code>.
//      *
//      * @param until
//      *         A time stamp of the date to fast forward to.
//      */
//     abstract void fastForward(long until);
// 
// }


import org.dmfs.rfc5545.recurrenceset.AbstractRecurrenceAdapter.InstanceIterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
/*
 * Copyright (C) 2013 Marten Gajda <marten@dmfs.org>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package org.dmfs.rfc5545.recurrenceset;




/**
 * A recurrence set. A recurrence set consists of all instances defined by a recurrence rule or a list if instances except for exception instances. Exception
 * instances are defined by exceptions rules or lists of exception instances. <p> This class allows you to add any number of recurrence rules, recurrence
 * instances, exception rules and exception instance. It returns an {@link Iterator} that iterates all resulting instances. </p>
 *
 * @author Marten Gajda
 */
public class RecurrenceSet
{

    /**
     * All the instances in the set. Not all of them may be iterated, since instances that are exceptions will be skipped.
     */
    private final List<AbstractRecurrenceAdapter> mInstances = new ArrayList<AbstractRecurrenceAdapter>();

    /**
     * All exceptions in the set.
     */
    private List<AbstractRecurrenceAdapter> mExceptions = null;

    /**
     * Indicates if the recurrence set is infinite.
     */
    private boolean mIsInfinite = false;


    /**
     * Add instances to the set of instances.
     *
     * @param adapter
     *         An {@link AbstractRecurrenceAdapter} that defines instances.
     */
    public void addInstances(AbstractRecurrenceAdapter adapter)
    {
        mInstances.add(adapter);

        // the entire set is infinite if there is at least one infinite instance set
        mIsInfinite |= adapter.isInfinite();
    }


    /**
     * Add exceptions to the set of instances (i.e. effectively remove instances from the instance set).
     *
     * @param adapter
     *         An {@link AbstractRecurrenceAdapter} that defines instances.
     */
    public void addExceptions(AbstractRecurrenceAdapter adapter)
    {
        if (mExceptions == null)
        {
            mExceptions = new ArrayList<AbstractRecurrenceAdapter>();
        }
        mExceptions.add(adapter);
    }


    /**
     * Get an iterator for the specified start time.
     *
     * @param timezone
     *         The {@link TimeZone} of the first instance.
     * @param start
     *         The start time in milliseconds since the epoch.
     *
     * @return A {@link RecurrenceSetIterator} that iterates all instances.
     */
    public RecurrenceSetIterator iterator(TimeZone timezone, long start)
    {
        return iterator(timezone, start, Long.MAX_VALUE);
    }


    /**
     * Return a new {@link RecurrenceSetIterator} for this recurrence set.
     *
     * @param timezone
     *         The {@link TimeZone} of the first instance.
     * @param start
     *         The start time in milliseconds since the epoch.
     * @param end
     *         The end of the time range to iterate in milliseconds since the epoch.
     *
     * @return A {@link RecurrenceSetIterator} that iterates all instances.
     */
    public RecurrenceSetIterator iterator(TimeZone timezone, long start, long end)
    {
// Path: src/main/java/org/dmfs/rfc5545/recurrenceset/AbstractRecurrenceAdapter.java
// interface InstanceIterator
// {
// 
//     /**
//      * Check if there is at least one more instance to iterate.
//      *
//      * @return <code>true</code> if the next call to {@link #next()} will return another instance, <code>false</code> otherwise.
//      */
//     abstract boolean hasNext();
// 
//     /**
//      * Get the next instance of this set. Do not call this if {@link #hasNext()} returns <code>false</code>.
//      *
//      * @return The time in milliseconds since the epoch of the next instance.
//      *
//      * @throws ArrayIndexOutOfBoundsException
//      *         if there are no more instances.
//      */
//     abstract long next();
// 
//     /**
//      * Skip all instances till <code>until</code>. If <code>until</code> is an instance itself it will be the next iterated instance. If the rule doesn't
//      * recur till that date the next call to {@link #hasNext()} will return <code>false</code>.
//      *
//      * @param until
//      *         A time stamp of the date to fast forward to.
//      */
//     abstract void fastForward(long until);
// 
// }


// Path: src/main/java/org/dmfs/rfc5545/recurrenceset/RecurrenceSet.java
import org.dmfs.rfc5545.recurrenceset.AbstractRecurrenceAdapter.InstanceIterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

/*
 * Copyright (C) 2013 Marten Gajda <marten@dmfs.org>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package org.dmfs.rfc5545.recurrenceset;




/**
 * A recurrence set. A recurrence set consists of all instances defined by a recurrence rule or a list if instances except for exception instances. Exception
 * instances are defined by exceptions rules or lists of exception instances. <p> This class allows you to add any number of recurrence rules, recurrence
 * instances, exception rules and exception instance. It returns an {@link Iterator} that iterates all resulting instances. </p>
 *
 * @author Marten Gajda
 */
public class RecurrenceSet
{

    /**
     * All the instances in the set. Not all of them may be iterated, since instances that are exceptions will be skipped.
     */
    private final List<AbstractRecurrenceAdapter> mInstances = new ArrayList<AbstractRecurrenceAdapter>();

    /**
     * All exceptions in the set.
     */
    private List<AbstractRecurrenceAdapter> mExceptions = null;

    /**
     * Indicates if the recurrence set is infinite.
     */
    private boolean mIsInfinite = false;


    /**
     * Add instances to the set of instances.
     *
     * @param adapter
     *         An {@link AbstractRecurrenceAdapter} that defines instances.
     */
    public void addInstances(AbstractRecurrenceAdapter adapter)
    {
        mInstances.add(adapter);

        // the entire set is infinite if there is at least one infinite instance set
        mIsInfinite |= adapter.isInfinite();
    }


    /**
     * Add exceptions to the set of instances (i.e. effectively remove instances from the instance set).
     *
     * @param adapter
     *         An {@link AbstractRecurrenceAdapter} that defines instances.
     */
    public void addExceptions(AbstractRecurrenceAdapter adapter)
    {
        if (mExceptions == null)
        {
            mExceptions = new ArrayList<AbstractRecurrenceAdapter>();
        }
        mExceptions.add(adapter);
    }


    /**
     * Get an iterator for the specified start time.
     *
     * @param timezone
     *         The {@link TimeZone} of the first instance.
     * @param start
     *         The start time in milliseconds since the epoch.
     *
     * @return A {@link RecurrenceSetIterator} that iterates all instances.
     */
    public RecurrenceSetIterator iterator(TimeZone timezone, long start)
    {
        return iterator(timezone, start, Long.MAX_VALUE);
    }


    /**
     * Return a new {@link RecurrenceSetIterator} for this recurrence set.
     *
     * @param timezone
     *         The {@link TimeZone} of the first instance.
     * @param start
     *         The start time in milliseconds since the epoch.
     * @param end
     *         The end of the time range to iterate in milliseconds since the epoch.
     *
     * @return A {@link RecurrenceSetIterator} that iterates all instances.
     */
    public RecurrenceSetIterator iterator(TimeZone timezone, long start, long end)
    {
        List<InstanceIterator> instances = new ArrayList<InstanceIterator>(mInstances.size());