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

net.amygdalum.extensions.hamcrest.conventions.BuilderMatcher Maven / Gradle / Ivy

package net.amygdalum.extensions.hamcrest.conventions;

import static java.lang.Character.toLowerCase;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;

import net.amygdalum.extensions.hamcrest.util.NameMapper;

public class BuilderMatcher extends TypeSafeDiagnosingMatcher {

	private Class builder;
	private boolean complete;
	private NameMapper resolver;

	public BuilderMatcher(Class builder) {
		this(builder, new DefaultNameMapper(), false);
	}

	public BuilderMatcher(Class builder, boolean complete) {
		this(builder, new DefaultNameMapper(), complete);
	}

	public BuilderMatcher(Class builder, NameMapper resolver, boolean complete) {
		this.builder = builder;
		this.resolver = resolver;
		this.complete = complete;
	}

	@Override
	public void describeTo(Description description) {
	}

	@Override
	protected boolean matchesSafely(O item, Description mismatchDescription) {
		try {
			B builderInstance = builder.newInstance();
			Map properties = new LinkedHashMap<>();
			for (Method method : builder.getMethods()) {
				if (method.getParameterTypes().length == 1) {
					String property = resolver.map(method.getName());
					if (property != null) {
						Object value = getValueFor(item, property);
						method.invoke(builderInstance, value);
						properties.put(property, value);
					}
				}
			}
			Method build = builder.getMethod(resolver.unMap("build"));
			Object result = build.invoke(builderInstance);
			if (complete) {
				return item.equals(result);
			} else {
				for (Map.Entry entry : properties.entrySet()) {
					String name = entry.getKey();
					Object expected = entry.getValue();
					Object found = getValueFor(result, name);
					if (expected == null) {
						return found == null;
					} else if (!expected.equals(found)) {
						return false;
					}
				}
				return true;
			}
		} catch (ReflectiveOperationException e) {
			return false;
		}
	}

	private Object getValueFor(Object item, String property) throws ReflectiveOperationException {
		try {
			Field field = item.getClass().getDeclaredField(property);
			field.setAccessible(true);
			return field.get(item);
		} catch (NoSuchFieldException e) {
			String getterName = "get" + Character.toUpperCase(property.charAt(0)) + property.substring(1);
			Method getter = item.getClass().getDeclaredMethod(getterName);
			return getter.invoke(item);
		}
	}

	private static class DefaultNameMapper implements NameMapper {

		@Override
		public String map(String name) {
			if (name.startsWith("with")) {
				return toLowerCase(name.charAt(4)) + name.substring(5);
			} else {
				return null;
			}
		}

		@Override
		public String unMap(String name) {
			if (name.equals("build")) {
				return "build";
			} else {
				return null;
			}
		}

	}

	public static  BuilderMatcher buildFrom(Class builder) {
		return new BuilderMatcher<>(builder, true);
	}

	public static  BuilderMatcher partiallyBuildFrom(Class builder) {
		return new BuilderMatcher<>(builder);
	}
}