Split out BindingDataObjectCodecTreeNode.streamChild() 93/100093/3
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 12 Mar 2022 13:17:40 +0000 (14:17 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 12 Mar 2022 16:23:33 +0000 (17:23 +0100)
BindingCodecTree does not have a way of entering the tree without
specifying an InstanceIdentifier construct. This is a mistake, as we
need more powerful addressing capabilities to be able to deal with
Notifications and other constructs.

SchemaRootCodecContext is then taught to handle Notifications specially,
now that they cannot legally come from InstanceIdentifier.

JIRA: MDSAL-730
Change-Id: I970cab36d2794472ef801cd0e0d67c264bd169f4
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingCodecTree.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeNode.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java

index 25e5ed54c7c8b20eaff1081559e60679817d2d19..124d9fd3e3b70be8718be73338bd8e1939e151a7 100644 (file)
@@ -12,6 +12,7 @@ import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 
@@ -21,7 +22,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
  * context.
  */
 // TODO: Add more detailed documentation
-public interface BindingCodecTree {
+public interface BindingCodecTree extends BindingDataObjectCodecTreeParent<Empty> {
 
     @Nullable <T extends DataObject> BindingDataObjectCodecTreeNode<T> getSubtreeCodec(InstanceIdentifier<T> path);
 
index 2dc3b398eeb5c248a0a74a1d724becc6d2a1505a..8d8a32f5efa2358a3643f6e6896b89588866824d 100644 (file)
@@ -16,12 +16,13 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.binding.BindingObject;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 
 @Beta
-public interface BindingDataObjectCodecTreeNode<T extends DataObject> extends BindingObjectCodecTreeNode<T>,
-        BindingNormalizedNodeCodec<T> {
+public interface BindingDataObjectCodecTreeNode<T extends DataObject>
+        extends BindingDataObjectCodecTreeParent<Empty>, BindingObjectCodecTreeNode<T>, BindingNormalizedNodeCodec<T> {
 
     /**
      * Returns binding class of interface which represents API of current schema node. The result is same as invoking
@@ -32,17 +33,6 @@ public interface BindingDataObjectCodecTreeNode<T extends DataObject> extends Bi
     @Override
     @NonNull Class<T> getBindingClass();
 
-    /**
-     * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case,
-     * one must issue getChild(ChoiceClass).getChild(CaseClass).
-     *
-     * @param childClass Child class by Binding Stream navigation
-     * @return Context of child
-     * @throws IllegalArgumentException
-     *             If supplied child class is not valid in specified context.
-     */
-    <E extends DataObject> @NonNull BindingDataObjectCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
-
     /**
      * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case,
      * one must issue getChild(ChoiceClass).getChild(CaseClass).
diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java
new file mode 100644 (file)
index 0000000..2e561e5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.api;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Common interface for entities which can supply a {@link BindingDataObjectCodecTreeNode} based on Binding DataObject
+ * class instance.
+ *
+ * @param <T> Dummy parameter to work around problems with streaming {@link ChoiceIn} classes. Essentially we want
+ *            {@link #streamChild(Class)} to also service such classes, which are not {@link DataObject}s. The problem
+ *            really is that {@code case} interfaces are DataObjects and hence are an alluring target for that method.
+ *            The workaround works with two sides:
+ *            <ul>
+ *              <li>Here the fact that we are generic means that binary compatibility dictates that our signature be
+ *                  backwards compatible with anyone who might have seen us as non-generic, i.e. in streamChild() taking
+ *                  a raw class (because there were no generics)</li>
+ *              <li>Users pick it up from there: all they need to do is to go to raw types, then accepts any class, but
+ *                  from there we can just chain on method return, arriving into a type-safe world again</li>
+ *            </ul>
+ */
+@Beta
+// FIXME: Now the above documentation is fine and dandy, but can we adjust the shape of our classes somehow?
+//        The problem seems to be grouping interfaces, which are pulling in 'DataObject' to the picture and thus
+//        and up marking case statements even if we do not mark them specially. If we could disconnect concrete cases
+//        from DataObject in a civil manner, then we could go the DataObject -> ChoiceIn -> CaseOf and not have
+//        choice/case statements in the picture. But what would that do to all the concepts hanging off of DataObject?
+public interface BindingDataObjectCodecTreeParent<T> {
+    /**
+     * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case,
+     * one must issue {@code streamChild(ChoiceClass).streamChild(CaseClass)}.
+     *
+     * @param <E> Stream child DataObject type
+     * @param childClass Child class by Binding Stream navigation
+     * @return Context of child
+     * @throws IllegalArgumentException If supplied child class is not valid in specified context.
+     */
+    <E extends DataObject> @NonNull BindingDataObjectCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
+}
index 167e65fb0da4931248ceca60081cd7eb19d56cc8..cc28ef3b80ee3110547c5b629417a41b55ecba40 100644 (file)
@@ -187,4 +187,9 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject
     public BindingRuntimeContext getRuntimeContext() {
         return delegate().getRuntimeContext();
     }
+
+    @Override
+    public <E extends DataObject> BindingDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
+        return delegate().streamChild(childClass);
+    }
 }
index a8537dab27dbc4a5e8abbf4f773299ad059dceaa..94bc76c79c9b586f45fc4e6ce17eb059d48018ae 100644 (file)
@@ -484,8 +484,13 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
         return IdentifiableItemCodec.of(type.statement(), identifier, listClz, valueCtx);
     }
 
-    @SuppressWarnings("unchecked")
     @Override
+    public <E extends DataObject> BindingDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
+        return root.streamChild(childClass);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
     public <T extends DataObject> BindingDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
         // TODO Do we need defensive check here?
         return (BindingDataObjectCodecTreeNode<T>) getCodecContextNode(path, null);
index 8b80d4152ba5e6b6f7f51a5bef96aab6d44cc41a..bc1b2d4b8d53a3548b273a2886cd24c6d06d7876 100644 (file)
@@ -225,10 +225,12 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         return getType().getEffectiveModelContext();
     }
 
-    @SuppressWarnings("unchecked")
     @Override
+    @SuppressWarnings("unchecked")
     public <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
-        return (DataContainerCodecContext<C, ?>) getOrRethrow(childrenByClass, childClass);
+        final DataContainerCodecContext<?, ?> result = Notification.class.isAssignableFrom(childClass)
+            ? getNotificationImpl(childClass) : getOrRethrow(childrenByClass, childClass);
+        return (DataContainerCodecContext<C, ?>) result;
     }
 
     @Override