Bug 7037 - Improve mapping of YANG extensions 17/55717/11
authorPeter Kajsa <pkajsa@cisco.com>
Thu, 20 Apr 2017 09:49:14 +0000 (11:49 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 5 Jun 2017 07:55:05 +0000 (09:55 +0200)
Each extension definition is able to create an unknown statement
form of a regular yang statement. Created definitions of regular yang
statements are created only once per each yang statement and extension
definition. So we do not create a new ModelDefinedStatementDefinition
and a corresponding UnknownStatementImpl.Definition for each instance
of a regular yang statement when it is placed in an unknown statement's
body.

Change-Id: I5613eb36273e6b512637fb14456c64b023df3f1f
Signed-off-by: Peter Kajsa <pkajsa@cisco.com>
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementDefinitionContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ModelDefinedStatementDefinition.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ModelDefinedStatementSupport.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/UnknownStatementImpl.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug1412Test.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7037Test.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bugs/bug7037/bar.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bugs/bug7037/foo.yang [new file with mode: 0644]

index 4ee6c84c1ea124cd2ad6485ee0ea9c68c22f4f48..4066ad684b85ccfb7541b9a2a55edc7ecfb36dc5 100644 (file)
@@ -20,9 +20,9 @@ import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementDefinitionContext;
 
 /**
- *
  * Support for processing concrete YANG statement.
  *
+ * <p>
  * This interface is intended to be implemented by developers, which want to
  * introduce support of statement to parser. Consider subclassing
  * {@link AbstractStatementSupport} for easier implementation of this interface.
@@ -189,4 +189,19 @@ public interface StatementSupport<A, D extends DeclaredStatement<A>, E extends E
     default String internArgument(final String rawArgument) {
         return rawArgument;
     }
+
+    /**
+     * Returns unknown statement form of a regular yang statement supplied as
+     * parameter to the method.
+     *
+     * @param yangStmtDef
+     *            statement definition of a regular yang statement
+     * @return Optional of unknown statement form of a regular yang statement or
+     *         Optional.empty() if it is not supported by this statement support
+     */
+    default Optional<StatementDefinitionContext<?, ?, ?>> getUnknownStatementDefinitionOf(
+            final StatementDefinitionContext<?, ?, ?> yangStmtDef) {
+        return Optional.empty();
+    }
 }
+
index 322b05ffb624c31fbdba2158b6771772896b664c..c16dc4d924a163802c93d3a77c6347b3931e846d 100644 (file)
@@ -75,6 +75,7 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
             .add(ModelProcessingPhase.FULL_DECLARATION).add(ModelProcessingPhase.EFFECTIVE_MODEL).build();
 
     private final Table<YangVersion, QName, StatementDefinitionContext<?, ?, ?>> definitions = HashBasedTable.create();
+    private final Map<QName, StatementDefinitionContext<?, ?, ?>> modelDefinedStmtDefs = new HashMap<>();
     private final Map<Class<?>, NamespaceBehaviourWithListeners<?, ?, ?>> supportedNamespaces = new HashMap<>();
     private final List<MutableStatement> mutableStatementsToSeal = new ArrayList<>();
     private final Map<ModelProcessingPhase, StatementSupportBundle> supports;
@@ -203,6 +204,14 @@ class BuildGlobalContext extends NamespaceStorageSupport implements NamespaceBeh
         return potential;
     }
 
