/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        re


public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();

            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);

            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);

            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


@Override
    public boolean useItem(Playable playable, Item item, boolean forceUse)

/*
 *
 * This file is part of the L2JOrg project.
 *
 */
package handlers.itemhandlers;

import org.l2j.gameserver.enums.ItemSkillType;
import org.l2j.gameserver.enums.ShotType;
import org.l2j.gameserver.handler.IItemHandler;
import org.l2j.gameserver.model.actor.Playable;
import org.l2j.gameserver.model.actor.instance.Player;
import org.l2j.gameserver.model.events.EventDispatcher;
import org.l2j.gameserver.model.events.impl.character.player.OnPlayeableChargeShots;
import org.l2j.gameserver.model.item.instance.Item;
import org.l2j.gameserver.network.SystemMessageId;
import org.l2j.gameserver.network.serverpackets.MagicSkillUse;
import org.l2j.gameserver.util.Broadcast;
import static org.l2j.commons.util.Util.isNullOrEmpty;

/**
 * @author JoeAlisson
 */
public abstract class AbstractShot implements IItemHandler {

{
        var player = playable.getActingPlayer();
        var skills = item.getSkills(ItemSkillType.NORMAL);
        if (isNullOrEmpty(skills)) {
            LOGGER.warn("item {} is missing skills!", item);
            return false;
        }
        // Check if Soul shot can be used
        if (!canUse(player)) {
            return false;
        }
        player.chargeShot(getShotType(), getBonus(player));
        player.sendPacket(getEnabledShotsMessage());
        EventDispatcher.getInstance().notifyEventAsync(new OnPlayeableChargeShots(player, getShotType(), isBlessed()), player);
        skills.forEach(holder -> Broadcast.toSelfAndKnownPlayersInRadius(player, new MagicSkillUse(player, player, holder.getSkillId(), holder.getLevel(), 0, 0), 600));
        return true;
    }

    protected abstract boolean isBlessed();

    protected abstract double getBonus(Player player);

    protected abstract boolean canUse(Player player);

    protected abstract ShotType getShotType();

    protected abstract SystemMessageId getEnabledShotsMessage();
}


        var player = playable.getActingPlayer();
        var skills = item.getSkills(ItemSkillType.NORMAL);
        if (isNullOrEmpty(skills)) {
            LOGGER.warn("item {} is missing skills!", item);
            return false;
        }

/*
 *
 * This file is part of the L2JOrg project.
 *
 */
package handlers.itemhandlers;

import org.l2j.gameserver.enums.ItemSkillType;
import org.l2j.gameserver.enums.ShotType;
import org.l2j.gameserver.handler.IItemHandler;
import org.l2j.gameserver.model.actor.Playable;
import org.l2j.gameserver.model.actor.instance.Player;
import org.l2j.gameserver.model.events.EventDispatcher;
import org.l2j.gameserver.model.events.impl.character.player.OnPlayeableChargeShots;
import org.l2j.gameserver.model.item.instance.Item;
import org.l2j.gameserver.network.SystemMessageId;
import org.l2j.gameserver.network.serverpackets.MagicSkillUse;
import org.l2j.gameserver.util.Broadcast;
import static org.l2j.commons.util.Util.isNullOrEmpty;

/**
 * @author JoeAlisson
 */
public abstract class AbstractShot implements IItemHandler {

    @Override
    public boolean useItem(Playable playable, Item item, boolean forceUse) {

        // Check if Soul shot can be used
        if (!canUse(player)) {
            return false;
        }
        player.chargeShot(getShotType(), getBonus(player));
        player.sendPacket(getEnabledShotsMessage());
        EventDispatcher.getInstance().notifyEventAsync(new OnPlayeableChargeShots(player, getShotType(), isBlessed()), player);
        skills.forEach(holder -> Broadcast.toSelfAndKnownPlayersInRadius(player, new MagicSkillUse(player, player, holder.getSkillId(), holder.getLevel(), 0, 0), 600));
        return true;
    }

    protected abstract boolean isBlessed();

    protected abstract double getBonus(Player player);

    protected abstract boolean canUse(Player player);

    protected abstract ShotType getShotType();

    protected abstract SystemMessageId getEnabledShotsMessage();
}


