MDSAL-298: properly handle unkeyed lists
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / DataContainerCodecPrototype.java
index 4560bd6274822ce1a3d6c1db3819c7a2eff40940..88b5cd984d538a169d70f69b6e3b4b18ce642457 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.collect.Iterables;
 import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode.ChildAddressabilitySummary;
 import org.opendaylight.yangtools.binding.data.codec.impl.NodeCodecContext.CodecContextFactory;
 import org.opendaylight.yangtools.yang.binding.DataRoot;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
@@ -18,16 +19,23 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.AnyDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 final class DataContainerCodecPrototype<T> implements NodeContextSupplier {
+    private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
 
     private final T schema;
     private final QNameModule namespace;
@@ -35,7 +43,9 @@ final class DataContainerCodecPrototype<T> implements NodeContextSupplier {
     private final Class<?> bindingClass;
     private final InstanceIdentifier.Item<?> bindingArg;
     private final YangInstanceIdentifier.PathArgument yangArg;
-    private volatile DataContainerCodecContext<?,T> instance = null;
+    private final ChildAddressabilitySummary childAddressabilitySummary;
+
+    private volatile DataContainerCodecContext<?, T> instance = null;
 
     @SuppressWarnings({"rawtypes", "unchecked"})
     private DataContainerCodecPrototype(final Class<?> cls, final YangInstanceIdentifier.PathArgument arg, final T nodeSchema,
@@ -52,6 +62,81 @@ final class DataContainerCodecPrototype<T> implements NodeContextSupplier {
         } else {
             this.namespace = arg.getNodeType().getModule();
         }
+
+        this.childAddressabilitySummary = computeChildAddressabilitySummary(nodeSchema);
+    }
+
+    private static ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
+        if (nodeSchema instanceof DataNodeContainer) {
+            boolean haveAddressable = false;
+            boolean haveUnaddressable = false;
+            for (DataSchemaNode child : ((DataNodeContainer) nodeSchema).getChildNodes()) {
+                if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchema) {
+                    haveAddressable = true;
+                } else if (child instanceof ListSchemaNode) {
+                    if (((ListSchemaNode) child).getKeyDefinition().isEmpty()) {
+                        haveUnaddressable = true;
+                    } else {
+                        haveAddressable = true;
+                    }
+                } else if (child instanceof AnyDataSchemaNode || child instanceof AnyXmlSchemaNode
+                        || child instanceof TypedSchemaNode) {
+                    haveUnaddressable = true;
+                } else if (child instanceof ChoiceSchemaNode) {
+                    switch (computeChildAddressabilitySummary(child)) {
+                        case ADDRESSABLE:
+                            haveAddressable = true;
+                            break;
+                        case MIXED:
+                            haveAddressable = true;
+                            haveUnaddressable = true;
+                            break;
+                        case UNADDRESSABLE:
+                            haveUnaddressable = true;
+                            break;
+                        default:
+                            throw new IllegalStateException("Unhandled accessibility summary for " + child);
+                    }
+                } else {
+                    LOG.warn("Unhandled child node {}", child);
+                }
+            }
+
+            if (!haveAddressable) {
+                // Empty or all are unaddressable
+                return ChildAddressabilitySummary.UNADDRESSABLE;
+            }
+
+            return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
+        } else if (nodeSchema instanceof ChoiceSchemaNode) {
+            boolean haveAddressable = false;
+            boolean haveUnaddressable = false;
+            for (ChoiceCaseNode child : ((ChoiceSchemaNode) nodeSchema).getCases()) {
+                switch (computeChildAddressabilitySummary(child)) {
+                    case ADDRESSABLE:
+                        haveAddressable = true;
+                        break;
+                    case UNADDRESSABLE:
+                        haveUnaddressable = true;
+                        break;
+                    case MIXED:
+                        // A child is mixed, which means we are mixed, too
+                        return ChildAddressabilitySummary.MIXED;
+                    default:
+                        throw new IllegalStateException("Unhandled accessibility summary for " + child);
+                }
+            }
+
+            if (!haveAddressable) {
+                // Empty or all are unaddressable
+                return ChildAddressabilitySummary.UNADDRESSABLE;
+            }
+
+            return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
+        }
+
+        // No child nodes possible: return unaddressable
+        return ChildAddressabilitySummary.UNADDRESSABLE;
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -63,7 +148,7 @@ final class DataContainerCodecPrototype<T> implements NodeContextSupplier {
     static DataContainerCodecPrototype<SchemaContext> rootPrototype(final CodecContextFactory factory) {
         final SchemaContext schema = factory.getRuntimeContext().getSchemaContext();
         final NodeIdentifier arg = NodeIdentifier.create(schema.getQName());
-        return new DataContainerCodecPrototype<SchemaContext>(DataRoot.class, arg, schema, factory);
+        return new DataContainerCodecPrototype<>(DataRoot.class, arg, schema, factory);
     }
 
 
@@ -75,13 +160,17 @@ final class DataContainerCodecPrototype<T> implements NodeContextSupplier {
 
     static DataContainerCodecPrototype<NotificationDefinition> from(final Class<?> augClass, final NotificationDefinition schema, final CodecContextFactory factory) {
         final PathArgument arg = NodeIdentifier.create(schema.getQName());
-        return new DataContainerCodecPrototype<NotificationDefinition>(augClass,arg, schema, factory);
+        return new DataContainerCodecPrototype<>(augClass,arg, schema, factory);
     }
 
     protected T getSchema() {
         return schema;
     }
 
+    ChildAddressabilitySummary getChildAddressabilitySummary() {
+        return childAddressabilitySummary;
+    }
+
     protected QNameModule getNamespace() {
         return namespace;
     }