Fix identity-ref value parsing/serialization 71/104871/6
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 6 Jan 2023 15:30:27 +0000 (16:30 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 15 Mar 2023 23:07:33 +0000 (00:07 +0100)
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>
(cherry picked from commit 7c8fd1ae5a6942fb3600ab9d05185cd4293f5f51)

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 cf7388f7e4cd66c26265a69796e1d52026c0a694..2f82dc4c24fdb666f17c476db2b4079c3a176bf4 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.yangtools.yang.data.codec.gson;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
 import com.google.gson.stream.JsonWriter;
@@ -19,6 +18,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;
@@ -31,8 +31,8 @@ abstract class JSONInstanceIdentifierCodec extends AbstractModuleStringInstanceI
 
     JSONInstanceIdentifierCodec(final EffectiveModelContext context, final JSONCodecFactory jsonCodecFactory) {
         this.context = requireNonNull(context);
-        this.dataContextTree = DataSchemaContextTree.from(context);
-        this.codecFactory = requireNonNull(jsonCodecFactory);
+        dataContextTree = DataSchemaContextTree.from(context);
+        codecFactory = requireNonNull(jsonCodecFactory);
     }
 
     @Override
@@ -56,9 +56,13 @@ abstract class JSONInstanceIdentifierCodec extends AbstractModuleStringInstanceI
     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");
-        final JSONCodec<?> objectJSONCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver);
-        return objectJSONCodec.parseValue(null, value);
+        if (schemaNode instanceof LeafSchemaNode) {
+            return codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver).parseValue(null, value);
+        } else if (schemaNode instanceof LeafListSchemaNode) {
+            return codecFactory.codecFor((LeafListSchemaNode) schemaNode, 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..40d19b57b04c92b79cb928be5f56eb8742e51078 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;
@@ -38,8 +38,8 @@ final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanc
 
     XmlStringInstanceIdentifierCodec(final EffectiveModelContext context, final XmlCodecFactory xmlCodecFactory) {
         this.context = requireNonNull(context);
-        this.dataContextTree = DataSchemaContextTree.from(context);
-        this.codecFactory = requireNonNull(xmlCodecFactory);
+        dataContextTree = DataSchemaContextTree.from(context);
+        codecFactory = requireNonNull(xmlCodecFactory);
     }
 
     @Override
@@ -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) {
+            objectXmlCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver);
+        } else if (schemaNode instanceof LeafListSchemaNode) {
+            objectXmlCodec = codecFactory.codecFor((LeafListSchemaNode) schemaNode, resolver);
+        } else {
+            throw new IllegalArgumentException("schemaNode " + schemaNode
+                    + " must be of type LeafSchemaNode or LeafListSchemaNode");
+        }
         return objectXmlCodec.parseValue(getNamespaceContext(), value);
     }
 
index ca94824ef8a5ff0734318914925be3b540a0716a..b887ac786435b82b25f57815d92be1771c47ae37 100644 (file)
@@ -92,7 +92,6 @@ public class YT1473Test {
     }
 
     @Test
-    @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
     public 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 @@ public class YT1473Test {
     }
 
     @Test
-    @Disabled("YT-1473: QName values need to be recognized and properly encoded via identity codec")
     public 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 1ed4172da32c12632d424205cc24c0874ff6bcb4..f441dad6ba6d38e3355505b007fdabd3717244ec 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 implies identity-ref, which can never be escaped
+            return appendQName(sb.append('\''), (QName) value, currentModule).append('\'');
+        }
+
         final var str = String.valueOf(value);
 
         // We have two specifications here: Section 6.1.3 of both RFC6020 and RFC7950:
index 9b2697e40d429538380cd7aff5166ef9a1bc4b3f..8f2d928d4fe200501b24776a11921d627746b32e 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);