Do not pretty-print body class
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / InstanceIdToNodes.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.impl.schema;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12
13 import com.google.common.collect.Iterables;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Optional;
17 import javax.xml.transform.dom.DOMSource;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.yangtools.concepts.AbstractSimpleIdentifiable;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
24 import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
30 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
31 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
35 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
38 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
39 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
43
44 /**
45  * Base strategy for converting an instance identifier into a normalized node structure.
46  * Use provided static methods for generic YangInstanceIdentifier -> NormalizedNode translation in ImmutableNodes.
47  */
48 abstract class InstanceIdToNodes<T extends PathArgument> extends AbstractSimpleIdentifiable<T> {
49     InstanceIdToNodes(final T identifier) {
50         super(identifier);
51     }
52
53     /**
54      * Build a strategy for the next path argument.
55      *
56      * @param child child identifier
57      * @return transformation strategy for a specific child
58      */
59     abstract @Nullable InstanceIdToNodes<?> getChild(PathArgument child);
60
61     /**
62      * Convert instance identifier into a NormalizedNode structure.
63      *
64      * @param instanceId Instance identifier to transform into NormalizedNodes
65      * @param deepestChild Optional normalized node to be inserted as the last child
66      * @param operation Optional modify operation to be set on the last child
67      * @return NormalizedNode structure corresponding to submitted instance ID
68      */
69     abstract @NonNull NormalizedNode create(PathArgument first, Iterator<PathArgument> others,
70             Optional<NormalizedNode> deepestChild);
71
72     abstract boolean isMixin();
73
74     private static final class UnkeyedListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
75         private final UnkeyedListItemNormalization innerNode;
76
77         UnkeyedListMixinNormalization(final ListSchemaNode list) {
78             super(NodeIdentifier.create(list.getQName()));
79             this.innerNode = new UnkeyedListItemNormalization(list);
80         }
81
82         @Override
83         CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> createBuilder(final PathArgument compositeNode) {
84             return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
85         }
86
87         @Override
88         InstanceIdToNodes<?> getChild(final PathArgument child) {
89             return child.getNodeType().equals(getIdentifier().getNodeType()) ? innerNode : null;
90         }
91
92         @Override
93         boolean isMixin() {
94             return true;
95         }
96     }
97
98     private abstract static class AbstractOpaqueNormalization extends InstanceIdToNodes<NodeIdentifier> {
99         AbstractOpaqueNormalization(final DataSchemaNode schema) {
100             super(NodeIdentifier.create(schema.getQName()));
101         }
102
103         @Override
104         final InstanceIdToNodes<?> getChild(final PathArgument child) {
105             return null;
106         }
107
108         @Override
109         final boolean isMixin() {
110             return false;
111         }
112     }
113
114     private static final class AnydataNormalization extends AbstractOpaqueNormalization {
115         AnydataNormalization(final AnydataSchemaNode schema) {
116             super(schema);
117         }
118
119         @Override
120         NormalizedNode create(final PathArgument first, final Iterator<PathArgument> others,
121                 final Optional<NormalizedNode> deepestChild) {
122             checkState(deepestChild.isPresent(), "Cannot instantiate anydata node without a value");
123             final NormalizedNode child = deepestChild.get();
124             checkState(child instanceof AnydataNode, "Invalid child %s", child);
125             return createAnydata((AnydataNode<?>) child);
126         }
127
128         private <T> AnydataNode<T> createAnydata(final AnydataNode<T> child) {
129             return Builders.anydataBuilder(child.bodyObjectModel()).withValue(child.body())
130             .withNodeIdentifier(getIdentifier()).build();
131         }
132     }
133
134     private static final class AnyXmlNormalization extends AbstractOpaqueNormalization {
135         AnyXmlNormalization(final AnyxmlSchemaNode schema) {
136             super(schema);
137         }
138
139         @Override
140         NormalizedNode create(final PathArgument first, final Iterator<PathArgument> others,
141                 final Optional<NormalizedNode> deepestChild) {
142             final NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> builder =
143                     Builders.anyXmlBuilder()
144                     .withNodeIdentifier(getIdentifier());
145             if (deepestChild.isPresent()) {
146                 final NormalizedNode child = deepestChild.get();
147                 checkState(child instanceof DOMSourceAnyxmlNode, "Invalid child %s", child);
148                 builder.withValue(((DOMSourceAnyxmlNode) child).body());
149             }
150
151             return builder.build();
152         }
153     }
154
155     private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
156         final Optional<DataSchemaNode> potential = parent.findDataChildByName(child);
157         return potential.isPresent() ? potential : Optional.ofNullable(
158             findChoice(Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class), child));
159     }
160
161     static @Nullable InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
162         final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
163         checkArgument(potential.isPresent(),
164                 "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema,
165                 schema.getChildNodes());
166
167         final DataSchemaNode result = potential.get();
168         // We try to look up if this node was added by augmentation
169         if (schema instanceof DataSchemaNode && result.isAugmenting()) {
170             return fromAugmentation(schema, (AugmentationTarget) schema, result);
171         }
172         return fromDataSchemaNode(result);
173     }
174
175     private static @Nullable ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
176         for (final ChoiceSchemaNode choice : choices) {
177             for (final CaseSchemaNode caze : choice.getCases()) {
178                 if (findChildSchemaNode(caze, child).isPresent()) {
179                     return choice;
180                 }
181             }
182         }
183         return null;
184     }
185
186     /**
187      * Returns a SchemaPathUtil for provided child node
188      * <p/>
189      * If supplied child is added by Augmentation this operation returns
190      * a SchemaPathUtil for augmentation,
191      * otherwise returns a SchemaPathUtil for child as
192      * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
193      */
194     private static @Nullable InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
195             final AugmentationTarget parentAug, final DataSchemaNode child) {
196         for (final AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
197             final Optional<DataSchemaNode> potential = aug.findDataChildByName(child.getQName());
198             if (potential.isPresent()) {
199                 return new InstanceIdToCompositeNodes.AugmentationNormalization(aug, parent);
200             }
201         }
202         return fromDataSchemaNode(child);
203     }
204
205     static @Nullable InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
206         if (potential instanceof ContainerLike) {
207             return new InstanceIdToCompositeNodes.ContainerTransformation((ContainerLike) potential);
208         } else if (potential instanceof ListSchemaNode) {
209             return fromListSchemaNode((ListSchemaNode) potential);
210         } else if (potential instanceof LeafSchemaNode) {
211             return new InstanceIdToSimpleNodes.LeafNormalization((LeafSchemaNode) potential);
212         } else if (potential instanceof ChoiceSchemaNode) {
213             return new InstanceIdToCompositeNodes.ChoiceNodeNormalization((ChoiceSchemaNode) potential);
214         } else if (potential instanceof LeafListSchemaNode) {
215             return fromLeafListSchemaNode((LeafListSchemaNode) potential);
216         } else if (potential instanceof AnydataSchemaNode) {
217             return new AnydataNormalization((AnydataSchemaNode) potential);
218         } else if (potential instanceof AnyxmlSchemaNode) {
219             return new AnyXmlNormalization((AnyxmlSchemaNode) potential);
220         }
221         return null;
222     }
223
224     private static InstanceIdToNodes<?> fromListSchemaNode(final ListSchemaNode potential) {
225         final List<QName> keyDefinition = potential.getKeyDefinition();
226         if (keyDefinition == null || keyDefinition.isEmpty()) {
227             return new UnkeyedListMixinNormalization(potential);
228         }
229         return potential.isUserOrdered() ? new InstanceIdToCompositeNodes.OrderedMapMixinNormalization(potential)
230                 : new InstanceIdToCompositeNodes.UnorderedMapMixinNormalization(potential);
231     }
232
233     private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
234         return potential.isUserOrdered() ? new InstanceIdToCompositeNodes.OrderedLeafListMixinNormalization(potential)
235                 : new InstanceIdToCompositeNodes.UnorderedLeafListMixinNormalization(potential);
236     }
237 }