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