Fix StringStringCodec length check 39/95339/2
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 24 Feb 2021 08:47:30 +0000 (09:47 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 24 Feb 2021 10:07:26 +0000 (11:07 +0100)
RFC7950 specifies that string length is counted in unicode
characters. String.length() returns the length in code units of
UTF-16, which are not the same thing.

Use String.codePointCount() to get correct results for strings
containing characters from outside of Unicode BMP.

JIRA: YANGTOOLS-1224
Change-Id: I6ff9557d61449625be975eaca00ad235bf429155
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit cbadacd601e11552b749edfaaa19b64b9804e55f)

yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/StringStringCodec.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/StringCodecStringTest.java

index 15a5b9c6d454117b512aef2bc85e9bc95919cffe..cb34f1cc4a0fc64cb90eca742801da78e36d51a5 100644 (file)
@@ -49,7 +49,7 @@ public class StringStringCodec extends TypeDefinitionAwareCodec<String, StringTy
     void validate(final String str) {
         if (lengthConstraint != null) {
             final RangeSet<Integer> ranges = lengthConstraint.getAllowedRanges();
-            if (!ranges.contains(str.length())) {
+            if (!ranges.contains(str.codePointCount(0, str.length()))) {
                 throw new YangInvalidValueException(ErrorType.PROTOCOL, lengthConstraint,
                     "String " + str + " does not match allowed lengths " + ranges);
             }
index 564d3437cce18b8654202c8006311f427956bceb..7554be63de7cd1baf9bd97a5e7802ab11cf3b103 100644 (file)
@@ -9,10 +9,20 @@
 package org.opendaylight.yangtools.yang.data.impl.codec;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
 
+import java.util.List;
 import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
 import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.InvalidLengthConstraintException;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.model.util.type.StringTypeBuilder;
 
 /**
  * Unit tests for StringCodecString.
@@ -20,8 +30,6 @@ import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
  * @author Thomas Pantelis
  */
 public class StringCodecStringTest {
-
-    @SuppressWarnings("unchecked")
     @Test
     public void testSerialize() {
         StringCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.stringType(),
@@ -31,7 +39,6 @@ public class StringCodecStringTest {
         assertEquals("serialize", "", codec.serialize(""));
     }
 
-    @SuppressWarnings("unchecked")
     @Test
     public void testDeserialize() {
         StringCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.stringType(),
@@ -40,4 +47,16 @@ public class StringCodecStringTest {
         assertEquals("deserialize", "bar", codec.deserialize("bar"));
         assertEquals("deserialize", "", codec.deserialize(""));
     }
+
+    @Test
+    public void testDeserializeUnicode() throws InvalidLengthConstraintException {
+        final StringTypeBuilder builder = RestrictedTypes.newStringBuilder(BaseTypes.stringType(),
+            SchemaPath.create(true, QName.create("foo", "foo")));
+        builder.setLengthConstraint(mock(ConstraintMetaDefinition.class), List.of(ValueRange.of(1)));
+        final StringTypeDefinition type = builder.build();
+
+        StringCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(type, StringCodec.class);
+
+        assertEquals("🌞", codec.deserialize("🌞"));
+    }
 }