Migrate getDataChildByName() users
[yangtools.git] / yang / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / CompositeNodeDataWithSchema.java
index 58f1975cd72037c667a028c979a2bd1572a41237..8d6bf20a5db4acb109bf9b9af1194dd1b2e4575c 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.data.util;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import com.google.common.annotations.Beta;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
 import java.io.IOException;
@@ -17,14 +18,17 @@ import java.util.Collection;
 import java.util.Deque;
 import java.util.List;
 import java.util.Map.Entry;
-import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyXmlSchemaNode;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerLike;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
@@ -38,6 +42,66 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
  * Represents a node which is composed of multiple simpler nodes.
  */
 public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends AbstractNodeDataWithSchema<T> {
+    /**
+     * Policy on how child nodes should be treated when an attempt is made to add them multiple times.
+     */
+    @Beta
+    public enum ChildReusePolicy {
+        /**
+         * Do not consider any existing nodes at all, just perform a straight append. Multiple occurrences of a child
+         * will result in multiple children being emitted. This is almost certainly the wrong policy unless the caller
+         * prevents such a situation from arising via some different mechanism.
+         */
+        NOOP,
+        /**
+         * Do not allow duplicate definition of a child node. This would typically be used when a child cannot be
+         * encountered multiple times, but the caller does not make any provision to detect such a conflict. If a child
+         * node would end up being defined a second time, {@link DuplicateChildNodeRejectedException} is reported.
+         */
+        REJECT {
+            @Override
+            AbstractNodeDataWithSchema<?> appendChild(final Collection<AbstractNodeDataWithSchema<?>> view,
+                    final AbstractNodeDataWithSchema<?> newChild) {
+                final DataSchemaNode childSchema = newChild.getSchema();
+                final AbstractNodeDataWithSchema<?> existing = findExistingChild(view, childSchema);
+                if (existing != null) {
+                    throw new DuplicateChildNodeRejectedException("Duplicate child " + childSchema.getQName());
+                }
+                return super.appendChild(view, newChild);
+            }
+        },
+        /**
+         * Reuse previously-defined child node. This is most appropriate when a child may be visited multiple times
+         * and the intent is to append content of each visit. A typical usage is list elements with RFC7950 XML
+         * encoding, where there is no encapsulating element and hence list entries may be interleaved with other
+         * children.
+         */
+        REUSE {
+            @Override
+            AbstractNodeDataWithSchema<?> appendChild(final Collection<AbstractNodeDataWithSchema<?>> view,
+                    final AbstractNodeDataWithSchema<?> newChild) {
+                final AbstractNodeDataWithSchema<?> existing = findExistingChild(view, newChild.getSchema());
+                return existing != null ? existing : super.appendChild(view, newChild);
+            }
+        };
+
+        AbstractNodeDataWithSchema<?> appendChild(final Collection<AbstractNodeDataWithSchema<?>> view,
+                final AbstractNodeDataWithSchema<?> newChild) {
+            view.add(newChild);
+            return newChild;
+        }
+
+        static @Nullable AbstractNodeDataWithSchema<?> findExistingChild(
+                final Collection<AbstractNodeDataWithSchema<?>> view, final DataSchemaNode childSchema) {
+            for (AbstractNodeDataWithSchema<?> existing : view) {
+                if (childSchema.equals(existing.getSchema())) {
+                    return existing;
+                }
+            }
+            return null;
+        }
+    }
+
     /**
      * nodes which were added to schema via augmentation and are present in data input.
      */
@@ -53,23 +117,19 @@ public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends Abstr
         super(schema);
     }
 
-    private AbstractNodeDataWithSchema<?> addChild(final DataSchemaNode schema) {
-        AbstractNodeDataWithSchema<?> newChild = addSimpleChild(schema);
-        return newChild == null ? addCompositeChild(schema) : newChild;
-    }
-
-    public void addChild(final AbstractNodeDataWithSchema<?> newChild) {
+    void addChild(final AbstractNodeDataWithSchema<?> newChild) {
         children.add(newChild);
     }
 
-    public AbstractNodeDataWithSchema<?> addChild(final Deque<DataSchemaNode> schemas) {
+    public final AbstractNodeDataWithSchema<?> addChild(final Deque<DataSchemaNode> schemas,
+            final ChildReusePolicy policy) {
         checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
 
         // Pop the first node...
         final DataSchemaNode schema = schemas.pop();
         if (schemas.isEmpty()) {
             // Simple, direct node
-            return addChild(schema);
+            return addChild(schema, policy);
         }
 
         // The choice/case mess, reuse what we already popped
@@ -83,9 +143,11 @@ public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends Abstr
             caseCandidate.getClass());
         final CaseSchemaNode caseNode = (CaseSchemaNode) caseCandidate;
 
-        AugmentationSchemaNode augSchema = null;
+        final AugmentationSchemaNode augSchema;
         if (choiceCandidate.isAugmenting()) {
             augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
+        } else {
+            augSchema = null;
         }
 
         // looking for existing choice
@@ -100,30 +162,42 @@ public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends Abstr
         if (caseNodeDataWithSchema == null) {
             ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
             childNodes.add(choiceNodeDataWithSchema);
-            caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
+            caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode, ChildReusePolicy.NOOP);
         }
 
-        return caseNodeDataWithSchema.addChild(schemas);
+        return caseNodeDataWithSchema.addChild(schemas, policy);
+    }
+
+    private AbstractNodeDataWithSchema<?> addChild(final DataSchemaNode schema, final ChildReusePolicy policy) {
+        AbstractNodeDataWithSchema<?> newChild = addSimpleChild(schema, policy);
+        return newChild == null ? addCompositeChild(schema, policy) : newChild;
     }
 
