
com.github.rschmitt.dynamicobject.Validation Maven / Gradle / Ivy
package com.github.rschmitt.dynamicobject;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
class Validation {
@SuppressWarnings("unchecked")
static void validateCollection(Collection> val, Type genericReturnType) {
if (val == null) return;
Class> baseCollectionType = getRawType(genericReturnType);
if (!baseCollectionType.isAssignableFrom(val.getClass()))
throw new IllegalStateException(format("Wrong collection type: expected %s, got %s",
baseCollectionType.getSimpleName(), val.getClass().getSimpleName()));
if (genericReturnType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
List typeArgs = Arrays.asList(parameterizedType.getActualTypeArguments());
assert typeArgs.size() == 1;
Type typeArg = typeArgs.get(0);
checkTypeVariable(typeArg);
val.forEach(element -> checkElement(typeArg, element));
}
}
private static void checkTypeVariable(Type typeArg) {
if (typeArg instanceof WildcardType)
throw new UnsupportedOperationException("Wildcard return types are not supported");
else if (typeArg instanceof ParameterizedType)
return;
else if (typeArg instanceof Class)
return;
else
throw new UnsupportedOperationException("Unknown generic type argument type: " + typeArg.getClass().getCanonicalName());
}
private static void checkElement(Type elementType, Object element) {
if (elementType instanceof Class)
checkAtomicElement((Class>) elementType, element);
else
checkNestedElement(elementType, element);
}
private static void checkAtomicElement(Class> elementType, Object element) {
if (element != null) {
Class> actualType = element.getClass();
Class> expectedType = elementType;
if (!expectedType.isAssignableFrom(actualType))
throw new IllegalStateException(format("Expected collection element of type %s, got %s",
expectedType.getCanonicalName(),
actualType.getCanonicalName()));
if (element instanceof DynamicObject)
((DynamicObject) element).validate();
}
}
private static void checkNestedElement(Type elementType, Object element) {
Class> rawType = getRawType(elementType);
if (List.class.isAssignableFrom(rawType) || Set.class.isAssignableFrom(rawType))
validateCollection((Collection>) element, elementType);
else if (Map.class.isAssignableFrom(rawType))
validateMap((Map, ?>) element, elementType);
else
throw new UnsupportedOperationException("Unsupported base type " + rawType.getCanonicalName());
}
static void validateMap(Map, ?> map, Type genericReturnType) {
if (map == null) return;
if (genericReturnType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
List typeArgs = Arrays.asList(parameterizedType.getActualTypeArguments());
assert typeArgs.size() == 2;
typeArgs.forEach(Validation::checkTypeVariable);
Type keyType = typeArgs.get(0);
Type valType = typeArgs.get(1);
map.keySet().forEach(k -> checkElement(keyType, k));
map.values().forEach(v -> checkElement(valType, v));
}
}
static String getValidationErrorMessage(Collection missingFields, Map> mismatchedFields) {
StringBuilder ret = new StringBuilder();
if (!missingFields.isEmpty()) {
ret.append("The following @Required fields were missing: ");
List fieldNames = missingFields.stream().map(Method::getName).collect(toList());
for (int i = 0; i < fieldNames.size(); i++) {
ret.append(fieldNames.get(i));
if (i != fieldNames.size() - 1)
ret.append(", ");
}
ret.append("\n");
}
if (!mismatchedFields.isEmpty()) {
ret.append("The following fields had the wrong type:\n");
for (Map.Entry> methodClassEntry : mismatchedFields.entrySet()) {
Method method = methodClassEntry.getKey();
String name = method.getName();
String expected = method.getReturnType().getSimpleName();
String actual = methodClassEntry.getValue().getSimpleName();
ret.append(format("\t%s (expected %s, got %s)%n", name, expected, actual));
}
}
return ret.toString();
}
private static Class> getRawType(Type genericType) {
if (genericType instanceof Class)
return (Class>) genericType;
else if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
return (Class>) parameterizedType.getRawType();
} else
throw new UnsupportedOperationException();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy