Fix identity-ref value parsing/serialization 44/103944/18
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 6 Jan 2023 15:30:27 +0000 (16:30 +0100)
committerRobert Varga <nite@hq.sk>
Wed, 15 Mar 2023 13:17:16 +0000 (13:17 +0000)
When encountering a QName value in a leaf, a leaf-list or implied as the
value of a key, we need to properly encode it. The same is true when
parsing, where we have to consult current namespace mapping.

As a side-effect of this, we are also fixing the case of a leaf-list
entry referenced in a path argument -- which is important for all
non-String types.

JIRA: YANGTOOLS-1473
Change-Id: Id50ebd9a3c0c1378f1af8451b86c66e323757eba
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
codec/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java
codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1473Test.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java
data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java
data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java

index 17832f940ed0e08fee90fee1c66723c0afac3d33..ad89e039f6f767413dbebc399f5eb34c423f6e18 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIde
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
@@ -82,8 +83,13 @@ abstract sealed class JSONInstanceIdentifierCodec extends AbstractModuleStringIn
     protected final Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver,
             final String value) {
         requireNonNull(schemaNode, "schemaNode cannot be null");
-        checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode");
-        return codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver).parseValue(null, value);
+        if (schemaNode instanceof LeafSchemaNode leafSchemaNode) {
+            return codecFactory.codecFor(leafSchemaNode, resolver).parseValue(null, value);
+        } else if (schemaNode instanceof LeafListSchemaNode leafListSchemaNode) {
+            return codecFactory.codecFor(leafListSchemaNode, resolver).parseValue(null, value);
+        }
+        throw new IllegalArgumentException("schemaNode " + schemaNode
+                + " must be of type LeafSchemaNode or LeafListSchemaNode");
     }
 
     @Override
index 48edc81796b15209f6f193cad41eb14355418c03..52e0fed7325cc0900c0e64d2fa302dca85203eb7 100644 (file)
@@ -92,13 +92,11 @@ class YT1473Test {
     }
 
     @Test
-    @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
     void testSerializeIdentityRefSame() throws Exception {
         assertSerdes("/foo:bar[qname='one']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE));
     }
 
     @Test
-    @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
     void testSerializeIdentityRefOther() throws Exception {
         // No escaping is needed, use double quotes and escape
         assertSerdes("/foo:bar[qname='bar:two']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO));
@@ -112,7 +110,6 @@ class YT1473Test {
     }
 
     @Test
-    @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
     void testSerializeIdentityValue() throws Exception {
         assertSerdes("/bar:foo[.='foo:one']", buildYangInstanceIdentifier(BAR_FOO, FOO_ONE));
         assertSerdes("/bar:foo[.='two']", buildYangInstanceIdentifier(BAR_FOO, BAR_TWO));
index 4f74ecb4f02fb0d245255e2aa97fc4ab577baa28..1b24568963080f1c236867ee014faa80238bf6c5 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.yangtools.yang.data.codec.xml;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
 import java.util.ArrayDeque;
@@ -23,6 +22,7 @@ import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIde
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
@@ -64,8 +64,15 @@ final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanc
     protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver,
             final String value) {
         requireNonNull(schemaNode, "schemaNode cannot be null");
-        checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode");
-        final XmlCodec<?> objectXmlCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver);
+        final XmlCodec<?> objectXmlCodec;
+        if (schemaNode instanceof LeafSchemaNode leafSchemaNode) {
+            objectXmlCodec = codecFactory.codecFor(leafSchemaNode, resolver);
+        } else if (schemaNode instanceof LeafListSchemaNode leafListSchemaNode) {
+            objectXmlCodec = codecFactory.codecFor(leafListSchemaNode, resolver);
+        } else {
+            throw new IllegalArgumentException("schemaNode " + schemaNode
+                    + " must be of type LeafSchemaNode or LeafListSchemaNode");
+        }
         return objectXmlCodec.parseValue(getNamespaceContext(), value);
     }
 
index 7bef13a35ea9de8b3d34045228004ff0fcf23a9a..51aaf19e594a35046ce1fb8264af54577120c8c3 100644 (file)
@@ -92,7 +92,6 @@ class YT1473Test {
     }
 
     @Test
-    @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
     void testSerializeIdentity() throws Exception {
         assertSerdes("/foo:bar[foo:qname='foo:one']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, FOO_ONE));
         assertSerdes("/foo:bar[foo:qname='bar:two']", buildYangInstanceIdentifier(FOO_BAR, FOO_QNAME, BAR_TWO));
@@ -106,7 +105,6 @@ class YT1473Test {
     }
 
     @Test
-    @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
     void testSerializeIdentityValue() throws Exception {
         assertSerdes("/bar:foo[.='foo:one']", buildYangInstanceIdentifier(BAR_FOO, FOO_ONE));
         assertSerdes("/bar:foo[.='bar:two']", buildYangInstanceIdentifier(BAR_FOO, BAR_TWO));
index 8ba7d0aef80b1f50a92ae5234a66eeedc6b4c924..a89ae431a80f5e54267618e893039a8df3fc4cd4 100644 (file)
@@ -79,8 +79,13 @@ public abstract class AbstractStringInstanceIdentifierCodec extends AbstractName
         return sb.toString();
     }
 
-    private static StringBuilder appendValue(final StringBuilder sb, final QNameModule currentModule,
+    private StringBuilder appendValue(final StringBuilder sb, final QNameModule currentModule,
             final Object value) {
+        if (value instanceof QName qname) {
+            // QName implies identity-ref, which can never be escaped
+            return appendQName(sb.append('\''), qname, currentModule).append('\'');
+        }
+
         final var str = String.valueOf(value);
 
         // We have two specifications here: Section 6.1.3 of both RFC6020 and RFC7950:
index 84000eea48959da96950ee3fac854e38dd9aa0e1..a4ac597e3c3c2aeabbdf8bfef9d8c6ead69aff3d 100644 (file)
@@ -160,7 +160,9 @@ final class XpathStringParsingPathArgumentBuilder implements Mutable {
             // Break-out from method for leaf-list case
             if (key == null && currentNode.isLeaf()) {
                 checkValid(offset == data.length(), "Leaf argument must be last argument of instance identifier.");
-                return new NodeWithValue<>(name, keyValue);
+                final Object value = codec.deserializeKeyValue(currentNode.getDataSchemaNode(),
+                    type -> resolveLeafref(currentNode.getIdentifier().getNodeType(), type), keyValue);
+                return new NodeWithValue<>(name, value);
             }
             final DataSchemaContextNode<?> keyNode = currentNode.getChild(key);
             checkValid(keyNode != null, "%s is not correct schema node identifier.", key);