// Check if Soul shot can be used
        if (!canUse(player)) {
            return false;
        }
        player.chargeShot(getShotType(), getBonus(player));
        player.sendPacket(getEnabledShotsMessage());

/*
 *
 * This file is part of the L2JOrg project.
 *
 */
package handlers.itemhandlers;

import org.l2j.gameserver.enums.ItemSkillType;
import org.l2j.gameserver.enums.ShotType;
import org.l2j.gameserver.handler.IItemHandler;
import org.l2j.gameserver.model.actor.Playable;
import org.l2j.gameserver.model.actor.instance.Player;
import org.l2j.gameserver.model.events.EventDispatcher;
import org.l2j.gameserver.model.events.impl.character.player.OnPlayeableChargeShots;
import org.l2j.gameserver.model.item.instance.Item;
import org.l2j.gameserver.network.SystemMessageId;
import org.l2j.gameserver.network.serverpackets.MagicSkillUse;
import org.l2j.gameserver.util.Broadcast;
import static org.l2j.commons.util.Util.isNullOrEmpty;

/**
 * @author JoeAlisson
 */
public abstract class AbstractShot implements IItemHandler {

    @Override
    public boolean useItem(Playable playable, Item item, boolean forceUse) {
        var player = playable.getActingPlayer();
        var skills = item.getSkills(ItemSkillType.NORMAL);
        if (isNullOrEmpty(skills)) {
            LOGGER.warn("item {} is missing skills!", item);
            return false;
        }

        EventDispatcher.getInstance().notifyEventAsync(new OnPlayeableChargeShots(player, getShotType(), isBlessed()), player);
        skills.forEach(holder -> Broadcast.toSelfAndKnownPlayersInRadius(player, new MagicSkillUse(player, player, holder.getSkillId(), holder.getLevel(), 0, 0), 600));
        return true;
    }

    protected abstract boolean isBlessed();

    protected abstract double getBonus(Player player);

    protected abstract boolean canUse(Player player);

    protected abstract ShotType getShotType();

    protected abstract SystemMessageId getEnabledShotsMessage();
}


EventDispatcher.getInstance().notifyEventAsync(new OnPlayeableChargeShots(player, getShotType(), isBlessed()), player);
        skills.forEach(holder -> Broadcast.toSelfAndKnownPlayersInRadius(player, new MagicSkillUse(player, player, holder.getSkillId(), holder.getLevel(), 0, 0), 600));
        return true;
    }

/*
 *
 * This file is part of the L2JOrg project.
 *
 */
package handlers.itemhandlers;

import org.l2j.gameserver.enums.ItemSkillType;
import org.l2j.gameserver.enums.ShotType;
import org.l2j.gameserver.handler.IItemHandler;
import org.l2j.gameserver.model.actor.Playable;
import org.l2j.gameserver.model.actor.instance.Player;
import org.l2j.gameserver.model.events.EventDispatcher;
import org.l2j.gameserver.model.events.impl.character.player.OnPlayeableChargeShots;
import org.l2j.gameserver.model.item.instance.Item;
import org.l2j.gameserver.network.SystemMessageId;
import org.l2j.gameserver.network.serverpackets.MagicSkillUse;
import org.l2j.gameserver.util.Broadcast;
import static org.l2j.commons.util.Util.isNullOrEmpty;

/**
 * @author JoeAlisson
 */
public abstract class AbstractShot implements IItemHandler {

    @Override
    public boolean useItem(Playable playable, Item item, boolean forceUse) {
        var player = playable.getActingPlayer();
        var skills = item.getSkills(ItemSkillType.NORMAL);
        if (isNullOrEmpty(skills)) {
            LOGGER.warn("item {} is missing skills!", item);
            return false;
        }
        // Check if Soul shot can be used
        if (!canUse(player)) {
            return false;
        }
        player.chargeShot(getShotType(), getBonus(player));
        player.sendPacket(getEnabledShotsMessage());

    protected abstract boolean isBlessed();

    protected abstract double getBonus(Player player);

    protected abstract boolean canUse(Player player);

    protected abstract ShotType getShotType();

    protected abstract SystemMessageId getEnabledShotsMessage();
}


/*
 *
 * This file is part of the L2JOrg project.
 *
 */
package handlers.itemhandlers;

import org.l2j.gameserver.enums.ItemSkillType;
import org.l2j.gameserver.enums.ShotType;
import org.l2j.gameserver.handler.IItemHandler;
import org.l2j.gameserver.model.actor.Playable;
import org.l2j.gameserver.model.actor.instance.Player;
import org.l2j.gameserver.model.events.EventDispatcher;
import org.l2j.gameserver.model.events.impl.character.player.OnPlayeableChargeShots;
import org.l2j.gameserver.model.item.instance.Item;
import org.l2j.gameserver.network.SystemMessageId;
import org.l2j.gameserver.network.serverpackets.MagicSkillUse;
import org.l2j.gameserver.util.Broadcast;
import static org.l2j.commons.util.Util.isNullOrEmpty;

/**
 * @author JoeAlisson
 */
public abstract class AbstractShot implements IItemHandler {

