|
17 | 17 | import com.retailsvc.http.spec.schema.Schema; |
18 | 18 | import com.retailsvc.http.spec.schema.StringSchema; |
19 | 19 | import com.retailsvc.http.spec.schema.TypeName; |
| 20 | +import java.time.LocalDate; |
| 21 | +import java.time.OffsetDateTime; |
20 | 22 | import java.util.Objects; |
| 23 | +import java.util.UUID; |
21 | 24 | import java.util.function.Function; |
| 25 | +import java.util.regex.Pattern; |
22 | 26 |
|
23 | 27 | public final class DefaultValidator implements Validator { |
24 | 28 | private final Function<String, Schema> refResolver; |
@@ -56,23 +60,110 @@ private void validateBoolean(Object value, String pointer) { |
56 | 60 | } |
57 | 61 |
|
58 | 62 | private void validateString(Object value, StringSchema s, String pointer) { |
59 | | - throw new UnsupportedOperationException("E3 implements string"); |
| 63 | + require(value instanceof String, pointer, "type", "expected string"); |
| 64 | + String str = (String) value; |
| 65 | + if (s.minLength() != null && str.length() < s.minLength()) |
| 66 | + fail(pointer, "minLength", "string shorter than " + s.minLength(), str); |
| 67 | + if (s.maxLength() != null && str.length() > s.maxLength()) |
| 68 | + fail(pointer, "maxLength", "string longer than " + s.maxLength(), str); |
| 69 | + if (s.pattern() != null && !Pattern.compile(s.pattern()).matcher(str).matches()) |
| 70 | + fail(pointer, "pattern", "does not match pattern " + s.pattern(), str); |
| 71 | + if (s.enumValues() != null && !s.enumValues().contains(str)) |
| 72 | + fail(pointer, "enum", "value not in enum", str); |
| 73 | + if (s.format() != null) validateStringFormat(str, s.format(), pointer); |
| 74 | + } |
| 75 | + |
| 76 | + private void validateStringFormat(String str, String format, String pointer) { |
| 77 | + switch (format) { |
| 78 | + case "uuid" -> { |
| 79 | + try { |
| 80 | + UUID.fromString(str); |
| 81 | + } catch (IllegalArgumentException e) { |
| 82 | + fail(pointer, "format", "not a valid uuid", str); |
| 83 | + } |
| 84 | + } |
| 85 | + case "date" -> { |
| 86 | + try { |
| 87 | + LocalDate.parse(str); |
| 88 | + } catch (Exception e) { |
| 89 | + fail(pointer, "format", "not a valid date", str); |
| 90 | + } |
| 91 | + } |
| 92 | + case "date-time" -> { |
| 93 | + try { |
| 94 | + OffsetDateTime.parse(str); |
| 95 | + } catch (Exception e) { |
| 96 | + fail(pointer, "format", "not a valid date-time", str); |
| 97 | + } |
| 98 | + } |
| 99 | + default -> {} |
| 100 | + } |
60 | 101 | } |
61 | 102 |
|
62 | 103 | private void validateInteger(Object value, IntegerSchema s, String pointer) { |
63 | | - throw new UnsupportedOperationException("E3 implements integer"); |
| 104 | + long n; |
| 105 | + if (value instanceof Number num) n = num.longValue(); |
| 106 | + else if (value instanceof String str) { |
| 107 | + try { |
| 108 | + n = Long.parseLong(str); |
| 109 | + } catch (NumberFormatException e) { |
| 110 | + fail(pointer, "type", "expected integer", value); |
| 111 | + return; |
| 112 | + } |
| 113 | + } else { |
| 114 | + fail(pointer, "type", "expected integer", value); |
| 115 | + return; |
| 116 | + } |
| 117 | + |
| 118 | + if (s.minimum() != null && n < s.minimum()) |
| 119 | + fail(pointer, "minimum", "integer below minimum " + s.minimum(), n); |
| 120 | + if (s.maximum() != null && n > s.maximum()) |
| 121 | + fail(pointer, "maximum", "integer above maximum " + s.maximum(), n); |
| 122 | + if (s.exclusiveMinimum() != null && n <= s.exclusiveMinimum()) |
| 123 | + fail(pointer, "exclusiveMinimum", "integer not greater than " + s.exclusiveMinimum(), n); |
| 124 | + if (s.exclusiveMaximum() != null && n >= s.exclusiveMaximum()) |
| 125 | + fail(pointer, "exclusiveMaximum", "integer not less than " + s.exclusiveMaximum(), n); |
| 126 | + if (s.multipleOf() != null && n % s.multipleOf() != 0) |
| 127 | + fail(pointer, "multipleOf", "not a multiple of " + s.multipleOf(), n); |
64 | 128 | } |
65 | 129 |
|
66 | 130 | private void validateNumber(Object value, NumberSchema s, String pointer) { |
67 | | - throw new UnsupportedOperationException("E3 implements number"); |
| 131 | + double n; |
| 132 | + if (value instanceof Number num) n = num.doubleValue(); |
| 133 | + else if (value instanceof String str) { |
| 134 | + try { |
| 135 | + n = Double.parseDouble(str); |
| 136 | + } catch (NumberFormatException e) { |
| 137 | + fail(pointer, "type", "expected number", value); |
| 138 | + return; |
| 139 | + } |
| 140 | + } else { |
| 141 | + fail(pointer, "type", "expected number", value); |
| 142 | + return; |
| 143 | + } |
| 144 | + |
| 145 | + if (s.minimum() != null && n < s.minimum().doubleValue()) |
| 146 | + fail(pointer, "minimum", "number below minimum " + s.minimum(), n); |
| 147 | + if (s.maximum() != null && n > s.maximum().doubleValue()) |
| 148 | + fail(pointer, "maximum", "number above maximum " + s.maximum(), n); |
| 149 | + if (s.exclusiveMinimum() != null && n <= s.exclusiveMinimum().doubleValue()) |
| 150 | + fail(pointer, "exclusiveMinimum", "number not greater than " + s.exclusiveMinimum(), n); |
| 151 | + if (s.exclusiveMaximum() != null && n >= s.exclusiveMaximum().doubleValue()) |
| 152 | + fail(pointer, "exclusiveMaximum", "number not less than " + s.exclusiveMaximum(), n); |
| 153 | + if (s.multipleOf() != null && (n / s.multipleOf().doubleValue()) % 1 != 0) |
| 154 | + fail(pointer, "multipleOf", "not a multiple of " + s.multipleOf(), n); |
68 | 155 | } |
69 | 156 |
|
70 | 157 | private void validateObject(Object value, ObjectSchema s, String pointer) { |
71 | 158 | throw new UnsupportedOperationException("E4 implements object"); |
72 | 159 | } |
73 | 160 |
|
74 | 161 | private void validateArray(Object value, ArraySchema s, String pointer) { |
75 | | - throw new UnsupportedOperationException("E4 implements array"); |
| 162 | + throw new UnsupportedOperationException("E5 implements array"); |
| 163 | + } |
| 164 | + |
| 165 | + private static void fail(String pointer, String keyword, String message, Object rejectedValue) { |
| 166 | + throw new ValidationException(new ValidationError(pointer, keyword, message, rejectedValue)); |
76 | 167 | } |
77 | 168 |
|
78 | 169 | static void require(boolean condition, String pointer, String keyword, String message) { |
|
0 commit comments