001package org.javasimon.callback.timeline;
002
003import org.javasimon.callback.lastsplits.CircularList;
004
005/**
006 * Collection of values sorted on a time line.
007 *
008 * @author gerald
009 */
010public abstract class Timeline<TR extends TimeRange> {
011
012        /** List of time ranges. */
013        protected final CircularList<TR> timeRanges;
014        /** Time range width in milliseconds. */
015        protected final long timeRangeWidth;
016
017        /** Last used time range. */
018        private TR lastTimeRange;
019
020        /**
021         * Main constructor.
022         *
023         * @param capacity Number of time ranges
024         * @param timeRangeWidth Width of each time range
025         */
026        protected Timeline(int capacity, long timeRangeWidth) {
027                this.timeRanges = new CircularList<>(capacity);
028                this.timeRangeWidth = timeRangeWidth;
029        }
030
031        /**
032         * Creates time range (factory method).
033         *
034         * @param startTimestamp Time range start
035         * @param endTimestamp Time range end
036         * @return created time range
037         */
038        protected abstract TR createTimeRange(long startTimestamp, long endTimestamp);
039
040        /**
041         * Returns existing time range if it already exists or create a new one.
042         */
043        protected final TR getOrCreateTimeRange(long timestamp) {
044                TR timeRange;
045                if (lastTimeRange == null || timestamp > lastTimeRange.getEndTimestamp()) {
046                        // New time range
047                        long roundedTimestamp = timestamp - timestamp % timeRangeWidth;
048                        timeRange = createTimeRange(roundedTimestamp, roundedTimestamp + timeRangeWidth);
049                        timeRanges.add(timeRange);
050                        lastTimeRange = timeRange;
051                } else if (timestamp >= lastTimeRange.getStartTimestamp()) {
052                        // Current time range
053                        timeRange = lastTimeRange;
054                } else {
055                        // Old time range
056                        timeRange = null;
057                        // Not very good from performance point of view
058                        // iterating from end till start would be a better solution
059                        for (TR oldTimeRange : timeRanges) {
060                                if (oldTimeRange.containsTimestamp(timestamp)) {
061                                        timeRange = oldTimeRange;
062                                        break;
063                                }
064                        }
065                }
066
067                return timeRange;
068        }
069
070        public abstract TimelineSample<TR> sample();
071}