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