Skip to content

Commit 7fb955f

Browse files
committed
Add new pragmas for date and time
1 parent bf32061 commit 7fb955f

File tree

6 files changed

+135
-117
lines changed

6 files changed

+135
-117
lines changed

src/main/java/com/relogiclabs/json/schema/internal/time/DateTimeContext.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.HashMap;
1010
import java.util.Map;
1111

12+
import static com.relogiclabs.json.schema.internal.time.JsonDateTime.UNSET;
1213
import static com.relogiclabs.json.schema.internal.util.StringHelper.concat;
1314
import static com.relogiclabs.json.schema.message.ErrorCode.DCNF01;
1415
import static com.relogiclabs.json.schema.message.ErrorCode.DDAY03;
@@ -76,8 +77,6 @@ private static void addWeekday(String key1, String key2, int value) {
7677
WEEKDAYS.put(key2, value);
7778
}
7879

79-
private static final int UNSET = -100;
80-
8180
private int era = UNSET;
8281
private int year = UNSET;
8382
private int month = UNSET;
@@ -88,8 +87,8 @@ private static void addWeekday(String key1, String key2, int value) {
8887
private int minute = UNSET;
8988
private int second = UNSET;
9089
private int fraction = UNSET;
91-
private int utcOffsetHour = UNSET;
92-
private int utcOffsetMinute = UNSET;
90+
private int utcHour = UNSET;
91+
private int utcMinute = UNSET;
9392

9493
@Getter
9594
public final DateTimeType type;
@@ -177,8 +176,8 @@ public void setUtcOffset(int hour, int minute) {
177176
concat("Invalid ", type, " UTC offset hour out of range"));
178177
if(minute < 0 || minute > 59) throw new InvalidDateTimeException(DUTC05,
179178
concat("Invalid ", type, " UTC offset minute out of range"));
180-
utcOffsetHour = checkField(utcOffsetHour, hour);
181-
utcOffsetMinute = checkField(utcOffsetMinute, minute);
179+
utcHour = checkField(utcHour, hour);
180+
utcMinute = checkField(utcMinute, minute);
182181
}
183182

