001/*
002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.test.generator.internal.net.java.quickcheck.generator.support;
017
018import de.cuioss.test.generator.internal.net.java.quickcheck.Generator;
019import de.cuioss.test.generator.internal.net.java.quickcheck.GeneratorException;
020
021/**
022 * Base class for generators which can reject the values generated by their
023 * wrapped generator. This will be tried until the maximum number of tries is
024 * reached.
025 */
026public abstract class VetoableGenerator<T> implements Generator<T> {
027
028    private final Generator<? extends T> generator;
029    private final int maxTries;
030
031    // TODO this could be a bit high
032    // for runs = 200 this means 20000 tries for the worst case
033    public static final int DEFAULT_MAX_TRIES = 100;
034
035    public static final int MIN_TRIES = 1;
036
037    protected VetoableGenerator(Generator<? extends T> generator) {
038        this(generator, DEFAULT_MAX_TRIES);
039    }
040
041    protected VetoableGenerator(Generator<? extends T> generator, int maxTries) {
042        if (MIN_TRIES > maxTries) {
043            throw new IllegalArgumentException("maxTries");
044        }
045        this.generator = generator;
046        this.maxTries = maxTries;
047    }
048
049    @Override
050    public T next() throws GeneratorException {
051        for (int idx = 0; idx < maxTries; idx++) {
052            T value = generator.next();
053            if (tryValue(value))
054                return value;
055        }
056        throw new GeneratorException(
057                "Failed to generate another value after [%s] tries (generator: %s)".formatted(maxTries, generator),
058                generator);
059    }
060
061    /**
062     * @return true to accept the current value.
063     */
064    protected abstract boolean tryValue(T value);
065}