    @Override
    public boolean useItem(Playable playable, Item item, boolean forceUse) {
        var player = playable.getActingPlayer();
        var skills = ite

        if (isNullOrEmpty(skills)) {
            LOGGER.warn("item {} is missing skills!", item);
            return false;
        }
        // Check if Soul shot can be used
        if (!canUse(player)) {
            return false;
        }
        player.chargeShot(getShotType(), getBonus(player));
        player.sendPacket(getEnabledShotsMessage());
        EventDispatcher.getInstance().notifyEventAsync(new OnPlayeableChargeShots(player, getShotType(), isBlessed()), player);
        skills.forEach(holder -> Broadcast.toSelfAndKnownPlayersInRadius(player, new MagicSkillUse(player, player, holder.getSkillId(), holder.getLevel(), 0, 0), 600));
        return true;
    }

    protected abstract boolean isBlessed();

    protected abstract double getBonus(Player player);

    protected abstract boolean canUse(Player player);

    protected abstract ShotType getShotType();

    protected abstract SystemMessageId getEnabledShotsMessage();
}


if (isNullOrEmpty(skills)) {
            LOGGER.warn("item {} is missing skills!", item);
            return false;
        }

/*
 *
 * This file is part of the L2JOrg project.
 *
 */
package handlers.itemhandlers;

import org.l2j.gameserver.enums.ItemSkillType;
import org.l2j.gameserver.enums.ShotType;
import org.l2j.gameserver.handler.IItemHandler;
import org.l2j.gameserver.model.actor.Playable;
import org.l2j.gameserver.model.actor.instance.Player;
import org.l2j.gameserver.model.events.EventDispatcher;
import org.l2j.gameserver.model.events.impl.character.player.OnPlayeableChargeShots;
import org.l2j.gameserver.model.item.instance.Item;
import org.l2j.gameserver.network.SystemMessageId;
import org.l2j.gameserver.network.serverpackets.MagicSkillUse;
import org.l2j.gameserver.util.Broadcast;
import static org.l2j.commons.util.Util.isNullOrEmpty;

/**
 * @author JoeAlisson
 */
public abstract class AbstractShot implements IItemHandler {

