Ignore empty augmentations at runtime
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataObjectCodecContext.java
index 52a7b06be46e728825533b2e1108251549d81ae8..2fa41e9585c2599e14dd84413affe9c588828c1b 100644 (file)
@@ -15,7 +15,6 @@ import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -33,6 +32,7 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
+import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
@@ -158,10 +158,13 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
         this.byBindingArgClass = byStreamClassBuilder.equals(byBindingArgClassBuilder) ? this.byStreamClass
                 : ImmutableMap.copyOf(byBindingArgClassBuilder);
 
-        final Iterable<AugmentRuntimeType> possibleAugmentations;
+        final List<AugmentRuntimeType> possibleAugmentations;
         if (Augmentable.class.isAssignableFrom(bindingClass)) {
+            // Verify we have the appropriate backing runtimeType
             final var type = getType();
-            possibleAugmentations = Iterables.concat(type.augments(), type.mismatchedAugments());
+            verify(type instanceof AugmentableRuntimeType, "Unexpected type %s backing augmenable %s", type,
+                bindingClass);
+            possibleAugmentations = ((AugmentableRuntimeType) type).augments();
             generatedClass = CodecDataObjectGenerator.generateAugmentable(prototype.getFactory().getLoader(),
                 bindingClass, tmpLeaves, tmpDataObjects, keyMethod);
         } else {
@@ -174,14 +177,16 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
         final Map<PathArgument, DataContainerCodecPrototype<?>> augByYang = new HashMap<>();
         final Map<Class<?>, DataContainerCodecPrototype<?>> augByStream = new HashMap<>();
         for (final AugmentRuntimeType augment : possibleAugmentations) {
-            final DataContainerCodecPrototype<?> augProto = getAugmentationPrototype(augment);
-            final PathArgument augYangArg = augProto.getYangArg();
-            if (augByYang.putIfAbsent(augYangArg, augProto) == null) {
-                LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this);
-            }
-            final Class<?> augBindingClass = augProto.getBindingClass();
-            if (augByStream.putIfAbsent(augBindingClass, augProto) == null) {
-                LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this);
+            final DataContainerCodecPrototype<?> augProto = loadAugmentPrototype(augment);
+            if (augProto != null) {
+                final PathArgument augYangArg = augProto.getYangArg();
+                if (augByYang.putIfAbsent(augYangArg, augProto) == null) {
+                    LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this);
+                }
+                final Class<?> augBindingClass = augProto.getBindingClass();
+                if (augByStream.putIfAbsent(augBindingClass, augProto) == null) {
+                    LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this);
+                }
             }
         }
         augmentationByYang = ImmutableMap.copyOf(augByYang);
@@ -383,23 +388,29 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
         return cls.equals(loaded);
     }
 
-    private @NonNull DataContainerCodecPrototype<?> getAugmentationPrototype(final AugmentRuntimeType augment) {
-        final BindingRuntimeContext ctx = factory().getRuntimeContext();
+    private @Nullable DataContainerCodecPrototype<?> loadAugmentPrototype(final AugmentRuntimeType augment) {
+        // FIXME: in face of deviations this code should be looking at declared view, i.e. all possibilities at augment
+        //        declaration site
+        final var possibleChildren = augment.statement()
+            .streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class)
+            .map(SchemaTreeEffectiveStatement::getIdentifier)
+            .collect(ImmutableSet.toImmutableSet());
+        if (possibleChildren.isEmpty()) {
+            return null;
+        }
 
+        final var factory = factory();
         final GeneratedType javaType = augment.javaType();
         final Class<? extends Augmentation<?>> augClass;
         try {
-            augClass = ctx.loadClass(javaType);
+            augClass = factory.getRuntimeContext().loadClass(javaType);
         } catch (final ClassNotFoundException e) {
             throw new IllegalStateException(
                 "RuntimeContext references type " + javaType + " but failed to load its class", e);
         }
 
-        // TODO: at some point we need the effective children
-        return DataContainerCodecPrototype.from(augClass, new AugmentationIdentifier(augment.statement()
-            .streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class)
-            .map(SchemaTreeEffectiveStatement::getIdentifier)
-            .collect(ImmutableSet.toImmutableSet())), augment, factory());
+        return DataContainerCodecPrototype.from(augClass, new AugmentationIdentifier(possibleChildren), augment,
+            factory);
     }
 
     @SuppressWarnings("checkstyle:illegalCatch")
@@ -433,7 +444,8 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
             final var augClass = value.getBindingClass();
             // Do not perform duplicate deserialization if we have already created the corresponding augmentation
             // and validate whether the proposed augmentation is valid ion this instantiation context.
-            if (!map.containsKey(augClass) && getType().augments().contains(value.getType())) {
+            if (!map.containsKey(augClass)
+                && ((AugmentableRuntimeType) getType()).augments().contains(value.getType())) {
                 final NormalizedNode augData = data.childByArg(value.getYangArg());
                 if (augData != null) {
                     // ... make sure we do not replace an e