/* * 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 Type of the container node */ class DataNodeContainerModificationStrategy extends Visible { private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerModificationStrategy.class); private final @NonNull DataTreeConfiguration treeConfig; @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater UPDATER = AtomicReferenceFieldUpdater.newUpdater(DataNodeContainerModificationStrategy.class, ImmutableMap.class, "children"); private volatile ImmutableMap 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 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 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 initial, final PathArgument identifier, final ModificationApplyOperation computed) { ImmutableMap previous = initial; while (true) { // Build up a new map based on observed snapshot and computed child final Builder builder = ImmutableMap.builderWithExpectedSize( previous.size() + 1); builder.putAll(previous); builder.put(identifier, computed); final ImmutableMap 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; } } } }