a594e5d763a8dbe51c8a33fd03d808b97d605d97
[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 org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
20 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
21 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
22 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
23 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
24 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
25 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
28
29 /**
30  * Abstract(base) Parser for DataContainerNodes e.g. ContainerNode, AugmentationNode.
31  */
32 public abstract class BaseDispatcherParser<E, N extends DataContainerNode<?>, S>
33         implements ToNormalizedNodeParser<E, N, S> {
34
35     /**
36      *
37      * @param schema
38      * @return New(empty) instance of a builder to build node identified by schema.
39      */
40     protected abstract DataContainerNodeBuilder<?, N> getBuilder(S schema);
41
42     /**
43      *
44      * @param schema
45      * @param childQName QName of a child being parsed, QName does not continue revision date
46      * @return schema object for child identified by parent schema: schema and QName childQName
47      */
48     protected abstract DataSchemaNode getSchemaForChild(S schema, QName childQName);
49
50     /**
51      *
52      * @param xml
53      * @return map from QName to child elements. Multiple elements are allowed under QName.
54      */
55     protected abstract LinkedListMultimap<QName, E> mapChildElements(Iterable<E> xml);
56
57     /**
58      *
59      * @param schema
60      * @return map from QName to ChoiceNode schema of child nodes that are
61      *         contained within a choice statement under current schema.
62      */
63     protected abstract Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(S schema);
64
65     /**
66      *
67      * @param schema
68      * @return map from QName to child elements that are added by augmentation
69      *         that targets current schema.
70      */
71     protected abstract Map<QName, AugmentationSchema> mapChildElementsFromAugments(S schema);
72
73     /**
74      *
75      * @param schema
76      * @param augmentSchema
77      * @return Set of real schema objects that represent child nodes of an
78      *         augmentation. Augmentation schema child nodes, if further
79      *         augmented, do not contain further augmented, that are crucial for
80      *         parsing. The real schema object can be retrieved from parent schema: schema.
81      */
82     protected abstract Set<DataSchemaNode> getRealSchemasForAugment(S schema, AugmentationSchema augmentSchema);
83
84     /**
85      *
86      * @return dispatcher object to dispatch parsing of child elements, might be
87      *         the same instance if provided parsers are immutable.
88      */
89     protected abstract NodeParserDispatcher<E> getDispatcher();
90
91     @Override
92     public N parse(final Iterable<E> elements, final S schema) {
93
94         checkAtLeastOneNode(schema, elements);
95
96         DataContainerNodeBuilder<?, N> containerBuilder = getBuilder(schema);
97
98         // Map child nodes to QName
99         LinkedListMultimap<QName, E> mappedChildElements = mapChildElements(elements);
100
101         // Map child nodes from Augments
102         Map<QName, AugmentationSchema> mappedAugmentChildNodes = mapChildElementsFromAugments(schema);
103         LinkedListMultimap<AugmentationSchema, E> augmentsToElements = LinkedListMultimap.create();
104
105         // Map child nodes from choices
106         Map<QName, ChoiceSchemaNode> mappedChoiceChildNodes = mapChildElementsFromChoices(schema);
107         LinkedListMultimap<ChoiceSchemaNode, E> choicesToElements = LinkedListMultimap.create();
108
109         // process Child nodes
110         for (QName childPartialQName : mappedChildElements.keySet()) {
111             DataSchemaNode childSchema = getSchemaForChild(schema, childPartialQName);
112             //with strict parsing an exception would be already thrown, with nonstrict we want to ignore this node
113             if (childSchema == null) {
114                 continue;
115             }
116             List<E> childrenForQName = mappedChildElements.get(childPartialQName);
117
118             // Augment
119             if (isMarkedAs(mappedAugmentChildNodes, childSchema.getQName())) {
120                 AugmentationSchema augmentationSchema = mappedAugmentChildNodes.get(childSchema.getQName());
121                 augmentsToElements.putAll(augmentationSchema, childrenForQName);
122                 // Choices
123             } else if (isMarkedAs(mappedChoiceChildNodes, childSchema.getQName())) {
124                 ChoiceSchemaNode choiceSchema = mappedChoiceChildNodes.get(childSchema.getQName());
125                 choicesToElements.putAll(choiceSchema, childrenForQName);
126                 // Regular child nodes
127             } else {
128                 DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> builtChildNode = getDispatcher()
129                         .dispatchChildElement(childSchema, childrenForQName);
130                 containerBuilder.withChild(builtChildNode);
131             }
132         }
133
134         // TODO ordering is not preserved for choice and augment elements
135         for (ChoiceSchemaNode choiceSchema : choicesToElements.keySet()) {
136             containerBuilder.withChild(getDispatcher().dispatchChildElement(choiceSchema,
137                     choicesToElements.get(choiceSchema)));
138         }
139
140         for (AugmentationSchema augmentSchema : augmentsToElements.keySet()) {
141             Set<DataSchemaNode> realChildSchemas = getRealSchemasForAugment(schema, augmentSchema);
142             EffectiveAugmentationSchema augSchemaProxy = new EffectiveAugmentationSchema(augmentSchema, realChildSchemas);
143             containerBuilder.withChild(getDispatcher().dispatchChildElement(augSchemaProxy, augmentsToElements.get(augmentSchema)));
144         }
145
146         if (containerBuilder instanceof AttributesBuilder) {
147             final int size = Iterables.size(elements);
148             Preconditions.checkArgument(size == 1, "Unexpected number of elements: %s, should be 1 for: %s",
149                     size, schema);
150             ((AttributesBuilder<?>) containerBuilder).withAttributes(getAttributes(elements.iterator().next()));
151         }
152
153         return containerBuilder.build();
154     }
155
156     protected Map<QName, String> getAttributes(final E e) {
157         return Collections.emptyMap();
158     }
159
160     protected boolean strictParsing() {
161         return true;
162     }
163
164     private boolean isMarkedAs(final Map<QName, ?> mappedAugmentChildNodes, final QName qName) {
165         return mappedAugmentChildNodes.containsKey(qName);
166     }
167
168     protected void checkOnlyOneNode(final S schema, final Iterable<E> childNodes) {
169         final int size = Iterables.size(childNodes);
170         Preconditions.checkArgument(size == 1,
171                 "Node detected multiple times, should be 1, identified by: %s, found: %s", schema, childNodes);
172     }
173
174     private void checkAtLeastOneNode(final S schema, final Iterable<E> childNodes) {
175         Preconditions.checkArgument(!Iterables.isEmpty(childNodes),
176                 "Node detected 0 times, should be at least 1, identified by: %s, found: %s", schema, childNodes);
177     }
178 }