2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.util;
10 import com.google.common.collect.ImmutableSet;
11 import com.google.common.collect.Iterables;
12 import java.util.Optional;
14 import java.util.stream.Collectors;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.yangtools.concepts.AbstractSimpleIdentifiable;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
22 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
26 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
29 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
30 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
38 * Schema derived data providing necessary information for mapping between
39 * {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} and serialization format defined in RFC6020,
40 * since the mapping is not one-to-one.
42 * @param <T> Path Argument type
44 public abstract class DataSchemaContextNode<T extends PathArgument> extends AbstractSimpleIdentifiable<T> {
45 // FIXME: this can be null only for AugmentationContextNode and in that case the interior part is handled by a
46 // separate field in DataContainerContextNode. We need to re-examine our base interface class hierarchy
47 // so that the underlying (effective in augment's case) SchemaNode is always available.
48 private final DataSchemaNode dataSchemaNode;
50 DataSchemaContextNode(final T identifier, final DataSchemaNode schema) {
52 this.dataSchemaNode = schema;
55 @Deprecated(forRemoval = true, since = "8.0.2")
56 protected DataSchemaContextNode(final T identifier, final SchemaNode schema) {
57 this(identifier, schema instanceof DataSchemaNode ? (DataSchemaNode) schema : null);
60 // FIXME: document this method
61 public boolean isMixin() {
65 // FIXME: document this method
66 public boolean isKeyedEntry() {
70 // FIXME: this is counter-intuitive: anydata/anyxml are considered non-leaf. This method needs a better name and
71 // a proper description.
72 public abstract boolean isLeaf();
74 protected Set<QName> getQNameIdentifiers() {
75 return ImmutableSet.of(getIdentifier().getNodeType());
79 * Find a child node identifier by its {@link PathArgument}.
81 * @param child Child path argument
82 * @return A child node, or null if not found
84 // FIXME: document PathArgument type mismatch
85 public abstract @Nullable DataSchemaContextNode<?> getChild(PathArgument child);
87 // FIXME: document child == null
88 public abstract @Nullable DataSchemaContextNode<?> getChild(QName child);
91 public @Nullable DataSchemaNode getDataSchemaNode() {
92 return dataSchemaNode;
96 * Find a child node as identified by a {@link YangInstanceIdentifier} relative to this node.
98 * @param path Path towards the child node
99 * @return Child node if present, or empty when corresponding child is not found.
100 * @throws NullPointerException if {@code path} is null
102 public final @NonNull Optional<@NonNull DataSchemaContextNode<?>> findChild(
103 final @NonNull YangInstanceIdentifier path) {
104 DataSchemaContextNode<?> currentOp = this;
105 for (PathArgument arg : path.getPathArguments()) {
106 currentOp = currentOp.getChild(arg);
107 if (currentOp == null) {
108 return Optional.empty();
111 return Optional.of(currentOp);
114 static DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
115 final DataSchemaNode potential = parent.dataChildByName(child);
116 return potential == null ? findChoice(Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class), child)
120 static DataSchemaContextNode<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
121 final DataSchemaNode result = findChildSchemaNode(schema, child);
122 // We try to look up if this node was added by augmentation
123 if (result != null && schema instanceof DataSchemaNode && result.isAugmenting()) {
124 return fromAugmentation(schema, (AugmentationTarget) schema, result);
126 return lenientOf(result);
129 // FIXME: this looks like it should be a Predicate on a stream with findFirst()
130 private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
131 for (ChoiceSchemaNode choice : choices) {
132 // FIXME: this looks weird: what are we looking for again?
133 for (CaseSchemaNode caze : choice.getCases()) {
134 if (findChildSchemaNode(caze, child) != null) {
143 * Create AugmentationIdentifier from an AugmentationSchemaNode.
145 * @param schema Augmentation schema
146 * @return AugmentationIdentifier for the schema
147 * @throws NullPointerException if {@code schema} is null
149 public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchemaNode schema) {
150 return new AugmentationIdentifier(schema.getChildNodes().stream().map(DataSchemaNode::getQName)
151 .collect(Collectors.toSet()));
154 static @NonNull DataSchemaContextNode<?> of(final @NonNull DataSchemaNode schema) {
155 if (schema instanceof ContainerLike) {
156 return new ContainerContextNode((ContainerLike) schema);
157 } else if (schema instanceof ListSchemaNode) {
158 return fromListSchemaNode((ListSchemaNode) schema);
159 } else if (schema instanceof LeafSchemaNode) {
160 return new LeafContextNode((LeafSchemaNode) schema);
161 } else if (schema instanceof ChoiceSchemaNode) {
162 return new ChoiceNodeContextNode((ChoiceSchemaNode) schema);
163 } else if (schema instanceof LeafListSchemaNode) {
164 return fromLeafListSchemaNode((LeafListSchemaNode) schema);
165 } else if (schema instanceof AnydataSchemaNode) {
166 return new AnydataContextNode((AnydataSchemaNode) schema);
167 } else if (schema instanceof AnyxmlSchemaNode) {
168 return new AnyXmlContextNode((AnyxmlSchemaNode) schema);
170 throw new IllegalStateException("Unhandled schema " + schema);
174 // FIXME: do we tolerate null argument? do we tolerate unknown subclasses?
175 static @Nullable DataSchemaContextNode<?> lenientOf(final @Nullable DataSchemaNode schema) {
176 if (schema instanceof ContainerLike) {
177 return new ContainerContextNode((ContainerLike) schema);
178 } else if (schema instanceof ListSchemaNode) {
179 return fromListSchemaNode((ListSchemaNode) schema);
180 } else if (schema instanceof LeafSchemaNode) {
181 return new LeafContextNode((LeafSchemaNode) schema);
182 } else if (schema instanceof ChoiceSchemaNode) {
183 return new ChoiceNodeContextNode((ChoiceSchemaNode) schema);
184 } else if (schema instanceof LeafListSchemaNode) {
185 return fromLeafListSchemaNode((LeafListSchemaNode) schema);
186 } else if (schema instanceof AnydataSchemaNode) {
187 return new AnydataContextNode((AnydataSchemaNode) schema);
188 } else if (schema instanceof AnyxmlSchemaNode) {
189 return new AnyXmlContextNode((AnyxmlSchemaNode) schema);
196 * Returns a DataContextNodeOperation for provided child node
199 * If supplied child is added by Augmentation this operation returns a DataSchemaContextNode for augmentation,
200 * otherwise returns a DataSchemaContextNode for child as call for {@link #lenientOf(DataSchemaNode)}.
202 static @Nullable DataSchemaContextNode<?> fromAugmentation(final DataNodeContainer parent,
203 final AugmentationTarget parentAug, final DataSchemaNode child) {
204 for (AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
205 if (aug.findDataChildByName(child.getQName()).isPresent()) {
206 return new AugmentationContextNode(aug, parent);
209 return lenientOf(child);
213 * Get a {@link DataSchemaContextNode} for a particular {@link DataSchemaNode}.
215 * @param potential Backing DataSchemaNode
216 * @return A {@link DataSchemaContextNode}, or null if the input is {@code null} or of unhandled type
218 @Deprecated(forRemoval = true, since = "8.0.2")
219 public static @Nullable DataSchemaContextNode<?> fromDataSchemaNode(final DataSchemaNode potential) {
220 return lenientOf(potential);
223 private static @NonNull DataSchemaContextNode<?> fromListSchemaNode(final ListSchemaNode potential) {
224 var keyDefinition = potential.getKeyDefinition();
225 if (keyDefinition.isEmpty()) {
226 return new UnkeyedListMixinContextNode(potential);
227 } else if (potential.isUserOrdered()) {
228 return new OrderedMapMixinContextNode(potential);
230 return new UnorderedMapMixinContextNode(potential);
234 private static @NonNull DataSchemaContextNode<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
235 if (potential.isUserOrdered()) {
236 return new OrderedLeafListMixinContextNode(potential);
238 return new UnorderedLeafListMixinContextNode(potential);
242 * Return a DataSchemaContextNode corresponding to specified {@link EffectiveModelContext}.
244 * @param ctx EffectiveModelContext
245 * @return A DataSchemaContextNode
246 * @throws NullPointerException if {@code ctx} is null
247 * @deprecated Use {@link DataSchemaContextTree#from(EffectiveModelContext)} and
248 * {@link DataSchemaContextTree#getRoot()} instead.
250 @Deprecated(forRemoval = true, since = "8.0.2")
251 public static @NonNull DataSchemaContextNode<?> from(final EffectiveModelContext ctx) {
252 return new ContainerContextNode(ctx);