EnumInitHelper.java
/*
* Copyright © 2015 The Authors
*
* https://www.gnu.org/licenses/lgpl-3.0-standalone.html
*/
package org.interstellarocean.terraforming.util;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.EnumSet.allOf;
import static java.util.EnumSet.complementOf;
import static java.util.EnumSet.copyOf;
import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import org.interstellarocean.terraforming.util.EnumInitUtil.SafeMapFrom;
import org.interstellarocean.terraforming.util.EnumInitUtil.SafeMapStore;
/**
* This class provides testable implementation for the static utility facade class.
*
* @see EnumInitUtil
*
* @author Dariusz Wakuliński
*/
class EnumInitHelper {
Supplier<AssertionError> nullMappingError(Enum<?> element) {
return () -> {
return new AssertionError(format("Null mapping for %s", element));
};
}
<E extends Enum<E>, M> EnumMap<E, M> assertAllMapped(EnumMap<E, M> mappings) {
ofNullable(mappings)
.orElseThrow(nullArgument("mappings"));
Set<E> keySet = mappings.keySet();
assertNonEmpty(keySet, "(see stack trace for type)");
Class<E> keyType = keySet.iterator().next().getDeclaringClass();
if (keySet.containsAll(allOf(keyType))) {
return mappings;
}
throw new AssertionError(format("Missing mapping(s) for %s", complementOf(copyOf(keySet))));
}
<E extends Enum<E>> SafeMapFrom<E> safeMap(E element) {
ofNullable(element)
.orElseThrow(nullArgument("element"));
return new SafeMapFrom<E>() {
@Override
public <M> SafeMapStore<E, M> from(Collection<M> mappings) {
ofNullable(mappings)
.orElseThrow(nullMappingError(element));
return new SafeMapStore<E, M>() {
@Override
public E withStore(Map<M, E> map) {
ofNullable(map)
.orElseThrow(nullMappingError(element));
return safeMap(element, mappings, map);
}
@Override
public SafeMapStore<E, M> includeSelf(Function<E, M> transformation) {
ofNullable(transformation)
.orElseThrow(nullArgument("transformation"));
M self = transformation.apply(element);
ofNullable(self)
.orElseThrow(nullMappingError(element));
return from(join(self, mappings));
}
private Collection<M> join(M self, Collection<M> mappings) {
return concat(of(self), mappings.stream()).collect(toList());
}
};
}
@Override
@SuppressWarnings("unchecked") // Safe, read only. @SafeVarargs requires final that causes mocking problems for tests
public <M> SafeMapStore<E, M> from(M... mappings) {
ofNullable(mappings)
.orElseThrow(nullMappingError(element)); // only for explicit null[], wicked!
return from(asList(mappings));
}
};
}
private Supplier<AssertionError> nullArgument(String argumentName) {
return () -> {
return new AssertionError(format("Invalid use: null %s argument", argumentName));
};
}
private <E extends Enum<E>, M> E safeMap(E element, Collection<M> mappings, Map<M, E> map) {
assertNonEmpty(mappings, element);
mappings.forEach(mapping -> {
E former = map.put(
ofNullable(mapping)
.orElseThrow(nullMappingError(element)),
element);
assertUniqueMapped(mapping, element, former);
});
return element;
}
private void assertNonEmpty(Collection<?> mappings, Object element) {
if (!mappings.isEmpty()) {
assertNonEmptyRequirementNotWaived(element, mappings);
return;
}
if (getNonEmptyRequirementWaiver(element).isPresent()) {
return;
}
throw new AssertionError(format("Missing all mappings for %s", element));
}
private void assertNonEmptyRequirementNotWaived(Object element, Collection<?> mappings) {
if (!getNonEmptyRequirementWaiver(element).isPresent()) {
return;
}
throw new AssertionError(format("Found non-empty mappings %2$s for @Unmapped %s", element, mappings));
}
private Optional<Unmapped> getNonEmptyRequirementWaiver(Object element) {
if (!(element instanceof Enum)) {
return empty();
}
Unmapped waiverAnnotation = tryToGetField((Enum<?>) element).getAnnotation(Unmapped.class);
return ofNullable(waiverAnnotation);
}
private Field tryToGetField(Enum<?> element) {
try {
return element.getDeclaringClass().getDeclaredField(element.name());
} catch (NoSuchFieldException | SecurityException exception) {
throw new AssertionError(format("Error examining element: %s", element), exception); // impossible
}
}
private void assertUniqueMapped(Object mapping, Enum<?> actual, Enum<?> former) {
if (former == null) {
return;
}
throw new AssertionError(format("Duplicate mapping for %s->%s, was: %s", mapping, actual, former));
}
}