Skip to content

Commit 99292fe

Browse files
committed
Add new functions and pragmas
1 parent 779acd1 commit 99292fe

File tree

7 files changed

+176
-41
lines changed

7 files changed

+176
-41
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.relogiclabs.jschema.function;
2+
3+
import com.relogiclabs.jschema.exception.FunctionArgumentValueException;
4+
import com.relogiclabs.jschema.exception.FunctionValidationException;
5+
import com.relogiclabs.jschema.internal.function.IPAddress;
6+
import com.relogiclabs.jschema.message.ActualDetail;
7+
import com.relogiclabs.jschema.message.ErrorDetail;
8+
import com.relogiclabs.jschema.message.ExpectedDetail;
9+
import com.relogiclabs.jschema.node.JInteger;
10+
import com.relogiclabs.jschema.node.JString;
11+
12+
import java.util.Arrays;
13+
import java.util.stream.Collectors;
14+
15+
import static com.relogiclabs.jschema.internal.function.IPAddress.IPV4_VERSION;
16+
import static com.relogiclabs.jschema.internal.function.IPAddress.IPV6_VERSION;
17+
import static com.relogiclabs.jschema.message.ErrorCode.IPV4CF01;
18+
import static com.relogiclabs.jschema.message.ErrorCode.IPV6CF01;
19+
import static com.relogiclabs.jschema.message.ErrorCode.IPVACF01;
20+
import static com.relogiclabs.jschema.message.ErrorCode.IPVACF02;
21+
import static com.relogiclabs.jschema.message.ErrorCode.IPVACF03;
22+
import static com.relogiclabs.jschema.message.MessageFormatter.formatForSchema;
23+
24+
public final class CoreFunctions5 extends CoreFunctions4 {
25+
public boolean ipv4(JString target) {
26+
// Based on RFC 790 and RFC 1918
27+
if(IPAddress.isValidIPv4(target.getValue())) return true;
28+
return fail(new FunctionValidationException(
29+
new ErrorDetail(IPV4CF01, "Invalid target IPv4 address"),
30+
new ExpectedDetail(caller, "a valid IPv4 address"),
31+
new ActualDetail(target, "found malformed target " + target)));
32+
}
33+
34+
public boolean ipv6(JString target) {
35+
// Based on RFC 2373 (including widely used format)
36+
// Also support IPv4 mapped IPv6 address
37+
if(IPAddress.isValidIPv6(target.getValue())) return true;
38+
return fail(new FunctionValidationException(
39+
new ErrorDetail(IPV6CF01, "Invalid target IPv6 address"),
40+
new ExpectedDetail(caller, "a valid IPv6 address"),
41+
new ActualDetail(target, "found malformed target " + target)));
42+
}
43+
44+
public boolean ipv(JString target, JInteger... versions) {
45+
if(versions.length == 0) return fail(new FunctionArgumentValueException(
46+
formatForSchema(IPVACF01, "Version number(s) are missing", caller)));
47+
for(var version : versions) {
48+
if(version.getValue() == IPV4_VERSION) {
49+
if(IPAddress.isValidIPv4(target.getValue())) return true;
50+
} else if(version.getValue() == IPV6_VERSION) {
51+
if(IPAddress.isValidIPv6(target.getValue())) return true;
52+
} else return fail(new FunctionArgumentValueException(formatForSchema(IPVACF02,
53+
"Invalid version number " + version, caller)));
54+
}
55+
var list = Arrays.stream(versions).map(JInteger::toString)
56+
.collect(Collectors.joining(" or "));
57+
return fail(new FunctionValidationException(
58+
new ErrorDetail(IPVACF03, "Invalid target IP address"),
59+
new ExpectedDetail(caller, "a valid IPv(" + list + ") address"),
60+
new ActualDetail(target, "found malformed target " + target)));
61+
}
62+
}

src/main/java/com/relogiclabs/jschema/internal/builder/JPragmaBuilder.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import lombok.Setter;
1010
import lombok.experimental.Accessors;
1111

