Split out yang-data-tree-impl
[yangtools.git] / data / yang-data-tree-ri / src / main / java / org / opendaylight / yangtools / yang / data / tree / impl / DataNodeContainerModificationStrategy.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.tree.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableMap.Builder;
14 import java.util.Optional;
15 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
21 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
22 import org.opendaylight.yangtools.yang.data.tree.impl.AbstractNodeContainerModificationStrategy.Visible;
23 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
24 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
25 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * Base strategy for applying changes to a ContainerNode, irrespective of its
32  * actual type.
33  *
34  * @param <T> Type of the container node
35  */
36 class DataNodeContainerModificationStrategy<T extends DataNodeContainer & WithStatus> extends Visible<T> {
37     private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerModificationStrategy.class);
38
39     private final @NonNull DataTreeConfiguration treeConfig;
40
41     @SuppressWarnings("rawtypes")
42     private static final AtomicReferenceFieldUpdater<DataNodeContainerModificationStrategy, ImmutableMap> UPDATER =
43             AtomicReferenceFieldUpdater.newUpdater(DataNodeContainerModificationStrategy.class, ImmutableMap.class,
44                 "children");
45     private volatile ImmutableMap<PathArgument, ModificationApplyOperation> children = ImmutableMap.of();
46
47     DataNodeContainerModificationStrategy(final NormalizedNodeContainerSupport<?, ?> support, final T schema,
48             final DataTreeConfiguration treeConfig) {
49         super(support, treeConfig, schema);
50         this.treeConfig = requireNonNull(treeConfig, "treeConfig");
51     }
52
53     @Override
54     public final ModificationApplyOperation childByArg(final PathArgument arg) {
55         final ImmutableMap<PathArgument, ModificationApplyOperation> local = children;
56         final ModificationApplyOperation existing = local.get(arg);
57         if (existing != null) {
58             return existing;
59         }
60
61         final ModificationApplyOperation childOperation = resolveChild(arg);
62         return childOperation != null ? appendChild(local, arg, childOperation) : null;
63     }
64
65     private ModificationApplyOperation resolveChild(final PathArgument identifier) {
66         final T schema = getSchema();
67         if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
68             return SchemaAwareApplyOperation.from(schema, (AugmentationTarget) schema,
69                 (AugmentationIdentifier) identifier, treeConfig);
70         }
71
72         final QName qname = identifier.getNodeType();
73         final Optional<DataSchemaNode> child = schema.findDataChildByName(qname);
74         if (!child.isPresent()) {
75             LOG.trace("Child {} not present in container schema {} children {}", identifier, this,
76                 schema.getChildNodes());
77             return null;
78         }
79
80         try {
81             return SchemaAwareApplyOperation.from(child.get(), treeConfig);
82         } catch (ExcludedDataSchemaNodeException e) {
83             LOG.trace("Failed to instantiate child {} in container schema {} children {}", identifier, this,
84                 schema.getChildNodes(), e);
85             return null;
86         }
87     }
88
89     private @Nullable ModificationApplyOperation appendChild(
90             final ImmutableMap<PathArgument, ModificationApplyOperation> initial, final PathArgument identifier,
91             final ModificationApplyOperation computed) {
92
93         ImmutableMap<PathArgument, ModificationApplyOperation> previous = initial;
94         while (true) {
95             // Build up a new map based on observed snapshot and computed child
96             final Builder<PathArgument, ModificationApplyOperation> builder = ImmutableMap.builderWithExpectedSize(
97                 previous.size() + 1);
98             builder.putAll(previous);
99             builder.put(identifier, computed);
100             final ImmutableMap<PathArgument, ModificationApplyOperation> updated = builder.build();
101
102             // Attempt to install the updated map
103             if (UPDATER.compareAndSet(this, previous, updated)) {
104                 return computed;
105             }
106
107             // We have raced, acquire a new snapshot, recheck presence and retry if needed
108             previous = children;
109             final ModificationApplyOperation raced = previous.get(identifier);
110             if (raced != null) {
111                 return raced;
112             }
113         }
114     }
115 }