8aa7321ed0c15a51a3825a294f15e1bfe0569eb3
[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 java.lang.invoke.MethodHandles;
14 import java.lang.invoke.VarHandle;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
18 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
19 import org.opendaylight.yangtools.yang.data.tree.impl.AbstractNodeContainerModificationStrategy.Visible;
20 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
21 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * Base strategy for applying changes to a ContainerNode, irrespective of its actual type.
27  *
28  * @param <T> Type of the container node
29  */
30 sealed class DataNodeContainerModificationStrategy<T extends DataSchemaNode & DataNodeContainer> extends Visible<T>
31         permits ContainerModificationStrategy, MapEntryModificationStrategy {
32     private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerModificationStrategy.class);
33     private static final VarHandle CHILDREN;
34
35     static {
36         try {
37             CHILDREN = MethodHandles.lookup().findVarHandle(
38                 DataNodeContainerModificationStrategy.class, "children", ImmutableMap.class);
39         } catch (NoSuchFieldException | IllegalAccessException e) {
40             throw new ExceptionInInitializerError(e);
41         }
42     }
43
44     private final @NonNull DataTreeConfiguration treeConfig;
45
46     @SuppressWarnings("unused")
47     private volatile ImmutableMap<PathArgument, ModificationApplyOperation> children = ImmutableMap.of();
48
49     DataNodeContainerModificationStrategy(final NormalizedNodeContainerSupport<?, ?> support, final T schema,
50             final DataTreeConfiguration treeConfig) {
51         super(support, treeConfig, schema);
52         this.treeConfig = requireNonNull(treeConfig, "treeConfig");
53     }
54
55     @Override
56     public final ModificationApplyOperation childByArg(final PathArgument arg) {
57         final var local = (ImmutableMap<PathArgument, ModificationApplyOperation>) CHILDREN.getAcquire(this);
58         final var existing = local.get(arg);
59         if (existing != null) {
60             return existing;
61         }
62
63         final var childOperation = resolveChild(arg);
64         return childOperation != null ? appendChild(local, arg, childOperation) : null;
65     }
66
67     private ModificationApplyOperation resolveChild(final PathArgument identifier) {
68         final var schema = getSchema();
69         final var qname = identifier.getNodeType();
70         final var child = schema.dataChildByName(qname);
71         if (child == null) {
72             LOG.trace("Child {} not present in container schema {} children {}", identifier, this,
73                 schema.getChildNodes());
74             return null;
75         }
76
77         try {
78             return SchemaAwareApplyOperation.from(child, treeConfig);
79         } catch (ExcludedDataSchemaNodeException e) {
80             LOG.trace("Failed to instantiate child {} in container schema {} children {}", identifier, this,
81                 schema.getChildNodes(), e);
82             return null;
83         }
84     }
85
86     private @Nullable ModificationApplyOperation appendChild(
87             final ImmutableMap<PathArgument, ModificationApplyOperation> initial, final PathArgument identifier,
88             final ModificationApplyOperation computed) {
89         var previous = initial;
90         while (true) {
91             // Build up a new map based on observed snapshot and computed child
92             final var updated = ImmutableMap
93                 .<PathArgument, ModificationApplyOperation>builderWithExpectedSize(previous.size() + 1)
94                 .putAll(previous)
95                 .put(identifier, computed)
96                 .build();
97
98             // Attempt to install the updated map
99             final var witness = (ImmutableMap<PathArgument, ModificationApplyOperation>)
100                 CHILDREN.compareAndExchangeRelease(this, previous, updated);
101             if (witness == previous) {
102                 return computed;
103             }
104
105             // We have raced, acquire a new snapshot, recheck presence and retry if needed
106             previous = witness;
107             final var raced = previous.get(identifier);
108             if (raced != null) {
109                 return raced;
110             }
111         }
112     }
113 }