Merge "Bug 1781: Fix serialization of leafrefs"
authorTony Tkacik <ttkacik@cisco.com>
Wed, 19 Nov 2014 14:02:57 +0000 (14:02 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 19 Nov 2014 14:02:57 +0000 (14:02 +0000)
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtilsTest.java
yang/yang-data-impl/src/test/resources/leafref-test.yang [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java

index 88e202d5cf7988fb5d7b51784cd772bfe22c6e41..c4787d5ba2dc4cad26a01e3695da3861e8502d38 100644 (file)
@@ -9,29 +9,62 @@
 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+
 import com.google.common.base.Optional;
 import com.google.common.collect.Maps;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.AbstractMap;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamWriter;
 import org.custommonkey.xmlunit.Diff;
 import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.util.StringType;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.w3c.dom.Document;
 
 public class XmlStreamUtilsTest {
 
     public static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
 
+    private static SchemaContext schemaContext;
+    private static Module leafRefModule;
+
+    @BeforeClass
+    public static void initialize() throws URISyntaxException {
+        final YangParserImpl yangParser = new YangParserImpl();
+        final File file = new File(XmlStreamUtils.class.getResource("/leafref-test.yang").toURI());
+        schemaContext = yangParser.parseFiles(Arrays.asList(file));
+        assertNotNull(schemaContext);
+        assertEquals(1,schemaContext.getModules().size());
+        leafRefModule = schemaContext.getModules().iterator().next();
+        assertNotNull(leafRefModule);
+    }
+
+
     @Test
     public void testWriteAttribute() throws Exception {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -69,6 +102,50 @@ public class XmlStreamUtilsTest {
         assertTrue("Xml differs: " + diff.toString(), identical);
     }
 
+    /**
+     * One leafref reference to other leafref via relative references
+     */
+    @Test
+    public void testLeafRefRelativeChaining() {
+        getTargetNodeForLeafRef("leafname3",StringType.class);
+    }
+
+    @Test
+    public void testLeafRefRelative() {
+        getTargetNodeForLeafRef("pointToStringLeaf",StringType.class);
+    }
+
+    @Test
+    public void testLeafRefAbsoluteWithSameTarget() {
+        getTargetNodeForLeafRef("absname",InstanceIdentifierType.class);
+    }
+
+    /**
+     * Tests relative path with double point inside path (e. g. "../../lf:interface/../lf:cont2/lf:stringleaf")
+     */
+    @Ignore //ignored because this isn't implemented
+    @Test
+    public void testLeafRefWithDoublePointInPath() {
+        getTargetNodeForLeafRef("lf-with-double-point-inside",StringType.class);
+    }
+
+    @Test
+    public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
+        final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname",InstanceIdentifierType.class);
+        final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname",InstanceIdentifierType.class);
+        assertEquals(targetNodeForAbsname, targetNodeForRelname);
+    }
+
+    private TypeDefinition<?> getTargetNodeForLeafRef(final String nodeName, final Class<?> clas) {
+        final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName);
+        assertNotNull(schemaNode);
+        final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode);
+        assertNotNull(leafrefTypedef);
+        final TypeDefinition<?> targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext, schemaNode);
+        assertEquals("Wrong class found.", clas, targetBaseType.getClass());
+        return targetBaseType;
+    }
+
     @Test
     public void testEmptyNodeWithAttribute() throws Exception {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -115,4 +192,29 @@ public class XmlStreamUtilsTest {
             return QName.create(namespace, revision, localName);
         }
     }
