Mark methods as static
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / transform / base / parser / BaseDispatcherParser.java
1 /*
2  * Copyright (c) 2013 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.transform.base.parser;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.Iterables;
12 import com.google.common.collect.LinkedListMultimap;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import javax.annotation.Nullable;
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.schema.DataContainerChild;
21 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
22 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
23 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
24 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
26 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
29 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
30
31 /**
32  * Abstract(base) Parser for DataContainerNodes e.g. ContainerNode, AugmentationNode.
33  */
34 public abstract class BaseDispatcherParser<E, P extends YangInstanceIdentifier.PathArgument, N extends DataContainerNode<P>, S>
35         implements ExtensibleParser<P, E, N, S> {
36
37     private final BuildingStrategy<P, N> buildingStrategy;
38
39     public BaseDispatcherParser(final BuildingStrategy<P, N> buildingStrategy) {
40         this.buildingStrategy = buildingStrategy;
41     }
42
43     public BaseDispatcherParser() {
44         this.buildingStrategy = new SimpleBuildingStrategy<>();
45     }
46
47     /**
48      *
49      * @param schema
50      * @return New(empty) instance of a builder to build node identified by schema.
51      */
52     protected abstract DataContainerNodeBuilder<P, N> getBuilder(S schema);
53
54     /**
55      *
56      * @param schema
57      * @param childQName QName of a child being parsed, QName does not continue revision date
58      * @return schema object for child identified by parent schema: schema and QName childQName
59      */
60     protected abstract DataSchemaNode getSchemaForChild(S schema, QName childQName);
61
62     /**
63      *
64      * @param xml
65      * @return map from QName to child elements. Multiple elements are allowed under QName.
66      */
67     protected abstract LinkedListMultimap<QName, E> mapChildElements(Iterable<E> xml);
68
69     /**
70      *
71      * @param schema
72      * @return map from QName to ChoiceNode schema of child nodes that are
73      *         contained within a choice statement under current schema.
74      */
75     protected abstract Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(S schema);
76
77     /**
78      *
79      * @param schema
80      * @return map from QName to child elements that are added by augmentation
81      *         that targets current schema.
82      */
83     protected abstract Map<QName, AugmentationSchema> mapChildElementsFromAugments(S schema);
84
85     /**
86      *
87      * @param schema
88      * @param augmentSchema
89      * @return Set of real schema objects that represent child nodes of an
90      *         augmentation. Augmentation schema child nodes, if further
91      *         augmented, do not contain further augmented, that are crucial for
92      *         parsing. The real schema object can be retrieved from parent schema: schema.
93      */
94     protected abstract Set<DataSchemaNode> getRealSchemasForAugment(S schema, AugmentationSchema augmentSchema);
95
96     /**
97      *
98      * @return dispatcher object to dispatch parsing of child elements, might be
99      *         the same instance if provided parsers are immutable.
100      */
101     protected abstract NodeParserDispatcher<E> getDispatcher();
102
103     /**
104      * can return null only if you override ParsingStrategy and explicitely return null
105      * @param elements
106      * @param schema
107      * @return
108      */
109     @Nullable
110     @Override
111     public N parse(final Iterable<E> elements, final S schema) {
112
113         checkAtLeastOneNode(schema, elements);
114
115         DataContainerNodeBuilder<P, N> containerBuilder = getBuilder(schema);
116
117         // Map child nodes to QName
118         LinkedListMultimap<QName, E> mappedChildElements = mapChildElements(elements);
119
120         // Map child nodes from Augments
121         Map<QName, AugmentationSchema> mappedAugmentChildNodes = mapChildElementsFromAugments(schema);
122         LinkedListMultimap<AugmentationSchema, E> augmentsToElements = LinkedListMultimap.create();
123
124         // Map child nodes from choices
125         Map<QName, ChoiceSchemaNode> mappedChoiceChildNodes = mapChildElementsFromChoices(schema);
126         LinkedListMultimap<ChoiceSchemaNode, E> choicesToElements = LinkedListMultimap.create();
127
128         Map<QName, String> attributes = getAttributes(elements.iterator().next());
129         if (containerBuilder instanceof AttributesBuilder) {
130             final int size = Iterables.size(elements);
131             Preconditions.checkArgument(size == 1, "Unexpected number of elements: %s, should be 1 for: %s",
132                     size, schema);
133             ((AttributesBuilder<?>) containerBuilder).withAttributes(attributes);
134         }
135
136         //parse keys first
137         if (schema instanceof ListSchemaNode) {
138             for (QName qname : ((ListSchemaNode) schema).getKeyDefinition()) {
139                 if(mappedChildElements.get(qname.withoutRevision()).isEmpty()) {
140                     continue;
141                 }
142
143                 DataSchemaNode childSchema = getSchemaForChild(schema, qname);
144                 List<E> childrenForQName = mappedChildElements.removeAll(qname.withoutRevision());
145
146
147                 DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> optionalChildNode = getDispatcher()
148                         .dispatchChildElement(childSchema, childrenForQName);
149                 if (optionalChildNode != null) {
150                     containerBuilder.withChild(optionalChildNode);
151                 }
152             }
153         }
154
155         //stage attribues for strategy before going deeper in the recursion
156         buildingStrategy.prepareAttributes(attributes, containerBuilder);
157
158         // process Child nodes
159         for (QName childPartialQName : mappedChildElements.keySet()) {
160             DataSchemaNode childSchema = getSchemaForChild(schema, childPartialQName);
161             //with strict parsing an exception would be already thrown, with nonstrict we want to ignore this node
162             if (childSchema == null) {
163                 continue;
164             }
165             List<E> childrenForQName = mappedChildElements.get(childPartialQName);
166
167             // Augment
168             if (isMarkedAs(mappedAugmentChildNodes, childSchema.getQName())) {
169                 AugmentationSchema augmentationSchema = mappedAugmentChildNodes.get(childSchema.getQName());
170                 augmentsToElements.putAll(augmentationSchema, childrenForQName);
171                 // Choices
172             } else if (isMarkedAs(mappedChoiceChildNodes, childSchema.getQName())) {
173                 ChoiceSchemaNode choiceSchema = mappedChoiceChildNodes.get(childSchema.getQName());
174                 choicesToElements.putAll(choiceSchema, childrenForQName);
175                 // Regular child nodes
176             } else {
177                 DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> optionalChildNode = getDispatcher()
178                         .dispatchChildElement(childSchema, childrenForQName);
179                 if (optionalChildNode != null) {
180                     containerBuilder.withChild(optionalChildNode);
181                 }
182             }
183         }
184
185         // TODO ordering is not preserved for choice and augment elements
186         for (ChoiceSchemaNode choiceSchema : choicesToElements.keySet()) {
187             DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> optionalChild = getDispatcher()
188                     .dispatchChildElement(choiceSchema, choicesToElements.get(choiceSchema));
189             if (optionalChild != null) {
190                 containerBuilder.withChild(optionalChild);
191             }
192         }
193
194         for (AugmentationSchema augmentSchema : augmentsToElements.keySet()) {
195             Set<DataSchemaNode> realChildSchemas = getRealSchemasForAugment(schema, augmentSchema);
196             EffectiveAugmentationSchema augSchemaProxy = new EffectiveAugmentationSchema(augmentSchema, realChildSchemas);
197             DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> optionalChild = getDispatcher()
198                     .dispatchChildElement(augSchemaProxy, augmentsToElements.get(augmentSchema));
199             if (optionalChild != null) {
200                 containerBuilder.withChild(optionalChild);
201             }
202         }
203
204         return buildingStrategy.build(containerBuilder);
205     }
206
207     @Override
208     public BuildingStrategy<P, N> getBuildingStrategy() {
209         return buildingStrategy;
210     }
211
212     protected Map<QName, String> getAttributes(final E e) {
213         return Collections.emptyMap();
214     }
215
216     protected boolean strictParsing() {
217         return true;
218     }
219
220     private static boolean isMarkedAs(final Map<QName, ?> mappedAugmentChildNodes, final QName qName) {
221         return mappedAugmentChildNodes.containsKey(qName);
222     }
223
224     protected void checkOnlyOneNode(final S schema, final Iterable<E> childNodes) {
225         final int size = Iterables.size(childNodes);
226         Preconditions.checkArgument(size == 1,
227                 "Node detected multiple times, should be 1, identified by: %s, found: %s", schema, childNodes);
228     }
229
230     private void checkAtLeastOneNode(final S schema, final Iterable<E> childNodes) {
231         Preconditions.checkArgument(!Iterables.isEmpty(childNodes),
232                 "Node detected 0 times, should be at least 1, identified by: %s, found: %s", schema, childNodes);
233     }
234
235     public static class SimpleBuildingStrategy<P extends YangInstanceIdentifier.PathArgument, N extends DataContainerNode<P>> implements BuildingStrategy<P, N> {
236         @Override
237         public N build(final NormalizedNodeBuilder<P, ?, N> builder) {
238             return builder.build();
239         }
240
241         @Override
242         public void prepareAttributes(final Map<QName, String> attributes, final NormalizedNodeBuilder<P, ?, N> containerBuilder) {
243             // NOOP
244         }
245     }
246 }