-    private AbstractNodeDataWithSchema<?> addSimpleChild(final DataSchemaNode schema) {
-        SimpleNodeDataWithSchema<?> newChild = null;
+    private AbstractNodeDataWithSchema<?> addSimpleChild(final DataSchemaNode schema, final ChildReusePolicy policy) {
+        final SimpleNodeDataWithSchema<?> newChild;
         if (schema instanceof LeafSchemaNode) {
             newChild = new LeafNodeDataWithSchema((LeafSchemaNode) schema);
-        } else if (schema instanceof AnyXmlSchemaNode) {
-            // YangModeledAnyXmlSchemaNode is handled by addCompositeChild method.
-            if (schema instanceof YangModeledAnyXmlSchemaNode) {
+        } else if (schema instanceof AnyxmlSchemaNode) {
+            // YangModeledAnyxmlSchemaNode is handled by addCompositeChild method.
+            if (schema instanceof YangModeledAnyxmlSchemaNode) {
                 return null;
             }
-            newChild = new AnyXmlNodeDataWithSchema((AnyXmlSchemaNode) schema);
+            newChild = new AnyXmlNodeDataWithSchema((AnyxmlSchemaNode) schema);
+        } else if (schema instanceof AnydataSchemaNode) {
+            newChild = new AnydataNodeDataWithSchema((AnydataSchemaNode) schema);
         } else {
             return null;
         }
 
-        AugmentationSchemaNode augSchema = null;
+        final AugmentationSchemaNode augSchema;
         if (schema.isAugmenting()) {
             augSchema = findCorrespondingAugment(getSchema(), schema);
+        } else {
+            augSchema = null;
         }
+
+        // FIXME: 7.0.0: use policy to determine if we should reuse or replace the child
+
         if (augSchema != null) {
             augmentationsToChild.put(augSchema, newChild);
         } else {
@@ -152,32 +226,31 @@ public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends Abstr
         return null;
     }
 
-    AbstractNodeDataWithSchema<?> addCompositeChild(final DataSchemaNode schema) {
+    AbstractNodeDataWithSchema<?> addCompositeChild(final DataSchemaNode schema, final ChildReusePolicy policy) {
         final CompositeNodeDataWithSchema<?> newChild;
 
         if (schema instanceof ListSchemaNode) {
             newChild = new ListNodeDataWithSchema((ListSchemaNode) schema);
         } else if (schema instanceof LeafListSchemaNode) {
             newChild = new LeafListNodeDataWithSchema((LeafListSchemaNode) schema);
-        } else if (schema instanceof ContainerSchemaNode) {
-            newChild = new ContainerNodeDataWithSchema((ContainerSchemaNode) schema);
-        } else if (schema instanceof YangModeledAnyXmlSchemaNode) {
-            newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyXmlSchemaNode)schema);
+        } else if (schema instanceof ContainerLike) {
+            newChild = new ContainerNodeDataWithSchema((ContainerLike) schema);
+        } else if (schema instanceof YangModeledAnyxmlSchemaNode) {
+            newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyxmlSchemaNode)schema);
         } else {
             newChild = new CompositeNodeDataWithSchema<>(schema);
         }
 
-        addCompositeChild(newChild);
-        return newChild;
+        return addCompositeChild(newChild, policy);
     }
 
-    void addCompositeChild(final CompositeNodeDataWithSchema<?> newChild) {
-        AugmentationSchemaNode augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
-        if (augSchema != null) {
-            augmentationsToChild.put(augSchema, newChild);
-        } else {
-            addChild(newChild);
-        }
+    final AbstractNodeDataWithSchema<?> addCompositeChild(final CompositeNodeDataWithSchema<?> newChild,
+            final ChildReusePolicy policy) {
+        final AugmentationSchemaNode augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
+        final Collection<AbstractNodeDataWithSchema<?>> view = augSchema == null ? children
+                : augmentationsToChild.get(augSchema);
+
+        return policy.appendChild(view, newChild);
     }
 
     /**
@@ -189,9 +262,10 @@ public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends Abstr
     }
 
     @Override
-    public void write(final NormalizedNodeStreamWriter writer) throws IOException {
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
         for (AbstractNodeDataWithSchema<?> child : children) {
-            child.write(writer);
+            child.write(writer, metaWriter);
         }
         for (Entry<AugmentationSchemaNode, Collection<AbstractNodeDataWithSchema<?>>> augmentationToChild
                 : augmentationsToChild.asMap().entrySet()) {
@@ -202,7 +276,7 @@ public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends Abstr
                     augmentationToChild.getKey()));
 
                 for (AbstractNodeDataWithSchema<?> nodeDataWithSchema : childsFromAgumentation) {
-                    nodeDataWithSchema.write(writer);
+                    nodeDataWithSchema.write(writer, metaWriter);
                 }
 
                 writer.endNode();
@@ -222,7 +296,7 @@ public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends Abstr
             final DataSchemaNode child) {
         if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) {
             for (AugmentationSchemaNode augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
-                DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
+                DataSchemaNode childInAugmentation = augmentation.dataChildByName(child.getQName());
                 if (childInAugmentation != null) {
                     return augmentation;
                 }