Bug 2766: Fixed parsing and serializing XPath Instance Identifiers
[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
11 import com.google.common.collect.FluentIterable;
12 import com.google.common.collect.ImmutableSet;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17 import javax.annotation.Nullable;
18 import org.opendaylight.yangtools.concepts.Identifiable;
19 import org.opendaylight.yangtools.yang.common.QName;
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.AugmentationSchema;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
25 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
26 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
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
39  * between {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode}
40  * and serialization format defined in RFC6020, since the mapping
41  * is not one-to-one.
42  *
43  * @param <T> Path Argument type
44  *
45  */
46 public abstract class DataSchemaContextNode<T extends PathArgument> implements Identifiable<T> {
47
48     private final T identifier;
49     private final DataSchemaNode dataSchemaNode;
50
51     @Override
52     public T getIdentifier() {
53         return identifier;
54     };
55
56     protected DataSchemaContextNode(final T identifier, final SchemaNode schema) {
57         super();
58         this.identifier = identifier;
59         if (schema instanceof DataSchemaNode) {
60             this.dataSchemaNode = (DataSchemaNode) schema;
61         } else {
62             this.dataSchemaNode = null;
63         }
64     }
65
66     public boolean isMixin() {
67         return false;
68     }
69
70     public boolean isKeyedEntry() {
71         return false;
72     }
73
74     protected Set<QName> getQNameIdentifiers() {
75         return Collections.singleton(identifier.getNodeType());
76     }
77
78     public abstract @Nullable DataSchemaContextNode<?> getChild(final PathArgument child);
79
80     public abstract @Nullable DataSchemaContextNode<?> getChild(QName child);
81
82     public abstract boolean isLeaf();
83
84
85     public @Nullable DataSchemaNode getDataSchemaNode() {
86         return dataSchemaNode;
87     }
88
89     static final DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
90         DataSchemaNode potential = parent.getDataChildByName(child);
91         if (potential == null) {
92             Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
93                     parent.getChildNodes()).filter(ChoiceNode.class);
94             potential = findChoice(choices, child);
95         }
96         return potential;
97     }
98
99     static DataSchemaContextNode<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
100         DataSchemaNode result = findChildSchemaNode(schema, child);
101         // We try to look up if this node was added by augmentation
102         if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
103             return fromAugmentation(schema, (AugmentationTarget) schema, result);
104         }
105         return fromDataSchemaNode(result);
106     }
107
108     private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
109             final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
110         org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
111         choiceLoop: for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
112             for (ChoiceCaseNode caze : choice.getCases()) {
113                 if (findChildSchemaNode(caze, child) != null) {
114                     foundChoice = choice;
115                     break choiceLoop;
116                 }
117             }
118         }
119         return foundChoice;
120     }
121
122     public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
123         ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
124         for (DataSchemaNode child : augmentation.getChildNodes()) {
125             potentialChildren.add(child.getQName());
126         }
127         return new AugmentationIdentifier(potentialChildren.build());
128     }
129
130     static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation,
131             final DataNodeContainer schema) {
132         Set<DataSchemaNode> children = new HashSet<>();
133         for (DataSchemaNode augNode : augmentation.getChildNodes()) {
134             children.add(schema.getDataChildByName(augNode.getQName()));
135         }
136         return new EffectiveAugmentationSchema(augmentation, children);
137     }
138
139     /**
140      * Returns a DataContextNodeOperation for provided child node
141      *
142      * If supplied child is added by Augmentation this operation returns a
143      * DataContextNodeOperation for augmentation, otherwise returns a
144      * DataContextNodeOperation for child as call for
145      * {@link #fromDataSchemaNode(DataSchemaNode)}.
146      *
147      *
148      * @param parent
149      * @param parentAug
150      * @param child
151      * @return
152      */
153     static @Nullable DataSchemaContextNode<?> fromAugmentation(final DataNodeContainer parent,
154             final AugmentationTarget parentAug, final DataSchemaNode child) {
155         AugmentationSchema augmentation = null;
156         for (AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
157             DataSchemaNode potential = aug.getDataChildByName(child.getQName());
158             if (potential != null) {
159                 augmentation = aug;
160                 break;
161             }
162         }
163         if (augmentation != null) {
164             return new AugmentationContextNode(augmentation, parent);
165         }
166         return fromDataSchemaNode(child);
167     }
168
169     public static @Nullable DataSchemaContextNode<?> fromDataSchemaNode(final DataSchemaNode potential) {
170         if (potential instanceof ContainerSchemaNode) {
171             return new ContainerContextNode((ContainerSchemaNode) potential);
172         } else if (potential instanceof ListSchemaNode) {
173             return fromListSchemaNode((ListSchemaNode) potential);
174         } else if (potential instanceof LeafSchemaNode) {
175             return new LeafContextNode((LeafSchemaNode) potential);
176         } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
177             return new ChoiceNodeContextNode((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
178         } else if (potential instanceof LeafListSchemaNode) {
179             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
180         } else if (potential instanceof AnyXmlSchemaNode) {
181             return new AnyXmlContextNode((AnyXmlSchemaNode) potential);
182         }
183         return null;
184     }
185
186     private static DataSchemaContextNode<?> fromListSchemaNode(final ListSchemaNode potential) {
187         List<QName> keyDefinition = potential.getKeyDefinition();
188         if (keyDefinition == null || keyDefinition.isEmpty()) {
189             return new UnkeyedListMixinContextNode(potential);
190         }
191         if (potential.isUserOrdered()) {
192             return new OrderedMapMixinContextNode(potential);
193         }
194         return new UnorderedMapMixinContextNode(potential);
195     }
196
197     private static DataSchemaContextNode<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
198         if (potential.isUserOrdered()) {
199             return new OrderedLeafListMixinContextNode(potential);
200         }
201         return new UnorderedLeafListMixinContextNode(potential);
202     }
203
204     public static DataSchemaContextNode<?> from(final SchemaContext ctx) {
205         return new ContainerContextNode(ctx);
206     }
207 }