ec92da849eee15c07bcfaec25580efcb1fdf0c9f
[yangtools.git] / data / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / DataSchemaContextNode.java
1 /*
2  * Copyright (c) 2015 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.util;
9
10 import com.google.common.collect.ImmutableSet;
11 import com.google.common.collect.Iterables;
12 import java.util.List;
13 import java.util.Optional;
14 import java.util.Set;
15 import java.util.stream.Collectors;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.yangtools.concepts.AbstractSimpleIdentifiable;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
23 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
27 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
33 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
37
38 /**
39  * Schema derived data providing necessary information for mapping between
40  * {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} and serialization format defined in RFC6020,
41  * since the mapping is not one-to-one.
42  *
43  * @param <T> Path Argument type
44  */
45 public abstract class DataSchemaContextNode<T extends PathArgument> extends AbstractSimpleIdentifiable<T> {
46     private final DataSchemaNode dataSchemaNode;
47
48     protected DataSchemaContextNode(final T identifier, final SchemaNode schema) {
49         super(identifier);
50         if (schema instanceof DataSchemaNode) {
51             this.dataSchemaNode = (DataSchemaNode) schema;
52         } else {
53             this.dataSchemaNode = null;
54         }
55     }
56
57     public boolean isMixin() {
58         return false;
59     }
60
61     public boolean isKeyedEntry() {
62         return false;
63     }
64
65     public abstract boolean isLeaf();
66
67     protected Set<QName> getQNameIdentifiers() {
68         return ImmutableSet.of(getIdentifier().getNodeType());
69     }
70
71     /**
72      * Find a child node identifier by its {@link PathArgument}.
73      *
74      * @param child Child path argument
75      * @return A child node, or null if not found
76      */
77     public abstract @Nullable DataSchemaContextNode<?> getChild(PathArgument child);
78
79     public abstract @Nullable DataSchemaContextNode<?> getChild(QName child);
80
81     public @Nullable DataSchemaNode getDataSchemaNode() {
82         return dataSchemaNode;
83     }
84
85     /**
86      * Find a child node as identified by a {@link YangInstanceIdentifier} relative to this node.
87      *
88      * @param path Path towards the child node
89      * @return Child node if present, or empty when corresponding child is not found.
90      * @throws NullPointerException if {@code path} is null
91      */
92     public final @NonNull Optional<@NonNull DataSchemaContextNode<?>> findChild(
93             final @NonNull YangInstanceIdentifier path) {
94         DataSchemaContextNode<?> currentOp = this;
95         for (PathArgument arg : path.getPathArguments()) {
96             currentOp = currentOp.getChild(arg);
97             if (currentOp == null) {
98                 return Optional.empty();
99             }
100         }
101         return Optional.of(currentOp);
102     }
103
104     static DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
105         final DataSchemaNode potential = parent.dataChildByName(child);
106         return potential == null ? findChoice(Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class), child)
107                 : potential;
108     }
109
110     static DataSchemaContextNode<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
111         final DataSchemaNode result = findChildSchemaNode(schema, child);
112         // We try to look up if this node was added by augmentation
113         if (result != null && schema instanceof DataSchemaNode && result.isAugmenting()) {
114             return fromAugmentation(schema, (AugmentationTarget) schema, result);
115         }
116         return fromDataSchemaNode(result);
117     }
118
119     // FIXME: this looks like it should be a Predicate on a stream with findFirst()
120     private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
121         for (ChoiceSchemaNode choice : choices) {
122             // FIXME: this looks weird: what are we looking for again?
123             for (CaseSchemaNode caze : choice.getCases()) {
124                 if (findChildSchemaNode(caze, child) != null) {
125                     return choice;
126                 }
127             }
128         }
129         return null;
130     }
131
132     /**
133      * Create AugmentationIdentifier from an AugmentationSchemaNode.
134      *
135      * @param schema Augmentation schema
136      * @return AugmentationIdentifier for the schema
137      * @throws NullPointerException if {@code schema} is null
138      */
139     public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchemaNode schema) {
140         return new AugmentationIdentifier(schema.getChildNodes().stream().map(DataSchemaNode::getQName)
141             .collect(Collectors.toSet()));
142     }
143
144     /**
145      * Returns a DataContextNodeOperation for provided child node
146      *
147      * <p>
148      * If supplied child is added by Augmentation this operation returns a
149      * DataContextNodeOperation for augmentation, otherwise returns a
150      * DataContextNodeOperation for child as call for
151      * {@link #fromDataSchemaNode(DataSchemaNode)}.
152      */
153     static @Nullable DataSchemaContextNode<?> fromAugmentation(final DataNodeContainer parent,
154             final AugmentationTarget parentAug, final DataSchemaNode child) {
155         for (AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
156             if (aug.findDataChildByName(child.getQName()).isPresent()) {
157                 return new AugmentationContextNode(aug, parent);
158             }
159         }
160         return fromDataSchemaNode(child);
161     }
162
163     public static @Nullable DataSchemaContextNode<?> fromDataSchemaNode(final DataSchemaNode potential) {
164         if (potential instanceof ContainerLike) {
165             return new ContainerContextNode((ContainerLike) potential);
166         } else if (potential instanceof ListSchemaNode) {
167             return fromListSchemaNode((ListSchemaNode) potential);
168         } else if (potential instanceof LeafSchemaNode) {
169             return new LeafContextNode((LeafSchemaNode) potential);
170         } else if (potential instanceof ChoiceSchemaNode) {
171             return new ChoiceNodeContextNode((ChoiceSchemaNode) potential);
172         } else if (potential instanceof LeafListSchemaNode) {
173             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
174         } else if (potential instanceof AnydataSchemaNode) {
175             return new AnydataContextNode((AnydataSchemaNode) potential);
176         } else if (potential instanceof AnyxmlSchemaNode) {
177             return new AnyXmlContextNode((AnyxmlSchemaNode) potential);
178         }
179         return null;
180     }
181
182     private static DataSchemaContextNode<?> fromListSchemaNode(final ListSchemaNode potential) {
183         List<QName> keyDefinition = potential.getKeyDefinition();
184         if (keyDefinition == null || keyDefinition.isEmpty()) {
185             return new UnkeyedListMixinContextNode(potential);
186         }
187         if (potential.isUserOrdered()) {
188             return new OrderedMapMixinContextNode(potential);
189         }
190         return new UnorderedMapMixinContextNode(potential);
191     }
192
193     private static DataSchemaContextNode<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
194         if (potential.isUserOrdered()) {
195             return new OrderedLeafListMixinContextNode(potential);
196         }
197         return new UnorderedLeafListMixinContextNode(potential);
198     }
199
200     public static DataSchemaContextNode<?> from(final EffectiveModelContext ctx) {
201         return new ContainerContextNode(ctx);
202     }
203 }