Skip to content

Commit aeb9c2a

Browse files
committed
Add new validation functions for date and time
1 parent 7fb955f commit aeb9c2a

File tree

14 files changed

+423
-62
lines changed

14 files changed

+423
-62
lines changed

src/main/java/com/relogiclabs/json/schema/function/CoreFunctions1.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,29 @@ public CoreFunctions1(RuntimeContext runtime) {
3333
}
3434

3535
public boolean length(JString target, JInteger length) {
36-
var len = target.getValue().length();
37-
if(len != length.getValue()) return failWith(new JsonSchemaException(
38-
new ErrorDetail(SLEN01, "Invalid string length"),
39-
new ExpectedDetail(function, "length ", length),
40-
new ActualDetail(target, "found ", len, " for ", target.getOutline())));
36+
var rLength = target.getValue().length();
37+
if(rLength != length.getValue()) return failWith(new JsonSchemaException(
38+
new ErrorDetail(SLEN01, "Invalid length of string ", target),
39+
new ExpectedDetail(function, "a string of length ", length),
40+
new ActualDetail(target, "found ", rLength, " which does not match")));
4141
return true;
4242
}
4343

4444
public boolean length(JArray target, JInteger length) {
45-
var len = target.getElements().size();
46-
if(len != length.getValue()) return failWith(new JsonSchemaException(
47-
new ErrorDetail(ALEN01, "Invalid array length"),
48-
new ExpectedDetail(function, "length ", length),
49-
new ActualDetail(target, "found ", len, " for ", target.getOutline())));
45+
var rLength = target.getElements().size();
46+
if(rLength != length.getValue()) return failWith(new JsonSchemaException(
47+
new ErrorDetail(ALEN01, "Invalid length of array ", target.getOutline()),
48+
new ExpectedDetail(function, "an array of length ", length),
49+
new ActualDetail(target, "found ", rLength, " which does not match")));
5050
return true;
5151
}
5252

5353
public boolean length(JObject target, JInteger length) {
54-
var len = target.getProperties().size();
55-
if(len != length.getValue()) return failWith(new JsonSchemaException(
56-
new ErrorDetail(OLEN01, "Invalid object size or length"),
57-
new ExpectedDetail(function, "length ", length),
58-
new ActualDetail(target, "found ", len, " for ", target.getOutline())));
54+
var rLength = target.getProperties().size();
55+
if(rLength != length.getValue()) return failWith(new JsonSchemaException(
56+
new ErrorDetail(OLEN01, "Invalid size or length of object ", target.getOutline()),
57+
new ExpectedDetail(function, "an object of length ", length),
58+
new ActualDetail(target, "found ", rLength, " which does not match")));
5959
return true;
6060
}
6161

src/main/java/com/relogiclabs/json/schema/function/CoreFunctions3.java

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package com.relogiclabs.json.schema.function;
22