12-
import static com.relogiclabs.jschema.message.ErrorCode.PRAG01;
13-
import static com.relogiclabs.jschema.message.ErrorCode.PRAG02;
12+
import static com.relogiclabs.jschema.message.ErrorCode.PRGCHK01;
13+
import static com.relogiclabs.jschema.message.ErrorCode.PRGCHK02;
1414
import static com.relogiclabs.jschema.message.MessageFormatter.formatForSchema;
1515

1616
@Getter @Setter
@@ -22,10 +22,10 @@ public final class JPragmaBuilder extends JNodeBuilder<JPragmaBuilder> {
2222
private void checkPragma() {
2323
var descriptor = PragmaDescriptor.from(name);
2424
if (descriptor == null)
25-
throw new PragmaNotFoundException(formatForSchema(PRAG01, "Invalid pragma '"
25+
throw new PragmaNotFoundException(formatForSchema(PRGCHK01, "Invalid pragma '"
2626
+ name + "' with value " + value.getOutline() + " found", context()));
2727
if (!descriptor.matchType(value.getClass()))
28-
throw new InvalidPragmaValueException(formatForSchema(PRAG02, "Invalid value "
28+
throw new InvalidPragmaValueException(formatForSchema(PRGCHK02, "Invalid value "
2929
+ value.getOutline() + " for pragma '" + name + "' found", value));
3030
}
3131

src/main/java/com/relogiclabs/jschema/internal/function/CoreLibrary.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.relogiclabs.jschema.internal.function;
22

3-
import com.relogiclabs.jschema.function.CoreFunctions4;
3+
import com.relogiclabs.jschema.function.CoreFunctions5;
44
import com.relogiclabs.jschema.internal.tree.EFunction;
55

66
import java.util.List;
77

88
public class CoreLibrary {
9-
private static final FunctionMap functions = FunctionLoader.load(CoreFunctions4.class, null);
9+
private static final FunctionMap functions = FunctionLoader.load(CoreFunctions5.class, null);
1010

1111
public static List<EFunction> getFunctions(String key) {
1212
return functions.get(key);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.relogiclabs.jschema.internal.function;
2+
3+
import java.util.regex.Pattern;
4+
5+
public final class IPAddress {
6+
public static final int IPV4_VERSION = 4;
7+
public static final int IPV6_VERSION = 6;
8+
9+
private static final String IPV4_REGEX =
10+
"((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]?[0-9])\\.){3}" +
11+
"(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]?[0-9])";
12+
13+
private static final String IPV6_REGEX =
14+
"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" +
15+
"([0-9a-fA-F]{1,4}:){1,7}:|" +
16+
"([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" +
17+
"([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" +
18+
"([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" +
19+
"([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" +
20+
"([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" +
21+
"[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" +
22+
":((:[0-9a-fA-F]{1,4}){1,7}|:)|" +
23+
"fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|" +
24+
"::(ffff(:0{1,4}){0,1}:){0,1}" +
25+
"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}" +
26+
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|" +
27+
"([0-9a-fA-F]{1,4}:){1,4}:" +
28+
"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}" +
29+
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))";
30+
31+
32+
private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4_REGEX);
33+
private static final Pattern IPV6_PATTERN = Pattern.compile(IPV6_REGEX);
34+
35+
private IPAddress() {
36+
throw new UnsupportedOperationException("This class is not intended for instantiation");
37+
}
38+
39+
public static boolean isValidIPv4(String ipAddress) {
40+
return IPV4_PATTERN.matcher(ipAddress).matches();
41+
}
42+
43+
public static boolean isValidIPv6(String ipAddress) {
44+
return IPV6_PATTERN.matcher(ipAddress).matches();
45+
}
46+
}

src/main/java/com/relogiclabs/jschema/internal/tree/PragmaDescriptor.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
package com.relogiclabs.jschema.internal.tree;
22

33
import com.relogiclabs.jschema.node.JBoolean;
4+
import com.relogiclabs.jschema.node.JInteger;
45
import com.relogiclabs.jschema.node.JNumber;
56
import com.relogiclabs.jschema.node.JString;
67
import lombok.Getter;
78

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

12+
import static com.relogiclabs.jschema.message.OutlineFormatter.DEFAULT_MAXIMUM_LENGTH;
13+
import static com.relogiclabs.jschema.tree.PragmaRegistry.NAME_DATE_DATA_TYPE_FORMAT;
14+
import static com.relogiclabs.jschema.tree.PragmaRegistry.NAME_ENABLE_CONTEXTUAL_EXCEPTION;
15+
import static com.relogiclabs.jschema.tree.PragmaRegistry.NAME_FLOATING_POINT_TOLERANCE;
16+
import static com.relogiclabs.jschema.tree.PragmaRegistry.NAME_IGNORE_OBJECT_PROPERTY_ORDER;
17+
import static com.relogiclabs.jschema.tree.PragmaRegistry.NAME_IGNORE_UNDEFINED_PROPERTIES;
18+
import static com.relogiclabs.jschema.tree.PragmaRegistry.NAME_OUTLINE_MAXIMUM_LENGTH;
19+
import static com.relogiclabs.jschema.tree.PragmaRegistry.NAME_TIME_DATA_TYPE_FORMAT;
20+
1121
@Getter
1222
public final class PragmaDescriptor<T> {
1323
public static final String ISO_8601_DATE = "YYYY-MM-DD";
@@ -16,15 +26,20 @@ public final class PragmaDescriptor<T> {
1626
private static final Map<String, PragmaDescriptor<?>> PRAGMAS = new HashMap<>();
1727

1828
public static final PragmaDescriptor<Boolean> IGNORE_UNDEFINED_PROPERTIES
19-
= new PragmaDescriptor<>("IgnoreUndefinedProperties", JBoolean.class, false);
29+
= new PragmaDescriptor<>(NAME_IGNORE_UNDEFINED_PROPERTIES, JBoolean.class, false);
2030
public static final PragmaDescriptor<Double> FLOATING_POINT_TOLERANCE
21-
= new PragmaDescriptor<>("FloatingPointTolerance", JNumber.class, 1E-10);
31+
= new PragmaDescriptor<>(NAME_FLOATING_POINT_TOLERANCE, JNumber.class, 1E-10);
2232
public static final PragmaDescriptor<Boolean> IGNORE_OBJECT_PROPERTY_ORDER
23-
= new PragmaDescriptor<>("IgnoreObjectPropertyOrder", JBoolean.class, true);
33+
= new PragmaDescriptor<>(NAME_IGNORE_OBJECT_PROPERTY_ORDER, JBoolean.class, true);
2434
public static final PragmaDescriptor<String> DATE_DATA_TYPE_FORMAT
25-
= new PragmaDescriptor<>("DateDataTypeFormat", JString.class, ISO_8601_DATE);
35+
= new PragmaDescriptor<>(NAME_DATE_DATA_TYPE_FORMAT, JString.class, ISO_8601_DATE);
2636
public static final PragmaDescriptor<String> TIME_DATA_TYPE_FORMAT
27-
= new PragmaDescriptor<>("TimeDataTypeFormat", JString.class, ISO_8601_TIME);
37+
= new PragmaDescriptor<>(NAME_TIME_DATA_TYPE_FORMAT, JString.class, ISO_8601_TIME);
38+
public static final PragmaDescriptor<Boolean> ENABLE_CONTEXTUAL_EXCEPTION
39+
= new PragmaDescriptor<>(NAME_ENABLE_CONTEXTUAL_EXCEPTION, JBoolean.class, false);
40+
public static final PragmaDescriptor<Integer> OUTLINE_MAXIMUM_LENGTH
41+
= new PragmaDescriptor<>(NAME_OUTLINE_MAXIMUM_LENGTH, JInteger.class,
42+
DEFAULT_MAXIMUM_LENGTH);
2843

2944
private final String name;
3045
private final Class<?> type;
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
11
package com.relogiclabs.jschema.message;
22

3+
import lombok.Getter;
4+
35
import static org.apache.commons.lang3.StringUtils.left;
46
import static org.apache.commons.lang3.StringUtils.right;
57

68
public final class OutlineFormatter {
9+
public static final int DEFAULT_MAXIMUM_LENGTH = 200;
710
private static final String ABBREVIATE_MARKER = "...";
8-
private static int outlineLength = 200;
9-
private static int startLength = 2 * outlineLength / 3;
10-
private static int endLength = outlineLength / 3;
11+
12+
@Getter
13+
private static int maximumLength = DEFAULT_MAXIMUM_LENGTH;
14+
private static int startLength = 2 * maximumLength / 3;
15+
private static int endLength = maximumLength / 3;
1116

1217
private OutlineFormatter() {
1318
throw new UnsupportedOperationException("This class is not intended for instantiation");
1419
}
1520

16-
public static void setOutlineLength(int length) {
17-
outlineLength = length;
21+
public static void setMaximumLength(int length) {
22+
maximumLength = length;
1823
startLength = 2 * length / 3;
1924
endLength = length / 3;
2025
}
2126

2227
public static String createOutline(Object object) {
2328
var string = object.toString();
24-
return outlineLength >= string.length() ? string
29+
return string.length() <= maximumLength ? string
2530
: left(string, startLength) + ABBREVIATE_MARKER + right(string, endLength);
2631
}
2732
}

src/main/java/com/relogiclabs/jschema/tree/PragmaRegistry.java

+31-24
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,43 @@
33
import com.relogiclabs.jschema.exception.DuplicatePragmaException;
44
import com.relogiclabs.jschema.internal.time.DateTimeParser;
55
import com.relogiclabs.jschema.internal.tree.PragmaDescriptor;
6+
import com.relogiclabs.jschema.message.OutlineFormatter;
67
import com.relogiclabs.jschema.node.JPragma;
78
import lombok.Getter;
89

910
import java.util.HashMap;
1011
import java.util.Iterator;
1112
import java.util.Map;
1213

13-
import static com.relogiclabs.jschema.message.ErrorCode.PRAG03;
14+
import static com.relogiclabs.jschema.internal.tree.PragmaDescriptor.DATE_DATA_TYPE_FORMAT;
15+
import static com.relogiclabs.jschema.internal.tree.PragmaDescriptor.ENABLE_CONTEXTUAL_EXCEPTION;
16+
import static com.relogiclabs.jschema.internal.tree.PragmaDescriptor.FLOATING_POINT_TOLERANCE;
17+
import static com.relogiclabs.jschema.internal.tree.PragmaDescriptor.IGNORE_OBJECT_PROPERTY_ORDER;
18+
import static com.relogiclabs.jschema.internal.tree.PragmaDescriptor.IGNORE_UNDEFINED_PROPERTIES;
19+
import static com.relogiclabs.jschema.internal.tree.PragmaDescriptor.TIME_DATA_TYPE_FORMAT;
20+
import static com.relogiclabs.jschema.message.ErrorCode.PRGDUP01;
1421
import static com.relogiclabs.jschema.message.MessageFormatter.formatForSchema;
1522
import static com.relogiclabs.jschema.time.DateTimeType.DATE_TYPE;
1623
import static com.relogiclabs.jschema.time.DateTimeType.TIME_TYPE;
1724

1825
@Getter
1926
public final class PragmaRegistry implements Iterable<Map.Entry<String, JPragma>> {
20-
21-
private static final String IGNORE_UNDEFINED_PROPERTIES = "IgnoreUndefinedProperties";
22-
private static final String FLOATING_POINT_TOLERANCE = "FloatingPointTolerance";
23-
private static final String IGNORE_OBJECT_PROPERTY_ORDER = "IgnoreObjectPropertyOrder";
24-
private static final String DATE_DATA_TYPE_FORMAT = "DateDataTypeFormat";
25-
private static final String TIME_DATA_TYPE_FORMAT = "TimeDataTypeFormat";
27+
public static final String NAME_IGNORE_UNDEFINED_PROPERTIES = "IgnoreUndefinedProperties";
28+
public static final String NAME_FLOATING_POINT_TOLERANCE = "FloatingPointTolerance";
29+
public static final String NAME_IGNORE_OBJECT_PROPERTY_ORDER = "IgnoreObjectPropertyOrder";
30+
public static final String NAME_DATE_DATA_TYPE_FORMAT = "DateDataTypeFormat";
31+
public static final String NAME_TIME_DATA_TYPE_FORMAT = "TimeDataTypeFormat";
32+
public static final String NAME_ENABLE_CONTEXTUAL_EXCEPTION = "EnableContextualException";
33+
public static final String NAME_OUTLINE_MAXIMUM_LENGTH = "OutlineMaximumLength";
2634

2735
private final Map<String, JPragma> pragmas;
2836

29-
private boolean ignoreUndefinedProperties = PragmaDescriptor
30-
.IGNORE_UNDEFINED_PROPERTIES.getDefaultValue();
31-
private double floatingPointTolerance = PragmaDescriptor
32-
.FLOATING_POINT_TOLERANCE.getDefaultValue();
33-
private boolean ignoreObjectPropertyOrder = PragmaDescriptor
34-
.IGNORE_OBJECT_PROPERTY_ORDER.getDefaultValue();
35-
private String dateDataTypeFormat = PragmaDescriptor
36-
.DATE_DATA_TYPE_FORMAT.getDefaultValue();
37-
private String timeDataTypeFormat = PragmaDescriptor
38-
.TIME_DATA_TYPE_FORMAT.getDefaultValue();
37+
private boolean ignoreUndefinedProperties = IGNORE_UNDEFINED_PROPERTIES.getDefaultValue();
38+
private double floatingPointTolerance = FLOATING_POINT_TOLERANCE.getDefaultValue();
39+
private boolean ignoreObjectPropertyOrder = IGNORE_OBJECT_PROPERTY_ORDER.getDefaultValue();
40+
private String dateDataTypeFormat = DATE_DATA_TYPE_FORMAT.getDefaultValue();
41+
private String timeDataTypeFormat = TIME_DATA_TYPE_FORMAT.getDefaultValue();
42+
private boolean enableContextualException = ENABLE_CONTEXTUAL_EXCEPTION.getDefaultValue();
3943

4044
private DateTimeParser dateTypeParser;
4145
private DateTimeParser timeTypeParser;
@@ -47,26 +51,29 @@ public PragmaRegistry() {
4751
}
4852

4953
public JPragma addPragma(JPragma pragma) {
50-
if(pragmas.containsKey(pragma.getName())) throw new DuplicatePragmaException(formatForSchema(
51-
PRAG03, "Duplication found for " + pragma.getOutline(), pragma));
54+
if(pragmas.containsKey(pragma.getName()))
55+
throw new DuplicatePragmaException(formatForSchema(PRGDUP01,
56+
"Duplication found for " + pragma.getOutline(), pragma));
5257
pragmas.put(pragma.getName(), pragma);
5358
setPragmaValue(pragma.getName(), pragma.getValue().toNativeValue());
5459
return pragma;
5560
}
5661

5762
public <T> void setPragmaValue(String name, T value) {
5863
switch (name) {
59-
case IGNORE_UNDEFINED_PROPERTIES -> ignoreUndefinedProperties = (boolean) value;
60-
case FLOATING_POINT_TOLERANCE -> floatingPointTolerance = (double) value;
61-
case IGNORE_OBJECT_PROPERTY_ORDER -> ignoreObjectPropertyOrder = (boolean) value;
62-
case DATE_DATA_TYPE_FORMAT -> {
64+
case NAME_IGNORE_UNDEFINED_PROPERTIES -> ignoreUndefinedProperties = (boolean) value;
65+
case NAME_FLOATING_POINT_TOLERANCE -> floatingPointTolerance = (double) value;
66+
case NAME_IGNORE_OBJECT_PROPERTY_ORDER -> ignoreObjectPropertyOrder = (boolean) value;
67+
case NAME_DATE_DATA_TYPE_FORMAT -> {
6368
dateDataTypeFormat = (String) value;
6469
dateTypeParser = new DateTimeParser(dateDataTypeFormat, DATE_TYPE);
6570
}
66-
case TIME_DATA_TYPE_FORMAT -> {
71+
case NAME_TIME_DATA_TYPE_FORMAT -> {
6772
timeDataTypeFormat = (String) value;
6873
timeTypeParser = new DateTimeParser(timeDataTypeFormat, TIME_TYPE);
6974
}
75+
case NAME_ENABLE_CONTEXTUAL_EXCEPTION -> enableContextualException = (boolean) value;
76+
case NAME_OUTLINE_MAXIMUM_LENGTH -> OutlineFormatter.setMaximumLength((int) (long) value);
7077
}
7178
}
7279

0 commit comments

Comments
 (0)