Skip to content

Commit cb9026b

Browse files
committed
Update data type validation process
1 parent ed4e83b commit cb9026b

File tree

5 files changed

+90
-60
lines changed

5 files changed

+90
-60
lines changed

README.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ The next example represents an expanded version of the previous one, which bring
9090
} #object #null
9191
}
9292

93-
%define $tags: @length(1, 10) #array($tag)
93+
%define $tags: @length(1, 10) #string*($tag) #array
9494
%define $tag: @length(3, 20) @regex("[A-Za-z_]+") #string
9595

9696
%schema:
@@ -99,10 +99,9 @@ The next example represents an expanded version of the previous one, which bring
9999
"id": @range(1, 10000) #integer,
100100
/*username does not allow special characters*/
101101
"username": @regex("[a-z_]{3,30}") #string,
102-
/*currently only one role is allowed by system*/
103-
"role": "user" #string,
102+
"role": @enum("user", "admin") #string,
104103
"isActive": #boolean, //user account current status
105-
"registeredAt": #time,
104+
"registeredAt": @time("DD-MM-YYYY hh:mm:ss") #string,
106105
"profile": {
107106
"firstName": @regex("[A-Za-z]{3,50}") #string,
108107
"lastName": @regex("[A-Za-z]{3,50}") #string,
@@ -137,9 +136,9 @@ The subsequent JSON sample is an illustrative example that successfully validate
137136
"user": {
138137
"id": 1234,
139138
"username": "johndoe",
140-
"role": "user",
139+
"role": "admin",
141140
"isActive": true,
142-
"registeredAt": "2023-09-06T15:10:30.639Z",
141+
"registeredAt": "06-09-2023 15:10:30",
143142
"profile": {
144143
"firstName": "John",
145144
"lastName": "Doe",
@@ -196,20 +195,20 @@ The subsequent JSON sample is an illustrative example that successfully validate
196195
"id": "p1",
197196
"name": "Smartphone",
198197
"brand": "TechGiant",
199-
"price": 1.99,
198+
"price": 599.99,
200199
"inStock": true,
201200
"specs": null
202201
},
203202
{
204203
"id": "p2",
205204
"name": "Laptop",
206205
"brand": "SuperTech",
207-
"price": 159999.99,
206+
"price": 1299.99,
208207
"inStock": false,
209208
"specs": {
210-
"cpu": "Ryzen 01",
211-
"ram": "8GB",
212-
"storage": "256GB SSD"
209+
"cpu": "Ryzen 11",
210+
"ram": "11GB",
211+
"storage": "11GB SSD"
213212
}
214213
}
215214
],

pom.xml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66

77
<groupId>com.relogiclabs.json</groupId>
88
<artifactId>relogiclabs-json-schema</artifactId>
9-
<version>1.4.0</version>
9+
<version>1.5.0</version>
1010
<name>New Json Schema</name>
11-
<description>A simplified, concise, intuitive, and extensible Json Schema</description>
11+
<description>
12+
The New JSON Schema prioritizes simplicity, conciseness, and readability, making
13+
it user-friendly and accessible without the need for extensive prior knowledge.
14+
It offers efficient read-write facilities, precise JSON document definition
15+
through various data types and functions, and extensibility to meet modern web
16+
service diverse requirements.
17+
</description>
1218
<url>https://www.relogiclabs.com/p/json-schema.html</url>
1319
<organization>
1420
<name>Relogic Labs</name>
@@ -45,7 +51,7 @@
4551
<dependency>
4652
<groupId>org.antlr</groupId>
4753
<artifactId>antlr4-runtime</artifactId>
48-
<version>4.13.0</version>
54+
<version>4.13.1</version>
4955
</dependency>
5056
<dependency>
5157
<groupId>org.projectlombok</groupId>
@@ -163,4 +169,4 @@
163169
</build>
164170
</profile>
165171
</profiles>
166-
</project>
172+
</project>

src/main/java/com/relogiclabs/json/schema/tree/RuntimeContext.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,22 @@
1717
import java.util.LinkedList;
1818
import java.util.Map;
1919
import java.util.Queue;
20+
import java.util.function.Supplier;
2021

2122
import static com.relogiclabs.json.schema.internal.util.StringHelper.concat;
2223
import static com.relogiclabs.json.schema.message.ErrorCode.DEFI01;
2324

2425

25-
@Getter
2626
public class RuntimeContext {
27-
2827
private final FunctionManager functionManager;
2928
private final PragmaManager pragmaManager;
30-
private final Map<JAlias, JValidator> definitions;
31-
private final boolean throwException;
32-
private final Queue<Exception> exceptions;
33-
private final MessageFormatter messageFormatter;
29+
private int disableException = 0;
30+
31+
@Getter private final Map<JAlias, JValidator> definitions;
32+
@Getter private final boolean throwException;
33+
@Getter private final Queue<Exception> exceptions;
34+
@Getter private final MessageFormatter messageFormatter;
35+
3436

3537
public RuntimeContext(MessageFormatter messageFormatter, boolean throwException) {
3638
this.messageFormatter = messageFormatter;
@@ -85,9 +87,18 @@ public boolean areEqual(double value1, double value2) {
8587
return Math.abs(value1 - value2) < getFloatingPointTolerance();
8688
}
8789

90+
public <T> T tryMatch(Supplier<T> function) {
91+
try {
92+
disableException += 1;
93+
return function.get();
94+
} finally {
95+
disableException -= 1;
96+
}
97+
}
98+
8899
public boolean failWith(RuntimeException exception) {
89-
if(throwException) throw exception;
90-
exceptions.add(exception);
100+
if(throwException && disableException == 0) throw exception;
101+
if(disableException == 0) exceptions.add(exception);
91102
return false;
92103
}
93104
}

src/main/java/com/relogiclabs/json/schema/types/JDataType.java

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.relogiclabs.json.schema.exception.JsonSchemaException;
55
import com.relogiclabs.json.schema.internal.message.ActualHelper;
66
import com.relogiclabs.json.schema.internal.message.ExpectedHelper;
7+
import com.relogiclabs.json.schema.internal.message.MatchReport;
78
import com.relogiclabs.json.schema.message.ErrorDetail;
89
import com.relogiclabs.json.schema.message.MessageFormatter;
910
import lombok.EqualsAndHashCode;
@@ -14,14 +15,16 @@
1415
import java.util.Collection;
1516
import java.util.List;
1617

18+
import static com.relogiclabs.json.schema.internal.message.MatchReport.AliasError;
19+
import static com.relogiclabs.json.schema.internal.message.MatchReport.ArgumentError;
20+
import static com.relogiclabs.json.schema.internal.message.MatchReport.Success;
21+
import static com.relogiclabs.json.schema.internal.message.MatchReport.TypeError;
22+
import static com.relogiclabs.json.schema.internal.message.MessageHelper.DataTypeArgumentFailed;
1723
import static com.relogiclabs.json.schema.internal.message.MessageHelper.DataTypeMismatch;
1824
import static com.relogiclabs.json.schema.internal.message.MessageHelper.InvalidNestedDataType;
1925
import static com.relogiclabs.json.schema.internal.util.StreamHelper.allTrue;
20-
import static com.relogiclabs.json.schema.message.ErrorCode.DEFI03;
21-
import static com.relogiclabs.json.schema.message.ErrorCode.DTYP02;
22-
import static com.relogiclabs.json.schema.message.ErrorCode.DTYP04;
23-
import static com.relogiclabs.json.schema.message.ErrorCode.DTYP05;
24-
import static com.relogiclabs.json.schema.message.ErrorCode.DTYP06;
26+
import static com.relogiclabs.json.schema.internal.util.StringHelper.quote;
27+
import static com.relogiclabs.json.schema.message.ErrorCode.DTYP03;
2528
import static java.util.Collections.emptyList;
2629
import static java.util.Objects.requireNonNull;
2730

@@ -57,47 +60,52 @@ protected <T extends JNode> T initialize() {
5760

5861
@Override
5962
public boolean match(JNode node) {
60-
if(!nested) return jsonType.match(node);
61-
if(!(node instanceof JComposite composite))
62-
return failWith(new JsonSchemaException(
63-
new ErrorDetail(DTYP02, InvalidNestedDataType),
64-
ExpectedHelper.asInvalidDataType(this),
65-
ActualHelper.asInvalidDataType(node)));
66-
return composite.components().stream().map(this::matchCurrent).allMatch(allTrue());
63+
if(!nested) return isMatchCurrent(node);
64+
if(!(node instanceof JComposite composite)) return false;
65+
return composite.components().stream().map(this::isMatchCurrent).allMatch(allTrue());
6766
}
6867

69-
private boolean matchCurrent(JNode node) {
70-
boolean result = true;
71-
result &= jsonType.match(node);
72-
if(alias == null) return result;
68+
private boolean isMatchCurrent(JNode node) {
69+
return matchCurrent(node) == Success;
70+
}
71+
72+
private MatchReport matchCurrent(JNode node) {
73+
var result = jsonType.match(node) ? Success : TypeError;
74+
if(alias == null || result != Success) return result;
7375
var validator = getRuntime().getDefinitions().get(alias);
74-
if(validator == null) return failWith(new DefinitionNotFoundException(
75-
MessageFormatter.formatForSchema(DEFI03, alias.getName(), getContext())));
76-
result &= validator.match(node);
76+
if(validator == null) return AliasError;
77+
result = validator.match(node) ? Success : ArgumentError;
7778
return result;
7879
}
7980

8081
public boolean matchForReport(JNode node) {
81-
if(!nested && !jsonType.match(node))
82-
return failWith(new JsonSchemaException(
83-
new ErrorDetail(DTYP04, DataTypeMismatch),
84-
ExpectedHelper.asDataTypeMismatch(this),
85-
ActualHelper.asDataTypeMismatch(node)));
82+
if(!nested) return matchForReport(node, false);
8683
if(!(node instanceof JComposite composite))
8784
return failWith(new JsonSchemaException(
88-
new ErrorDetail(DTYP05, InvalidNestedDataType),
89-
ExpectedHelper.asInvalidDataType(this),
90-
ActualHelper.asInvalidDataType(node)));
85+
new ErrorDetail(DTYP03, InvalidNestedDataType),
86+
ExpectedHelper.asInvalidNestedDataType(this),
87+
ActualHelper.asInvalidNestedDataType(node)));
9188
boolean result = true;
92-
for(var c : composite.components()) {
93-
if(!matchCurrent(c)) result &= failWith(new JsonSchemaException(
94-
new ErrorDetail(DTYP06, DataTypeMismatch),
95-
ExpectedHelper.asDataTypeMismatch(this),
96-
ActualHelper.asDataTypeMismatch(c)));
97-
}
89+
for(var c : composite.components()) result &= matchForReport(c, true);
9890
return result;
9991
}
10092

93+
private boolean matchForReport(JNode node, boolean nested) {
94+
var result = matchCurrent(node);
95+
if(result == TypeError) return failWith(new JsonSchemaException(
96+
new ErrorDetail(TypeError.getCode(nested), DataTypeMismatch),
97+
ExpectedHelper.asDataTypeMismatch(this),
98+
ActualHelper.asDataTypeMismatch(node)));
99+
if(result == AliasError) return failWith(new DefinitionNotFoundException(
100+
MessageFormatter.formatForSchema(AliasError.getCode(nested),
101+
"No definition found for " + quote(alias), getContext())));
102+
if(result == ArgumentError) return failWith(new JsonSchemaException(
103+
new ErrorDetail(ArgumentError.getCode(nested), DataTypeArgumentFailed),
104+
ExpectedHelper.asDataTypeArgumentFailed(this),
105+
ActualHelper.asDataTypeArgumentFailed(node)));
106+
return true;
107+
}
108+
101109
public boolean isApplicable(JNode node) {
102110
return !nested || node instanceof JComposite;
103111
}

src/main/java/com/relogiclabs/json/schema/types/JValidator.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ public boolean match(JNode node) {
7979
ExpectedHelper.asValueMismatch(value),
8080
ActualHelper.asValueMismatch(node)));
8181
var rDataType = matchDataType(node);
82-
if(!rDataType) dataTypes.forEach(d -> d.matchForReport(node));
8382
var fDataType = rDataType && dataTypes.size() != 0;
8483
boolean rFunction = allTrue(functions.stream()
8584
.filter(f -> f.isApplicable(node) || !fDataType)
@@ -88,12 +87,19 @@ public boolean match(JNode node) {
8887
}
8988

9089
private boolean matchDataType(JNode node) {
90+
if(getRuntime().tryMatch(() -> checkDataType(node))) return true;
91+
dataTypes.stream().filter(d -> !d.getNested()).forEach(d -> d.matchForReport(node));
92+
dataTypes.stream().filter(d -> d.getNested()).forEach(d -> d.matchForReport(node));
93+
return false;
94+
}
95+
96+
private boolean checkDataType(JNode node) {
9197
var list1 = dataTypes.stream().filter(d -> !d.getNested()).map(d -> d.match(node)).toList();
92-
var result1 = list1.stream().anyMatch(anyTrue()) || list1.size() == 0;
98+
var result1 = list1.stream().anyMatch(anyTrue());
9399
var list2 = dataTypes.stream().filter(d -> d.getNested() && (d.isApplicable(node) || !result1))
94-
.map(d -> d.match(node)).toList();
100+
.map(d -> d.match(node)).toList();
95101
var result2 = list2.stream().anyMatch(anyTrue()) || list2.size() == 0;
96-
return result1 && result2;
102+
return (result1 || list1.size() == 0) && result2;
97103
}
98104

99105
@Override

0 commit comments

Comments
 (0)