001    /*
002     * The MIT License
003     *
004     * Copyright (c) 2013, Ninja Squad
005     *
006     * Permission is hereby granted, free of charge, to any person obtaining a copy
007     * of this software and associated documentation files (the "Software"), to deal
008     * in the Software without restriction, including without limitation the rights
009     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010     * copies of the Software, and to permit persons to whom the Software is
011     * furnished to do so, subject to the following conditions:
012     *
013     * The above copyright notice and this permission notice shall be included in
014     * all copies or substantial portions of the Software.
015     *
016     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
022     * THE SOFTWARE.
023     */
024    
025    package com.ninja_squad.dbsetup.generator;
026    
027    import com.ninja_squad.dbsetup.util.Preconditions;
028    
029    import javax.annotation.Nonnull;
030    import java.sql.Timestamp;
031    import java.util.Calendar;
032    import java.util.Date;
033    import java.util.TimeZone;
034    
035    /**
036     * A {@link ValueGenerator} that returns a sequence of dates, starting at a given date and incremented by a given
037     * time, specified as an increment and a calendar field.
038     * @author JB
039     */
040    public final class DateSequenceValueGenerator implements ValueGenerator<Date> {
041    
042        // the number of chars in yyyy-mm-dd hh:mm:ss
043        private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;
044    
045        /**
046         * The available units for the increment of this sequence
047         */
048        public enum CalendarField {
049            YEAR(Calendar.YEAR),
050            MONTH(Calendar.MONTH),
051            DAY(Calendar.DATE),
052            HOUR(Calendar.HOUR),
053            MINUTE(Calendar.MINUTE),
054            SECOND(Calendar.SECOND),
055            MILLISECOND(Calendar.MILLISECOND);
056    
057            private int field;
058    
059            CalendarField(int field) {
060                this.field = field;
061            }
062    
063            private int getField() {
064                return field;
065            }
066        }
067    
068        private Calendar next;
069        private int increment;
070        private CalendarField unit;
071    
072        DateSequenceValueGenerator() {
073            this(today(), 1, CalendarField.DAY);
074        }
075    
076        private DateSequenceValueGenerator(Calendar next, int increment, CalendarField unit) {
077            this.next = next;
078            this.increment = increment;
079            this.unit = unit;
080        }
081    
082        private static Calendar today() {
083            Calendar result = Calendar.getInstance();
084            result.set(Calendar.HOUR_OF_DAY, 0);
085            result.set(Calendar.MINUTE, 0);
086            result.set(Calendar.SECOND, 0);
087            result.set(Calendar.MILLISECOND, 0);
088            return result;
089        }
090    
091        /**
092         * Restarts the sequence at the given date, in the given time zone
093         * @return this instance, for chaining
094         */
095        public DateSequenceValueGenerator startingAt(@Nonnull Date startDate, @Nonnull TimeZone timeZone) {
096            Preconditions.checkNotNull(startDate, "startDate may not be null");
097            Preconditions.checkNotNull(timeZone, "timeZone may not be null");
098            next = Calendar.getInstance(timeZone);
099            next.setTime(startDate);
100            return this;
101        }
102    
103        /**
104         * Restarts the sequence at the given date, in the default time zone
105         * @return this instance, for chaining
106         */
107        public DateSequenceValueGenerator startingAt(@Nonnull Date startDate) {
108            return startingAt(startDate, TimeZone.getDefault());
109        }
110    
111        /**
112         * Restarts the sequence at the given date
113         * @return this instance, for chaining
114         */
115        public DateSequenceValueGenerator startingAt(@Nonnull Calendar startDate) {
116            Preconditions.checkNotNull(startDate, "startDate may not be null");
117            next = (Calendar) startDate.clone();
118            return this;
119        }
120    
121        /**
122         * Restarts the sequence at the given date, in the default time zone
123         * @param startDate the starting date, as a String. The supported formats are the same as the ones supported by
124         * {@link com.ninja_squad.dbsetup.bind.Binders#timestampBinder()}, i.e. the formats supported by
125         * <code>java.sql.Timestamp.valueOf()</code> and <code>java.sql.Date.valueOf()</code>
126         * @return this instance, for chaining
127         */
128        public DateSequenceValueGenerator startingAt(@Nonnull String startDate) {
129            Preconditions.checkNotNull(startDate, "startDate may not be null");
130            if (startDate.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {
131                return startingAt(Timestamp.valueOf(startDate));
132            }
133            else {
134                return startingAt(java.sql.Date.valueOf(startDate));
135            }
136        }
137    
138        /**
139         * Increments the date by the given increment of the given unit.
140         * @return this instance, for chaining
141         */
142        public DateSequenceValueGenerator incrementingBy(int increment, @Nonnull CalendarField unit) {
143            Preconditions.checkNotNull(unit, "unit may not be null");
144            this.increment = increment;
145            this.unit = unit;
146            return this;
147        }
148    
149        @Override
150        public Date nextValue() {
151            Date result = next.getTime();
152            next.add(unit.getField(), increment);
153            return result;
154        }
155    
156        @Override
157        public String toString() {
158            return "DateSequenceValueGenerator["
159                   + "next=" + next
160                   + ", increment=" + increment
161                   + ", unit=" + unit
162                   + "]";
163        }
164    }