+    StatementDefinitionContext<?, ?, ?> getModelDefinedStatementDefinition(final QName name) {
+        return modelDefinedStmtDefs.get(name);
+    }
+
+    void putModelDefinedStatementDefinition(final QName name, final StatementDefinitionContext<?, ?, ?> def) {
+        modelDefinedStmtDefs.put(name, def);
+    }
+
     private void executePhases() throws ReactorException {
         for (final ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
             startPhase(phase);
index ee72425c428943737255952f4491ce053c896b6d..3cbb6d389a203488c48a547fbb3a47cb46f88fba 100644 (file)
@@ -41,6 +41,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Storag
 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitionNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToModuleContext;
 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleIdentifier;
 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
@@ -54,9 +55,6 @@ import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinit
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.ModelDefinedStatementDefinition;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl;
-import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -113,22 +111,22 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh
 
         StatementDefinitionContext<?, ?, ?> def = currentContext.getStatementDefinition(getRootVersion(), name);
         if (def == null) {
-            final StatementSupport<?, ?, ?> extension = qNameToStmtDefMap.get(name);
-            if (extension != null) {
-                def = new StatementDefinitionContext<>(extension);
+            def = currentContext.getModelDefinedStatementDefinition(name);
+            if (def == null) {
+                final StatementSupport<?, ?, ?> extension = qNameToStmtDefMap.get(name);
+                if (extension != null) {
+                    def = new StatementDefinitionContext<>(extension);
+                    currentContext.putModelDefinedStatementDefinition(name, def);
+                }
             }
-        } else if (current != null && current.definition().getRepresentingClass().equals(UnknownStatementImpl.class)) {
+        } else if (current != null && StmtContextUtils.isUnknownStatement(current)) {
             /*
-             * This code wraps statements encountered inside an extension so they do not get confused with regular
-             * statements.
-             *
-             * FIXME: BUG-7037: re-evaluate whether this is really needed, as this is a very expensive way of making
-             *        this work. We really should be peeking into the extension definition to find these nodes,
-             *        as otherwise we are not reusing definitions nor support for these nodes.
+             * This code wraps statements encountered inside an extension so
+             * they do not get confused with regular statements.
              */
-            final QName qName = Utils.qNameFromArgument(current, name.getLocalName());
-            def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition(
-                new ModelDefinedStatementDefinition(qName, argument != null)));
+            def = Preconditions.checkNotNull(current.definition().getAsUnknownStatementDefinition(def),
+                    "Unable to create unknown statement definition of yang statement %s in unknown statement %s", def,
+                    current);
         }
 
         InferenceException.throwIfNull(def, ref, "Statement %s does not have type mapping defined.", name);
index bbd883938ed2ea4e177cff754872ad9f40184f0c..c805c66c137e1084ae15dd56f190642d22187842 100644 (file)
@@ -29,6 +29,7 @@ import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReferenc
 public class StatementDefinitionContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> {
     private final StatementSupport<A, D, E> support;
     private final Map<String, StatementDefinitionContext<?, ?, ?>> argumentSpecificSubDefinitions;
+    private Map<StatementDefinitionContext<?,?,?>, StatementDefinitionContext<?,?,?>> unknownStmtDefsOfYangStmts;
 
     public StatementDefinitionContext(final StatementSupport<A, D, E> support) {
         this.support = Preconditions.checkNotNull(support);
@@ -89,10 +90,18 @@ public class StatementDefinitionContext<A, D extends DeclaredStatement<A>, E ext
         return support.getArgumentName() != null;
     }
 
+    public boolean isArgumentYinElement() {
+        return support.isArgumentYinElement();
+    }
+
     public QName getStatementName() {
         return support.getStatementName();
     }
 
+    public QName getArgumentName() {
+        return support.getArgumentName();
+    }
+
     @Override
     public final String toString() {
         return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
@@ -103,7 +112,7 @@ public class StatementDefinitionContext<A, D extends DeclaredStatement<A>, E ext
     }
 
     @Nonnull
-    public StatementDefinitionContext<?, ?, ?> getSubDefinitionSpecificForArgument(final String argument) {
+    StatementDefinitionContext<?, ?, ?> getSubDefinitionSpecificForArgument(final String argument) {
         if (!hasArgumentSpecificSubDefinitions()) {
             return this;
         }
@@ -119,11 +128,30 @@ public class StatementDefinitionContext<A, D extends DeclaredStatement<A>, E ext
         return potential;
     }
 
-    public boolean hasArgumentSpecificSubDefinitions() {
+    boolean hasArgumentSpecificSubDefinitions() {
         return support.hasArgumentSpecificSupports();
     }
 
     String internArgument(final String rawArgument) {
         return support.internArgument(rawArgument);
     }
+
+    StatementDefinitionContext<?, ?, ?> getAsUnknownStatementDefinition(
+            final StatementDefinitionContext<?, ?, ?> yangStmtDef) {
+        if (unknownStmtDefsOfYangStmts == null) {
+            unknownStmtDefsOfYangStmts = new HashMap<>();
+        }
+
+        StatementDefinitionContext<?, ?, ?> ret = unknownStmtDefsOfYangStmts.get(yangStmtDef);
+        if (ret != null) {
+            return ret;
+        }
+
+        ret = support.getUnknownStatementDefinitionOf(yangStmtDef).orElse(null);
+
+        if (ret != null) {
+            unknownStmtDefsOfYangStmts.put(yangStmtDef, ret);
+        }
+        return ret;
+    }
 }
index 4c8827c32f3eab19351a87601d7aeb6dba605297..90435323af76c60c10927d6b2b1c06120736b84f 100644 (file)
@@ -34,11 +34,6 @@ public final class ModelDefinedStatementDefinition implements StatementDefinitio
         this.yinElement = yinElement;
     }
 
-    @Deprecated
-    public ModelDefinedStatementDefinition(final QName qname, final boolean hasArgument) {
-        this(qname, hasArgument ? qname : null, false);
-    }
-
     @Nonnull
     @Override
     public QName getStatementName() {
index d1e269771b3ebb569801843c70e8afcf69e0d447..6f616ef099cc8e246187cb5548c325da1a0e8345 100644 (file)
@@ -7,12 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
 
+import java.util.Optional;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
 import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator;
 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementDefinitionContext;
 
 /**
  * StatementSupport for statements defined via YANG extensions. This is implemented by piggy-backing
@@ -49,4 +51,10 @@ public final class ModelDefinedStatementSupport extends AbstractStatementSupport
     protected SubstatementValidator getSubstatementValidator() {
         return null;
     }
+
+    @Override
+    public Optional<StatementDefinitionContext<?, ?, ?>> getUnknownStatementDefinitionOf(
+            final StatementDefinitionContext<?, ?, ?> yangStmtDef) {
+        return definition.getUnknownStatementDefinitionOf(yangStmtDef);
+    }
 }
index 47e74fc20764dfe599ff4ce27f19fc719d0464b5..0295f09c11a483be4265b557c66e940edb9afd28 100644 (file)
@@ -7,7 +7,9 @@
  */
 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
 
+import java.util.Optional;
 import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
@@ -15,6 +17,7 @@ import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator;
 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementDefinitionContext;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.UnknownEffectiveStatementImpl;
 
 public class UnknownStatementImpl extends AbstractDeclaredStatement<String> implements UnknownStatement<String> {
@@ -50,6 +53,18 @@ public class UnknownStatementImpl extends AbstractDeclaredStatement<String> impl
         protected SubstatementValidator getSubstatementValidator() {
             return null;
         }
+
+        @Override
+        public Optional<StatementDefinitionContext<?, ?, ?>> getUnknownStatementDefinitionOf(
+                final StatementDefinitionContext<?, ?, ?> yangStmtDef) {
+            final QName baseQName = getStatementName();
+            return Optional.of(new StatementDefinitionContext<>(
+                    new ModelDefinedStatementSupport(new ModelDefinedStatementDefinition(
+                            QName.create(baseQName, yangStmtDef.getStatementName().getLocalName()),
+                            yangStmtDef.hasArgument()
+                                    ? QName.create(baseQName, yangStmtDef.getArgumentName().getLocalName()) : null,
+                            yangStmtDef.isArgumentYinElement()))));
+        }
     }
 
     @Nullable
index dc34e0af79b19f62bc752f092c0bd371b3c85572..43f475617c54d6b46a3aa4b38a8ea36aca0a6b4c 100644 (file)
@@ -83,7 +83,7 @@ public class Bug1412Test {
         assertEquals(expectedNodeType, info.getNodeType());
         assertEquals("greeting", info.getNodeParameter());
 
-        expectedNodeType = QName.create(qm, "description");
+        expectedNodeType = QName.create("urn:test:bug1412:ext:definitions", "2014-07-25", "description");
         assertEquals(expectedNodeType, description.getNodeType());
         assertEquals("say greeting", description.getNodeParameter());
 
@@ -91,7 +91,7 @@ public class Bug1412Test {
         assertEquals(expectedNodeType, actionPoint.getNodeType());
         assertEquals("entry", actionPoint.getNodeParameter());
 
-        expectedNodeType = QName.create(qm, "output");
+        expectedNodeType = QName.create("urn:test:bug1412:ext:definitions", "2014-07-25", "output");
         assertEquals(expectedNodeType, output.getNodeType());
         assertEquals("", output.getNodeParameter());
     }
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7037Test.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7037Test.java
new file mode 100644 (file)
index 0000000..c10a155
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 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
+ */
+
+package org.opendaylight.yangtools.yang.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public class Bug7037Test {
+    private static final String FOO_NS = "foo";
+    private static final String BAR_NS = "bar";
+    private static final String REV = "1970-01-01";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug7037");
+        assertNotNull(context);
+
+        final List<UnknownSchemaNode> unknownSchemaNodes = context.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+
+        final UnknownSchemaNode first = unknownSchemaNodes.iterator().next();
+        final List<UnknownSchemaNode> firstUnknownNodes = first.getUnknownSchemaNodes();
+        assertEquals(1, firstUnknownNodes.size());
+
+        final UnknownSchemaNode barExtCont = firstUnknownNodes.iterator().next();
+        assertEquals(bar("container"), barExtCont.getNodeType());
+        assertEquals(foo("bar-ext-con"), barExtCont.getQName());
+
+        final DataSchemaNode root = context.getDataChildByName(foo("root"));
+        assertTrue(root instanceof ContainerSchemaNode);
+
+        final List<UnknownSchemaNode> rootUnknownNodes = root.getUnknownSchemaNodes();
+        assertEquals(2, rootUnknownNodes.size());
+
+        final Map<QName, UnknownSchemaNode> rootUnknownNodeMap = rootUnknownNodes.stream()
+                .collect(Collectors.toMap(u -> u.getNodeType(), u -> u));
+
+        final UnknownSchemaNode barExt = rootUnknownNodeMap.get(bar("bar-ext"));
+        final List<UnknownSchemaNode> barExtUnknownNodes = barExt.getUnknownSchemaNodes();
+        assertEquals(3, barExtUnknownNodes.size());
+
+        final Iterator<UnknownSchemaNode> iterator = barExtUnknownNodes.iterator();
+        UnknownSchemaNode barExtCont2 = null;
+        while(iterator.hasNext()) {
+            final UnknownSchemaNode next = iterator.next();
+            if(bar("container").equals(next.getNodeType())) {
+                barExtCont2 = next;
+                break;
+            }
+        }
+        assertNotNull(barExtCont2);
+        assertEquals(foo("bar-ext-con-2"), barExtCont2.getQName());
+
+        final UnknownSchemaNode fooExt = rootUnknownNodeMap.get(foo("foo-ext"));
+        final List<UnknownSchemaNode> fooUnknownNodes = fooExt.getUnknownSchemaNodes();
+        assertEquals(1, fooUnknownNodes.size());
+
+        final UnknownSchemaNode fooExtCont = fooUnknownNodes.iterator().next();
+        assertEquals(foo("container"), fooExtCont.getNodeType());
+        assertEquals(foo("foo-ext-con"), fooExtCont.getQName());
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, REV, localName);
+    }
+
+    private static QName bar(final String localName) {
+        return QName.create(BAR_NS, REV, localName);
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/bugs/bug7037/bar.yang b/yang/yang-parser-impl/src/test/resources/bugs/bug7037/bar.yang
new file mode 100644 (file)
index 0000000..99f7acd
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    extension bar-ext {
+        argument arg;
+    }
+
+    extension bar-ext-2 {
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/bugs/bug7037/foo.yang b/yang/yang-parser-impl/src/test/resources/bugs/bug7037/foo.yang
new file mode 100644 (file)
index 0000000..81262a4
--- /dev/null
@@ -0,0 +1,33 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar { prefix bar; revision-date 1970-01-01; }
+
+    extension foo-ext {
+        argument arg;
+    }
+
+    bar:bar-ext "first" {
+        container bar-ext-con {
+        }
+    }
+
+    container root {
+        bar:bar-ext "bar" {
+            container bar-ext-con-2 {
+            }
+            bar:bar-ext "sub-bar" {
+                container bar-ext-con-3 {
+                }
+            }
+            bar:bar-ext-2 {
+                container bar-ext-2-con {
+                }
+            }
+        }
+        foo:foo-ext "foo" {
+            container foo-ext-con;
+        }
+    }
+}