00d752b194a922dce7398da506d6fad3e2407500
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / tree / 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.impl.schema.tree;
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.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
21 import org.opendaylight.yangtools.yang.data.impl.schema.tree.AbstractNodeContainerModificationStrategy.Visible;
22 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
23 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * Base strategy for applying changes to a ContainerNode, irrespective of its
31  * actual type.
32  *
33  * @param <T> Type of the container node
34  */
35 class DataNodeContainerModificationStrategy<T extends DataNodeContainer & WithStatus> extends Visible<T> {
36     private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerModificationStrategy.class);
37
38     private final @NonNull DataTreeConfiguration treeConfig;
39
40     @SuppressWarnings("rawtypes")
41     private static final AtomicReferenceFieldUpdater<DataNodeContainerModificationStrategy, ImmutableMap> UPDATER =
42             AtomicReferenceFieldUpdater.newUpdater(DataNodeContainerModificationStrategy.class, ImmutableMap.class,
43                 "children");
44     private volatile ImmutableMap<PathArgument, ModificationApplyOperation> children = ImmutableMap.of();
45
46     DataNodeContainerModificationStrategy(final NormalizedNodeContainerSupport<?, ?> support, final T schema,
47             final DataTreeConfiguration treeConfig) {
48         super(support, treeConfig, schema);
49         this.treeConfig = requireNonNull(treeConfig, "treeConfig");
50     }
51
52     @Override
53     public final Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
54         final ImmutableMap<PathArgument, ModificationApplyOperation> local = children;
55         final ModificationApplyOperation existing = local.get(identifier);
56         if (existing != null) {
57             return Optional.of(existing);
58         }
59
60         final ModificationApplyOperation childOperation = resolveChild(identifier);
61         return childOperation != null ? appendChild(local, identifier, childOperation) : Optional.empty();
62     }
63
64     private ModificationApplyOperation resolveChild(final PathArgument identifier) {
65         final T schema = getSchema();
66         if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
67             return SchemaAwareApplyOperation.from(schema, (AugmentationTarget) schema,
68                 (AugmentationIdentifier) identifier, treeConfig);
69         }
70
71         final QName qname = identifier.getNodeType();
72         final Optional<DataSchemaNode> child = schema.findDataChildByName(qname);
73         if (!child.isPresent()) {
74             LOG.trace("Child {} not present in container schema {} children {}", identifier, this,
75                 schema.getChildNodes());
76             return null;
77         }
78
79         try {
80             return SchemaAwareApplyOperation.from(child.get(), treeConfig);
81         } catch (IllegalArgumentException e) {
82             LOG.trace("Failed to instantiate child {} in container schema {} children {}", identifier, this,
83                 schema.getChildNodes(), e);
84             return null;
85         }
86     }
87
88     private Optional<ModificationApplyOperation> appendChild(
89             final ImmutableMap<PathArgument, ModificationApplyOperation> initial, final PathArgument identifier,
90             final ModificationApplyOperation computed) {
91
92         ImmutableMap<PathArgument, ModificationApplyOperation> previous = initial;
93         while (true) {
94             // Build up a new map based on observed snapshot and computed child
95             final Builder<PathArgument, ModificationApplyOperation> builder = ImmutableMap.builderWithExpectedSize(
96                 previous.size() + 1);
97             builder.putAll(previous);
98             builder.put(identifier, computed);
99             final ImmutableMap<PathArgument, ModificationApplyOperation> updated = builder.build();
100
101             // Attempt to install the updated map
102             if (UPDATER.compareAndSet(this, previous, updated)) {
103                 return Optional.of(computed);
104             }
105
106             // We have raced, acquire a new snapshot, recheck presence and retry if needed
107             previous = children;
108             final ModificationApplyOperation raced = previous.get(identifier);
109             if (raced != null) {
110                 return Optional.of(raced);
111             }
112         }
113     }
114 }