Centralize AugmentationIdentifier instantiation
[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.FluentIterable;
11 import com.google.common.collect.ImmutableSet;
12 import java.util.List;
13 import java.util.Optional;
14 import java.util.Set;
15 import java.util.stream.Collectors;
16 import javax.annotation.Nullable;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.yangtools.concepts.Identifiable;
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 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
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> implements Identifiable<T> {
46     private final DataSchemaNode dataSchemaNode;
47     private final T identifier;
48
49     protected DataSchemaContextNode(final T identifier, final SchemaNode schema) {
50         this.identifier = identifier;
51         if (schema instanceof DataSchemaNode) {
52             this.dataSchemaNode = (DataSchemaNode) schema;
53         } else {
54             this.dataSchemaNode = null;
55         }
56     }
57
58     @Override
59     public T getIdentifier() {
60         return identifier;
61     }
62
63     public boolean isMixin() {
64         return false;
65     }
66
67     public boolean isKeyedEntry() {
68         return false;
69     }
70
71     public abstract boolean isLeaf();
72
73     protected Set<QName> getQNameIdentifiers() {
74         return ImmutableSet.of(identifier.getNodeType());
75     }
76
77     /**
78      * Find a child node identifier by its {@link PathArgument}.
79      *
80      * @param child Child path argument
81      * @return A child node, or null if not found
82      */
83     @Nullable public abstract DataSchemaContextNode<?> getChild(PathArgument child);
84
85     @Nullable public abstract DataSchemaContextNode<?> getChild(QName child);
86
87     @Nullable public DataSchemaNode getDataSchemaNode() {
88         return dataSchemaNode;
89     }
90
91     /**
92      * Find a child node as identified by a {@link YangInstanceIdentifier} relative to this node.
93      *
94      * @param path Path towards the child node
95      * @return Child node if present, or empty when corresponding child is not found.
96      * @throws NullPointerException if {@code path} is null
97      */
98     public final @NonNull Optional<@NonNull DataSchemaContextNode<?>> findChild(
99             final @NonNull YangInstanceIdentifier path) {
100         DataSchemaContextNode<?> currentOp = this;
101         for (PathArgument arg : path.getPathArguments()) {
102             currentOp = currentOp.getChild(arg);
103             if (currentOp == null) {
104                 return Optional.empty();
105             }
106         }
107         return Optional.of(currentOp);
108     }
109
110     static DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
111         DataSchemaNode potential = parent.getDataChildByName(child);
112         if (potential == null) {
113             Iterable<ChoiceSchemaNode> choices = FluentIterable.from(
114                     parent.getChildNodes()).filter(ChoiceSchemaNode.class);
115             potential = findChoice(choices, child);
116         }
117         return potential;
118     }
119
120     static DataSchemaContextNode<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
121         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);
125         }
126         return fromDataSchemaNode(result);
127     }
128
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().values()) {
134                 if (findChildSchemaNode(caze, child) != null) {
135                     return choice;
136                 }
137             }
138         }
139         return null;
140     }
141
142     /**
143      * Create AugmentationIdentifier from an AugmentationSchemaNode.
144      *
145      * @param schema Augmentation schema
146      * @return AugmentationIdentifier for the schema
147      * @throws NullPointerException if {@code schema} is null
148      */
149     public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchemaNode schema) {
150         return new AugmentationIdentifier(schema.getChildNodes().stream().map(DataSchemaNode::getQName)
151             .collect(Collectors.toSet()));
152     }
153
154     /**
155      * Returns an AugmentationSchemaNode as effective in a parent node.
156      *
157      * @param schema Augmentation schema
158      * @param parent Parent schema
159      * @return Adjusted Augmentation schema
160      * @throws NullPointerException if any of the arguments is null
161      * @deprecated Use {@link EffectiveAugmentationSchema#create(AugmentationSchemaNode, DataNodeContainer)} instead.
162      */
163     @Deprecated
164     public static AugmentationSchemaNode augmentationProxy(final AugmentationSchemaNode schema,
165             final DataNodeContainer parent) {
166         return EffectiveAugmentationSchema.create(schema, parent);
167     }
168
169     /**
170      * Returns a DataContextNodeOperation for provided child node
171      *
172      * <p>
173      * If supplied child is added by Augmentation this operation returns a
174      * DataContextNodeOperation for augmentation, otherwise returns a
175      * DataContextNodeOperation for child as call for
176      * {@link #fromDataSchemaNode(DataSchemaNode)}.
177      */
178     @Nullable static DataSchemaContextNode<?> fromAugmentation(final DataNodeContainer parent,
179             final AugmentationTarget parentAug, final DataSchemaNode child) {
180         AugmentationSchemaNode augmentation = null;
181         for (AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
182             DataSchemaNode potential = aug.getDataChildByName(child.getQName());
183             if (potential != null) {
184                 augmentation = aug;
185                 break;
186             }
187         }
188         if (augmentation != null) {
189             return new AugmentationContextNode(augmentation, parent);
190         }
191         return fromDataSchemaNode(child);
192     }
193
194     @Nullable public static DataSchemaContextNode<?> fromDataSchemaNode(final DataSchemaNode potential) {
195         if (potential instanceof ContainerSchemaNode) {
196             return new ContainerContextNode((ContainerSchemaNode) potential);
197         } else if (potential instanceof ListSchemaNode) {
198             return fromListSchemaNode((ListSchemaNode) potential);
199         } else if (potential instanceof LeafSchemaNode) {
200             return new LeafContextNode((LeafSchemaNode) potential);
201         } else if (potential instanceof ChoiceSchemaNode) {
202             return new ChoiceNodeContextNode((ChoiceSchemaNode) potential);
203         } else if (potential instanceof LeafListSchemaNode) {
204             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
205         } else if (potential instanceof AnyXmlSchemaNode) {
206             return new AnyXmlContextNode((AnyXmlSchemaNode) potential);
207         }
208         return null;
209     }
210
211     private static DataSchemaContextNode<?> fromListSchemaNode(final ListSchemaNode potential) {
212         List<QName> keyDefinition = potential.getKeyDefinition();
213         if (keyDefinition == null || keyDefinition.isEmpty()) {
214             return new UnkeyedListMixinContextNode(potential);
215         }
216         if (potential.isUserOrdered()) {
217             return new OrderedMapMixinContextNode(potential);
218         }
219         return new UnorderedMapMixinContextNode(potential);
220     }
221
222     private static DataSchemaContextNode<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
223         if (potential.isUserOrdered()) {
224             return new OrderedLeafListMixinContextNode(potential);
225         }
226         return new UnorderedLeafListMixinContextNode(potential);
227     }
228
229     public static DataSchemaContextNode<?> from(final SchemaContext ctx) {
230         return new ContainerContextNode(ctx);
231     }
232 }