3-
import com.relogiclabs.json.schema.exception.DateTimeLexerException;
4-
import com.relogiclabs.json.schema.exception.InvalidDateTimeException;
53
import com.relogiclabs.json.schema.exception.JsonSchemaException;
6-
import com.relogiclabs.json.schema.internal.time.DateTimeType;
7-
import com.relogiclabs.json.schema.internal.time.DateTimeValidator;
84
import com.relogiclabs.json.schema.message.ActualDetail;
95
import com.relogiclabs.json.schema.message.ErrorDetail;
106
import com.relogiclabs.json.schema.message.ExpectedDetail;
@@ -21,7 +17,6 @@
2117
import static com.relogiclabs.json.schema.internal.util.CollectionHelper.containsKeys;
2218
import static com.relogiclabs.json.schema.internal.util.CollectionHelper.containsValues;
2319
import static com.relogiclabs.json.schema.internal.util.StreamHelper.count;
24-
import static com.relogiclabs.json.schema.internal.util.StringHelper.concat;
2520
import static com.relogiclabs.json.schema.internal.util.StringHelper.quote;
2621
import static com.relogiclabs.json.schema.message.ErrorCode.ELEM01;
2722
import static com.relogiclabs.json.schema.message.ErrorCode.EMAL01;
@@ -156,34 +151,4 @@ public boolean phone(JString target) {
156151
new ActualDetail(target, "found ", target, " that is invalid")));
157152
return true;
158153
}
159-
160-
public boolean date(JString target, JString pattern) {
161-
return dateTime(target, pattern, DateTimeType.DATE_TYPE);
162-
}
163-
164-
public boolean time(JString target, JString pattern) {
165-
return dateTime(target, pattern, DateTimeType.TIME_TYPE);
166-
}
167-
168-
private boolean dateTime(JString target, JString pattern, DateTimeType type) {
169-
try {
170-
var validator = new DateTimeValidator(pattern.getValue());
171-
if(type == DateTimeType.DATE_TYPE) validator.ValidateDate(target.getValue());
172-
else if(type == DateTimeType.TIME_TYPE) validator.ValidateTime(target.getValue());
173-
else throw new IllegalArgumentException(concat("Invalid ",
174-
DateTimeType.class.getSimpleName(), " value"));
175-
return true;
176-
} catch(DateTimeLexerException e) {
177-
return failWith(new JsonSchemaException(
178-
new ErrorDetail(e.getCode(), e.getMessage()),
179-
new ExpectedDetail(function, "a valid ", type, " pattern"),
180-
new ActualDetail(target, "found ", pattern, " that is invalid"), e));
181-
} catch(InvalidDateTimeException e) {
182-
return failWith(new JsonSchemaException(
183-
new ErrorDetail(e.getCode(), e.getMessage()),
184-
new ExpectedDetail(function, "a valid ", type, " formatted as ", pattern),
185-
new ActualDetail(target, "found ", target,
186-
" that is invalid or malformatted"), e));
187-
}
188-
}
189154
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package com.relogiclabs.json.schema.function;
2+
3+
import com.relogiclabs.json.schema.exception.JsonSchemaException;
4+
import com.relogiclabs.json.schema.internal.time.DateTimeParser;
5+
import com.relogiclabs.json.schema.internal.time.DateTimeType;
6+
import com.relogiclabs.json.schema.message.ActualDetail;
7+
import com.relogiclabs.json.schema.message.ErrorDetail;
8+
import com.relogiclabs.json.schema.message.ExpectedDetail;
9+
import com.relogiclabs.json.schema.tree.RuntimeContext;
10+
import com.relogiclabs.json.schema.types.JDateTime;
11+
import com.relogiclabs.json.schema.types.JString;
12+
import com.relogiclabs.json.schema.types.JUndefined;
13+
14+
import static com.relogiclabs.json.schema.internal.time.DateTimeType.DATE_TYPE;
15+
import static com.relogiclabs.json.schema.message.ErrorCode.AFTR01;
16+
import static com.relogiclabs.json.schema.message.ErrorCode.AFTR02;
17+
import static com.relogiclabs.json.schema.message.ErrorCode.BFOR01;
18+
import static com.relogiclabs.json.schema.message.ErrorCode.BFOR02;
19+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG01;
20+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG02;
21+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG03;
22+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG04;
23+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG05;
24+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG06;
25+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG07;
26+
import static com.relogiclabs.json.schema.message.ErrorCode.DRNG08;
27+
import static com.relogiclabs.json.schema.message.ErrorCode.ENDE01;
28+
import static com.relogiclabs.json.schema.message.ErrorCode.ENDE02;
29+
import static com.relogiclabs.json.schema.message.ErrorCode.STRT01;
30+
import static com.relogiclabs.json.schema.message.ErrorCode.STRT02;
31+
32+
public class CoreFunctions4 extends CoreFunctions3 {
33+
public CoreFunctions4(RuntimeContext runtime) {
34+
super(runtime);
35+
}
36+
37+
public boolean date(JString target, JString pattern) {
38+
return dateTime(target, pattern, DATE_TYPE);
39+
}
40+
41+
public boolean time(JString target, JString pattern) {
42+
return dateTime(target, pattern, DateTimeType.TIME_TYPE);
43+
}
44+
45+
private boolean dateTime(JString target, JString pattern, DateTimeType type) {
46+
return new DateTimeAgent(pattern.getValue(), type).parse(function, target) != null;
47+
}
48+
49+
public boolean before(JDateTime target, JString reference) {
50+
var dateTime = getDateTime(target.getDateTimeParser(), reference);
51+
if(dateTime == null) return false;
52+
if(target.getDateTime().compare(dateTime.getDateTime()) < 0) return true;
53+
var type = target.getDateTime().getType();
54+
var code = type == DATE_TYPE ? BFOR01 : BFOR02;
55+
return failWith(new JsonSchemaException(
56+
new ErrorDetail(code, type, " is not earlier than specified"),
57+
new ExpectedDetail(reference, "a ", type, " before ", reference),
58+
new ActualDetail(target, "found ", target, " which is not inside limit")
59+
));
60+
}
61+
62+
public boolean after(JDateTime target, JString reference) {
63+
var dateTime = getDateTime(target.getDateTimeParser(), reference);
64+
if(dateTime == null) return false;
65+
if(target.getDateTime().compare(dateTime.getDateTime()) > 0) return true;
66+
var type = target.getDateTime().getType();
67+
var code = type == DATE_TYPE ? AFTR01 : AFTR02;
68+
return failWith(new JsonSchemaException(
69+
new ErrorDetail(code, type, " is not later than specified"),
70+
new ExpectedDetail(reference, "a ", type, " after ", reference),
71+
new ActualDetail(target, "found ", target, " which is not inside limit")
72+
));
73+
}
74+
75+
public boolean range(JDateTime target, JString start, JString end) {
76+
var rStart = getDateTime(target.getDateTimeParser(), start);
77+
if(rStart == null) return false;
78+
var rEnd = getDateTime(target.getDateTimeParser(), end);
79+
if(rEnd == null) return false;
80+
boolean result = true;
81+
result &= isValidStart(target, rStart, DRNG01, DRNG02);
82+
result &= isValidEnd(target, rEnd, DRNG03, DRNG04);
83+
return result;
84+
}
85+
86+
public boolean range(JDateTime target, JUndefined start, JString end) {
87+
var rEnd = getDateTime(target.getDateTimeParser(), end);
88+
if(rEnd == null) return false;
89+
return isValidEnd(target, rEnd, DRNG05, DRNG06);
90+
}
91+
92+
public boolean range(JDateTime target, JString start, JUndefined end) {
93+
var rStart = getDateTime(target.getDateTimeParser(), start);
94+
if(rStart == null) return false;
95+
return isValidStart(target, rStart, DRNG07, DRNG08);
96+
}
97+
98+
private boolean isValidStart(JDateTime target, JDateTime start, String codeDate, String codeTime) {
99+
if(target.getDateTime().compare(start.getDateTime()) < 0) {
100+
var type = target.getDateTime().getType();
101+
var code = type == DATE_TYPE ? codeDate : codeTime;
102+
return failWith(new JsonSchemaException(
103+
new ErrorDetail(code, type, " is earlier than start ", type),
104+
new ExpectedDetail(start, "a ", type, " from or after ", start),
105+
new ActualDetail(target, "found ", target, " which is before start ", type)
106+
));
107+
}
108+
return true;
109+
}
110+
111+
private boolean isValidEnd(JDateTime target, JDateTime end, String codeDate, String codeTime) {
112+
if(target.getDateTime().compare(end.getDateTime()) > 0) {
113+
var type = target.getDateTime().getType();
114+
var code = type == DATE_TYPE ? codeDate : codeTime;
115+
return failWith(new JsonSchemaException(
116+
new ErrorDetail(code, type, " is later than end ", type),
117+
new ExpectedDetail(end, "a ", type, " until or before ", end),
118+
new ActualDetail(target, "found ", target, " which is after end ", type)
119+
));
120+
}
121+
return true;
122+
}
123+
124+
public boolean start(JDateTime target, JString reference) {
125+
var dateTime = getDateTime(target.getDateTimeParser(), reference);
126+
if(dateTime == null) return false;
127+
if(target.getDateTime().compare(dateTime.getDateTime()) < 0) {
128+
var type = target.getDateTime().getType();
129+
var code = type == DATE_TYPE ? STRT01 : STRT02;
130+
return failWith(new JsonSchemaException(
131+
new ErrorDetail(code, type, " is earlier than specified"),
132+
new ExpectedDetail(dateTime, "a ", type, " from or after ", dateTime),
133+
new ActualDetail(target, "found ", target, " which is before limit")
134+
));
135+
}
136+
return true;
137+
}
138+
139+
public boolean end(JDateTime target, JString reference) {
140+
var dateTime = getDateTime(target.getDateTimeParser(), reference);
141+
if(dateTime == null) return false;
142+
if(target.getDateTime().compare(dateTime.getDateTime()) > 0) {
143+
var type = target.getDateTime().getType();
144+
var code = type == DATE_TYPE ? ENDE01 : ENDE02;
145+
return failWith(new JsonSchemaException(
146+
new ErrorDetail(code, type, " is later than specified"),
147+
new ExpectedDetail(dateTime, "a ", type, " until or before ", dateTime),
148+
new ActualDetail(target, "found ", target, " which is after limit")
149+
));
150+
}
151+
return true;
152+
}
153+
154+
private JDateTime getDateTime(DateTimeParser parser, JString dateTime) {
155+
if(dateTime.getDerived() instanceof JDateTime result
156+
&& result.getDateTime().getType() == parser.getType()) return result;
157+
var jDateTime = new DateTimeAgent(parser).parse(function, dateTime);
158+
if(jDateTime == null) return null;
159+
dateTime.setDerived(jDateTime.create(dateTime));
160+
return (JDateTime) dateTime.getDerived();
161+
}
162+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.relogiclabs.json.schema.function;
2+
3+
import com.relogiclabs.json.schema.exception.DateTimeLexerException;
4+
import com.relogiclabs.json.schema.exception.InvalidDateTimeException;
5+
import com.relogiclabs.json.schema.exception.JsonSchemaException;
6+
import com.relogiclabs.json.schema.internal.time.DateTimeParser;
7+
import com.relogiclabs.json.schema.internal.time.DateTimeType;
8+
import com.relogiclabs.json.schema.internal.time.JsonDateTime;
9+
import com.relogiclabs.json.schema.message.ActualDetail;
10+
import com.relogiclabs.json.schema.message.ErrorDetail;
11+
import com.relogiclabs.json.schema.message.ExpectedDetail;
12+
import com.relogiclabs.json.schema.types.JFunction;
13+
import com.relogiclabs.json.schema.types.JString;
14+
15+
public class DateTimeAgent {
16+
private final String pattern;
17+
private final DateTimeType type;
18+
private DateTimeParser parser;
19+
20+
public DateTimeAgent(String pattern, DateTimeType type) {
21+
this.pattern = pattern;
22+
this.type = type;
23+
}
24+
25+
public DateTimeAgent(DateTimeParser parser) {
26+
this.pattern = parser.getPattern();
27+
this.type = parser.getType();
28+
this.parser = parser;
29+
}
30+
31+
public JsonDateTime parse(JFunction function, JString dateTime) {
32+
try {
33+
if(parser == null) parser = new DateTimeParser(pattern, type);
34+
return parser.parse(dateTime.getValue());
35+
} catch(DateTimeLexerException ex) {
36+
function.failWith(new JsonSchemaException(
37+
new ErrorDetail(ex.getCode(), ex.getMessage()),
38+
new ExpectedDetail(function, "a valid ", type, " pattern"),
39+
new ActualDetail(dateTime, "found ", pattern, " that is invalid"),
40+
ex));
41+
} catch(InvalidDateTimeException ex) {
42+
function.failWith(new JsonSchemaException(
43+
new ErrorDetail(ex.getCode(), ex.getMessage()),
44+
new ExpectedDetail(function, "a valid ", type, " formatted as ", pattern),
45+
new ActualDetail(dateTime, "found ", dateTime, " that is invalid or malformatted"),
46+
ex));
47+
}
48+
return null;
49+
}
50+
}

0 commit comments

Comments
 (0)