11package org .javawebstack .abstractdata .schema ;
22
33import org .javawebstack .abstractdata .AbstractElement ;
4+ import org .javawebstack .abstractdata .AbstractObject ;
45import org .javawebstack .abstractdata .AbstractPath ;
56import org .javawebstack .abstractdata .AbstractPrimitive ;
67
@@ -14,18 +15,36 @@ public class AbstractNumberSchema implements AbstractSchema {
1415 private boolean integerOnly = false ;
1516 private Number min ;
1617 private Number max ;
18+ private boolean minExclusive = false ;
19+ private boolean maxExclusive = false ;
20+ private Number step ;
1721 private final List <CustomValidation <AbstractPrimitive >> customValidations = new ArrayList <>();
1822
1923 public AbstractNumberSchema min (Number min ) {
24+ return min (min ,false );
25+ }
26+
27+ public AbstractNumberSchema max (Number max ) {
28+ return max (max ,false );
29+ }
30+
31+ public AbstractNumberSchema min (Number min , boolean exclusive ) {
32+ this .minExclusive = exclusive ;
2033 this .min = min ;
2134 return this ;
2235 }
2336
24- public AbstractNumberSchema max (Number max ) {
37+ public AbstractNumberSchema max (Number max , boolean exclusive ) {
38+ this .maxExclusive = exclusive ;
2539 this .max = max ;
2640 return this ;
2741 }
2842
43+ public AbstractNumberSchema step (Number step ){
44+ this .step = step ;
45+ return this ;
46+ }
47+
2948 public AbstractNumberSchema integerOnly () {
3049 this .integerOnly = true ;
3150 return this ;
@@ -52,6 +71,36 @@ public List<CustomValidation<AbstractPrimitive>> getCustomValidations() {
5271 return customValidations ;
5372 }
5473
74+ @ Override
75+ public AbstractObject toJsonSchema () {
76+ AbstractObject obj = new AbstractObject ();
77+
78+ obj .set ("type" ,integerOnly ? "integer" : "number" );
79+ if (min != null && max != null && !minExclusive && !maxExclusive ){
80+ BigDecimal dMin = (min instanceof Float || min instanceof Double ) ? BigDecimal .valueOf (min .doubleValue ()) : BigDecimal .valueOf (min .longValue ());
81+ BigDecimal dMax = (max instanceof Float || max instanceof Double ) ? BigDecimal .valueOf (max .doubleValue ()) : BigDecimal .valueOf (max .longValue ());
82+
83+ if (dMin .compareTo (dMax )==0 ){
84+ obj .set ("const" ,min );
85+ }
86+
87+ }
88+ if (!obj .has ("const" )) {
89+ if (min != null ) {
90+ obj .set (minExclusive ? "exclusiveMinimum" : "minimum" , min );
91+ }
92+ if (max != null ) {
93+ obj .set (maxExclusive ? "exclusiveMaximum" : "maximum" , max );
94+ }
95+ if (step != null ) {
96+ obj .set ("multipleOf" , step );
97+ }
98+ }
99+
100+
101+ return obj ;
102+ }
103+
55104 public List <SchemaValidationError > validate (AbstractPath path , AbstractElement value ) {
56105 List <SchemaValidationError > errors = new ArrayList <>();
57106 if (value .getType () != AbstractElement .Type .NUMBER ) {
@@ -66,16 +115,27 @@ public List<SchemaValidationError> validate(AbstractPath path, AbstractElement v
66115 }
67116 if (min != null ) {
68117 BigDecimal dMin = (min instanceof Float || min instanceof Double ) ? BigDecimal .valueOf (min .doubleValue ()) : BigDecimal .valueOf (min .longValue ());
69- if (dN .compareTo (dMin ) < 0 ) {
118+ if (!( dN .compareTo (dMin ) > ( minExclusive ? 0 : - 1 )) ) {
70119 errors .add (new SchemaValidationError (path , "number_smaller_than_min" ).meta ("min" , dMin .toPlainString ()).meta ("actual" , dN .toPlainString ()));
71120 }
72121 }
73122 if (max != null ) {
74- BigDecimal dMax = (max instanceof Float || min instanceof Double ) ? BigDecimal .valueOf (max .doubleValue ()) : BigDecimal .valueOf (max .longValue ());
75- if (dN .compareTo (dMax ) > 0 ) {
123+ BigDecimal dMax = (max instanceof Float || max instanceof Double ) ? BigDecimal .valueOf (max .doubleValue ()) : BigDecimal .valueOf (max .longValue ());
124+ if (!( dN .compareTo (dMax ) < ( maxExclusive ? 0 : 1 )) ) {
76125 errors .add (new SchemaValidationError (path , "number_larger_than_max" ).meta ("max" , dMax .toPlainString ()).meta ("actual" , dN .toPlainString ()));
77126 }
78127 }
128+ if (step != null ) {
129+ if (min != null && minExclusive ){
130+ throw new UnsupportedOperationException ("Step is not supported together with an exclusive minimum" );
131+ }
132+ BigDecimal dMin = min == null ? BigDecimal .ZERO : (min instanceof Float || min instanceof Double ) ? BigDecimal .valueOf (min .doubleValue ()) : BigDecimal .valueOf (min .longValue ());
133+ BigDecimal dStep = (step instanceof Float || step instanceof Double ) ? BigDecimal .valueOf (step .doubleValue ()) : BigDecimal .valueOf (step .longValue ());
134+
135+ if (dN .subtract (dMin ).remainder (dStep ).compareTo (BigDecimal .ZERO ) != 0 ) {
136+ errors .add (new SchemaValidationError (path , "number_not_within_step" ).meta ("step" , dStep .toPlainString ()).meta ("actual" , dN .toPlainString ()).meta ("start" ,dMin .toPlainString ()));
137+ }
138+ }
79139 for (CustomValidation <AbstractPrimitive > validation : customValidations ) {
80140 errors .addAll (validation .validate (path , value .primitive ()));
81141 }
0 commit comments