package org.magenta.core;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.magenta.DataSet;
import org.magenta.DataSpecification;
import org.magenta.FixtureFactory;
import org.magenta.GenerationStrategy;
import org.magenta.Generator;
import org.magenta.generators.TransformedStrategy;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* Implementation of a {@link Generator} that delegates to a
* {@link GenerationStrategy} to actually generate data.
*
* @author ngagnon
*
* @param <T>
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class GeneratorImpl<T, S extends DataSpecification> implements Generator<T> {
private final FixtureFactory<S> dataSetMap;
private final GenerationStrategy<T, ? super S> strategy;
private final Class<T> type;
private static final int MAX_COUNT = 1000;
/**
* Default constructor.
*
* @param dataSetMap
* the dataSetMap to which this generator belongs
* @param strategy
* the strategy to use to generate data.
* @param type
* the type of generated data.
*/
public GeneratorImpl(FixtureFactory dataSetMap, GenerationStrategy<T, ? super S> strategy, Class<T> type) {
this.dataSetMap = dataSetMap;
this.strategy = strategy;
this.type = type;
}
@Override
public Generator<T> restrictTo(Object... objects) {
FixtureFactory child = this.dataSetMap.newNode("restricted " + this.dataSetMap.getName());
RestrictionHelper.applyRestrictions(child, objects);
return new GeneratorImpl(child, this.strategy, type);
}
@Override
public T any() {
// TODO this implementation always generate the "first" element, this may
// prevent the result from being truly generated
return strategy.generate(1, this.dataSetMap).iterator().next();
}
@Override
public T any(Predicate<? super T> filter) {
int i = 0;
T candidate = null;
do {
candidate = any();
} while (i++ < MAX_COUNT && filter.apply(candidate));
if (i >= MAX_COUNT) {
throw new IllegalStateException(String.format("reach maximum count while trying to generate a %s that matches the filter %s", type, filter));
}
return candidate;
}
@Override
public Iterable<T> get() {
return strategy.generate(this.dataSetMap);
}
@Override
public T[] array() {
return Iterables.toArray(get(), this.type);
}
@Override
public T[] array(int size) {
return Iterables.toArray(list(size), this.type);
}
@Override
public T[] randomArray() {
return Iterables.toArray(randomList(), this.type);
}
@Override
public T[] randomArray(int size) {
return Iterables.toArray(randomList(size), this.type);
}
@Override
public List<T> list() {
return Lists.newArrayList(strategy.generate(this.dataSetMap));
}
@Override
public List<T> list(int size) {
return Lists.newArrayList(strategy.generate(size, this.dataSetMap));
}
@Override
public List<T> randomList() {
return list();
}
@Override
public List<T> randomList(int size) {
return list(size);
}
@Override
public Set<T> set() {
return Sets.newHashSet(strategy.generate(this.dataSetMap));
}
@Override
public Set<T> set(int size) {
return Sets.newHashSet(strategy.generate(size, this.dataSetMap));
}
@Override
public DataSet<T> subset(final int size) {
return new GenericDataSet<T>(Suppliers.memoize(new Supplier<Iterable<T>>() {
@Override
public Iterable<T> get() {
return strategy.generate(size, dataSetMap);
}
}), this.type, dataSetMap.getRandomizer());
}
@Override
public Generator<T> filter(final Predicate<? super T> filter) {
return new GeneratorImpl<T, S>(dataSetMap, new TransformedStrategy<T, T, S>(strategy, filter, Functions.<T> identity()), type);
}
@Override
public Generator<T> without(T... items) {
return filter(not(in(Arrays.asList(items))));
}
@Override
public Generator<T> without(Collection<T> items) {
return filter(not(in(items)));
}
@Override
public <X> Generator<X> transform(final Function<? super T, X> function, Class<X> newType) {
return new GeneratorImpl<X, S>(dataSetMap, new TransformedStrategy<>(strategy, Predicates.alwaysTrue(), function), newType);
}
@Override
public <X> DataSet<X> cast(final Class<X> type) {
return transform(new Function<T,X>(){
@Override
public X apply(T input) {
return type.cast(input);
}
}, type);
}
/*
* @Override public Generator<T> process(final Function<? super T, Void>
* processor) { return new GeneratorImpl<T,S>(dataSetMap,new
* TransformedStrategy<>(strategy, Predicates.alwaysTrue(), new Function<T,
* T>() {
*
* @Override public T apply(T input) { processor.apply(input); return input; }
* }),type); }
*/
@Override
public T link(Object o) {
throw new UnsupportedOperationException("Linking objects is not supported on strategy");
}
@Override
public <L> Iterable<L> reverseLink(Class<L> type, T referred) {
throw new UnsupportedOperationException("reverseLink objects is not supported on strategy");
}
@Override
public Class<T> getType() {
return this.type;
}
@Override
public boolean isGenerated() {
return true;
}
@Override
public boolean isPersistent() {
return false;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public DataSet<T> toTransient() {
return this;
}
@Override
public DataSet<T> persist() {
throw new UnsupportedOperationException("This dataset is not persistent:"+this.toString());
}
@Override
public DataSet<T> load() {
return this;
}
}