184183
private int checkField(int current, int newValue) {
@@ -192,31 +191,31 @@ private static boolean isAllSet(int... values) {
192191
return Arrays.stream(values).allMatch(v -> v != UNSET);
193192
}
194193

195-
public void validate() {
194+
public JsonDateTime validate() {
196195
try {
197-
LocalDate date;
196+
JsonDateTime dateTime;
198197
if(isAllSet(year, month, day)) {
199198
DAYS_IN_MONTH[2] = isLeapYear(year)? 29 : 28;
200199
if(day < 1 || day > DAYS_IN_MONTH[month])
201200
throw new InvalidDateTimeException(DDAY03,
202201
concat("Invalid ", type, " day out of range"));
203-
date = LocalDate.of(year, month, day);
204-
if(weekday != UNSET && date.getDayOfWeek().getValue() != weekday)
202+
dateTime = new JsonDateTime(type, year, month, day);
203+
if(weekday != UNSET && dateTime.getDayOfWeek().getValue() != weekday)
205204
throw new InvalidDateTimeException(DWKD03, concat("Invalid ",
206205
type, " weekday input"));
207206
}
208-
if(isAllSet(year, month)) date = LocalDate.of(year, month, 1);
209-
if(isAllSet(year)) date = LocalDate.of(year, 1, 1);
207+
if(isAllSet(hour, amPm)) convertTo24Hour();
208+
if(hour != UNSET && (hour < 0 || hour > 23))
209+
throw new InvalidDateTimeException(DHUR05, concat("Invalid ",
210+
type, " hour out of range"));
211+
return new JsonDateTime(type, year, month, day, hour, minute, second,
212+
fraction, utcHour, utcMinute);
210213
} catch(InvalidDateTimeException e) {
211214
throw e;
212215
} catch(Exception e) {
213216
throw new InvalidDateTimeException(DINV01,
214217
concat("Invalid ", type, " year, month or day out of range", e));
215218
}
216-
if(isAllSet(hour, amPm)) convertTo24Hour();
217-
if(hour != UNSET && (hour < 0 || hour > 23))
218-
throw new InvalidDateTimeException(DHUR05, concat("Invalid ",
219-
type, " hour out of range"));
220219
}
221220

222221
private void convertTo24Hour() {
@@ -246,8 +245,8 @@ public String toString() {
246245
if(minute != UNSET) append(builder, "Minute: ", minute);
247246
if(second != UNSET) append(builder, "Second: ", second);
248247
if(fraction != UNSET) append(builder, "Fraction: ", fraction);
249-
if(utcOffsetHour != UNSET) append(builder, "UTC Offset Hour: ", utcOffsetHour);
250-
if(utcOffsetMinute != UNSET) append(builder, "UTC Offset Minute: ", utcOffsetMinute);
248+
if(utcHour != UNSET) append(builder, "UTC Offset Hour: ", utcHour);
249+
if(utcMinute != UNSET) append(builder, "UTC Offset Minute: ", utcMinute);
251250
return removeEnd(builder.toString(), ", ") + "}";
252251
}
253252

src/main/java/com/relogiclabs/json/schema/internal/time/DateTimeValidator.java renamed to src/main/java/com/relogiclabs/json/schema/internal/time/DateTimeParser.java

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.relogiclabs.json.schema.internal.util.DebugUtilities;
66
import com.relogiclabs.json.schema.internal.util.LexerErrorListener;
77
import com.relogiclabs.json.schema.internal.util.Reference;
8+
import lombok.Getter;
89
import org.antlr.v4.runtime.CharStreams;
910
import org.antlr.v4.runtime.Token;
1011

@@ -77,14 +78,14 @@
7778
import static com.relogiclabs.json.schema.internal.util.StringHelper.concat;
7879
import static com.relogiclabs.json.schema.message.ErrorCode.DINV02;
7980

80-
public final class DateTimeValidator {
81-
public static final String ISO_8601_DATE = "YYYY-MM-DD";
82-
public static final String ISO_8601_TIME = "YYYY-MM-DD'T'hh:mm:ss.FZZ";
83-
81+
public final class DateTimeParser {
8482
private static final Map<String, SegmentProcessor> PROCESSORS;
8583
private final DateTimeLexer dateTimeLexer;
8684
private final List<Token> lexerTokens;
8785

86+
@Getter private final String pattern;
87+
@Getter private final DateTimeType type;
88+
8889
static {
8990
PROCESSORS = new HashMap<>(50);
9091
addProcessor(TEXT, Text);
@@ -125,52 +126,39 @@ private static void addProcessor(int index, SegmentProcessor processor) {
125126
}
126127

127128
@SuppressWarnings("unchecked")
128-
public DateTimeValidator(String pattern) {
129-
dateTimeLexer = new DateTimeLexer(CharStreams.fromString(pattern));
130-
dateTimeLexer.removeErrorListeners();
131-
dateTimeLexer.addErrorListener(LexerErrorListener.DATE_TIME);
132-
lexerTokens = (List<Token>) dateTimeLexer.getAllTokens();
129+
public DateTimeParser(String pattern, DateTimeType type) {
130+
this.pattern = pattern;
131+
this.type = type;
132+
this.dateTimeLexer = new DateTimeLexer(CharStreams.fromString(pattern));
133+
this.dateTimeLexer.removeErrorListeners();
134+
this.dateTimeLexer.addErrorListener(LexerErrorListener.DATE_TIME);
135+
this.lexerTokens = (List<Token>) dateTimeLexer.getAllTokens();
133136
}
134137

135-
private void Validate(String input, DateTimeContext context) {
138+
private JsonDateTime parse(String input, DateTimeContext context) {
136139
for(var token : lexerTokens) {
137140
var processor = PROCESSORS.get(dateTimeLexer.getVocabulary().getSymbolicName(token.getType()));
138141
input = processor.process(input, token, context);
139142
}
140143
if(input.length() != 0) throw new InvalidDateTimeException(DINV02,
141144
concat("Invalid ", context.getType(), " input format"));
142145

143-
context.validate();
146+
var dateTime = context.validate();
144147
DebugUtilities.print(context);
148+
return dateTime;
145149
}
146150

147-
public void ValidateDate(String input) {
148-
Validate(input, new DateTimeContext(DateTimeType.DATE_TYPE));
149-
}
150-
151-
public void ValidateTime(String input) {
152-
Validate(input, new DateTimeContext(DateTimeType.TIME_TYPE));
153-
}
154-
155-
public boolean IsValidDate(String input, Reference<String> error) {
156-
try {
157-
ValidateDate(input);
158-
return true;
159-
} catch(InvalidDateTimeException e) {
160-
DebugUtilities.print(e);
161-
error.setValue(e.getMessage());
162-
return false;
163-
}
151+
public JsonDateTime parse(String input) {
152+
return parse(input, new DateTimeContext(type));
164153
}
165154

166-
public boolean IsValidTime(String input, Reference<String> error) {
155+
public JsonDateTime tryParse(String input, Reference<String> error) {
167156
try {
168-
ValidateTime(input);
169-
return true;
157+
return parse(input);
170158
} catch(InvalidDateTimeException e) {
171159
DebugUtilities.print(e);
172160
error.setValue(e.getMessage());
173-
return false;
161+
return null;
174162
}
175163
}
176164
}

src/main/java/com/relogiclabs/json/schema/internal/time/DateTimeType.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package com.relogiclabs.json.schema.internal.time;
22

3+
import com.relogiclabs.json.schema.types.JsonType;
34
import lombok.AllArgsConstructor;
45
import lombok.Getter;
56

7+
import static com.relogiclabs.json.schema.types.JsonType.DATE;
8+
import static com.relogiclabs.json.schema.types.JsonType.TIME;
9+
610
@Getter
711
@AllArgsConstructor
812
public enum DateTimeType {
9-
DATE_TYPE("date"),
10-
TIME_TYPE("time");
13+
DATE_TYPE("date", DATE),
14+
TIME_TYPE("time", TIME);
1115

1216
private final String name;
17+
private final JsonType type;
1318

1419
@Override
1520
public String toString() {

src/main/java/com/relogiclabs/json/schema/internal/tree/PragmaDescriptor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
import com.relogiclabs.json.schema.types.JBoolean;
44
import com.relogiclabs.json.schema.types.JNumber;
5+
import com.relogiclabs.json.schema.types.JString;
56
import lombok.Getter;
67

78
import java.util.HashMap;
89
import java.util.Map;
910

1011
@Getter
1112
public final class PragmaDescriptor<T> {
13+
public static final String ISO_8601_DATE = "YYYY-MM-DD";
14+
public static final String ISO_8601_TIME = "YYYY-MM-DD'T'hh:mm:ss.FZZ";
1215

1316
private static final Map<String, PragmaDescriptor<?>> PRAGMAS = new HashMap<>();
1417

@@ -18,6 +21,10 @@ public final class PragmaDescriptor<T> {
1821
= new PragmaDescriptor<>("FloatingPointTolerance", JNumber.class, 1E-10);
1922
public static final PragmaDescriptor<Boolean> IGNORE_OBJECT_PROPERTY_ORDER
2023
= new PragmaDescriptor<>("IgnoreObjectPropertyOrder", JBoolean.class, true);
24+
public static final PragmaDescriptor<String> DATE_DATA_TYPE_FORMAT
25+
= new PragmaDescriptor<>("DateDataTypeFormat", JString.class, ISO_8601_DATE);
26+
public static final PragmaDescriptor<String> TIME_DATA_TYPE_FORMAT
27+
= new PragmaDescriptor<>("TimeDataTypeFormat", JString.class, ISO_8601_TIME);
2128

2229
private final String name;
2330
private final Class<?> type;

src/main/java/com/relogiclabs/json/schema/internal/tree/PragmaManager.java

Lines changed: 0 additions & 65 deletions
This file was deleted.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.relogiclabs.json.schema.internal.tree;
2+
3+
import com.relogiclabs.json.schema.exception.DuplicatePragmaException;
4+
import com.relogiclabs.json.schema.internal.time.DateTimeParser;
5+
import com.relogiclabs.json.schema.message.MessageFormatter;
6+
import com.relogiclabs.json.schema.types.JPragma;
7+
import lombok.Getter;
8+
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
12+
import static com.relogiclabs.json.schema.internal.time.DateTimeType.DATE_TYPE;
13+
import static com.relogiclabs.json.schema.internal.time.DateTimeType.TIME_TYPE;
14+
import static com.relogiclabs.json.schema.message.ErrorCode.PRAG03;
15+
16+
@Getter
17+
public final class PragmaRegistry {
18+
19+
private static final String IGNORE_UNDEFINED_PROPERTIES = "IgnoreUndefinedProperties";
20+
private static final String FLOATING_POINT_TOLERANCE = "FloatingPointTolerance";
21+
private static final String IGNORE_OBJECT_PROPERTY_ORDER = "IgnoreObjectPropertyOrder";
22+
private static final String DATE_DATA_TYPE_FORMAT = "DateDataTypeFormat";
23+
private static final String TIME_DATA_TYPE_FORMAT = "TimeDataTypeFormat";
24+
25+
public final Map<String, JPragma> pragmas;
26+
27+
private boolean ignoreUndefinedProperties = PragmaDescriptor
28+
.IGNORE_UNDEFINED_PROPERTIES.getDefaultValue();
29+
private double floatingPointTolerance = PragmaDescriptor
30+
.FLOATING_POINT_TOLERANCE.getDefaultValue();
31+
private boolean ignoreObjectPropertyOrder = PragmaDescriptor
32+
.IGNORE_OBJECT_PROPERTY_ORDER.getDefaultValue();
33+
public String dateDataTypeFormat = PragmaDescriptor
34+
.DATE_DATA_TYPE_FORMAT.getDefaultValue();
35+
public String timeDataTypeFormat = PragmaDescriptor
36+
.TIME_DATA_TYPE_FORMAT.getDefaultValue();
37+
38+
private DateTimeParser dateTypeParser;
39+
private DateTimeParser timeTypeParser;
40+
41+
public PragmaRegistry() {
42+
this.pragmas = new HashMap<>();
43+
this.dateTypeParser = new DateTimeParser(dateDataTypeFormat, DATE_TYPE);
44+
this.timeTypeParser = new DateTimeParser(timeDataTypeFormat, TIME_TYPE);
45+
}
46+
47+
public JPragma addPragma(JPragma pragma) {
48+
if(pragmas.containsKey(pragma.getName()))
49+
throw new DuplicatePragmaException(MessageFormatter.formatForSchema(
50+
PRAG03, "Duplication found for " + pragma.getOutline(), pragma.getContext()));
51+
pragmas.put(pragma.getName(), pragma);
52+
setPragmaValue(pragma.getName(), pragma.getValue().toNativeValue());
53+
return pragma;
54+
}
55+
56+
public <T> void setPragmaValue(String name, T value) {
57+
switch (name) {
58+
case IGNORE_UNDEFINED_PROPERTIES -> ignoreUndefinedProperties = (boolean) value;
59+
case FLOATING_POINT_TOLERANCE -> floatingPointTolerance = (double) value;
60+
case IGNORE_OBJECT_PROPERTY_ORDER -> ignoreObjectPropertyOrder = (boolean) value;
61+
case DATE_DATA_TYPE_FORMAT -> {
62+
dateDataTypeFormat = (String) value;
63+
dateTypeParser = new DateTimeParser(dateDataTypeFormat, DATE_TYPE);
64+
}
65+
case TIME_DATA_TYPE_FORMAT -> {
66+
timeDataTypeFormat = (String) value;
67+
timeTypeParser = new DateTimeParser(timeDataTypeFormat, TIME_TYPE);
68+
}
69+
}
70+
}
71+
72+
public <T> T getPragmaValue(String name) {
73+
var entry = PragmaDescriptor.<T>from(name);
74+
var pragma = pragmas.get(entry.getName());
75+
return pragma == null
76+
? entry.getDefaultValue()
77+
: pragma.<T>getValue().toNativeValue();
78+
}
79+
80+
public JPragma getPragma(String name) {
81+
var entry = PragmaDescriptor.from(name);
82+
return pragmas.get(entry.getName());
83+
}
84+
}

0 commit comments

Comments
 (0)