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

net.amygdalum.extensions.hamcrest.collections.MapMatcher Maven / Gradle / Ivy

package net.amygdalum.extensions.hamcrest.collections;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNull.nullValue;

import java.util.AbstractMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.hamcrest.TypeSafeMatcher;
import org.hamcrest.core.IsNull;

public class MapMatcher extends TypeSafeMatcher>{
	
	private Class key;
	private Class value;
	private Map,Matcher> entries;
	
	public MapMatcher(Class key, Class value) {
		this.key = key;
		this.value = value;
		this.entries = new LinkedHashMap<>();
	}

	public MapMatcher entry(K key, V value) {
		return entry(matchKey(key), matchValue(value));
	}

	public MapMatcher entry(Matcher key, V value) {
		return entry(key, matchValue(value));
	}

	public MapMatcher entry(K key, Matcher value) {
		return entry(matchKey(key), value);
	}

	public MapMatcher entry(Matcher key, Matcher value) {
		entries.put(key, value);
		return this;
	}
	
	@SuppressWarnings("unchecked")
	private Matcher matchKey(K element) {
		if (element == null) {
			return nullValue(key);
		} else if (element instanceof Matcher) {
			return (Matcher) element;
		} else {
			return equalTo(element);
		}
	}

	@SuppressWarnings("unchecked")
	private Matcher matchValue(V element) {
		if (element == null) {
			return nullValue(value);
		} else if (element instanceof Matcher) {
			return (Matcher) element;
		} else {
			return equalTo(element);
		}
	}
	
	@Override
	public void describeTo(Description description) {
		description.appendValue(entries);
	}
	
	@Override
	protected void describeMismatchSafely(Map item, Description mismatchDescription) {
		List, Matcher>> unmatched = new LinkedList<>(entries.entrySet());
		List> notfound = new LinkedList<>();
		
		for (Entry entry : item.entrySet()) {
		
			boolean success = tryMatch(unmatched, entry);
			if (!success) {
				notfound.add(new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue()));
			}
		}
		
		if (!unmatched.isEmpty()) {
			mismatchDescription.appendText("missing entries ").appendValue(toMap(unmatched));
		}
		if (!unmatched.isEmpty() && !notfound.isEmpty()) {
			mismatchDescription.appendText(", ");
		}
		if (!notfound.isEmpty()) {
			mismatchDescription.appendText("unmatched entries ").appendValue(toDescriptionMap(notfound));
		}
	}

	private Map, Matcher> toMap(List, Matcher>> entries) {
		Map, Matcher> map = new LinkedHashMap<>();
		for (Entry, Matcher> entry : entries) {
			map.put(entry.getKey(), entry.getValue());
		}
		return map;
	}

	private Map toDescriptionMap(List> entries) {
		Matcher keyMatcher = bestKeyMatcher();
		Matcher valueMatcher = bestValueMatcher();
		Map map = new LinkedHashMap<>();
		for (Entry entry : entries) {
			String key = descriptionOf(keyMatcher, entry.getKey());
			String value = descriptionOf(valueMatcher, entry.getValue());
			map.put(key, value);
		}
		return map;
	}

	private Matcher bestValueMatcher() {
		for (Matcher matcher : entries.values()) {
			if (matcher.getClass() != IsNull.class) {
				return matcher;
			}
		}
		return equalTo(null);
	}

	private Matcher bestKeyMatcher() {
		for (Matcher matcher : entries.keySet()) {
			if (matcher.getClass() != IsNull.class) {
				return matcher;
			}
		}
		return equalTo(null);
	}

	private  String descriptionOf(Matcher valueMatcher, T value) {
		StringDescription description = new StringDescription();
		valueMatcher.describeMismatch(value, description);
		return description.toString();
	}

	@Override
	protected boolean matchesSafely(Map item) {
		List, Matcher>> unmatched = new LinkedList<>(entries.entrySet());
		
		for (Entry entry : item.entrySet()) {
		
			boolean success = tryMatch(unmatched, entry);
			if (!success) {
				return false;
			}
		}
		
		return unmatched.isEmpty();
	}

	private boolean tryMatch(List, Matcher>> unmatched, Entry entry) {
		K key = entry.getKey();
		V value = entry.getValue();

		Iterator, Matcher>> matchers = unmatched.iterator();
		while (matchers.hasNext()) {
			Entry, Matcher> matcher = matchers.next();
			if (matcher.getKey().matches(key) && matcher.getValue().matches(value)) {
				matchers.remove();
				return true;
			}
		}
		return false;
	}

	public static  MapMatcher noEntries(Class key, Class value) {
		return new MapMatcher<>(key, value);
	}

	public static  MapMatcher containsEntries(Class key, Class value) {
		return new MapMatcher<>(key, value);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy