Populate data/ hierarchy
[yangtools.git] / data / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / tree / DataNodeContainerModificationStrategy.java
diff --git a/data/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataNodeContainerModificationStrategy.java b/data/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataNodeContainerModificationStrategy.java
new file mode 100644 (file)
index 0000000..f3c08e9
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014 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.data.impl.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.AbstractNodeContainerModificationStrategy.Visible;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base strategy for applying changes to a ContainerNode, irrespective of its
+ * actual type.
+ *
+ * @param <T> Type of the container node
+ */
+class DataNodeContainerModificationStrategy<T extends DataNodeContainer & WithStatus> extends Visible<T> {
+    private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerModificationStrategy.class);
+
+    private final @NonNull DataTreeConfiguration treeConfig;
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<DataNodeContainerModificationStrategy, ImmutableMap> UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(DataNodeContainerModificationStrategy.class, ImmutableMap.class,
+                "children");
+    private volatile ImmutableMap<PathArgument, ModificationApplyOperation> children = ImmutableMap.of();
+
+    DataNodeContainerModificationStrategy(final NormalizedNodeContainerSupport<?, ?> support, final T schema,
+            final DataTreeConfiguration treeConfig) {
+        super(support, treeConfig, schema);
+        this.treeConfig = requireNonNull(treeConfig, "treeConfig");
+    }
+
+    @Override
+    public final ModificationApplyOperation childByArg(final PathArgument arg) {
+        final ImmutableMap<PathArgument, ModificationApplyOperation> local = children;
+        final ModificationApplyOperation existing = local.get(arg);
+        if (existing != null) {
+            return existing;
+        }
+
+        final ModificationApplyOperation childOperation = resolveChild(arg);
+        return childOperation != null ? appendChild(local, arg, childOperation) : null;
+    }
+
+    private ModificationApplyOperation resolveChild(final PathArgument identifier) {
+        final T schema = getSchema();
+        if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
+            return SchemaAwareApplyOperation.from(schema, (AugmentationTarget) schema,
+                (AugmentationIdentifier) identifier, treeConfig);
+        }
+
+        final QName qname = identifier.getNodeType();
+        final Optional<DataSchemaNode> child = schema.findDataChildByName(qname);
+        if (!child.isPresent()) {
+            LOG.trace("Child {} not present in container schema {} children {}", identifier, this,
+                schema.getChildNodes());
+            return null;
+        }
+
+        try {
+            return SchemaAwareApplyOperation.from(child.get(), treeConfig);
+        } catch (ExcludedDataSchemaNodeException e) {
+            LOG.trace("Failed to instantiate child {} in container schema {} children {}", identifier, this,
+                schema.getChildNodes(), e);
+            return null;
+        }
+    }
+
+    private @Nullable ModificationApplyOperation appendChild(
+            final ImmutableMap<PathArgument, ModificationApplyOperation> initial, final PathArgument identifier,
+            final ModificationApplyOperation computed) {
+
+        ImmutableMap<PathArgument, ModificationApplyOperation> previous = initial;
+        while (true) {
+            // Build up a new map based on observed snapshot and computed child
+            final Builder<PathArgument, ModificationApplyOperation> builder = ImmutableMap.builderWithExpectedSize(
+                previous.size() + 1);
+            builder.putAll(previous);
+            builder.put(identifier, computed);
+            final ImmutableMap<PathArgument, ModificationApplyOperation> updated = builder.build();
+
+            // Attempt to install the updated map
+            if (UPDATER.compareAndSet(this, previous, updated)) {
+                return computed;
+            }
+
+            // We have raced, acquire a new snapshot, recheck presence and retry if needed
+            previous = children;
+            final ModificationApplyOperation raced = previous.get(identifier);
+            if (raced != null) {
+                return raced;
+            }
+        }
+    }
+}