OperationFactories.java

package net.bmahe.genetics4j.gp;

import java.util.Arrays;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import org.apache.commons.lang3.Validate;

import net.bmahe.genetics4j.gp.math.ImmutableCoefficientOperation;
import net.bmahe.genetics4j.gp.math.ImmutableCoefficientOperation.Builder;

public final class OperationFactories {

	private OperationFactories() {
	}

	@SuppressWarnings("rawtypes")
	public static OperationFactory ofOperationSupplier(final Class[] acceptedTypes, final Class returnedType,
			final Supplier<Operation> buildSupplier) {
		Objects.requireNonNull(acceptedTypes);
		Objects.requireNonNull(returnedType);
		Objects.requireNonNull(buildSupplier);

		return new OperationFactory() {

			@Override
			public Class returnedType() {
				return returnedType;
			}

			@Override
			public Class[] acceptedTypes() {
				return acceptedTypes;
			}

			@Override
			public Operation build(final InputSpec inputSpec) {
				return buildSupplier.get();
			}
		};
	}

	@SuppressWarnings("rawtypes")
	public static OperationFactory of(final Class[] acceptedTypes, final Class returnedType,
			final Function<InputSpec, Operation> operationBuilder) {
		Objects.requireNonNull(acceptedTypes);
		Objects.requireNonNull(returnedType);
		Objects.requireNonNull(operationBuilder);

		return new OperationFactory() {
			@Override
			public Class[] acceptedTypes() {
				return acceptedTypes;
			}

			@Override
			public Class returnedType() {
				return returnedType;
			}

			@Override
			public Operation build(final InputSpec inputSpec) {
				return operationBuilder.apply(inputSpec);
			}
		};
	}

	@SuppressWarnings("rawtypes")
	public static OperationFactory of(final String name, final Class[] acceptedTypes, final Class returnedType,
			final BiFunction<Object[], Object[], Object> compute) {
		Validate.notBlank(name);
		Objects.requireNonNull(acceptedTypes);
		Objects.requireNonNull(returnedType);
		Objects.requireNonNull(compute);

		return new OperationFactory() {

			@Override
			public Class[] acceptedTypes() {
				return acceptedTypes;
			}

			@Override
			public Class returnedType() {
				return returnedType;
			}

			@Override
			public Operation build(final InputSpec inputSpec) {
				return ImmutableOperation.of(name, Arrays.asList(acceptedTypes), returnedType, compute);
			}
		};
	}

	@SuppressWarnings("rawtypes")
	public static OperationFactory ofCoefficient(final String name, final Class returnedType, final Object value) {
		Validate.notBlank(name);
		Objects.requireNonNull(returnedType);
		Objects.requireNonNull(value);

		return new OperationFactory() {

			@Override
			public Class[] acceptedTypes() {
				return new Class[] {};
			}

			@Override
			public Class returnedType() {
				return returnedType;
			}

			@Override
			public Operation build(final InputSpec inputSpec) {
				final Builder<Object> operationBuilder = ImmutableCoefficientOperation.builder();

				operationBuilder.name(name)
						.value(value)
						.returnedType(returnedType)
						.prettyName(name + "[" + value + "]");

				return operationBuilder.build();
			}
		};
	}

	public static <T> OperationFactory ofTerminal(final String name, final Class<T> returnedType,
			final Supplier<T> compute) {
		Validate.notBlank(name);
		Objects.requireNonNull(returnedType);
		Objects.requireNonNull(compute);

		return of(name, new Class[] {}, returnedType, (input, parameter) -> {
			return compute.get();
		});
	}

	public static <T, U> OperationFactory ofUnary(final String name, final Class<T> acceptedType,
			final Class<U> returnedType, final Function<T, U> compute) {
		Validate.notBlank(name);
		Objects.requireNonNull(acceptedType);
		Objects.requireNonNull(returnedType);
		Objects.requireNonNull(compute);

		return of(name, new Class[] { acceptedType }, returnedType, (input, parameters) -> {
			Objects.requireNonNull(parameters);

			final Object parameter1 = parameters[0];
			Objects.requireNonNull(parameter1);
			Validate.isInstanceOf(acceptedType, parameter1);

			@SuppressWarnings("unchecked")
			final T operand = (T) parameter1;

			return compute.apply(operand);
		});
	}

	@SuppressWarnings("unchecked")
	public static <T, U, V> OperationFactory ofBinary(final String name, final Class<T> acceptedType1,
			final Class<U> acceptedType2, final Class<V> returnedType, final BiFunction<T, U, V> compute) {
		Validate.notBlank(name);
		Objects.requireNonNull(acceptedType1);
		Objects.requireNonNull(acceptedType2);
		Objects.requireNonNull(returnedType);
		Objects.requireNonNull(compute);

		return of(name, new Class[] { acceptedType1, acceptedType2 }, returnedType, (input, parameters) -> {
			Objects.requireNonNull(parameters);

			final Object parameter1 = parameters[0];
			Objects.requireNonNull(parameter1);
			Validate.isInstanceOf(acceptedType1, parameter1);
			final T operand1 = (T) parameter1;

			final Object parameter2 = parameters[1];
			Objects.requireNonNull(parameter2);
			Validate.isInstanceOf(acceptedType2, parameter2);
			final U operand2 = (U) parameter2;

			return compute.apply(operand1, operand2);
		});
	}
}