All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.navercorp.fixturemonkey.FixtureMonkeyBuilder Maven / Gradle / Ivy

There is a newer version: 1.1.8
Show newest version
/*
 * Fixture Monkey
 *
 * Copyright (c) 2021-present NAVER Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.navercorp.fixturemonkey;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.UnaryOperator;

import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;

import com.navercorp.fixturemonkey.api.constraint.JavaConstraintGenerator;
import com.navercorp.fixturemonkey.api.container.DecomposedContainerValueFactory;
import com.navercorp.fixturemonkey.api.context.MonkeyContext;
import com.navercorp.fixturemonkey.api.context.MonkeyContextBuilder;
import com.navercorp.fixturemonkey.api.generator.ArbitraryContainerInfoGenerator;
import com.navercorp.fixturemonkey.api.generator.ArbitraryGenerator;
import com.navercorp.fixturemonkey.api.generator.ContainerPropertyGenerator;
import com.navercorp.fixturemonkey.api.generator.InterfaceObjectPropertyGenerator;
import com.navercorp.fixturemonkey.api.generator.NullInjectGenerator;
import com.navercorp.fixturemonkey.api.generator.ObjectPropertyGenerator;
import com.navercorp.fixturemonkey.api.introspector.ArbitraryIntrospector;
import com.navercorp.fixturemonkey.api.introspector.MatchArbitraryIntrospector;
import com.navercorp.fixturemonkey.api.introspector.NullArbitraryIntrospector;
import com.navercorp.fixturemonkey.api.matcher.AssignableTypeMatcher;
import com.navercorp.fixturemonkey.api.matcher.ExactTypeMatcher;
import com.navercorp.fixturemonkey.api.matcher.Matcher;
import com.navercorp.fixturemonkey.api.matcher.MatcherOperator;
import com.navercorp.fixturemonkey.api.option.FixtureMonkeyOptions;
import com.navercorp.fixturemonkey.api.option.FixtureMonkeyOptionsBuilder;
import com.navercorp.fixturemonkey.api.plugin.InterfacePlugin;
import com.navercorp.fixturemonkey.api.plugin.Plugin;
import com.navercorp.fixturemonkey.api.property.CandidateConcretePropertyResolver;
import com.navercorp.fixturemonkey.api.property.PropertyGenerator;
import com.navercorp.fixturemonkey.api.property.PropertyNameResolver;
import com.navercorp.fixturemonkey.api.random.Randoms;
import com.navercorp.fixturemonkey.api.type.Types;
import com.navercorp.fixturemonkey.api.validator.ArbitraryValidator;
import com.navercorp.fixturemonkey.buildergroup.ArbitraryBuilderCandidate;
import com.navercorp.fixturemonkey.buildergroup.ArbitraryBuilderGroup;
import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory;
import com.navercorp.fixturemonkey.expression.ArbitraryExpressionFactory;
import com.navercorp.fixturemonkey.expression.MonkeyExpressionFactory;
import com.navercorp.fixturemonkey.resolver.ManipulatorOptimizer;
import com.navercorp.fixturemonkey.resolver.NoneManipulatorOptimizer;
import com.navercorp.fixturemonkey.tree.ApplyStrictModeResolver;
import com.navercorp.fixturemonkey.tree.ArbitraryTraverser;

@SuppressWarnings("unused")
@API(since = "0.4.0", status = Status.MAINTAINED)
public final class FixtureMonkeyBuilder {
	private final FixtureMonkeyOptionsBuilder fixtureMonkeyOptionsBuilder = FixtureMonkeyOptions.builder();
	private final List>>>
		registeredArbitraryBuilders = new ArrayList<>();
	private ManipulatorOptimizer manipulatorOptimizer = new NoneManipulatorOptimizer();
	private MonkeyExpressionFactory monkeyExpressionFactory = new ArbitraryExpressionFactory();
	private final MonkeyContextBuilder monkeyContextBuilder = MonkeyContext.builder();
	private long seed = System.nanoTime();

	// The default plugins are listed below.
	private InterfacePlugin defaultInterfacePlugin = new InterfacePlugin();

	public FixtureMonkeyBuilder pushPropertyGenerator(MatcherOperator propertyGenerator) {
		fixtureMonkeyOptionsBuilder.insertFirstPropertyGenerator(propertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushAssignableTypePropertyGenerator(
		Class type,
		PropertyGenerator propertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstPropertyGenerator(type, propertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushExactTypePropertyGenerator(Class type, PropertyGenerator propertyGenerator) {
		fixtureMonkeyOptionsBuilder.insertFirstPropertyGenerator(
			MatcherOperator.assignableTypeMatchOperator(type, propertyGenerator)
		);
		return this;
	}

	public FixtureMonkeyBuilder manipulatorOptimizer(ManipulatorOptimizer manipulatorOptimizer) {
		this.manipulatorOptimizer = manipulatorOptimizer;
		return this;
	}

	public FixtureMonkeyBuilder defaultObjectPropertyGenerator(
		ObjectPropertyGenerator objectPropertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.defaultObjectPropertyGenerator(objectPropertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushAssignableTypeObjectPropertyGenerator(
		Class type,
		ObjectPropertyGenerator objectPropertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryObjectPropertyGenerator(type, objectPropertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushExactTypeObjectPropertyGenerator(
		Class type,
		ObjectPropertyGenerator objectPropertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryObjectPropertyGenerator(
			MatcherOperator.exactTypeMatchOperator(type, objectPropertyGenerator)
		);
		return this;
	}

	public FixtureMonkeyBuilder pushObjectPropertyGenerator(
		MatcherOperator objectPropertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryObjectPropertyGenerator(objectPropertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushAssignableTypeContainerPropertyGenerator(
		Class type,
		ContainerPropertyGenerator containerPropertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryContainerPropertyGenerator(type, containerPropertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushExactTypeContainerPropertyGenerator(
		Class type,
		ContainerPropertyGenerator containerPropertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryContainerPropertyGenerator(
			MatcherOperator.exactTypeMatchOperator(type, containerPropertyGenerator)
		);
		return this;
	}

	public FixtureMonkeyBuilder pushContainerPropertyGenerator(
		MatcherOperator containerPropertyGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryContainerPropertyGenerator(containerPropertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushAssignableTypePropertyNameResolver(
		Class type,
		PropertyNameResolver propertyNameResolver
	) {
		fixtureMonkeyOptionsBuilder.insertFirstPropertyNameResolver(type, propertyNameResolver);
		return this;
	}

	public FixtureMonkeyBuilder pushExactTypePropertyNameResolver(
		Class type,
		PropertyNameResolver propertyNameResolver
	) {
		fixtureMonkeyOptionsBuilder.insertFirstPropertyNameResolver(
			MatcherOperator.exactTypeMatchOperator(type, propertyNameResolver)
		);
		return this;
	}

	public FixtureMonkeyBuilder pushPropertyNameResolver(
		MatcherOperator propertyNameResolver
	) {
		fixtureMonkeyOptionsBuilder.insertFirstPropertyNameResolver(propertyNameResolver);
		return this;
	}

	public FixtureMonkeyBuilder defaultPropertyNameResolver(PropertyNameResolver propertyNameResolver) {
		fixtureMonkeyOptionsBuilder.defaultPropertyNameResolver(propertyNameResolver);
		return this;
	}

	public FixtureMonkeyBuilder pushExactTypeNullInjectGenerator(
		Class type,
		NullInjectGenerator nullInjectGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstNullInjectGenerators(
			MatcherOperator.exactTypeMatchOperator(type, nullInjectGenerator)
		);
		return this;
	}

	public FixtureMonkeyBuilder pushAssignableTypeNullInjectGenerator(
		Class type,
		NullInjectGenerator nullInjectGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstNullInjectGenerators(type, nullInjectGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushNullInjectGenerator(MatcherOperator nullInjectGenerator) {
		fixtureMonkeyOptionsBuilder.insertFirstNullInjectGenerators(nullInjectGenerator);
		return this;
	}

	public FixtureMonkeyBuilder defaultNullInjectGenerator(NullInjectGenerator nullInjectGenerator) {
		fixtureMonkeyOptionsBuilder.defaultNullInjectGenerator(nullInjectGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushArbitraryContainerInfoGenerator(
		MatcherOperator arbitraryContainerInfoGenerator
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryContainerInfoGenerator(arbitraryContainerInfoGenerator);
		return this;
	}

	public FixtureMonkeyBuilder defaultArbitraryContainerInfoGenerator(
		ArbitraryContainerInfoGenerator defaultArbitraryContainerInfoGenerator) {
		fixtureMonkeyOptionsBuilder.defaultArbitraryContainerInfoGenerator(defaultArbitraryContainerInfoGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushAssignableTypeArbitraryIntrospector(
		Class type,
		ArbitraryIntrospector arbitraryIntrospector
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryIntrospector(type, arbitraryIntrospector);
		return this;
	}

	public FixtureMonkeyBuilder pushExactTypeArbitraryIntrospector(
		Class type,
		ArbitraryIntrospector arbitraryIntrospector
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryIntrospector(
			MatcherOperator.exactTypeMatchOperator(type, arbitraryIntrospector)
		);
		return this;
	}

	public FixtureMonkeyBuilder pushArbitraryIntrospector(
		MatcherOperator arbitraryIntrospector
	) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryIntrospector(arbitraryIntrospector);
		return this;
	}

	public FixtureMonkeyBuilder objectIntrospector(ArbitraryIntrospector objectIntrospector) {
		this.fixtureMonkeyOptionsBuilder.objectIntrospector(it -> objectIntrospector);
		return this;
	}

	public FixtureMonkeyBuilder arbitraryValidator(ArbitraryValidator arbitraryValidator) {
		this.fixtureMonkeyOptionsBuilder.defaultArbitraryValidator(arbitraryValidator);
		return this;
	}

	public FixtureMonkeyBuilder pushExceptGenerateType(Matcher matcher) {
		fixtureMonkeyOptionsBuilder.insertFirstArbitraryIntrospector(
			new MatcherOperator<>(
				matcher,
				NullArbitraryIntrospector.INSTANCE
			)
		);
		return this;
	}

	public FixtureMonkeyBuilder addExceptGenerateClass(Class type) {
		return pushExceptGenerateType(new AssignableTypeMatcher(type));
	}

	public FixtureMonkeyBuilder addExceptGenerateClasses(Class... types) {
		for (Class type : types) {
			addExceptGenerateClass(type);
		}
		return this;
	}

	public FixtureMonkeyBuilder addExceptGeneratePackage(String exceptGeneratePackage) {
		return pushExceptGenerateType(
			property -> Types.primitiveToWrapper(Types.getActualType(property.getType()))
				.getPackage()
				.getName()
				.startsWith(exceptGeneratePackage)
		);
	}

	public FixtureMonkeyBuilder addExceptGeneratePackages(String... exceptGeneratePackages) {
		for (String exceptGeneratePackage : exceptGeneratePackages) {
			addExceptGeneratePackage(exceptGeneratePackage);
		}
		return this;
	}

	public FixtureMonkeyBuilder register(
		Class type,
		Function> registeredArbitraryBuilder
	) {
		return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder));
	}

	public FixtureMonkeyBuilder registerExactType(
		Class type,
		Function> registeredArbitraryBuilder
	) {
		return this.register(MatcherOperator.exactTypeMatchOperator(type, registeredArbitraryBuilder));
	}

	public FixtureMonkeyBuilder registerAssignableType(
		Class type,
		Function> registeredArbitraryBuilder
	) {
		return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder));
	}

	public FixtureMonkeyBuilder register(
		MatcherOperator>> registeredArbitraryBuilder
	) {
		this.registeredArbitraryBuilders.add(registeredArbitraryBuilder);
		return this;
	}

	public FixtureMonkeyBuilder registerGroup(Class... arbitraryBuilderGroups) {
		for (Class arbitraryBuilderGroup : arbitraryBuilderGroups) {
			Method[] methods = arbitraryBuilderGroup.getMethods();
			for (Method method : methods) {
				int paramCount = method.getParameterCount();
				Class returnType = method.getReturnType();
				if (paramCount != 1 || !ArbitraryBuilder.class.isAssignableFrom(returnType)) {
					continue;
				}
				try {
					Class actualType = Types.getActualType(
						Types.getGenericsTypes(method.getAnnotatedReturnType()).get(0)
					);
					Object noArgsInstance = arbitraryBuilderGroup.getDeclaredConstructor().newInstance();
					Function> registerArbitraryBuilder =
						(fixtureMonkey) -> {
							try {
								return (ArbitraryBuilder)method.invoke(noArgsInstance, fixtureMonkey);
							} catch (IllegalAccessException | InvocationTargetException ex) {
								ex.printStackTrace();
								throw new RuntimeException(ex);
							}
						};
					this.register(actualType, registerArbitraryBuilder);
				} catch (Exception ex) {
					// ignored
				}
			}
		}
		return this;
	}

	public FixtureMonkeyBuilder registerGroup(ArbitraryBuilderGroup... arbitraryBuilderGroups) {
		for (ArbitraryBuilderGroup arbitraryBuilderGroup : arbitraryBuilderGroups) {
			List> candidates = arbitraryBuilderGroup.generateCandidateList()
				.getCandidates();

			for (ArbitraryBuilderCandidate candidate : candidates) {
				this.register(
					candidate.getClassType(),
					candidate.getArbitraryBuilderRegisterer()
				);
			}
		}
		return this;
	}

	public FixtureMonkeyBuilder plugin(Plugin plugin) {
		if (plugin instanceof InterfacePlugin) { // TODO: Added for backward compatibility. It will be removed in 1.1.0
			this.defaultInterfacePlugin = (InterfacePlugin)plugin;
			return this;
		}

		fixtureMonkeyOptionsBuilder.plugin(plugin);
		return this;
	}

	public FixtureMonkeyBuilder defaultDecomposedContainerValueFactory(
		DecomposedContainerValueFactory defaultDecomposedContainerValueFactory
	) {
		fixtureMonkeyOptionsBuilder.decomposedContainerValueFactory(defaultDecomposedContainerValueFactory);
		return this;
	}

	public FixtureMonkeyBuilder addDecomposedContainerValueFactory(
		Class type,
		DecomposedContainerValueFactory additionalDecomposedContainerValueFactory
	) {
		fixtureMonkeyOptionsBuilder.addDecomposedContainerValueFactory(type, additionalDecomposedContainerValueFactory);
		return this;
	}

	public FixtureMonkeyBuilder pushContainerIntrospector(ArbitraryIntrospector containerIntrospector) {
		this.fixtureMonkeyOptionsBuilder.containerIntrospector(it ->
			new MatchArbitraryIntrospector(
				Arrays.asList(
					containerIntrospector,
					it
				)
			)
		);
		return this;
	}

	public FixtureMonkeyBuilder addContainerType(
		Class type,
		ContainerPropertyGenerator containerObjectPropertyGenerator,
		ArbitraryIntrospector containerArbitraryIntrospector,
		DecomposedContainerValueFactory decomposedContainerValueFactory
	) {
		this.pushAssignableTypeContainerPropertyGenerator(type, containerObjectPropertyGenerator);
		this.pushContainerIntrospector(containerArbitraryIntrospector);
		this.fixtureMonkeyOptionsBuilder.addDecomposedContainerValueFactory(type, decomposedContainerValueFactory);
		return this;
	}

	public FixtureMonkeyBuilder defaultPropertyGenerator(PropertyGenerator propertyGenerator) {
		this.fixtureMonkeyOptionsBuilder.defaultPropertyGenerator(propertyGenerator);
		return this;
	}

	public FixtureMonkeyBuilder defaultNotNull(boolean defaultNotNull) {
		this.fixtureMonkeyOptionsBuilder.defaultNotNull(defaultNotNull);
		return this;
	}

	public FixtureMonkeyBuilder nullableContainer(boolean nullableContainer) {
		this.fixtureMonkeyOptionsBuilder.nullableContainer(nullableContainer);
		return this;
	}

	public FixtureMonkeyBuilder nullableElement(boolean nullableElement) {
		this.fixtureMonkeyOptionsBuilder.nullableElement(nullableElement);
		return this;
	}

	/**
	 * It is deprecated. Please use {@link InterfacePlugin#interfaceImplements(Matcher, List)} instead.
	 */
	@Deprecated
	public  FixtureMonkeyBuilder interfaceImplements(
		Matcher matcher,
		List> implementations
	) {
		this.pushObjectPropertyGenerator(
			new MatcherOperator<>(matcher, new InterfaceObjectPropertyGenerator<>(implementations))
		);
		return this;
	}

	/**
	 * It is deprecated. Please use {@link InterfacePlugin#interfaceImplements(Class, List)} instead.
	 */
	@Deprecated
	public  FixtureMonkeyBuilder interfaceImplements(
		Class interfaceClass,
		List> implementations
	) {
		return this.interfaceImplements(new ExactTypeMatcher(interfaceClass), implementations);
	}

	public FixtureMonkeyBuilder useExpressionStrictMode() {
		this.monkeyExpressionFactory = expression ->
			() -> new ApplyStrictModeResolver(new ArbitraryExpressionFactory().from(expression).toNodeResolver());
		return this;
	}

	public FixtureMonkeyBuilder defaultArbitraryGenerator(
		UnaryOperator arbitraryGeneratorUnaryOperator
	) {
		this.fixtureMonkeyOptionsBuilder.defaultArbitraryGenerator(arbitraryGeneratorUnaryOperator);
		return this;
	}

	public FixtureMonkeyBuilder generateMaxTries(int generateMaxTries) {
		fixtureMonkeyOptionsBuilder.generateMaxTries(generateMaxTries);
		return this;
	}

	public FixtureMonkeyBuilder generateUniqueMaxTries(int generateUniqueMaxTries) {
		fixtureMonkeyOptionsBuilder.generateUniqueMaxTries(generateUniqueMaxTries);
		return this;
	}

	public FixtureMonkeyBuilder javaConstraintGenerator(JavaConstraintGenerator javaConstraintGenerator) {
		fixtureMonkeyOptionsBuilder.javaConstraintGenerator(javaConstraintGenerator);
		return this;
	}

	public FixtureMonkeyBuilder pushJavaConstraintGeneratorCustomizer(
		UnaryOperator javaConstraintGeneratorCustomizer
	) {
		fixtureMonkeyOptionsBuilder.insertFirstJavaConstraintGeneratorCustomizer(javaConstraintGeneratorCustomizer);
		return this;
	}

	/**
	 * It is deprecated. Please use {@code @Seed} in fixture-monkey-junit-jupiter module.
	 */
	@Deprecated
	public FixtureMonkeyBuilder seed(long seed) {
		this.seed = seed;
		return this;
	}

	/**
	 * Use {@link InterfacePlugin#interfaceImplements(Class, List)}
	 * or {@link InterfacePlugin#abstractClassExtends(Class, List)} instead.
	 */
	@Deprecated
	public FixtureMonkeyBuilder pushExactTypePropertyCandidateResolver(
		Class type,
		CandidateConcretePropertyResolver candidateConcretePropertyResolver
	) {
		fixtureMonkeyOptionsBuilder.insertFirstCandidateConcretePropertyResolvers(
			new MatcherOperator<>(
				new ExactTypeMatcher(type),
				candidateConcretePropertyResolver
			)
		);
		return this;
	}

	public FixtureMonkey build() {
		defaultInterfacePlugin.accept(fixtureMonkeyOptionsBuilder);
		FixtureMonkeyOptions fixtureMonkeyOptions = fixtureMonkeyOptionsBuilder.build();
		ArbitraryTraverser traverser = new ArbitraryTraverser(fixtureMonkeyOptions);
		MonkeyManipulatorFactory monkeyManipulatorFactory = new MonkeyManipulatorFactory(
			new AtomicInteger(),
			monkeyExpressionFactory,
			traverser,
			fixtureMonkeyOptions.getDecomposedContainerValueFactory()
		);

		MonkeyContext monkeyContext = monkeyContextBuilder.build();
		Randoms.create(String.valueOf(seed));
		return new FixtureMonkey(
			fixtureMonkeyOptions,
			traverser,
			manipulatorOptimizer,
			monkeyContext,
			registeredArbitraryBuilders,
			monkeyManipulatorFactory
		);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy