1515import io .vertx .json .schema .SchemaException ;
1616import io .vertx .json .schema .ValidationException ;
1717
18+ import java .math .BigDecimal ;
19+
1820public class MultipleOfValidatorFactory implements ValidatorFactory {
1921
2022 @ Override
2123 public Validator createValidator (JsonObject schema , JsonPointer scope , SchemaParserInternal parser , MutableStateValidator parent ) {
2224 try {
2325 Number multipleOf = (Number ) schema .getValue ("multipleOf" );
24- return new MultipleOfValidator (multipleOf . doubleValue () );
26+ return new MultipleOfValidator (multipleOf );
2527 } catch (ClassCastException e ) {
2628 throw new SchemaException (schema , "Wrong type for multipleOf keyword" , e );
2729 } catch (NullPointerException e ) {
@@ -35,17 +37,50 @@ public boolean canConsumeSchema(JsonObject schema) {
3537 }
3638
3739 static class MultipleOfValidator extends BaseSyncValidator {
38- private final double multipleOf ;
40+ private final Long multipleOfL ;
41+ private final BigDecimal multipleOfD ;
42+
43+ public MultipleOfValidator (Number multipleOf ) {
44+ // can be null to signal integer arithmetic
45+ multipleOfD = toBigDecimal (multipleOf , false );
46+ if (multipleOfD == null ) {
47+ multipleOfL = multipleOf .longValue ();
48+ } else {
49+ multipleOfL = null ;
50+ }
51+ }
3952
40- public MultipleOfValidator (double multipleOf ) {
41- this .multipleOf = multipleOf ;
53+ private BigDecimal toBigDecimal (Number in , boolean force ) {
54+ if (in instanceof BigDecimal ) {
55+ return (BigDecimal ) in ;
56+ }
57+ if (in instanceof Float ) {
58+ return BigDecimal .valueOf (in .floatValue ());
59+ }
60+ if (in instanceof Double ) {
61+ return BigDecimal .valueOf (in .doubleValue ());
62+ }
63+ if (force ) {
64+ return BigDecimal .valueOf (in .longValue ());
65+ }
66+ return null ;
4267 }
4368
4469 @ Override
4570 public void validateSync (ValidatorContext context , Object in ) throws ValidationException {
4671 if (in instanceof Number ) {
47- if (((Number ) in ).doubleValue () % multipleOf != 0 ) {
48- throw ValidationException .create ("provided number should be multiple of " + multipleOf , "multipleOf" , in );
72+ // floating point arithmetic,
73+ // if the multipleOf is decimal, we always need to handle this operation as decimal
74+ final BigDecimal inBD = toBigDecimal ((Number ) in , multipleOfD != null );
75+ if (inBD != null ) {
76+ if (inBD .remainder (multipleOfD ).compareTo (BigDecimal .ZERO ) != 0 ) {
77+ throw ValidationException .create ("provided number should be multiple of " + multipleOfD , "multipleOf" , in );
78+ }
79+ } else {
80+ // integer arithmetic, fallback for simpler arithmetic
81+ if (((Number ) in ).longValue () % multipleOfL != 0L ) {
82+ throw ValidationException .create ("provided number should be multiple of " + multipleOfL , "multipleOf" , in );
83+ }
4984 }
5085 }
5186 }
0 commit comments