class ModelValidator {

    public List<ValidationError> getCreateValidationErrors(Object create, FieldData fieldData) {
        if (create == null) {
            return ImmutableList.of(ValidationError.of("", "create is null"));
        }

        ImmutableList.Builder<ValidationError> errors = ImmutableList.builder();
        Map<String,Object> createMap = mapper.convertValue(create, new TypeReference<Map<String,Object>>() { });
        Set<String> createFields = createMap.keySet();
        for (String fieldName : createFields) {
            Field field = fieldData.getMap().get(fieldName);
            if (field == null) {
                errors.add(ValidationError.of(fieldName, "does not exist"));
            } else if (!field.isCreatable() && !field.isUpdatable()) {
                errors.add(ValidationError.of(fieldName, "is not creatable"));
            }
        }
        for (Field field : fieldData.getMap().values()) {
            Object value = createMap.get(field.getName());
            if (!field.isOptional() && (field.isCreatable() || field.isUpdatable()) && (value == null || "".equals(value))) {
                errors.add(ValidationError.of(field.getName(), "is required"));
            }
        }
        Set<ConstraintViolation<Object>> violations = validator.validate(create, Default.class);
        Set<String> errorFields = new HashSet<>();
        for (ConstraintViolation<?> cv : violations) {
            String field = cv.getPropertyPath().toString();
            if (createFields.contains(field) && !errorFields.contains(field)) {
                errors.add(ValidationError.of(field, cv.getMessage()));
                errorFields.add(field);
            }
        }
        return errors.build();
    }

}

class ModelValidatorTest {

    private ModelValidator validator;
    private FieldData fieldData;

    @Test
    public void testGetCreateValidationErrors() throws Exception {

        List<ValidationError> errors = validator.getCreateValidationErrors(null, fieldData);
        assertEquals(1, errors.size());

        TestValidateModel model = TestValidateModel.builder().build();
        errors = validator.getCreateValidationErrors(model, fieldData);
        assertEquals(2, errors.size());
        assertEquals("is required", errors.get(0).getMessage());
        assertEquals("is required", errors.get(1).getMessage());

        model = TestValidateModel.builder().email("invalid").build();
        errors = validator.getCreateValidationErrors(model, fieldData);
        assertEquals(2, errors.size());
        assertEquals("is required", errors.get(0).getMessage());
        assertEquals("must be a well-formed email address", errors.get(1).getMessage());

        model = TestValidateModel.builder().email("example@example.com").type("type").loginAt(Instant.now()).build();
        errors = validator.getCreateValidationErrors(model, fieldData);
        assertEquals(1, errors.size());
        assertEquals("loginAt", errors.get(0).getField());
        assertEquals("is not creatable", errors.get(0).getMessage());
    }

}