@Override
    public boolean isValid(final Collection<String> values,
                           final ConstraintValidatorContext context) {
        boolean result = true;

        if (values != null && !values.isEmpty()) {

            final Set<String> invalidValues = new HashSet<>(values);
            invalidValues.removeAll(allowedValues);

            if (!invalidValues.isEmpty()) {
                // We want the exception details in the message so bin the default constraint
                // violation and make a new one.
                final String plural = invalidValues.size() > 1
                        ? "s"
                        : "";
                final String msg = "Set contains invalid value"
                        + plural
                        + " ["
                        + String.join(",", invalidValues) + "]";
                context.disableDefaultConstraintViolation();
                context
                        .buildConstraintViolationWithTemplate(msg)
                        .addConstraintViolation();
                result = false;
            }
        }
        return result;
    }
Implements the validation logic.
The state of {@code value} must not be altered.
<p>
This method can be accessed concurrently, thread-safety must be ensured
by the implementation.

@param values  object to validate
@param context context in which the constraint is evaluated
@return {@code false} if {@code value} does not pass the constraint
stroom-util/src/main/java/stroom/util/validation/IsSubsetOfValidatorImpl.java