From 510b213cd6b505aed7edd3febfbb058e48bcc4e9 Mon Sep 17 00:00:00 2001 From: ramya vasanth Date: Mon, 9 Sep 2019 10:51:15 +0530 Subject: [PATCH] [OLINGO-1393]Validate the values for a Decimal data type as per latest spec --- .../olingo/fit/tecsvc/client/BasicITCase.java | 12 +-- .../commons/api/edm/EdmPrimitiveType.java | 18 +++- .../core/edm/primitivetype/EdmDecimal.java | 61 ++++++++++++- .../edm/primitivetype/EdmDecimalTest.java | 91 +++++++++++++++++-- .../primitivetype/PrimitiveTypeBaseTest.java | 2 +- 5 files changed, 168 insertions(+), 16 deletions(-) diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java index aa1fea56d..1295c4d09 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java @@ -1275,7 +1275,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { entity.getProperties().add(getFactory().newPrimitiveProperty(PROPERTY_INT64, getFactory().newPrimitiveValueBuilder().buildInt64(Long.MAX_VALUE))); entity.getProperties().add(getFactory().newPrimitiveProperty(PROPERTY_DECIMAL, - getFactory().newPrimitiveValueBuilder().buildDecimal(BigDecimal.valueOf(Long.MAX_VALUE)))); + getFactory().newPrimitiveValueBuilder().buildDecimal(BigDecimal.valueOf(922337203685477L)))); final ODataEntityUpdateRequest requestUpdate = getEdmEnabledClient().getCUDRequestFactory() .getEntityUpdateRequest(uri, UpdateType.PATCH, entity); @@ -1292,7 +1292,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { final ODataRetrieveResponse responseGet = requestGet.execute(); assertEquals(Long.MAX_VALUE, responseGet.getBody().getProperty(PROPERTY_INT64).getPrimitiveValue().toValue()); - assertEquals(BigDecimal.valueOf(Long.MAX_VALUE), responseGet.getBody().getProperty(PROPERTY_DECIMAL) + assertEquals(BigDecimal.valueOf(922337203685477L), responseGet.getBody().getProperty(PROPERTY_DECIMAL) .getPrimitiveValue() .toValue()); } @@ -1395,7 +1395,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { .add(getFactory().newPrimitiveProperty(PROPERTY_INT64, getFactory().newPrimitiveValueBuilder().buildInt64(Long.MIN_VALUE))) .add(getFactory().newPrimitiveProperty(PROPERTY_DECIMAL, - getFactory().newPrimitiveValueBuilder().buildDecimal(BigDecimal.valueOf(12345678912L)))) + getFactory().newPrimitiveValueBuilder().buildDecimal(BigDecimal.valueOf(123456.78912)))) .add(getFactory().newPrimitiveProperty(PROPERTY_INT16, getFactory().newPrimitiveValueBuilder().buildInt16((short) 2))))); @@ -1413,7 +1413,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { final ClientComplexValue complexValue = responseGet.getBody().getComplexValue(); assertEquals(Long.MIN_VALUE, complexValue.get(PROPERTY_INT64).getPrimitiveValue().toValue()); - assertEquals(BigDecimal.valueOf(12345678912L), complexValue.get(PROPERTY_DECIMAL).getPrimitiveValue().toValue()); + assertEquals(BigDecimal.valueOf(123456.78912), complexValue.get(PROPERTY_DECIMAL).getPrimitiveValue().toValue()); assertEquals(2, complexValue.get(PROPERTY_INT16).getPrimitiveValue().toValue()); } @@ -1428,7 +1428,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { final ODataPropertyUpdateRequest requestUpdate = getEdmEnabledClient().getCUDRequestFactory() .getPropertyPrimitiveValueUpdateRequest(uri, getFactory().newPrimitiveProperty(PROPERTY_DECIMAL, - getFactory().newPrimitiveValueBuilder().buildInt64(Long.MAX_VALUE))); + getFactory().newPrimitiveValueBuilder().buildInt64(922337203685477L))); requestUpdate.setContentType(CONTENT_TYPE_JSON_IEEE754_COMPATIBLE); requestUpdate.setAccept(CONTENT_TYPE_JSON_IEEE754_COMPATIBLE); @@ -1441,7 +1441,7 @@ public class BasicITCase extends AbstractParamTecSvcITCase { requestGet.setAccept(CONTENT_TYPE_JSON_IEEE754_COMPATIBLE); final ODataRetrieveResponse responseGet = requestGet.execute(); - assertEquals(BigDecimal.valueOf(Long.MAX_VALUE), responseGet.getBody().getPrimitiveValue().toValue()); + assertEquals(BigDecimal.valueOf(922337203685477L), responseGet.getBody().getPrimitiveValue().toValue()); } @Test diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/EdmPrimitiveType.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/EdmPrimitiveType.java index 8e4b6b5d9..ebe9cd7b5 100644 --- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/EdmPrimitiveType.java +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/EdmPrimitiveType.java @@ -72,7 +72,7 @@ package org.apache.olingo.commons.api.edm; public interface EdmPrimitiveType extends EdmType { String EDM_NAMESPACE = "Edm"; - + /** * Checks type compatibility. * @@ -164,4 +164,20 @@ public interface EdmPrimitiveType extends EdmType { * @throws EdmPrimitiveTypeException if a required prefix or required surrounding quotation marks are missing */ String fromUriLiteral(String literal) throws EdmPrimitiveTypeException; + + /** + * Validates literal value for Decimal values in V4.01 + * + * @param value the literal value + * @param isNullable whether the null value is allowed + * @param maxLength the maximum length + * @param precision the precision + * @param scale the scale (could be variable or floating) + * @param isUnicode whether non-ASCII characters are allowed (relevant only for Edm.String) + * @return true if the validation is successful + */ + default boolean validateDecimals(String value, Boolean isNullable, Integer maxLength, + Integer precision, String scale, Boolean isUnicode) { + return false; + } } diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimal.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimal.java index 8b9f8f95f..e177d7d7f 100644 --- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimal.java +++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimal.java @@ -83,8 +83,65 @@ public final class EdmDecimal extends SingletonPrimitiveType { } final int significantIntegerDigits = "0".equals(matcher.group(1)) ? 0 : matcher.group(1).length(); final int decimals = matcher.group(2) == null ? 0 : matcher.group(2).length(); - return (precision == null || precision >= significantIntegerDigits + decimals) - && (decimals <= (scale == null ? 0 : scale)); + return (precision == null || (significantIntegerDigits >= 0 && + significantIntegerDigits <= precision - ((scale == null) ? 0 : scale))) && + (decimals >= 0 && decimals <= ((scale == null) ? 0 : scale)); + } + + @Override + public boolean validateDecimals(final String value, + final Boolean isNullable, final Integer maxLength, final Integer precision, + final String scale, final Boolean isUnicode) { + + return value == null + ? isNullable == null || isNullable + : validateLiteral(value) && validatePrecisionAndScale(value, precision, scale); + } + + private boolean validatePrecisionAndScale(String value, Integer precision, String scale) { + Matcher matcher = PATTERN.matcher(value); + matcher.matches(); + if (matcher.group(3) != null) { + String plainValue = new BigDecimal(value).toPlainString(); + matcher = PATTERN.matcher(plainValue); + matcher.matches(); + } + int significantIntegerDigits = "0".equals(matcher.group(1)) ? 0 : matcher.group(1).length(); + int decimals = matcher.group(2) == null ? 0 : matcher.group(2).length(); + + try { + int scaleValue = (scale == null) ? 0 : Integer.parseInt(scale); + return (precision == null || (significantIntegerDigits >= 0 && + significantIntegerDigits <= precision - scaleValue)) && + (decimals >= 0 && decimals <= scaleValue); + } catch(NumberFormatException e) { + String scaleValue = (scale == null) ? String.valueOf(0) : scale; + if (scaleValue.equals("variable")) { + return (precision == null || + (significantIntegerDigits >= 0 && + (significantIntegerDigits <= precision - decimals))) && + (decimals >= 0 && decimals <= ((precision == null) ? 0 : precision)); + } else if (scaleValue.equals("floating")) { + Matcher matcher1 = PATTERN.matcher(value); + matcher1.matches(); + significantIntegerDigits = "0".equals(matcher1.group(1)) ? 0 : matcher1.group(1).length(); + decimals = matcher1.group(2) == null ? 0 : matcher1.group(2).length(); + int exponents = 0; + if (matcher1.group(3) != null) { + exponents = Integer.parseInt(matcher1.group(3).substring(1)); + if (exponents < -95 || exponents > 96) { + if (String.valueOf(exponents).startsWith("-")) { + significantIntegerDigits += Integer.parseInt(String.valueOf(exponents + 95).substring(1)); + exponents = -95; + } + } + return (significantIntegerDigits + decimals) <= 7 && (exponents >= -95 && exponents <= 96); + } + } else { + return false; + } + } + return false; } @Override diff --git a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimalTest.java b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimalTest.java index 3fb1c344c..14ec1106b 100644 --- a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimalTest.java +++ b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/EdmDecimalTest.java @@ -19,6 +19,7 @@ package org.apache.olingo.commons.core.edm.primitivetype; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; @@ -97,11 +98,10 @@ public class EdmDecimalTest extends PrimitiveTypeBaseTest { assertEquals(Double.valueOf(0.5), instance.valueOfString("0.5", null, null, 1, 1, null, Double.class)); assertEquals(Float.valueOf(0.5F), instance.valueOfString("0.5", null, null, null, 1, null, Float.class)); assertEquals(new BigDecimal("12.3"), instance.valueOfString("12.3", null, null, 3, 1, null, BigDecimal.class)); - assertEquals(new BigDecimal("31991163"), instance.valueOfString("3.1991163E7", null, null, 8, 7, - null, BigDecimal.class)); assertEquals(new BigDecimal("31991163.34"), instance.valueOfString("3.199116334E7", null, null, 10, 2, null, BigDecimal.class)); + expectFacetsErrorInValueOfString(instance, "3.1991163E7", null, 8, 7, null, null); expectFacetsErrorInValueOfString(instance, "0.5", null, null, null, null, null); expectFacetsErrorInValueOfString(instance, "-1234", null, null, 2, null, null); expectFacetsErrorInValueOfString(instance, "1234", null, null, 3, null, null); @@ -110,6 +110,9 @@ public class EdmDecimalTest extends PrimitiveTypeBaseTest { expectFacetsErrorInValueOfString(instance, "12.34", null, null, 4, 1, null); expectFacetsErrorInValueOfString(instance, "0.00390625", null, null, 5, null, null); expectFacetsErrorInValueOfString(instance, "0.00390625", null, null, null, 7, null); + expectFacetsErrorInValueOfString(instance, "-129", null, null, Integer.MAX_VALUE, Integer.MAX_VALUE, null); + expectFacetsErrorInValueOfString(instance, "-32769", null, null, Integer.MAX_VALUE, Integer.MAX_VALUE, null); + expectFacetsErrorInValueOfString(instance, "32768", null, null, Integer.MAX_VALUE, Integer.MAX_VALUE, null); expectContentErrorInValueOfString(instance, "1."); expectContentErrorInValueOfString(instance, ".1"); @@ -117,10 +120,6 @@ public class EdmDecimalTest extends PrimitiveTypeBaseTest { expectContentErrorInValueOfString(instance, "1M"); expectContentErrorInValueOfString(instance, "0x42"); - expectUnconvertibleErrorInValueOfString(instance, "-129", Byte.class); - expectUnconvertibleErrorInValueOfString(instance, "128", Byte.class); - expectUnconvertibleErrorInValueOfString(instance, "-32769", Short.class); - expectUnconvertibleErrorInValueOfString(instance, "32768", Short.class); expectUnconvertibleErrorInValueOfString(instance, "-2147483649", Integer.class); expectUnconvertibleErrorInValueOfString(instance, "2147483648", Integer.class); expectUnconvertibleErrorInValueOfString(instance, "-9223372036854775809", Long.class); @@ -130,4 +129,84 @@ public class EdmDecimalTest extends PrimitiveTypeBaseTest { expectTypeErrorInValueOfString(instance, "1"); } + + @Test + public void validateDecimal() throws Exception { + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("3.1991163E7", null, null, 8, 7, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("1", null, null, null, null, null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("1.2", null, null, null, 0, null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("10.2", false, null, 3, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("1.2", false, null, 3, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("0.12", false, null, 3, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("1.23", false, null, 3, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("1.03", false, null, 3, 2, null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("1.03", false, null, 3, null, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("12", false, null, 3, null, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("12", false, null, null, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("0.12", false, null, null, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("12", false, null, null, null, null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("0.12", false, null, null, null, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("0.23", false, null, 2, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("0.7", false, null, 2, 2, null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("12e-101", false, null, 2, 2, null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("12e101", false, null, 2, 2, null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("-1.234567e3", false, null, 7, 3, null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validate("0.7", false, null, 2, 3, null)); + } + + @Test + public void validateDecimalInV401() throws Exception { + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("0.123", null, null, 3, "variable", null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("1.23", null, null, 3, "variable", null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("0.23", null, null, 3, "variable", null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("0.7", null, null, 3, "variable", null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("123", null, null, 3, "variable", null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("12.3", null, null, 3, "variable", null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("12.34", null, null, 3, "variable", null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("1234", null, null, 3, "variable", null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("123.4", null, null, 3, "variable", null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("123.4", null, null, null, "variable", null)); + + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("-1.234567e3", null, null, 7, "floating", null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("1e-101", null, null, 7, "floating", null)); + assertTrue(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("9.999999e96", null, null, 7, "floating", null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("1e-102", null, null, 7, "floating", null)); + assertFalse(EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal). + validateDecimals("1e97", null, null, 7, "floating", null)); + + } } diff --git a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/PrimitiveTypeBaseTest.java b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/PrimitiveTypeBaseTest.java index 40152c929..5d5851c25 100644 --- a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/PrimitiveTypeBaseTest.java +++ b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/edm/primitivetype/PrimitiveTypeBaseTest.java @@ -85,7 +85,7 @@ public abstract class PrimitiveTypeBaseTest { protected void expectUnconvertibleErrorInValueOfString(final EdmPrimitiveType instance, final String value, final Class type) { - expectErrorInValueOfString(instance, value, true, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, true, + expectErrorInValueOfString(instance, value, true, Integer.MAX_VALUE, Integer.MAX_VALUE, null, true, type, "cannot be converted to"); }