Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.reprezen.kaizen.oasparser.val.ValidatorBase Maven / Gradle / Ivy
package com.reprezen.kaizen.oasparser.val;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.BadEmail;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.BadPattern;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.BadUrl;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.DuplicateValue;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.EmptyList;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.MissingField;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.NumberConstraint;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.PatternMatchFail;
import static com.reprezen.kaizen.oasparser.val.BaseValidationMessages.WrongTypeJson;
import static com.reprezen.kaizen.oasparser.val.msg.Messages.msg;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BigIntegerNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.NumericNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ShortNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.reprezen.jsonoverlay.BooleanOverlay;
import com.reprezen.jsonoverlay.IntegerOverlay;
import com.reprezen.jsonoverlay.JsonOverlay;
import com.reprezen.jsonoverlay.ListOverlay;
import com.reprezen.jsonoverlay.MapOverlay;
import com.reprezen.jsonoverlay.NumberOverlay;
import com.reprezen.jsonoverlay.ObjectOverlay;
import com.reprezen.jsonoverlay.Overlay;
import com.reprezen.jsonoverlay.PrimitiveOverlay;
import com.reprezen.jsonoverlay.PropertiesOverlay;
import com.reprezen.jsonoverlay.StringOverlay;
public abstract class ValidatorBase implements Validator {
protected Overlay value;
protected ValidationResults results;
@Override
public final void validate(Overlay value) {
this.value = value;
this.results = ValidationContext.getValidationResults();
checkJsonType(value, getAllowedJsonTypes(value), results);
runValidations();
}
public abstract void runValidations();
public Overlay validateBooleanField(String name, boolean required) {
return validateField(name, required, Boolean.class, null);
}
public Overlay validateStringField(String name, boolean required) {
return validateStringField(name, required, (Pattern) null);
}
public Overlay validateStringField(String name, boolean required, String pattern) {
return validateStringField(name, required, Pattern.compile(pattern));
}
@SafeVarargs
public final Overlay validateStringField(String name, boolean required, Pattern pattern,
Consumer>... otherChecks) {
Overlay field = validateField(name, required, String.class, null);
checkMissing(field, required);
if (field != null && field.isPresent()) {
if (pattern != null) {
checkPattern(field, pattern);
}
for (Consumer> otherCheck : otherChecks) {
otherCheck.accept(field);
}
}
return field;
}
void checkPattern(Overlay field, Pattern pattern) {
if (!pattern.matcher(field.get()).matches()) {
results.addError(msg(PatternMatchFail, field.get(), pattern), field);
}
}
public Overlay validatePatternField(String name, boolean required) {
return validateStringField(name, required, null, this::checkRegex);
}
private void checkRegex(Overlay field) {
String regex = field.get();
try {
Pattern.compile(regex);
} catch (PatternSyntaxException e) {
results.addWarning(msg(BadPattern, regex), field);
}
}
public Overlay validateUrlField(String name, boolean required, boolean allowRelative, boolean allowVars) {
return validateUrlField(name, required, allowRelative, allowVars, (Pattern) null);
}
public Overlay validateUrlField(String name, boolean required, boolean allowRelative, boolean allowVars,
String pattern) {
return validateUrlField(name, required, allowRelative, allowVars, Pattern.compile(pattern));
}
public Overlay validateUrlField(String name, boolean required, boolean allowRelative, boolean allowVars,
Pattern pattern) {
return validateStringField(name, required, pattern, field -> checkUrl(field, allowRelative, allowVars));
}
private static String FAKE_SCHEME = "oasparser.fake.scheme";
private static URLStreamHandler fakeHandler = new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
};
private void checkUrl(Overlay overlay, boolean allowRelative, boolean allowVars) {
// TODO Q: Any help from spec in being able to validate URLs with vars? E.g is
// our treatment here valid? We assume vars can only appear where simple text
// can appear, so handling vars means relacing {.*} with "1" and testing for URL
// validity. We use a digit instead of a letter because it covers vars in the
// port, and elsewhere digits are always allowed where letters are.
String origUrl = overlay.get();
String url = origUrl;
boolean fake = false;
if (allowVars) {
url = url.replaceAll("\\{[^}]+\\}", "1");
if (url.startsWith("1:")) {
// "1" is not a valid scheme name, so we need to replace it with special scheme,
// for which we provide a do-nothing protocol handler implementation
url = FAKE_SCHEME + url.substring(1);
fake = true;
}
}
try {
new URL(null, url, fake ? fakeHandler : null);
} catch (MalformedURLException e) {
try {
URL context = new URL(null, FAKE_SCHEME + ":/", fakeHandler);
new URL(context, url);
if (!allowRelative) {
results.addError(msg(BaseValidationMessages.NoRelUrl, origUrl, e.toString()), overlay);
}
} catch (MalformedURLException e1) {
results.addError(msg(BadUrl, origUrl, e.toString()), overlay);
}
}
}
public Overlay validateEmailField(String name, boolean required) {
return validateEmailField(name, required, (Pattern) null);
}
public Overlay validateUrlField(String name, boolean required, String pattern) {
return validateEmailField(name, required, Pattern.compile(pattern));
}
public Overlay validateEmailField(String name, boolean required, Pattern pattern) {
return validateStringField(name, required, pattern, this::checkEmail);
}
private void checkEmail(Overlay overlay) {
String email = overlay.get();
try {
InternetAddress addr = new InternetAddress();
addr.setAddress(email);
addr.validate();
} catch (AddressException e) {
results.addError(msg(BadEmail, email, e.toString()), overlay);
}
}
public Overlay validatePositiveField(String name, boolean required) {
return validateNumericField(name, required, NumericUtils::isPositive, "be positive");
}
public Overlay validateNonNegativeField(String name, boolean required) {
return validateNumericField(name, required, NumericUtils::isNonNegative, "not be positive");
}
public Overlay validateNumericField(String name, boolean required, Function test,
String desc) {
Overlay field = validateField(name, required, Number.class, null);
checkMissing(field, required);
if (field != null && field.isPresent() && test != null) {
Number n = field.get();
if (!test.apply(n)) {
results.addError(msg(NumberConstraint, desc, n), field);
}
}
return field;
}
@SafeVarargs
public final Overlay validateField(String name, boolean required, Class fieldClass,
Validator validator, Consumer>... otherChecks) {
@SuppressWarnings("unchecked")
PropertiesOverlay propValue = (PropertiesOverlay) value.get();
Overlay field = Overlay.of(propValue, name, fieldClass);
checkJsonType(field, getAllowedJsonTypes(field), results);
checkMissing(field, required);
if (field != null && field.isPresent() && validator != null) {
validator.validate(field);
for (Consumer> otherCheck : otherChecks) {
otherCheck.accept(field);
}
}
return field;
}
public Overlay> validateListField(String name, boolean nonEmpty, boolean unique, Class itemClass,
Validator itemValidator) {
@SuppressWarnings("unchecked")
Overlay> list = (Overlay>) (Object) Overlay.of((PropertiesOverlay) value.get(), name,
List.class);
validateList(list, nonEmpty, unique, itemValidator);
return list;
}
private void validateList(Overlay> list, boolean nonEmpty, boolean unique, Validator itemValidator) {
new ListValidator(itemValidator).validate(list);
checkListNotEmpty(list, nonEmpty);
checkListUnique(list, unique);
}
private void checkListNotEmpty(Overlay> list, boolean nonEmpty) {
if (nonEmpty) {
ListOverlay listOverlay = Overlay.getListOverlay(list);
if (list != null && !list.isPresent()) {
if (nonEmpty && listOverlay.size() == 0) {
results.addError(msg(EmptyList), list);
}
}
}
}
private void checkListUnique(Overlay> list, boolean unique) {
if (unique) {
ListOverlay listOverlay = Overlay.getListOverlay(list);
Set seen = new HashSet<>();
for (int i = 0; i < listOverlay.size(); i++) {
X item = listOverlay.get(i);
if (seen.contains(item)) {
results.addError(msg(DuplicateValue, item, i), Overlay.of(listOverlay, i));
} else {
seen.add(item);
}
}
}
}
public Overlay> validateMapField(String name, boolean nonEmpty, boolean unique,
Class valueClass, Validator valueValidator) {
@SuppressWarnings("unchecked")
Overlay> map = (Overlay>) (Object) Overlay.of((PropertiesOverlay) value.get(),
name, Map.class);
validateMap(map, nonEmpty, unique, valueValidator);
return map;
}
private void validateMap(Overlay> map, boolean nonEmpty, boolean unique,
Validator valueValidator) {
new MapValidator(valueValidator).validate(map);
checkMapNotEmpty(map, nonEmpty);
checkMapUnique(map, unique);
}
private void checkMapNotEmpty(Overlay> list, boolean nonEmpty) {
if (nonEmpty) {
MapOverlay mapOverlay = Overlay.getMapOverlay(list);
if (list != null && !list.isPresent()) {
if (nonEmpty && mapOverlay.size() == 0) {
results.addError(msg(EmptyList), list);
}
}
}
}
private void checkMapUnique(Overlay> map, boolean unique) {
if (unique) {
MapOverlay mapOverlay = Overlay.getMapOverlay(map);
Set seen = new HashSet<>();
for (String key : mapOverlay.keySet()) {
X value = mapOverlay.get(key);
if (seen.contains(value)) {
results.addError(msg(DuplicateValue, value, key), Overlay.of(mapOverlay, key));
} else {
seen.add(value);
}
}
}
}
void checkMissing(Overlay> field, boolean required) {
if (required && (field == null || !field.isPresent())) {
results.addError(msg(MissingField, field.getPathInParent()), value);
}
}
public Overlay> validateExtensions(Map extensions) {
return validateExtensions(extensions, null);
}
public Overlay> validateExtensions(Map extensions, String crumb) {
Overlay> mapOverlay = Overlay.of(extensions);
validateMap(mapOverlay, false, false, null);
return mapOverlay;
}
public Overlay validateFormatField(String name, boolean required, String type) {
Overlay field = validateStringField(name, required);
if (field != null && field.isPresent()) {
String normalType = null;
switch (field.get()) {
case "int32":
case "int64":
normalType = "integer";
break;
case "float":
case "double":
normalType = "number";
break;
case "byte":
case "binary":
case "date":
case "date-time":
case "password":
normalType = "string";
}
if (normalType != null) {
if (type == null || !type.equals(normalType)) {
results.addWarning(msg(BaseValidationMessages.WrongTypeFormat, field, type, normalType), field);
}
}
}
return field;
}
public void checkDefault(Overlay> overlay, String type) {
if (overlay != null && overlay.isPresent() && type != null) {
Object defaultValue = overlay.get();
boolean ok = false;
switch (type) {
case "string":
ok = defaultValue instanceof String;
break;
case "number":
ok = NumericUtils.isNumeric(defaultValue);
break;
case "integer":
ok = NumericUtils.isIntegral(defaultValue);
break;
case "boolean":
ok = defaultValue instanceof Boolean;
break;
case "object":
ok = defaultValue instanceof Map, ?>;
break;
case "array":
ok = defaultValue instanceof List>;
break;
}
if (!ok) {
results.addError(msg(BaseValidationMessages.WrongTypeValue, type, defaultValue), overlay);
}
}
}
public void checkJsonType(Overlay> value, Collection> allowedJsonTypes,
ValidationResults results) {
JsonNode json = value.getParsedJson();
if (json != null && !json.isMissingNode()) {
for (Class extends JsonNode> type : allowedJsonTypes) {
if (type.isAssignableFrom(json.getClass())) {
return;
}
}
String allowed = allowedJsonTypes.stream().map(type -> getJsonValueType(type))
.collect(Collectors.joining(", "));
results.addError(msg(WrongTypeJson, getJsonValueType(json.getClass()), allowed), value);
}
}
private String getJsonValueType(Class extends JsonNode> node) {
String type = node.getSimpleName();
return type.endsWith("Node") ? type.substring(0, type.length() - 4) : type;
}
protected static Map, List>> allowedJsonTypes = null;
protected Collection> getAllowedJsonTypes(Overlay> value) {
if (allowedJsonTypes == null) {
createAllowedJsonTypes();
}
JsonOverlay> overlay = value.getOverlay();
return allowedJsonTypes
.get(overlay instanceof PropertiesOverlay ? PropertiesOverlay.class : overlay.getClass());
}
private static void createAllowedJsonTypes() {
Map, List>> types = new HashMap<>();
types.put(StringOverlay.class, Arrays.asList(TextNode.class));
types.put(BooleanOverlay.class, Arrays.asList(BooleanNode.class));
types.put(IntegerOverlay.class, Arrays.asList(IntNode.class, ShortNode.class, BigIntegerNode.class));
types.put(NumberOverlay.class, Arrays.asList(NumericNode.class));
types.put(PrimitiveOverlay.class, Arrays.asList(TextNode.class, NumericNode.class, BooleanNode.class));
types.put(ObjectOverlay.class, Arrays.asList(JsonNode.class));
types.put(MapOverlay.class, Arrays.asList(ObjectNode.class));
types.put(ListOverlay.class, Arrays.asList(ArrayNode.class));
types.put(PropertiesOverlay.class, Arrays.asList(ObjectNode.class));
allowedJsonTypes = types;
}
}