-}
+
+    private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) {
+        for (final DataSchemaNode childNode : module.getChildNodes()) {
+            if (childNode instanceof DataNodeContainer) {
+                LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer)childNode, nodeName);
+                if (leafrefFromRecursion != null) {
+                    return leafrefFromRecursion;
+                }
+            } else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) {
+                final TypeDefinition<?> leafSchemaNodeType = ((LeafSchemaNode)childNode).getType();
+                if (leafSchemaNodeType instanceof LeafrefTypeDefinition) {
+                    return (LeafSchemaNode)childNode;
+                }
+            }
+        }
+        return null;
+    }
+
+    private LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
+        final TypeDefinition<?> type = schemaNode.getType();
+        if (type instanceof LeafrefTypeDefinition) {
+            return (LeafrefTypeDefinition)type;
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/leafref-test.yang b/yang/yang-data-impl/src/test/resources/leafref-test.yang
new file mode 100644 (file)
index 0000000..3d31c89
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module leafref-test {
+    yang-version 1;
+    namespace "urn:opendaylight:yangtools:leafref:test";
+    prefix "lt";
+
+    revision 2014-11-04 {
+        description "Test deserialization value of leafref type.";
+    }
+
+    container interface {
+        leaf simpleValue {
+            type instance-identifier;
+        }
+    }
+
+    container cont2 {
+        container cont3 {
+            leaf leafname3 {
+                type leafref {
+                    path "../../pointToStringLeaf";
+                }
+            }
+        }
+        leaf pointToStringLeaf {
+            type leafref {
+                path "../stringleaf";
+            }
+        }
+        leaf stringleaf {
+            type string;
+        }
+        leaf absname {
+            type leafref {
+                path "/lt:interface/lt:simpleValue";
+            }
+        }
+        leaf relname {
+            type leafref {
+                path "../../lt:interface/lt:simpleValue";
+            }
+        }
+
+        leaf lf-with-double-point-inside {
+            type leafref {
+                path "../../lt:interface/../lt:cont2/lt:stringleaf";
+            }
+        }
+    }
+}
\ No newline at end of file
index 2fecd7f84904cf543095a7854d017313ac02af95..f451b4155e1ec323f6a592c87a7bc35440dcebea 100644 (file)
@@ -571,32 +571,43 @@ public final class SchemaContextUtil {
      *            Yang Module
      * @param relativeXPath
      *            Non conditional Revision Aware Relative XPath
-     * @param leafrefSchemaPath
-     *            Schema Path for Leafref
+     * @param actualSchemaNode
+     *            actual schema node
      * @return list of QName
      */
     private static Iterable<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
-            final RevisionAwareXPath relativeXPath, final SchemaNode leafrefParentNode) {
+            final RevisionAwareXPath relativeXPath, final SchemaNode actualSchemaNode) {
         Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
         Preconditions.checkArgument(module != null, "Module reference cannot be NULL");
         Preconditions.checkArgument(relativeXPath != null, "Non Conditional Revision Aware XPath cannot be NULL");
         Preconditions.checkState(!relativeXPath.isAbsolute(),
                 "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
                         + "for non relative Revision Aware XPath use findDataSchemaNode method");
-        Preconditions.checkState(leafrefParentNode.getPath() != null,
+        Preconditions.checkState(actualSchemaNode.getPath() != null,
                 "Schema Path reference for Leafref cannot be NULL");
 
         final Iterable<String> xpaths = SLASH_SPLITTER.split(relativeXPath.toString());
 
         // Find out how many "parent" components there are
         // FIXME: is .contains() the right check here?
+        // FIXME: case ../../node1/node2/../node3/../node4
         int colCount = 0;
         for (Iterator<String> it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) {
             ++colCount;
         }
 
-        final Iterable<QName> parent = leafrefParentNode.getPath().getPathFromRoot();
-        return Iterables.concat(Iterables.limit(parent, Iterables.size(parent) - colCount),
+        final Iterable<QName> schemaNodePath = actualSchemaNode.getPath().getPathFromRoot();
+
+        if (Iterables.size(schemaNodePath) - colCount >= 0) {
+            return Iterables.concat(Iterables.limit(schemaNodePath, Iterables.size(schemaNodePath) - colCount),
+                    Iterables.transform(Iterables.skip(xpaths, colCount), new Function<String, QName>() {
+                        @Override
+                        public QName apply(final String input) {
+                            return stringPathPartToQName(context, module, input);
+                        }
+                    }));
+        }
+        return Iterables.concat(schemaNodePath,
                 Iterables.transform(Iterables.skip(xpaths, colCount), new Function<String, QName>() {
                     @Override
                     public QName apply(final String input) {
@@ -627,9 +638,7 @@ public final class SchemaContextUtil {
         if(pathStatement.isAbsolute()) {
             dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule, pathStatement);
         } else {
-            SchemaPath parentSchemaPath = schema.getPath().getParent();
-            SchemaNode parentSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, parentSchemaPath);
-            dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule, parentSchemaNode, pathStatement);
+            dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule, schema, pathStatement);
         }
 
         final TypeDefinition<?> targetTypeDefinition = typeDefinition(dataSchemaNode);