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 de.cuioss.tools.base.Preconditions.checkArgument;
020
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Objects;
024
025import de.cuioss.test.generator.internal.net.java.quickcheck.FrequencyGenerator;
026import de.cuioss.test.generator.internal.net.java.quickcheck.Generator;
027
028public class DefaultFrequencyGenerator<T> implements FrequencyGenerator<T> {
029
030    /**
031     * Weight used to generate equal weighted frequency generator.
032     */
033    public static final int EQUAL_WEIGHT_OF_GENERATORS = 1;
034
035    private final List<Frequency<T>> frequencies = new ArrayList<>();
036    private IntegerGenerator choose;
037    private int sum = 0;
038
039    public DefaultFrequencyGenerator(Generator<T> generator) {
040        this(generator, EQUAL_WEIGHT_OF_GENERATORS);
041    }
042
043    public DefaultFrequencyGenerator(Generator<T> generator, int weight) {
044        add(generator, weight);
045    }
046
047    @Override
048    public FrequencyGenerator<T> add(Generator<T> generator) {
049        return add(generator, EQUAL_WEIGHT_OF_GENERATORS);
050    }
051
052    @Override
053    public FrequencyGenerator<T> add(Generator<T> generator, int weight) {
054        Objects.requireNonNull(generator, "generator");
055        checkArgument(EQUAL_WEIGHT_OF_GENERATORS <= weight, "weight");
056
057        this.frequencies.add(new Frequency<>(generator, weight));
058        this.sum += weight;
059        this.choose = null;
060        return this;
061    }
062
063    @Override
064    public T next() {
065        checkArgument(1 <= this.sum, "number of generators");
066
067        int next = choose().nextInt();
068        for (Frequency<T> pair : this.frequencies) {
069            int weight = pair.getWeight();
070            if (next <= weight) {
071                return pair.getGenerator().next();
072            }
073            next -= weight;
074        }
075        throw new IllegalStateException();
076    }
077
078    private IntegerGenerator choose() {
079        if (this.choose == null) {
080            this.choose = new IntegerGenerator(1, this.sum);
081        }
082        return this.choose;
083    }
084
085    private static class Frequency<T> {
086
087        private final Generator<T> generator;
088        private final int weight;
089
090        private Frequency(Generator<T> generator, int weight) {
091            this.generator = generator;
092            this.weight = weight;
093        }
094
095        private Generator<T> getGenerator() {
096            return this.generator;
097        }
098
099        private int getWeight() {
100            return this.weight;
101        }
102    }
103}