package org.opendaylight.yangtools.yang.data.impl.schema;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.Map.Entry;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.ImmutableOffsetMap;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.ModifyAction;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerLike;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
-* Base strategy for converting an instance identifier into a normalized node structure for container-like types.
-*/
+ * Base strategy for converting an instance identifier into a normalized node structure for container-like types.
+ */
abstract class InstanceIdToCompositeNodes<T extends PathArgument> extends InstanceIdToNodes<T> {
+ private static final Logger LOG = LoggerFactory.getLogger(InstanceIdToCompositeNodes.class);
- protected InstanceIdToCompositeNodes(final T identifier) {
+ InstanceIdToCompositeNodes(final T identifier) {
super(identifier);
}
- private static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
- final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
- for (final DataSchemaNode child : augmentation.getChildNodes()) {
- potentialChildren.add(child.getQName());
- }
- return new AugmentationIdentifier(potentialChildren.build());
- }
-
- private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
- final Set<DataSchemaNode> children = new HashSet<>();
- for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
- children.add(schema.getDataChildByName(augNode.getQName()));
- }
- return new EffectiveAugmentationSchema(augmentation, children);
- }
-
@Override
@SuppressWarnings("unchecked")
- public final NormalizedNode<?, ?> create(final YangInstanceIdentifier instanceId, final Optional<NormalizedNode<?, ?>> lastChild, final Optional<Map.Entry<QName,ModifyAction>> operation) {
- checkNotNull(instanceId);
- final Iterator<PathArgument> iterator = instanceId.getPathArguments().iterator();
- final PathArgument legacyData = iterator.next();
-
- if (!isMixin() && getIdentifier().getNodeType() != null) {
- checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
- "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
+ final NormalizedNode<?, ?> create(final PathArgument first, final Iterator<PathArgument> others,
+ final Optional<NormalizedNode<?, ?>> lastChild) {
+ if (!isMixin()) {
+ final QName type = getIdentifier().getNodeType();
+ if (type != null) {
+ final QName firstType = first.getNodeType();
+ checkArgument(type.equals(firstType), "Node QName must be %s was %s", type, firstType);
+ }
}
+
@SuppressWarnings("rawtypes")
- final NormalizedNodeContainerBuilder builder = createBuilder(legacyData);
+ final NormalizedNodeContainerBuilder builder = createBuilder(first);
- if (iterator.hasNext()) {
- final PathArgument childPath = iterator.next();
+ if (others.hasNext()) {
+ final PathArgument childPath = others.next();
final InstanceIdToNodes<?> childOp = getChildOperation(childPath);
-
- final YangInstanceIdentifier childId = YangInstanceIdentifier.create(Iterables.skip(instanceId.getPathArguments(), 1));
- builder.addChild(childOp.create(childId, lastChild, operation));
- } else {
- if (lastChild.isPresent()) {
- builder.withValue(Lists.newArrayList((Collection<?>) lastChild.get().getValue()));
- }
- if (operation.isPresent()) {
- Preconditions.checkArgument(builder instanceof AttributesBuilder<?>);
- addModifyOpIfPresent(operation, ((AttributesBuilder<?>) builder));
- }
+ builder.addChild(childOp.create(childPath, others, lastChild));
+ } else if (lastChild.isPresent()) {
+ builder.withValue(ImmutableList.copyOf((Collection<?>) lastChild.get().getValue()));
}
return builder.build();
}
+ @SuppressWarnings("checkstyle:illegalCatch")
private InstanceIdToNodes<?> getChildOperation(final PathArgument childPath) {
final InstanceIdToNodes<?> childOp;
try {
return childOp;
}
- protected abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode);
+ abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(PathArgument compositeNode);
- static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
- InstanceIdToCompositeNodes<T> {
+ abstract static class DataContainerNormalizationOperation<T extends PathArgument, S extends DataNodeContainer>
+ extends InstanceIdToCompositeNodes<T> {
- private final DataNodeContainer schema;
- private final Map<PathArgument, InstanceIdToNodes<?>> byArg;
+ private final Map<PathArgument, InstanceIdToNodes<?>> byArg = new ConcurrentHashMap<>();
+ private final @NonNull S schema;
- protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
+ DataContainerNormalizationOperation(final T identifier, final S schema) {
super(identifier);
- this.schema = schema;
- this.byArg = new ConcurrentHashMap<>();
+ this.schema = requireNonNull(schema);
}
@Override
- public InstanceIdToNodes<?> getChild(final PathArgument child) {
- InstanceIdToNodes<?> potential = byArg.get(child);
- if (potential != null) {
- return potential;
+ final InstanceIdToNodes<?> getChild(final PathArgument child) {
+ final InstanceIdToNodes<?> existing = byArg.get(child);
+ if (existing != null) {
+ return existing;
}
- potential = fromLocalSchema(child);
- return register(potential);
+ return register(fromLocalSchema(child));
+ }
+
+ final @NonNull S schema() {
+ return schema;
}
private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) {
}
}
- static final class ListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
- protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
- super(identifier, schema);
+ static final class MapEntryNormalization
+ extends DataContainerNormalizationOperation<NodeIdentifierWithPredicates, ListSchemaNode> {
+ MapEntryNormalization(final ListSchemaNode schema) {
+ super(NodeIdentifierWithPredicates.of(schema.getQName()), schema);
+ }
+
+ @Override
+ boolean isMixin() {
+ return false;
}
@Override
- protected DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(final PathArgument currentArg) {
- final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
- .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
- for (final Map.Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
+ DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
+ final PathArgument currentArg) {
+ final NodeIdentifierWithPredicates arg = (NodeIdentifierWithPredicates) currentArg;
+ return createBuilder(arg.size() < 2 ? arg : reorderPredicates(schema().getKeyDefinition(), arg));
+ }
+
+ private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
+ final NodeIdentifierWithPredicates arg) {
+ final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+ .mapEntryBuilder().withNodeIdentifier(arg);
+ for (final Entry<QName, Object> keyValue : arg.entrySet()) {
builder.addChild(Builders.leafBuilder()
.withNodeIdentifier(NodeIdentifier.create(keyValue.getKey())).withValue(keyValue.getValue())
.build());
return builder;
}
- @Override
- boolean isMixin() {
- return false;
+ private static NodeIdentifierWithPredicates reorderPredicates(final List<QName> keys,
+ final NodeIdentifierWithPredicates arg) {
+ if (Iterables.elementsEqual(keys, arg.keySet())) {
+ // Iteration order matches key order, reuse the identifier
+ return arg;
+ }
+
+ // We care about iteration order here!
+ final LinkedHashMap<QName, Object> map = Maps.newLinkedHashMapWithExpectedSize(arg.size());
+ for (QName qname : keys) {
+ final Object value = arg.getValue(qname);
+ if (value != null) {
+ map.put(qname, value);
+ }
+ }
+ if (map.size() < arg.size()) {
+ // Okay, this should not happen, but let's handle that anyway
+ LOG.debug("Extra predicates in {} while expecting {}", arg, keys);
+ for (Entry<QName, Object> entry : arg.entrySet()) {
+ map.putIfAbsent(entry.getKey(), entry.getValue());
+ }
+ }
+
+ // This copy retains iteration order and since we have more than one argument, it should always be
+ // and ImmutableOffsetMap -- which is guaranteed to be taken as-is
+ final Map<QName, Object> copy = ImmutableOffsetMap.orderedCopyOf(map);
+ verify(copy instanceof ImmutableOffsetMap);
+ return NodeIdentifierWithPredicates.of(arg.getNodeType(), (ImmutableOffsetMap<QName, Object>) copy);
}
}
- static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
-
- protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
+ static final class UnkeyedListItemNormalization
+ extends DataContainerNormalizationOperation<NodeIdentifier, ListSchemaNode> {
+ UnkeyedListItemNormalization(final ListSchemaNode schema) {
super(NodeIdentifier.create(schema.getQName()), schema);
}
@Override
- protected DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> createBuilder(final PathArgument compositeNode) {
+ DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> createBuilder(
+ final PathArgument compositeNode) {
return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
}
}
}
- static final class ContainerTransformation extends DataContainerNormalizationOperation<NodeIdentifier> {
- protected ContainerTransformation(final ContainerSchemaNode schema) {
+ static final class ContainerTransformation
+ extends DataContainerNormalizationOperation<NodeIdentifier, ContainerLike> {
+ ContainerTransformation(final ContainerLike schema) {
super(NodeIdentifier.create(schema.getQName()), schema);
}
@Override
- protected DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> createBuilder(final PathArgument compositeNode) {
+ DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createBuilder(final PathArgument compositeNode) {
return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
}
}
static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
-
-
- public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+ OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
super(potential);
}
@Override
- protected ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
+ ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
}
}
static class UnorderedLeafListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
-
private final InstanceIdToNodes<?> innerOp;
- public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+ UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
super(NodeIdentifier.create(potential.getQName()));
innerOp = new InstanceIdToSimpleNodes.LeafListEntryNormalization(potential);
}
@Override
- protected ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
+ ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
}
@Override
- public InstanceIdToNodes<?> getChild(final PathArgument child) {
- if (child instanceof NodeWithValue) {
- return innerOp;
- }
- return null;
+ final InstanceIdToNodes<?> getChild(final PathArgument child) {
+ return child instanceof NodeWithValue ? innerOp : null;
}
@Override
- boolean isMixin() {
+ final boolean isMixin() {
return true;
}
}
- static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> {
-
- public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
- super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation, schema));
+ static final class AugmentationNormalization
+ extends DataContainerNormalizationOperation<AugmentationIdentifier, AugmentationSchemaNode> {
+ AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
+ super(DataSchemaContextNode.augmentationIdentifierFrom(augmentation),
+ EffectiveAugmentationSchema.create(augmentation, schema));
}
@Override
- protected DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> createBuilder(final PathArgument compositeNode) {
+ DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> createBuilder(
+ final PathArgument compositeNode) {
return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
}
}
static class UnorderedMapMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
+ private final MapEntryNormalization innerNode;
- private final ListItemNormalization innerNode;
-
- public UnorderedMapMixinNormalization(final ListSchemaNode list) {
+ UnorderedMapMixinNormalization(final ListSchemaNode list) {
super(NodeIdentifier.create(list.getQName()));
- this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
- Collections.<QName, Object>emptyMap()), list);
+ this.innerNode = new MapEntryNormalization(list);
}
@Override
- protected CollectionNodeBuilder<MapEntryNode, ? extends MapNode> createBuilder(final PathArgument compositeNode) {
+ CollectionNodeBuilder<MapEntryNode, ? extends MapNode> createBuilder(final PathArgument compositeNode) {
return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
}
@Override
- public InstanceIdToNodes<?> getChild(final PathArgument child) {
- if (child.getNodeType().equals(getIdentifier().getNodeType())) {
- return innerNode;
- }
- return null;
+ final InstanceIdToNodes<?> getChild(final PathArgument child) {
+ return child.getNodeType().equals(getIdentifier().getNodeType()) ? innerNode : null;
}
@Override
- boolean isMixin() {
+ final boolean isMixin() {
return true;
}
}
static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
-
- public OrderedMapMixinNormalization(final ListSchemaNode list) {
+ OrderedMapMixinNormalization(final ListSchemaNode list) {
super(list);
}
@Override
- protected CollectionNodeBuilder<MapEntryNode, OrderedMapNode> createBuilder(final PathArgument compositeNode) {
+ CollectionNodeBuilder<MapEntryNode, OrderedMapNode> createBuilder(final PathArgument compositeNode) {
return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
}
-
}
static final class ChoiceNodeNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
-
private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
- protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
+ ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
super(NodeIdentifier.create(schema.getQName()));
final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
- for (final ChoiceCaseNode caze : schema.getCases()) {
+ for (final CaseSchemaNode caze : schema.getCases()) {
for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
byArgBuilder.put(childOp.getIdentifier(), childOp);
}
@Override
- public InstanceIdToNodes<?> getChild(final PathArgument child) {
+ InstanceIdToNodes<?> getChild(final PathArgument child) {
return byArg.get(child);
}
@Override
- protected DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> createBuilder(final PathArgument compositeNode) {
+ DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> createBuilder(final PathArgument compositeNode) {
return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
}