001/*
002 * Licensed to the author under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package de.cuioss.test.generator.internal.net.java.quickcheck.generator.support;
018
019import static java.lang.Math.signum;
020import static java.lang.String.format;
021
022import java.util.Calendar;
023import java.util.Date;
024import java.util.concurrent.TimeUnit;
025
026import de.cuioss.test.generator.internal.net.java.quickcheck.Generator;
027
028public class DateGenerator implements Generator<Date> {
029
030    private final VetoableGenerator<Long> generator;
031
032    public DateGenerator(TimeUnit precision, long low, long high, int tries) {
033        generator = new VetoableGenerator<>(new MillisGenerator(precision,
034                low, high), tries) {
035
036            @Override
037            protected boolean tryValue(Long value) {
038                return value != null;
039            }
040        };
041    }
042
043    @Override
044    public Date next() {
045        return new Date(generator.next());
046    }
047
048    private static class MillisGenerator implements Generator<Long> {
049
050        private final TimeUnit precision;
051        private final LongGenerator times;
052        private final long low;
053        private final long high;
054
055        public MillisGenerator(TimeUnit precision, long low, long high) {
056            this.precision = precision;
057            this.times = new LongGenerator(low, high);
058            this.low = low;
059            this.high = high;
060        }
061
062        @Override
063        public Long next() {
064            Long millis = times.next();
065            Calendar time = Calendar.getInstance();
066            time.setTimeInMillis(millis);
067            switch (precision) {
068                case DAYS:
069                    time.set(Calendar.HOUR, 0); //$FALL-THROUGH$
070                case HOURS:
071                    time.set(Calendar.MINUTE, 0); //$FALL-THROUGH$
072                case MINUTES:
073                    time.set(Calendar.SECOND, 0); //$FALL-THROUGH$
074                case SECONDS:
075                    time.set(Calendar.MILLISECOND, 0); //$FALL-THROUGH$
076                default:
077            }
078            long correctedMillis = time.getTimeInMillis();
079            return isOutOffBounds(correctedMillis) || isOverflow(millis, correctedMillis) ? null : correctedMillis;
080        }
081
082        private boolean isOutOffBounds(long correctedMillis) {
083            return correctedMillis < low || correctedMillis > high;
084        }
085
086        // TODO define this differently (signum changes near 0 without overflow)
087        private boolean isOverflow(long millis, long correctedMillis) {
088            return signum(correctedMillis) != signum(millis);
089        }
090
091        @Override
092        public String toString() {
093            return format("%s[low=%s, high=%s, precision=%s", getClass().getSimpleName(), low, high, precision);
094        }
095
096    }
097}