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;
020
021import java.util.Calendar;
022import java.util.Date;
023import java.util.concurrent.TimeUnit;
024
025import de.cuioss.test.generator.internal.net.java.quickcheck.Generator;
026
027public class DateGenerator implements Generator<Date> {
028
029    private final VetoableGenerator<Long> generator;
030
031    public DateGenerator(TimeUnit precision, long low, long high, int tries) {
032        generator = new VetoableGenerator<>(new MillisGenerator(precision, low, high), tries) {
033
034            @Override
035            protected boolean tryValue(Long value) {
036                return value != null;
037            }
038        };
039    }
040
041    @Override
042    public Date next() {
043        return new Date(generator.next());
044    }
045
046    private static class MillisGenerator implements Generator<Long> {
047
048        private final TimeUnit precision;
049        private final LongGenerator times;
050        private final long low;
051        private final long high;
052
053        public MillisGenerator(TimeUnit precision, long low, long high) {
054            this.precision = precision;
055            this.times = new LongGenerator(low, high);
056            this.low = low;
057            this.high = high;
058        }
059
060        @Override
061        public Long next() {
062            Long millis = times.next();
063            Calendar time = Calendar.getInstance();
064            time.setTimeInMillis(millis);
065            switch (precision) {
066            case DAYS:
067                time.set(Calendar.HOUR, 0); //$FALL-THROUGH$
068            case HOURS:
069                time.set(Calendar.MINUTE, 0); //$FALL-THROUGH$
070            case MINUTES:
071                time.set(Calendar.SECOND, 0); //$FALL-THROUGH$
072            case SECONDS:
073                time.set(Calendar.MILLISECOND, 0); //$FALL-THROUGH$
074            default:
075            }
076            long correctedMillis = time.getTimeInMillis();
077            return isOutOffBounds(correctedMillis) || isOverflow(millis, correctedMillis) ? null : correctedMillis;
078        }
079
080        private boolean isOutOffBounds(long correctedMillis) {
081            return correctedMillis < low || correctedMillis > high;
082        }
083
084        // TODO define this differently (signum changes near 0 without overflow)
085        private boolean isOverflow(long millis, long correctedMillis) {
086            return signum(correctedMillis) != signum(millis);
087        }
088
089        @Override
090        public String toString() {
091            return "%s[low=%s, high=%s, precision=%s".formatted(getClass().getSimpleName(), low, high, precision);
092        }
093
094    }
095}