    @Override
    public boolean useItem(Playable playable, Item item, boolean forceUse) {
        var player = playable.getActingPlayer();
        var skills = item.getSkills(ItemSkillType.NORMAL);


public static void main(String[] args) throws Exception

package org.quartz.examples.example4;

import static org.quartz.DateBuilder.nextGivenSecondDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;

/**
 * This Example will demonstrate how job parameters can be passed into jobs and how state can be maintained
 *
 * @author Bill Kratzer
 */
public class JobStateExample {

    public void run() throws Exception {
        Logger log = LoggerFactory.getLogger(JobStateExample.class);
        log.info("------- Initializing -------------------");
        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();
        log.info("------- Initialization Complete --------");
        log.info("------- Scheduling Jobs ----------------");
        // get a "nice round" time a few seconds in the future....
        Date startTime = nextGivenSecondDate(null, 10);
        // job1 will only run 5 times (at start time, plus 4 repeats), every 10 seconds
        JobDetail job1 = newJob(ColorJob.class).withIdentity("job1", "group1").build();
        SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job
        job1.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Green");
        job1.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime1 = sched.scheduleJob(job1, trigger1);
        log.info(job1.getKey() + " will run at: " + scheduleTime1 + " and repeat: " + trigger1.getRepeatCount() + " times, every " + trigger1.getRepeatInterval() / 1000 + " seconds");
        // job2 will also run 5 times, every 10 seconds
        JobDetail job2 = newJob(ColorJob.class).withIdentity("job2", "group1").build();
        SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job
        // this job has a different favorite color!
        job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
        job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime2 = sched.scheduleJob(job2, trigger2);
        log.info(job2.getKey().toString() + " will run at: " + scheduleTime2 + " and repeat: " + trigger2.getRepeatCount() + " times, every " + trigger2.getRepeatInterval() / 1000 + " seconds");
        log.info("------- Starting Scheduler ----------------");
        // All of the jobs have been added to the scheduler, but none of the jobs
        // will run until the scheduler has been started
        sched.start();
        log.info("------- Started Scheduler -----------------");
        log.info("------- Waiting 60 seconds... -------------");
        try {
            // wait five minutes to show jobs
            Thread.sleep(60L * 1000L);
        } catch (Exception e) {
        }
        log.info("------- Shutting Down ---------------------");
        sched.shutdown(true);
        log.info("------- Shutdown Complete -----------------");
        SchedulerMetaData metaData = sched.getMetaData();
        log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
    }

{
        JobStateExample example = new JobStateExample();
        example.run();
    }
}


{
        JobStateExample example = new JobStateExample();
        example.run();
    }

package org.quartz.examples.example4;

import static org.quartz.DateBuilder.nextGivenSecondDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;

/**
 * This Example will demonstrate how job parameters can be passed into jobs and how state can be maintained
 *
 * @author Bill Kratzer
 */
public class JobStateExample {

    public void run() throws Exception {
        Logger log = LoggerFactory.getLogger(JobStateExample.class);
        log.info("------- Initializing -------------------");
        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();
        log.info("------- Initialization Complete --------");
        log.info("------- Scheduling Jobs ----------------");
        // get a "nice round" time a few seconds in the future....
        Date startTime = nextGivenSecondDate(null, 10);
        // job1 will only run 5 times (at start time, plus 4 repeats), every 10 seconds
        JobDetail job1 = newJob(ColorJob.class).withIdentity("job1", "group1").build();
        SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job
        job1.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Green");
        job1.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime1 = sched.scheduleJob(job1, trigger1);
        log.info(job1.getKey() + " will run at: " + scheduleTime1 + " and repeat: " + trigger1.getRepeatCount() + " times, every " + trigger1.getRepeatInterval() / 1000 + " seconds");
        // job2 will also run 5 times, every 10 seconds
        JobDetail job2 = newJob(ColorJob.class).withIdentity("job2", "group1").build();
        SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job
        // this job has a different favorite color!
        job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
        job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime2 = sched.scheduleJob(job2, trigger2);
        log.info(job2.getKey().toString() + " will run at: " + scheduleTime2 + " and repeat: " + trigger2.getRepeatCount() + " times, every " + trigger2.getRepeatInterval() / 1000 + " seconds");
        log.info("------- Starting Scheduler ----------------");
        // All of the jobs have been added to the scheduler, but none of the jobs
        // will run until the scheduler has been started
        sched.start();
        log.info("------- Started Scheduler -----------------");
        log.info("------- Waiting 60 seconds... -------------");
        try {
            // wait five minutes to show jobs
            Thread.sleep(60L * 1000L);
        } catch (Exception e) {
        }
        log.info("------- Shutting Down ---------------------");
        sched.shutdown(true);
        log.info("------- Shutdown Complete -----------------");
        SchedulerMetaData metaData = sched.getMetaData();
        log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
    }

    public static void main(String[] args) throws Exception


        Logger log = LoggerFactory.getLogger(JobStateExample.class);
        log.info("------- Initializing -------------------");
        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();
        log.info("------- Initialization Complete --------");
        log.info("------- Scheduling Jobs ----------------");
        // get a "nice round" time a few seconds in the future....
        Date startTime = nextGivenSecondDate(null, 10);
        // job1 will only run 5 times (at start time, plus 4 repeats), every 10 seconds
        JobDetail job1 = newJob(ColorJob.class).withIdentity("job1", "group1").build();
        SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job

package org.quartz.examples.example4;

import static org.quartz.DateBuilder.nextGivenSecondDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;

/**
 * This Example will demonstrate how job parameters can be passed into jobs and how state can be maintained
 *
 * @author Bill Kratzer
 */
public class JobStateExample {

    public void run() throws Exception {

        job1.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Green");
        job1.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime1 = sched.scheduleJob(job1, trigger1);
        log.info(job1.getKey() + " will run at: " + scheduleTime1 + " and repeat: " + trigger1.getRepeatCount() + " times, every " + trigger1.getRepeatInterval() / 1000 + " seconds");
        // job2 will also run 5 times, every 10 seconds
        JobDetail job2 = newJob(ColorJob.class).withIdentity("job2", "group1").build();
        SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job
        // this job has a different favorite color!
        job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
        job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime2 = sched.scheduleJob(job2, trigger2);
        log.info(job2.getKey().toString() + " will run at: " + scheduleTime2 + " and repeat: " + trigger2.getRepeatCount() + " times, every " + trigger2.getRepeatInterval() / 1000 + " seconds");
        log.info("------- Starting Scheduler ----------------");
        // All of the jobs have been added to the scheduler, but none of the jobs
        // will run until the scheduler has been started
        sched.start();
        log.info("------- Started Scheduler -----------------");
        log.info("------- Waiting 60 seconds... -------------");
        try {
            // wait five minutes to show jobs
            Thread.sleep(60L * 1000L);
        } catch (Exception e) {
        }
        log.info("------- Shutting Down ---------------------");
        sched.shutdown(true);
        log.info("------- Shutdown Complete -----------------");
        SchedulerMetaData metaData = sched.getMetaData();
        log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
    }

    public static void main(String[] args) throws Exception {
        JobStateExample example = new JobStateExample();
        example.run();
    }
}


job1.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Green");
        job1.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime1 = sched.scheduleJob(job1, trigger1);
        log.info(job1.getKey() + " will run at: " + scheduleTime1 + " and repeat: " + trigger1.getRepeatCount() + " times, every " + trigger1.getRepeatInterval() / 1000 + " seconds");
        // job2 will also run 5 times, every 10 seconds
        JobDetail job2 = newJob(ColorJob.class).withIdentity("job2", "group1").build();
        SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job
        // this job has a different favorite color!
        job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
        job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
        // schedule the job to run
        Date scheduleTime2 = sched.scheduleJob(job2, trigger2);

package org.quartz.examples.example4;

import static org.quartz.DateBuilder.nextGivenSecondDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;

/**
 * This Example will demonstrate how job parameters can be passed into jobs and how state can be maintained
 *
 * @author Bill Kratzer
 */
public class JobStateExample {

    public void run() throws Exception {
        Logger log = LoggerFactory.getLogger(JobStateExample.class);
        log.info("------- Initializing -------------------");
        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();
        log.info("------- Initialization Complete --------");
        log.info("------- Scheduling Jobs ----------------");
        // get a "nice round" time a few seconds in the future....
        Date startTime = nextGivenSecondDate(null, 10);
        // job1 will only run 5 times (at start time, plus 4 repeats), every 10 seconds
        JobDetail job1 = newJob(ColorJob.class).withIdentity("job1", "group1").build();
        SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(4)).build();
        // pass initialization parameters into the job

        log.info(job2.getKey().toString() + " will run at: " + scheduleTime2 + " and repeat: " + trigger2.getRepeatCount() + " times, every " + trigger2.getRepeatInterval() / 1000 + " seconds");
        log.info("------- Starting Scheduler ----------------");
        // All of the jobs have been added to the scheduler, but none of the jobs
        // will run until the scheduler has been started
        sched.start();
        log.info("------- Started Scheduler -----------------");
        log.info("------- Waiting 60 seconds... -------------");
        try {
            // wait five minutes to show jobs
            Thread.sleep(60L * 1000L);
        } catch (Exception e) {
        }
        log.info("------- Shutting Down ---------------------");
        sched.shutdown(true);
        log.info("------- Shutdown Complete -----------------");
        SchedulerMetaData metaData = sched.getMetaData();
        log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
    }

    public static void main(String[] args) throws Exception {
        JobStateExample example = new JobStateExample();
        example.run();
    }
}